moinejf / abc2svg

project moved to https://chiselapp.com/user/moinejf/repository/abc2svg
GNU Lesser General Public License v3.0
48 stars 7 forks source link

parsing abc into an object and converting back to a string #32

Open jisike opened 7 years ago

jisike commented 7 years ago

Hello, I'm trying to computationally manipulate an abc tune and wanted to ask how I can use your library to parse an abc string into an object, manipulate the object (ie remove notes, change pitch) then convert it into an abc string. Thanks!

jisike commented 7 years ago

Hello @moinejf just following up on this. Is there any way to access the objects generated from the parser or just the parser itself?

jisike commented 7 years ago

After searching through the code more I found the get_abcmodel but its not working when I call tosvg.

var user = {
      read_file: function(fn) {
        return tune2.value;
      },
      errmsg: function(msg, l, c) {
        var diverr = document.getElementById("warnings2");
        msg = msg;//clean_txt(msg)
        if (l)
        diverr.innerHTML += '<b onclick="gotoabc(' +
        l + ',' + c +
        ')" style="cursor: pointer; display: inline-block">' +
        msg + "</b><br/>\n"
        else
        diverr.innerHTML += msg + "<br/>\n"
      },
      get_abcmodel:function(tsfirst,voice_tb,music_types,info){
        console.log('get_abcmodel');
        console.log(tsfirst);
        console.log(voice_tb);
        console.log(music_types);
        console.log(info);
      },
      img_out: function(){}
    }

   var abc = new Abc(user)
    abc.tosvg('test.abc', tune2.value);
moinejf commented 7 years ago

I am adding a 'to ABC' script for you, but it is not ready yet. I hope it will be out before the end of this week.

jisike commented 7 years ago

Thanks for getting back to me. Will toABC create an object from an ABC string?

moinejf commented 7 years ago

I uploaded the new script toabc.js. It permits to re-generate ABC from ABC. There are some limitations: only the tunes are dumped (no global definitions), and some parameters are lost. To use it, just include it as the first file in the command line. Example:

abcjs24 toabc.js myfile.abc

You may add your own scripts after this one, but using %%abc-include. Example:

abcjs24 toabc.js --abc-include test.js my file.abc

Here is an example of what can be done:

// test: set a sharp sign on all f's
var old_gm = user.get_abcmodel
    user.get_abcmodel = abc_change

function abc_change(tsfirst, voice_tb, music_types, info) {
    for (var s = tsfirst; s; s = s.ts_next) {
        if (s.notes) {
            for (i = 0; i < s.notes.length; i++) {
                if (s.notes[i].apit == 26)  // 'f'
                    s.notes[i].acc = 1  // sharp
            }
        }
    }

    // call the previous get_abcmodel function
    if (old_gm)
        old_gm(tsfirst, voice_tb, music_types, info)
}
jisike commented 7 years ago

Awesome! I just tried it but it didnt work. Also there was an error on line 875:

user.get_abcmodel = abc_dump

which I commented out.

Here's my code

var user = {
  read_file: function(fn) {
    fn = function() {
      //a textarea
      return tune2.value;
    }
  },
  errmsg: function(msg, l, c) {
    var diverr = document.getElementById("warnings2");
    msg = msg;//clean_txt(msg)
    if (l)
    diverr.innerHTML += '<b onclick="gotoabc(' +
    l + ',' + c +
    ')" style="cursor: pointer; display: inline-block">' +
    msg + "</b><br/>\n"
    else
    diverr.innerHTML += msg + "<br/>\n"
  }
} 

var old_gm = user.get_abcmodel
user.get_abcmodel = abc_change

function abc_change(tsfirst, voice_tb, music_types, info) {
  for (var s = tsfirst; s; s = s.ts_next) {
    if (s.notes) {
      for (i = 0; i < s.notes.length; i++) {
        if (s.notes[i].apit == 26)  // 'f'
        s.notes[i].acc = 1  // sharp
      }
    }
  }

  // call the previous get_abcmodel function
  if (old_gm)
  old_gm(tsfirst, voice_tb, music_types, info)
}

var abc2svg = new Abc(user)
jisike commented 7 years ago

Sorry I think my question should be how can I use this in the browser as opposed to the console.

moinejf commented 7 years ago

Then, I don't see the problem. You just load your script after abc2svg-1.js, you set user.get_abcmodel to your function, and you have all the music when the tunes are loaded. About the error on line 875, this is because the script abc2svg-1.js is not loaded.

jisike commented 7 years ago

I double checked it. Yes the abc2svg-1.js is included before toabc.js and after I include my script. However I'm still getting that error.

jisike commented 7 years ago

And what do you mean by convert ABC to ABC? Convert a string to an obj? Or vice versa? or both?

moinejf commented 7 years ago

