UNPKG

audiomaker

Version:

A simple, light weight javascript plugin for audio editing functionalites.

264 lines (247 loc) 10.3 kB
class AudioMaker { constructor(config) { config = config || {}; this.aContext = this._getContext(); this.sampleRate = this.aContext.sampleRate; this.outputType = config.type || 'wav'; } _getContext () { window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext; return new AudioContext(); }; _getArrayBuffer (data) { let bufferRequest; if(data && data instanceof Blob) { bufferRequest = data.arrayBuffer(); } else { bufferRequest = fetch(data,{mode: 'no-cors'}).then((res)=>{ return res.arrayBuffer(); }); } return bufferRequest; } trim (data,sTime,eTime) { return new Promise(async(resolve)=>{ let _self = this; _self._getArrayBuffer(data).then(async(arrayBuffer)=>{ await _self.aContext.decodeAudioData(arrayBuffer).then((decodedData)=>{ let trimmedData, trimStart = decodedData.sampleRate*sTime, trimEnd = eTime ? decodedData.sampleRate*eTime : decodedData.sampleRate*decodedData.duration; if (decodedData.numberOfChannels === 2) { trimmedData = _self.interleave(decodedData.getChannelData(0).slice(trimStart,trimEnd), decodedData.getChannelData(1).slice(trimStart,trimEnd)); } else { trimmedData = decodedData.getChannelData(0).slice(trimStart,trimEnd); } resolve(_self._exportAudio(trimmedData,decodedData.numberOfChannels)); }); }); }); } add (data) { return new Promise(async(resolve)=>{ let _self = this; let arrayBuffers = data.map(async(audio)=>{ return await _self._getArrayBuffer(audio).then((arrayBuffer)=>{ return _self.aContext.decodeAudioData(arrayBuffer); }); }); Promise.all(arrayBuffers).then((audioBuffers)=>{ let floatData = []; audioBuffers.forEach((decodedData)=>{ if (decodedData.numberOfChannels === 2) { floatData.push(Array.from(_self.interleave(decodedData.getChannelData(0), decodedData.getChannelData(1)))); } else { floatData.push(Array.from(decodedData.getChannelData(0))); } }); let concatinatedArray = floatData.flat(); resolve(_self._exportAudio(new Float32Array(concatinatedArray),audioBuffers[0].numberOfChannels)); }); }); } merge (data) { return new Promise(async(resolve)=>{ let _self = this; let arrayBuffers = data.map(async(audio)=>{ return await _self._getArrayBuffer(audio).then((arrayBuffer)=>{ return _self.aContext.decodeAudioData(arrayBuffer); }); }); Promise.all(arrayBuffers).then((audioBuffers)=>{ let maxChannels = Math.max(...audioBuffers.map(o => o.numberOfChannels), 0); let maxDuration = Math.max(...audioBuffers.map(o => o.duration), 0); var mixedBuffer = _self.aContext.createBuffer(maxChannels, _self.aContext.sampleRate * maxDuration, _self.aContext.sampleRate); audioBuffers.forEach((buffer)=>{ for (var srcChannel = 0; srcChannel < buffer.numberOfChannels; srcChannel++) { var _out = mixedBuffer.getChannelData(srcChannel); var _in = buffer.getChannelData(srcChannel); for (var i = 0; i < _in.length; i++) { _out[i] += _in[i]; } } }); let floatData; if (maxChannels === 2) { floatData = _self.interleave(mixedBuffer.getChannelData(0), mixedBuffer.getChannelData(1)); } else { floatData = mixedBuffer.getChannelData(0); } resolve(_self._exportAudio(floatData,maxChannels)); }); }); } loop (data,loopCount=0) { return new Promise(async(resolve)=>{ let _self = this,loopedArray = []; let audioBuffer = await _self._getArrayBuffer(data).then((arrayBuffer)=>{ return _self.aContext.decodeAudioData(arrayBuffer); }); for(let i=0;i<=loopCount;i++) { if (audioBuffer.numberOfChannels === 2) { loopedArray.push(Array.from(_self.interleave(audioBuffer.getChannelData(0), audioBuffer.getChannelData(1)))); } else { loopedArray.push(Array.from(audioBuffer.getChannelData(0))); } } resolve(_self._exportAudio(new Float32Array(loopedArray.flat()),audioBuffer.numberOfChannels)); }); } reverse (data) { return new Promise(async(resolve)=>{ let _self = this,reversedData = []; let audioBuffer = await _self._getArrayBuffer(data).then((arrayBuffer)=>{ return _self.aContext.decodeAudioData(arrayBuffer); }); if (audioBuffer.numberOfChannels === 2) { reversedData = Array.from(_self.interleave(audioBuffer.getChannelData(0), audioBuffer.getChannelData(1))).reverse(); } else { reversedData = Array.from(audioBuffer.getChannelData(0)).reverse(); } resolve(_self._exportAudio(new Float32Array(reversedData),audioBuffer.numberOfChannels)); }); } timeline (data) { return new Promise(async(resolve)=>{ let _self = this, timelineData = [...data], resultArray = []; let audioTimelineLoop = (i) => { let modifedArray; _self._getArrayBuffer(timelineData[i].audio).then((arrayBuffer)=>{ _self.aContext.decodeAudioData(arrayBuffer).then((audioBuffer)=>{ timelineData[i].audioBuffer = audioBuffer; if(timelineData[i].trim && timelineData[i].trim instanceof Array) { let sTime = timelineData[i].trim[0], eTime = timelineData[i].trim[1] ? timelineData[i].trim[1] : audioBuffer.duration; if (audioBuffer.numberOfChannels === 2) { modifedArray = Array.from(_self.interleave(audioBuffer.getChannelData(0).slice(audioBuffer.sampleRate*sTime, audioBuffer.sampleRate*eTime), audioBuffer.getChannelData(1).slice(audioBuffer.sampleRate*sTime, audioBuffer.sampleRate*eTime))); } else { modifedArray = Array.from(audioBuffer.getChannelData(0).slice(audioBuffer.sampleRate*sTime, audioBuffer.sampleRate*(eTime))); } } else { if (audioBuffer.numberOfChannels === 2) { modifedArray = Array.from(_self.interleave(audioBuffer.getChannelData(0), audioBuffer.getChannelData(1))); } else { modifedArray = Array.from(audioBuffer.getChannelData(0)); } } if(timelineData[i].reverse) { modifedArray = modifedArray.reverse(); } if(timelineData[i].loop && (typeof timelineData[i].loop == 'number')) { for(let j=0;j<timelineData[i].loop;j++) { modifedArray.push(modifedArray); } } timelineData[i].modifedArray = modifedArray.flat(); resultArray.push(timelineData[i].modifedArray); if(timelineData.length === resultArray.length) { let maxChannels = Math.max(...timelineData.map(o => o.audioBuffer.numberOfChannels), 0); resolve(_self._exportAudio(new Float32Array(resultArray.flat()),maxChannels)); } else { modifedArray = null; audioTimelineLoop(i+1); } }); }); } audioTimelineLoop(0); }); }; _exportAudio (samplesData,numberOfChannels) { let _self = this,arrayBuffer,blob; if(_self.outputType == 'wav') { let format = 3, bitDepth = 32; arrayBuffer = _self.encodeWAV(samplesData,format,_self.sampleRate,numberOfChannels,bitDepth); blob = new Blob([new Uint8Array(arrayBuffer)],{type: 'audio/wav'}); } return blob; }; interleave (inputL, inputR) { let 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; } writeString (view, offset, string) { for (var i = 0; i < string.length; i++) { view.setUint8(offset + i, string.charCodeAt(i)); } } writeFloat32 (output, offset, input) { for (var i = 0; i < input.length; i++, offset += 4) { output.setFloat32(offset, input[i], true); } } 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); } } encodeWAV (samples, format, sampleRate, numChannels, bitDepth) { let _self = this, bytesPerSample = bitDepth / 8, blockAlign = numChannels * bytesPerSample, buffer = new ArrayBuffer(44 + samples.length * bytesPerSample), view = new DataView(buffer); /* RIFF identifier */ _self.writeString(view, 0, 'RIFF'); /* RIFF chunk length */ view.setUint32(4, 36 + samples.length * bytesPerSample, true); /* RIFF type */ _self.writeString(view, 8, 'WAVE'); /* format chunk identifier */ _self.writeString(view, 12, 'fmt '); /* format chunk length */ view.setUint32(16, 16, true); /* sample format (raw) */ view.setUint16(20, format, true); /* channel count */ view.setUint16(22, numChannels, true); /* sample rate */ view.setUint32(24, sampleRate, true); /* byte rate (sample rate * block align) */ view.setUint32(28, sampleRate * blockAlign, true); /* block align (channel count * bytes per sample) */ view.setUint16(32, blockAlign, true); /* bits per sample */ view.setUint16(34, bitDepth, true); /* data chunk identifier */ _self.writeString(view, 36, 'data'); /* data chunk length */ view.setUint32(40, samples.length * bytesPerSample, true); _self.writeFloat32(view, 44, samples); return buffer; } } module.exports = AudioMaker;