Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
/*
Copyright notice for the base64 to arraybuffer conversion algorithm.
Copyright (c) 2011, Daniel Guerrero
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL DANIEL GUERRERO BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// might adopt some strange functions from Module?
// var maximJs = Module;
......@@ -25,9 +49,28 @@ maximJs.maxiDistortion = Module.maxiDistortion;
maximJs.maxiFlanger = Module.maxiFlanger;
maximJs.maxiChorus = Module.maxiChorus;
maximJs.maxiDCBlocker = Module.maxiDCBlocker;
maximJs.maxiSVF = Module.maxiSVF;
maximJs.maxiEnvelopeFollower = Module.maxiEnvelopeFollower;
maximJs.maxiKick = Module.maxiKick;
maximJs.maxiSnare = Module.maxiSnare;
maximJs.maxiHats = Module.maxiHats;
maximJs.maxiClock = Module.maxiClock;
// LIBS
// FFT-ish
maximJs.maxiFFT = Module.maxiFFT;
maximJs.maxiIFFT = Module.maxiIFFT;
maximJs.maxiMFCC = Module.maxiMFCC;
maximJs.maxiFFTOctaveAnalyzer = Module.maxiFFTOctaveAnalyzer;
// Grains
maximJs.maxiTimestretch = Module.maxiTimestretch;
maximJs.maxiPitchShift = Module.maxiPitchShift;
maximJs.maxiPitchStretch = Module.maxiPitchStretch;
// ------------------------------------------------
// maxiArray - could extend Array object?
// cheaty array method to avoid mixing vector terminology with arrays
......@@ -52,7 +95,7 @@ maximJs.maxiArray = function maxiArray(){
// // reset length var
// this.length = this.vec.size();
// }
// }
// };
};
......@@ -106,160 +149,212 @@ maximJs.maxiArray.prototype.clear = function(useSq){
// tools
maximJs.maximTools = function(){};
maximJs.maxiTools = function(){};
maximJs.maximTools.prototype.ArrayOfObj = function(obj, num){
var array = [];
// not sure this is good
// maximJs.maxiTools.arrayOfObj = function(obj, num){
// var array = [];
for(var i = 0; i < num; i++){
array.push(new obj());
}
return array;
};
// for(var i = 0; i < num; i++){
// array.push(new obj());
// }
// return array;
// };
function GetArrayAsVectorDbl(arrayIn){
maximJs.maxiTools.getArrayAsVectorDbl = function(arrayIn){
var vecOut = new Module.VectorDouble();
for(var i = 0; i < arrayIn.length; i++){
vecOut.push_back(arrayIn[i]);
}
return vecOut;
};
maximJs.maxiTools.getBase64 = function(str) {
//check if the string is a data URI
if (str.indexOf(';base64,') != -1 ) {
//see where the actual data begins
var dataStart = str.indexOf(';base64,') + 8;
//check if the data is base64-encoded, if yes, return it
// taken from
// http://stackoverflow.com/a/8571649
return str.slice(dataStart).match(/^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/) ? str.slice(dataStart) : false;
}
else return false;
}
maximJs.maxiTools._keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
maximJs.maxiTools.removePaddingFromBase64 = function(input) {
var lkey = maximJs.maxiTools._keyStr.indexOf(input.charAt(input.length - 1));
if(lkey == 64){
return input.substring(0,input.length - 1);
}
return input;
}
// ------------------------------------------------
// would be nice to put this (all below) in function/class of some sort
// Temporary patch until all browsers support unprefixed context.
// AudioContext = AudioContext || webkitAudioContext;
// var context;
// var context = new AudioContext();
// var context = new webkitAudioContext() || new AudioContext();
var context = new (window.AudioContext || window.webkitAudioContext)();
var source;
var jsProcessor = 0;
var analyser;
var output = 0;
var bufferLoader;
var myBufferData = new Module.VectorDouble();//this data will be used to make an envelope. Value and time to value in ms.
var sampleIsLoaded = false;
maximJs.maxiAudio = function() {
this.numChannels = 2;
this.output = 0;
this.context = null;
this.source = null;
this.analyser = null;
this.jsProcessor = null;
this.bufferSize = 1024;
this.initDone = false;
};
// var vectorTools = new Module.vectorTools();
// init() once the page has finished loading.
window.onload = init;
maximJs.maxiAudio.play = function(){};
function setup(){
// don't really need setup??
maximJs.maxiAudio.setup = function(){
console.log("non-overrided setup");
// setNumChannels(2);
}
};
// this will be overriden in the users script
function play(){
// output = 0;
// console.log("non-overrided play happening");
}
maximJs.maxiAudio.prototype.getNumChannels = function(){
return this.numChannels;
};
// isArray should be second param really
// set num channels and set output as an array
// use this if you want to change number of channels
function setNumChannels(isArray, numChannelsOut_){
maximJs.maxiAudio.prototype.setNumChannels = function(isArray, numChannels_){
if(isArray){
OutputIsArray(true, numChannelsOut_);
} else {
OutputIsArray(false, numChannelsOut_);
}
source.disconnect();
jsProcessor.disconnect();
analyser.disconnect();
this.numChannels = numChannels_;
this.outputIsArray(isArray, numChannels_);
initAudio(numChannelsOut_);
this.resetAudio();
};
}
maximJs.maxiAudio.prototype.setBufferSize = function(newBufferSize) {
this.bufferSize = newBufferSize;
this.resetAudio();
};
// use this if you want to keep num of outputs but change
// method e.g. array or not
function OutputIsArray(isArray, numChannelsOut_){
maximJs.maxiAudio.prototype.outputIsArray = function(isArray){
if(isArray){
output = new Array(numChannelsOut_);
this.output = new Array(this.numChannels);
for(var i = 0; i < numChannelsOut_; i++){
output[i] = 0;
for(var i = 0; i < this.numChannels; i++){
this.output[i] = 0;
}
} else {
output = 0;
this.output = 0;
}
}
};
maximJs.maxiAudio.prototype.init = function() {
// Temporary patch until all browsers support unprefixed context.
this.context = new (window.AudioContext || window.webkitAudioContext)();
var outputErrorLogged = false;
// function handling audio processing
// called byjsProcessor
function process(event) {
var numChannels = event.outputBuffer.numberOfChannels;
var outputLength = event.outputBuffer.getChannelData(0).length;
this.source = this.context.createBufferSource();
this.jsProcessor = this.context.createScriptProcessor(this.bufferSize, this.numChannels, this.numChannels);
// var process = this.process;
for (var i = 0; i < outputLength; ++i) {
play();
this.jsProcessor.onaudioprocess = function(event) {
var numChannels = event.outputBuffer.numberOfChannels;
var outputLength = event.outputBuffer.getChannelData(0).length;
// console.log("n");
for (var i = 0; i < outputLength; ++i) {
this.play();
if(output instanceof Array){
if(this.output instanceof Array){
for (var channel = 0; channel < numChannels; channel++) {
event.outputBuffer.getChannelData(channel)[i] = output[channel];
event.outputBuffer.getChannelData(channel)[i] = this.output[channel];
}
}
else
{
for (var channel = 0; channel < numChannels; channel++) {
event.outputBuffer.getChannelData(channel)[i] = output;
event.outputBuffer.getChannelData(channel)[i] = this.output;
}
}
}
}
.bind(this)
;
this.analyser = this.context.createAnalyser();
this.analyser.fftSize = 2048;
// Connect the processing graph: source -> jsProcessor -> analyser -> destination
this.source.connect(this.jsProcessor);
this.jsProcessor.connect(this.analyser);
this.analyser.connect(this.context.destination);
this.initDone = true;
};
maximJs.maxiAudio.prototype.resetAudio = function(){
if(this.initDone){
this.source.disconnect();
this.jsProcessor.disconnect();
this.analyser.disconnect();
}
}
}
// var myArrayBuffer = audioCtx.createBuffer(2, 4096, context.sampleRate);
this.init();
};
function initAudio(numChannels) {
source = context.createBufferSource();
jsProcessor = context.createScriptProcessor(4096, numChannels, numChannels);
jsProcessor.onaudioprocess = process;
// option to load sample if a different context is used
maximJs.maxiAudio.prototype.loadSample = function(url, samplePlayer, contextIn) {
var data = [];
var context;
analyser = context.createAnalyser();
analyser.fftSize = 2048;
if(!contextIn){
context = this.context;
} else {
context = contextIn;
}
// // Connect the processing graph: source -> jsProcessor -> analyser -> destination
source.connect(jsProcessor);
jsProcessor.connect(analyser);
analyser.connect(context.destination);
}
samplePlayer.clear();
//check if url is actually a base64-encoded string
var b64 = maximJs.maxiTools.getBase64(url);
if (b64) {
//convert to arraybuffer
//modified version of this:
// https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js
var ab_bytes = (b64.length/4) * 3;
var arrayBuffer = new ArrayBuffer(ab_bytes);
function init() {
initAudio(2);
setup();
}
b64 = maximJs.maxiTools.removePaddingFromBase64(maximJs.maxiTools.removePaddingFromBase64(b64));
function loadSample(url, samplePlayer) {
var data = [];
var bytes = parseInt((b64.length / 4) * 3, 10);
samplePlayer.clear();
var uarray;
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
var j = 0;
sampleIsLoaded = false;
// Load asynchronously
var request = new XMLHttpRequest();
request.addEventListener("load", transferComplete);
request.open("GET", url, true);
uarray = new Uint8Array(arrayBuffer);
request.responseType = "arraybuffer";
b64 = b64.replace(/[^A-Za-z0-9\+\/\=]/g, "");
for (i=0; i<bytes; i+=3) {
//get the 3 octects in 4 ascii chars
enc1 = maximJs.maxiTools._keyStr.indexOf(b64.charAt(j++));
enc2 = maximJs.maxiTools._keyStr.indexOf(b64.charAt(j++));
enc3 = maximJs.maxiTools._keyStr.indexOf(b64.charAt(j++));
enc4 = maximJs.maxiTools._keyStr.indexOf(b64.charAt(j++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
uarray[i] = chr1;
if (enc3 != 64) uarray[i+1] = chr2;
if (enc4 != 64) uarray[i+2] = chr3;
}
request.onload = function() {
context.decodeAudioData(
request.response,
arrayBuffer,
function(buffer) {
// source.buffer = buffer;
// source.loop = true;
......@@ -267,14 +362,14 @@ function loadSample(url, samplePlayer) {
data = buffer.getChannelData(0);
if(data){
Module.vectorTools.clearVectorDbl(myBufferData);
var myBufferData = new Module.VectorDouble();
// Module.vectorTools.clearVectorDbl(myBufferData);
for(var n = 0; n < data.length; n++){
myBufferData.push_back(data[n]);
}
samplePlayer.setSample(myBufferData);
sampleIsLoaded = true;
samplePlayer.setSample(myBufferData/*, context.sampleRate*/);
}
},
......@@ -282,13 +377,50 @@ function loadSample(url, samplePlayer) {
function(buffer) {
console.log("Error decoding source!");
}
);
};
);
request.send();
}
}
else {
// Load asynchronously
var request = new XMLHttpRequest();
request.addEventListener("load",
function (evt) {
console.log("The transfer is complete.");
});
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = function() {
context.decodeAudioData(
request.response,
function(buffer) {
// source.buffer = buffer;
// source.loop = true;
// source.start(0);
data = buffer.getChannelData(0);
if(data){
var myBufferData = new Module.VectorDouble();
// Module.vectorTools.clearVectorDbl(myBufferData);
for(var n = 0; n < data.length; n++){
myBufferData.push_back(data[n]);
}
samplePlayer.setSample(myBufferData/*, context.sampleRate*/);
}
function transferComplete(evt) {
console.log("The transfer is complete.");
}
},
function(buffer) {
console.log("Error decoding source!");
}
);
};
request.send();
}
};
// might adopt some strange functions from Module?
// var maximJs = Module;
// alternatively...
// probably better this way
// no inheritance of Module stuff...
var maximJs = {};
maximJs.maxiSettings = Module.maxiSettings;
maximJs.maxiOsc = Module.maxiOsc;
maximJs.maxiEnvelope = Module.maxiEnvelope;
maximJs.maxiDelayline = Module.maxiDelayline;
maximJs.maxiFilter = Module.maxiFilter;
maximJs.maxiMix = Module.maxiMix;
maximJs.maxiLagExp = Module.maxiLagExp;
maximJs.maxiSample = Module.maxiSample;
maximJs.maxiMap = Module.maxiMap;
maximJs.maxiDyn = Module.maxiDyn;
maximJs.maxiEnv = Module.maxiEnv;
maximJs.convert = Module.convert;
maximJs.maxiDistortion = Module.maxiDistortion;
maximJs.maxiFlanger = Module.maxiFlanger;
maximJs.maxiChorus = Module.maxiChorus;
maximJs.maxiDCBlocker = Module.maxiDCBlocker;
maximJs.maxiSVF = Module.maxiSVF;
maximJs.maxiEnvelopeFollower = Module.maxiEnvelopeFollower;
maximJs.maxiKick = Module.maxiKick;
maximJs.maxiSnare = Module.maxiSnare;
maximJs.maxiHats = Module.maxiHats;
maximJs.maxiClock = Module.maxiClock;
// LIBS
// FFT-ish
maximJs.maxiFFT = Module.maxiFFT;
maximJs.maxiIFFT = Module.maxiIFFT;
maximJs.maxiMFCC = Module.maxiMFCC;
maximJs.maxiFFTOctaveAnalyzer = Module.maxiFFTOctaveAnalyzer;
// Grains
maximJs.maxiTimestretch = Module.maxiTimestretch;
maximJs.maxiPitchShift = Module.maxiPitchShift;
maximJs.maxiPitchStretch = Module.maxiPitchStretch;
// ------------------------------------------------
// maxiArray - could extend Array object?
// cheaty array method to avoid mixing vector terminology with arrays
// but have to copy everything?!
// better to use GetArrayAsVectorDbl function ???
maximJs.maxiArray = function maxiArray(){
this.length = 0;
var vec = new Module.VectorDouble();
// this.update = function(){
// var lengthsMatch = this.length !== this.vec.size();
// if(!lengthsMatch){
// if(this.length < this.vec.size()){
// for(var i = this.length; i < this.vec.size(); i++){
// this[i] = this.vec.get(i);
// }
// } else{
// for(var i = this.length; i < this.vec.size(); i++){
// delete this[i];
// }
// }
// // reset length var
// this.length = this.vec.size();
// }
// };
};
maximJs.maxiArray.prototype.asVector = function(arrayIn){
return this.vec;
};
maximJs.maxiArray.prototype.asJsArray = function(arrayIn){
var arrayOut = [];
for(var i = 0; i < this.length; i++){
array.push(this.vec.get(i));
}
return arrayOut;
};
maximJs.maxiArray.prototype.set = function(arrayIn){
this.clear();
this.vec = GetArrayAsVectorDbl(arrayIn);
this.length = this.vec.size();
this.SetSqBrackets(true);
};
maximJs.maxiArray.prototype.push = function(num){
this.vec.push_back(num);
this[this.length] = num;
this.length++;
};
// set object properties to mimic array
// this doesn't seem particularly efficient or smart
maximJs.maxiArray.prototype.SetSqBrackets = function(useSq){
for(var i = 0; i < this.length; i++){
if(useSq){
this[i] = this.vec.get(i);
} else{
delete this[i];
}
}
};
maximJs.maxiArray.prototype.clear = function(useSq){
for(var i = 0; i < this.length; i++){
delete this[i];
}
Module.vectorTools.clearVectorDbl(this.vec);
this.length = 0;
};
// tools
maximJs.maxiTools = function(){};
maximJs.maxiTools.arrayOfObj = function(obj, num){
var array = [];
for(var i = 0; i < num; i++){
array.push(new obj());
}
return array;
};
maximJs.maxiTools.getArrayAsVectorDbl = function(arrayIn){
var vecOut = new Module.VectorDouble();
for(var i = 0; i < arrayIn.length; i++){
vecOut.push_back(arrayIn[i]);
}
return vecOut;
};
// ------------------------------------------------
maximJs.maxiAudio = (function() {
var numChannels = 2;
var output = 0;
this.context = null;
this.source = null;
this.analyser = null;
this.jsProcessor = null;
var initDone = false;
this.play = function(){};
// don't really need setup??
this.setup = function(){
console.log("non-overrided setup");
};
this.getNumChannels = function(){
return numChannels;
};
// isArray should be second param really
// set num channels and set output as an array
// use this if you want to change number of channels
this.setNumChannels = function(isArray, numChannels_){
numChannels = numChannels_;
this.outputIsArray(isArray, numChannels_);
this.resetAudio();
};
// use this if you want to keep num of outputs but change
// method e.g. array or not
this.outputIsArray = function(isArray){
if(isArray){
maximJs.maxiAudio.output = new Array(numChannels);
for(var i = 0; i < numChannels; i++){
maximJs.maxiAudio.output[i] = 0;
}
} else {
maximJs.maxiAudio.output = 0;
}
};
// function handling audio processing
// called byjsProcessor
var process = function(event) {
var numChannels = event.outputBuffer.numberOfChannels;
var outputLength = event.outputBuffer.getChannelData(0).length;
for (var i = 0; i < outputLength; ++i) {
maximJs.maxiAudio.play();
if(maximJs.maxiAudio.output instanceof Array){
for (var channel = 0; channel < numChannels; channel++) {
event.outputBuffer.getChannelData(channel)[i] = maximJs.maxiAudio.output[channel];
}
}
else
{
for (var channel = 0; channel < numChannels; channel++) {
event.outputBuffer.getChannelData(channel)[i] = maximJs.maxiAudio.output;
}
}
}
};
this.resetAudio = function(){
if(initDone){
this.source.disconnect();
this.jsProcessor.disconnect();
this.analyser.disconnect();
}
this.init();
};
this.init = function() {
// Temporary patch until all browsers support unprefixed context.
this.context = new (window.AudioContext || window.webkitAudioContext)();
this.source = this.context.createBufferSource();
this.jsProcessor = this.context.createScriptProcessor(4096, numChannels, numChannels);
this.jsProcessor.onaudioprocess = process;
this.analyser = this.context.createAnalyser();
this.analyser.fftSize = 2048;
// Connect the processing graph: source -> jsProcessor -> analyser -> destination
this.source.connect(this.jsProcessor);
this.jsProcessor.connect(this.analyser);
this.analyser.connect(this.context.destination);
initDone = true;
};
// option to load sample if a different context is used
this.loadSample = function(url, samplePlayer, contextIn) {
var data = [];
var context;
if(!contextIn){
context = this.context;
} else {
context = contextIn;
}
samplePlayer.clear();
// Load asynchronously
var request = new XMLHttpRequest();
request.addEventListener("load",
function (evt) {
console.log("The transfer is complete.");
});
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = function() {
context.decodeAudioData(
request.response,
function(buffer) {
// source.buffer = buffer;
// source.loop = true;
// source.start(0);
data = buffer.getChannelData(0);
if(data){
var myBufferData = new Module.VectorDouble();
// Module.vectorTools.clearVectorDbl(myBufferData);
for(var n = 0; n < data.length; n++){
myBufferData.push_back(data[n]);
}
samplePlayer.setSample(myBufferData/*, context.sampleRate*/);
}
},
function(buffer) {
console.log("Error decoding source!");
}
);
};
request.send();
};
return this;
}());
\ No newline at end of file