0

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.

3
  • Why aren't you using timestamps? Commented Feb 15, 2015 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. Here's a couple of related code examples: a metronome skeleton in tkinter and scripts that generate data at a constant rate. Commented Feb 15, 2015 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? Commented Feb 16, 2015 at 15:15

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.