Hello Fons,
many thanks for the suggestions and your code. Taking all that into account I
agree with your assessment and leave the metronome in MIDI, though I will keep
the modified code around.
Btw. it was Python's prompt_toolkit library that made me turn from good old
c++ to Python. From all the examples I've seen it is a fantastic library for
shell/fullscreen terminal UIs. And possibly a few minor solutions I did in
Python. It is enticing to use it. :)
Best wishes and many thanks once again for valuable code and advice,
Jeanette
Dec 12 2024, Fons Adriaensen has written:
  On Wed, Dec 11, 2024 at 11:42:57AM +0100, Jeanette C.
wrote:
  Is there a better, hopefully simple, way to play
these clicks? 
 If you're building a sequencer anyway, just output the metronome
 clicks as one additional track. i.e. just a repeating sequence
 of note on/off events to be rendered by a synth.
 Or you could use the JackSignal class from zita-jacktools
 to play the ticks. Giant overkill but it will work.
 JackSignal allows to play and/or capture Jack audio from/to numpy
 buffers. It's meant to do automated audio measurements with all
 signals generated and/or analysed in Python.
 The example below receives MIDI clock (using the Aseqmidi class)
 and outputs a tick every quarter note. In this case the two tick
 sounds are generated in Python, but you could as well input them
 from files (using zita-audiotools).
 When S.process () is called, output will start at the next Jack
 period boundary, so with -p 256 you'd have +/- 2.7 ms jitter.
 Note that if the metronome is a separate program (like the one
 below), it also needs to handle song position messages to be in
 sync with the sequencer.
 #!/usr/bin/python
 from zita_alsamidi.aseqmidi import *
 from zita_jacktools.jacksignal import *
 from time import sleep
 from math import pi, sin
 import numpy as np
 # Play sound
 #
 def playsound ():
    global K  # Beat counter
    if S.get_state () != JackSignal.SILENCE:
        return  # Still playing previous one...
    # Enable one of the two waveforms.
    if K == 0:
        S.set_output_gain (0, 1.0)
        S.set_output_gain (1, 0.0)
    else:
        S.set_output_gain (0, 0.0)
        S.set_output_gain (1, 1.0)
    # Start playback.
    S.process ()
    # Handle 4/4 signature.
    K += 1
    if K == 4: K = 0
 # Midi message handler
 #
 def handler (args):
    global C  # Clock counter
    if args [1][0] == 36: # 36 = SND_SEQ_EVENT_CLOCK
        C += 1
        if C == 24:
            playsound ()
            C = 0
 # Generate a 'ping' waveform.
 #
 def genping (freq, rate, size):
    D = np.zeros ((size,), dtype = np.float32)
    w = 2 * pi * freq / rate
    a = 0.3
    m = pow (0.01, 1.0 / size)
    for i in range (size):
        D [i] = a * sin (i * w)
        a *= m
    return D
 S = JackSignal ("Metronome")
 if S.get_state () < 0:
    print ("Can't create JackSignal")
    exit (1)
 name, rate, frag = S.get_jack_info ()
 # Should be short enough so they don't overlap.
 size = int (0.15 * rate)
 D1 = genping (880, rate, size)
 D2 = genping (440, rate, size)
 S.create_output (0, 'out-1')
 S.create_output (1, 'out-2')
 S.silence ()
 # Connect Jack outputs...
 # S.connect_output (0, '...')
 # S.connect_output (1, '...')
 S.set_output_data (0, D1)
 S.set_output_data (1, D2)
 C = 0 # Clock message counter.
 K = 0 # Beat counter
 M = Aseqmidi ("Metronome", handler)
 sleep (10000)
 --
 FA
 _______________________________________________
 Linux-audio-dev mailing list -- linux-audio-dev(a)lists.linuxaudio.org
 To unsubscribe send an email to linux-audio-dev-leave(a)lists.linuxaudio.org
 
I used to think
I had the answers to everything
But now I know
... :) <3
(Britney Spears)