How to stream MP3 chunks given a NumPy array in Python?

让人想犯罪 __ 提交于 2021-01-29 14:41:44

问题


I'm struggling to find a solution for streaming synthesized audio from a Python server. The synthesized audio is incrementally generated and returned as a np.float32 NumPy array. It then needs to be transformed from a NumPy array into an MP3 chunk. Finally, the MP3 chunk is served via flask.

Here is some pseudo-code:

import numpy

from flask import Flask
from flask import Response

app = Flask(__name__)
sample_rate = 24000


def pcm_to_mp3():
    raise NotImplementedError()


def get_synthetic_audio():
    """ Mock function for synthetic audio. """
    while True:
        yield numpy.random.rand(1024) * 2 - 1  # Return: 32-bit Floating Point PCM


@app.route('/stream', methods=['GET'])
def get_stream():
    """ Stream synthetic audio. """

    def response():
        for numpy_array in get_synthetic_audio():
            # NOTE: The raw audio needs additional metadata to be playable like sample rate.
            yield pcm_to_mp3(numpy_array, sample_rate=sample_rate)

    return Response(
        response(),
        headers={
            # NOTE: Ensure stream is not cached.
            'Cache-Control': 'no-cache, no-store, must-revalidate',
            'Pragma': 'no-cache',
            'Expires': '0',
            # NOTE: Enable streaming.
            'Transfer-Encoding': 'chunked'
        },
        mimetype='audio/mpeg')


if __name__ == "__main__":
    app.run()

While a similar setup works for WAV files, I am unable to figure out how to do something similar for MP3 files.

Thank you!


Sources

  • NumPy Array to MP3 File via PyDub: How to read a MP3 audio file into a numpy array / save a numpy array to MP3?
  • Stream Audio WAV File with Flask: HTTP realtime audio streaming server
  • Stream Audio WAV File with PyAudio: Python: realtime audio streaming with PyAudio (or something else)?
  • Stream with FFMpeg and Flask: https://gist.github.com/anthonyeden/f3b3bdf6f62badd8f87bb574283f488a

回答1:


I was able to figure out a working approach:

import select
import subprocess

import numpy

from flask import Flask
from flask import Response

app = Flask(__name__)


def get_synthetic_audio(num_samples):
    audio = numpy.random.rand(num_samples).astype(numpy.float32) * 2 - 1
    assert audio.max() <= 1.0
    assert audio.min() >= -1.0
    assert audio.dtype == numpy.float32
    return audio


def response():
    pipe = subprocess.Popen(
        'ffmpeg -f f32le -acodec pcm_f32le -ar 24000 -ac 1 -i pipe: -f mp3 pipe:'
        .split(),
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)
    poll = select.poll()
    poll.register(pipe.stdout, select.POLLIN)
    while True:
        pipe.stdin.write(get_synthetic_audio(24000).tobytes())
        while poll.poll(0):
            yield pipe.stdout.readline()


@app.route('/stream.mp3', methods=['GET'])
def stream():
    return Response(
        response(),
        headers={
            # NOTE: Ensure stream is not cached.
            'Cache-Control': 'no-cache, no-store, must-revalidate',
            'Pragma': 'no-cache',
            'Expires': '0',
        },
        mimetype='audio/mpeg')


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8000, debug=True)

During my exploration, I learned that flask does not support chunked transfer encoding. This was surprising because chunked transfer encoding was introduced in 1997 as part of HTTP 1.1.

Regardless, I was surprised to learn that ffmpeg's stream is compatible with flask, and that it was supported on Safari, Firefox, and Chrome.



来源:https://stackoverflow.com/questions/61351764/how-to-stream-mp3-chunks-given-a-numpy-array-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!