【问题标题】:How to save microphone audio input?如何保存麦克风音频输入?
【发布时间】:2014-02-16 09:10:04
【问题描述】:

我需要保存麦克风输入以供稍后在 AudioElement 中使用。我这样做是为了获得麦克风输入:

window.navigator.getUserMedia(audio: true).then((MediaStream stream) {
  # what should go here?
});

我应该怎么做才能保存音频?

【问题讨论】:

    标签: dart html5-audio audio-recording dart-html


    【解决方案1】:

    有许多可怕的愚蠢示例,您可以在当前浏览器窗口中播放当前录音。有没有一个用例。对于视频,我可以想象一个人想要构建一个类似 Skype 的应用程序并有一个预览窗口来查看你在视频上是否看起来很愚蠢,但是音频......

    我发现了一篇不错的帖子:From microphone to .WAV with: getUserMedia and Web Audio

    我已经移植了链接文章中的部分代码,展示了如何获取数据。

    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;
      });
    }
    

    【讨论】:

    • 感谢您分享上述链接和部分解决方案。完整的解决方案需要处理原始数据并生成类似于链接页面的实际声音文件。我正在努力将 JS 解决方案翻译成 Dart。我会尽快发布有关结果的更新。
    • This 是我在 Dart 中重写 JS 解决方案的最佳尝试。它从原始数据生成一个 wav 文件,但文件的开头有两条格式错误的行破坏了文件(据我所知,将它与工作 wav 文件进行比较)。
    • 我已经成功地重写了你引用的 JS 解决方案。有关完整代码,请参阅我的答案。谢谢。
    • @NawafAlsulami 我觉得你认为我的回答不够好​​,无法被接受?
    • 我刚刚将您的答案标记为已接受。我感谢您为我指明了正确的方向并帮助我摆脱困境。谢谢。
    【解决方案2】:

    感谢 Günter Zöchbauer 指向this JS solution。我在 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上拉代码here

    【讨论】:

      猜你喜欢
      • 2023-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-01
      • 2011-04-29
      • 2023-01-10
      • 1970-01-01
      • 2017-10-26
      相关资源
      最近更新 更多