11

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
JSON C11
  • 11,272
  • 7
  • 78
  • 65
open source guy
  • 2,727
  • 8
  • 38
  • 61
  • 7
    A simple Google search of `mp3 encoder python` is all I did to discover this link: http://pymedia.org/tut/recode_audio.html Seems like that is exactly what you want. – selbie Aug 24 '14 at 06:48
  • 4
    @selbie ... why don't you convert this to an answer and collect the bounty? – jimm101 Aug 26 '14 at 11:42
  • 4
    @jimm101 - All I did was Google for the answer. It would be lame to expect points when my effort to answer this question took no longer than 10 seconds, added no insight, nor demonstrated any relevant experience/knowledge other than my l33t Internet search skills. :) If I was regularly coding in Python and had implemented audio code, it might be a different story. But for this problem, all I did was Google for the answer.... – selbie Aug 27 '14 at 01:56
  • @jimm101: There are a dozen Python MP3 encoder libraries. If this question is just asking someone to pick one, it should be closed, not answered, regardless of the bounty. – abarnert Aug 27 '14 at 11:35
  • 2
    @jimm101 can you give example, i tried pydub,pyffmpg,pyaudio. no option there to encode on fly. every library have option to write to file – open source guy Aug 27 '14 at 12:19
  • Although you can use file-io solutions to stream things in memory as if they were files, I would first try encoding the 1s chunks independently rather than streaming. You _may_ get some audible artifacts, but I doubt it. – mdurant Aug 27 '14 at 16:12
  • 1
    why not use zlib to compress the data. I know the preferred option is to encode to mp3 and thus compressing the audio more efficiently, but if you are looking for a quick answer which would work as a stream, I would use zlib. – SatA Aug 28 '14 at 19:49
  • I you're OK with a stand-alone solution (without using Python), you can try live streaming with IceS and Icecast (http://www.icecast.org/ices.php/). – Matthias Aug 29 '14 at 08:30
  • 1
    @selbie i contacted author of pymedia . he said it is not possible to do this with that library – open source guy Sep 02 '14 at 06:14
  • @messi fan Can you show how you finally made it work ? Can you show your wav_to_mp3(data) method ? – Waroulolz Sep 16 '14 at 16:47

2 Answers2

2

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?

1

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

Bogdan L
  • 71
  • 4
  • Python API docs (as opposed to command line) are at http://audiotools.sourceforge.net/programming/index.html – Dragon Apr 20 '15 at 15:12