2

I'm working on a software to stream audio from a source over HTTP using a Flask webservice. I can get the sound frames through sounddevice and route them to a browser through a Flask route with yield and the right mimetype, but the raw audio format is quite cumbersome for remote streaming, and not really the best when it comes to client compatibility.

I'd love to use pydub to convert the raw audio frames to a format like mp3 or ogg, but it's not clear to me neither from the documentation nor from the source code how to achieve an on-the-fly format conversion without dumping the output to a file through .export().

The skeleton of my code so far is something like:

### audio.py

import queue
import sounddevice as sd
from pydub.audio_segment import AudioSegment


def input_stream(device, sample_width=2, sample_rate=44100,
        channels=1, latency=0, blocksize=2048, timeout=5.0):
    audio_queue = queue.Queue()

    def audio_callback(indata, frames, time_duration, status):
        audio = AudioSegment(indata, sample_width=sample_width,
                channels=channels, frame_rate=sample_rate)

        # Some pydub magic should happen here to convert the raw frame to mp3/ogg

        audio_queue.put(audio.raw_data)


    with sd.InputStream(samplerate=sample_rate, device=device,
                        channels=channels, callback=audio_callback,
                        latency=latency, blocksize=blocksize):
        while not recording_terminated():
            yield audio_queue.get(block=True, timeout=timeout)


### web.py

from flask import route, request, Response

from audio import input_stream


@route('/sound/stream', methods=['GET'])
def get_sound_feed():
    device = request.args.get('device')
    return Response(input_stream(device), mimetype='audio/ogg')

How would one convert the raw AudioSegment object in audio_callback into a compressed mp3/ogg suitable for web streaming? I know that it's possible to create a segment from an mp3 through AudioSegment.from_file, or dump it to an mp3 file through .export(), but that wouldn't really be an option as such I/O operations would introduce non-negligible latency. I think it might be theoretically possible to hack .export() to get it to dump to a socket or fifo file descriptor, but that sounds a bit as a hacky workaround to me, plus I'm not sure whether it's sufficient for the file descriptor to provide the .write() method or if it'd break because other methods (e.g. seek) are required.

BlackLight
  • 122
  • 1
  • 8
  • I've bumped into this question from 2014: https://stackoverflow.com/questions/25469161/how-to-convert-wav-to-mp3-in-live-using-python. The last comment seems to confirm my suspicion that exporting on-the-fly (not to a file) is not an option in pydub. I'm not sure if any progress has been made in the meantime or if some user has already made a fork to support it. If that's not the case, I might give it a try and submit a PR to pydub. – BlackLight Jul 23 '19 at 12:24
  • have you found how to do this? I'm trying to save the file in a BytesIO to remove the persistence aspect –  Nov 23 '19 at 19:22

1 Answers1

2

I don't know if you can prevent pydub from saving the file to the disk, but you can just get the file at the end of conversion without reopen it. Actually, the .export() function return the file object at the end of execution.

convert_file = audio_file.export(format="flac")

I have done this and I could process the convert_file as if I had used the open() function. (I convert to flac for my own project but you can do any format)
I found out that if you don't provide a file name, the .export() function wont even write the file to disk without any errors.
I hope you can found a workaround for your issue.

Noomaok
  • 21
  • 3