66#define PPQN 24
77#define QUEUE_TEMPO_PPQ 96
88
9+ // Event callback type - called when MIDI events are received
10+ // event_type: one of the SND_SEQ_EVENT_* constants
11+ // channel: MIDI channel (0-15)
12+ // param1, param2, param3: event-specific parameters
13+ typedef void (* midi_event_callback_t )(int event_type , int channel , int param1 , int param2 , int param3 );
14+
915// Global handles
1016static snd_seq_t * seq_handle = NULL ;
1117static int port_id = -1 ;
@@ -16,9 +22,21 @@ static char virtualMidiPortName[] = "LinkBridge MIDI Clock";
1622 previously queued events) */
1723static snd_seq_tick_time_t max_scheduled_tick = 0 ;
1824
25+ // Global callback function pointer
26+ static midi_event_callback_t event_callback = NULL ;
27+
1928// Forward declarations
2029static int has_write_capability (snd_seq_t * seq , int client , int port );
21- // static int has_read_capability(snd_seq_t *seq, int client, int port);
30+ static int has_read_capability (snd_seq_t * seq , int client , int port );
31+
32+ // Register a callback function for MIDI events
33+ // Call this from Python to set up the event handler
34+ void midi_register_event_callback (midi_event_callback_t callback ) {
35+ event_callback = callback ;
36+ if (callback != NULL ) {
37+ printf ("[C] MIDI event callback registered\n" );
38+ }
39+ }
2240
2341// Connect to all existing MIDI ports on startup (except ports 0 and 14)
2442static void midi_connect_all_ports (void ) {
@@ -55,14 +73,14 @@ static void midi_connect_all_ports(void) {
5573
5674
5775 if (has_write_capability (seq_handle , client , port )) {
58- printf ("Connecting %s -> %d:%d\n" , virtualMidiPortName , client , port );
76+ printf ("[C] Connecting %s -> %d:%d\n" , virtualMidiPortName , client , port );
5977 snd_seq_connect_to (seq_handle , port_id , client , port );
6078 }
6179
62- // if (has_read_capability(seq_handle, client, port)) {
63- // printf("Connecting %d:%d -> %s\n", client, port, virtualMidiPortName);
64- // snd_seq_connect_from(seq_handle, port_id, client, port);
65- // }
80+ if (has_read_capability (seq_handle , client , port )) {
81+ printf ("[C] Connecting %d:%d -> %s\n" , client , port , virtualMidiPortName );
82+ snd_seq_connect_from (seq_handle , port_id , client , port );
83+ }
6684 }
6785 }
6886}
@@ -72,11 +90,11 @@ static void midi_connect_all_ports(void) {
7290int midi_init (void ) {
7391 int err ;
7492 snd_seq_queue_tempo_t * queue_tempo ;
75-
76- // Open ALSA sequencer
93+ printf ( "[C] Initializing MIDI subsystem...\n" );
94+ // Open ALSA sequencer
7795 err = snd_seq_open (& seq_handle , "default" , SND_SEQ_OPEN_DUPLEX , 0 );
7896 if (err < 0 ) {
79- fprintf (stderr , "Error opening ALSA sequencer: %s\n" , snd_strerror (err ));
97+ fprintf (stderr , "[C] Error opening ALSA sequencer: %s\n" , snd_strerror (err ));
8098 return -1 ;
8199 }
82100
@@ -91,7 +109,7 @@ int midi_init(void) {
91109 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE ,
92110 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION );
93111 if (port_id < 0 ) {
94- fprintf (stderr , "Error creating port: %s\n" , snd_strerror (port_id ));
112+ fprintf (stderr , "[C] Error creating port: %s\n" , snd_strerror (port_id ));
95113 snd_seq_close (seq_handle );
96114 seq_handle = NULL ;
97115 return -1 ;
@@ -103,17 +121,17 @@ int midi_init(void) {
103121 port_id ,
104122 SND_SEQ_CLIENT_SYSTEM ,
105123 SND_SEQ_PORT_SYSTEM_ANNOUNCE )) < 0 ) {
106- fprintf (stderr , "Cannot subscribe to system announce: %s\n" ,
124+ fprintf (stderr , "[C] Cannot subscribe to system announce: %s\n" ,
107125 snd_strerror (err ));
108126 exit (1 );
109127 }
110128
111- printf ("Listening for PORT_START events...\n" );
129+ printf ("[C] Listening for PORT_START events...\n" );
112130
113131 // Create queue
114132 queue_id = snd_seq_alloc_queue (seq_handle );
115133 if (queue_id < 0 ) {
116- fprintf (stderr , "Error creating queue: %s\n" , snd_strerror (queue_id ));
134+ fprintf (stderr , "[C] Error creating queue: %s\n" , snd_strerror (queue_id ));
117135 snd_seq_close (seq_handle );
118136 seq_handle = NULL ;
119137 return -1 ;
@@ -126,7 +144,7 @@ int midi_init(void) {
126144 snd_seq_queue_tempo_set_ppq (queue_tempo , QUEUE_TEMPO_PPQ );
127145 err = snd_seq_set_queue_tempo (seq_handle , queue_id , queue_tempo );
128146 if (err < 0 ) {
129- fprintf (stderr , "Error setting queue tempo: %s\n" , snd_strerror (err ));
147+ fprintf (stderr , "[C] Error setting queue tempo: %s\n" , snd_strerror (err ));
130148 snd_seq_free_queue (seq_handle , queue_id );
131149 snd_seq_close (seq_handle );
132150 seq_handle = NULL ;
@@ -150,11 +168,11 @@ int midi_init(void) {
150168// Returns 0 on success, -1 on error
151169int midi_set_tempo (int bpm10 ) {
152170 if (seq_handle == NULL ) {
153- fprintf (stderr , "Error: MIDI not initialized\n" );
171+ fprintf (stderr , "[C] Error: MIDI not initialized\n" );
154172 return -1 ;
155173 }
156174 if (bpm10 <= 0 ) {
157- fprintf (stderr , "Error: invalid BPM (tenths) %d\n" , bpm10 );
175+ fprintf (stderr , "[C] Error: invalid BPM (tenths) %d\n" , bpm10 );
158176 return -1 ;
159177 }
160178
@@ -187,7 +205,7 @@ int midi_set_tempo(int bpm10) {
187205
188206 int err = snd_seq_event_output (seq_handle , & ev );
189207 if (err < 0 ) {
190- fprintf (stderr , "Error enqueuing tempo event: %s\n" , snd_strerror (err ));
208+ fprintf (stderr , "[C] Error enqueuing tempo event: %s\n" , snd_strerror (err ));
191209 return -1 ;
192210 }
193211 snd_seq_drain_output (seq_handle );
@@ -201,7 +219,7 @@ int midi_set_tempo(int bpm10) {
201219// Returns 0 on success, -1 on error
202220int midi_send_start (void ) {
203221 if (seq_handle == NULL ) {
204- fprintf (stderr , "Error: MIDI not initialized\n" );
222+ fprintf (stderr , "[C] Error: MIDI not initialized\n" );
205223 return -1 ;
206224 }
207225
@@ -230,7 +248,7 @@ int midi_send_start(void) {
230248// Returns 0 on success, -1 on error
231249int midi_send_clock (void ) {
232250 if (seq_handle == NULL ) {
233- fprintf (stderr , "Error: MIDI not initialized\n" );
251+ fprintf (stderr , "[C] Error: MIDI not initialized\n" );
234252 return -1 ;
235253 }
236254
@@ -255,7 +273,7 @@ int midi_send_clock(void) {
255273// Returns 0 on success, -1 on error
256274int midi_send_stop (void ) {
257275 if (seq_handle == NULL ) {
258- fprintf (stderr , "Error: MIDI not initialized\n" );
276+ fprintf (stderr , "[C] Error: MIDI not initialized\n" );
259277 return -1 ;
260278 }
261279
@@ -325,25 +343,25 @@ static int has_write_capability(snd_seq_t *seq, int client, int port)
325343 (caps & SND_SEQ_PORT_CAP_SUBS_WRITE );
326344}
327345
328- // // Check if the MIDI port is able to receive MIDI messages
329- // static int has_read_capability(snd_seq_t *seq, int client, int port)
330- // {
331- // snd_seq_port_info_t *pinfo;
332- // snd_seq_port_info_alloca(&pinfo);
346+ // Check if the MIDI port is able to receive MIDI messages
347+ static int has_read_capability (snd_seq_t * seq , int client , int port )
348+ {
349+ snd_seq_port_info_t * pinfo ;
350+ snd_seq_port_info_alloca (& pinfo );
333351
334- // if (snd_seq_get_any_port_info(seq, client, port, pinfo) < 0)
335- // return 0;
352+ if (snd_seq_get_any_port_info (seq , client , port , pinfo ) < 0 )
353+ return 0 ;
336354
337- // unsigned int caps = snd_seq_port_info_get_capability(pinfo);
355+ unsigned int caps = snd_seq_port_info_get_capability (pinfo );
338356
339- // return (caps & SND_SEQ_PORT_CAP_READ) &&
340- // (caps & SND_SEQ_PORT_CAP_SUBS_READ);
341- // }
357+ return (caps & SND_SEQ_PORT_CAP_READ ) &&
358+ (caps & SND_SEQ_PORT_CAP_SUBS_READ );
359+ }
342360
343361// Poll ALSA sequencer events for PORT_START when a new MIDI device is connected
344362int midi_read_events (void ) {
345363 if (seq_handle == NULL ) {
346- fprintf (stderr , "Error: MIDI not initialized\n" );
364+ fprintf (stderr , "[C] Error: MIDI not initialized\n" );
347365 return -1 ;
348366 }
349367
@@ -362,11 +380,11 @@ int midi_read_events(void) {
362380 if (client == snd_seq_client_id (seq_handle ))
363381 continue ;
364382
365- printf ("New port: %d:%d\n" , client , port );
383+ printf ("[C] New port: %d:%d\n" , client , port );
366384
367385 if (has_write_capability (seq_handle , client , port )) {
368386
369- printf ("Connecting %s -> %d:%d\n" , virtualMidiPortName , client , port );
387+ printf ("[C] Connecting %s -> %d:%d\n" , virtualMidiPortName , client , port );
370388
371389 snd_seq_connect_to (
372390 seq_handle ,
@@ -375,16 +393,79 @@ int midi_read_events(void) {
375393 port );
376394 }
377395
378- // if (has_read_capability(seq_handle, client, port)) {
396+ if (has_read_capability (seq_handle , client , port )) {
379397
380- // printf("Connecting %d:%d -> %s\n", client, port,virtualMidiPortName);
398+ printf ("[C] Connecting %d:%d -> %s\n" , client , port ,virtualMidiPortName );
381399
382- // snd_seq_connect_from(
383- // seq_handle,
384- // port_id,
385- // client,
386- // port);
387- // }
400+ snd_seq_connect_from (
401+ seq_handle ,
402+ port_id ,
403+ client ,
404+ port );
405+ }
406+ }
407+ else if (event -> type == SND_SEQ_EVENT_NOTEON ) {
408+ if (event_callback != NULL ) {
409+ event_callback (SND_SEQ_EVENT_NOTEON ,
410+ event -> data .note .channel ,
411+ event -> data .note .note ,
412+ event -> data .note .velocity ,
413+ 0 );
414+ }
415+ }
416+ else if (event -> type == SND_SEQ_EVENT_NOTEOFF ) {
417+ if (event_callback != NULL ) {
418+ event_callback (SND_SEQ_EVENT_NOTEOFF ,
419+ event -> data .note .channel ,
420+ event -> data .note .note ,
421+ event -> data .note .velocity ,
422+ 0 );
423+ }
424+ }
425+ else if (event -> type == SND_SEQ_EVENT_CONTROLLER ) {
426+ if (event_callback != NULL ) {
427+ event_callback (SND_SEQ_EVENT_CONTROLLER ,
428+ event -> data .control .channel ,
429+ event -> data .control .param ,
430+ event -> data .control .value ,
431+ 0 );
432+ }
433+ }
434+ else if (event -> type == SND_SEQ_EVENT_PGMCHANGE ) {
435+ if (event_callback != NULL ) {
436+ event_callback (SND_SEQ_EVENT_PGMCHANGE ,
437+ event -> data .control .channel ,
438+ event -> data .control .value ,
439+ 0 ,
440+ 0 );
441+ }
442+ }
443+ else if (event -> type == SND_SEQ_EVENT_PITCHBEND ) {
444+ if (event_callback != NULL ) {
445+ event_callback (SND_SEQ_EVENT_PITCHBEND ,
446+ event -> data .control .channel ,
447+ event -> data .control .value ,
448+ 0 ,
449+ 0 );
450+ }
451+ }
452+ else if (event -> type == SND_SEQ_EVENT_CHANPRESS ) {
453+ if (event_callback != NULL ) {
454+ event_callback (SND_SEQ_EVENT_CHANPRESS ,
455+ event -> data .control .channel ,
456+ event -> data .control .value ,
457+ 0 ,
458+ 0 );
459+ }
460+ }
461+ else if (event -> type == SND_SEQ_EVENT_KEYPRESS ) {
462+ if (event_callback != NULL ) {
463+ event_callback (SND_SEQ_EVENT_KEYPRESS ,
464+ event -> data .note .channel ,
465+ event -> data .note .note ,
466+ event -> data .note .velocity ,
467+ 0 );
468+ }
388469 }
389470 count ++ ;
390471 }
0 commit comments