Last active
January 2, 2024 17:21
-
-
Save rareyman/663a55e277e47724d926535092b46e37 to your computer and use it in GitHub Desktop.
Running this script will start a tempo and broadcast it to the Arturia KeyStep 37, then wait for you to enter "s" to stop the clock. Example: `python midi-tempo-broadcast.py 125` to set BPM to 125.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# MIDI Tempo Broadcast | |
# Ross A. Reyman • soundcloud.com/r89music | |
# This script broadcasts a MIDI clock signal to hardware devices. | |
# | |
# prerequisites: | |
# - Python 3+ | |
# - install Mido `pip install mido` | |
# | |
# Run this script with: | |
# `python midi-tempo-broadcast.py` OR | |
# `python midi-tempo-broadcast.py <bpm>` | |
# where <bpm> is the tempo in beats per minute. | |
# | |
# The default tempo is 125 BPM. | |
import mido | |
import time | |
import threading | |
import argparse | |
import sys | |
# Define constants: the name of the MIDI device and the initial tempo | |
DEVICE_NAME = 'Arturia KeyStep 37' | |
INITIAL_TEMPO = 125 | |
# Check if the device is available | |
if DEVICE_NAME not in mido.get_output_names(): | |
print(f"\033[91mThe device '{DEVICE_NAME}' is not available. Check your MIDI connections.\033[0m") | |
sys.exit(1) | |
def start_midi_clock(bpm, stop_event, current_bpm): | |
# Calculate the delay between clock messages for the given tempo | |
delay = 60.0 / bpm / 24 | |
# Open the output port | |
with mido.open_output(DEVICE_NAME) as outport: | |
# Send MIDI clock messages continually | |
while not stop_event.is_set(): | |
start_time = time.perf_counter() | |
# Check if the tempo has changed | |
if bpm != current_bpm[0]: | |
bpm = current_bpm[0] | |
delay = 60.0 / bpm / 24 | |
# Send a MIDI clock message | |
msg = mido.Message('clock') | |
outport.send(msg) | |
# Calculate the actual delay taking into account the time spent sending the message | |
actual_delay = delay - (time.perf_counter() - start_time) | |
actual_delay = max(0, actual_delay) # Ensure the delay is not negative | |
# Sleep for the calculated delay using time.perf_counter | |
while time.perf_counter() - start_time < actual_delay: | |
pass | |
def wait_for_stop_command(stop_event, current_bpm): | |
# Wait for user input in a loop | |
while True: | |
command = input("\033[2mEnter 's' to stop the MIDI clock or a number to change the tempo: ") | |
if command.lower() == 's': | |
stop_event.set() | |
break | |
elif command.isdigit(): | |
current_bpm[0] = int(command) | |
print(f"\033[2mThe current tempo is now: \033[92m{current_bpm[0]} BPM.\033[0m") | |
# Create a stop event | |
stop_event = threading.Event() | |
# Parse command-line arguments | |
parser = argparse.ArgumentParser(description='Start a MIDI clock with a specified tempo.') | |
parser.add_argument('bpm', type=int, nargs='?', default=INITIAL_TEMPO, help='The tempo in beats per minute.') | |
args = parser.parse_args() | |
# Print the initial tempo | |
print(f"\033####################################################") | |
print(f"\033[2mThe initial tempo is: \033[92m{args.bpm} BPM.\033[0m") | |
# Create a shared variable for the current BPM | |
current_bpm = [args.bpm] # Default BPM | |
# Start the MIDI clock at the specified BPM in a new thread | |
clock_thread = threading.Thread(target=start_midi_clock, args=(args.bpm, stop_event, current_bpm)) | |
clock_thread.start() | |
# Start the command listener in a new thread | |
command_thread = threading.Thread(target=wait_for_stop_command, args=(stop_event, current_bpm)) | |
command_thread.start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment