how to convert wav to mp3 in live using python?

2020-08-15 01:23发布

问题:

I have code like what is shown below to get audio from microphone:

import pyaudio
p = pyaudio.PyAudio()
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 1024*10
RECORD_SECONDS = 10
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    send_via_socket(data) # function to send each frame to remote system

This code is working fine. However each data frame has a size of 4kb. That means 40kb of internet data is needed to send 1 sec of audio data. It's only 6kb of data When I saved the 10 frames (1 second audio) to disc and convert it to mp3 using the pdub module. How can I convert each wav frame to mp3 before sending via socket? (I just need to reduce the size of the frame to save network usage). For example:

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)  # data =4kb
    mp3_frame = wav_to_mp3(data) # mp3_frame should be 1kb or less
    send_via_socket(mp3_frame) # function to send each frame to remote system

回答1:

try python-audiotools. I think it will help you stream the audio file that you want.



回答2:

From reading the code for pydub, it appears that an AudioSegment only allows an output to a file using the out_f variable. So, you can read the WAV file and encode each chunk to a file and then read the file and send it out, decoding it on the other end. However, this is not very efficient. I'd suggest actually extending pydub to handle streams and contributing to the project. The export code is pretty straightforward and I bet it would not be too difficult to do. The author would likely be grateful.

The code for AudioSegment is here: https://github.com/jiaaro/pydub/blob/master/pydub/audio_segment.py



回答3:

I was able to figure out a working approach using flask and ffmpeg...

import select
import subprocess

import numpy

from flask import Flask
from flask import Response

app = Flask(__name__)


def get_microphone_audio(num_samples):
    # TODO: Add the above microphone code. 
    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)

This solution allows for live streaming and is supported in Chrome, Firefox, and Safari.

This solution also worked for this similar question: How to stream MP3 chunks given a NumPy array in Python?