1

I'm trying to wrtie a metronome script which gives me audio feedback as well as sending MIDI messages to a synthesizer. I use Python 2.7.5+ and Pygame 1.9.1.release on Linux Mint 16. I'm okay with the MIDI part. What troubles me is the timing.

First of all, here is basically what I do:

import time
import pygame.mixer

pygame.mixer.init()
sound = pygame.mixer.Sound('click.wav')

interval = 0.5 #should be equivalent to 120 bpm

t0 = time.time()

while True: #infinite loop, use keyboard interrupt Ctrl-C
    if time.time() - t0 >= interval:
        sound.play()
        t0 = time.time()

However, this is fairly unstable and impossible to play an instrument to.

I also looked into time.clock():

import time
import pygame.mixer

pygame.mixer.init()
sound = pygame.mixer.Sound('click.wav')

interval = 0.5 #should be equivalent to 120 bpm

t0 = time.clock()

while True:
    if time.clock() - t0 >= interval:
        sound.play()
        t0 = time.clock()

...as well as pygame.time.Clock(), specifically pygame.time.Clock.tick_busy_loop(), which supposedly provides better precision by eating more into the processor:

import pygame.time
import pygame.mixer

pygame.mixer.init()
#pygame.time has no init function

sound = pygame.mixer.Sound('click.wav')
clock = pygame.time.Clock()

interval = 0.5 #should be equivalent to 120 bpm

time_passed = 0
clock.tick_busy_loop()

while True: #infinite loop, use keyboard interrupt Ctrl-C
    time_passed += clock.tick_busy_loop()
    if time_passed >= interval:
        sound.play()
        time_passed = 0

All of which yielded the same issues. Although, I have to admit, when I tried these minimal examples, the pygame.time solution was very stable. Problem here is that in my actual script I do some calculation (like incrementing counters and sending MIDI messages) within the while loop which appear to influence the time the loop takes and the click start to stutter and stumble.

I looked at this answer, but I didn't quite get the idea of the midi files. May that would help here? Some explanation would be most helpful.

Moreover, I tried to put

import os
os.nice(-19)

at the beginning, to gain a higher process priority, but no improvement was noticeable.

I wonder how I can achive a stable metronome like common DAWs like Cubase, Ableton and Logic do. I read about the approach to generate or prerecord audio samples of the tempo in question and chain those samples to achieve a higher prescision, but I would really like to avoid this approach as it seems very laborious.

Is there some clever way I may use the stable pygame.time variant and do the processing elsewhere?

If it is of any use: here is the actual while loop I'm running:

bpm = 80.0
beats_in_one_second = bpm/60.0
midi_send_interval = beats_in_one_second/24.0 #MIDI convention

playing = True
player.write_short(250) #start
frame_count = 0
quarter_count = 0
time_0 = time.time()

while playing:
    if time.time() - time_0 >= midi_send_interval:
        player.write_short(248) #clock tick
        time_0 = time.time()
        frame_count += 1
        if frame_count == 24:
            sound.play()
            quarter_count += 1
            frame_count = 0
            if quarter_count == 16:
                playing = False

player.write_short(252) #stop

The problem is that the MIDI standard demands for 24 messages per quarter note, which increases the demand for precision significantly.

Community
  • 1
  • 1
ravenfrost
  • 143
  • 1
  • 1
  • 8
  • Why aren't you using [timestamps](http://www.pygame.org/docs/ref/midi.html)? – CL. Feb 15 '15 at 21:27
  • You should probably use a different approach with MIDI but if all you need is to call `sound.play()` periodically (say no more than 60 times per second) then you could [try the code example from my answer to related question](http://stackoverflow.com/a/26605840/4279). Here's a couple of related code examples: a [metronome skeleton in tkinter](http://stackoverflow.com/a/26609843/4279) and [scripts that generate data at a constant rate](http://stackoverflow.com/questions/26595419/trying-to-simulate-constant-byte-rate-confusion-with-time-sleep-results#comment41808839_26595419). – jfs Feb 15 '15 at 21:53
  • Could you guys elabroate on the MIDI solution? I don't know how to use those timestamps and I can't find a decent tutorial online. Is it possible to have the sound triggered and at the same time send a MIDI signal with the approach you suggest? – ravenfrost Feb 16 '15 at 15:15

0 Answers0