I thought abc2svg was simple enough. The core (abc2svg-1.js) is a library that contains a ABC parser and a SVG generator. There must be some program to call it. Such programs, or 'drivers', may be run from the command line (abcjs24, abcnode...) or from a web browser (abcemb-2.js, edit-1.xhtml...). They create an instance of the object Abc with some user parameters in the object instance 'user'. This user is responsible about what is to be done with the result of the library: put the SVG images in a file, put the SVG images in a web page, generate sounds... In your first message, you wanted "to parse an abc string into an object, manipulate the object (ie remove notes, change pitch) then convert it into an abc string". That's what 'toabc.js' does. As a backend, it gets the result of the ABC parser by get_abcmodel, converts the model to ABC and stops the SVG generation. So, the layout is:

The user 'get_abcmodel' may be replaced by an other function which may 'manipulate' the model before sending it back to the original get_abcmodel function. That's what the 'test.js' script does in my previous message.

So, in brief, you must:

jisike commented 7 years ago

Hello, yes I understand how it works but as I mentioned it's not working with the code I shared. Could you create a code example? Also, one of my questions were can to ABC convert both ways: ABC str to obj and obj to str?

moinejf commented 7 years ago

OK, let's restart from the beginning: which driver are you using?

Otherwise, I don't understand your 'obj'. ABC is music, and music is composed of a lot of objects: notes, rests, staves, clef, key and time signatures, various decorations... In get_abcmodel, I propose two main entry points:

You may know what are the properties of each music object (called 'symbol' in the source) by looking at the script toabc.js.

jisike commented 7 years ago

What do you mean by 'driver'? By 'obj' I mean converting ABC notation into a Javascript object/JSON. It's good to know what tsfirst and voice_tb mean. How difficult would it be to recreate the ABC notation as a string using them.

moinejf commented 7 years ago

The driver is the entity which is responsible to inject the source into the translator. The entities that treat the translation result are the backends. With abc2svg, there are 2 kinds of drivers: the batch one and the web browser one. For batch, the drivers are built from a javascript interpreter main script (as abcjs24) and the command line parser (cmdline.js). This last script is responsible for getting the ABC source from the files and injecting it into the ABC parser (part of the abc2svg core). For web browser, the drivers get the ABC source from the browser (HTML element or remote request) and inject it into the ABC parser (same as for batch). As told in my previous message, there are 2 main drivers:

About your 'obj', there is no such entity in abc2svg. The music representation cannot be done by a linear structure as JSON. But, if you absolutely want it, the abc2svg package contains the script json.js which generates a simplified JSON from the ABC model.

The recreation of ABC from the ABC model is done by the new script toabc.js. It was created for batch, so it uses the 'print' function of the standalone javascript interpreters. If you want to use this function in the web browser world, just replace 'print' by some other function.

bwl21 commented 7 years ago

I was away from keyboard for a while and saw this ticket. I think what @jisike asks for is provided by AbcJSON.gen_json

Of course it should be possible to implement a processor which renders the result of gen_json to an ABC-String, even if I think this is not a simple task.

moinejf commented 7 years ago

JSON is not the right way to serialize data as the music representation: it lacks references. Then, it is quite impossible to rebuild ABC from a JSON representation: look at toabc.js and see how the voice and time links are used. YAML would be better. But, anyway, serialization is usefull to transfer data between processes or systems. Is there such a need here?

bwl21 commented 7 years ago

Well JSON vs. YAML is just another serialization format. So if YAML works fine, then JSON also will do.

I agree, that serialization is bascically useful to to transfer data. I case of Zupfnoter AbcJSON provided a safe way to get the model, dump it to a log file, and and transfer it to Opalrb (Ruby) land.

ABCJSON.gen_json solved two issues:

  1. The problem was, that abc2svg model has circular references which cannot be serialized. But IMHO they can be regnerated using the object ids.

  2. ABCJSON provides a model which is a bit more stable, since some changes in the internal model can be transformed. In this sense ABCJSON.gen_json provides a kind of "Exchange" model.

Therefore I anticipated that it can be helpful for @jisike as well.

Of course it is not possible to create exactly the same ABC as in the input file. But I am convinced that it would be possible to create a valid ABC representation. I sometimes feel tempted to do this in order to achieve a kind of beautifier / normalizer for ABC.

moinejf commented 7 years ago

You wrote:

if YAML works fine, then JSON also will do.

That's not true: YAML has references, so that you can rebuild the exact data, including the pointers, in any language. It solves your two issues and serializes the circular references .

About a 'beautifier / normalizer for ABC', did you try toabc.js?

jisike commented 7 years ago

i've been using musicxml and xml2abc (https://wim.vree.org/js/xml2abc-js.html) to manipulate it like a DOM using d3.js and translate it into abc. i just needed the music to be in a format that was easier to manipulate so it had to be either xml or json. itd be great if the libraries that rendered abc also parsed them into standard formats like musicxml or musicjson.

bwl21 commented 7 years ago

That's not true: YAML has references, so that you can rebuild the exact data, including the pointers, in any language. It solves your two issues and serializes the circular references .

oh, I was not aware of this. Thanks for the hint.

About a 'beautifier / normalizer for ABC', did you try toabc.js?

not yet, but I surely will. Thanks.

moinejf commented 7 years ago

@jisike, now I see what you want. You may easily generate musicjson from the get_abcmodel callback function looking at the source of util/json.js. Translating back musicjson to ABC should be easy too (musicjson is a small subset of ABC), but, be aware of losses.