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@lists.linuxaudio.org
To unsubscribe send an email to linux-audio-dev-le...@lists.linuxaudio.org


--
 * Website: http://juliencoder.de - for summer is a state of sound
 * Youtube: https://www.youtube.com/channel/UCMS4rfGrTwz8W7jhC1Jnv7g
 * Audiobombs: https://www.audiobombs.com/users/jeanette_c
 * GitHub: https://github.com/jeanette-c

I used to think
I had the answers to everything
But now I know
... :) <3
(Britney Spears)
_______________________________________________
Linux-audio-dev mailing list -- linux-audio-dev@lists.linuxaudio.org
To unsubscribe send an email to linux-audio-dev-le...@lists.linuxaudio.org

Reply via email to