Frage

I am developing a Piano application. I have a json array with note names and their duration of playing time.

var data= [{"duration":300,"value":"2C"},{"duration":400,"value":"2D"},{"duration":420,"value":"2E"},{"duration":600,"value":"2F"},{"duration":400,"value":"2G"}];

I need to play 2C note for 300 microseconds, 2D note for 400 microseconds, 2E for 420 and so on in a sequence, that is playing next note after completion of previous note.

I have audio files in .ogg format for all notes and all notes have same duration of 1018.776 microseconds.

To play the notes of above json data, I have tried setTimeout function of javascript:

$.each( data, function( key, value ) {
    setTimeout(function(){
        var audioElement = document.createElement('audio');
        audioElement.setAttribute('src', './audio/'+value.value+'.ogg');
        audioElement.play();

    }, value.duration*key); 
});

But this is not working. The main problem is on duration. When I use console.log(value.value), the result was 2C, 2D, 2E, 2G, 2F. Here the sequence of 2F and 2G is not in correct order. So, How to play these notes in correct sequence with their respective duration?

War es hilfreich?

Lösung

You need to use a recursive function instead of loop:

function playNotes(notes) {
    var i = 0;
    playNextNote();
    function playNextNote() {
        if (i < notes.length) {
            var value = notes[i];
            var audioElement = document.createElement('audio');
            audioElement.setAttribute('src', './audio/'+value.value+'.ogg');
            audioElement.play();
            i++;
            setTimeout(playNextNote, value.duration);
        }
    }
}

In this way, the next note isn't triggered to start playing until the current note has completed.

Andere Tipps

You can use a function that receives the array and index and then call itself with the next index after the delay.

var data= [{"duration":300,"value":"2C"},{"duration":400,"value":"2D"},{"duration":420,"value":"2E"},{"duration":600,"value":"2F"},{"duration":400,"value":"2G"}];

function playNote(data, index) {
    var audioElement = document.createElement('audio');
    audioElement.setAttribute('src', './audio/'+data[index].value+'.ogg');
    audioElement.play();

    if (index + 1 < data.length) {
        setTimeout(function() {
            playNote(data, index + 1);
        }, data[index].duration);
    }
}

playNote(data, 0);

prefer to call other value after the timeout.

function callAudio (index) {
    var
    value = this[i],
    audioElement = document.createElement('audio');

    if (!value) return;

    audioElement.setAttribute('src', './audio/'+value.value+'.ogg');
    audioElement.play();
    setTimeout(callAudio.bind(this, index++), value.duration);
};

callAudio.call(data, 0);

There's a couple assumptions you're making with this code - the first one I see is that the sound file loads instantly. The issue you're probably having is that the loop is not keeping track of the delay so far - so basically you're calling setTimeout({play}, 400) and immediately after setTimeout({play}, 500) so they end up overlapping after 500ms until 800 ms.

The way I would write what I think you're looking for is like this:

var audio = $.map(data, function(key,val) {return $("<audio>",{src:val.value})});
audio.each(function(indx, $ele) {
      if(indx !=0){
          audio[indx-1].on("ended", function(){$ele.get().play()});//play next sound when previous one finishes playing
      }
});
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top