From f322810735dc77f95a23d094f57da645319e4500 Mon Sep 17 00:00:00 2001 From: c vw Date: Thu, 6 May 2021 11:41:10 +0200 Subject: [PATCH] Merged MIDI menu (not yet complete) --- alsa_midi.h | 12 + mac_midi.c | 138 ++++-- midi.h | 91 +++- midi2.c | 343 +++++++++----- midi3.c | 15 +- midi_menu.c | 1253 +++++++++++++++++++++++++++++++++++++++++++++++++++ midi_menu.h | 25 + new_menu.c | 22 + radio.c | 34 +- radio.h | 3 + 10 files changed, 1749 insertions(+), 187 deletions(-) create mode 100644 alsa_midi.h create mode 100644 midi_menu.c create mode 100644 midi_menu.h diff --git a/alsa_midi.h b/alsa_midi.h new file mode 100644 index 0000000..02acc84 --- /dev/null +++ b/alsa_midi.h @@ -0,0 +1,12 @@ +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); diff --git a/mac_midi.c b/mac_midi.c index 60c7b01..36570b5 100644 --- a/mac_midi.c +++ b/mac_midi.c @@ -27,10 +27,30 @@ * */ +#include +#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 @@ -43,6 +63,10 @@ #include #include +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. @@ -69,6 +93,8 @@ static enum { 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; @@ -119,19 +145,39 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co // 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; @@ -142,40 +188,36 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co } // j-loop through the list of packets } +void close_midi_device() { + fprintf(stderr,"%s\n",__FUNCTION__); +} -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%s<\n", name); - } else { - fprintf(stderr,"MIDI: looking for >%s< so >%s< does not match\n", myname,name); - } + for (i=0; i>>%s<<<\n", midi_devices[i].name); + } else { + fprintf(stderr,"MIDI device found BUT NOT SELECTED: >>>%s<<<\n", midi_devices[i].name); } } @@ -190,6 +232,46 @@ void register_midi_device(char *myname) { 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 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 @@ -76,6 +98,13 @@ enum MIDIaction { 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 @@ -83,17 +112,24 @@ enum MIDIaction { 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 @@ -148,12 +184,15 @@ enum MIDIaction { // 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 @@ -167,6 +206,15 @@ enum MIDIevent { 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 // @@ -212,17 +260,18 @@ struct desc { 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) @@ -235,7 +284,9 @@ void register_midi_device(char *name); // 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 diff --git a/midi2.c b/midi2.c index 50c6743..e13b36e 100644 --- a/midi2.c +++ b/midi2.c @@ -8,6 +8,7 @@ */ #include + #include #include #include @@ -16,12 +17,19 @@ #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) { @@ -53,13 +61,13 @@ 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) { @@ -83,15 +91,18 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) { } // 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; } @@ -113,101 +124,139 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) { } 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 */ @@ -227,12 +276,64 @@ static void keyword2action(char *s, enum MIDIaction *action, int *onoff) { *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; @@ -248,11 +349,16 @@ void MIDIstartup() { 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; @@ -277,7 +383,7 @@ void MIDIstartup() { 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 @@ -290,17 +396,17 @@ void MIDIstartup() { 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 @@ -311,18 +417,18 @@ void MIDIstartup() { 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 @@ -342,16 +448,16 @@ void MIDIstartup() { 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", @@ -364,7 +470,7 @@ void MIDIstartup() { 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! @@ -395,23 +501,12 @@ void MIDIstartup() { // 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; } diff --git a/midi3.c b/midi3.c index 38ea44a..ab5045e 100644 --- a/midi3.c +++ b/midi3.c @@ -39,7 +39,7 @@ // 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 @@ -61,7 +61,8 @@ typedef struct _MIDIcmd MIDIcmd; 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. @@ -85,11 +86,11 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) { 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; } @@ -511,7 +512,7 @@ int DoTheRestOfTheMIDI(void *data) { /////////////////////////////////////////////////////////// "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; @@ -666,8 +667,8 @@ int DoTheRestOfTheMIDI(void *data) { 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); diff --git a/midi_menu.c b/midi_menu.c new file mode 100644 index 0000000..766e7dd --- /dev/null +++ b/midi_menu.c @@ -0,0 +1,1253 @@ +/* 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 +#include +#include +#include +#include +#include + +#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;ithisMax) 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;inext = 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) +} + diff --git a/midi_menu.h b/midi_menu.h new file mode 100644 index 0000000..bed7e0a --- /dev/null +++ b/midi_menu.h @@ -0,0 +1,25 @@ +/* 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(); + diff --git a/new_menu.c b/new_menu.c index 9c7bf7c..cf9b4a0 100644 --- a/new_menu.c +++ b/new_menu.c @@ -63,6 +63,9 @@ #ifdef CLIENT_SERVER #include "server_menu.h" #endif +#ifdef MIDI +#include "midi_menu.h" +#endif static GtkWidget *menu_b=NULL; @@ -466,6 +469,18 @@ static gboolean server_cb (GtkWidget *widget, GdkEventButton *event, gpointer da } #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; @@ -625,6 +640,13 @@ void new_menu() } #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 // diff --git a/radio.c b/radio.c index 2e7c649..e086865 100644 --- a/radio.c +++ b/radio.c @@ -73,10 +73,8 @@ #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" @@ -97,6 +95,10 @@ void MIDIstartup(); #define TOOLBAR_HEIGHT (30) #define WATERFALL_HEIGHT (105) +#ifdef MIDI +gboolean midi_enabled = false; +#endif + GtkWidget *fixed; static GtkWidget *vfo_panel; static GtkWidget *meter; @@ -1302,7 +1304,14 @@ void start_radio() { // 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 @@ -2131,6 +2140,12 @@ g_print("radioRestoreState: %s\n",property_path); } #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); @@ -2457,6 +2472,12 @@ g_print("radioSaveState: %s\n",property_path); } #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); } @@ -2564,9 +2585,6 @@ int remote_start(void *data) { 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