]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Finished "multi-device" code, this eliminates the midi_enable variable
authorc vw <dl1ycf@darc.de>
Fri, 7 May 2021 09:13:34 +0000 (11:13 +0200)
committerc vw <dl1ycf@darc.de>
Fri, 7 May 2021 09:13:34 +0000 (11:13 +0200)
since we now enable each device separately.

mac_midi.c
midi2.c
midi_menu.c
radio.c
radio.h

index fb45b29f30b3d5d11ccbb9c4bc5be2c5185178b5..15bace7b9f653357467d461e864741d592224b73 100644 (file)
@@ -180,13 +180,15 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co
 
 //
 // store the ports and clients locally such that we
-// can properly close a MIDI connection
+// can properly close a MIDI connection.
+// This can be local static data, no one outside this file
+// needs it.
 //
 static MIDIPortRef myMIDIports[MAX_MIDI_DEVICES];
 static MIDIClientRef myClients[MAX_MIDI_DEVICES];
 
 void close_midi_device(index) {
-    fprintf(stderr,"%s index=\n",__FUNCTION__);
+    fprintf(stderr,"%s index=%d\n",__FUNCTION__, index);
     if (index < 0 || index > n_midi_devices) return;
     //
     // This should release the resources associated with the pending connection
@@ -196,64 +198,145 @@ void close_midi_device(index) {
     MIDIClientDispose(myClients[index]);
     myMIDIports[index]=0;
     myClients[index]=0;
+    midi_devices[index].active=0;
 }
 
 int register_midi_device(int index) {
-    int ret;
+    OSStatus osret;
 
     g_print("%s: index=%d\n",__FUNCTION__,index);
-
-
 //
 //  Register a callback routine for the device
 //
+    if (index < 0 || index > MAX_MIDI_DEVICES) return -1;
 
-    if (index >= 0 && index < n_midi_devices) {
+     myClients[index]=0;
+     myMIDIports[index] = 0;
+     //Create client and port, and connect
+     osret=MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &myClients[index]);
+     if (osret !=0) {
+       g_print("%s: MIDIClientCreate failed with ret=%d\n", __FUNCTION__, (int) osret);
+       return -1;
+     }
+     osret=MIDIInputPortCreate(myClients[index], CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIports[index]);
+     if (osret !=0) {
+        g_print("%s: MIDIInputPortCreate failed with ret=%d\n", __FUNCTION__, (int) osret);
+        MIDIClientDispose(myClients[index]);
         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;
-    }
-
-    return ret;
+        return -1;
+     }
+     osret=MIDIPortConnectSource(myMIDIports[index] ,MIDIGetSource(index), NULL);
+     if (osret != 0) {
+        g_print("%s: MIDIPortConnectSource failed with ret=%d\n", __FUNCTION__, (int) osret);
+        MIDIClientDispose(myClients[index]);
+        myClients[index]=0;
+        MIDIPortDispose(myMIDIports[index]);
+        myMIDIports[index]=0;
+        return -1;
+     }
+     //
+     // Now we have successfully opened the device.
+     //
+     midi_devices[index].active=1;
+     return 0;
 }
 
 void get_midi_devices() {
     int n;
     int i;
-    CFStringRef pname;
-    char name[100];
-    int FoundMIDIref=-1;
+    CFStringRef pname;   // MacOS name of the device
+    char name[100];      // C name of the device
+    OSStatus osret;
+    static int first=1;
+
+    if (first) {
+      //
+      // perhaps not necessary in C, but good programming practise:
+      // initialize the table upon the first call
+      //
+      first=0;
+      for (i=0; i<MAX_MIDI_DEVICES; i++) {
+        midi_devices[i].name=NULL;
+        midi_devices[i].active=0;
+      }
+    }
 
+//
+//  This is called at startup (via midi_restore) and each time
+//  the MIDI menu is opened. So we have to take care that this
+//  function is essentially a no-op if the device list has not
+//  changed.
+//  If the device list has changed because of hot-plugging etc.
+//  close any MIDI device which changed position and mark
+//  it as inactive. Note that upon a hot-plug, MIDI devices that were
+//  there before may change its position in the device list and will then
+//  be closed.
+//
     n=MIDIGetNumberOfSources();
     n_midi_devices=0;
     for (i=0; i<n; i++) {
         MIDIEndpointRef dev = MIDIGetSource(i);
         if (dev != 0) {
-            MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
+            osret=MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
+            if (osret !=0) break; // in this case pname is invalid
             CFStringGetCString(pname, name, sizeof(name), 0);
             CFRelease(pname);
             //
             // Some users have reported that MacOS reports a string of length zero
             // for some MIDI devices. In this case, we replace the name by
-            // "NoPort"
+            // "NoPort<n>"
             //
-            if (strlen(name) == 0) strcpy(name,"NoPort");
+            if (strlen(name) == 0) sprintf(name,"NoPort%d",n_midi_devices);
             g_print("%s: %s\n",__FUNCTION__,name);
             if (midi_devices[n_midi_devices].name != NULL) {
-              g_free(midi_devices[n_midi_devices].name);
+              if (strncmp(name, midi_devices[n_midi_devices].name,sizeof(name))) {
+                //
+                // This slot was occupied and the names do not match:
+                // Close device (if active), insert new name
+                //
+                if (midi_devices[n_midi_devices].active) {
+                  close_midi_device(n_midi_devices);
+                }
+                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);
+              } else {
+                //
+                // This slot was occupied and the names match: do nothing!
+                // If there was no hot-plug or hot-unplug, we should always
+                // arrive here!
+                //
+              }
+            } else {
+             //
+              // This slot was unoccupied. Insert name and mark inactive
+              //
+              midi_devices[n_midi_devices].name=g_new(gchar,strlen(name)+1);
+              strcpy(midi_devices[n_midi_devices].name, name);
+              midi_devices[n_midi_devices].active=0;
             }
-            midi_devices[n_midi_devices].name=g_new(gchar,strlen(name)+1);
-            strcpy(midi_devices[n_midi_devices].name,name);
             n_midi_devices++;
         }
+        //
+        // If there are more devices than we have slots in our Table
+        // just stop processing.
+        //
+        if (n_midi_devices >= MAX_MIDI_DEVICES) break;
+    }
+    g_print("%s: number of devices=%d\n",__FUNCTION__,n_midi_devices);
+    //
+    // Get rid of all devices lingering around above the high-water mark
+    // (this happens in the case of hot-unplugging)
+    //
+    for (i=n_midi_devices; i<MAX_MIDI_DEVICES; i++) {
+      if (midi_devices[i].active) {
+        close_midi_device(i);
+      }
+      if (midi_devices[i].name != NULL) {
+        g_free(midi_devices[i].name);
+        midi_devices[i].name=NULL;
+      }
     }
