Hi :) I am programming an independent multi-metronome and need to run the funciont beat() on a while loop for every instances of my class Metronome(), starting at the same time.

import time
import mido
from numpy import interp
from IPython.display import clear_output

inport = mido.open_input() #my MIDI is a KORG nanoKontrol 2

class Metronome(): 
    
    #A dict for my MIDI controller
    metronomes_controls = {
          'inst_number':[n + 0 for n in range(0,7)], 
          'vol_slide':[n + 0 for n in range(0,7)],
          'tempo_knob': [n + 16 for n in range(0,7)],
          'play_stop': [n + 32 for n in range(0,7)],
          'sync_selected': [n + 48 for n in range(0,7)],
          'tap_button': [n + 64 for n in range(0,7)]} 
    

    def __init__(self, inport, inst_number = 0, tempo=60, active=True,
                 on_off_list = ['ON','OFF','ON','OFF'], selector = 0):
        self.inport = inport
        self.inst_number = inst_number
        self.tempo = tempo
        self.active = active
        self.on_off_list = on_off_list #The controller is not so precise
        self.selector = selector
        self.controls = dict(zip(list(metronomes_controls.keys()), 
                        [val[self.inst_number] for val in metronomes_controls.values()]))
    
    def beat(self):
        if self.active == True:
            print('Tick', self.tempo) #this is going to be a sound
            time.sleep(int(round(60/self.tempo)))
            clear_output()
            self.update()
        else:
            self.update()    

    def update(self):
        msg = self.inport.receive(block=False)

        for msg in inport.iter_pending():
            if msg.control == self.controls['tempo_knob']:
                self.tempo = int(interp(msg.value,[0,127],[20,99]))

            if msg.control == self.controls['play_stop']:
                self.selector += 1
                if self.selector >3:
                    self.selector = 0
                if 'ON' in self.on_off_list[self.selector]:
                    print('ON')
                    self.active = True 
                if 'OFF' in self.on_off_list[self.selector]:
                    print('OFF')
                    self.active = False


#Creating two instances of my class
m0 = Metronome(inport = inport, inst_number = 0)
m1 = Metronome(inport = inport,inst_number = 1)
m2 = Metronome(inport = inport,inst_number = 1)  
m3 = Metronome(inport = inport,inst_number = 1)            


#They run on a while loop. All should start at the same time.
while True:
    m0.beat()
    m1.beat()
    m2.beat()
    m3.beat()

I read about threading but it seems to create some starting delay. Then I got into barries, but I couldn't imagine how to implement it :/ or maybe should I try something with multiprocess? I got really lost! Any advice is highly appreciated

Thanks for the help!!!

1

There are 1 answers

1
Louis Lac On BEST ANSWER

Create one thread per metronome and start them at (almost) the same time:

from threading import Thread


# Previous code...
...


def create_thread(inport, inst_number):
  # The function the thread is going to execute
  def inner():
    m = Metronome(inport, inst_number)
    m.beat()

  return Thread(target=inner)


if __name__ == "__main__":
  inport = mido.open_input()

  # Create the threads
  threads = [
    create_thread(inport, i) for i in (0, 1, 1, 1)
  ]

  # Start them at (almost) the same time
  for t in threads:
    t.start()

  # Wait for them to finish execution
  for t in threads:
    t.join()