webdaw-modules
Version:
a set of modules for building a web-based DAW
288 lines (244 loc) • 10.7 kB
JavaScript
function audioRecorderWorker() {
'use strict';
function createWorker() {
var
data,
bufferIndexStart,
bufferIndexEnd,
planarSamples,
interleavedSamples,
numFrames,
recBuffersLeft,
recBuffersRight,
sampleRate,
numberOfChannels;
self.onmessage = function (e) {
switch (e.data.command) {
case 'init':
sampleRate = e.data.sampleRate;
numFrames = 0;
recBuffersLeft = [];
recBuffersRight = [];
break;
case 'record_mono':
numberOfChannels = 1;
recBuffersLeft.push(e.data.buffer);
numFrames += e.data.buffer.length;
break;
case 'record_stereo':
numberOfChannels = 2;
recBuffersLeft.push(e.data.buffer[0]);
recBuffersRight.push(e.data.buffer[1]);
numFrames += e.data.buffer[0].length;
break;
case 'get_wavfile':
bufferIndexStart = e.data.bufferIndexStart;
bufferIndexEnd = e.data.bufferIndexEnd;
data = {
id: 'new',
wavArrayBuffer: getWavFile(),
interleavedSamples: interleavedSamples
};
self.postMessage(data, [data.wavArrayBuffer, data.interleavedSamples.buffer]);
/*
// funny: this is something different
data = {
id: 'new',
wavArrayBuffer: getWavFile(),
interleavedSamples: interleavedSamples.buffer
};
self.postMessage(data, [data.wavArrayBuffer, data.interleavedSamples]);
*/
break;
case 'get_wavfile2':
bufferIndexStart = e.data.bufferIndexStart;
bufferIndexEnd = e.data.bufferIndexEnd;
data = getWavFile2();
data.id = 'new';
self.postMessage(data, [data.planarSamples, data.interleavedSamples, data.wavArrayBuffer]);
break;
case 'update_wavfile':
bufferIndexStart = e.data.bufferIndexStart;
bufferIndexEnd = e.data.bufferIndexEnd;
//interleavedSamples = new Float32Array(e.data.samples);
interleavedSamples = e.data.samples;
data = {
id: 'update',
wavArrayBuffer: updateWavFile()
};
self.postMessage(data, [data.wavArrayBuffer]);
break;
}
};
function getWavFile() {
var dataview, i, index = 0, result;
if (numberOfChannels === 1) {
interleavedSamples = mergeBuffers(recBuffersLeft, numFrames);
} else if (numberOfChannels === 2) {
interleavedSamples = toInterleavedBuffer(
mergeBuffers(recBuffersLeft, numFrames),
mergeBuffers(recBuffersRight, numFrames)
);
}
//console.log('1:' + interleavedSamples.length);
if (bufferIndexEnd > 0 || bufferIndexStart > 0) {
if (bufferIndexEnd === -1) {
bufferIndexEnd = interleavedSamples.length;
}
result = new Float32Array(bufferIndexEnd - bufferIndexStart + 1);
for (i = bufferIndexStart; i < bufferIndexEnd; i++) {
result[index++] = interleavedSamples[i];
}
interleavedSamples = result;
}
//console.log('2:' + interleavedSamples.length);
dataview = encodeWAV(interleavedSamples);
return dataview.buffer;
}
function updateWavFile() {
var dataview, i, result, index = 0;
//console.log(bufferIndexStart + ':' + interleavedSamples);
if (bufferIndexEnd === -1) {
bufferIndexEnd = interleavedSamples.length;
}
result = new Float32Array(bufferIndexEnd - bufferIndexStart + 1);
for (i = bufferIndexStart; i < bufferIndexEnd; i++) {
result[index++] = interleavedSamples[i];
}
dataview = encodeWAV(result);
return dataview.buffer;
}
function mergeBuffers(recBuffers, numFrames) {
var result = new Float32Array(numFrames);
var offset = 0;
for (var i = 0, maxi = recBuffers.length; i < maxi; i++) {
result.set(recBuffers[i], offset);
offset += recBuffers[i].length;
}
return result;
}
function toInterleavedBuffer(inputL, inputR) {
var length = inputL.length + inputR.length,
result = new Float32Array(length),
index = 0,
inputIndex = 0;
while (index < length) {
result[index++] = inputL[inputIndex];
result[index++] = inputR[inputIndex];
inputIndex++;
}
return result;
}
function toPlanarBuffer(inputL, inputR) {
var length = inputL.length,
result = new Float32Array(length * 2),
index = 0,
inputIndex = 0;
while (index < length) {
result[index++] = inputL[inputIndex++];
}
index = 0;
while (index < length) {
result[index++] = inputR[inputIndex++];
}
return result;
}
function floatTo16BitPCM(output, offset, input) {
for (var i = 0; i < input.length; i++ , offset += 2) {
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
function writeString(view, offset, string) {
for (var i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
// see: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
// samples is a Float32Array
function encodeWAV(samples) {
var bitsPerSample = 16,
bytesPerSample = bitsPerSample / 8,
buffer = new ArrayBuffer(44 + samples.length * bytesPerSample),
view = new DataView(buffer);
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* RIFF chunk length */
view.setUint32(4, 36 + samples.length * bytesPerSample, true);
/* RIFF type */
writeString(view, 8, 'WAVE');
/* format chunk identifier */
writeString(view, 12, 'fmt ');
/* format chunk length */
view.setUint32(16, 16, true);
/* sample format (raw) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, numberOfChannels, true);
/* sample rate */
view.setUint32(24, sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, sampleRate * numberOfChannels * bytesPerSample, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, numberOfChannels * bytesPerSample, true);
/* bits per sample */
view.setUint16(34, bitsPerSample, true);
/* data chunk identifier */
writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, samples.length * bytesPerSample, true);
floatTo16BitPCM(view, 44, samples);
return view;
}
function getWavFile2() {
var dataview, i, index = 0,
resultLeft, resultRight,
mergedBuffersLeft, mergedBuffersRight;
if (numberOfChannels === 1) {
mergedBuffersLeft = mergeBuffers(recBuffersLeft, numFrames);
} else if (numberOfChannels === 2) {
mergedBuffersLeft = mergeBuffers(recBuffersLeft, numFrames);
mergedBuffersRight = mergeBuffers(recBuffersRight, numFrames);
}
//console.log('1:' + mergedBufferLeft.length);
if (bufferIndexEnd > 0 || bufferIndexStart > 0) {
if (bufferIndexEnd === -1) {
bufferIndexEnd = mergedBuffersLeft.length;
}
resultLeft = new Float32Array(bufferIndexEnd - bufferIndexStart + 1);
if (numberOfChannels === 2) {
resultRight = new Float32Array(bufferIndexEnd - bufferIndexStart + 1);
}
for (i = bufferIndexStart; i < bufferIndexEnd; i++) {
resultLeft[index] = mergedBuffersLeft[i];
if (numberOfChannels === 2) {
resultRight = mergedBuffersRight[i];
}
index++;
}
}
//console.log('2:' + mergedBufferLeft.length);
if (numberOfChannels === 1) {
planarSamples = mergedBuffersLeft;
interleavedSamples = new Float32Array(numFrames);
//planarSamples.copyWithin(interleavedSamples, 0);
for (i = 0; i < numFrames; i++) {
interleavedSamples[i] = planarSamples[i];
}
} else if (numberOfChannels === 2) {
planarSamples = toPlanarBuffer(mergedBuffersLeft, mergedBuffersRight);
interleavedSamples = toInterleavedBuffer(mergedBuffersLeft, mergedBuffersRight);
}
dataview = encodeWAV(interleavedSamples);
return {
planarSamples: planarSamples.buffer,
interleavedSamples: interleavedSamples.buffer,
wavArrayBuffer: dataview.buffer
};
}
}
sequencer.protectedScope.createAudioRecorderWorker = function () {
var blobURL = URL.createObjectURL(new Blob(['(', createWorker.toString(), ')()'], { type: 'application/javascript' }));
return new Worker(blobURL);
};
}