yes - sorry I thought about it afterwards adn don't think that is the issue/
no worries
the errors are tilting me much more
i think i have it....
i used xml2js and had to install another Node.js package const glob = nativeRequire('glob');
use npm i glob
to install it.
here's the code:
const path = nativeRequire('path')
const fs = nativeRequire('fs');
const xml2js = nativeRequire('xml2js');
const glob = nativeRequire('glob');
const files = glob.sync(path.join(__dirname, '../../Steinberg/Cubase/Expression Maps/Native Instruments/Symphony Series/*.expressionmap'));
const articulations = {}
const maps = {}
files.forEach(function(f) {
fs.readFile(f, function(err, data) {
var parser = new xml2js.Parser({explicitArray: false, mergeAttrs: true});
parser.parseString(data, function(err, maps) {
// parsing done here
for (var inst in maps) {
try {
articulations[inst] = maps.InstrumentMap.member[1].list.obj[0].member[1].string.value
maps = maps.InstrumentMap.string.value
} catch (err) {
console.error(`failed to extract articulations from ${inst}.json:`)
console.error(err)
}
}
console.log('Extracted articulations:\n')
console.log(articulations[inst])
console.log(maps)
});
});
});
Which directory did you install your modules into?
in the custom module folder
Haveing some more thinking time...
At the moment we're ripping all the xml out of all the maps in one go and this will all sit in the memory until we need to use it.
And also, the code currently only gets the first articulation in the map.
This gives me two problems - 1. I have nearly 300 maps
Some of those maps have 60 articulations. This means there could be up to 18000 articulations sitting in the memory...
So here's an approach - and it will reply on the sysex track names working and I will reference my tracks with the articulation map as follows:
We can then pull the map number from the sysex and only go and get that map.
//now accessing an xml in a file structure
const path = nativeRequire('path')
const fs = nativeRequire('fs');
const xml2js = nativeRequire('xml2js');
const glob = nativeRequire('glob');
//const files = glob.sync(path.join(__dirname, '../../Steinberg/Cubase/Expression Maps/Native Instruments/Symphony Series/*.expressionmap'));
const mapFiles = glob.sync(path.join(__dirname, '../ExpressionMaps/*.expressionmap'));
const articulations = {}
var maps = {}
var trackID = '117-7' //test this will come from the TRACK ID sysex stuff
mapFiles.forEach(function (mapFile) {
//debug
//console.log('Selected Tracks ' + mapFile); // List out all the expression maps
//console.log('mapFiles length' + mapFiles.length) // gets the length of list of the files - number of expression maps in directory
//debug
var parser = new xml2js.Parser();
if (mapFile.includes(trackID)) {
console.log('Selected Map ' + mapFile);
fs.readFile(mapFile, function (err, data) {
parser.parseString(data, function (err, result) {
//Need to add a loop here that collects all the articulations in the map, not just the one at
//InstrumentMap.member[1].list.obj[0].member[1].string.value
// I need to collect the articulation and the corresponding color designation (1 - 6)
//Colour is at: /InstrumentMap/member/list/obj/int[@name=""color""]
//result id of the format
//{
// InstrumentMap: { string: [[Object]], member: [[Object], [Object], [Object]] }
//}
console.dir(result);
console.log('Done');
});
});
}
});
Ok, so we've worked hard to solve a problem where we can now parse a lot of xml into the CM and create an object within the functions that contains all the data from the xml that we want. Credit must go to @gcat who has done most of the hard work.
I've now transferred this into a function that we should be able to call on eventually to populate the OSC labels etc.
By my JS skills are very limited and now what I can't do is get the function to return the object as a result so that I can use it in my wider code.
I've logged to console each exit point of each nested function and worked out where it gets stuck but can't work out how to get the data any further...
Could someone (@jean-emmanuel I know you're really busy but....) have a look and see why it is stuck at the point where I've commented. Many thanks if you choose to help a little!
//now accessing an xml in a file structure
const path = nativeRequire('path')
const fs = nativeRequire('fs');
const xml2js = nativeRequire('xml2js');
const glob = nativeRequire('glob');
//const files = glob.sync(path.join(__dirname, '../../Steinberg/Cubase/Expression Maps/Native Instruments/Symphony Series/*.expressionmap'));
const mapFiles = glob.sync(path.join(__dirname, '../ExpressionMaps/*.expressionmap'));
var artArr = []
var artColor = []
var trackID = '117-4' //test value - this will come from the TRACK ID sysex stuff
buildMap() //I need Build Map to populate Objects artArr and artColor and return them so I can use them in other places in the code
function buildMap() {
mapFiles.forEach(function (mapFile) { //sends mapFile into the parser
//debug
//console.log('Selected Tracks ' + mapFile); // List out all the expression maps
//console.log('mapFiles length' + mapFiles.length) // gets the length of list of the files - number of expression maps in directory
//debug
var parser = new xml2js.Parser({ explicitArray: false, mergeAttrs: true }); // 'mergeAttrs: true' was essential
if (mapFile.includes(trackID)) {
console.log('Selected Map: ' + mapFile);
fs.readFile(mapFile, function (err, data) {
parser.parseString(data, function (err, result) {
let art = result.InstrumentMap.member[1].list.obj;
for (let i = 0, len = art.length; i < len; i++) {
console.log('Articulation at poition: ' + i + ' ' + art[i].member[1].string.value + '; ' + 'Color Value: ' + art[i].int.value) // articulatins and color value
artArr[i] = art[i].member[1].string.value
artColor[i] = art[i].int.value
}
});
//THE BELOW LOGS TO CONSOLE OK
console.log('CHECK LOG artArr[0] ' + artArr[0])
console.log('CHECK LOG artArr[1] ' + artArr[1])
console.log('CHECK LOG artArr[2] ' + artArr[2])
console.log('CHECK LOG artArr[3] ' + artArr[3])
console.log('CHECK LOG artArr[4] ' + artArr[4])
console.log('CHECK LOG artArr[5] ' + artArr[5])
console.log('CHECK LOG artArr[6] ' + artArr[6])
console.log('CHECK LOG artArr[7] ' + artArr[7])
});
//THE BELOW LOGS TO CONSOLE as UNDEFINED artArr and artColor get stuck here
console.log('CHECK LOG 2 artArr[0] ' + artArr[0])
console.log('CHECK LOG 2 artArr[1] ' + artArr[1])
console.log('CHECK LOG 2 artArr[2] ' + artArr[2])
console.log('CHECK LOG 2 artArr[3] ' + artArr[3])
console.log('CHECK LOG 2 artArr[4] ' + artArr[4])
console.log('CHECK LOG 2 artArr[5] ' + artArr[5])
console.log('CHECK LOG 2 artArr[6] ' + artArr[6])
console.log('CHECK LOG 2 artArr[7] ' + artArr[7])
}
});
}
console.log('Test log a value from artArr ' + artArr[3]) //Needs to return a value but currently returns 'undefined'
console.log('Test log a value from artColor ' + artColor[3]) //Needs to return a value but currently returns 'undefined'
I’ve just discovered asynchronous programming. Functions like fs.readFile()
and parser.parseString()
are asynchronous apparently…
Houston we may have a problem…
we have to declare the variables without 'var' inside the function. with this it worked:
//now accessing an xml in a file structure
const path = nativeRequire('path')
const fs = nativeRequire('fs');
const xml2js = nativeRequire('xml2js');
const glob = nativeRequire('glob');
const mapFiles = glob.sync(path.join(__dirname, '../../Steinberg/Expression Maps/*.expressionmap'));
//const mapFiles = glob.sync(path.join(__dirname, '../ExpressionMaps/*.expressionmap'));
buildMap() //I need Build Map to populate Objects artArr and artColor and return them so I can use them in other places in the code
function buildMap() {
artArr = []
artColor = []
trackID = '117-7' //test value - this will come from the TRACK ID sysex stuff
mapFiles.forEach(function (mapFile) { //sends mapFile into the parser
//debug
//console.log('Selected Tracks ' + mapFile); // List out all the expression maps
//console.log('mapFiles length' + mapFiles.length) // gets the length of list of the files - number of expression maps in directory
//debug
var parser = new xml2js.Parser({ explicitArray: false, mergeAttrs: true }); // 'mergeAttrs: true' was essential
if (mapFile.includes(trackID)) {
console.log('Selected Map: ' + mapFile);
fs.readFile(mapFile, function (err, data) {
parser.parseString(data, function (err, result) {
let art = result.InstrumentMap.member[1].list.obj;
for (let i = 0, len = art.length; i < len; i++) {
console.log('Articulation at poition: ' + i + ' ' + art[i].member[1].string.value + '; ' + 'Color Value: ' + art[i].int.value) // articulatins and color value
artArr[i] = art[i].member[1].string.value
artColor[i] = art[i].int.value
}
});
//THE BELOW LOGS TO CONSOLE OK
console.log('CHECK LOG artArr[0] ' + artArr[0])
console.log('CHECK LOG artArr[1] ' + artArr[1])
console.log('CHECK LOG artArr[2] ' + artArr[2])
console.log('CHECK LOG artArr[3] ' + artArr[3])
console.log('CHECK LOG artArr[4] ' + artArr[4])
console.log('CHECK LOG artArr[5] ' + artArr[5])
console.log('CHECK LOG artArr[6] ' + artArr[6])
console.log('CHECK LOG artArr[7] ' + artArr[7])
});
//THE BELOW LOGS TO CONSOLE as UNDEFINED artArr and artColor get stuck here
console.log('CHECK LOG 2 artArr[0] ' + artArr[0])
console.log('CHECK LOG 2 artArr[1] ' + artArr[1])
console.log('CHECK LOG 2 artArr[2] ' + artArr[2])
console.log('CHECK LOG 2 artArr[3] ' + artArr[3])
console.log('CHECK LOG 2 artArr[4] ' + artArr[4])
console.log('CHECK LOG 2 artArr[5] ' + artArr[5])
console.log('CHECK LOG 2 artArr[6] ' + artArr[6])
console.log('CHECK LOG 2 artArr[7] ' + artArr[7])
}
});
}
console.log('Test log a value from artArr ' + artArr[3]) //Needs to return a value but currently returns 'undefined'
console.log('Test log a value from artColor ' + artColor[3]) //Needs to return a value but currently returns 'undefined'
what does that mean?
too much reading...my head's still squashed
EDIT: i get the log2 now. i'll have a read later
i meant i got it. looked at the wrong log.
@Sylvain credit goes to @Sub3OneDay for this
I was about to reply about the asynchronous thing but it seems you found it !
Ok, so finally got back to my machine and have been through the Stack Overflow answer. I understand what they are saying about the asynchronous operations and see why that will be a problem.
Here's the new code:
//now accessing an xml in a file structure
const path = nativeRequire('path');
const fs = nativeRequire('fs');
const xml2js = nativeRequire('xml2js');
const glob = nativeRequire('glob');
const mapFiles = glob.sync(path.join(__dirname, '../ExpressionMaps/*.expressionmap'));
const trackID = '117-4' //test value - this will come from the TRACK ID sysex stuff
async function buildMap() {
const artArr = [];
const artColor = [];
for (const mapFile of mapFiles) {
console.log('Selected Map: ' + mapFile);
if (mapFile.includes(trackID)) {
const parser = new xml2js.Parser({
explicitArray: false,
mergeAttrs: true
}); // 'mergeAttrs: true' was essential
console.log('Selected Map: ' + mapFile);
const data = await fs.promises.readFile(mapFile);
const result = await parser.parseString(data);
let art = result.InstrumentMap.member[1].list.obj;
// const art = result.InstrumentMap.member[1].list.obj;
// NOTE: this code assumes that this part only ever runs for
// one mapFile, that all the others don't match the trackID test
// because if you run this more than once, it will just
// overwrite the members of artArr and artColor
for (let i = 0, len = art.length; i < len; i++) {
console.log('Articulation at poition: ' + i + ' ' + art[i].member[1].string.value + '; ' +
'Color Value: ' + art[i].int.value) // articulatins and color value
artArr[i] = art[i].member[1].string.value
artColor[i] = art[i].int.value
}
break;
}
}
return { artArr, artColor };
}
buildMap().then(result => {
// use your results here
console.log(result);
}).catch(err => {
console.log(err);
});
However, we now seem to be coming up with a problem. The code runs ok in so far as it gets the correct xml map but it fails at let art = result.InstrumentMap.member[1].list.obj;
and I get an error message saying TypeError: Cannot read properties of undefined (reading 'member') at buildMap
This seems to say that the code can't now access the xml file, which it could do before? Are we back to the beginning??!
that's about as far as i got. i understand the logic.
checked a few youtube videos about sync and async. but my brain is not at deep thinking level yet.
maybe i'll try later this evening.
EDIT:
i think we have the same problem as befor. logging outside the function gets an error:
EDIT2:
when we remove the const of 'artArr' and 'artColor' then we can reference them outside the function.
Someone answered on stack :
I corrected one line of code. Change
const result = await parser.parseString(data);
toconst result = await parser.parseStringPromise(data);
. The xml2js did not support promises the same way almost every other library does - no idea why. Anyway, it is corrected now.
It works now :
Now we’re cooking…. This is going to be amazing….
I just need ti implement the sysex thing first…
AMAZING!!!
can you reference artArr and artColor outside now. i thought i have it before but now for me this part is not working:
or am i missing something?
how do we access our articulations?
i can't console.log outside the function but somehow my buttons work.
HALLE “#$%& LUJAH