0

I wanted to build a metronome in Python.

I have 2 sound files. Every second the first sound file should played and every fourth second the second sound file should played.

I found the twisted module, but couldn't figure out how to pass an parameter and iterate it. So I have the variable counter which I want to iterate, but it's not working with following code:

import simpleaudio as sa
from twisted.internet import task
from twisted.internet import reactor


def beat(bpm, counter):
    timeout = 60/bpm
    l = task.LoopingCall(play_beat, counter=counter)
    l.start(timeout)
    reactor.run()


def play_beat(counter):
    counter += 1
    print(counter) #This prints always 2, I am expecting an iteration like 2, 3, 4, 5, 6 ...
    if counter % 4 == 0:
        wave_obj = sa.WaveObject.from_wave_file("wav/beat_end.wav")
    else:
        wave_obj = sa.WaveObject.from_wave_file("wav/beat_start.wav")
    play_obj = wave_obj.play()
    play_obj.wait_done()
    pass

beat(60, 1)
  • When you say that it's not working, what does that mean? Does it throw an error? Does it work in an unexpected way? – sobek May 08 '18 at 20:53
  • Ah, sorry. The variable counter is starting always from 1. For example in the function "play_beat(counter)": If you make a print statement after counter+=1 it is always 2. But I am expecting that it should be 3, than 4, than 5, than 6 and so on –  May 08 '18 at 21:10
  • That's not really surprising, you are calling it from `beat()` with the argument `counter=counter` and you never update `counter` in the scope of `beat()`. `counter` is not a global variable. – sobek May 08 '18 at 21:14
  • 1
    oh, I am so dumb xD Thanks, it worked with the global variable! –  May 08 '18 at 21:23
  • 1
    My comment was meant to point out the error, not as a suggestion on how to fix it. Please have a look at https://stackoverflow.com/questions/19158339/why-are-global-variables-evil. In this context, Jean-Paul Calderones solution is far better. – sobek May 09 '18 at 04:59

1 Answers1

1

Here's another solution.

import simpleaudio as sa
from twisted.internet import task
from twisted.internet import reactor

def beat(bpm, wave_obj):
    timeout = 60.0 / bpm
    l = task.LoopingCall(play_beat, wave_obj)
    l.start(timeout)

def play_beat(wave_obj):
    wave_obj.play()

beat_start = sa.WaveObject.from_wave_file(...)
beat_end = sa.WaveObject.from_wave_file(...)

bpm = 60.0
bps = bpm / 60
reactor.callLater(0 / bps, beat, bpm / 4, beat_start)
reactor.callLater(1 / bps, beat, bpm / 4, beat_start)
reactor.callLater(2 / bps, beat, bpm / 4, beat_start)
reactor.callLater(3 / bps, beat, bpm / 4, beat_end)
reactor.run()

It may be noteworthy that I've removed the wait_done call. If you block the reactor thread like that, you can expect less reliable scheduling of time-based events.

Jean-Paul Calderone
  • 47,755
  • 6
  • 94
  • 122