0

I am translating a metronome I wrote for Android into Python for classic Desktop machines.

Under Android, the buffer was 2s long and always filled wich means it was 2s-lag-proof. With Python and Pyaudio, I am doing something like (bar with 4 beats):

bar = 0
while self.isRunning:
    stream.write(beat[bar])
    print("beat {} was read".format(bar)
    bar += 1
    bar %= 4

And see:

*sound of beat[0] is played*
beat 0 was read
*sound of beat[1] is played*
beat 1 was read
...

PyAudio is waiting for his buffer to get empty before resuming. The "Blocking" way as I understand.

Instead, I would like to see (as in Android)

*sound of beat[0] is played*
beat 0 was read
beat 1 was read
beat 2 was read
*sound of beat[1] is played*
beat 3 was read
*sound of beat[2] is played*
beat 3 was read

With a BPM of 120 it means the stream always has a 2sec buffer ready to be read.

My question: Is there any to feed the buffer at the same time it is read? When I try my metronome in a virtual machine on crappy hardware host, the first beats are lagging wich worry me...

I am not sure the callback method would allow that and since I need to play specific sound depending on where I am in the structure (my metronome handle complex structure) and bar, it would be painful to implement.

funkygoby
  • 13
  • 6

1 Answers1

0

You could do that using Python threads (from the threading module), but it makes much more sense to just use the callback API (which will create a separate audio thread for you).

You can implement buffering with a Queue.

I just created an example program for the sounddevice module, which shows how this can be done.

Matthias
  • 4,524
  • 2
  • 31
  • 50
  • The Queue module seems perfect. I'll try it. I am already using Thread so the GUI doesn't freeze but as already mentioned, I can't see how the callback method could suit the program at all (I have to play differents sounds depending on the iteration in nested loops.) – funkygoby Nov 15 '16 at 10:06
  • I guess you can use either one (blocking or callback) in your case then. Just use whatever you are more comfortable with. Don't forget to check for buffer over/undderruns in either case! – Matthias Nov 15 '16 at 11:36
  • I tried the callback method but as the doc mentions, as soon as the callback is returning something, pyaudio will feed on it and play the buffer even if the last play is still ongoing. So instead of hearing distinct 'clicks', I hearded a 'Bbbrrrrrrrrrr'. I didn't manage to feed a Queue instance with my bytearray(). I guess the blocking method will do... – funkygoby Nov 18 '16 at 18:06