Skip to content

Instantly share code, notes, and snippets.

@jean-emmanuel
Last active January 3, 2025 09:30
Show Gist options
  • Save jean-emmanuel/17ee54acde4862f2e33184639dbf7012 to your computer and use it in GitHub Desktop.
Save jean-emmanuel/17ee54acde4862f2e33184639dbf7012 to your computer and use it in GitHub Desktop.
"""
AutoNoteOff creates a virtual midi device with one midi input and one midi output
that passes through all events and outputs an additional note off immediately after every note on.
Usage:
python3 autonoteoff.py
(uses alsa midi, requires python3-pyalsa)
or
python3 autonoteoff.py --jack
(uses jack midi, require python3-jack-client)
"""
from sys import argv
from time import sleep
from signal import signal, SIGINT, SIGTERM
running = True
def stop():
global running
running = False
signal(SIGINT, lambda a,b: stop())
signal(SIGTERM, lambda a,b: stop())
print('Press Ctrl+C to quit...')
name = 'AutoNoteOff'
if '--jack' in argv:
import jack
import struct
client = jack.Client(name)
inport = client.midi_inports.register('input')
outport = client.midi_outports.register('output')
NOTEON = 0x9
@client.set_process_callback
def process(frames):
outport.clear_buffer()
for offset, indata in inport.incoming_midi_events():
# pass through
outport.write_midi_event(offset, indata)
if len(indata) == 3:
status, note, vel = struct.unpack('3B', indata)
if status >> 4 is NOTEON:
# output note off for every note on
outport.write_midi_event(offset, (status & 0b11101111, note, 0))
client.activate()
while running:
sleep(0.01)
client.deactivate()
else:
from pyalsa import alsaseq
server = alsaseq.Sequencer(clientname=name)
port_type = alsaseq.SEQ_PORT_TYPE_MIDI_GENERIC | alsaseq.SEQ_PORT_TYPE_APPLICATION
port_caps = (alsaseq.SEQ_PORT_CAP_WRITE | alsaseq.SEQ_PORT_CAP_SUBS_WRITE |
alsaseq.SEQ_PORT_CAP_READ | alsaseq.SEQ_PORT_CAP_SUBS_READ)
port_id = server.create_simple_port(name, port_type, port_caps)
while running:
for event in server.receive_events():
# pass through
copy = alsaseq.SeqEvent(event.type)
copy.set_data(event.get_data())
copy.source = (server.client_id, port_id)
server.output_event(copy)
if event.type is alsaseq.SEQ_EVENT_NOTEON:
# output note off for every note on
data = event.get_data()
noteoff = alsaseq.SeqEvent(alsaseq.SEQ_EVENT_NOTEOFF)
noteoff.set_data({'note.channel': data['note.channel'], 'note.note': data['note.note']})
noteoff.source = event.source
server.output_event(noteoff)
server.drain_output()
server.sync_output_queue()
sleep(0.01)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment