From df19d4ababd6cc738db93887bbda0b3a5c40a31e Mon Sep 17 00:00:00 2001 From: c vw Date: Thu, 6 May 2021 19:01:59 +0200 Subject: [PATCH] Support for multiple MIDI devices (so far only MacOS) --- alsa_midi.h | 2 +- mac_midi.c | 78 +++++++---------- midi.h | 4 +- midi2.c | 39 ++------- midi_menu.c | 239 +++++++++++++++++++++++++++++++--------------------- midi_menu.h | 1 - new_menu.c | 1 + radio.c | 14 +-- 8 files changed, 192 insertions(+), 186 deletions(-) diff --git a/alsa_midi.h b/alsa_midi.h index 02acc84..234a57e 100644 --- a/alsa_midi.h +++ b/alsa_midi.h @@ -1,6 +1,7 @@ typedef struct _midi_device { char *name; char *port; + int active; } MIDI_DEVICE; #define MAX_MIDI_DEVICES 10 @@ -9,4 +10,3 @@ extern MIDI_DEVICE midi_devices[MAX_MIDI_DEVICES]; extern int n_midi_devices; extern void get_midi_devices(); -extern int register_midi_device(char *myname); diff --git a/mac_midi.c b/mac_midi.c index 36570b5..fb45b29 100644 --- a/mac_midi.c +++ b/mac_midi.c @@ -37,20 +37,10 @@ #include "radio.h" #include "midi.h" #include "midi_menu.h" +#include "alsa_midi.h" #ifdef __APPLE__ -typedef struct _midi_device { - char *name; - char *port; -} MIDI_DEVICE; - -#define MAX_MIDI_DEVICES 10 - -MIDI_DEVICE midi_devices[MAX_MIDI_DEVICES]; -int n_midi_devices; - - /* * For MacOS, things are easy: * The OS takes care of everything, we only have to register a callback @@ -188,50 +178,43 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co } // j-loop through the list of packets } -void close_midi_device() { - fprintf(stderr,"%s\n",__FUNCTION__); +// +// store the ports and clients locally such that we +// can properly close a MIDI connection +// +static MIDIPortRef myMIDIports[MAX_MIDI_DEVICES]; +static MIDIClientRef myClients[MAX_MIDI_DEVICES]; + +void close_midi_device(index) { + fprintf(stderr,"%s index=\n",__FUNCTION__); + if (index < 0 || index > n_midi_devices) return; + // + // This should release the resources associated with the pending connection + // + MIDIPortDisconnectSource(myMIDIports[index], MIDIGetSource(index)); + MIDIPortDispose(myMIDIports[index]); + MIDIClientDispose(myClients[index]); + myMIDIports[index]=0; + myClients[index]=0; } -int register_midi_device(char *myname) { - int i; - CFStringRef pname; - char name[100]; - int FoundMIDIref=-1; - int mylen; +int register_midi_device(int index) { int ret; - configure=false; - if (myname == NULL) { - g_print("%s: myname is NULL\n", __FUNCTION__); - return -1; - } - mylen=strlen(myname); + g_print("%s: index=%d\n",__FUNCTION__,index); - g_print("%s: %s\n",__FUNCTION__,myname); -// -// Go through the list of MIDI devices and -// look whether the one we are looking for is there -// - for (i=0; i>>%s<<<\n", midi_devices[i].name); - } else { - fprintf(stderr,"MIDI device found BUT NOT SELECTED: >>>%s<<<\n", midi_devices[i].name); - } - } // -// If we found "our" device, register a callback routine +// Register a callback routine for the device // - if (FoundMIDIref >= 0) { - MIDIClientRef client = 0; - MIDIPortRef myMIDIport = 0; - //Create client - MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &client); - MIDIInputPortCreate(client, CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIport); - MIDIPortConnectSource(myMIDIport,MIDIGetSource(FoundMIDIref), NULL); + if (index >= 0 && index < n_midi_devices) { + myClients[index]=0; + myMIDIports[index] = 0; + //Create client and port, and connect + MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &myClients[index]); + MIDIInputPortCreate(myClients[index], CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIports[index]); + MIDIPortConnectSource(myMIDIports[index] ,MIDIGetSource(index), NULL); ret=0; } else { ret=-1; @@ -262,6 +245,9 @@ void get_midi_devices() { // if (strlen(name) == 0) strcpy(name,"NoPort"); g_print("%s: %s\n",__FUNCTION__,name); + if (midi_devices[n_midi_devices].name != NULL) { + g_free(midi_devices[n_midi_devices].name); + } midi_devices[n_midi_devices].name=g_new(gchar,strlen(name)+1); strcpy(midi_devices[n_midi_devices].name,name); n_midi_devices++; diff --git a/midi.h b/midi.h index 72cd62a..a617e0c 100644 --- a/midi.h +++ b/midi.h @@ -269,8 +269,8 @@ extern int midi_debug; // that have been defined. This is called upon startup by // Layer-2 through the function MIDIstartup. // -int register_midi_device(char *name); -void close_midi_device(); +int register_midi_device(int index); +void close_midi_device(int index); void configure_midi_device(gboolean state); // diff --git a/midi2.c b/midi2.c index e13b36e..202be32 100644 --- a/midi2.c +++ b/midi2.c @@ -25,9 +25,7 @@ #include "radio.h" #include "main.h" #include "midi.h" - -static double midi_startup_time; -static int midi_wait_startup=0; +#include "alsa_midi.h" struct desc *MidiCommandsTable[129]; @@ -46,20 +44,6 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) { //now=ts.tv_sec + 1E-9*ts.tv_nsec; //g_print("%s:%12.3f:EVENT=%d CHAN=%d NOTE=%d VAL=%d\n",__FUNCTION__,now,event,channel,note,val); - // - // the midi_wait_startup/midi_startup_time mechanism takes care that in the first - // second after registering a MIDI device, all incoming MIDI messages are just discarded. - // This has been introduced since sometimes "old" MIDI messages are lingering around in the - // the system and get delivered immediately after registering the MIDI device. - // The midi_wait_startup variable takes care that we do not check the clock again and again - // after the first second. - // - if (midi_wait_startup) { - clock_gettime(CLOCK_MONOTONIC, &ts); - now=ts.tv_sec + 1E-9*ts.tv_nsec; - if (now < midi_startup_time + 1.0) return; - midi_wait_startup=0; - } if (event == MIDI_EVENT_PITCH) { desc=MidiCommandsTable[128]; } else { @@ -278,7 +262,11 @@ static void keyword2action(char *s, enum MIDIaction *action, int *onoff) { int MIDIstop() { midi_enabled=FALSE; - close_midi_device(); + for (int i=0; i>>%s<<<\n",cp+7); - midi_wait_startup=1; - clock_gettime(CLOCK_MONOTONIC, &ts); - midi_startup_time=ts.tv_sec + 1E-9*ts.tv_nsec; - int result=register_midi_device(cp+7); - continue; // nothing more in this line - } chan=-1; // default: any channel t1=t3=t5=t7= t9=t11=128; // range that never occurs t2=t4=t6=t8=t10=t12=-1; // range that never occurs diff --git a/midi_menu.c b/midi_menu.c index 766e7dd..bea800a 100644 --- a/midi_menu.c +++ b/midi_menu.c @@ -75,9 +75,11 @@ static GtkWidget *newMin; static GtkWidget *newMax; static GtkWidget *newAction; static GtkWidget *configure_b; +static GtkWidget *any_b; static GtkWidget *add_b; static GtkWidget *update_b; static GtkWidget *delete_b; +static GtkWidget *device_b[MAX_MIDI_DEVICES]; static enum MIDIevent thisEvent=MIDI_EVENT_NONE; static int thisChannel; @@ -88,8 +90,7 @@ static int thisMax; static enum MIDItype thisType; static enum MIDIaction thisAction; -gchar *midi_device_name=NULL; -static gint device_index=-1; +static gboolean accept_any=FALSE; enum { UPDATE_NEW, @@ -121,38 +122,50 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d } static gboolean midi_enable_cb(GtkWidget *widget,gpointer data) { + int i; if(midi_enabled) { - close_midi_device(); + for (i=0; ichannel==thisChannel || cmd->channel==-1) && cmd->type==thisType && cmd->action==thisAction) { + if((cmd->channel==thisChannel) && cmd->type==thisType && cmd->action==thisAction) { g_print("%s: found cmd %p\n",__FUNCTION__,cmd); break; } @@ -313,13 +330,8 @@ static void save_cb(GtkWidget *widget,gpointer user_data) { NULL); chooser = GTK_FILE_CHOOSER (save_dialog); gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE); - if(midi_device_name==NULL) { - filename=g_new(gchar,10); - sprintf(filename,"midi.midi"); - } else { - filename=g_new(gchar,strlen(midi_device_name)+6); - sprintf(filename,"%s.midi",midi_device_name); - } + filename=g_new(gchar,10); + sprintf(filename,"midi.midi"); gtk_file_chooser_set_current_name(chooser,filename); res = gtk_dialog_run (GTK_DIALOG (save_dialog)); if(res==GTK_RESPONSE_ACCEPT) { @@ -350,13 +362,8 @@ static void load_cb(GtkWidget *widget,gpointer user_data) { GTK_RESPONSE_ACCEPT, NULL); chooser = GTK_FILE_CHOOSER (load_dialog); - if(midi_device_name==NULL) { - filename=g_new(gchar,10); - sprintf(filename,"midi.midi"); - } else { - filename=g_new(gchar,strlen(midi_device_name)+6); - sprintf(filename,"%s.midi",midi_device_name); - } + filename=g_new(gchar,10); + sprintf(filename,"midi.midi"); gtk_file_chooser_set_current_name(chooser,filename); res = gtk_dialog_run (GTK_DIALOG (load_dialog)); if(res==GTK_RESPONSE_ACCEPT) { @@ -389,13 +396,8 @@ static void load_original_cb(GtkWidget *widget,gpointer user_data) { GTK_RESPONSE_ACCEPT, NULL); chooser = GTK_FILE_CHOOSER (load_dialog); - if(midi_device_name==NULL) { - filename=g_new(gchar,10); - sprintf(filename,"midi.midi"); - } else { - filename=g_new(gchar,strlen(midi_device_name)+6); - sprintf(filename,"%s.midi",midi_device_name); - } + filename=g_new(gchar,10); + sprintf(filename,"midi.midi"); gtk_file_chooser_set_current_name(chooser,filename); res = gtk_dialog_run (GTK_DIALOG (load_dialog)); if(res==GTK_RESPONSE_ACCEPT) { @@ -674,6 +676,10 @@ void midi_menu(GtkWidget *parent) { int row=0; GtkCellRenderer *renderer; + g_print("MENU: ndev=%d\n", n_midi_devices); + for (i=0; i0) { - GtkWidget *devices_label=gtk_label_new("Select MIDI device: "); - gtk_grid_attach(GTK_GRID(grid),devices_label,col,row,3,1); - col+=3; - - GtkWidget *devices=gtk_combo_box_text_new(); - for(int i=0;i 0) { + GtkWidget *devices_label=gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(devices_label), "Select MIDI device(s)"); + gtk_label_set_justify(GTK_LABEL(devices_label),GTK_JUSTIFY_LEFT); + gtk_grid_attach(GTK_GRID(grid),devices_label,row,col,2,1); + col +=2; + for (i=0; i 0) { + col=0; + row++; } - gtk_grid_attach(GTK_GRID(grid),devices,col,row,6,1); - gtk_combo_box_set_active(GTK_COMBO_BOX(devices),device_index); - g_signal_connect(devices,"changed",G_CALLBACK(device_changed_cb),NULL); } else { - GtkWidget *message=gtk_label_new("No MIDI devices found!"); - gtk_grid_attach(GTK_GRID(grid),message,col,row,1,1); + GtkWidget *devices_label=gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(devices_label), "No MIDI devices found!"); + gtk_label_set_justify(GTK_LABEL(devices_label),GTK_JUSTIFY_LEFT); + gtk_grid_attach(GTK_GRID(grid),devices_label,col,row,3,1); + row=1; + col=0; } - row++; - col=0; + midi_enable_b=gtk_check_button_new_with_label("MIDI Enable"); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (midi_enable_b), midi_enabled); - gtk_grid_attach(GTK_GRID(grid),midi_enable_b,col,row,3,1); + gtk_grid_attach(GTK_GRID(grid),midi_enable_b,col,row,2,1); g_signal_connect(midi_enable_b,"toggled",G_CALLBACK(midi_enable_cb),NULL); + col+=2; + configure_b=gtk_check_button_new_with_label("MIDI Configure"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (configure_b), FALSE); + gtk_grid_attach(GTK_GRID(grid),configure_b,col,row,2,1); + g_signal_connect(configure_b,"toggled",G_CALLBACK(configure_cb),NULL); + + col+=2; + any_b=gtk_check_button_new_with_label("Configure for any channel"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (any_b), FALSE); + gtk_grid_attach(GTK_GRID(grid),any_b,col,row,6,1); + g_signal_connect(any_b,"toggled",G_CALLBACK(any_cb),NULL); + row++; col=0; @@ -749,15 +775,6 @@ void midi_menu(GtkWidget *parent) { g_signal_connect(load_original_b,"clicked",G_CALLBACK(load_original_cb),NULL); col++; - row++; - col=0; - - configure_b=gtk_check_button_new_with_label("MIDI Configure"); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (configure_b), FALSE); - gtk_grid_attach(GTK_GRID(grid),configure_b,col,row,3,1); - g_signal_connect(configure_b,"toggled",G_CALLBACK(configure_cb),NULL); - - row++; col=0; GtkWidget *label=gtk_label_new("Evt"); @@ -859,6 +876,7 @@ void midi_menu(GtkWidget *parent) { gtk_container_add(GTK_CONTAINER(content),grid); sub_menu=dialog; gtk_widget_show_all(dialog); + gtk_widget_hide(any_b); } static int update(void *data) { @@ -884,7 +902,11 @@ static int update(void *data) { gtk_label_set_text(GTK_LABEL(newEvent),"PITCH"); break; } - sprintf(text,"%d",thisChannel); + if (thisChannel >= 0) { + sprintf(text,"%d",thisChannel); + } else { + strcpy(text,"Any"); + } gtk_label_set_text(GTK_LABEL(newChannel),text); sprintf(text,"%d",thisNote); gtk_label_set_text(GTK_LABEL(newNote),text); @@ -1037,6 +1059,7 @@ void NewMidiConfigureEvent(enum MIDIevent event, int channel, int note, int val) //g_print("%s: new or existing event\n",__FUNCTION__); thisEvent=event; thisChannel=channel; + if (accept_any) thisChannel=-1; thisNote=note; thisVal=val; thisMin=val; @@ -1065,10 +1088,14 @@ void NewMidiConfigureEvent(enum MIDIevent event, int channel, int note, int val) } else { tree_event=MIDI_EVENT_NONE; } - tree_channel=atoi(str_channel); + if (!strncmp(str_channel,"Any", 3)) { + tree_channel=-1; + } else { + tree_channel=atoi(str_channel); + } tree_note=atoi(str_note); - if(thisEvent==tree_event && (thisChannel==tree_channel || tree_channel==-1) && thisNote==tree_note) { + if(thisEvent==tree_event && thisChannel==tree_channel && thisNote==tree_note) { thisVal=0; thisMin=0; thisMax=0; @@ -1109,8 +1136,15 @@ void midi_save_state() { struct desc *cmd; gint index; - if(device_index!=-1) { - setProperty("midi_device",midi_devices[device_index].name); + index=0; + for (int i=0; ichannel,index); + sprintf(name,"midi[%d].index[%d].event",i,index); setProperty(name,midi_events[cmd->event]); - sprintf(name,"midi[%d].channel[%d].index[%d].type",i,cmd->channel,index); + sprintf(name,"midi[%d].index[%d].type",i,index); setProperty(name,midi_types[cmd->type]); - sprintf(name,"midi[%d].channel[%d].index[%d].action",i,cmd->channel,index); + sprintf(name,"midi[%d].index[%d].action",i,index); setProperty(name,(char *) ActionTable[cmd->action].str); + sprintf(name,"midi[%d].index[%d].channel",i,index); + sprintf(value,"%d",cmd->channel); + setProperty(name, value); + cmd=cmd->next; index++; } @@ -1143,7 +1181,6 @@ void midi_save_state() { } } - } } void midi_restore_state() { @@ -1162,18 +1199,18 @@ void midi_restore_state() { MidiReleaseCommands(); //g_print("%s\n",__FUNCTION__); - value=getProperty("midi_device"); - if(value) { - //g_print("%s: device=%s\n",__FUNCTION__,value); - midi_device_name=g_new(gchar,strlen(value)+1); - strcpy(midi_device_name,value); - for(int i=0;i 15) channel=0; + } struct desc *desc; desc = (struct desc *) malloc(sizeof(struct desc)); @@ -1227,7 +1272,7 @@ void midi_restore_state() { desc->action = action; // MIDIaction desc->type = type; // MIDItype desc->event = event; // MIDevent - desc->onoff = ActionTable[action].onoff; + desc->onoff = onoff; desc->delay = 0; desc->vfl1 = -1; desc->vfl2 = -1; @@ -1243,7 +1288,7 @@ void midi_restore_state() { desc->vfr2 = 128; desc->channel = channel; - g_print("DESK INIT Note=%d Action=%d Type=%d Event=%d OnOff=%d Chan=%d\n", i, action, type, event, onoff, channel); + //g_print("DESC INIT Note=%d Action=%d Type=%d Event=%d OnOff=%d Chan=%d\n", i, action, type, event, onoff, channel); MidiAddCommand(i, desc); } // index diff --git a/midi_menu.h b/midi_menu.h index bed7e0a..02220e1 100644 --- a/midi_menu.h +++ b/midi_menu.h @@ -17,7 +17,6 @@ * */ -extern gchar *midi_device_name; extern void midi_menu(GtkWidget *parent); extern void NewMidiConfigureEvent(enum MIDIevent event, int channel, int note, int val); extern void midi_save_state(); diff --git a/new_menu.c b/new_menu.c index cf9b4a0..e1ed1e1 100644 --- a/new_menu.c +++ b/new_menu.c @@ -64,6 +64,7 @@ #include "server_menu.h" #endif #ifdef MIDI +#include "midi.h" #include "midi_menu.h" #endif diff --git a/radio.c b/radio.c index e086865..8d7a314 100644 --- a/radio.c +++ b/radio.c @@ -73,6 +73,7 @@ #endif #include "rigctl_menu.h" #ifdef MIDI +#include "midi.h" #include "alsa_midi.h" #include "midi_menu.h" #endif @@ -1304,13 +1305,14 @@ void start_radio() { // running. So this is the last thing we do when starting the radio. // #ifdef MIDI - g_print("%s: midi_enabled=%d midi_device_name=%s\n",__FUNCTION__,midi_enabled,midi_device_name); - if(midi_enabled && (midi_device_name!=NULL)) { - if(register_midi_device(midi_device_name)<0) { - midi_enabled=FALSE; + g_print("%s: midi_enabled=%d \n",__FUNCTION__,midi_enabled); + if(midi_enabled) { + for (i=0; i