<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
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |