/*
* midi.c
* 27-sep-2004/FK Symbolic MIDI controller assignment.
* 16-sep-2004/FK Struggling with channel A split.
* NOTE: Drop the leftmost-rightmost stuff and this line when done.
* 10-sep-2004/FK It basically works. Whee...
* 09-sep-2004/FK Close to testing.
* 05-sep-2004/FK Work continues (slowly)
* 28-aug-2004/FK Working...
* 25-aug-2004/FK Generalised version of midiIn.c.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/soundcard.h>
#include <limits.h>
#include <assert.h>
#include "config.h"
#include "midi.h"
#include "tonegen.h"
#include "preamp.h"
#include "scanner.h"
#include "program.h"
#include "whirl.h"
/*
* The size of the MIDI input buffer.
*/
#define BUFFER_SIZE_BYTES 1024
/*
* Symbolic names for functions that can be governed by MIDI controllers.
* These names are used by the configuration machinery to allow arbitrary
* mappings of controllers. This is how it works:
* Other modules, like the tonegenerator and the effects, call the function
* useMIDIControlFunction (name, function)
* with a name and a pointer to a function. The function implements some
* reaction to a MIDI controller, e.g. a drawbar setting. The name is a
* symbolic name that identifies the function. The name must be defined
* in array ccFuncNames[] (immediately below) and its position in the array
* is used as an opaque numeric id for the reaction. That numeric id is then
* used to examine the three arrays ctrlUseA, ctrlUseB and ctrlUseC in turn.
* When the accessed element contain a value less than 128 the function is
* entered in the corresponding ctrlvecA, ctrlvecB and ctrlvecC table, which
* are the runtime maps from MIDI controller numbers to functions.
*
* If you are with me so far, the tables cltrUse* define how reactions are
* mapped to MIDI controllers. The ctrlUse* tables are filled in on two
* occasions. The first, that always take place, is the function
* midiPrimeControllerMapping()
* which simplay provides the default initialization.
* The second, which MAY happen, is the configuration processing where the
* user can provide config options on the format:
* midi.controller.{upper,lower,pedals}.<cc>=<name>
* Here, again, the <name> is defined in ccFuncNames and the keywords upper,
* lower and pedals will determine which of the three ctrlUse* tables that are
* updated with the controller number <cc>.
*/
char * ccFuncNames[] = {
"upper.drawbar16",
"upper.drawbar513",
"upper.drawbar8",
"upper.drawbar4",
"upper.drawbar223",
"upper.drawbar2",
"upper.drawbar135",
"upper.drawbar113",
"upper.drawbar1",
"lower.drawbar16",
"lower.drawbar513",
"lower.drawbar8",
"lower.drawbar4",
"lower.drawbar223",
"lower.drawbar2",
"lower.drawbar135",
"lower.drawbar113",
"lower.drawbar1",
"pedal.drawbar16",
"pedal.drawbar513",
"pedal.drawbar8",
"pedal.drawbar4",
"pedal.drawbar223",
"pedal.drawbar2",
"pedal.drawbar135",
"pedal.drawbar113",
"pedal.drawbar1",
"percussion.enable", /* off/normal/soft/off */
"percussion.decay", /* fast/slow */
"percussion.harmonic", /* 3rd/2nd */
"vibrato.knob", /* off/v1/c1/v2/c2/v3/c3 */
"vibrato.routing", /* off/lower/upper/both */
"swellpedal1", /* Volume, for primary ctrlr (mod wheel) */
"swellpedal2", /* Volume, for secondary ctrlr (expression) */
"overdrive.character",
"rotary.speed-select", /* stop, slow, fast, stop */
"rotary.speed-toggle", /* sustain pedal */
"whirl.horn.filter.a.type",
"whirl.horn.filter.a.hz",
"whirl.horn.filter.a.q",
"whirl.horn.filter.a.gain",
"whirl.horn.filter.b.type",
"whirl.horn.filter.b.hz",
"whirl.horn.filter.b.q",
"whirl.horn.filter.b.gain",
"whirl.comb.a.feedback",
"whirl.comb.a.delay",
"whirl.comb.b.feedback",
"whirl.comb.b.delay",
"whirl.drum.filter.type",
"whirl.drum.filter.hz",
"whirl.drum.filter.q",
"whirl.drum.filter.gain",
"xov.ctl_biased",
"xov.ctl_biased_fb",
"xov.ctl_biased_fb2",
"xov.ctl_biased_gfb",
"xov.ctl_sagtobias",
"xov.inputgain",
"xov.outputgain",
NULL
};
/*
* States for the MIDI parser.
*/
enum parserState {
stIgnore, dtaIgnore,
stNoteOff, dtaNoteOff1, dtaNoteOff2,
stNoteOn, dtaNoteOn1, dtaNoteOn2,
stControlChg, dtaControlChg1, dtaControlChg2,
stProgramChg, dtaProgramChg
};
/*
* The row definition of the statusTable. The state field tells the parser
* where to go next and the handback is a pointer to further data dependent
* on the channel encapsulated in the status byte.
*/
typedef struct statusTableRowStruct {
int state;
void * handback;
} StatusTableRow;
/* Indexed by the MIDI status byte, positions 0-127 are unused because
* they represent data bytes. Wasteful? We trade 128*8=1024 bytes for
* a few CPU cycles.
*/
static StatusTableRow statusTable[256];
/* Used by the MIDI parser to record message bytes */
static unsigned char byte1;
static unsigned char byte2;
static unsigned char byte3;
static int midi_fd; /* File descriptor for the MIDI device */
static char * midiDevice = "/dev/midi00"; /* Default path to MIDI device */
static char midiDeviceBuffer[PATH_MAX + 1]; /* Actual path to MIDI device */
static unsigned char rcvChA = 0; /* MIDI receive channel */
static unsigned char rcvChB = 1; /* MIDI receive channel */
static unsigned char rcvChC = 2; /* MIDI receive channel */
/*
* The all channel transpose is used to transpose the entire instrument
* into a different mode and is a common function on synthesizers.
* The channel- and split-dependent transposes are for adjusting the
* reach of the user's MIDI controller(s) to the desired range.
*/
static int transpose = 0; /* All channel transpose */
static int nshA = 0; /* Channel A transpose (no split) */
static int nshA_U = 0; /* Channel A upper region transpose */
static int nshA_PL = 0; /* Channel A pedal region transpose */
static int nshA_UL = 0; /* Channel A lower region transpose */
static int nshB = 0; /* Channel B transpose */
static int nshC = 0; /* Channel C transpose */
static int splitA_PL = 0; /* A channel pedal region */
static int splitA_UL = 0; /* A channel lower region */
/*
* This flag controls how to map MIDI input that falls outside of
* the virtual instrument's manuals. A value of zero means such MIDI notes
* make no sound. A value of one means that the MIDI notes map back
* into the nearest octave with a playable key, a mechanism similar to
* the foldback used in some organ models.
*/
static int userExcursionStrategy = 0;
#ifdef COMMENT
/* MIDI note numbers on the user's controller. */
static int leftmost_MIDINoteA = 36; /* Input device ch A (default 61 key) */
static int rightmostMIDINoteA = 96; /* Input device ch A (default 61 key) */
static int leftmost_MIDINoteB = 36; /* Input device ch B (default 61 key) */
static int rightmostMIDINoteB = 96; /* Input device ch B (default 61 key) */
static int leftmost_MIDINoteC = 36; /* Input device ch C (default 61 key) */
static int rightmostMIDINoteC = 96; /* Input device ch C (default 61 key) */
#endif /* COMMENT */
static unsigned char inBuffer [BUFFER_SIZE_BYTES]; /* Input buffer */
static unsigned char keyTableA[128]; /* MIDI note to Beatrix key transl. tbl */
static unsigned char keyTableB[128]; /* MIDI note to Beatrix key transl. tbl */
static unsigned char keyTableC[128]; /* MIDI note to Beatrix key transl. tbl */
static unsigned char * keyTable; /* Activated table */
/* Arrays that map from usage codes to MIDI controller numbers. */
#define CTRL_USE_MAX 128
static unsigned char ctrlUseA[CTRL_USE_MAX];
static unsigned char ctrlUseB[CTRL_USE_MAX];
static unsigned char ctrlUseC[CTRL_USE_MAX];
/* Arrays of pointers to functions that handle controller values. */
static void (*ctrlvecA[128])(unsigned char uc);
static void (*ctrlvecB[128])(unsigned char uc);
static void (*ctrlvecC[128])(unsigned char uc);
static void (**ctrlvec) (unsigned char); /* Activated table */
static unsigned char controllerMap[3][128];
#define IS_SYSTEM_REAL_TIME(C) ((0xF8 & (C)) == 0xF8)
#define IS_STATUS(C) ((C) & 0x80)
#define GET_MIDI_CHANNEL(C) ((C) & 0x0F)
#define IS_CHANNEL(C) ((0 <= (C)) && ((C) < 16))
/* ---------------------------------------------------------------- */
/*
*
*/
static int getCCFunctionId (const char * name) {
int i;
assert (name != NULL);
for (i = 0; ccFuncNames
!= NULL; i++) {
if (0 == strcmp (name, ccFuncNames)) {
return i;
}
}
return -1;
}
/*
* This is the default (empty) controller function. It does nothing at all
* and therefore we initialize the ctrlvec tables with pointers to it.
*/
static void emptyControlFunction (unsigned char uc) {}
/*
* Assigns a control function to a MIDI controller.
* This function is programmed to recognise multiple allocations that do
* not refer to the no-op function. It MAY be an error if a controller
* is allocated twice, so the code just prints a warning.
*
* @param vec Array of pointers to control functions.
* @param controller MIDI controller number
* @param f Pointer to control function to assign to controller
*/
static void assignMIDIControllerFunction (void (* vec []) (unsigned char),
unsigned char controller,
void (*f) (unsigned char)) {
assert (vec != NULL);
if (f != NULL) {
if ((vec[controller] != emptyControlFunction) &&
(vec[controller] != NULL)) {
fprintf (stderr,
"midiIn.c:WARNING, multiple allocation of controller %d!",
(int) controller);
}
vec[controller] = f;
#if 0
if (vec == ctrlvecA) {
fprintf (stdout, "A[%d]=%x\n", (int) controller, f);
}
#endif
}
else {
vec[controller] = emptyControlFunction;
}
}
/*
* This function sets the controller function for the given MIDI controller.
* If the provided function pointer is NULL, the external controller is
* released. (I do not expect this feature to be used, but you never know).
* NOTE: 22-jul-2004/FK In the face of generality, this remain as a backwards
* compatible option for channel A (upper).
*/
void setMIDIControllerFunction (unsigned char controller,
void (*f) (unsigned char c)) {
fprintf (stderr, "Rogue call to setMIDIControllerFunction(controller=%d,");
fprintf (stderr, " f=...);\n", controller);
return;
assignMIDIControllerFunction (ctrlvecA, controller, f);
}
/*
* 22-jul-2004: new function to assign controllers to any of three MIDI
* channels. Utility refers to the sections that are separated by MIDI
* channels, not the channels themselves.
*/
void setuMIDIControllerFunction (int utility,
unsigned char controller,
void (*f) (unsigned char)) {
fprintf (stderr, "Rogue call to setuMIDIControllerFunction(utility=%d,");
fprintf (stderr, " controller=%d, f=...);\n", utility, controller);
return;
switch (utility) {
case MIDI_UTIL_SWELL:
assignMIDIControllerFunction (ctrlvecA, controller, f);
break;
case MIDI_UTIL_GREAT:
assignMIDIControllerFunction (ctrlvecB, controller, f);
break;
case MIDI_UTIL_PEDAL:
assignMIDIControllerFunction (ctrlvecC, controller, f);
break;
default:
assert (0);
break;
}
}
/*
* 26-sep-2004/FK This is the entry point for modules that wish to register
* functions that accept MIDI controller data. Functions entered through this
* interface can be freely remapped by the user via configuration files.
*
* @param cfname The symbolic name (defined in ccFuncNames) of the reaction
* implemented by the function pointed to by the f parameter.
* @param f Pointer to function that acts on the controller message.
*/
void useMIDIControlFunction (char * cfname, void (* f) (unsigned char)) {
int x = getCCFunctionId (cfname);
assert (-1 < x);
if (ctrlUseA[x] < 128) {
assignMIDIControllerFunction (ctrlvecA, ctrlUseA[x], f);
}
if (ctrlUseB[x] < 128) {
assignMIDIControllerFunction (ctrlvecB, ctrlUseB[x], f);
}
if (ctrlUseC[x] < 128) {
assignMIDIControllerFunction (ctrlvecC, ctrlUseC[x], f);
}
}
/*
* This initializes the MIDI controller vector tables.
*/
static void loadControllerTable () {
int i;
for (i = 0; i < 128; i++) {
if (ctrlvecA == NULL) {
ctrlvecA = emptyControlFunction;
}
if (ctrlvecB == NULL) {
ctrlvecB = emptyControlFunction;
}
if (ctrlvecC == NULL) {
ctrlvecC = emptyControlFunction;
}
}
}
/*
* This function is used to map a region of the MIDI keyboard to a
* region of Beatrix keys. The supplied translation table will only be
* written in the specified MIDI range.
*
* @param translationTable Pointer to lookup table used by parser.
* @param firstMIDINote MIDI note number of the first note in the input.
* @param lastMIDINote MIDI note number of the last note in the input.
* @param firstKey First key number in the target region.
* @param lastKey Last key number in the target region.
* @param transpose Applies a small transpose to the target region.
* @param excursionStrategy If zero, MIDI notes mapped outside the target
* become silent. If 1, notes outside the target
* wraps around to the closest octave key.
*/
static void loadKeyTableRegion (unsigned char * translationTable,
int first_MIDINote,
int last_MIDINote,
int firstKey,
int lastKey,
int transpose,
int excursionStrategy)
{
int note;
int offset = transpose + firstKey - first_MIDINote;
int firstKeyAdjust = firstKey + 12 - (firstKey % 12);
int lastKeyAdjust = lastKey - (lastKey % 12) - 12;
for (note = first_MIDINote; note <= last_MIDINote; note++) {
int key = note + offset;
if (key < firstKey) {
key = (excursionStrategy == 1) ? firstKeyAdjust + (key % 12) : 255;
}
else if (lastKey < key) {
key = (excursionStrategy == 1) ? lastKeyAdjust + (key % 12) : 255;
}
/* This may happen if the key range is smaller than an octave. */
if ((key < firstKey) || (lastKey < key)) {
key = 255;
}
translationTable[note] = key;
}
}
/*
* Clears a note-to-key translation table by setting all entries to 255.
*/
static void clearKeyTable (unsigned char * table) {
int i;
for (i = 0; i < 128; i++) {
table = 255;
}
}
/*
* This function loads the channel A note-to-key translation table.
*/
static void loadKeyTableA () {
int left = 0;
int first_MIDI_Note;
clearKeyTable (keyTableA);
if (0 < splitA_PL) {
loadKeyTableRegion (keyTableA,
24, splitA_PL - 1,
128, 159,
transpose + nshA_PL,
0);
left = splitA_PL;
}
if (left < splitA_UL) {
first_MIDI_Note = (36 < left) ? left : 36;
loadKeyTableRegion (keyTableA,
first_MIDI_Note, splitA_UL - 1,
64 + (first_MIDI_Note % 12), 124,
transpose + nshA_UL,
0);
left = splitA_UL;
}
first_MIDI_Note = (36 < left) ? left : 36;
/*
* Here we allow the upper MIDI
* parameter to extend up to 127 (maximum). That way a liberal use of
* nshA_U (noteshift for upper manual in split mode) can exploit a
* wide controller (e.g. 88 keys).
*/
loadKeyTableRegion (keyTableA,
first_MIDI_Note, 127,
0 + (first_MIDI_Note - 36), 60,
transpose + ((0 < left) ? nshA_U : nshA),
0);
} /* loadKeyTableA */
/*
* Loads the B channel (lower manual) MIDI to key mapping table.
*/
static void loadKeyTableB () {
clearKeyTable (keyTableB);
loadKeyTableRegion (keyTableB,
36, 96,
64, 124,
transpose + nshB,
userExcursionStrategy);
}
/*
* Loads the C channel (pedals) MIDI to key mapping table.
*/
static void loadKeyTableC () {
clearKeyTable (keyTableC);
loadKeyTableRegion (keyTableC,
24, 55,
128, 159,
transpose + nshC,
userExcursionStrategy);
}
/*
* External interface to set and unset the A keyboard split points.
*/
void setKeyboardSplitMulti (int flags,
int p_splitA_PL,
int p_splitA_UL,
int p_nshA_PL,
int p_nshA_UL,
int p_nshA_U)
{
if (flags & 1) splitA_PL = p_splitA_PL;
if (flags & 2) splitA_UL = p_splitA_UL;
if (flags & 4) nshA_PL = p_nshA_PL;
if (flags &
nshA_UL = p_nshA_UL;
if (flags & 16) nshA_U = p_nshA_U;
loadKeyTableA ();
}
void setKeyboardTransposeA (int transpose) {
nshA = transpose;
loadKeyTableA ();
}
void setKeyboardTransposeB (int transpose) {
nshB = transpose;
loadKeyTableB ();
}
void setKeyboardTransposeC (int transpose) {
nshC = transpose;
loadKeyTableC ();
}
void setKeyboardTranspose (int trsp) {
transpose = trsp;
loadKeyTableA ();
loadKeyTableB ();
loadKeyTableC ();
}
/*
* Loads the status table. The table is used by the MIDI parser to look
* up status bytes. The table gives the parser's initial state and a pointer
* to items used in the processing of the data in the message. For example,
* note on/off messages uses a pointer to a note-to-key translation table.
*/
static void loadStatusTable () {
int i;
for (i = 128; i < 256; i++) {
statusTable.state = stIgnore;
statusTable[i].handback = NULL;
}
i = MIDI_NOTEOFF | rcvChA;
statusTable[i].state = stNoteOff;
statusTable[i].handback = (void *) keyTableA;
i = MIDI_NOTEOFF | rcvChB;
statusTable[i].state = stNoteOff;
statusTable[i].handback = (void *) keyTableB;
i = MIDI_NOTEOFF | rcvChC;
statusTable[i].state = stNoteOff;
statusTable[i].handback = (void *) keyTableC;
i = MIDI_NOTEON | rcvChA;
statusTable[i].state = stNoteOn;
statusTable[i].handback = (void *) keyTableA;
i = MIDI_NOTEON | rcvChB;
statusTable[i].state = stNoteOn;
statusTable[i].handback = (void *) keyTableB;
i = MIDI_NOTEON | rcvChC;
statusTable[i].state = stNoteOn;
statusTable[i].handback = (void *) keyTableC;
i = MIDI_CTL_CHANGE | rcvChA;
statusTable[i].state = stControlChg;
statusTable[i].handback = (void *) ctrlvecA;
i = MIDI_CTL_CHANGE | rcvChB;
statusTable[i].state = stControlChg;
statusTable[i].handback = (void *) ctrlvecB;
i = MIDI_CTL_CHANGE | rcvChC;
statusTable[i].state = stControlChg;
statusTable[i].handback = (void *) ctrlvecC;
i = MIDI_PGM_CHANGE | rcvChA;
statusTable[i].state = stProgramChg;
statusTable[i].handback = NULL;
}
/*
* Auxillary function to midiPrimeControllerMapping below.
*/
static void loadCCMap (char * cfname,
int ccn,
unsigned char * A,
unsigned char * B,
unsigned char * C) {
int x = getCCFunctionId (cfname);
if (!(-1 < x)) {
fprintf (stderr, "Unrecognized controller function name:'%s'\n", cfname);
assert (-1 < x);
}
if (A != NULL) A[x] = (unsigned char) ccn;
if (B != NULL) B[x] = (unsigned char) ccn;
if (C != NULL) C[x] = (unsigned char) ccn;
}
/*
* Sets the initial state of the tables that map from a controllable function
* id to a MIDI controller number. This function must be
* run before the configuration sequence because it is part of the static
* default. Maintaining a static default in source becomes much to odious
* when things move around.
* What we do is that we load the tables ctrlUseA, ctrlUseB and ctrlUseC
*/
void midiPrimeControllerMapping () {
int i;
for (i = 0; i < CTRL_USE_MAX; i++) {
ctrlUseA[i] = 255;
ctrlUseB[i] = 255;
ctrlUseC[i] = 255;
}
loadCCMap ("swellpedal1", 1, ctrlUseA, ctrlUseB, ctrlUseC);
loadCCMap ("swellpedal2", 11, ctrlUseA, ctrlUseB, ctrlUseC);
loadCCMap ("xov.ctl_biased", 3, ctrlUseA, NULL, NULL);
loadCCMap ("xov.ctl_biased_fb", 9, ctrlUseA, NULL, NULL);
loadCCMap ("xov.ctl_biased_fb2", 14, ctrlUseA, NULL, NULL);
loadCCMap ("xov.ctl_biased_gfb", 15, ctrlUseA, NULL, NULL);
loadCCMap ("xov.ctl_sagtobias", 20, ctrlUseA, NULL, NULL);
loadCCMap ("xov.inputgain", 21, ctrlUseA, NULL, NULL);
loadCCMap ("xov.outputgain", 22, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.drum.filter.type", 23, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.drum.filter.hz", 24, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.drum.filter.q", 25, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.drum.filter.gain", 26, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.a.type", 27, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.a.hz", 28, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.a.q", 29, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.a.gain", 30, ctrlUseA, NULL, NULL);
/* 32-63 are least significant bits of controller 0-31 */
loadCCMap ("rotary.speed-toggle", 64, ctrlUseA, ctrlUseB, ctrlUseC);
loadCCMap ("upper.drawbar16", 70, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar513", 71, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar8", 72, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar4", 73, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar223", 74, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar2", 75, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar135", 76, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar113", 77, ctrlUseA, NULL, NULL);
loadCCMap ("upper.drawbar1", 78, ctrlUseA, NULL, NULL);
loadCCMap ("lower.drawbar16", 70, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar513", 71, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar8", 72, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar4", 73, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar223", 74, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar2", 75, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar135", 76, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar113", 77, NULL, ctrlUseB, NULL);
loadCCMap ("lower.drawbar1", 78, NULL, ctrlUseB, NULL);
loadCCMap ("pedal.drawbar16", 70, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar513", 71, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar8", 72, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar4", 73, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar223", 74, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar2", 75, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar135", 76, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar113", 77, NULL, NULL, ctrlUseC);
loadCCMap ("pedal.drawbar1", 78, NULL, NULL, ctrlUseC);
loadCCMap ("percussion.enable", 80, ctrlUseA, NULL, NULL);
loadCCMap ("percussion.decay", 81, ctrlUseA, NULL, NULL);
loadCCMap ("percussion.harmonic", 82, ctrlUseA, NULL, NULL);
loadCCMap ("vibrato.knob", 83, ctrlUseA, NULL, NULL);
loadCCMap ("vibrato.routing", 92, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.b.type", 85, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.b.hz", 86, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.b.q", 87, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.horn.filter.b.gain", 88, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.comb.a.feedback", 89, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.comb.a.delay", 90, ctrlUseA, NULL, NULL);
loadCCMap ("rotary.speed-select", 91, ctrlUseA, NULL, NULL);
loadCCMap ("overdrive.character", 93, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.comb.b.feedback", 102, ctrlUseA, NULL, NULL);
loadCCMap ("whirl.comb.b.delay", 103, ctrlUseA, NULL, NULL);
}
/*
* Initialises the MIDI file descriptor and loads tables.
*/
int prepareMIDI () {
int timeToWait = 1;
int version;
if ((midi_fd = open (midiDevice, O_RDONLY, 0)) == -1) {
perror (midiDevice);
exit (1);
}
#ifdef COMMENT /* This worked under Red Hat 6.2 but not under Debian */
if (ioctl (midi_fd, SNDCTL_MIDI_PRETIME, & timeToWait) == -1) {
perror ("SNDCTL_MIDI_PRETIME");
exit (1);
}
if (ioctl (midi_fd, OSS_GETVERSION, & version) == -1) {
perror ("OSS_GETVERSION");
exit (1);
}
fprintf (stderr, "MIDI DRIVER VERSION %x\n", version);
#endif /* COMMENT */
/* Load the tables */
loadControllerTable ();
loadKeyTableA ();
loadKeyTableB ();
loadKeyTableC ();
loadStatusTable ();
return 0;
} /* prepareMIDI */
/*
*
*/
void setMIDIDevice (char * s) {
if (s != NULL) {
strncpy (midiDeviceBuffer, s, PATH_MAX);
midiDevice = midiDeviceBuffer;
}
}
/*
* Deprecated and impotent.
*/
void setMIDIChannelSwell (int ch) {
}
/*
* Sets global transpose (for playing in alternate scales).
*/
void setMIDINoteShift (char offset) {
transpose = offset;
loadKeyTableA ();
loadKeyTableB ();
loadKeyTableC ();
}
/*
* This call configures this module.
*/
int midiConfig (ConfigContext * cfg) {
int v;
int ack = 0;
if ((ack = getConfigParameter_ir ("midi.upper.channel",
cfg,
&v,
1, 16)) == 1) {
rcvChA = v - 1;
}
else if ((ack = getConfigParameter_ir ("midi.lower.channel",
cfg,
&v,
1, 16)) == 1) {
rcvChB = v - 1;
}
else if ((ack = getConfigParameter_ir ("midi.pedals.channel",
cfg,
&v,
1, 16)) == 1) {
rcvChC = v - 1;
}
else if ((ack = getConfigParameter_ir ("midi.transpose",
cfg,
&v,
-127, 127)) == 1) {
transpose = v;
}
else if ((ack = getConfigParameter_ir ("midi.upper.transpose",
cfg,
&v,
-127, 127)) == 1) {
nshA = v;
}
else if ((ack = getConfigParameter_ir ("midi.lower.transpose",
cfg,
&v,
-127, 127)) == 1) {
nshB = v;
}
else if ((ack = getConfigParameter_ir ("midi.pedals.transpose",
cfg,
&v,
-127, 127)) == 1) {
nshC = v;
}
else if ((ack = getConfigParameter_ir ("midi.pedals.transpose.split",
cfg,
&v,
-127, 127)) == 1) {
nshA_PL = v;
}
else if ((ack = getConfigParameter_ir ("midi.lower.transpose.split",
cfg,
&v,
-127, 127)) == 1) {
nshA_UL = v;
}
else if ((ack = getConfigParameter_ir ("midi.upper.transpose.split",
cfg,
&v,
-127, 127)) == 1) {
nshA_U = v;
}
else if (strcasecmp (cfg->name, "midi.device") == 0) {
setMIDIDevice (cfg->value);
ack++;
}
/*
* The syntax for this config option is:
* midi.controller.{upper,lower,pedals}.<cc>=<fname>
* where <cc> is a MIDI controller number, and
* <fname> is the symbolic name of a controllable function.
*/
else if (strncasecmp (cfg->name, "midi.controller.", 16) == 0) {
unsigned char * ctrlUse = ctrlUseA;
int ccIdx = 0;
if (strncasecmp ((cfg->name) + 16, "upper", 5) == 0) {
ctrlUse = ctrlUseA;
ccIdx = 22;
}
else if (strncasecmp ((cfg->name) + 16, "lower", 5) == 0) {
ctrlUse = ctrlUseB;
ccIdx = 22;
}
else if (strncasecmp ((cfg->name) + 16, "pedals", 6) == 0) {
ctrlUse = ctrlUseC;
ccIdx = 23;
}
else {
showConfigfileContext (cfg, "directive 'upper', 'lower' or 'pedals' expected");
}
/* If the code above managed to parse a channel name... */
if (0 < ccIdx) {
int ccn;
/* ... and we manage to parse a controller number... */
if (sscanf ((cfg->name) + ccIdx, "%d", &ccn) == 1) {
/* ...and the controller number is in the allowed range... */
if ((0 <= ccn) && (ccn < 128)) {
int i = getCCFunctionId (cfg->value);
if (-1 < i) {
/* Store the controller number indexed by abstract function id */
ctrlUse[i] = ccn;
ack++;
}
else {
/* No match found for symbolic function name. */
showConfigfileContext (cfg,
"name of controllable function not found");
}
}
else {
showConfigfileContext (cfg, "controller number out of range");
}
}
}
}
return ack;
}
/*
*
*/
int closeMIDI () {
(void) close (midi_fd);
return 0;
}
/*
* This is the entry point of the MIDI reader thread.
*/
void * MIDIInReader (void * arg) {
int inBytes;
unsigned char * mbp;
unsigned char * mbpEnd;
int state = stIgnore;
(void) prepareMIDI ();
while (1) {
/* Read the next chunk of waiting MIDI bytes */
if ((inBytes = read (midi_fd,
(void *) inBuffer,
(size_t) BUFFER_SIZE_BYTES)) == -1) {
perror ("read midi_fd");
return NULL;
}
if (inBytes == 0) continue;
/* Point to start and end of input bytes in buffer */
mbp = inBuffer;
mbpEnd = mbp + inBytes;
/* Feed each byte to the parser */
while (mbp < mbpEnd) {
unsigned char B = * mbp++; /* Get next MIDI byte from buffer */
if (IS_SYSTEM_REAL_TIME(B)) {
continue; /* Ignore */
}
else if (IS_STATUS(B)) {
byte1 = B;
state = statusTable.state;
}
switch (state) {
case stNoteOff:
keyTable = (unsigned char *) statusTable.handback;
state = dtaNoteOff1;
break; /* stNoteOff */
case dtaNoteOff1:
byte2 = B; /* Note number */
state = dtaNoteOff2;
break; /* dtaNoteOff1 */
case dtaNoteOff2:
oscKeyOff (keyTable[byte2]); /* B is release velocity */
state = dtaNoteOff1;
break; /* dtaNoteOff2 */
case stNoteOn:
keyTable = (unsigned char *) statusTable.handback;
state = dtaNoteOn1;
break; /* stNoteOn */
case dtaNoteOn1:
byte2 = B; /* Note number */
state = dtaNoteOn2;
break; /* dtaNoteOn1 */
case dtaNoteOn2:
if (0 < B) { /* B is note on velocity */
oscKeyOn (keyTable[byte2]);
} else {
oscKeyOff (keyTable[byte2]); /* Zero velocity on = off */
}
state = dtaNoteOn1;
break; /* dtaNoteOn2 */
case stControlChg:
ctrlvec = (void (**) (unsigned char)) statusTable.handback;
state = dtaControlChg1;
break; /* stControlChg */
case dtaControlChg1:
byte2 = B; /* Controller number */
state = dtaControlChg2;
break; /* dtaControlChg1 */
case dtaControlChg2:
(ctrlvec[byte2])(B); /* B is controller data */
state = dtaControlChg1;
break; /* dtaControlChg2 */
case stProgramChg:
state = dtaProgramChg;
break; /* stProgramChg */
case dtaProgramChg:
installProgram (B); /* B is program number */
state = dtaProgramChg;
break;
case stIgnore:
state = dtaIgnore;
break; /* stIgnore */
case dtaIgnore:
state = dtaIgnore;
break; /* dtaIgnore */
default:
assert (0);
break;
} /* switch state */
} /* while MIDI bytes remain in the input buffer */
} /* while true */
}