Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
  <script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/1.2.2/bluebird.js"></script>
  <script src="https://rawgithub.com/keithwhor/audiosynth/master/audiosynth.js"></script>
</body>
</html>
 
if (!window.AssertException) {
  window.AssertException = function (msg) {
    this.message = msg;
  };
  window.AssertException.prototype.toString = function () {
    return 'AssertException: ' + this.message;
  };
  window.assert = function (exp, msg) {
    if (!exp) throw new AssertException(msg);
  };
}
window.AudioContext = window.AudioContext || window.webkitAudioContext;
assert(window.AudioContext, 'AudioContext unavailable in this environment');
assert(window.Promise, 'Promises unavailable in this environment');
function RiffPlayer(instrument, articulation) {  
  // initial note values, relative to the beat (denote rhythm)
  // NOTE these values assume */4 time signature (quarter note is the beat)
  var noteValues = {
    W : 4,   // Whole
    H : 2,   // Half
    Q : 1,   // Quarter
    I : 1/2, // eIghth (deconflicting with the scale tone E, unnecessary but less ambiguous)
    S : 1/4, // Sixteenth
    T : 1/8, // Thirty-second
    X : 1/16 // siXty-fourth (obviously deconflicting with the Sixteenth and eIghth notes)
  };
  
  var noteExtractor      = /[A-G]#?/i,
      octaveExtractor    = /[1-7]/,
      noteValueExtractor = /[WHQISTX]/i;
  // initialize synth
  var ctx        = new AudioContext(),
      synth      = Synth.createInstrument(instrument || 'piano'),
      noteLength = articulation;
  function getNoteUri(noteData) {
    var note      = noteData.match(noteExtractor)[0],
        octave    = noteData.match(octaveExtractor)[0],
        noteValue = noteData.match(noteValueExtractor)[0],
        length    = noteLength;
    assert(note, 'unable to generate note uri, note was unspecified');
    assert(octave, 'unable to generate note uri, octave was unspecified');
    assert(noteValue, 'unable to generate note uri, noteValue was unspecified');
    return {
      uri: synth.generate(note, octave, length),
      value: noteValue
    };
  }
  function getNoteData(noteData) {
    var promise = new Promise(function (resolve, reject) {
      var request = new XMLHttpRequest();
      request.open('GET', noteData.uri, true);
      request.responseType = 'arraybuffer';
      // TODO is onload standard? I think not...
      request.onload = function() {
        ctx.decodeAudioData(request.response, function (response) {
          resolve({
            note: response,
            value: noteData.value
          });
        }, reject);
      };
      request.send();
    });
    return promise;
  }
  function playNote(audio, time) {
    var src = ctx.createBufferSource();
    src.buffer = audio;
    src.connect(ctx.destination);
    src.start(time);
  }
  function atTempo(tempo) {
    return function (time, noteValue) {
      return time + 60 / tempo * noteValues[noteValue];
    };
  }
  /////////////
  // public API
  
  this.play = function (notes, tempo) {
    // TODO .map(f).map(g) === .map(compose (g, f))
    Promise.all(notes.map(getNoteUri).map(getNoteData)).then(function (samples) {
      var time = ctx.currentTime;
      var stepTime = atTempo(tempo);
      samples.forEach(function (sample) {
        playNote(sample.note, time);
        time = stepTime(time, sample.value);
      });
    }).catch(function (error) {
      // FIXME implement
      console.log(error);
    });
  };
}
// pre-built instuments
RiffPlayer.instruments = {
  piano    : 0,
  organ    : 1,
  acoustic : 2,
  edm      : 3
};
// sample (note) lengths for the synth, i.e., how long each sample plays
// NOTE these are different from note values, which denote rhythm
RiffPlayer.articulations = {
  legato   : 2.0,
  tenuto   : 1.0,
  staccato : 0.5
};
////////////////////////////////
// end library, begin test usage
var notes        = ['C3S','E3S','G3S','C4S','G3S','E3S','C3S'],
    notes2       = ['A2I', 'C3S', 'F3S', 'A3I', 'G3S', 'F3S', 'E3I', 'C3I', 'A2I'],
    tempo        = 108,
    instrument   = RiffPlayer.instruments.acoustic,
    articulation = RiffPlayer.articulations.tenuto;
var player = new RiffPlayer(instrument, articulation);
player.play(notes2, tempo);
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
wormmdpro
0viewers