12

Is there a way to read frames of .wav format binary data? I am streaming .wav binary to a python server which I want to be able to read and use from processing by pyaudio, which complains about the frame size.

Since I cannot use wave.open('filename.wav'), because I already have the binary data being streamed, is there a way to read the binary data so that I can use the readframes method in the wave python library?

EDIT: I tried streaming readframes from the client side, however pyaudio gives an error that the bytes are not in .wav format. It would be ideal however if I can get this done on the server.

  • Do you mean `getnframes` or `readframes`? I don't see a `getframes` in the documentation. –  May 10 '17 at 18:26
  • readframes, sorry for the confusion. I tried streaming readframes from the client side, however pyaudio gives an error that the bytes are not in .wav format. It would be ideal however if I can get this done on the server side. –  May 10 '17 at 18:28
  • You could create an object with all of the `file` methods that are needed by `wave` and pass it to `wave.open`: "If file is a string, open the file by that name, otherwise treat it as a seekable file-like object." –  May 10 '17 at 19:48
  • What is in your binary stream? Is it the whole content of the WAV file including headers, or is it just the plain audio samples? If latter, you can directly pass the bytes to PyAudio, you just have to make sure you pass the right amount of data and that you tell PyAudio the right data type and number of channels. BTW, PyAudio doesn't know about WAV files, and I'm pretty sure it doesn't talk about them in its error messages. You should provide the actual error message! – Matthias May 11 '17 at 10:21

3 Answers3

3
from scipy.io import wavfile
fs, data = wavfile.read('your file path')
Gaurav Kulkarni
  • 190
  • 1
  • 14
3

As @Vishesh Mangla pointed out use librosa a great library for audio signals

import librosa

sr = librosa.get_samplerate('/path/to/file.wav')

# Set the frame parameters to be equivalent to the librosa defaults
# in the file's native sampling rate
frame_length = (2048 * sr) // 22050
hop_length = (512 * sr) // 22050

# Stream the data, working on 128 frames at a time
stream = librosa.stream('path/to/file.wav',
                        block_length=128,
                        frame_length=frame_length,
                        hop_length=hop_length)

chromas = []
for y in stream:
   chroma_block = librosa.feature.chroma_stft(y=y, sr=sr,
                                              n_fft=frame_length,
                                              hop_length=hop_length,
                                              center=False)
   chromas.append(chromas)

In this example, each audio fragment y will consist of 128 frames worth of samples, or more specifically, len(y) == frame_length + (block_length - 1) * hop_length. Each fragment y will overlap with the subsequent fragment by frame_length - hop_length samples, which ensures that stream processing will provide equivalent results to if the entire sequence was processed in one step (assuming padding / centering is disabled).

For more details about the streaming interface, refer to librosa.core.stream.

Yash Gupta
  • 331
  • 2
  • 8
0

Answering to my own bounty, there is an elegant solution that I borrow from this more general context relating to getting a virtual file object.

import io
audio = wave.open(io.BytesIO(bytes))

This will enable all of wave's API, at least the subset I use for the same scenario as the original question, for a python bytes object. For example with the audio variable from above you can now:

format=self.audio.get_format_from_width(
   audio.getsampwidth()),
   channels=audio.getnchannels(),
   rate=audio.getframerate(),
   frames_per_buffer=self.chunk_size,
   output=True)

Since it seems the wave library does not directly support anything but a disk file object, this is a nice workaround and at least it hinges on a standard python library (io) that provides just what we need to bridge the gap in the API.

matanster
  • 15,072
  • 19
  • 88
  • 167