Question

I've been tinkering around with pyaudio for a while now, trying to reverse a simple wave file with no success.

In (my) theory I would only have to iterate from end to beginning through the file with every callback of pyaudio (1024 frames) fetch the audio data from the according index in the file, reverse the resulting string and play it.

Here is my code (only pyaudio callback and file handling, the rest is untouched from the example code):

import pyaudio
import wave
import time
import sys

if len(sys.argv) < 2:
    print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
    sys.exit(-1)


index = 40*1024
wf = wave.open(sys.argv[1], 'rb')
wf.setpos(index)
p = pyaudio.PyAudio()


def callback(in_data, frame_count, time_info, status):
    global index
    data = wf.readframes(frame_count)
    data = data[::-1]
    index-=1024
    wf.setpos(index)

    return (data, pyaudio.paContinue)

stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True,
                stream_callback=callback)


stream.start_stream()

while stream.is_active():
    time.sleep(0.1)

stream.stop_stream()
stream.close()
wf.close()

p.terminate()

I know this will crash when it reaches the file beginning, but it should play 40 × 1024 frames of reversed audio...

Was it helpful?

Solution

If the file you want reversed is small enough to fit in memory, your best bet would be loading it entirely and reversing the data, then streaming it:

import pyaudio
import wave

wavefile_name = 'wavefile.wav'
wf = wave.open(wavefile_name, 'rb')

p = pyaudio.PyAudio()
stream = p.open(format =
                p.get_format_from_width(wf.getsampwidth()),
                channels = wf.getnchannels(),
                rate = wf.getframerate(),
                output = True)

full_data = []
data = wf.readframes(1024)

while data:    
    full_data.append(data)
    data = wf.readframes(1024)

data = ''.join(full_data)[::-1]

for i in range(0, len(data), 1024):
    stream.write(data[i:i+1024])

However, if the file is too big to fit in memory, you'll need some way of reading the file backwards and feed into the sound system. That's an entirely different problem, because it involves some low level I/O programming to handle the backward reading.


Edit: after seeing your full code, I don't see any errors. The code runs properly in my computer, only to fail at the ending of the playback. However, you can do two things. First, you can go to the end of the wavefile to play the entire sound backwards, using this:

wf = wave.open(sys.argv[1], 'rb')
index = wf.getnframes() - 1024
wf.setpos(index)

Second, you have to modify the callback so it doesn't fail when the seek head goes beyond the beginning of the file:

def callback(in_data, frame_count, time_info, status):
    global index
    data = wf.readframes(frame_count)
    data = data[::-1]
    index-=1024
    if index < 0:
        return (data, pyaudio.paAbort)
    else:
        wf.setpos(max(index,0))
        return (data, pyaudio.paContinue)

Other than that, it works pretty ok.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top