-    g_print("%s: devices=%d\n",__FUNCTION__,n_midi_devices);
 }
 
 void configure_midi_device(gboolean state) {
diff --git a/midi2.c b/midi2.c
index 202be3235c84eb49db5c8909ac7ae7ce42682113..ee2746df22050849dbae7d8e8b40a4793f0b345d 100644 (file)
--- a/midi2.c
+++ b/midi2.c
@@ -261,7 +261,6 @@ static void keyword2action(char *s, enum MIDIaction *action, int *onoff) {
 }
 
 int MIDIstop() {
-  midi_enabled=FALSE;
   for (int i=0; i<n_midi_devices; i++) {
     if (midi_devices[i].active) {
       close_midi_device(i);
index bea800ae824ca0de62284c286e85a3a4fc5d541a..e75f0c45f3e9f21e290456bc55ea7970a34e65bb 100644 (file)
@@ -121,33 +121,16 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
   return FALSE;
 }
 
-static gboolean midi_enable_cb(GtkWidget *widget,gpointer data) {
-  int i;
-  if(midi_enabled) {
-    for (i=0; i<n_midi_devices; i++) {
-      if (midi_devices[i].active) {
-        close_midi_device(i);
-      }
-    }
-  }
-  midi_enabled=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
-  if(midi_enabled) {
-    for (i=0; i<n_midi_devices; i++) {
-      if (midi_devices[i].active) {
-        register_midi_device(i);
-      }
-    }
-  }
-  return TRUE;
-}
-
 static void device_cb(GtkWidget *widget, gpointer data) {
   int index=GPOINTER_TO_INT(data);
   int val=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
-  g_print("DEVICE =%d STATUS=%d\n", index, val);
-  midi_devices[index].active=val;
   if (val == 1) {
-    register_midi_device(index);
+    if (register_midi_device(index) != 0) {
+      //
+      // If the open fails, set button inactive again
+      //
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
+    }
   } else {
     close_midi_device(index);
   }
@@ -330,9 +313,7 @@ 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);
-  filename=g_new(gchar,10);
-  sprintf(filename,"midi.midi");
-  gtk_file_chooser_set_current_name(chooser,filename);
+  gtk_file_chooser_set_current_name(chooser,"midi.midi");
   res = gtk_dialog_run (GTK_DIALOG (save_dialog));
   if(res==GTK_RESPONSE_ACCEPT) {
     char *savefilename=gtk_file_chooser_get_filename(chooser);
@@ -342,7 +323,6 @@ static void save_cb(GtkWidget *widget,gpointer user_data) {
     g_free(savefilename);
   }
   gtk_widget_destroy(save_dialog);
-  g_free(filename);
 }
 
 static void load_cb(GtkWidget *widget,gpointer user_data) {
@@ -362,9 +342,7 @@ static void load_cb(GtkWidget *widget,gpointer user_data) {
                                       GTK_RESPONSE_ACCEPT,
                                       NULL);
   chooser = GTK_FILE_CHOOSER (load_dialog);
-  filename=g_new(gchar,10);
-  sprintf(filename,"midi.midi");
-  gtk_file_chooser_set_current_name(chooser,filename);
+  gtk_file_chooser_set_current_name(chooser,"midi.midi");
   res = gtk_dialog_run (GTK_DIALOG (load_dialog));
   if(res==GTK_RESPONSE_ACCEPT) {
     char *loadfilename=gtk_file_chooser_get_filename(chooser);
@@ -376,7 +354,6 @@ static void load_cb(GtkWidget *widget,gpointer user_data) {
     g_free(loadfilename);
   }
   gtk_widget_destroy(load_dialog);
-  g_free(filename);
 }
 
 static void load_original_cb(GtkWidget *widget,gpointer user_data) {
@@ -396,9 +373,7 @@ static void load_original_cb(GtkWidget *widget,gpointer user_data) {
                                       GTK_RESPONSE_ACCEPT,
                                       NULL);
   chooser = GTK_FILE_CHOOSER (load_dialog);
-  filename=g_new(gchar,10);
-  sprintf(filename,"midi.midi");
-  gtk_file_chooser_set_current_name(chooser,filename);
+  gtk_file_chooser_set_current_name(chooser,"midi.midi");
   res = gtk_dialog_run (GTK_DIALOG (load_dialog));
   if(res==GTK_RESPONSE_ACCEPT) {
     char *loadfilename=gtk_file_chooser_get_filename(chooser);
@@ -408,7 +383,6 @@ static void load_original_cb(GtkWidget *widget,gpointer user_data) {
     g_free(loadfilename);
   }
   gtk_widget_destroy(load_dialog);
-  g_free(filename);
 }
 
 static void add_store(int key,struct desc *cmd) {
@@ -455,7 +429,7 @@ static void add_store(int key,struct desc *cmd) {
   }
   strcpy(str_action,ActionTable[cmd->action].str);
   
-  g_print("%s: Event=%s Channel=%s Note=%s Type=%s Action=%s\n", __FUNCTION__, str_event, str_channel, str_note, str_type, str_action);
+  //g_print("%s: Event=%s Channel=%s Note=%s Type=%s Action=%s\n", __FUNCTION__, str_event, str_channel, str_note, str_type, str_action);
   gtk_list_store_prepend(store,&iter);
   gtk_list_store_set(store,&iter,
       EVENT_COLUMN,str_event,
@@ -647,6 +621,7 @@ static void delete_cb(GtkButton *widget,gpointer user_data) {
     g_print("%s: remove first\n",__FUNCTION__);
     MidiCommandsTable[thisNote]=current_cmd->next;
     g_free(current_cmd);
+    current_cmd=NULL;
   } else {
     previous_cmd=MidiCommandsTable[thisNote];
     while(previous_cmd->next!=NULL) {
@@ -655,6 +630,7 @@ static void delete_cb(GtkButton *widget,gpointer user_data) {
         g_print("%s: remove next\n",__FUNCTION__);
        previous_cmd->next=next_cmd->next;
        g_free(next_cmd);
+        current_cmd=NULL;  // note next_cmd == current_cmd
        break;
       }
       previous_cmd=next_cmd;
@@ -676,10 +652,6 @@ void midi_menu(GtkWidget *parent) {
   int row=0;
   GtkCellRenderer *renderer;
 
-  g_print("MENU: ndev=%d\n", n_midi_devices);
-  for (i=0; i<n_midi_devices; i++) {
-    g_print("Name=%s Active=%d\n", midi_devices[i].name, midi_devices[i].active);
-  }
   dialog=gtk_dialog_new();
   gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent_window));
   char title[64];
@@ -702,7 +674,7 @@ void midi_menu(GtkWidget *parent) {
   row=0;
   col=0;
 
-  //get_midi_devices();
+  get_midi_devices();
   if (n_midi_devices > 0) {
     GtkWidget *devices_label=gtk_label_new(NULL);
     gtk_label_set_markup(GTK_LABEL(devices_label), "<b>Select MIDI device(s)</b>");
@@ -734,13 +706,6 @@ void midi_menu(GtkWidget *parent) {
     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,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);
@@ -1150,7 +1115,7 @@ void midi_save_state() {
       index=0;
       cmd=MidiCommandsTable[i];
       while(cmd!=NULL) {
-        g_print("%s:  channel=%d key=%d event=%s onoff=%d type=%s action=%s\n",__FUNCTION__,cmd->channel,i,midi_events[cmd->event],cmd->onoff,midi_types[cmd->type],ActionTable[cmd->action].str);
+        //g_print("%s:  channel=%d key=%d event=%s onoff=%d type=%s action=%s\n",__FUNCTION__,cmd->channel,i,midi_events[cmd->event],cmd->onoff,midi_types[cmd->type],ActionTable[cmd->action].str);
 
         //
         // There might be events that share the channel and the note value (one NOTE and one CTRL, for example)
@@ -1200,9 +1165,14 @@ void midi_restore_state() {
 
   //g_print("%s\n",__FUNCTION__);
 
+  //
+  // Note this is too early to open the MIDI devices, since the
+  // radio has not yet fully been configured. Therefore, only
+  // set the "active" flag, and the devices will be opened in
+  // radio.c when it is appropriate
+  //
     
   for(int i=0; i<MAX_MIDI_DEVICES; i++) {
-    midi_devices[i].active=0;
     sprintf(name,"mididevice[%d].name",i);
     value=getProperty(name);
     if (value) {
diff --git a/radio.c b/radio.c
index 8d7a314c75f21a37eba5fd7e4914e8731077055e..272372a9f25cdec999ffb55ab031c20dd281bd38 100644 (file)
--- a/radio.c
+++ b/radio.c
 #define TOOLBAR_HEIGHT (30)
 #define WATERFALL_HEIGHT (105)
 
-#ifdef MIDI
-gboolean midi_enabled = false;
-#endif
-
 GtkWidget *fixed;
 static GtkWidget *vfo_panel;
 static GtkWidget *meter;
@@ -1299,18 +1295,23 @@ void start_radio() {
 
   gdk_window_set_cursor(gtk_widget_get_window(top_window),gdk_cursor_new(GDK_ARROW));
 
+#ifdef MIDI
   //
-  // MIDIstartup must not be called before the radio is completely set up, since
-  // then MIDI can asynchronously trigger actions which require the radio already
-  // running. So this is the last thing we do when starting the radio.
+  // The MIDI devices could not be opened in midi_restore_state() since MIDI events
+  // must not fly in before the radio is fully configured. Therefore midi_restore_state()
+  // simply marks the devices to be opened here by hi-jacking the "active" flag. Note that
+  // apart from this (ab)use, this flag is updated ONLY in register_midi_device() and
+  // close_midi_device().
   //
-#ifdef MIDI
-  g_print("%s: midi_enabled=%d \n",__FUNCTION__,midi_enabled);
-  if(midi_enabled) {
-    for (i=0; i<n_midi_devices; i++) {
-      if (midi_devices[i].active) {
+  for (i=0; i<n_midi_devices; i++) {
+    if (midi_devices[i].active) {
+      //
+      // If device was marked "active" in the props file, open (register) it
+      //
+      if (register_midi_device(i) == 0) {
         g_print("%s: MIDI device %s (index=%d) registered\n", __FUNCTION__, midi_devices[i].name, i);
-        register_midi_device(i);
+      } else {
+        g_print("%s: MIDI device %s (index=%d) could not be opened\n", __FUNCTION__, midi_devices[i].name, i);
       }
     }
   }
@@ -2144,8 +2145,6 @@ g_print("radioRestoreState: %s\n",property_path);
 
 #ifdef MIDI
     midi_restore_state();
-    value=getProperty("radio.midi_enabled");
-    if(value) midi_enabled=atoi(value);
 #endif
 
     value=getProperty("radio.display_sequence_errors");
@@ -2475,8 +2474,6 @@ g_print("radioSaveState: %s\n",property_path);
 #endif
 
 #ifdef MIDI
-  sprintf(value,"%d",midi_enabled);
-  setProperty("radio.midi_enabled",value);
   midi_save_state();
 #endif
 
diff --git a/radio.h b/radio.h
index da9580f7116d4ea686aab7d314aba2151a8fb30a..6be879da84818f46a9aa25d47ef5cdef725725a6 100644 (file)
--- a/radio.h
+++ b/radio.h
@@ -105,10 +105,6 @@ extern RECEIVER *active_receiver;
 
 extern TRANSMITTER *transmitter;
 
-#ifdef MIDI
-extern gboolean midi_enabled;
-#endif
-
 #define PA_DISABLED 0
 #define PA_ENABLED 1