JavaScript in Rhino Interpreter - function returns undefined

537 Views Asked by At

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())
2

There are 2 best solutions below

2
On

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...

});
1
On

(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).