since we now enable each device separately.
//
// 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
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) {
}
int MIDIstop() {
- midi_enabled=FALSE;
for (int i=0; i<n_midi_devices; i++) {
if (midi_devices[i].active) {
close_midi_device(i);
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);
}
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);
g_free(savefilename);
}
gtk_widget_destroy(save_dialog);
- g_free(filename);
}
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);
g_free(loadfilename);
}
gtk_widget_destroy(load_dialog);
- g_free(filename);
}
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);
g_free(loadfilename);
}
gtk_widget_destroy(load_dialog);
- g_free(filename);
}
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,
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) {
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;
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];
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>");
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);
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)
//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) {
#define TOOLBAR_HEIGHT (30)
#define WATERFALL_HEIGHT (105)
-#ifdef MIDI
-gboolean midi_enabled = false;
-#endif
-
GtkWidget *fixed;
static GtkWidget *vfo_panel;
static GtkWidget *meter;
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);
}
}
}
#ifdef MIDI
midi_restore_state();
- value=getProperty("radio.midi_enabled");
- if(value) midi_enabled=atoi(value);
#endif
value=getProperty("radio.display_sequence_errors");
#endif
#ifdef MIDI
- sprintf(value,"%d",midi_enabled);
- setProperty("radio.midi_enabled",value);
midi_save_state();
#endif
extern TRANSMITTER *transmitter;
-#ifdef MIDI
-extern gboolean midi_enabled;
-#endif
-
#define PA_DISABLED 0
#define PA_ENABLED 1