How can I play wav binary data with pyaudio?

Viewed 2600

I am getting audio binary data from a web request, and I would like to pass that data and play it over the speakers immediately. How can I do this? Currently I the errors I get are relating to the properties of the .wav, such as number of channels not specified, samplewidth etc. I dont want to write it to a .wav file, and then load the file and play it. I would like to go from binary audio data -> to playing on speakers, maybe I am going in the wrong direction to solve this but any help would be appreciated. I am assuming there has to be some way to pipe this data into an audio stream and I just dont know how to do that.

I try opening the binary data, writing a .wav object to a variable and then opening an audio stream from that obj. I have also tried explicitly setting the fields in the audio stream to avail.

wav = wave.open(response.audio_content, 'wb')
pa = pyaudio.PyAudio()
audio_stream = pa.open(format=pa.get_format_from_width(wav.getsampwidth()),
                       channels=wav.getnchannels(),
                       rate=wav.getframerate(),
                       output=True)

data = wav.readframes(CHUNK)

while data != '':
    audio_stream.write(data)
    data = wav.readframes(CHUNK)

audio_stream.stop_stream()
audio_stream.close()
pa.terminate()

Here is an example of the channel error:

wave.Error: # channels not specified
2 Answers

Here's a simpler solution using only pyaudio and builtins:

import pyaudio
import wave
import io

# response.audio_content is a byte string
with wave.open(io.BytesIO(response.audio_content), 'rb') as f:
    width = f.getsampwidth()
    channels = f.getnchannels()
    rate = f.getframerate()
pa = pyaudio.PyAudio()
pa_stream = pa.open(
    format=pyaudio.get_format_from_width(width),
    channels=channels,
    rate=rate,
    output=True
)
pa_stream.write(response.audio_content)

You cannot "open" the byte string directly; io.BytesIO provides a file-like wrapper so wave.open can work with the byte string.

Try this:

import pyaudio
import wave
import numpy as np
import sounddevice as sd

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

print("* recording")

frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    # read data-chunks in strings
    data = stream.read(CHUNK)
    #frames.append(data)
    # change the format to numpy int16
    frames.append(np.fromstring(data,dtype=np.int16))
#newS = np.fromstring(frames,dtype=np.int16)
print("* done recording")
sound = np.array(frames)
sound = sound.flatten()
sd.play(sound,44100*2)

stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
Related