-
Notifications
You must be signed in to change notification settings - Fork 0
/
save_wav.html
123 lines (110 loc) · 4.16 KB
/
save_wav.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<html>
<head>
<title>save WAV file from mic input</title>
</head>
<body>
<button onclick="onClickStartWAV();">Record</button>
<button onclick="onClickStopWAV();">Stop</button>
<a id="dl">Download</a>
<script>
var videoStream = undefined;
var audioData = [];
const bufferSize = 4096;
let audio_sample_rate = undefined;
// save audio data
var onAudioProcess = function (e) {
var input = e.inputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = input[i];
}
audioData.push(bufferData);
};
function exportWAV(audioData) {
let encodeWAV = function (samples, sampleRate) {
let buffer = new ArrayBuffer(44 + samples.length * 2);
let view = new DataView(buffer);
let writeString = function (view, offset, string) {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
};
let floatTo16BitPCM = function (output, offset, input) {
for (let i = 0; i < input.length; i++ , offset += 2) {
let s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
};
writeString(view, 0, 'RIFF'); // RIFF Header
view.setUint32(4, 32 + samples.length * 2, true); // File Size
writeString(view, 8, 'WAVE'); // WAVE Header
writeString(view, 12, 'fmt '); // fmt chunk
view.setUint32(16, 16, true); // bytes of fmt chunk
view.setUint16(20, 1, true); // format ID
view.setUint16(22, 1, true); // channels
view.setUint32(24, sampleRate, true); // sample rate
view.setUint32(28, sampleRate * 2, true); // velocity
view.setUint16(32, 2, true); // block size
view.setUint16(34, 16, true); // bit per sample
writeString(view, 36, 'data'); // data chunk
view.setUint32(40, samples.length * 2, true); // bytes of PCM data
floatTo16BitPCM(view, 44, samples); // PCM data
return view;
}
let mergeBuffers = function (data) {
let sampleLength = 0;
for (let i = 0; i < data.length; i++) {
sampleLength += data[i].length;
}
let samples = new Float32Array(sampleLength);
let sampleIdx = 0;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < data[i].length; j++) {
samples[sampleIdx] = data[i][j];
sampleIdx++;
}
}
return samples;
};
let dataview = encodeWAV(mergeBuffers(audioData), audio_sample_rate);
return URL.createObjectURL(new Blob([dataview], { type: 'audio/wav' }));
};
function handleAudioContext (stream) {
videoStream = stream;
audioContext = new AudioContext();
audio_sample_rate = audioContext.sampleRate;
console.log('Audio source sampling rate:' + audio_sample_rate);
const scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamsource = audioContext.createMediaStreamSource(stream);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.onaudioprocess = onAudioProcess;
scriptProcessor.connect(audioContext.destination);
}
function onClickStartWAV() {
audioData = []
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(handleAudioContext)
.catch((e) => {
console.log(e);
});
}
function onClickStopWAV() {
if (videoStream) {
videoStream.getVideoTracks().forEach((track) => {
track.stop();
});
videoStream.getAudioTracks().forEach((track) => {
track.stop();
});
}
var dlWav = document.querySelector("#dl");
dlWav.href = exportWAV(audioData);
dlWav.download = 'wav_test.wav';
dlWav.click();
};
</script>
</body>
</html>