uploadable-react-audio-recorder
Version:
131 lines (118 loc) • 4.19 kB
text/typescript
import encodeWAV from './waveEncoder';
/*
interface Navigator {
webkitGetUserMedia?: typeof navigator.getUserMedia,
mozGetUserMedia?: typeof navigator.getUserMedia,
msGetUserMedia?: typeof navigator.getUserMedia,
};
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
*/
function getAudioContext(){
var ctxt = {
sampleRate: null,
createGain: null,
createMediaStreamSource: null,
createScriptProcessor: null,
destination:null,
decodeAudioData:null,
createBufferSource:null
};
try{
ctxt = new AudioContext();
}catch(e){
console.log(e);
}
return ctxt;
}
export default class WAVEInterface {
static audioContext = getAudioContext();
static bufferSize = 2048;
playbackNode: AudioBufferSourceNode;
recordingNodes: AudioNode[] = [];
recordingStream: MediaStream;
buffers: Float32Array[][]; // one buffer for each channel L,R
encodingCache?: Blob;
get bufferLength() { return this.buffers[0].length * WAVEInterface.bufferSize; }
get audioDuration() { return this.bufferLength / WAVEInterface.audioContext.sampleRate; }
get audioData() {
return this.encodingCache || encodeWAV(this.buffers, this.bufferLength, WAVEInterface.audioContext.sampleRate);
}
startRecording() {
return new Promise((resolve, reject) => {
try{
navigator.getUserMedia({ audio: true }, (stream) => {
const { audioContext } = WAVEInterface;
const recGainNode = audioContext.createGain();
const recSourceNode = audioContext.createMediaStreamSource(stream);
const recProcessingNode = audioContext.createScriptProcessor(WAVEInterface.bufferSize, 2, 2);
if(recGainNode && recSourceNode && recProcessingNode)
{
if (this.encodingCache) this.encodingCache = null;
recProcessingNode.onaudioprocess = (event) => {
// console.log('audio process', this);
if (this.encodingCache) this.encodingCache = null;
// save left and right buffers
for (let i = 0; i < 2; i++) {
const channel = event.inputBuffer.getChannelData(i);
this.buffers[i].push(new Float32Array(channel));
}
};
recSourceNode.connect(recGainNode);
recGainNode.connect(recProcessingNode);
recProcessingNode.connect(audioContext.destination);
this.recordingStream = stream;
this.recordingNodes.push(recSourceNode, recGainNode, recProcessingNode);
resolve(stream);
}
}, (err) => {
reject(err);
});
} catch(e){
reject(e);
}
});
}
stopRecording() {
if (this.recordingStream) {
this.recordingStream.getTracks()[0].stop();
delete this.recordingStream;
}
for (let i in this.recordingNodes) {
this.recordingNodes[i].disconnect();
delete this.recordingNodes[i];
}
}
startPlayback(loop: boolean = false, onended: () => void) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(this.audioData);
reader.onloadend = () => {
WAVEInterface.audioContext.decodeAudioData(reader.result, (buffer) => {
const source = WAVEInterface.audioContext.createBufferSource();
source.buffer = buffer;
source.connect(WAVEInterface.audioContext.destination);
source.loop = loop;
source.start(0);
source.onended = onended;
this.playbackNode = source;
resolve(source);
});
};
});
}
stopPlayback() {
this.playbackNode.stop();
}
reset() {
if (this.playbackNode) {
this.playbackNode.stop();
this.playbackNode.disconnect(0);
delete this.playbackNode;
}
this.stopRecording();
this.buffers = [[], []];
}
}