summaryrefslogtreecommitdiff
path: root/public/js/vendor/Audiolet.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/js/vendor/Audiolet.js')
-rw-r--r--public/js/vendor/Audiolet.js680
1 files changed, 498 insertions, 182 deletions
diff --git a/public/js/vendor/Audiolet.js b/public/js/vendor/Audiolet.js
index b7be12e..66dc9e8 100644
--- a/public/js/vendor/Audiolet.js
+++ b/public/js/vendor/Audiolet.js
@@ -1,4 +1,19 @@
/**
+ * The base audiolet object. Contains an output node which pulls data from
+ * connected nodes.
+ *
+ * @constructor
+ * @param {Number} [sampleRate=44100] The sample rate to run at.
+ * @param {Number} [numberOfChannels=2] The number of output channels.
+ * @param {Number} [bufferSize] Block size. If undefined uses a sane default.
+ */
+var Audiolet = function(sampleRate, numberOfChannels, bufferSize) {
+ this.output = new AudioletDestination(this, sampleRate,
+ numberOfChannels, bufferSize);
+};
+
+
+/**
* A variable size multi-channel audio buffer.
*
* @constructor
@@ -836,21 +851,6 @@ AudioletInput.prototype.toString = function() {
/**
- * The base audiolet object. Contains an output node which pulls data from
- * connected nodes.
- *
- * @constructor
- * @param {Number} [sampleRate=44100] The sample rate to run at.
- * @param {Number} [numberOfChannels=2] The number of output channels.
- * @param {Number} [bufferSize] Block size. If undefined uses a sane default.
- */
-var Audiolet = function(sampleRate, numberOfChannels, bufferSize) {
- this.output = new AudioletDestination(this, sampleRate,
- numberOfChannels, bufferSize);
-};
-
-
-/**
* Class representing a single output of an AudioletNode
*
* @constructor
@@ -2604,6 +2604,80 @@ CrossFade.prototype.toString = function() {
*/
/**
+ * Filter for leaking DC offset. Maths is taken from
+ * https://ccrma.stanford.edu/~jos/filters/DC_Blocker.html
+ *
+ * **Inputs**
+ *
+ * - Audio
+ * - Filter coefficient
+ *
+ * **Outputs**
+ *
+ * - Filtered audio
+ *
+ * **Parameters**
+ *
+ * - coefficient The filter coefficient. Linked to input 1.
+ *
+ * @constructor
+ * @extends AudioletNode
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {Number} [coefficient=0.995] The initial coefficient.
+ */
+var DCFilter = function(audiolet, coefficient) {
+ AudioletNode.call(this, audiolet, 2, 1);
+
+ // Same number of output channels as input channels
+ this.linkNumberOfOutputChannels(0, 0);
+
+ this.coefficient = new AudioletParameter(this, 1, coefficient || 0.995);
+
+ // Delayed values
+ this.xValues = [];
+ this.yValues = [];
+};
+extend(DCFilter, AudioletNode);
+
+/**
+ * Process samples
+ */
+DCFilter.prototype.generate = function() {
+ var coefficient = this.coefficient.getValue();
+ var input = this.inputs[0];
+ var numberOfChannels = input.samples.length;
+ for (var i = 0; i < numberOfChannels; i++) {
+ if (i >= this.xValues.length) {
+ this.xValues.push(0);
+ }
+ if (i >= this.yValues.length) {
+ this.yValues.push(0);
+ }
+
+ var x0 = input.samples[i];
+ var y0 = x0 - this.xValues[i] + coefficient * this.yValues[i];
+
+ this.outputs[0].samples[i] = y0;
+
+ this.xValues[i] = x0;
+ this.yValues[i] = y0;
+ }
+};
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+DCFilter.prototype.toString = function() {
+ return 'DC Filter';
+};
+
+/*!
+ * @depends ../core/AudioletNode.js
+ */
+
+/**
* Damped comb filter
*
* **Inputs**
@@ -2704,80 +2778,6 @@ DampedCombFilter.prototype.toString = function() {
*/
/**
- * Filter for leaking DC offset. Maths is taken from
- * https://ccrma.stanford.edu/~jos/filters/DC_Blocker.html
- *
- * **Inputs**
- *
- * - Audio
- * - Filter coefficient
- *
- * **Outputs**
- *
- * - Filtered audio
- *
- * **Parameters**
- *
- * - coefficient The filter coefficient. Linked to input 1.
- *
- * @constructor
- * @extends AudioletNode
- * @param {Audiolet} audiolet The audiolet object.
- * @param {Number} [coefficient=0.995] The initial coefficient.
- */
-var DCFilter = function(audiolet, coefficient) {
- AudioletNode.call(this, audiolet, 2, 1);
-
- // Same number of output channels as input channels
- this.linkNumberOfOutputChannels(0, 0);
-
- this.coefficient = new AudioletParameter(this, 1, coefficient || 0.995);
-
- // Delayed values
- this.xValues = [];
- this.yValues = [];
-};
-extend(DCFilter, AudioletNode);
-
-/**
- * Process samples
- */
-DCFilter.prototype.generate = function() {
- var coefficient = this.coefficient.getValue();
- var input = this.inputs[0];
- var numberOfChannels = input.samples.length;
- for (var i = 0; i < numberOfChannels; i++) {
- if (i >= this.xValues.length) {
- this.xValues.push(0);
- }
- if (i >= this.yValues.length) {
- this.yValues.push(0);
- }
-
- var x0 = input.samples[i];
- var y0 = x0 - this.xValues[i] + coefficient * this.yValues[i];
-
- this.outputs[0].samples[i] = y0;
-
- this.xValues[i] = x0;
- this.yValues[i] = y0;
- }
-};
-
-/**
- * toString
- *
- * @return {String} String representation.
- */
-DCFilter.prototype.toString = function() {
- return 'DC Filter';
-};
-
-/*!
- * @depends ../core/AudioletNode.js
- */
-
-/**
* A simple delay line.
*
* **Inputs**
@@ -2934,98 +2934,6 @@ DiscontinuityDetector.prototype.toString = function() {
*/
/**
- * Delay line with feedback
- *
- * **Inputs**
- *
- * - Audio
- * - Delay Time
- * - Feedback
- * - Mix
- *
- * **Outputs**
- *
- * - Delayed audio
- *
- * **Parameters**
- *
- * - delayTime The delay time in seconds. Linked to input 1.
- * - feedback The amount of feedback. Linked to input 2.
- * - mix The amount of delay to mix into the dry signal. Linked to input 3.
- *
- * @constructor
- * @extends AudioletNode
- * @param {Audiolet} audiolet The audiolet object.
- * @param {Number} maximumDelayTime The largest allowable delay time.
- * @param {Number} delayTime The initial delay time.
- * @param {Number} feedabck The initial feedback amount.
- * @param {Number} mix The initial mix amount.
- */
-var FeedbackDelay = function(audiolet, maximumDelayTime, delayTime, feedback,
- mix) {
- AudioletNode.call(this, audiolet, 4, 1);
- this.linkNumberOfOutputChannels(0, 0);
- this.maximumDelayTime = maximumDelayTime;
- this.delayTime = new AudioletParameter(this, 1, delayTime || 1);
- this.feedback = new AudioletParameter(this, 2, feedback || 0.5);
- this.mix = new AudioletParameter(this, 3, mix || 1);
- var bufferSize = maximumDelayTime * this.audiolet.device.sampleRate;
- this.buffers = [];
- this.readWriteIndex = 0;
-};
-extend(FeedbackDelay, AudioletNode);
-
-/**
- * Process samples
- */
-FeedbackDelay.prototype.generate = function() {
- var input = this.inputs[0];
- var output = this.outputs[0];
-
- var sampleRate = this.audiolet.output.device.sampleRate;
-
- var delayTime = this.delayTime.getValue() * sampleRate;
- var feedback = this.feedback.getValue();
- var mix = this.mix.getValue();
-
- var numberOfChannels = input.samples.length;
- var numberOfBuffers = this.buffers.length;
- for (var i = 0; i < numberOfChannels; i++) {
- if (i >= numberOfBuffers) {
- // Create buffer for channel if it doesn't already exist
- var bufferSize = this.maximumDelayTime * sampleRate;
- this.buffers.push(new Float32Array(bufferSize));
- }
-
- var buffer = this.buffers[i];
-
- var inputSample = input.samples[i];
- var bufferSample = buffer[this.readWriteIndex];
-
- output.samples[i] = mix * bufferSample + (1 - mix) * inputSample;
- buffer[this.readWriteIndex] = inputSample + feedback * bufferSample;
- }
-
- this.readWriteIndex += 1;
- if (this.readWriteIndex >= delayTime) {
- this.readWriteIndex = 0;
- }
-};
-
-/**
- * toString
- *
- * @return {String} String representation.
- */
-FeedbackDelay.prototype.toString = function() {
- return 'Feedback Delay';
-};
-
-/*!
- * @depends ../core/AudioletNode.js
- */
-
-/**
* Fast Fourier Transform
*
* **Inputs**
@@ -3161,6 +3069,98 @@ FFT.prototype.toString = function() {
* @depends ../core/AudioletNode.js
*/
+/**
+ * Delay line with feedback
+ *
+ * **Inputs**
+ *
+ * - Audio
+ * - Delay Time
+ * - Feedback
+ * - Mix
+ *
+ * **Outputs**
+ *
+ * - Delayed audio
+ *
+ * **Parameters**
+ *
+ * - delayTime The delay time in seconds. Linked to input 1.
+ * - feedback The amount of feedback. Linked to input 2.
+ * - mix The amount of delay to mix into the dry signal. Linked to input 3.
+ *
+ * @constructor
+ * @extends AudioletNode
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {Number} maximumDelayTime The largest allowable delay time.
+ * @param {Number} delayTime The initial delay time.
+ * @param {Number} feedabck The initial feedback amount.
+ * @param {Number} mix The initial mix amount.
+ */
+var FeedbackDelay = function(audiolet, maximumDelayTime, delayTime, feedback,
+ mix) {
+ AudioletNode.call(this, audiolet, 4, 1);
+ this.linkNumberOfOutputChannels(0, 0);
+ this.maximumDelayTime = maximumDelayTime;
+ this.delayTime = new AudioletParameter(this, 1, delayTime || 1);
+ this.feedback = new AudioletParameter(this, 2, feedback || 0.5);
+ this.mix = new AudioletParameter(this, 3, mix || 1);
+ var bufferSize = maximumDelayTime * this.audiolet.device.sampleRate;
+ this.buffers = [];
+ this.readWriteIndex = 0;
+};
+extend(FeedbackDelay, AudioletNode);
+
+/**
+ * Process samples
+ */
+FeedbackDelay.prototype.generate = function() {
+ var input = this.inputs[0];
+ var output = this.outputs[0];
+
+ var sampleRate = this.audiolet.output.device.sampleRate;
+
+ var delayTime = this.delayTime.getValue() * sampleRate;
+ var feedback = this.feedback.getValue();
+ var mix = this.mix.getValue();
+
+ var numberOfChannels = input.samples.length;
+ var numberOfBuffers = this.buffers.length;
+ for (var i = 0; i < numberOfChannels; i++) {
+ if (i >= numberOfBuffers) {
+ // Create buffer for channel if it doesn't already exist
+ var bufferSize = this.maximumDelayTime * sampleRate;
+ this.buffers.push(new Float32Array(bufferSize));
+ }
+
+ var buffer = this.buffers[i];
+
+ var inputSample = input.samples[i];
+ var bufferSample = buffer[this.readWriteIndex];
+
+ output.samples[i] = mix * bufferSample + (1 - mix) * inputSample;
+ buffer[this.readWriteIndex] = inputSample + feedback * bufferSample;
+ }
+
+ this.readWriteIndex += 1;
+ if (this.readWriteIndex >= delayTime) {
+ this.readWriteIndex = 0;
+ }
+};
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+FeedbackDelay.prototype.toString = function() {
+ return 'Feedback Delay';
+};
+
+/*!
+ * @depends ../core/AudioletNode.js
+ */
+
/*
* Multiply values
*
@@ -5634,6 +5634,322 @@ var EqualTemperamentTuning = function(pitchesPerOctave) {
};
extend(EqualTemperamentTuning, Tuning);
+function AudioFileRequest(url, async) {
+ this.url = url;
+ if (typeof async == 'undefined' || async == null) {
+ async = true;
+ }
+ this.async = async;
+ var splitURL = url.split('.');
+ this.extension = splitURL[splitURL.length - 1].toLowerCase();
+}
+
+AudioFileRequest.prototype.onSuccess = function(decoded) {
+};
+
+AudioFileRequest.prototype.onFailure = function(decoded) {
+};
+
+
+AudioFileRequest.prototype.send = function() {
+ if (this.extension != 'wav' &&
+ this.extension != 'aiff' &&
+ this.extension != 'aif') {
+ this.onFailure();
+ return;
+ }
+
+ var request = new XMLHttpRequest();
+ request.open('GET', this.url, this.async);
+ request.overrideMimeType('text/plain; charset=x-user-defined');
+ request.onreadystatechange = function(event) {
+ if (request.readyState == 4) {
+ if (request.status == 200 || request.status == 0) {
+ this.handleResponse(request.responseText);
+ }
+ else {
+ this.onFailure();
+ }
+ }
+ }.bind(this);
+ request.send(null);
+};
+
+AudioFileRequest.prototype.handleResponse = function(data) {
+ var decoder, decoded;
+ if (this.extension == 'wav') {
+ decoder = new WAVDecoder();
+ decoded = decoder.decode(data);
+ }
+ else if (this.extension == 'aiff' || this.extension == 'aif') {
+ decoder = new AIFFDecoder();
+ decoded = decoder.decode(data);
+ }
+ this.onSuccess(decoded);
+};
+
+
+function Decoder() {
+}
+
+Decoder.prototype.readString = function(data, offset, length) {
+ return data.slice(offset, offset + length);
+};
+
+Decoder.prototype.readIntL = function(data, offset, length) {
+ var value = 0;
+ for (var i = 0; i < length; i++) {
+ value = value + ((data.charCodeAt(offset + i) & 0xFF) *
+ Math.pow(2, 8 * i));
+ }
+ return value;
+};
+
+Decoder.prototype.readChunkHeaderL = function(data, offset) {
+ var chunk = {};
+ chunk.name = this.readString(data, offset, 4);
+ chunk.length = this.readIntL(data, offset + 4, 4);
+ return chunk;
+};
+
+Decoder.prototype.readIntB = function(data, offset, length) {
+ var value = 0;
+ for (var i = 0; i < length; i++) {
+ value = value + ((data.charCodeAt(offset + i) & 0xFF) *
+ Math.pow(2, 8 * (length - i - 1)));
+ }
+ return value;
+};
+
+Decoder.prototype.readChunkHeaderB = function(data, offset) {
+ var chunk = {};
+ chunk.name = this.readString(data, offset, 4);
+ chunk.length = this.readIntB(data, offset + 4, 4);
+ return chunk;
+};
+
+Decoder.prototype.readFloatB = function(data, offset) {
+ var expon = this.readIntB(data, offset, 2);
+ var range = 1 << 16 - 1;
+ if (expon >= range) {
+ expon |= ~(range - 1);
+ }
+
+ var sign = 1;
+ if (expon < 0) {
+ sign = -1;
+ expon += range;
+ }
+
+ var himant = this.readIntB(data, offset + 2, 4);
+ var lomant = this.readIntB(data, offset + 6, 4);
+ var value;
+ if (expon == himant == lomant == 0) {
+ value = 0;
+ }
+ else if (expon == 0x7FFF) {
+ value = Number.MAX_VALUE;
+ }
+ else {
+ expon -= 16383;
+ value = (himant * 0x100000000 + lomant) * Math.pow(2, expon - 63);
+ }
+ return sign * value;
+};
+
+function WAVDecoder(data) {
+}
+
+WAVDecoder.prototype.__proto__ = Decoder.prototype;
+
+WAVDecoder.prototype.decode = function(data) {
+ var decoded = {};
+ var offset = 0;
+ // Header
+ var chunk = this.readChunkHeaderL(data, offset);
+ offset += 8;
+ if (chunk.name != 'RIFF') {
+ console.error('File is not a WAV');
+ return null;
+ }
+
+ var fileLength = chunk.length;
+ fileLength += 8;
+
+ var wave = this.readString(data, offset, 4);
+ offset += 4;
+ if (wave != 'WAVE') {
+ console.error('File is not a WAV');
+ return null;
+ }
+
+ while (offset < fileLength) {
+ var chunk = this.readChunkHeaderL(data, offset);
+ offset += 8;
+ if (chunk.name == 'fmt ') {
+ // File encoding
+ var encoding = this.readIntL(data, offset, 2);
+ offset += 2;
+
+ if (encoding != 0x0001) {
+ // Only support PCM
+ console.error('Cannot decode non-PCM encoded WAV file');
+ return null;
+ }
+
+ // Number of channels
+ var numberOfChannels = this.readIntL(data, offset, 2);
+ offset += 2;
+
+ // Sample rate
+ var sampleRate = this.readIntL(data, offset, 4);
+ offset += 4;
+
+ // Ignore bytes/sec - 4 bytes
+ offset += 4;
+
+ // Ignore block align - 2 bytes
+ offset += 2;
+
+ // Bit depth
+ var bitDepth = this.readIntL(data, offset, 2);
+ var bytesPerSample = bitDepth / 8;
+ offset += 2;
+ }
+
+ else if (chunk.name == 'data') {
+ // Data must come after fmt, so we are okay to use it's variables
+ // here
+ var length = chunk.length / (bytesPerSample * numberOfChannels);
+ var channels = [];
+ for (var i = 0; i < numberOfChannels; i++) {
+ channels.push(new Float32Array(length));
+ }
+
+ for (var i = 0; i < numberOfChannels; i++) {
+ var channel = channels[i];
+ for (var j = 0; j < length; j++) {
+ var index = offset;
+ index += (j * numberOfChannels + i) * bytesPerSample;
+ // Sample
+ var value = this.readIntL(data, index, bytesPerSample);
+ // Scale range from 0 to 2**bitDepth -> -2**(bitDepth-1) to
+ // 2**(bitDepth-1)
+ var range = 1 << bitDepth - 1;
+ if (value >= range) {
+ value |= ~(range - 1);
+ }
+ // Scale range to -1 to 1
+ channel[j] = value / range;
+ }
+ }
+ offset += chunk.length;
+ }
+ else {
+ offset += chunk.length;
+ }
+ }
+ decoded.sampleRate = sampleRate;
+ decoded.bitDepth = bitDepth;
+ decoded.channels = channels;
+ decoded.length = length;
+ return decoded;
+};
+
+
+function AIFFDecoder() {
+}
+
+AIFFDecoder.prototype.__proto__ = Decoder.prototype;
+
+AIFFDecoder.prototype.decode = function(data) {
+ var decoded = {};
+ var offset = 0;
+ // Header
+ var chunk = this.readChunkHeaderB(data, offset);
+ offset += 8;
+ if (chunk.name != 'FORM') {
+ console.error('File is not an AIFF');
+ return null;
+ }
+
+ var fileLength = chunk.length;
+ fileLength += 8;
+
+ var aiff = this.readString(data, offset, 4);
+ offset += 4;
+ if (aiff != 'AIFF') {
+ console.error('File is not an AIFF');
+ return null;
+ }
+
+ while (offset < fileLength) {
+ var chunk = this.readChunkHeaderB(data, offset);
+ offset += 8;
+ if (chunk.name == 'COMM') {
+ // Number of channels
+ var numberOfChannels = this.readIntB(data, offset, 2);
+ offset += 2;
+
+ // Number of samples
+ var length = this.readIntB(data, offset, 4);
+ offset += 4;
+
+ var channels = [];
+ for (var i = 0; i < numberOfChannels; i++) {
+ channels.push(new Float32Array(length));
+ }
+
+ // Bit depth
+ var bitDepth = this.readIntB(data, offset, 2);
+ var bytesPerSample = bitDepth / 8;
+ offset += 2;
+
+ // Sample rate
+ var sampleRate = this.readFloatB(data, offset);
+ offset += 10;
+ }
+ else if (chunk.name == 'SSND') {
+ // Data offset
+ var dataOffset = this.readIntB(data, offset, 4);
+ offset += 4;
+
+ // Ignore block size
+ offset += 4;
+
+ // Skip over data offset
+ offset += dataOffset;
+
+ for (var i = 0; i < numberOfChannels; i++) {
+ var channel = channels[i];
+ for (var j = 0; j < length; j++) {
+ var index = offset;
+ index += (j * numberOfChannels + i) * bytesPerSample;
+ // Sample
+ var value = this.readIntB(data, index, bytesPerSample);
+ // Scale range from 0 to 2**bitDepth -> -2**(bitDepth-1) to
+ // 2**(bitDepth-1)
+ var range = 1 << bitDepth - 1;
+ if (value >= range) {
+ value |= ~(range - 1);
+ }
+ // Scale range to -1 to 1
+ channel[j] = value / range;
+ }
+ }
+ offset += chunk.length - dataOffset - 8;
+ }
+ else {
+ offset += chunk.length;
+ }
+ }
+ decoded.sampleRate = sampleRate;
+ decoded.bitDepth = bitDepth;
+ decoded.channels = channels;
+ decoded.length = length;
+ return decoded;
+};
+
var Sink = this.Sink = function (global) {
/**
@@ -6431,7 +6747,7 @@ sinks('webaudio', function (readFn, channelCount, bufferSize, sampleRate) {
soundData = null,
zeroBuffer = null;
self.start.apply(self, arguments);
- node = context.createJavaScriptNode(self.bufferSize, 0, self.channelCount);
+ node = context.createJavaScriptNode(self.bufferSize, self.channelCount, self.channelCount);
function bufferFill(e) {
var outputBuffer = e.outputBuffer,