Skip to content

Instantly share code, notes, and snippets.

@seemikehack
Created September 17, 2014 04:55
Show Gist options
  • Save seemikehack/287d3c8cd836b1807b44 to your computer and use it in GitHub Desktop.
Save seemikehack/287d3c8cd836b1807b44 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<script src="http://cdnjs.cloudflare.com/ajax/libs/bluebird/1.2.2/bluebird.js"></script>
<script src="https://rawgithub.com/keithwhor/audiosynth/master/audiosynth.js"></script>
<script id="jsbin-javascript">
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);
</script>
<script id="jsbin-source-html" type="text/html"><!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></script>
<script id="jsbin-source-javascript" type="text/javascript">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);
</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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment