如何将blob音频转换为wav文件,以供Python Flask应用程序使用?

qij5mzcb  于 2023-03-06  发布在  Python
关注(0)|答案(1)|浏览(372)

我正在编写一个Flask应用程序,并使用Javascript MediaRecorder来记录用户的音频。我设法填充了Blob并将其发送回我的Flask端点,但似乎无法将其转换为工作音频文件。
Javascript语言

const blob = new Blob(chunks, { type: "audio/wav" });
        chunks = [];
        const audioURL = window.URL.createObjectURL(blob);
        audio.src = audioURL;
        console.log("recorder stopped");

        const formData = new FormData();
        formData.append("audioData", blob);
        console.log(blob);

        fetch("/upload-audio", {
          method: "POST",
          body: formData,
        })
          .then((response) => {
            // Handle the response from the server
          })
          .catch((error) => {
            // Handle any errors that occurred
          });

Python- flask

@app.route('/upload-audio', methods=['POST'])
def upload_audio():
    audio_data = request.files['audioData'].read()
    print("audio data", audio_data)
    audio_data_save_attempt = request.files['audioData']
    audio_data_save_attempt.save("filenametest.wav")

    return "Trying to get audio data saved to disk"

Python返回以下内容:
print(“音频数据”,audio_data)返回:音频数据b“***@ M************************************
audio_data_保存_attempt创建文件,但该文件无法打开。
我的直觉是,音频数据确实有帧信息,但我不能正确地转换它。我尝试使用io.BytesIO和python wave模块,但似乎都不适合我,尽管我对音频非常陌生,可能会犯错误。

# Create a wave file from the audio data
    with io.BytesIO(audio_data) as audio_stream:
        with wave.open(audio_stream, mode="rb") as audio_file:
            # Create a new wave file in write mode
            with wave.open("audio.wav", mode="wb") as output_file:
                output_file.setparams(audio_file.getparams())
                output_file.writeframes(audio_file.readframes(audio_file.getnframes()))
pokxtpni

pokxtpni1#

音频文件无法播放的原因可能有多种。
一方面,当你完全读取一个文件时,指针指向文件的结尾,在你第二次读取数据并保存它之前,你必须使用file.seek(0)把指针放回到文件的开头。
另一方面,在我看来,你的数据并不是wave格式,根据平台和浏览器的不同,音频数据会以不同的格式导出,Chrome浏览器倾向于'WebA'格式。
下面是一个用浏览器录制音频的例子。数据被发送到服务器,然后保存。用户收到录制文件的概览,并可以播放它们。
在本例中,支持几种文件格式,但可能不支持所有文件格式。

from flask import (
    Flask,
    Response,
    abort,
    current_app,
    render_template,
    redirect,
    request,
    stream_with_context,
    url_for

)
from collections import namedtuple
from glob import glob
from mimetypes import add_type, guess_extension, guess_type
from werkzeug.utils import secure_filename
import os

add_type('audio/aac', '.m4a', strict=True)

Record = namedtuple('Record', ('filename', 'created_at'))

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
try:
    os.makedirs(os.path.join(
        app.instance_path,
        app.config.get('UPLOAD_FOLDER', 'uploads')
    ), exist_ok=True)
except:
    pass

@app.route('/')
def audio_index():
    patterns = [
        '*.m4a', # MacOS & Safari
        '*.wav', # Windows & ?
        '*.weba' # MacOS & Chrome
    ]
    path = os.path.join(
        current_app.instance_path,
        current_app.config.get('UPLOAD_FOLDER', 'uploads')
    )
    records = [
        Record(fn[len(path)+1:], os.path.getctime(fn)) \
         for ptrn in patterns for fn in glob(os.path.join(path, ptrn))
    ]
    return render_template('index.html', records=records)

@app.route('/audio-upload', methods=['POST'])
def audio_upload():
    if 'audio_file' in request.files:
        file = request.files['audio_file']
        extname = guess_extension(file.mimetype)
        if not extname:
            abort(400)

        # Check for allowed file extensions.

        i = 1
        while True:
            dst = os.path.join(
                current_app.instance_path,
                current_app.config.get('UPLOAD_FOLDER', 'uploads'),
                secure_filename(f'audio_record_{i}{extname}'))
            if not os.path.exists(dst): break
            i += 1

        file.save(dst)

    return redirect(url_for('audio_index'))

@app.route('/audio/stream/<path:fn>', methods=['GET'])
def audio_download(fn):
    @stream_with_context
    def generator(src):
        CHUNK_SIZE = 1024
        with open(src, 'rb') as fp:
            while True:
                data = fp.read(CHUNK_SIZE)
                if not data: break
                yield data

    src = os.path.join(
        current_app.instance_path,
        current_app.config.get('UPLOAD_FOLDER', 'uploads'),
        fn
    )

    if not os.path.exists(src):
        return abort(404)

    mime,_ = guess_type(src)
    return Response(generator(src), mimetype=mime)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Audio Index</title>
  </head>
  <body>
      <div class="rec-container">
        <div class="rec-column rec-column-1">
          <button class="rec-btn" id="toggle-rec-btn">Record</button>
        </div>
        <div class="rec-column rec-column-2">
          {% for record in records|sort(attribute='created_at', reverse=True) -%}
          <div class="rec-item">
            <div class="content">
              <span class="rec-title">{{ record.filename }}</span>
              <audio
                controls
                src="{{ url_for('audio_download', fn=record.filename) }}"
              ></audio>
            </div>
          </div>
          {% endfor -%}
        </div>
    </div>

    <script type="text/javascript">
    (function() {
      const uploadURL = "{{ url_for('audio_upload') }}";
      const startButton = document.getElementById("toggle-rec-btn");
      startButton.addEventListener("click", function() {
        if (!navigator.mediaDevices) {
          console.error("getUserMedia not supported.")
          return;
        }

        const constraints = { audio: true };
        navigator.mediaDevices.getUserMedia(constraints)
        .then(function(stream) {
            let chunks = []
            let recorder = new MediaRecorder(stream);
            recorder.ondataavailable = event => {
                chunks.push(event.data);
            };
            recorder.onstop = event => {
              console.log("Recording stopped.")
              let blob = new Blob(chunks, { type: recorder.mimeType }); 
              chunks = [];
              startButton.disabled = false;

              let formData = new FormData();
              formData.append('audio_file', blob);

              fetch(uploadURL, {
                method: "POST",
                cache: "no-cache",
                body: formData
              }).then(resp => {
                if (resp.status === 200) {
                  window.location.reload(true);
                } else {
                  console.error("Error:", resp)
                }
              }).catch(err => {
                console.error(err);
              });
            };
            recorder.onstart = event => {
              console.log("Recording started.");
              startButton.disabled = true;
              setTimeout(function() { recorder.stop(); }, 10000);
            };
            recorder.start();
        })
        .catch(function(err) {
            console.error(err);
        });
      });
    })();
    </script>
  </body>
</html>

相关问题