Your explanation of alarm/4 explains a lot.
What you mean is pulse or beat (metronome, tempo in bpm), not Rhythm (which is the pattern of durations of notes/sounds/accents and rests). The problem with a fixed tempo is that it sounds artificial, because good musicians accelerate and slow down to express a musical phrase. This is currently an important (research) issue with computer music.
I’ve a new problem with MIDI input coming from some programs.
One example is FractMus 2000 ( FractMus ), a program that generates fractal music. I selected this program as it is much easier to install than the big software program I use on a daily basis. For this little program to work properly you need to install Coolsoft software including soundfonts. I’m willing to search for another program on my PC if I’m asking too much.
The following Prolog program doesn’t work as expected as soon as FractMus is running and the output port selected for FractMus is the same as the input port selected in the Prolog program. I expect it to wait for a Note On event to trigger something (writeln/1), but nothing happens.
:- use_module(library(janus)).
:- dynamic
inport/1.
open_in_port :-
py_call(mido:open_input('loopMIDI Port 1 1'), InPort), % substitute for your own input port name
asserta(inport(InPort)),
thread_create(input_from(InPort), _).
close_in_port :-
(retract(inport(InPort))
-> py_call(InPort:close())
; true
).
input_from(Port) :-
forall(repeat, receive_one(Port)).
receive_one(Port) :-
py_call(Port:receive(), Msg),
py_call(Msg:type, Type),
handle_midi_message(Type, Msg),
py_free(Msg).
handle_midi_message('note_on', _) :-
writeln(handle_midi_message('note_on')),
close_in_port.
:- open_in_port.
I tested this with a Python (mido) script and it works well with FractMus. FractMus also works with MIDI Tools and VSTs in a simple DAW like LMMS or standalone synths like Surge XT. It also works with non-Prolog scripts I wrote some time ago.
Here is the Python script that works fine :
def print_message(message): # callback function
print(message)
import mido
mido.open_input('loopMIDI Port 1 1', callback=print_message)
import time
while True: # keep the script running
print(".")
time.sleep(60) # sleep for 1 minute