--- /dev/null
+typedef struct _midi_device {
+ char *name;
+ char *port;
+} MIDI_DEVICE;
+
+#define MAX_MIDI_DEVICES 10
+
+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);
*
*/
+#include <gtk/gtk.h>
+#include "discovered.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "receiver.h"
+#include "adc.h"
+#include "dac.h"
+#include "radio.h"
#include "midi.h"
+#include "midi_menu.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
#include <CoreAudio/HostTime.h>
#include <CoreAudio/CoreAudio.h>
+MIDI_DEVICE midi_devices[MAX_MIDI_DEVICES];
+int n_midi_devices;
+int running;
+
//
// MIDI callback function
// called by MacOSX when data from the specified MIDI device arrives.
CMD_PITCH,
} command;
+static gboolean configure=FALSE;
+
static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
int i,j,byte,chan,arg1,arg2;
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
// messages with velocity == 0 when releasing
// a push-button.
if (arg2 == 0) {
- NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+ if(configure) {
+ NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+ } else {
+ NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+ }
} else {
- NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+ if(configure) {
+ NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+ } else {
+ NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+ }
}
break;
case CMD_NOTEOFF:
- NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+ if(configure) {
+ NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+ } else {
+ NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+ }
break;
case CMD_CTRL:
- NewMidiEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+ if(configure) {
+ NewMidiConfigureEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+ } else {
+ NewMidiEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+ }
break;
case CMD_PITCH:
- NewMidiEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+ if(configure) {
+ NewMidiConfigureEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+ } else {
+ NewMidiEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+ }
break;
}
state=STATE_SKIP;
} // j-loop through the list of packets
}
+void close_midi_device() {
+ fprintf(stderr,"%s\n",__FUNCTION__);
+}
-void register_midi_device(char *myname) {
- unsigned long nDevices;
+int register_midi_device(char *myname) {
int i;
CFStringRef pname;
char name[100];
int FoundMIDIref=-1;
- int mylen=strlen(myname);
+ int mylen;
+ int ret;
+ configure=false;
+ if (myname == NULL) {
+ g_print("%s: myname is NULL\n", __FUNCTION__);
+ return -1;
+ }
+ mylen=strlen(myname);
+ 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
//
-
- nDevices=MIDIGetNumberOfSources();
- for (i=0; i<nDevices; i++) {
- MIDIEndpointRef dev = MIDIGetSource(i);
- if (dev != 0) {
- MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
- 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"
- //
- if (strlen(name) == 0) strcpy(name,"NoPort");
- if (!strncmp(name, myname, mylen)) {
- FoundMIDIref=i;
- fprintf(stderr,"MIDI: registering device >%s<\n", name);
- } else {
- fprintf(stderr,"MIDI: looking for >%s< so >%s< does not match\n", myname,name);
- }
+ for (i=0; i<n_midi_devices; i++) {
+ if(!strncmp(midi_devices[i].name, myname, mylen)) {
+ FoundMIDIref=i;
+ fprintf(stderr,"MIDI device found and selected: >>>%s<<<\n", midi_devices[i].name);
+ } else {
+ fprintf(stderr,"MIDI device found BUT NOT SELECTED: >>>%s<<<\n", midi_devices[i].name);
}
}
MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &client);
MIDIInputPortCreate(client, CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIport);
MIDIPortConnectSource(myMIDIport,MIDIGetSource(FoundMIDIref), NULL);
+ ret=0;
+ } else {
+ ret=-1;
+ }
+
+ return ret;
+}
+
+void get_midi_devices() {
+ int n;
+ int i;
+ CFStringRef pname;
+ char name[100];
+ int FoundMIDIref=-1;
+
+ n=MIDIGetNumberOfSources();
+ n_midi_devices=0;
+ for (i=0; i<n; i++) {
+ MIDIEndpointRef dev = MIDIGetSource(i);
+ if (dev != 0) {
+ MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
+ 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"
+ //
+ if (strlen(name) == 0) strcpy(name,"NoPort");
+ g_print("%s: %s\n",__FUNCTION__,name);
+ midi_devices[n_midi_devices].name=g_new(gchar,strlen(name)+1);
+ strcpy(midi_devices[n_midi_devices].name,name);
+ n_midi_devices++;
+ }
}
+ g_print("%s: devices=%d\n",__FUNCTION__,n_midi_devices);
}
+
+void configure_midi_device(gboolean state) {
+ configure=state;
+}
+
#endif
MIDI_ACTION_ANF, // ANF: toggel ANF on/off
MIDI_ACTION_ATT, // ATT: Step attenuator or Programmable attenuator
MIDI_ACTION_VFO_B2A, // B2A: VFO B -> A
+ MIDI_ACTION_BAND_10, // BAND10
+ MIDI_ACTION_BAND_12, // BAND12
+ MIDI_ACTION_BAND_1240, // BAND1240
+ MIDI_ACTION_BAND_144, // BAND144
+ MIDI_ACTION_BAND_15, // BAND15
+ MIDI_ACTION_BAND_160, // BAND160
+ MIDI_ACTION_BAND_17, // BAND17
+ MIDI_ACTION_BAND_20, // BAND20
+ MIDI_ACTION_BAND_220, // BAND220
+ MIDI_ACTION_BAND_2300, // BAND2300
+ MIDI_ACTION_BAND_30, // BAND30
+ MIDI_ACTION_BAND_3400, // BAND3400
+ MIDI_ACTION_BAND_40, // BAND40
+ MIDI_ACTION_BAND_430, // BAND430
+ MIDI_ACTION_BAND_6, // BAND6
+ MIDI_ACTION_BAND_60, // BAND60
+ MIDI_ACTION_BAND_70, // BAND70
+ MIDI_ACTION_BAND_80, // BAND80
+ MIDI_ACTION_BAND_902, // BAND902
+ MIDI_ACTION_BAND_AIR, // BANDAIR
MIDI_ACTION_BAND_DOWN, // BANDDOWN: cycle through bands downwards
+ MIDI_ACTION_BAND_GEN, // BANDGEN
MIDI_ACTION_BAND_UP, // BANDUP: cycle through bands upwards
+ MIDI_ACTION_BAND_WWV, // BANDWWVUP: cycle through bands upwards
MIDI_ACTION_COMPRESS, // COMPRESS: TX compressor value
MIDI_ACTION_CTUN, // CTUN: toggle CTUN on/off
MIDI_ACTION_VFO, // CURRVFO: change VFO frequency
- MIDI_ACTION_CWKEY, // CWKEY: Unconditional CW key-down/up (outside keyer)
- MIDI_ACTION_CWL, // CWL: Left paddle pressed (use with ONOFF)
- MIDI_ACTION_CWR, // CWR: Right paddle pressed (use with ONOFF)
+ MIDI_ACTION_CWKEYER, // CW(Keyer): Unconditional CW key-down/up (outside keyer)
+ MIDI_ACTION_CWLEFT, // CWLEFT: Left paddle pressed (use with ONOFF)
+ MIDI_ACTION_CWRIGHT, // CWRIGHT: Right paddle pressed (use with ONOFF)
MIDI_ACTION_CWSPEED, // CWSPEED: Set speed of (iambic) CW keyer
MIDI_ACTION_DIV_COARSEGAIN, // DIVCOARSEGAIN: change DIVERSITY gain in large increments
- MIDI_ACTION_DIV_COARSEPHASE, // DIVPHASE: change DIVERSITY phase in large increments
+ MIDI_ACTION_DIV_COARSEPHASE, // DIVCOARSEPHASE: change DIVERSITY phase in large increments
MIDI_ACTION_DIV_FINEGAIN, // DIVFINEGAIN: change DIVERSITY gain in small increments
MIDI_ACTION_DIV_FINEPHASE, // DIVFINEPHASE: change DIVERSITY phase in small increments
MIDI_ACTION_DIV_GAIN, // DIVGAIN: change DIVERSITY gain in medium increments
MIDI_ACTION_FILTER_DOWN, // FILTERDOWN: cycle through filters downwards
MIDI_ACTION_FILTER_UP, // FILTERUP: cycle through filters upwards
MIDI_ACTION_LOCK, // LOCK: lock VFOs, disable frequency changes
+ MIDI_ACTION_MEM_RECALL_M0, // RECALLM0: load current freq/mode/filter from memory slot #0
+ MIDI_ACTION_MEM_RECALL_M1, // RECALLM1: load current freq/mode/filter from memory slot #1
+ MIDI_ACTION_MEM_RECALL_M2, // RECALLM2: load current freq/mode/filter from memory slot #2
+ MIDI_ACTION_MEM_RECALL_M3, // RECALLM3: load current freq/mode/filter from memory slot #3
+ MIDI_ACTION_MEM_RECALL_M4, // RECALLM4: load current freq/mode/filter from memory slot #4
+ MIDI_ACTION_MENU_FILTER, // MENU_FILTER
+ MIDI_ACTION_MENU_MODE, // MENU_MODE
MIDI_ACTION_MIC_VOLUME, // MICGAIN: MIC gain
MIDI_ACTION_MODE_DOWN, // MODEDOWN: cycle through modes downwards
MIDI_ACTION_MODE_UP, // MODEUP: cycle through modes upwards
MIDI_ACTION_MUTE, // MUTE: toggle mute on/off
MIDI_ACTION_NB, // NOISEBLANKER: cycle through NoiseBlanker states (none, NB, NB2)
MIDI_ACTION_NR, // NOISEREDUCTION: cycle through NoiseReduction states (none, NR, NR2)
+ MIDI_ACTION_NUMPAD_0, // NUMPAD0
+ MIDI_ACTION_NUMPAD_1, // NUMPAD1
+ MIDI_ACTION_NUMPAD_2, // NUMPAD2
+ MIDI_ACTION_NUMPAD_3, // NUMPAD3
+ MIDI_ACTION_NUMPAD_4, // NUMPAD4
+ MIDI_ACTION_NUMPAD_5, // NUMPAD5
+ MIDI_ACTION_NUMPAD_6, // NUMPAD6
+ MIDI_ACTION_NUMPAD_7, // NUMPAD7
+ MIDI_ACTION_NUMPAD_8, // NUMPAD8
+ MIDI_ACTION_NUMPAD_9, // NUMPAD9
+ MIDI_ACTION_NUMPAD_CL, // NUMPADCL
+ MIDI_ACTION_NUMPAD_ENTER, // NUMPADENTER
MIDI_ACTION_PAN, // PAN: change panning of panadater/waterfall when zoomed
MIDI_ACTION_PAN_HIGH, // PANHIGH: "high" value of current panadapter
MIDI_ACTION_PAN_LOW, // PANLOW: "low" value of current panadapter
MIDI_ACTION_PRE, // PREAMP: preamp on/off
- MIDI_ACTION_PTTONOFF, // PTT: set PTT state to "on" or "off"
+ MIDI_ACTION_PTTKEYER, // PTT(Keyer): set PTT state to "on" or "off"
MIDI_ACTION_PS, // PURESIGNAL: toggle PURESIGNAL on/off
- MIDI_ACTION_MEM_RECALL_M0, // RECALLM0: load current freq/mode/filter from memory slot #0
- MIDI_ACTION_MEM_RECALL_M1, // RECALLM1: load current freq/mode/filter from memory slot #1
- MIDI_ACTION_MEM_RECALL_M2, // RECALLM2: load current freq/mode/filter from memory slot #2
- MIDI_ACTION_MEM_RECALL_M3, // RECALLM3: load current freq/mode/filter from memory slot #3
- MIDI_ACTION_MEM_RECALL_M4, // RECALLM4: load current freq/mode/filter from memory slot #4
MIDI_ACTION_RF_GAIN, // RFGAIN: receiver RF gain
MIDI_ACTION_TX_DRIVE, // RFPOWER: adjust TX RF output power
MIDI_ACTION_RIT_CLEAR, // RITCLEAR: clear RIT and XIT value
//
enum MIDItype {
- MIDI_TYPE_NONE=0,
- MIDI_TYPE_KEY, // Button (press event)
- MIDI_TYPE_KNOB, // Knob (value between 0 and 100)
- MIDI_TYPE_WHEEL // Wheel (direction and speed)
+ MIDI_TYPE_NONE =0,
+ MIDI_TYPE_KEY =1, // Button (press event)
+ MIDI_TYPE_KNOB =2, // Knob (value between 0 and 100)
+ MIDI_TYPE_WHEEL=4 // Wheel (direction and speed)
};
+extern gchar *midi_types[];
+extern gchar *midi_events[];
+
//
// MIDIevent encodes the actual MIDI event "seen" in Layer-1 and
// passed to Layer-2. MIDI_NOTE events end up as MIDI_KEY and
MIDI_EVENT_PITCH
};
+typedef struct _action_table {
+ enum MIDIaction action;
+ const char *str;
+ enum MIDItype type;
+ int onoff;
+} ACTION_TABLE;
+
+extern ACTION_TABLE ActionTable[];
+
//
// Data structure for Layer-2
//
struct desc *next; // Next defined action for a controller/key with that note value (NULL for end of list)
};
-struct cmdtable{
- struct desc *desc[128]; // description for Note On/Off and ControllerChange
- struct desc *pitch; // description for PitchChanges
-};
+extern struct desc *MidiCommandsTable[129];
+
+extern int midi_debug;
//
// Layer-1 entry point, called once for all the MIDI devices
// that have been defined. This is called upon startup by
// Layer-2 through the function MIDIstartup.
//
-void register_midi_device(char *name);
+int register_midi_device(char *name);
+void close_midi_device();
+void configure_midi_device(gboolean state);
//
// Layer-2 entry point (called by Layer1)
// for each device description that was successfully read.
void NewMidiEvent(enum MIDIevent event, int channel, int note, int val);
-void MIDIstartup();
+int MIDIstartup(char *filename);
+void MidiAddCommand(int note, struct desc *desc);
+void MidiReleaseCommands();
//
// Layer-3 entry point (called by Layer2). In Layer-3, all the pihpsdr
*/
#include <gtk/gtk.h>
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "MacOS.h" // emulate clock_gettime on old MacOS systems
#endif
+#include "receiver.h"
+#include "discovered.h"
+#include "adc.h"
+#include "dac.h"
+#include "transmitter.h"
+#include "radio.h"
+#include "main.h"
#include "midi.h"
static double midi_startup_time;
static int midi_wait_startup=0;
-struct cmdtable MidiCommandsTable;
+struct desc *MidiCommandsTable[129];
void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
midi_wait_startup=0;
}
if (event == MIDI_EVENT_PITCH) {
- desc=MidiCommandsTable.pitch;
+ desc=MidiCommandsTable[128];
} else {
- desc=MidiCommandsTable.desc[note];
+ desc=MidiCommandsTable[note];
}
-//fprintf(stderr,"MIDI:init DESC=%p\n",desc);
+//g_print("MIDI:init DESC=%p\n",desc);
while (desc) {
-//fprintf(stderr,"DESC=%p next=%p CHAN=%d EVENT=%d\n", desc,desc->next,desc->channel,desc->event);
+//g_print("DESC=%p next=%p CHAN=%d EVENT=%d\n", desc,desc->next,desc->channel,desc->event);
if ((desc->channel == channel || desc->channel == -1) && (desc->event == event)) {
// Found matching entry
switch (desc->event) {
}
// translate value to direction
new=0;
+ new=val-64; // FIXME: allow for different speed
+ /*
if ((val >= desc->vfl1) && (val <= desc->vfl2)) new=-100;
if ((val >= desc-> fl1) && (val <= desc-> fl2)) new=-10;
if ((val >= desc->lft1) && (val <= desc->lft2)) new=-1;
if ((val >= desc->rgt1) && (val <= desc->rgt2)) new= 1;
if ((val >= desc-> fr1) && (val <= desc-> fr2)) new= 10;
if ((val >= desc->vfr1) && (val <= desc->vfr2)) new= 100;
-// fprintf(stderr,"WHEEL: val=%d new=%d thrs=%d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d\n",
-// val, new, desc->vfl1, desc->vfl2, desc->fl1, desc->fl2, desc->lft1, desc->lft2,
-// desc->rgt1, desc->rgt2, desc->fr1, desc->fr2, desc->vfr1, desc->vfr2);
+ */
+// g_print("WHEEL: val=%d new=%d thrs=%d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d\n",
+// val, new, desc->vfl1, desc->vfl2, desc->fl1, desc->fl2, desc->lft1, desc->lft2,
+// desc->rgt1, desc->rgt2, desc->fr1, desc->fr2, desc->vfr1, desc->vfr2);
if (new != 0) DoTheMidi(desc->action, desc->type, new);
last_wheel_action=desc->action;
}
}
if (!desc) {
// Nothing found. This is nothing to worry about, but log the key to stderr
- if (event == MIDI_EVENT_PITCH) fprintf(stderr, "Unassigned PitchBend Value=%d\n", val);
- if (event == MIDI_EVENT_NOTE ) fprintf(stderr, "Unassigned Key Note=%d Val=%d\n", note, val);
- if (event == MIDI_EVENT_CTRL ) fprintf(stderr, "Unassigned Controller Ctl=%d Val=%d\n", note, val);
+ if (event == MIDI_EVENT_PITCH) g_print("Unassigned PitchBend Value=%d\n", val);
+ if (event == MIDI_EVENT_NOTE ) g_print("Unassigned Key Note=%d Val=%d\n", note, val);
+ if (event == MIDI_EVENT_CTRL ) g_print("Unassigned Controller Ctl=%d Val=%d\n", note, val);
}
}
+gchar *midi_types[] = {"NONE","KEY","KNOB/SLIDER","*INVALID*","WHEEL"};
+gchar *midi_events[] = {"NONE","NOTE","CTRL","PITCH"};
+
/*
* This data structre connects names as used in the midi.props file with
* our MIDIaction enum values.
- * Take care that no key word is contained in another one!
- * Example: use "CURRVFO" not "VFO" otherwise there is possibly
- * a match for "VFO" when the key word is "VFOA".
+ *
+ * At some places in the code, it is assumes that ActionTable[i].action == i
+ * so keep the entries strictly in the order the enum is defined, and
+ * add one entry with ACTION_NONE at the end.
*/
-static struct {
- enum MIDIaction action; // the MIDI action
- const char *str; // the key word in the midi.props file
- int onoff; // =1 if action both on press + release
-} ActionTable[] = {
- { MIDI_ACTION_VFO_A2B, "A2B", 0},
- { MIDI_ACTION_AF_GAIN, "AFGAIN", 0},
- { MIDI_ACTION_AGCATTACK, "AGCATTACK", 0},
- { MIDI_ACTION_AGC, "AGCVAL", 0},
- { MIDI_ACTION_ANF, "ANF", 0},
- { MIDI_ACTION_ATT, "ATT", 0},
- { MIDI_ACTION_VFO_B2A, "B2A", 0},
- { MIDI_ACTION_BAND_DOWN, "BANDDOWN", 0},
- { MIDI_ACTION_BAND_UP, "BANDUP", 0},
- { MIDI_ACTION_COMPRESS, "COMPRESS", 0},
- { MIDI_ACTION_CTUN, "CTUN", 0},
- { MIDI_ACTION_VFO, "CURRVFO", 0},
- { MIDI_ACTION_CWKEY, "CWKEY", 1},
- { MIDI_ACTION_CWL, "CWL", 1},
- { MIDI_ACTION_CWR, "CWR", 1},
- { MIDI_ACTION_CWSPEED, "CWSPEED", 0},
- { MIDI_ACTION_DIV_COARSEGAIN, "DIVCOARSEGAIN", 0},
- { MIDI_ACTION_DIV_COARSEPHASE, "DIVCOARSEPHASE", 0},
- { MIDI_ACTION_DIV_FINEGAIN, "DIVFINEGAIN", 0},
- { MIDI_ACTION_DIV_FINEPHASE, "DIVFINEPHASE", 0},
- { MIDI_ACTION_DIV_GAIN, "DIVGAIN", 0},
- { MIDI_ACTION_DIV_PHASE, "DIVPHASE", 0},
- { MIDI_ACTION_DIV_TOGGLE, "DIVTOGGLE", 0},
- { MIDI_ACTION_DUP, "DUP", 0},
- { MIDI_ACTION_FILTER_DOWN, "FILTERDOWN", 0},
- { MIDI_ACTION_FILTER_UP, "FILTERUP", 0},
- { MIDI_ACTION_LOCK, "LOCK", 0},
- { MIDI_ACTION_MIC_VOLUME, "MICGAIN", 0},
- { MIDI_ACTION_MODE_DOWN, "MODEDOWN", 0},
- { MIDI_ACTION_MODE_UP, "MODEUP", 0},
- { MIDI_ACTION_MOX, "MOX", 0},
- { MIDI_ACTION_MUTE, "MUTE", 0},
- { MIDI_ACTION_NB, "NOISEBLANKER", 0},
- { MIDI_ACTION_NR, "NOISEREDUCTION", 0},
- { MIDI_ACTION_PAN, "PAN", 0},
- { MIDI_ACTION_PAN_HIGH, "PANHIGH", 0},
- { MIDI_ACTION_PAN_LOW, "PANLOW", 0},
- { MIDI_ACTION_PRE, "PREAMP", 0},
- { MIDI_ACTION_PTTONOFF, "PTT", 1},
- { MIDI_ACTION_PS, "PURESIGNAL", 0},
- { MIDI_ACTION_MEM_RECALL_M0, "RECALLM0", 0},
- { MIDI_ACTION_MEM_RECALL_M1, "RECALLM1", 0},
- { MIDI_ACTION_MEM_RECALL_M2, "RECALLM2", 0},
- { MIDI_ACTION_MEM_RECALL_M3, "RECALLM3", 0},
- { MIDI_ACTION_MEM_RECALL_M4, "RECALLM4", 0},
- { MIDI_ACTION_RF_GAIN, "RFGAIN", 0},
- { MIDI_ACTION_TX_DRIVE, "RFPOWER", 0},
- { MIDI_ACTION_RIT_CLEAR, "RITCLEAR", 0},
- { MIDI_ACTION_RIT_STEP, "RITSTEP", 0},
- { MIDI_ACTION_RIT_TOGGLE, "RITTOGGLE", 0},
- { MIDI_ACTION_RIT_VAL, "RITVAL", 0},
- { MIDI_ACTION_SAT, "SAT", 0},
- { MIDI_ACTION_SNB, "SNB", 0},
- { MIDI_ACTION_SPLIT, "SPLIT", 0},
- { MIDI_ACTION_MEM_STORE_M0, "STOREM0", 0},
- { MIDI_ACTION_MEM_STORE_M1, "STOREM1", 0},
- { MIDI_ACTION_MEM_STORE_M2, "STOREM2", 0},
- { MIDI_ACTION_MEM_STORE_M3, "STOREM3", 0},
- { MIDI_ACTION_MEM_STORE_M4, "STOREM4", 0},
- { MIDI_ACTION_SWAP_RX, "SWAPRX", 0},
- { MIDI_ACTION_SWAP_VFO, "SWAPVFO", 0},
- { MIDI_ACTION_TUNE, "TUNE", 0},
- { MIDI_ACTION_VFOA, "VFOA", 0},
- { MIDI_ACTION_VFOB, "VFOB", 0},
- { MIDI_ACTION_VFO_STEP_UP, "VFOSTEPUP", 0},
- { MIDI_ACTION_VFO_STEP_DOWN, "VFOSTEPDOWN", 0},
- { MIDI_ACTION_VOX, "VOX", 0},
- { MIDI_ACTION_VOXLEVEL, "VOXLEVEL", 0},
- { MIDI_ACTION_XIT_CLEAR, "XITCLEAR", 0},
- { MIDI_ACTION_XIT_VAL, "XITVAL", 0},
- { MIDI_ACTION_ZOOM, "ZOOM", 0},
- { MIDI_ACTION_ZOOM_UP, "ZOOMUP", 0},
- { MIDI_ACTION_ZOOM_DOWN, "ZOOMDOWN", 0},
- { MIDI_ACTION_NONE, "NONE", 0}
+ACTION_TABLE ActionTable[] = {
+ { MIDI_ACTION_NONE, "NONE", MIDI_TYPE_NONE, 0},
+ { MIDI_ACTION_VFO_A2B, "A2B", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_AF_GAIN, "AFGAIN", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_AGCATTACK, "AGCATTACK", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_AGC, "AGCVAL", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_ANF, "ANF", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_ATT, "ATT", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_VFO_B2A, "B2A", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_10, "BAND10", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_12, "BAND12", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_1240, "BAND1240", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_144, "BAND144", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_15, "BAND15", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_160, "BAND160", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_17, "BAND17", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_20, "BAND20", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_220, "BAND220", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_2300, "BAND2300", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_30, "BAND30", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_3400, "BAND3400", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_40, "BAND40", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_430, "BAND430", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_6, "BAND6", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_60, "BAND60", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_70, "BAND70", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_80, "BAND80", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_902, "BAND902", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_AIR, "BANDAIR", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_DOWN, "BANDDOWN", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_GEN, "BANDGEN", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_UP, "BANDUP", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_BAND_WWV, "BANDWWV", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_COMPRESS, "COMPRESS", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_CTUN, "CTUN", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_VFO, "CURRVFO", MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_CWKEYER, "CW(Keyer)", MIDI_TYPE_KEY, 1},
+ { MIDI_ACTION_CWLEFT, "CWLEFT", MIDI_TYPE_KEY, 1},
+ { MIDI_ACTION_CWRIGHT, "CWRIGHT", MIDI_TYPE_KEY, 1},
+ { MIDI_ACTION_CWSPEED, "CWSPEED", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_DIV_COARSEGAIN, "DIVCOARSEGAIN", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_DIV_COARSEPHASE, "DIVCOARSEPHASE", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_DIV_FINEGAIN, "DIVFINEGAIN", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_DIV_FINEPHASE, "DIVFINEPHASE", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_DIV_GAIN, "DIVGAIN", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_DIV_PHASE, "DIVPHASE", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_DIV_TOGGLE, "DIVTOGGLE", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_DUP, "DUP", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_FILTER_DOWN, "FILTERDOWN", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_FILTER_UP, "FILTERUP", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_LOCK, "LOCK", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_RECALL_M0, "RECALLM0", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_RECALL_M1, "RECALLM1", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_RECALL_M2, "RECALLM2", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_RECALL_M3, "RECALLM3", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_RECALL_M4, "RECALLM4", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MENU_FILTER, "MENU_FILTER", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MENU_MODE, "MENU_MODE", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MIC_VOLUME, "MICGAIN", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_MODE_DOWN, "MODEDOWN", MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_MODE_UP, "MODEUP", MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_MOX, "MOX", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MUTE, "MUTE", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NB, "NOISEBLANKER", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NR, "NOISEREDUCTION", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_0, "NUMPAD0", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_1, "NUMPAD1", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_2, "NUMPAD2", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_3, "NUMPAD3", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_4, "NUMPAD4", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_5, "NUMPAD5", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_6, "NUMPAD6", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_7, "NUMPAD7", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_8, "NUMPAD8", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_9, "NUMPAD9", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_CL, "NUMPADCL", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NUMPAD_ENTER, "NUMPADENTER", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_PAN, "PAN", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_PAN_HIGH, "PANHIGH", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_PAN_LOW, "PANLOW", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_PRE, "PREAMP", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_PTTKEYER, "PTT(Keyer)", MIDI_TYPE_KEY, 1},
+ { MIDI_ACTION_PS, "PURESIGNAL", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_RF_GAIN, "RFGAIN", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_TX_DRIVE, "RFPOWER", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_RIT_CLEAR, "RITCLEAR", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_RIT_STEP, "RITSTEP", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_RIT_TOGGLE, "RITTOGGLE", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_RIT_VAL, "RITVAL", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_SAT, "SAT", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_SNB, "SNB", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_SPLIT, "SPLIT", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_STORE_M0, "STOREM0", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_STORE_M1, "STOREM1", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_STORE_M2, "STOREM2", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_STORE_M3, "STOREM3", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_MEM_STORE_M4, "STOREM4", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_SWAP_RX, "SWAPRX", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_SWAP_VFO, "SWAPVFO", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_TUNE, "TUNE", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_VFOA, "VFOA", MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_VFOB, "VFOB", MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_VFO_STEP_UP, "VFOSTEPUP", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_VFO_STEP_DOWN, "VFOSTEPDOWN", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_VOX, "VOX", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_VOXLEVEL, "VOXLEVEL", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_XIT_CLEAR, "XITCLEAR", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_XIT_VAL, "XITVAL", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_ZOOM, "ZOOM", MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL, 0},
+ { MIDI_ACTION_ZOOM_UP, "ZOOMUP", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_ZOOM_DOWN, "ZOOMDOWN", MIDI_TYPE_KEY, 0},
+ { MIDI_ACTION_NONE, "NONE", MIDI_TYPE_NONE, 0}
};
+
/*
* Translation from keyword in midi.props file to MIDIaction
*/
*onoff = 0;
}
+int MIDIstop() {
+ midi_enabled=FALSE;
+ close_midi_device();
+ return 0;
+}
+
+/*
+ * Release data from MidiCommandsTable
+ */
+
+void MidiReleaseCommands() {
+ int i;
+ struct desc *loop;
+ for (i=0; i<129; i++) {
+ loop = MidiCommandsTable[i];
+ while (loop != NULL) {
+ free(loop);
+ loop = loop->next;
+ }
+ MidiCommandsTable[i]=NULL;
+ }
+}
+
+/*
+ * Add a command to MidiCommandsTable
+ */
+
+void MidiAddCommand(int note, struct desc *desc) {
+ struct desc *loop;
+
+ if (note < 0 || note > 128) return;
+
+ //
+ // Actions with channel == -1 (ANY) must go to the end of the list
+ //
+ if (MidiCommandsTable[note] == NULL) {
+ // initialize linked list
+ MidiCommandsTable[note]=desc;
+ } else if (desc->channel >= 0) {
+ // add to top of the list
+ desc->next = MidiCommandsTable[note];
+ MidiCommandsTable[note]=desc;
+ } else {
+ // add to tail of the list
+ loop = MidiCommandsTable[note];
+ while (loop->next != NULL) {
+ loop = loop->next;
+ }
+ loop->next=desc;
+ }
+}
+
/*
- * Here we read in a MIDI description file "midi.def" and fill the MidiCommandsTable
+ * Here we read in a MIDI description file and fill the MidiCommandsTable
* data structure
*/
-void MIDIstartup() {
+int MIDIstartup(char *filename) {
FILE *fpin;
char zeile[255];
char *cp,*cq;
char c;
struct timespec ts;
- for (i=0; i<128; i++) MidiCommandsTable.desc[i]=NULL;
- MidiCommandsTable.pitch=NULL;
+ MidiReleaseCommands();
+
+ g_print("%s: %s\n",__FUNCTION__,filename);
+ fpin=fopen(filename, "r");
- fpin=fopen("midi.props", "r");
- if (!fpin) return;
+ g_print("%s: fpin=%p\n",__FUNCTION__,fpin);
+ if (!fpin) {
+ g_print("%s: failed to open MIDI device\n",__FUNCTION__);
+ return -1;
+ }
for (;;) {
if (fgets(zeile, 255, fpin) == NULL) break;
cp++;
}
-//fprintf(stderr,"\nMIDI:INP:%s\n",zeile);
+g_print("\n%s:INP:%s\n",__FUNCTION__,zeile);
if ((cp = strstr(zeile, "DEVICE="))) {
// Delete comments and trailing blanks
midi_wait_startup=1;
clock_gettime(CLOCK_MONOTONIC, &ts);
midi_startup_time=ts.tv_sec + 1E-9*ts.tv_nsec;
- register_midi_device(cp+7);
+ 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
+ onoff=0;
event=MIDI_EVENT_NONE;
type=MIDI_TYPE_NONE;
key=0;
delay=0;
- onoff=0;
//
// The KEY=, CTRL=, and PITCH= cases are mutually exclusive
sscanf(cp+4, "%d", &key);
event=MIDI_EVENT_NOTE;
type=MIDI_TYPE_KEY;
-//fprintf(stderr,"MIDI:KEY:%d\n", key);
+g_print("%s: MIDI:KEY:%d\n",__FUNCTION__, key);
}
if ((cp = strstr(zeile, "CTRL="))) {
sscanf(cp+5, "%d", &key);
event=MIDI_EVENT_CTRL;
type=MIDI_TYPE_KNOB;
-//fprintf(stderr,"MIDI:CTL:%d\n", key);
+g_print("%s: MIDI:CTL:%d\n",__FUNCTION__, key);
}
if ((cp = strstr(zeile, "PITCH "))) {
event=MIDI_EVENT_PITCH;
type=MIDI_TYPE_KNOB;
-//fprintf(stderr,"MIDI:PITCH\n");
+g_print("%s: MIDI:PITCH\n",__FUNCTION__);
}
//
// If event is still undefined, skip line
sscanf(cp+5, "%d", &chan);
chan--;
if (chan<0 || chan>15) chan=-1;
-//fprintf(stderr,"MIDI:CHA:%d\n",chan);
+g_print("%s:CHAN:%d\n",__FUNCTION__,chan);
}
if ((cp = strstr(zeile, "WHEEL")) && (type == MIDI_TYPE_KNOB)) {
// change type from MIDI_TYPE_KNOB to MIDI_TYPE_WHEEL
type=MIDI_TYPE_WHEEL;
-//fprintf(stderr,"MIDI:WHEEL\n");
+g_print("%s:WHEEL\n",__FUNCTION__);
}
if ((cp = strstr(zeile, "DELAY="))) {
sscanf(cp+6, "%d", &delay);
-//fprintf(stderr,"MIDI:DELAY:%d\n",delay);
+g_print("%s:DELAY:%d\n",__FUNCTION__,delay);
}
if ((cp = strstr(zeile, "THR="))) {
sscanf(cp+4, "%d %d %d %d %d %d %d %d %d %d %d %d",
while (*cq != 0 && *cq != '\n' && *cq != ' ' && *cq != '\t') cq++;
*cq=0;
keyword2action(cp+7, &action, &onoff);
-//fprintf(stderr,"MIDI:ACTION:%s (%d), onoff=%d\n",cp+7, action, onoff);
+g_print("MIDI:ACTION:%s (%d), onoff=%d\n",cp+7, action, onoff);
}
//
// All data for a descriptor has been read. Construct it!
//
if (event == MIDI_EVENT_PITCH) {
//fprintf(stderr,"MIDI:TAB:Insert desc=%p in PITCH table\n",desc);
- dp = MidiCommandsTable.pitch;
- if (dp == NULL) {
- MidiCommandsTable.pitch = desc;
- } else {
- while (dp->next != NULL) dp=dp->next;
- dp->next=desc;
- }
+ MidiAddCommand(129, desc);
}
if (event == MIDI_EVENT_NOTE || event == MIDI_EVENT_CTRL) {
-//fprintf(stderr,"MIDI:TAB:Insert desc=%p in CMDS[%d] table\n",desc,key);
- dp = MidiCommandsTable.desc[key];
- if (dp == NULL) {
- MidiCommandsTable.desc[key]=desc;
- } else {
- while (dp->next != NULL) dp=dp->next;
- dp->next=desc;
- }
+g_print("%s:TAB:Insert desc=%p in CMDS[%d] table\n",__FUNCTION__,desc,key);
+ MidiAddCommand(key, desc);
}
}
+ return 0;
}
// code below, one can queue the "big switch statement" into the GTK
// idle queue and exectue all GUI functions directly.
//
-// However, this is not wanted for CWKEY, CWL and CWR since
+// However, this is not wanted for CWKEYER, CWLEFT and CWRIGHT since
// these have to be processed with minimal delay (and do not call GUI functions).
//
// Therefore, these three cases are already handled in the MIDI callback
static int DoTheRestOfTheMIDI(void *data);
void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
- if (action == MIDI_ACTION_CWKEY) {
+ g_print("%s: ACTION=%d TYPE=%d VAL=%d\n", __FUNCTION__, action, type, val);
+ if (action == MIDI_ACTION_CWKEYER) {
//
// This is a CW key-up/down which uses functions from the keyer
// that by-pass the interrupt-driven standard action.
return;
}
#ifdef LOCALCW
- if (action == MIDI_ACTION_CWL) {
+ if (action == MIDI_ACTION_CWLEFT) {
keyer_event(1, val);
return;
}
- if (action == MIDI_ACTION_CWR) {
+ if (action == MIDI_ACTION_CWRIGHT) {
keyer_event(0, val);
return;
}
/////////////////////////////////////////////////////////// "MOX"
case MIDI_ACTION_MOX: // only key supported
// Note this toggles the PTT state without knowing the
- // actual state. See MIDI_ACTION_PTTONOFF for actually
+ // actual state. See MIDI_ACTION_PTTKEYER for actually
// *setting* PTT
if (type == MIDI_TYPE_KEY && can_transmit) {
new = !mox;
update_att_preamp();
}
break;
- /////////////////////////////////////////////////////////// "PTTONOFF"
- case MIDI_ACTION_PTTONOFF: // key only
+ /////////////////////////////////////////////////////////// "PTT(Keyer)"
+ case MIDI_ACTION_PTTKEYER: // key only
// always use with "ONOFF"
if (type == MIDI_TYPE_KEY && can_transmit) {
mox_update(val);
--- /dev/null
+/* Copyright (C)
+* 2020 - John Melton, G0ORX/N6LYT
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+*/
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "discovered.h"
+#include "mode.h"
+#include "filter.h"
+#include "band.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "receiver.h"
+#include "adc.h"
+#include "dac.h"
+#include "radio.h"
+#include "midi.h"
+#include "alsa_midi.h"
+#include "new_menu.h"
+#include "midi_menu.h"
+#include "property.h"
+
+enum {
+ EVENT_COLUMN,
+ CHANNEL_COLUMN,
+ NOTE_COLUMN,
+ TYPE_COLUMN,
+ ACTION_COLUMN,
+ N_COLUMNS
+};
+
+static GtkWidget *parent_window=NULL;
+static GtkWidget *menu_b=NULL;
+static GtkWidget *dialog=NULL;
+
+static GtkWidget *midi_enable_b;
+
+static GtkListStore *store;
+static GtkWidget *view;
+static GtkWidget *scrolled_window=NULL;
+static gulong selection_signal_id;
+GtkTreeSelection *selection;
+static GtkTreeModel *model;
+static GtkTreeIter iter;
+struct desc *current_cmd;
+
+static GtkWidget *filename;
+
+static GtkWidget *newEvent;
+static GtkWidget *newChannel;
+static GtkWidget *newNote;
+static GtkWidget *newVal;
+static GtkWidget *newType;
+static GtkWidget *newMin;
+static GtkWidget *newMax;
+static GtkWidget *newAction;
+static GtkWidget *configure_b;
+static GtkWidget *add_b;
+static GtkWidget *update_b;
+static GtkWidget *delete_b;
+
+static enum MIDIevent thisEvent=MIDI_EVENT_NONE;
+static int thisChannel;
+static int thisNote;
+static int thisVal;
+static int thisMin;
+static int thisMax;
+static enum MIDItype thisType;
+static enum MIDIaction thisAction;
+
+gchar *midi_device_name=NULL;
+static gint device_index=-1;
+
+enum {
+ UPDATE_NEW,
+ UPDATE_CURRENT,
+ UPDATE_EXISTING
+};
+
+static int update(void *data);
+static void load_store();
+static void add_store(int key,struct desc *cmd);
+
+static void cleanup() {
+ configure_midi_device(FALSE);
+ if(dialog!=NULL) {
+ gtk_widget_destroy(dialog);
+ dialog=NULL;
+ sub_menu=NULL;
+ }
+}
+
+static gboolean close_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
+ cleanup();
+ return TRUE;
+}
+
+static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
+ cleanup();
+ return FALSE;
+}
+
+static gboolean midi_enable_cb(GtkWidget *widget,gpointer data) {
+ if(midi_enabled) {
+ close_midi_device();
+ }
+ midi_enabled=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+ if(midi_enabled) {
+ if(register_midi_device(midi_device_name)<0) {
+ midi_enabled=FALSE;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (widget), midi_enabled);
+ }
+ }
+ return TRUE;
+}
+
+static void configure_cb(GtkWidget *widget, gpointer data) {
+ gboolean conf=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+ configure_midi_device(conf);
+}
+
+static void device_changed_cb(GtkWidget *widget, gpointer data) {
+ device_index = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ if(midi_device_name!=NULL) {
+ g_free(midi_device_name);
+ }
+ midi_device_name=g_new(gchar,strlen(midi_devices[device_index].name)+1);
+ strcpy(midi_device_name,midi_devices[device_index].name);
+ if(midi_enabled) {
+ close_midi_device();
+ if(register_midi_device(midi_device_name)) {
+ midi_enabled=FALSE;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(midi_enable_b), midi_enabled);
+ }
+ }
+}
+
+static void type_changed_cb(GtkWidget *widget, gpointer data) {
+ int i=1;
+ int j=1;
+
+ // update actions available for the type
+ gchar *type=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget));
+
+ g_print("%s: type=%s action=%d\n",__FUNCTION__,type,thisAction);
+ gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newAction));
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[0].str);
+ if(type==NULL || strcmp(type,"NONE")==0) {
+ // leave empty
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),0);
+ } else if(strcmp(type,"KEY")==0) {
+ // add all the Key actions
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(ActionTable[i].type&MIDI_TYPE_KEY) {
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+ if(ActionTable[i].action==thisAction) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(newAction),j);
+ }
+ j++;
+ }
+ i++;
+ }
+ } else if(strcmp(type,"KNOB/SLIDER")==0) {
+ // add all the Knob actions
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(ActionTable[i].type&MIDI_TYPE_KNOB) {
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+ if(ActionTable[i].action==thisAction) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),j);
+ }
+ j++;
+ }
+ i++;
+ }
+ } else if(strcmp(type,"WHEEL")==0) {
+ // add all the Wheel actions
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(ActionTable[i].type&MIDI_TYPE_WHEEL || ActionTable[i].type&MIDI_TYPE_KNOB) {
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+ if(ActionTable[i].action==thisAction) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),j);
+ }
+ j++;
+ }
+ i++;
+ }
+ }
+}
+
+static void row_inserted_cb(GtkTreeModel *tree_model,GtkTreePath *path, GtkTreeIter *iter,gpointer user_data) {
+ //g_print("%s\n",__FUNCTION__);
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(view),path,NULL,FALSE);
+}
+
+
+static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data) {
+ char *str_event;
+ char *str_channel;
+ char *str_note;
+ char *str_type;
+ char *str_action;
+
+ //g_print("%s\n",__FUNCTION__);
+ //if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(configure_b))) {
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, EVENT_COLUMN, &str_event, -1);
+ gtk_tree_model_get(model, &iter, CHANNEL_COLUMN, &str_channel, -1);
+ gtk_tree_model_get(model, &iter, NOTE_COLUMN, &str_note, -1);
+ gtk_tree_model_get(model, &iter, TYPE_COLUMN, &str_type, -1);
+ gtk_tree_model_get(model, &iter, ACTION_COLUMN, &str_action, -1);
+
+ g_print("%s: %s %s %s %s %s\n",__FUNCTION__,str_event,str_channel,str_note,str_type,str_action);
+
+ if(str_event!=NULL && str_channel!=NULL && str_note!=NULL && str_type!=NULL && str_action!=NULL) {
+
+ if(strcmp(str_event,"CTRL")==0) {
+ thisEvent=MIDI_EVENT_CTRL;
+ } else if(strcmp(str_event,"PITCH")==0) {
+ thisEvent=MIDI_EVENT_PITCH;
+ } else if(strcmp(str_event,"NOTE")==0) {
+ thisEvent=MIDI_EVENT_NOTE;
+ } else {
+ thisEvent=MIDI_EVENT_NONE;
+ }
+ thisChannel=atoi(str_channel);
+ thisNote=atoi(str_note);
+ thisVal=0;
+ thisMin=0;
+ thisMax=0;
+ if(strcmp(str_type,"KEY")==0) {
+ thisType=MIDI_TYPE_KEY;
+ } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+ thisType=MIDI_TYPE_KNOB;
+ } else if(strcmp(str_type,"WHEEL")==0) {
+ thisType=MIDI_TYPE_WHEEL;
+ } else {
+ thisType=MIDI_TYPE_NONE;
+ }
+ thisAction=MIDI_ACTION_NONE;
+ int i=1;
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(strcmp(ActionTable[i].str,str_action)==0) {
+ thisAction=ActionTable[i].action;
+ break;
+ }
+ i++;
+ }
+ g_idle_add(update,GINT_TO_POINTER(UPDATE_EXISTING));
+ }
+ }
+ //}
+}
+
+static void find_current_cmd() {
+ struct desc *cmd;
+ g_print("%s:\n",__FUNCTION__);
+ cmd=MidiCommandsTable[thisNote];
+ while(cmd!=NULL) {
+ if((cmd->channel==thisChannel || cmd->channel==-1) && cmd->type==thisType && cmd->action==thisAction) {
+ g_print("%s: found cmd %p\n",__FUNCTION__,cmd);
+ break;
+ }
+ cmd=cmd->next;
+ }
+ current_cmd=cmd; // NULL if not found
+}
+
+static void clear_cb(GtkWidget *widget,gpointer user_data) {
+ struct desc *cmd;
+ struct desc *next;
+
+ g_signal_handler_block(G_OBJECT(selection), selection_signal_id);
+ gtk_list_store_clear(store);
+ MidiReleaseCommands();
+ g_signal_handler_unblock(G_OBJECT(selection), selection_signal_id);
+}
+
+static void save_cb(GtkWidget *widget,gpointer user_data) {
+ GtkWidget *save_dialog;
+ GtkFileChooser *chooser;
+ GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ gchar *filename;
+ gint res;
+ struct desc *cmd;
+
+ save_dialog = gtk_file_chooser_dialog_new ("Save File",
+ GTK_WINDOW(dialog),
+ action,
+ "_Cancel",
+ GTK_RESPONSE_CANCEL,
+ "_Save",
+ GTK_RESPONSE_ACCEPT,
+ 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);
+ }
+ gtk_file_chooser_set_current_name(chooser,filename);
+ res = gtk_dialog_run (GTK_DIALOG (save_dialog));
+ if(res==GTK_RESPONSE_ACCEPT) {
+ char *savefilename=gtk_file_chooser_get_filename(chooser);
+ clearProperties();
+ midi_save_state();
+ saveProperties(savefilename);
+ g_free(savefilename);
+ }
+ gtk_widget_destroy(save_dialog);
+ g_free(filename);
+}
+
+static void load_cb(GtkWidget *widget,gpointer user_data) {
+ GtkWidget *load_dialog;
+ GtkFileChooser *chooser;
+ GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
+ gchar *filename;
+ gint res;
+ struct desc *cmd;
+
+ load_dialog = gtk_file_chooser_dialog_new ("Open MIDI File",
+ GTK_WINDOW(dialog),
+ action,
+ "_Cancel",
+ GTK_RESPONSE_CANCEL,
+ "_Save",
+ 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);
+ }
+ gtk_file_chooser_set_current_name(chooser,filename);
+ res = gtk_dialog_run (GTK_DIALOG (load_dialog));
+ if(res==GTK_RESPONSE_ACCEPT) {
+ char *loadfilename=gtk_file_chooser_get_filename(chooser);
+ clear_cb(NULL,NULL);
+ clearProperties();
+ loadProperties(loadfilename);
+ midi_restore_state();
+ load_store();
+ g_free(loadfilename);
+ }
+ gtk_widget_destroy(load_dialog);
+ g_free(filename);
+}
+
+static void load_original_cb(GtkWidget *widget,gpointer user_data) {
+ GtkWidget *load_dialog;
+ GtkFileChooser *chooser;
+ GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
+ gchar *filename;
+ gint res;
+ struct desc *cmd;
+
+ load_dialog = gtk_file_chooser_dialog_new ("Open ORIGINAL MIDI File",
+ GTK_WINDOW(dialog),
+ action,
+ "_Cancel",
+ GTK_RESPONSE_CANCEL,
+ "_Save",
+ 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);
+ }
+ gtk_file_chooser_set_current_name(chooser,filename);
+ res = gtk_dialog_run (GTK_DIALOG (load_dialog));
+ if(res==GTK_RESPONSE_ACCEPT) {
+ char *loadfilename=gtk_file_chooser_get_filename(chooser);
+ clear_cb(NULL,NULL);
+ MIDIstartup(loadfilename);
+ load_store();
+ g_free(loadfilename);
+ }
+ gtk_widget_destroy(load_dialog);
+ g_free(filename);
+}
+
+static void add_store(int key,struct desc *cmd) {
+ char str_event[16];
+ char str_channel[16];
+ char str_note[16];
+ char str_type[32];
+ char str_action[32];
+
+ //g_print("%s: key=%d desc=%p\n",__FUNCTION__,key,cmd);
+ switch(cmd->event) {
+ case MIDI_EVENT_NONE:
+ strcpy(str_event,"NONE");
+ break;
+ case MIDI_EVENT_NOTE:
+ strcpy(str_event,"NOTE");
+ break;
+ case MIDI_EVENT_CTRL:
+ strcpy(str_event,"CTRL");
+ break;
+ case MIDI_EVENT_PITCH:
+ strcpy(str_event,"PITCH");
+ break;
+ }
+ if (cmd->channel >= 0) {
+ sprintf(str_channel,"%d",cmd->channel);
+ } else {
+ sprintf(str_channel,"%s","Any");
+ }
+ sprintf(str_note,"%d",key);
+ switch(cmd->type) {
+ case MIDI_TYPE_NONE:
+ strcpy(str_type,"NONE");
+ break;
+ case MIDI_TYPE_KEY:
+ strcpy(str_type,"KEY");
+ break;
+ case MIDI_TYPE_KNOB:
+ strcpy(str_type,"KNOB/SLIDER");
+ break;
+ case MIDI_TYPE_WHEEL:
+ strcpy(str_type,"WHEEL");
+ break;
+ }
+ 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);
+ gtk_list_store_prepend(store,&iter);
+ gtk_list_store_set(store,&iter,
+ EVENT_COLUMN,str_event,
+ CHANNEL_COLUMN,str_channel,
+ NOTE_COLUMN,str_note,
+ TYPE_COLUMN,str_type,
+ ACTION_COLUMN,str_action,
+ -1);
+
+ if(scrolled_window!=NULL) {
+ GtkAdjustment *adjustment=gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(scrolled_window));
+ //g_print("%s: adjustment=%f lower=%f upper=%f\n",__FUNCTION__,gtk_adjustment_get_value(adjustment),gtk_adjustment_get_lower(adjustment),gtk_adjustment_get_upper(adjustment));
+ if(gtk_adjustment_get_value(adjustment)!=0.0) {
+ gtk_adjustment_set_value(adjustment,0.0);
+ }
+ }
+}
+
+static void load_store() {
+ struct desc *cmd;
+ gtk_list_store_clear(store);
+ for(int i=127;i>=0;i--) {
+ cmd=MidiCommandsTable[i];
+ while(cmd!=NULL) {
+ add_store(i,cmd);
+ cmd=cmd->next;
+ }
+ }
+}
+
+static void add_cb(GtkButton *widget,gpointer user_data) {
+
+ gchar *str_type=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newType));
+ gchar *str_action=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newAction));
+;
+
+ gint i;
+ gint type;
+ gint action;
+ gint onoff;
+
+ if(str_type==NULL || str_action==NULL) {
+ return;
+ }
+
+ if(strcmp(str_type,"KEY")==0) {
+ type=MIDI_TYPE_KEY;
+ } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+ type=MIDI_TYPE_KNOB;
+ } else if(strcmp(str_type,"WHEEL")==0) {
+ type=MIDI_TYPE_WHEEL;
+ } else {
+ type=MIDI_TYPE_NONE;
+ }
+
+ action=MIDI_ACTION_NONE;
+ i=1;
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(strcmp(ActionTable[i].str,str_action)==0) {
+ action=ActionTable[i].action;
+ onoff=ActionTable[i].onoff;
+ break;
+ }
+ i++;
+ }
+
+ g_print("%s: type=%s (%d) action=%s (%d)\n",__FUNCTION__,str_type,type,str_action,action);
+
+ struct desc *desc;
+ desc = (struct desc *) malloc(sizeof(struct desc));
+ desc->next = NULL;
+ desc->action = action; // MIDIaction
+ desc->type = type; // MIDItype
+ desc->event = thisEvent; // MIDevent
+ desc->onoff = onoff;
+ desc->delay = 0;
+ desc->vfl1 = -1;
+ desc->vfl2 = -1;
+ desc->fl1 = -1;
+ desc->fl2 = -1;
+ desc->lft1 = -1;
+ desc->lft2 = 63;
+ desc->rgt1 = 64;
+ desc->rgt2 = 128;
+ desc->fr1 = 128;
+ desc->fr2 = 128;
+ desc->vfr1 = 128;
+ desc->vfr2 = 128;
+ desc->channel = thisChannel;
+
+ gint key=thisNote;
+ if(key<0) key=0;
+ if(key>127) key=0;
+
+
+ MidiAddCommand(key, desc);
+ add_store(key,desc);
+
+ gtk_widget_set_sensitive(add_b,FALSE);
+ gtk_widget_set_sensitive(update_b,TRUE);
+ gtk_widget_set_sensitive(delete_b,TRUE);
+
+}
+
+static void update_cb(GtkButton *widget,gpointer user_data) {
+ char str_event[16];
+ char str_channel[16];
+ char str_note[16];
+ int i;
+
+ if (current_cmd == NULL) {
+ g_print("%s: current_cmd is NULL!\n", __FUNCTION__);
+ return;
+ }
+
+ gchar *str_type=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newType));
+ gchar *str_action=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newAction));
+;
+ //g_print("%s: type=%s action=%s\n",__FUNCTION__,str_type,str_action);
+
+ if(strcmp(str_type,"KEY")==0) {
+ thisType=MIDI_TYPE_KEY;
+ } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+ thisType=MIDI_TYPE_KNOB;
+ } else if(strcmp(str_type,"WHEEL")==0) {
+ thisType=MIDI_TYPE_WHEEL;
+ } else {
+ thisType=MIDI_TYPE_NONE;
+ }
+
+ thisAction=MIDI_ACTION_NONE;
+ i=1;
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(strcmp(ActionTable[i].str,str_action)==0) {
+ thisAction=ActionTable[i].action;
+ break;
+ }
+ i++;
+ }
+
+ current_cmd->channel=thisChannel;
+ current_cmd->type=thisType;
+ current_cmd->action=thisAction;
+
+ switch(current_cmd->event) {
+ case MIDI_EVENT_NONE:
+ strcpy(str_event,"NONE");
+ break;
+ case MIDI_EVENT_NOTE:
+ strcpy(str_event,"NOTE");
+ break;
+ case MIDI_EVENT_CTRL:
+ strcpy(str_event,"CTRL");
+ break;
+ case MIDI_EVENT_PITCH:
+ strcpy(str_event,"PITCH");
+ break;
+ }
+ sprintf(str_channel,"%d",current_cmd->channel);
+ sprintf(str_note,"%d",thisNote);
+
+ 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_set(store,&iter,
+ EVENT_COLUMN,str_event,
+ CHANNEL_COLUMN,str_channel,
+ NOTE_COLUMN,str_note,
+ TYPE_COLUMN,str_type,
+ ACTION_COLUMN,str_action,
+ -1);
+}
+
+static void delete_cb(GtkButton *widget,gpointer user_data) {
+ struct desc *previous_cmd;
+ struct desc *next_cmd;
+ GtkTreeIter saved_iter;
+ g_print("%s: thisNote=%d current_cmd=%p\n",__FUNCTION__,thisNote,current_cmd);
+
+ if (current_cmd == NULL) {
+ g_print("%s: current_cmd is NULL!\n", __FUNCTION__);
+ return;
+ }
+
+ saved_iter=iter;
+
+
+ // remove from MidiCommandsTable
+ if(MidiCommandsTable[thisNote]==current_cmd) {
+ g_print("%s: remove first\n",__FUNCTION__);
+ MidiCommandsTable[thisNote]=current_cmd->next;
+ g_free(current_cmd);
+ } else {
+ previous_cmd=MidiCommandsTable[thisNote];
+ while(previous_cmd->next!=NULL) {
+ next_cmd=previous_cmd->next;
+ if(next_cmd==current_cmd) {
+ g_print("%s: remove next\n",__FUNCTION__);
+ previous_cmd->next=next_cmd->next;
+ g_free(next_cmd);
+ break;
+ }
+ previous_cmd=next_cmd;
+ }
+ }
+
+ // remove from list store
+ gtk_list_store_remove(store,&saved_iter);
+
+ gtk_widget_set_sensitive(add_b,TRUE);
+ gtk_widget_set_sensitive(update_b,FALSE);
+ gtk_widget_set_sensitive(delete_b,FALSE);
+
+}
+
+void midi_menu(GtkWidget *parent) {
+ int i;
+ int col=0;
+ int row=0;
+ GtkCellRenderer *renderer;
+
+ dialog=gtk_dialog_new();
+ gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent_window));
+ char title[64];
+ sprintf(title,"piHPSDR - MIDI");
+ gtk_window_set_title(GTK_WINDOW(dialog),title);
+ g_signal_connect (dialog, "delete_event", G_CALLBACK (delete_event), NULL);
+
+ GdkRGBA color;
+ color.red = 1.0;
+ color.green = 1.0;
+ color.blue = 1.0;
+ color.alpha = 1.0;
+ gtk_widget_override_background_color(dialog,GTK_STATE_FLAG_NORMAL,&color);
+
+ GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+ GtkWidget *grid=gtk_grid_new();
+ gtk_grid_set_column_spacing (GTK_GRID(grid),2);
+
+ row=0;
+ col=0;
+
+ get_midi_devices();
+ if(n_midi_devices>0) {
+ 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<n_midi_devices;i++) {
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(devices),NULL,midi_devices[i].name);
+ if(midi_device_name!=NULL) {
+ if(strcmp(midi_device_name,midi_devices[i].name)==0) {
+ device_index=i;
+ }
+ }
+ }
+ 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);
+ }
+ 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);
+ g_signal_connect(midi_enable_b,"toggled",G_CALLBACK(midi_enable_cb),NULL);
+
+ row++;
+ col=0;
+
+ GtkWidget *clear_b=gtk_button_new_with_label("Clear");
+ gtk_grid_attach(GTK_GRID(grid),clear_b,col,row,1,1);
+ g_signal_connect(clear_b,"clicked",G_CALLBACK(clear_cb),NULL);
+ col++;
+
+ GtkWidget *save_b=gtk_button_new_with_label("Save");
+ gtk_grid_attach(GTK_GRID(grid),save_b,col,row,1,1);
+ g_signal_connect(save_b,"clicked",G_CALLBACK(save_cb),NULL);
+ col++;
+
+ GtkWidget *load_b=gtk_button_new_with_label("Load");
+ gtk_grid_attach(GTK_GRID(grid),load_b,col,row,1,1);
+ g_signal_connect(load_b,"clicked",G_CALLBACK(load_cb),NULL);
+ col++;
+
+ GtkWidget *load_original_b=gtk_button_new_with_label("Load Original");
+ gtk_grid_attach(GTK_GRID(grid),load_original_b,col,row,1,1);
+ 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");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+ label=gtk_label_new("Ch");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+ label=gtk_label_new("Note");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+ label=gtk_label_new("Type");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+ label=gtk_label_new("Value");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+ label=gtk_label_new("Min");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+ label=gtk_label_new("Max");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+ label=gtk_label_new("Action");
+ gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+
+
+ row++;
+ col=0;
+ newEvent=gtk_label_new("");
+ gtk_grid_attach(GTK_GRID(grid),newEvent,col++,row,1,1);
+ newChannel=gtk_label_new("");
+ gtk_grid_attach(GTK_GRID(grid),newChannel,col++,row,1,1);
+ newNote=gtk_label_new("");
+ gtk_grid_attach(GTK_GRID(grid),newNote,col++,row,1,1);
+ newType=gtk_combo_box_text_new();
+ gtk_grid_attach(GTK_GRID(grid),newType,col++,row,1,1);
+ g_signal_connect(newType,"changed",G_CALLBACK(type_changed_cb),NULL);
+ newVal=gtk_label_new("");
+ gtk_grid_attach(GTK_GRID(grid),newVal,col++,row,1,1);
+ newMin=gtk_label_new("");
+ gtk_grid_attach(GTK_GRID(grid),newMin,col++,row,1,1);
+ newMax=gtk_label_new("");
+ gtk_grid_attach(GTK_GRID(grid),newMax,col++,row,1,1);
+ newAction=gtk_combo_box_text_new();
+ gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(newAction),5);
+ gtk_grid_attach(GTK_GRID(grid),newAction,col++,row,1,1);
+
+ add_b=gtk_button_new_with_label("Add");
+ g_signal_connect(add_b, "pressed", G_CALLBACK(add_cb),NULL);
+ gtk_grid_attach(GTK_GRID(grid),add_b,col++,row,1,1);
+ gtk_widget_set_sensitive(add_b,FALSE);
+
+
+ update_b=gtk_button_new_with_label("Update");
+ g_signal_connect(update_b, "pressed", G_CALLBACK(update_cb),NULL);
+ gtk_grid_attach(GTK_GRID(grid),update_b,col++,row,1,1);
+ gtk_widget_set_sensitive(update_b,FALSE);
+
+ delete_b=gtk_button_new_with_label("Delete");
+ g_signal_connect(delete_b, "pressed", G_CALLBACK(delete_cb),NULL);
+ gtk_grid_attach(GTK_GRID(grid),delete_b,col++,row,1,1);
+ gtk_widget_set_sensitive(delete_b,FALSE);
+ row++;
+ col=0;
+
+ scrolled_window=gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),GTK_POLICY_AUTOMATIC,GTK_POLICY_ALWAYS);
+ gtk_widget_set_size_request(scrolled_window,400,300);
+
+ view=gtk_tree_view_new();
+
+ renderer=gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Event", renderer, "text", EVENT_COLUMN, NULL);
+
+ renderer=gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "CHANNEL", renderer, "text", CHANNEL_COLUMN, NULL);
+
+ renderer=gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "NOTE", renderer, "text", NOTE_COLUMN, NULL);
+
+ renderer=gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "TYPE", renderer, "text", TYPE_COLUMN, NULL);
+
+ renderer=gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "ACTION", renderer, "text", ACTION_COLUMN, NULL);
+
+ store=gtk_list_store_new(N_COLUMNS,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING);
+
+ load_store();
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
+
+ gtk_container_add(GTK_CONTAINER(scrolled_window),view);
+
+ gtk_grid_attach(GTK_GRID(grid), scrolled_window, col, row, 6, 10);
+
+ model=gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+ g_signal_connect(model,"row-inserted",G_CALLBACK(row_inserted_cb),NULL);
+
+ selection=gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ selection_signal_id=g_signal_connect(G_OBJECT(selection),"changed",G_CALLBACK(tree_selection_changed_cb),NULL);
+
+ gtk_container_add(GTK_CONTAINER(content),grid);
+ sub_menu=dialog;
+ gtk_widget_show_all(dialog);
+}
+
+static int update(void *data) {
+ int state=GPOINTER_TO_INT(data);
+ gchar text[32];
+ gint i=1;
+ gint j;
+
+ switch(state) {
+ case UPDATE_NEW:
+ g_print("%s: UPDATE_NEW\n",__FUNCTION__);
+ switch(thisEvent) {
+ case MIDI_EVENT_NONE:
+ gtk_label_set_text(GTK_LABEL(newEvent),"NONE");
+ break;
+ case MIDI_EVENT_NOTE:
+ gtk_label_set_text(GTK_LABEL(newEvent),"NOTE");
+ break;
+ case MIDI_EVENT_CTRL:
+ gtk_label_set_text(GTK_LABEL(newEvent),"CTRL");
+ break;
+ case MIDI_EVENT_PITCH:
+ gtk_label_set_text(GTK_LABEL(newEvent),"PITCH");
+ break;
+ }
+ sprintf(text,"%d",thisChannel);
+ gtk_label_set_text(GTK_LABEL(newChannel),text);
+ sprintf(text,"%d",thisNote);
+ gtk_label_set_text(GTK_LABEL(newNote),text);
+ gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newType));
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"NONE");
+ switch(thisEvent) {
+ case MIDI_EVENT_NONE:
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+ break;
+ case MIDI_EVENT_NOTE:
+ case MIDI_EVENT_PITCH:
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KEY");
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),1);
+ break;
+ case MIDI_EVENT_CTRL:
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KNOB/SLIDER");
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"WHEEL");
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+ break;
+ }
+ gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newAction));
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,"NONE");
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),0);
+ if(thisEvent==MIDI_EVENT_PITCH || thisEvent==MIDI_EVENT_NOTE) {
+ i=1;
+ j=0;
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(ActionTable[i].type&MIDI_TYPE_KEY) {
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+ if(ActionTable[i].action==thisAction) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(newAction),j);
+ }
+ j++;
+ }
+ i++;
+ }
+
+ }
+ sprintf(text,"%d",thisVal);
+ gtk_label_set_text(GTK_LABEL(newVal),text);
+ sprintf(text,"%d",thisMin);
+ gtk_label_set_text(GTK_LABEL(newMin),text);
+ sprintf(text,"%d",thisMax);
+ gtk_label_set_text(GTK_LABEL(newMax),text);
+
+ gtk_widget_set_sensitive(add_b,TRUE);
+ gtk_widget_set_sensitive(update_b,FALSE);
+ gtk_widget_set_sensitive(delete_b,FALSE);
+ break;
+
+ case UPDATE_CURRENT:
+ g_print("%s: UPDATE_CURRENT\n",__FUNCTION__);
+ sprintf(text,"%d",thisVal);
+ gtk_label_set_text(GTK_LABEL(newVal),text);
+ sprintf(text,"%d",thisMin);
+ gtk_label_set_text(GTK_LABEL(newMin),text);
+ sprintf(text,"%d",thisMax);
+ gtk_label_set_text(GTK_LABEL(newMax),text);
+ break;
+
+ case UPDATE_EXISTING:
+ g_print("%s: UPDATE_EXISTING\n",__FUNCTION__);
+ switch(thisEvent) {
+ case MIDI_EVENT_NONE:
+ gtk_label_set_text(GTK_LABEL(newEvent),"NONE");
+ break;
+ case MIDI_EVENT_NOTE:
+ gtk_label_set_text(GTK_LABEL(newEvent),"NOTE");
+ break;
+ case MIDI_EVENT_CTRL:
+ gtk_label_set_text(GTK_LABEL(newEvent),"CTRL");
+ break;
+ case MIDI_EVENT_PITCH:
+ gtk_label_set_text(GTK_LABEL(newEvent),"PITCH");
+ break;
+ }
+ sprintf(text,"%d",thisChannel);
+ gtk_label_set_text(GTK_LABEL(newChannel),text);
+ sprintf(text,"%d",thisNote);
+ gtk_label_set_text(GTK_LABEL(newNote),text);
+ gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newType));
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"NONE");
+ switch(thisEvent) {
+ case MIDI_EVENT_NONE:
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+ break;
+ case MIDI_EVENT_NOTE:
+ case MIDI_EVENT_PITCH:
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KEY");
+ if(thisType==MIDI_TYPE_NONE) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+ } else if(thisType==MIDI_TYPE_KEY) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),1);
+ }
+ break;
+ case MIDI_EVENT_CTRL:
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KNOB/SLIDER");
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"WHEEL");
+ if(thisType==MIDI_TYPE_NONE) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+ } else if(thisType==MIDI_TYPE_KNOB) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),1);
+ } else if(thisType==MIDI_TYPE_WHEEL) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(newType),2);
+ }
+ break;
+ }
+ sprintf(text,"%d",thisVal);
+ gtk_label_set_text(GTK_LABEL(newVal),text);
+ sprintf(text,"%d",thisMin);
+ gtk_label_set_text(GTK_LABEL(newMin),text);
+ sprintf(text,"%d",thisMax);
+ gtk_label_set_text(GTK_LABEL(newMax),text);
+
+ find_current_cmd();
+ g_print("%s: current_cmd %p\n",__FUNCTION__,current_cmd);
+
+ gtk_widget_set_sensitive(add_b,FALSE);
+ gtk_widget_set_sensitive(update_b,TRUE);
+ gtk_widget_set_sensitive(delete_b,TRUE);
+ break;
+
+ }
+
+ return 0;
+}
+
+void NewMidiConfigureEvent(enum MIDIevent event, int channel, int note, int val) {
+
+ gboolean valid;
+ char *str_event;
+ char *str_channel;
+ char *str_note;
+ char *str_type;
+ char *str_action;
+
+ gint tree_event;
+ gint tree_channel;
+ gint tree_note;
+
+ //g_print("%s: event=%d channel=%d note=%d val=%d\n", __FUNCTION__,event,channel,note,val);
+
+ if(event==thisEvent && channel==thisChannel && note==thisNote) {
+ //g_print("%s: current event\n",__FUNCTION__);
+ thisVal=val;
+ if(val<thisMin) thisMin=val;
+ if(val>thisMax) thisMax=val;
+ g_idle_add(update,GINT_TO_POINTER(UPDATE_CURRENT));
+ } else {
+ //g_print("%s: new or existing event\n",__FUNCTION__);
+ thisEvent=event;
+ thisChannel=channel;
+ thisNote=note;
+ thisVal=val;
+ thisMin=val;
+ thisMax=val;
+ thisType=MIDI_TYPE_NONE;
+ thisAction=MIDI_ACTION_NONE;
+
+ // search tree to see if it is existing event
+ valid=gtk_tree_model_get_iter_first(model,&iter);
+ while(valid) {
+ gtk_tree_model_get(model, &iter, EVENT_COLUMN, &str_event, -1);
+ gtk_tree_model_get(model, &iter, CHANNEL_COLUMN, &str_channel, -1);
+ gtk_tree_model_get(model, &iter, NOTE_COLUMN, &str_note, -1);
+ gtk_tree_model_get(model, &iter, TYPE_COLUMN, &str_type, -1);
+ gtk_tree_model_get(model, &iter, ACTION_COLUMN, &str_action, -1);
+
+ //g_print("%s: %s %s %s %s %s\n",__FUNCTION__,str_event,str_channel,str_note,str_type,str_action);
+
+ if(str_event!=NULL && str_channel!=NULL && str_note!=NULL && str_type!=NULL && str_action!=NULL) {
+ if(strcmp(str_event,"CTRL")==0) {
+ tree_event=MIDI_EVENT_CTRL;
+ } else if(strcmp(str_event,"PITCH")==0) {
+ tree_event=MIDI_EVENT_PITCH;
+ } else if(strcmp(str_event,"NOTE")==0) {
+ tree_event=MIDI_EVENT_NOTE;
+ } else {
+ tree_event=MIDI_EVENT_NONE;
+ }
+ tree_channel=atoi(str_channel);
+ tree_note=atoi(str_note);
+
+ if(thisEvent==tree_event && (thisChannel==tree_channel || tree_channel==-1) && thisNote==tree_note) {
+ thisVal=0;
+ thisMin=0;
+ thisMax=0;
+ if(strcmp(str_type,"KEY")==0) {
+ thisType=MIDI_TYPE_KEY;
+ } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+ thisType=MIDI_TYPE_KNOB;
+ } else if(strcmp(str_type,"WHEEL")==0) {
+ thisType=MIDI_TYPE_WHEEL;
+ } else {
+ thisType=MIDI_TYPE_NONE;
+ }
+ thisAction=MIDI_ACTION_NONE;
+ int i=1;
+ while(ActionTable[i].action!=MIDI_ACTION_NONE) {
+ if(strcmp(ActionTable[i].str,str_action)==0) {
+ thisAction=ActionTable[i].action;
+ break;
+ }
+ i++;
+ }
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(view),gtk_tree_model_get_path(model,&iter),NULL,FALSE);
+ g_idle_add(update,GINT_TO_POINTER(UPDATE_EXISTING));
+ return;
+ }
+ }
+
+ valid=gtk_tree_model_iter_next(model,&iter);
+ }
+
+ g_idle_add(update,GINT_TO_POINTER(UPDATE_NEW));
+ }
+}
+
+void midi_save_state() {
+ char name[80];
+ char value[80];
+ struct desc *cmd;
+ gint index;
+
+ if(device_index!=-1) {
+ setProperty("midi_device",midi_devices[device_index].name);
+ // the value i=128 is for the PitchBend
+ for(int i=0;i<129;i++) {
+ 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);
+
+ //
+ // There might be events that share the channel and the note value (one NOTE and one CTRL, for example)
+ // These must not share the same key in the property database so the "running index" must be part of the key
+ //
+
+ sprintf(name,"midi[%d].channel[%d].index[%d].event",i,cmd->channel,index);
+ setProperty(name,midi_events[cmd->event]);
+
+ sprintf(name,"midi[%d].channel[%d].index[%d].type",i,cmd->channel,index);
+ setProperty(name,midi_types[cmd->type]);
+
+ sprintf(name,"midi[%d].channel[%d].index[%d].action",i,cmd->channel,index);
+ setProperty(name,(char *) ActionTable[cmd->action].str);
+
+ cmd=cmd->next;
+ index++;
+ }
+
+ if(index!=0) {
+ sprintf(name,"midi[%d].indices",i);
+ sprintf(value,"%d",index);
+ setProperty(name,value);
+ }
+
+ }
+ }
+}
+
+void midi_restore_state() {
+ char name[80];
+ char *value;
+ gint indices;
+ gint channel;
+ gint event;
+ gint onoff;
+ gint type;
+ gint action;
+
+ struct desc *cmd, *loop;
+
+ get_midi_devices();
+ 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<n_midi_devices;i++) {
+ if(strcmp(midi_devices[i].name,value)==0) {
+ device_index=i;
+ g_print("%s: found device at %d\n",__FUNCTION__,i);
+ break;
+ }
+ }
+ }
+
+ // the value i=128 is for the PitchBend
+ for(int i=0;i<129;i++) {
+ sprintf(name,"midi[%d].indices",i);
+ value=getProperty(name);
+ if(value) {
+ indices=atoi(value);
+ for(int index=0; index<indices; index++) {
+ sprintf(name,"midi[%d].channel[%d].index[%d].event",i,channel,index);
+ value=getProperty(name);
+ event=MIDI_EVENT_NONE;
+ if(value) {
+ for(int j=0;j<4;j++) {
+ if(strcmp(value,midi_events[j])==0) {
+ event=j;
+ break;
+ }
+ }
+ }
+ sprintf(name,"midi[%d].channel[%d].index[%d],type",i,channel,index);
+ value=getProperty(name);
+ type=MIDI_TYPE_NONE;
+ if(value) {
+ for(int j=0;j<5;j++) {
+ if(strcmp(value,midi_types[j])==0) {
+ type=j;
+ break;
+ }
+ }
+ }
+ sprintf(name,"midi[%d].channel[%d].index[%d].action",i,channel,index);
+ value=getProperty(name);
+ action=MIDI_ACTION_NONE;
+ if(value) {
+ int j=1;
+ while(ActionTable[j].type!=MIDI_ACTION_NONE) {
+ if(strcmp(value,ActionTable[j].str)==0) {
+ action=ActionTable[j].action;
+ break;
+ }
+ j++;
+ }
+ }
+
+ struct desc *desc;
+ desc = (struct desc *) malloc(sizeof(struct desc));
+ desc->next = NULL;
+ desc->action = action; // MIDIaction
+ desc->type = type; // MIDItype
+ desc->event = event; // MIDevent
+ desc->onoff = ActionTable[action].onoff;
+ desc->delay = 0;
+ desc->vfl1 = -1;
+ desc->vfl2 = -1;
+ desc->fl1 = -1;
+ desc->fl2 = -1;
+ desc->lft1 = -1;
+ desc->lft2 = 63;
+ desc->rgt1 = 64;
+ desc->rgt2 = 128;
+ desc->fr1 = 128;
+ desc->fr2 = 128;
+ desc->vfr1 = 128;
+ 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);
+
+ MidiAddCommand(i, desc);
+ } // index
+ } // if (value)
+ } // for (int i)
+}
+
--- /dev/null
+/* Copyright (C)
+* 2021 - John Melton, G0ORX/N6LYT
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+*/
+
+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();
+extern void midi_restore_state();
+
#ifdef CLIENT_SERVER
#include "server_menu.h"
#endif
+#ifdef MIDI
+#include "midi_menu.h"
+#endif
static GtkWidget *menu_b=NULL;
}
#endif
+#ifdef MIDI
+void start_midi() {
+ cleanup();
+ midi_menu(top_window);
+}
+
+static gboolean midi_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
+ start_midi();
+ return TRUE;
+}
+#endif
+
void new_menu()
{
int i;
}
#endif
+#ifdef MIDI
+ GtkWidget *midi_b=gtk_button_new_with_label("MIDI");
+ g_signal_connect (midi_b, "button-press-event", G_CALLBACK(midi_cb), NULL);
+ gtk_grid_attach(GTK_GRID(grid),midi_b,(i%5),i/5,1,1);
+ i++;
+#endif
+
//
// We need at least two receivers and two ADCs to do DIVERSITY
//
#endif
#include "rigctl_menu.h"
#ifdef MIDI
-// rather than including MIDI.h with all its internal stuff
-// (e.g. enum components) we just declare the single bit thereof
-// we need here to make a strict compiler happy.
-void MIDIstartup();
+#include "alsa_midi.h"
+#include "midi_menu.h"
#endif
#ifdef CLIENT_SERVER
#include "client_server.h"
#define TOOLBAR_HEIGHT (30)
#define WATERFALL_HEIGHT (105)
+#ifdef MIDI
+gboolean midi_enabled = false;
+#endif
+
GtkWidget *fixed;
static GtkWidget *vfo_panel;
static GtkWidget *meter;
// running. So this is the last thing we do when starting the radio.
//
#ifdef MIDI
- MIDIstartup();
+ 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;
+ }
+ } else {
+ midi_enabled=FALSE;
+ }
#endif
#ifdef CLIENT_SERVER
}
#endif
+#ifdef MIDI
+ midi_restore_state();
+ value=getProperty("radio.midi_enabled");
+ if(value) midi_enabled=atoi(value);
+#endif
+
value=getProperty("radio.display_sequence_errors");
if(value!=NULL) display_sequence_errors=atoi(value);
}
#endif
+#ifdef MIDI
+ sprintf(value,"%d",midi_enabled);
+ setProperty("radio.midi_enabled",value);
+ midi_save_state();
+#endif
+
saveProperties(property_path);
g_mutex_unlock(&property_mutex);
}
reconfigure_radio();
g_idle_add(ext_vfo_update,(gpointer)NULL);
gdk_window_set_cursor(gtk_widget_get_window(top_window),gdk_cursor_new(GDK_ARROW));
-#ifdef MIDI
- MIDIstartup();
-#endif
for(int i=0;i<receivers;i++) {
gint timer_id=gdk_threads_add_timeout_full(G_PRIORITY_DEFAULT_IDLE,100, start_spectrum, receiver[i], NULL);
}
extern TRANSMITTER *transmitter;
+#ifdef MIDI
+extern gboolean midi_enabled;
+#endif
#define PA_DISABLED 0
#define PA_ENABLED 1