我需要保存麦克风输入以供稍后在 AudioElement 中使用。我这样做是为了获得麦克风输入:
window.navigator.getUserMedia(audio: true).then((MediaStream stream) {
# what should go here?
});
我应该怎么做才能保存音频?
我需要保存麦克风输入以供稍后在 AudioElement 中使用。我这样做是为了获得麦克风输入:
window.navigator.getUserMedia(audio: true).then((MediaStream stream) {
# what should go here?
});
我应该怎么做才能保存音频?
有许多可怕的愚蠢示例,您可以在当前浏览器窗口中播放当前录音。有没有一个用例。对于视频,我可以想象一个人想要构建一个类似 Skype 的应用程序并有一个预览窗口,看看你在视频上是否看起来很愚蠢,但是音频......
不过我发现了一篇不错的帖子:从麦克风到 .WAV 与:getUserMedia 和 Web 音频
我在链接的文章中移植了一部分代码,展示了如何获取数据。
import 'dart:html';
import 'dart:async';
import 'dart:web_audio';
void main() {
window.navigator.getUserMedia(video: true, audio: true).then((MediaStream stream) {
var context = new AudioContext();
GainNode volume = context.createGain();
MediaStreamAudioSourceNode audioInput = context.createMediaStreamSource(stream);
audioInput.connectNode(volume);
int bufferSize = 2048;
ScriptProcessorNode recorder = context.createJavaScriptNode(bufferSize, 2, 2);
recorder.onAudioProcess.listen((AudioProcessingEvent e) {
print('recording');
var left = e.inputBuffer.getChannelData(0);
var right = e.inputBuffer.getChannelData(1);
print(left);
// process Data
});
volume.connectNode(recorder);
recorder.connectNode(context.destination);
/**
* [How to get a file or blob from an object URL?](http://stackoverflow.com/questions/11876175)
* [Convert blob URL to normal URL](http://stackoverflow.com/questions/14952052/convert-blob-url-to-normal-url)
* Doesn't work as it seems blob urls are not supported in Dart
*/
// String url = Url.createObjectUrlFromStream(stream);
// var xhr = new HttpRequest();
// xhr.responseType = 'blob';
// xhr.onLoad.listen((ProgressEvent e) {
// print(xhr.response);
// var recoveredBlog = xhr.response;
// var reader = new FileReader();
//
// reader.onLoad.listen((e) {
// var blobAsDataUrl = reader.result;
// reader.readAsDataUrl(blobAsDataUrl);
// });
// });
// xhr.open('GET', url);
// xhr.send();
/**
* only for testing purposes
**/
// var audio = document.querySelector('audio') as AudioElement;
// audio.controls = true;
// audio.src = url;
});
}
感谢 Günter Zöchbauer 指出这个 JS 解决方案。我已经在 Dart 中重写了代码并且它可以工作。
import 'dart:html';
import 'dart:async';
import 'dart:web_audio';
import 'dart:typed_data';
bool recording;
List leftchannel;
List rightchannel;
int recordingLength;
int sampleRate;
void main() {
leftchannel = [];
rightchannel = [];
recordingLength = 0;
sampleRate = 44100;
recording = true;
// add stop button
ButtonElement stopBtn = new ButtonElement()
..text = 'Stop'
..onClick.listen((_) {
// stop recording
recording = false;
// we flat the left and right channels down
var leftBuffer = mergeBuffers ( leftchannel, recordingLength );
var rightBuffer = mergeBuffers ( rightchannel, recordingLength );
// we interleave both channels together
var interleaved = interleave( leftBuffer, rightBuffer );
// we create our wav file
var buffer = new Uint8List(44 + interleaved.length * 2);
ByteData view = new ByteData.view(buffer);
// RIFF chunk descriptor
writeUTFBytes(view, 0, 'RIFF');
view.setUint32(4, 44 + interleaved.length * 2, Endianness.LITTLE_ENDIAN);
writeUTFBytes(view, 8, 'WAVE');
// FMT sub-chunk
writeUTFBytes(view, 12, 'fmt ');
view.setUint32(16, 16, Endianness.LITTLE_ENDIAN);
view.setUint16(20, 1, Endianness.LITTLE_ENDIAN);
// stereo (2 channels)
view.setUint16(22, 2, Endianness.LITTLE_ENDIAN);
view.setUint32(24, sampleRate, Endianness.LITTLE_ENDIAN);
view.setUint32(28, sampleRate * 4, Endianness.LITTLE_ENDIAN);
view.setUint16(32, 4, Endianness.LITTLE_ENDIAN);
view.setUint16(34, 16, Endianness.LITTLE_ENDIAN);
// data sub-chunk
writeUTFBytes(view, 36, 'data');
view.setUint32(40, interleaved.length * 2, Endianness.LITTLE_ENDIAN);
// write the PCM samples
var lng = interleaved.length;
var index = 44;
var volume = 1;
for (var i = 0; i < lng; i++){
view.setInt16(index, (interleaved[i] * (0x7FFF * volume)).truncate(), Endianness.LITTLE_ENDIAN);
index += 2;
}
// our final binary blob
var blob = new Blob ( [ view ] , 'audio/wav' );
// let's save it locally
String url = Url.createObjectUrlFromBlob(blob);
AnchorElement link = new AnchorElement()
..href = url
..text = 'download'
..download = 'output.wav';
document.body.append(link);
});
document.body.append(stopBtn);
window.navigator.getUserMedia(audio: true).then((MediaStream stream) {
var context = new AudioContext();
GainNode volume = context.createGain();
MediaStreamAudioSourceNode audioInput = context.createMediaStreamSource(stream);
audioInput.connectNode(volume);
int bufferSize = 2048;
ScriptProcessorNode recorder = context.createJavaScriptNode(bufferSize, 2, 2);
recorder.onAudioProcess.listen((AudioProcessingEvent e) {
if (!recording) return;
print('recording');
var left = e.inputBuffer.getChannelData(0);
var right = e.inputBuffer.getChannelData(1);
print(left);
// process Data
leftchannel.add(new Float32List.fromList(left));
rightchannel.add(new Float32List.fromList(right));
recordingLength += bufferSize;
});
volume.connectNode(recorder);
recorder.connectNode(context.destination);
});
}
void writeUTFBytes(ByteData view, offset, String string){
var lng = string.length;
for (var i = 0; i < lng; i++){
view.setUint8(offset + i, string.codeUnitAt(i));
}
}
Float32List interleave(leftChannel, rightChannel){
var length = leftChannel.length + rightChannel.length;
var result = new Float32List(length);
var inputIndex = 0;
for (var index = 0; index < length; ){
result[index++] = leftChannel[inputIndex];
result[index++] = rightChannel[inputIndex];
inputIndex++;
}
return result;
}
List mergeBuffers(channelBuffer, recordingLength){
List result = new List();
var offset = 0;
var lng = channelBuffer.length;
for (var i = 0; i < lng; i++){
var buffer = channelBuffer[i];
result.addAll(buffer);
}
return result;
}
你可以在这里从 github 拉取代码。