0

I'm using the embedded Rhino Interpreter in Blue (a music composition environment for Csound) to generate a "score" (music notation). In blue you can do this by writing a function an then doing

score = myFunction()

My function gets an image using onLoad and extracts the pixel information, which will be used to generate the score. The problem is my function doesn't get enough time to load the image and return the data before it assigns it to a variable. I've tried using setTimeout() but that didn't help.

I tried this in a browser and it returns "undefined" indeed.

Basically I need a way of delaying the assignment to the score variable. Is this possible?

Thank you

function score(){
    var img = new Image();
    img.src = "http://static.webshopapp.com/shops/023001/files/024718445/256x256x2/major-dog-barbell-mini.jpg";
    img.crossOrigin = "Anonymous";
    var score = "abc";
    img.onload = function(){
        var canvas = document.createElement("canvas");
        canvas.width = img.width;
        canvas.height = img.height;
        var ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0);   
        var imgData=ctx.getImageData(0,0,canvas.width,canvas.height);
        score = "i1 0 2 440 0.5\n"
        for (var i=0;i<imgData.data.length;i+=4){
            score += "i1 + 0.1 " + (imgData.data[i] + 500).toString() + " 0.5\n"
        }
        return score;
    }
}
score = score();
// TRY THIS IN BROWSER - RETURNS UNDEFINED
//console.log(score())
Morteza Asadi
  • 1,819
  • 2
  • 22
  • 39

2 Answers2

1

(Author of Blue here)

For Blue, it is actually using Nashorn now which is built into Java 8. (I have renamed the object to JavaScriptObject in the new Blue release.)

Nashorn provides a JS engine but does not, as far as I understand, provide all of the APIs one expects in a browser. I ran and debugged your code and found some exceptions being thrown regarding "document" and "Image" not being defined. I rewrote the code using Java objects, such as:

function genScore(){

    var url = new java.net.URL("http://static.webshopapp.com/shops/023001/files/024718445/256x256x2/major-dog-barbell-mini.jpg");
    var img = javax.imageio.ImageIO.read(url);


    score = "i1 0 2 440 0.5\n"
    for (var i = 0; i < img.getHeight(); i++) {
        for (var j = 0; j < img.getWidth(); j++) {
            var rgb = img.getRGB(i, j);
            score += "i1 + 0.1 " + (rgb + 500).toString() + " 0.5\n"
        };
    }
    return score;
}
score = genScore();

and that roughly worked. (I think your code is using just the red values if I understood correctly; this code would have to be modified with a bit mask and shift to get just the R value from the RGB; more information about Java's BufferedImage class available at https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html).

Steven Yi
  • 121
  • 2
  • Additionally, the Blue code looks like it was only printing exception data to the application log, which made this hard to debug. I've modified Blue for 2.7.0 so that it will report code issues like this to the user. – Steven Yi Jan 15 '17 at 20:35
0

What you need, is a callback function passed into the score function that will be fired when the image has been loaded:

// Adding a callback function as parameter
function score(callback){
    var img = new Image();
    img.src = "http://static.webshopapp.com/shops/023001/files/024718445/256x256x2/major-dog-barbell-mini.jpg";
    img.crossOrigin = "Anonymous";
    var score = "abc";

    img.onload = function(){

        var canvas = document.createElement("canvas");

        canvas.width = img.width;
        canvas.height = img.height;

        var ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0);   
        var imgData=ctx.getImageData(0,0,canvas.width,canvas.height);

        score = "i1 0 2 440 0.5\n"
        for (var i=0;i<imgData.data.length;i+=4)
        {
            score += "i1 + 0.1 " + (imgData.data[i] + 500).toString() + " 0.5\n"
        }
        // Now we can run the callback with our score data
        return callback(score);

    }
}

score(function(score){

  console.log(score);
  // Do your stuff with score data...

});
Kostas Minaidis
  • 4,681
  • 3
  • 17
  • 25
  • Thank you for your response, that works like a charm in the browser, however it won't work in Blue, it needs something of the form score = myFunction() in the main program (the assignment needs to happen outside of any functions). Do you think there's any way I can do this? – DrumPower3004 Jan 12 '17 at 14:20
  • The nature of asynchronous calls, requires that you use callback functions to get back the result. Unfortunately, I am not familiar with Rhino and the Blue platform, in order to provide any more help on this. You will have to either place the score=myFunction() inside the anonymous function (where the console.log(score) sits in the code above right now) or consider using Promises, if available. – Kostas Minaidis Jan 13 '17 at 02:18