UNPKG

apphouse

Version:

Component library for React that uses observable state management and theme-able components.

161 lines (141 loc) 4.74 kB
interface WavePCMConfigType { sampleRate?: number; bitDepth?: number; } interface WavePCMMessageType { pcmArrays: Float32Array[]; config: WavePCMConfigType; } export default class WavePCM { recordedBuffers: any; sampleRate: any; bitDepth: any; bytesPerSample: any; numberOfChannels: any; wav: any; constructor(config: WavePCMConfigType) { this.sampleRate = config.sampleRate || 48000; this.bitDepth = config.bitDepth || 16; this.recordedBuffers = []; this.bytesPerSample = this.bitDepth / 8; } record = (buffers: any) => { this.numberOfChannels = this.numberOfChannels || buffers.length; var bufferLength = buffers[0].length; var reducedData = new Uint8Array( bufferLength * this.numberOfChannels * this.bytesPerSample, ); // Interleave for (var i = 0; i < bufferLength; i++) { for ( var channel = 0; channel < this.numberOfChannels; channel++ ) { var outputIndex = (i * this.numberOfChannels + channel) * this.bytesPerSample; var sample = buffers[channel][i]; // Check for clipping if (sample > 1) { sample = 1; } else if (sample < -1) { sample = -1; } // bit reduce and convert to uInt switch (this.bytesPerSample) { case 4: sample = sample * 2147483648; reducedData[outputIndex] = sample; reducedData[outputIndex + 1] = sample >> 8; reducedData[outputIndex + 2] = sample >> 16; reducedData[outputIndex + 3] = sample >> 24; break; case 3: sample = sample * 8388608; reducedData[outputIndex] = sample; reducedData[outputIndex + 1] = sample >> 8; reducedData[outputIndex + 2] = sample >> 16; break; case 2: sample = sample * 32768; reducedData[outputIndex] = sample; reducedData[outputIndex + 1] = sample >> 8; break; case 1: reducedData[outputIndex] = (sample + 1) * 128; break; default: throw "Only 8, 16, 24 and 32 bits per sample are supported"; } } } this.recordedBuffers.push(reducedData); }; requestData = () => { var bufferLength = this.recordedBuffers[0].length; var dataLength = this.recordedBuffers.length * bufferLength; var headerLength = 44; var wav = new Uint8Array(headerLength + dataLength); var view = new DataView(wav.buffer); view.setUint32(0, 1380533830, false); // RIFF identifier 'RIFF' view.setUint32(4, 36 + dataLength, true); // file length minus RIFF identifier length and file description length view.setUint32(8, 1463899717, false); // RIFF type 'WAVE' view.setUint32(12, 1718449184, false); // format chunk identifier 'fmt ' view.setUint32(16, 16, true); // format chunk length view.setUint16(20, 1, true); // sample format (raw) view.setUint16(22, this.numberOfChannels, true); // channel count view.setUint32(24, this.sampleRate, true); // sample rate view.setUint32( 28, this.sampleRate * this.bytesPerSample * this.numberOfChannels, true, ); // byte rate (sample rate * block align) view.setUint16( 32, this.bytesPerSample * this.numberOfChannels, true, ); // block align (channel count * bytes per sample) view.setUint16(34, this.bitDepth, true); // bits per sample view.setUint32(36, 1684108385, false); // data chunk identifier 'data' view.setUint32(40, dataLength, true); // data chunk length for (var i = 0; i < this.recordedBuffers.length; i++) { wav.set( this.recordedBuffers[i], i * bufferLength + headerLength, ); } // WavePCM.postMessage(wav, [wav.buffer]); }; static postMessage = (message: WavePCMMessageType) => { const wavPCM = new WavePCM(message.config); wavPCM.record(message.pcmArrays); wavPCM.requestData(); }; } // potential usage: // const audioBuffer = await AudioSource.getAudioBuffer( // player.blob, // ); // return new Promise(function (resolve, reject) { // var worker = new Worker(wavWorker); // console.log({ worker }); // worker.onmessage = function (e) { // var blob = new Blob([e.data.buffer], { // type: "audio/wav", // }); // resolve(blob); // }; // if (audioBuffer) { // let pcmArrays = []; // for ( // let i = 0; // i < audioBuffer.numberOfChannels; // i++ // ) { // pcmArrays.push(audioBuffer.getChannelData(i)); // } // WavePCM.postMessage({ // pcmArrays, // config: { sampleRate: audioBuffer.sampleRate }, // }); // }