Refactoring and bugfixes. Also added the capability to add another pair of MIDI ports (-o2).
parent
eb5ce16cb7
commit
6f0bec4b74
255
jackdriver.c
255
jackdriver.c
|
@ -140,16 +140,19 @@ queue_message(jack_ringbuffer_t* ringbuffer, MidiMessage *ev)
|
|||
void
|
||||
process_midi_input(JACK_SEQ* seq,jack_nframes_t nframes)
|
||||
{
|
||||
int k;
|
||||
|
||||
for (k = 0; k < seq->n_in; k++) {
|
||||
|
||||
int read, events, i;
|
||||
void *port_buffer;
|
||||
MidiMessage rev;
|
||||
jack_midi_event_t event;
|
||||
|
||||
port_buffer = jack_port_get_buffer(seq->input_port, nframes);
|
||||
void *port_buffer = jack_port_get_buffer(seq->input_port[k], nframes);
|
||||
if (port_buffer == NULL)
|
||||
{
|
||||
fprintf(stderr, "jack_port_get_buffer failed, cannot receive anything.\n");
|
||||
return;
|
||||
fprintf(stderr, "jack_port_get_buffer failed, cannot receive anything.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef JACK_MIDI_NEEDS_NFRAMES
|
||||
|
@ -162,47 +165,49 @@ process_midi_input(JACK_SEQ* seq,jack_nframes_t nframes)
|
|||
{
|
||||
|
||||
#ifdef JACK_MIDI_NEEDS_NFRAMES
|
||||
read = jack_midi_event_get(&event, port_buffer, i, nframes);
|
||||
read = jack_midi_event_get(&event, port_buffer, i, nframes);
|
||||
#else
|
||||
read = jack_midi_event_get(&event, port_buffer, i);
|
||||
read = jack_midi_event_get(&event, port_buffer, i);
|
||||
#endif
|
||||
if (!read)
|
||||
{
|
||||
//successful event get
|
||||
if (!read)
|
||||
{
|
||||
//successful event get
|
||||
|
||||
if (event.size <= 3 && event.size >=1)
|
||||
{
|
||||
//not sysex or something
|
||||
|
||||
//PUSH ONTO CIRCULAR BUFFER
|
||||
//not sure if its a true copy onto buffer, if not this won't work
|
||||
rev.len = event.size;
|
||||
rev.time = event.time;
|
||||
memcpy(rev.data, event.buffer, rev.len);
|
||||
queue_message(seq->ringbuffer_in,&rev);
|
||||
}
|
||||
}
|
||||
if (event.size <= 3 && event.size >= 1)
|
||||
{
|
||||
//not sysex or something
|
||||
|
||||
//PUSH ONTO CIRCULAR BUFFER
|
||||
//not sure if its a true copy onto buffer, if not this won't work
|
||||
rev.len = event.size;
|
||||
rev.time = event.time;
|
||||
memcpy(rev.data, event.buffer, rev.len);
|
||||
queue_message(seq->ringbuffer_in[k],&rev);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
process_midi_output(JACK_SEQ* seq,jack_nframes_t nframes)
|
||||
{
|
||||
jack_nframes_t last_frame_time = jack_last_frame_time(seq->jack_client);
|
||||
int k;
|
||||
|
||||
for (k = 0; k < seq->n_out; k++) {
|
||||
|
||||
int read, t;
|
||||
uint8_t *buffer;
|
||||
void *port_buffer;
|
||||
jack_nframes_t last_frame_time;
|
||||
MidiMessage ev;
|
||||
|
||||
last_frame_time = jack_last_frame_time(seq->jack_client);
|
||||
|
||||
port_buffer = jack_port_get_buffer(seq->output_port, nframes);
|
||||
port_buffer = jack_port_get_buffer(seq->output_port[k], nframes);
|
||||
if (port_buffer == NULL)
|
||||
{
|
||||
fprintf(stderr, "jack_port_get_buffer failed, cannot send anything.\n");
|
||||
return;
|
||||
fprintf(stderr, "jack_port_get_buffer failed, cannot send anything.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef JACK_MIDI_NEEDS_NFRAMES
|
||||
|
@ -211,44 +216,45 @@ process_midi_output(JACK_SEQ* seq,jack_nframes_t nframes)
|
|||
jack_midi_clear_buffer(port_buffer);
|
||||
#endif
|
||||
|
||||
while (jack_ringbuffer_read_space(seq->ringbuffer_out))
|
||||
while (jack_ringbuffer_read_space(seq->ringbuffer_out[k]))
|
||||
{
|
||||
read = jack_ringbuffer_peek(seq->ringbuffer_out, (char *)&ev, sizeof(ev));
|
||||
read = jack_ringbuffer_peek(seq->ringbuffer_out[k], (char *)&ev, sizeof(ev));
|
||||
|
||||
if (read != sizeof(ev))
|
||||
{
|
||||
//warn_from_jack_thread_context("Short read from the ringbuffer, possible note loss.");
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_out, read);
|
||||
continue;
|
||||
}
|
||||
if (read != sizeof(ev))
|
||||
{
|
||||
//warn_from_jack_thread_context("Short read from the ringbuffer, possible note loss.");
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_out[k], read);
|
||||
continue;
|
||||
}
|
||||
|
||||
t = ev.time + nframes - last_frame_time;
|
||||
t = ev.time + nframes - last_frame_time;
|
||||
|
||||
/* If computed time is too much into the future, we'll need
|
||||
to send it later. */
|
||||
if (t >= (int)nframes)
|
||||
break;
|
||||
/* If computed time is too much into the future, we'll need
|
||||
to send it later. */
|
||||
if (t >= (int)nframes)
|
||||
break;
|
||||
|
||||
/* If computed time is < 0, we missed a cycle because of xrun. */
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
/* If computed time is < 0, we missed a cycle because of xrun. */
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_out, sizeof(ev));
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_out[k], sizeof(ev));
|
||||
|
||||
#ifdef JACK_MIDI_NEEDS_NFRAMES
|
||||
buffer = jack_midi_event_reserve(port_buffer, t, ev.len, nframes);
|
||||
buffer = jack_midi_event_reserve(port_buffer, t, ev.len, nframes);
|
||||
#else
|
||||
buffer = jack_midi_event_reserve(port_buffer, t, ev.len);
|
||||
buffer = jack_midi_event_reserve(port_buffer, t, ev.len);
|
||||
#endif
|
||||
|
||||
if (buffer == NULL)
|
||||
{
|
||||
//warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST.");
|
||||
break;
|
||||
}
|
||||
if (buffer == NULL)
|
||||
{
|
||||
//warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST.");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(buffer, ev.data, ev.len);
|
||||
memcpy(buffer, ev.data, ev.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -260,9 +266,9 @@ process_callback(jack_nframes_t nframes, void *seqq)
|
|||
fprintf(stderr, "Had to wait too long for JACK callback; scheduling problem?\n");
|
||||
#endif
|
||||
|
||||
if(seq->usein)
|
||||
if(seq->n_in)
|
||||
process_midi_input( seq,nframes );
|
||||
if(seq->useout)
|
||||
if(seq->n_out)
|
||||
process_midi_output( seq,nframes );
|
||||
|
||||
#ifdef MEASURE_TIME
|
||||
|
@ -276,7 +282,7 @@ process_callback(jack_nframes_t nframes, void *seqq)
|
|||
///////////////////////////////////////////////
|
||||
//these functions are executed in other threads
|
||||
///////////////////////////////////////////////
|
||||
void queue_midi(void* seqq, uint8_t msg[])
|
||||
void queue_midi(void* seqq, uint8_t msg[], uint8_t port_no)
|
||||
{
|
||||
MidiMessage ev;
|
||||
JACK_SEQ* seq = (JACK_SEQ*)seqq;
|
||||
|
@ -330,34 +336,37 @@ void queue_midi(void* seqq, uint8_t msg[])
|
|||
ev.data[2] = msg[2];
|
||||
|
||||
ev.time = jack_frame_time(seq->jack_client);
|
||||
queue_message(seq->ringbuffer_out,&ev);
|
||||
queue_message(seq->ringbuffer_out[port_no],&ev);
|
||||
}
|
||||
|
||||
int pop_midi(void* seqq, uint8_t msg[])
|
||||
int pop_midi(void* seqq, uint8_t msg[], uint8_t *port_no)
|
||||
{
|
||||
int read;
|
||||
MidiMessage ev;
|
||||
JACK_SEQ* seq = (JACK_SEQ*)seqq;
|
||||
int read, k;
|
||||
MidiMessage ev;
|
||||
JACK_SEQ* seq = (JACK_SEQ*)seqq;
|
||||
|
||||
if (jack_ringbuffer_read_space(seq->ringbuffer_in))
|
||||
for (k = 0; k < seq->n_in; k++) {
|
||||
|
||||
if (jack_ringbuffer_read_space(seq->ringbuffer_in[k]))
|
||||
{
|
||||
read = jack_ringbuffer_peek(seq->ringbuffer_in, (char *)&ev, sizeof(ev));
|
||||
read = jack_ringbuffer_peek(seq->ringbuffer_in[k], (char *)&ev, sizeof(ev));
|
||||
|
||||
if (read != sizeof(ev))
|
||||
{
|
||||
//warn_from_jack_thread_context("Short read from the ringbuffer, possible note loss.");
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_in, read);
|
||||
return -1;
|
||||
}
|
||||
if (read != sizeof(ev))
|
||||
{
|
||||
//warn_from_jack_thread_context("Short read from the ringbuffer, possible note loss.");
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_in[k], read);
|
||||
return -1;
|
||||
}
|
||||
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_in, sizeof(ev));
|
||||
jack_ringbuffer_read_advance(seq->ringbuffer_in[k], sizeof(ev));
|
||||
|
||||
memcpy(msg,ev.data,ev.len);
|
||||
memcpy(msg,ev.data,ev.len);
|
||||
*port_no = k;
|
||||
|
||||
return ev.len;
|
||||
return ev.len;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
@ -366,7 +375,8 @@ int pop_midi(void* seqq, uint8_t msg[])
|
|||
int
|
||||
init_jack(JACK_SEQ* seq, uint8_t verbose)
|
||||
{
|
||||
int err;
|
||||
int err, k;
|
||||
char portname[100];
|
||||
|
||||
if(verbose)printf("opening client...\n");
|
||||
seq->jack_client = jack_client_open("midizap", JackNullOption, NULL);
|
||||
|
@ -386,53 +396,85 @@ init_jack(JACK_SEQ* seq, uint8_t verbose)
|
|||
}
|
||||
|
||||
|
||||
if(seq->usein)
|
||||
seq->ringbuffer_in = NULL;
|
||||
seq->input_port = NULL;
|
||||
if(seq->n_in)
|
||||
{
|
||||
|
||||
if(verbose)printf("initializing JACK input: \ncreating ringbuffer...\n");
|
||||
seq->ringbuffer_in = jack_ringbuffer_create(RINGBUFFER_SIZE);
|
||||
|
||||
if (seq->ringbuffer_in == NULL)
|
||||
seq->ringbuffer_in = calloc(seq->n_in, sizeof(jack_ringbuffer_t*));
|
||||
seq->input_port = calloc(seq->n_in, sizeof(jack_port_t*));
|
||||
if (!seq->ringbuffer_in || !seq->input_port)
|
||||
{
|
||||
fprintf(stderr, "Cannot create JACK ringbuffer.\n");
|
||||
fprintf(stderr, "Cannot allocate memory for ports and ringbuffers.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
jack_ringbuffer_mlock(seq->ringbuffer_in);
|
||||
for (k = 0; k < seq->n_in; k++) {
|
||||
seq->ringbuffer_in[k] = jack_ringbuffer_create(RINGBUFFER_SIZE);
|
||||
|
||||
seq->input_port = jack_port_register(seq->jack_client, "midi_in",
|
||||
JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsInput, 0);
|
||||
if (seq->ringbuffer_in[k] == NULL)
|
||||
{
|
||||
fprintf(stderr, "Cannot create JACK ringbuffer.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seq->input_port == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not register JACK port.\n");
|
||||
return 0;
|
||||
jack_ringbuffer_mlock(seq->ringbuffer_in[k]);
|
||||
|
||||
if (k)
|
||||
sprintf(portname, "midi_in%d", k+1);
|
||||
else
|
||||
strcpy(portname, "midi_in");
|
||||
seq->input_port[k] = jack_port_register(seq->jack_client, portname,
|
||||
JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsInput, 0);
|
||||
|
||||
if (seq->input_port[k] == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not register JACK port.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(seq->useout)
|
||||
seq->ringbuffer_out = NULL;
|
||||
seq->output_port = NULL;
|
||||
if(seq->n_out)
|
||||
{
|
||||
|
||||
if(verbose)printf("initializing JACK output: \ncreating ringbuffer...\n");
|
||||
seq->ringbuffer_out = jack_ringbuffer_create(RINGBUFFER_SIZE);
|
||||
|
||||
if (seq->ringbuffer_out == NULL)
|
||||
seq->ringbuffer_out = calloc(seq->n_out, sizeof(jack_ringbuffer_t*));
|
||||
seq->output_port = calloc(seq->n_out, sizeof(jack_port_t*));
|
||||
if (!seq->ringbuffer_out || !seq->output_port)
|
||||
{
|
||||
fprintf(stderr, "Cannot create JACK ringbuffer.\n");
|
||||
return 0;
|
||||
fprintf(stderr, "Cannot allocate memory for ports and ringbuffers.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
jack_ringbuffer_mlock(seq->ringbuffer_out);
|
||||
for (k = 0; k < seq->n_out; k++) {
|
||||
seq->ringbuffer_out[k] = jack_ringbuffer_create(RINGBUFFER_SIZE);
|
||||
|
||||
seq->output_port = jack_port_register(seq->jack_client, "midi_out",
|
||||
JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsOutput, 0);
|
||||
if (seq->ringbuffer_out[k] == NULL)
|
||||
{
|
||||
fprintf(stderr, "Cannot create JACK ringbuffer.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seq->output_port == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not register JACK port.\n");
|
||||
return 0;
|
||||
jack_ringbuffer_mlock(seq->ringbuffer_out[k]);
|
||||
|
||||
if (k)
|
||||
sprintf(portname, "midi_out%d", k+1);
|
||||
else
|
||||
strcpy(portname, "midi_out");
|
||||
seq->output_port[k] = jack_port_register(seq->jack_client, portname,
|
||||
JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsOutput, 0);
|
||||
|
||||
if (seq->output_port[k] == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not register JACK port.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,6 +488,13 @@ init_jack(JACK_SEQ* seq, uint8_t verbose)
|
|||
|
||||
void close_jack(JACK_SEQ* seq)
|
||||
{
|
||||
if(seq->useout)jack_ringbuffer_free(seq->ringbuffer_out);
|
||||
if(seq->usein)jack_ringbuffer_free(seq->ringbuffer_in);
|
||||
int k;
|
||||
if(seq->n_out) {
|
||||
for (k = 0; k < seq->n_out; k++)
|
||||
jack_ringbuffer_free(seq->ringbuffer_out[k]);
|
||||
}
|
||||
if(seq->n_in) {
|
||||
for (k = 0; k < seq->n_in; k++)
|
||||
jack_ringbuffer_free(seq->ringbuffer_in[k]);
|
||||
}
|
||||
}
|
||||
|
|
16
jackdriver.h
16
jackdriver.h
|
@ -5,18 +5,18 @@
|
|||
|
||||
typedef struct _jseq
|
||||
{
|
||||
jack_ringbuffer_t *ringbuffer_out;
|
||||
jack_ringbuffer_t *ringbuffer_in;
|
||||
jack_ringbuffer_t **ringbuffer_out;
|
||||
jack_ringbuffer_t **ringbuffer_in;
|
||||
jack_client_t *jack_client;
|
||||
jack_port_t *output_port;
|
||||
jack_port_t *input_port;
|
||||
uint8_t usein;
|
||||
uint8_t useout;
|
||||
jack_port_t **output_port;
|
||||
jack_port_t **input_port;
|
||||
uint8_t n_in;
|
||||
uint8_t n_out;
|
||||
} JACK_SEQ;
|
||||
|
||||
int init_jack(JACK_SEQ* seq, uint8_t verbose);
|
||||
void close_jack(JACK_SEQ* seq);
|
||||
void queue_midi(void* seqq, uint8_t msg[]);
|
||||
int pop_midi(void* seqq, uint8_t msg[]);
|
||||
void queue_midi(void* seqq, uint8_t msg[], uint8_t port_no);
|
||||
int pop_midi(void* seqq, uint8_t msg[], uint8_t *port_no);
|
||||
|
||||
#endif
|
||||
|
|
385
midizap.c
385
midizap.c
|
@ -20,13 +20,6 @@
|
|||
|
||||
typedef struct input_event EV;
|
||||
|
||||
extern int debug_regex;
|
||||
extern translation *default_translation;
|
||||
|
||||
unsigned short jogvalue = 0xffff;
|
||||
int shuttlevalue = 0xffff;
|
||||
struct timeval last_shuttle;
|
||||
int need_synthetic_shuttle;
|
||||
Display *display;
|
||||
|
||||
JACK_SEQ seq;
|
||||
|
@ -75,7 +68,7 @@ static int16_t pbvalue[16] =
|
|||
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192};
|
||||
|
||||
void
|
||||
send_midi(int status, int data, int step, int incr, int index, int dir)
|
||||
send_midi(uint8_t portno, int status, int data, int step, int incr, int index, int dir)
|
||||
{
|
||||
if (!enable_jack_output) return; // MIDI output not enabled
|
||||
uint8_t msg[3];
|
||||
|
@ -153,14 +146,14 @@ send_midi(int status, int data, int step, int incr, int index, int dir)
|
|||
default:
|
||||
return;
|
||||
}
|
||||
queue_midi(&seq, msg);
|
||||
queue_midi(&seq, msg, portno);
|
||||
}
|
||||
|
||||
stroke *
|
||||
fetch_stroke(translation *tr, int status, int chan, int data,
|
||||
fetch_stroke(translation *tr, uint8_t portno, int status, int chan, int data,
|
||||
int index, int dir)
|
||||
{
|
||||
if (tr != NULL) {
|
||||
if (tr && tr->portno == portno) {
|
||||
switch (status) {
|
||||
case 0x90:
|
||||
return tr->note[chan][data][index];
|
||||
|
@ -183,18 +176,54 @@ fetch_stroke(translation *tr, int status, int chan, int data,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#define MAX_WINNAME_SIZE 1024
|
||||
static char last_window_name[MAX_WINNAME_SIZE];
|
||||
static char last_window_class[MAX_WINNAME_SIZE];
|
||||
static Window last_focused_window = 0;
|
||||
static translation *last_window_translation = NULL, *last_translation = NULL;
|
||||
static int have_window = 0;
|
||||
|
||||
void reload_callback(void)
|
||||
{
|
||||
last_focused_window = 0;
|
||||
last_window_translation = last_translation = NULL;
|
||||
have_window = 0;
|
||||
}
|
||||
|
||||
static char *note_names[] = { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" };
|
||||
|
||||
void
|
||||
send_strokes(translation *tr, int status, int chan, int data,
|
||||
send_strokes(translation *tr, uint8_t portno, int status, int chan, int data,
|
||||
int index, int dir)
|
||||
{
|
||||
int nkeys = 0;
|
||||
stroke *s = fetch_stroke(tr, status, chan, data, index, dir);
|
||||
stroke *s = fetch_stroke(tr, portno, status, chan, data, index, dir);
|
||||
|
||||
if (s == NULL) {
|
||||
if (!s && enable_jack_output) {
|
||||
// fall back to default MIDI translation
|
||||
tr = default_midi_translation[portno];
|
||||
s = fetch_stroke(tr, portno, status, chan, data, index, dir);
|
||||
// Ignore all MIDI input on the second port if no translation was found in
|
||||
// the [MIDI2] section (or the section is missing altogether).
|
||||
if (portno && !s) return;
|
||||
}
|
||||
|
||||
if (!s) {
|
||||
// fall back to the default translation
|
||||
tr = default_translation;
|
||||
s = fetch_stroke(tr, status, chan, data, index, dir);
|
||||
s = fetch_stroke(tr, portno, status, chan, data, index, dir);
|
||||
}
|
||||
|
||||
if (s && debug_regex && (!have_window || tr != last_translation)) {
|
||||
last_translation = tr;
|
||||
have_window = 1;
|
||||
if (tr != NULL) {
|
||||
printf("translation: %s for %s (class %s)\n",
|
||||
tr->name, last_window_name, last_window_class);
|
||||
} else {
|
||||
printf("no translation found for %s (class %s)\n",
|
||||
last_window_name, last_window_class);
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_keys && s) {
|
||||
|
@ -242,7 +271,7 @@ send_strokes(translation *tr, int status, int chan, int data,
|
|||
send_key(s->keysym, s->press);
|
||||
nkeys++;
|
||||
} else {
|
||||
send_midi(s->status, s->data, s->step, s->incr, index, dir);
|
||||
send_midi(portno, s->status, s->data, s->step, s->incr, index, dir);
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
|
@ -316,34 +345,29 @@ walk_window_tree(Window win, char **window_class)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static Window last_focused_window = 0;
|
||||
static translation *last_window_translation = NULL;
|
||||
|
||||
translation *
|
||||
get_focused_window_translation()
|
||||
{
|
||||
Window focus;
|
||||
int revert_to;
|
||||
char *window_name = NULL, *window_class = NULL;
|
||||
char *name;
|
||||
|
||||
XGetInputFocus(display, &focus, &revert_to);
|
||||
if (focus != last_focused_window) {
|
||||
last_focused_window = focus;
|
||||
window_name = walk_window_tree(focus, &window_class);
|
||||
if (window_name == NULL) {
|
||||
name = "-- Unlabeled Window --";
|
||||
last_window_translation = get_translation(window_name, window_class);
|
||||
if (window_name && *window_name) {
|
||||
strncpy(last_window_name, window_name, MAX_WINNAME_SIZE);
|
||||
last_window_name[MAX_WINNAME_SIZE-1] = 0;
|
||||
} else {
|
||||
name = window_name;
|
||||
strcpy(last_window_name, "Unnamed");;
|
||||
}
|
||||
last_window_translation = get_translation(name, window_class);
|
||||
if (debug_regex) {
|
||||
if (last_window_translation != NULL) {
|
||||
printf("translation: %s for %s (class %s)\n",
|
||||
last_window_translation->name, name, window_class);
|
||||
} else {
|
||||
printf("no translation found for %s (class %s)\n", name, window_class);
|
||||
}
|
||||
if (window_class && *window_class) {
|
||||
strncpy(last_window_class, window_class, MAX_WINNAME_SIZE);
|
||||
last_window_class[MAX_WINNAME_SIZE-1] = 0;
|
||||
} else {
|
||||
strcpy(last_window_name, "Unnamed");;
|
||||
}
|
||||
if (window_name != NULL) {
|
||||
XFree(window_name);
|
||||
|
@ -355,155 +379,201 @@ get_focused_window_translation()
|
|||
return last_window_translation;
|
||||
}
|
||||
|
||||
static int8_t inccvalue[16][128];
|
||||
static int16_t inpbvalue[16] =
|
||||
{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
|
||||
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192};
|
||||
static int8_t inccvalue[2][16][128];
|
||||
static int16_t inpbvalue[2][16] =
|
||||
{{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
|
||||
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192},
|
||||
{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
|
||||
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192}};
|
||||
|
||||
static uint8_t notedown[16][128];
|
||||
static uint8_t inccdown[16][128];
|
||||
static uint8_t inpbdown[16];
|
||||
static uint8_t notedown[2][16][128];
|
||||
static uint8_t inccdown[2][16][128];
|
||||
static uint8_t inpbdown[2][16];
|
||||
|
||||
int
|
||||
check_incr(translation *tr, int chan, int data)
|
||||
check_incr(translation *tr, uint8_t portno, int chan, int data)
|
||||
{
|
||||
if (tr->ccs[chan][data][0] || tr->ccs[chan][data][1])
|
||||
if (tr && tr->portno == portno &&
|
||||
(tr->ccs[chan][data][0] || tr->ccs[chan][data][1]))
|
||||
return tr->is_incr[chan][data];
|
||||
tr = default_midi_translation[portno];
|
||||
if (tr && tr->portno == portno &&
|
||||
(tr->ccs[chan][data][0] || tr->ccs[chan][data][1]))
|
||||
return tr->is_incr[chan][data];
|
||||
tr = default_translation;
|
||||
if (tr->ccs[chan][data][0] || tr->ccs[chan][data][1])
|
||||
if (tr && tr->portno == portno &&
|
||||
(tr->ccs[chan][data][0] || tr->ccs[chan][data][1]))
|
||||
return tr->is_incr[chan][data];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
check_pbs(translation *tr, int chan)
|
||||
get_cc_step(translation *tr, uint8_t portno, int chan, int data, int dir)
|
||||
{
|
||||
if (tr->pbs[chan][0] || tr->pbs[chan][1])
|
||||
if (tr && tr->portno == portno &&
|
||||
tr->ccs[chan][data][dir>0])
|
||||
return tr->cc_step[chan][data][dir>0];
|
||||
tr = default_midi_translation[portno];
|
||||
if (tr && tr->portno == portno &&
|
||||
tr->ccs[chan][data][dir>0])
|
||||
return tr->cc_step[chan][data][dir>0];
|
||||
tr = default_translation;
|
||||
if (tr && tr->portno == portno &&
|
||||
tr->ccs[chan][data][dir>0])
|
||||
return tr->cc_step[chan][data][dir>0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
check_pbs(translation *tr, uint8_t portno, int chan)
|
||||
{
|
||||
if (tr && tr->portno == portno &&
|
||||
(tr->pbs[chan][0] || tr->pbs[chan][1]))
|
||||
return 1;
|
||||
tr = default_midi_translation[portno];
|
||||
if (tr && tr->portno == portno &&
|
||||
(tr->pbs[chan][0] || tr->pbs[chan][1]))
|
||||
return 1;
|
||||
tr = default_translation;
|
||||
if (tr->pbs[chan][0] || tr->pbs[chan][1])
|
||||
if (tr && tr->portno == portno &&
|
||||
(tr->pbs[chan][0] || tr->pbs[chan][1]))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
get_pb_step(translation *tr, uint8_t portno, int chan, int dir)
|
||||
{
|
||||
if (tr && tr->portno == portno &&
|
||||
tr->pbs[chan][dir>0])
|
||||
return tr->pb_step[chan][dir>0];
|
||||
tr = default_midi_translation[portno];
|
||||
if (tr && tr->portno == portno &&
|
||||
tr->pbs[chan][dir>0])
|
||||
return tr->pb_step[chan][dir>0];
|
||||
tr = default_translation;
|
||||
if (tr && tr->portno == portno &&
|
||||
tr->pbs[chan][dir>0])
|
||||
return tr->pb_step[chan][dir>0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
handle_event(uint8_t *msg)
|
||||
handle_event(uint8_t *msg, uint8_t portno)
|
||||
{
|
||||
translation *tr = get_focused_window_translation();
|
||||
|
||||
//fprintf(stderr, "midi: %0x %0x %0x\n", msg[0], msg[1], msg[2]);
|
||||
if (tr != NULL) {
|
||||
int status = msg[0] & 0xf0, chan = msg[0] & 0x0f;
|
||||
if (status == 0x80) {
|
||||
status = 0x90;
|
||||
msg[0] = status | chan;
|
||||
msg[2] = 0;
|
||||
//fprintf(stderr, "midi [%d]: %0x %0x %0x\n", portno, msg[0], msg[1], msg[2]);
|
||||
int status = msg[0] & 0xf0, chan = msg[0] & 0x0f;
|
||||
if (status == 0x80) {
|
||||
status = 0x90;
|
||||
msg[0] = status | chan;
|
||||
msg[2] = 0;
|
||||
}
|
||||
switch (status) {
|
||||
case 0xc0:
|
||||
send_strokes(tr, portno, status, chan, msg[1], 0, 0);
|
||||
send_strokes(tr, portno, status, chan, msg[1], 1, 0);
|
||||
break;
|
||||
case 0x90:
|
||||
if (msg[2]) {
|
||||
if (!notedown[portno][chan][msg[1]]) {
|
||||
send_strokes(tr, portno, status, chan, msg[1], 0, 0);
|
||||
notedown[portno][chan][msg[1]] = 1;
|
||||
}
|
||||
} else {
|
||||
if (notedown[portno][chan][msg[1]]) {
|
||||
send_strokes(tr, portno, status, chan, msg[1], 1, 0);
|
||||
notedown[portno][chan][msg[1]] = 0;
|
||||
}
|
||||
}
|
||||
switch (status) {
|
||||
case 0xc0:
|
||||
send_strokes(tr, status, chan, msg[1], 0, 0);
|
||||
send_strokes(tr, status, chan, msg[1], 1, 0);
|
||||
break;
|
||||
case 0x90:
|
||||
if (msg[2]) {
|
||||
if (!notedown[chan][msg[1]]) {
|
||||
send_strokes(tr, status, chan, msg[1], 0, 0);
|
||||
notedown[chan][msg[1]] = 1;
|
||||
}
|
||||
} else {
|
||||
if (notedown[chan][msg[1]]) {
|
||||
send_strokes(tr, status, chan, msg[1], 1, 0);
|
||||
notedown[chan][msg[1]] = 0;
|
||||
}
|
||||
break;
|
||||
case 0xb0:
|
||||
if (msg[2]) {
|
||||
if (!inccdown[portno][chan][msg[1]]) {
|
||||
send_strokes(tr, portno, status, chan, msg[1], 0, 0);
|
||||
inccdown[portno][chan][msg[1]] = 1;
|
||||
}
|
||||
break;
|
||||
case 0xb0:
|
||||
if (msg[2]) {
|
||||
if (!inccdown[chan][msg[1]]) {
|
||||
send_strokes(tr, status, chan, msg[1], 0, 0);
|
||||
inccdown[chan][msg[1]] = 1;
|
||||
}
|
||||
} else {
|
||||
if (inccdown[chan][msg[1]]) {
|
||||
send_strokes(tr, status, chan, msg[1], 1, 0);
|
||||
inccdown[chan][msg[1]] = 0;
|
||||
}
|
||||
} else {
|
||||
if (inccdown[portno][chan][msg[1]]) {
|
||||
send_strokes(tr, portno, status, chan, msg[1], 1, 0);
|
||||
inccdown[portno][chan][msg[1]] = 0;
|
||||
}
|
||||
if (check_incr(tr, chan, msg[1])) {
|
||||
// Incremental controller a la MCU. NB: This assumes a signed bit
|
||||
// representation (values above 0x40 indicate counter-clockwise
|
||||
// rotation), which seems to be what most DAWs expect nowadays.
|
||||
// But some DAWs may also have it the other way round, so that you may
|
||||
// have to swap the actions for increment and decrement. XXXTODO:
|
||||
// Maybe the encoding should be a configurable parameter?
|
||||
if (msg[2] < 64) {
|
||||
int d = msg[2];
|
||||
while (d) {
|
||||
send_strokes(tr, status, chan, msg[1], 0, 1);
|
||||
d--;
|
||||
}
|
||||
} else if (msg[2] > 64) {
|
||||
int d = msg[2]-64;
|
||||
while (d) {
|
||||
send_strokes(tr, status, chan, msg[1], 0, -1);
|
||||
d--;
|
||||
}
|
||||
}
|
||||
} else if (inccvalue[chan][msg[1]] != msg[2]) {
|
||||
int dir = inccvalue[chan][msg[1]] > msg[2] ? -1 : 1;
|
||||
int step = tr->cc_step[chan][msg[1]][dir>0];
|
||||
if (step) {
|
||||
while (inccvalue[chan][msg[1]] != msg[2]) {
|
||||
int d = abs(inccvalue[chan][msg[1]] - msg[2]);
|
||||
if (d > step) d = step;
|
||||
if (d < step) break;
|
||||
send_strokes(tr, status, chan, msg[1], 0, dir);
|
||||
inccvalue[chan][msg[1]] += dir*d;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xe0: {
|
||||
int bend = ((msg[2] << 7) | msg[1]) - 8192;
|
||||
//fprintf(stderr, "pb %d\n", bend);
|
||||
if (bend) {
|
||||
if (!inpbdown[chan]) {
|
||||
send_strokes(tr, status, chan, 0, 0, 0);
|
||||
inpbdown[chan] = 1;
|
||||
}
|
||||
} else {
|
||||
if (inpbdown[chan]) {
|
||||
send_strokes(tr, status, chan, 0, 1, 0);
|
||||
inpbdown[chan] = 0;
|
||||
}
|
||||
}
|
||||
if (check_pbs(tr, chan) && inpbvalue[chan] - 8192 != bend) {
|
||||
int dir = inpbvalue[chan] - 8192 > bend ? -1 : 1;
|
||||
int step = tr->pb_step[chan][dir>0];
|
||||
if (step) {
|
||||
while (inpbvalue[chan] - 8192 != bend) {
|
||||
int d = abs(inpbvalue[chan] - 8192 - bend);
|
||||
if (d > step) d = step;
|
||||
if (d < step) break;
|
||||
send_strokes(tr, status, chan, 0, 0, dir);
|
||||
inpbvalue[chan] += dir*d;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// ignore everything else
|
||||
break;
|
||||
if (check_incr(tr, portno, chan, msg[1])) {
|
||||
// Incremental controller a la MCU. NB: This assumes a signed bit
|
||||
// representation (values above 0x40 indicate counter-clockwise
|
||||
// rotation), which seems to be what most DAWs expect nowadays.
|
||||
// But some DAWs may also have it the other way round, so that you may
|
||||
// have to swap the actions for increment and decrement. XXXTODO:
|
||||
// Maybe the encoding should be a configurable parameter?
|
||||
if (msg[2] < 64) {
|
||||
int d = msg[2];
|
||||
while (d) {
|
||||
send_strokes(tr, portno, status, chan, msg[1], 0, 1);
|
||||
d--;
|
||||
}
|
||||
} else if (msg[2] > 64) {
|
||||
int d = msg[2]-64;
|
||||
while (d) {
|
||||
send_strokes(tr, portno, status, chan, msg[1], 0, -1);
|
||||
d--;
|
||||
}
|
||||
}
|
||||
} else if (inccvalue[portno][chan][msg[1]] != msg[2]) {
|
||||
int dir = inccvalue[portno][chan][msg[1]] > msg[2] ? -1 : 1;
|
||||
int step = get_cc_step(tr, portno, chan, msg[1], dir);
|
||||
if (step) {
|
||||
while (inccvalue[portno][chan][msg[1]] != msg[2]) {
|
||||
int d = abs(inccvalue[portno][chan][msg[1]] - msg[2]);
|
||||
if (d > step) d = step;
|
||||
if (d < step) break;
|
||||
send_strokes(tr, portno, status, chan, msg[1], 0, dir);
|
||||
inccvalue[portno][chan][msg[1]] += dir*d;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xe0: {
|
||||
int bend = ((msg[2] << 7) | msg[1]) - 8192;
|
||||
//fprintf(stderr, "pb %d\n", bend);
|
||||
if (bend) {
|
||||
if (!inpbdown[portno][chan]) {
|
||||
send_strokes(tr, portno, status, chan, 0, 0, 0);
|
||||
inpbdown[portno][chan] = 1;
|
||||
}
|
||||
} else {
|
||||
if (inpbdown[portno][chan]) {
|
||||
send_strokes(tr, portno, status, chan, 0, 1, 0);
|
||||
inpbdown[portno][chan] = 0;
|
||||
}
|
||||
}
|
||||
if (check_pbs(tr, portno, chan) && inpbvalue[portno][chan] - 8192 != bend) {
|
||||
int dir = inpbvalue[portno][chan] - 8192 > bend ? -1 : 1;
|
||||
int step = get_pb_step(tr, portno, chan, dir);
|
||||
if (step) {
|
||||
while (inpbvalue[portno][chan] - 8192 != bend) {
|
||||
int d = abs(inpbvalue[portno][chan] - 8192 - bend);
|
||||
if (d > step) d = step;
|
||||
if (d < step) break;
|
||||
send_strokes(tr, portno, status, chan, 0, 0, dir);
|
||||
inpbvalue[portno][chan] += dir*d;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// ignore everything else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void help(char *progname)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-h] [-o] [-r rcfile] [-d[rskj]]\n", progname);
|
||||
fprintf(stderr, "Usage: %s [-h] [-o[2]] [-r rcfile] [-d[rskj]]\n", progname);
|
||||
fprintf(stderr, "-h print this message\n");
|
||||
fprintf(stderr, "-o enable MIDI output\n");
|
||||
fprintf(stderr, "-o enable MIDI output (add 2 for a second pair of ports)\n");
|
||||
fprintf(stderr, "-r config file name (default: MIDIZAP_CONFIG_FILE variable or ~/.midizaprc)\n");
|
||||
fprintf(stderr, "-d debug (r = regex, s = strokes, k = keys, j = jack; default: all)\n");
|
||||
}
|
||||
|
@ -521,19 +591,31 @@ void quitter()
|
|||
#define CONF_FREQ 1
|
||||
#define MAX_COUNT (1000000/CONF_FREQ/POLL_INTERVAL)
|
||||
|
||||
int n_ports = 1;
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
uint8_t msg[3];
|
||||
int opt, count = 0;
|
||||
|
||||
while ((opt = getopt(argc, argv, "hod::r:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "ho::d::r:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help(argv[0]);
|
||||
exit(0);
|
||||
case 'o':
|
||||
enable_jack_output = 1;
|
||||
if (optarg && *optarg) {
|
||||
const char *a = optarg;
|
||||
if (*a == '2') {
|
||||
n_ports = 2;
|
||||
} else if (*a && *a != '1') {
|
||||
fprintf(stderr, "%s: wrong port number (-o), must be 1 or 2\n", argv[0]);
|
||||
fprintf(stderr, "Try -h for help.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (optarg && *optarg) {
|
||||
|
@ -580,7 +662,7 @@ main(int argc, char **argv)
|
|||
|
||||
initdisplay();
|
||||
|
||||
seq.usein = 1; seq.useout = enable_jack_output;
|
||||
seq.n_in = n_ports; seq.n_out = enable_jack_output?n_ports:0;
|
||||
if (!init_jack(&seq, debug_jack)) {
|
||||
exit(1);
|
||||
}
|
||||
|
@ -589,8 +671,9 @@ main(int argc, char **argv)
|
|||
// force the config file to be loaded initially
|
||||
count = MAX_COUNT;
|
||||
while (!quit) {
|
||||
while (pop_midi(&seq, msg)) {
|
||||
handle_event(msg);
|
||||
uint8_t portno;
|
||||
while (pop_midi(&seq, msg, &portno)) {
|
||||
handle_event(msg, portno);
|
||||
count = 0;
|
||||
}
|
||||
usleep(POLL_INTERVAL);
|
||||
|
|
|
@ -67,6 +67,7 @@ typedef struct _translation {
|
|||
char *name;
|
||||
int is_default;
|
||||
regex_t regex;
|
||||
uint8_t portno;
|
||||
// XXXFIXME: This way of storing the translation tables is easy to
|
||||
// construct, but wastes quite a lot of memory (needs some 128 KB per
|
||||
// translation section even if most of the entries are NULL pointers). We
|
||||
|
@ -83,9 +84,12 @@ typedef struct _translation {
|
|||
int pb_step[NUM_CHAN][2];
|
||||
} translation;
|
||||
|
||||
extern void reload_callback(void);
|
||||
extern int read_config_file(void);
|
||||
extern translation *get_translation(char *win_title, char *win_class);
|
||||
extern void print_stroke_sequence(char *name, char *up_or_down, stroke *s);
|
||||
extern translation *default_translation, *default_midi_translation[2];
|
||||
extern int debug_regex, debug_strokes, debug_keys;
|
||||
extern int default_debug_regex, default_debug_strokes, default_debug_keys;
|
||||
extern char *config_file_name;
|
||||
extern int enable_jack_output;
|
||||
|
|
92
readconfig.c
92
readconfig.c
|
@ -33,15 +33,39 @@
|
|||
handle most common use cases. (In any case, adding more message types
|
||||
should be a piece of cake.)
|
||||
|
||||
MIDI messages are on channel 1 by default; a suffix of the form
|
||||
-<1..16> can be used to specify a different MIDI channel. E.g., C3-10
|
||||
denotes note C3 on MIDI channel 10.
|
||||
|
||||
Note messages are specified using the cutomary notation (note name
|
||||
A..G, optionally followed by an accidental, # or b, followed by a
|
||||
(zero-based) MIDI octave number. Note that all MIDI octaves start at
|
||||
the note C, so B0 comes before C1. C5 denotes middle C, A5 is the
|
||||
chamber pitch (usually at 440 Hz).
|
||||
chamber pitch (usually at 440 Hz). Enharmonic spellings are
|
||||
equivalent, so, e.g., D# and Eb denote exactly the same MIDI note.
|
||||
|
||||
MIDI messages are on channel 1 by default; a suffix of the form
|
||||
-<1..16> can be used to specify a different MIDI channel. E.g., C3-10
|
||||
denotes note C3 on MIDI channel 10.
|
||||
*NOTE:* There are various different standards for numbering octaves,
|
||||
and different programs use different standards, which can be rather
|
||||
confusing. There's the Helmholtz standard, which is still widely
|
||||
used, but only in German-speaking countries, and the ASA standard
|
||||
where middle C is C4 (one less than zero-based octave numbers, so the
|
||||
sub-contra octave starts at C-1). At least two standards exist for
|
||||
MIDI octave numbering, one in which middle C is C3 (so the sub-contra
|
||||
octave starts at C-2), and zero-based octave numbers, where the
|
||||
sub-contra octave starts at C0 a.k.a. MIDI note 0. The latter is what
|
||||
we use here, as it probably appeals most to mathematically-inclined
|
||||
and computer-science people like myself. It also relates nicely to
|
||||
MIDI note numbers, since the octave number is just the MIDI note
|
||||
number divided by 12, with the remainder of the division telling you
|
||||
which note in the octave it is (0 = C, 1 = C#, ..., 10 = Bb, 11 = B).
|
||||
|
||||
Thus, if you use some MIDI monitoring software to figure out which
|
||||
notes to put in your midizaprc file, first check how the program
|
||||
prints middle C, so that you know how to adjust the octave numbers
|
||||
reported by the monitoring program.
|
||||
|
||||
More details on the syntax of MIDI messages can be found in the
|
||||
comments preceding the parse_midi() routine below.
|
||||
|
||||
By default, all messages are interpreted in the same way as keys on a
|
||||
computer keyboard, i.e., they can be "on" ("pressed") or "off"
|
||||
|
@ -134,11 +158,21 @@
|
|||
CC1< CC7
|
||||
CC1> CC7
|
||||
|
||||
Furthermore, PB (pitch bends) can have a step size associated with
|
||||
them. The default step size is 1. To indicate a different step size,
|
||||
the notation PB[<step size>] is used. E.g., PB[1170] will give you
|
||||
about 7 steps up and down, which is useful to emulate a shuttle wheel
|
||||
such as those on the Contour Design devices. Example:
|
||||
Furthermore, incremental CC and PB messages can have a step size
|
||||
associated with them, which enable you to scale controller and pitch
|
||||
bend changes. The default step size is 1 (no scaling). To change it,
|
||||
the desired step size is written in brackets immediately after the
|
||||
message token, but before the increment suffix. Thus, e.g., CC1[2]=
|
||||
denotes a sequence to be executed once whenever the controller changes
|
||||
by an amount of 2. For instance, the following translation scales
|
||||
down the values of a controller, effectively dividing them by 2, so
|
||||
that the output range becomes 0..63 (127/2, rounded down):
|
||||
|
||||
CC1[2]= CC1
|
||||
|
||||
As another example, PB[1170] will give you 7 steps up and down, which
|
||||
is useful to emulate a shuttle wheel such as those on the Contour
|
||||
Design devices. Example:
|
||||
|
||||
PB[1170]- "j"
|
||||
PB[1170]+ "l"
|
||||
|
@ -159,7 +193,9 @@
|
|||
*input* message determines whether it is a key press or value change
|
||||
type of event, and which direction it goes in the latter case. Only
|
||||
the "~" suffix can be used to indicate an incremental CC message in
|
||||
sign bit encoding.
|
||||
sign bit encoding. Specifying step sizes with incremental CC and PB
|
||||
messages works as well, but scales the values *up* rather than down on
|
||||
the output side.
|
||||
|
||||
Finally, on the output side there's a special token of the form
|
||||
CH<1..16>, which doesn't actually generate any MIDI message. Rather,
|
||||
|
@ -267,8 +303,7 @@ read_line(FILE *f, char *name)
|
|||
|
||||
static translation *first_translation_section = NULL;
|
||||
static translation *last_translation_section = NULL;
|
||||
|
||||
translation *default_translation;
|
||||
translation *default_translation, *default_midi_translation[2];
|
||||
|
||||
translation *
|
||||
new_translation_section(char *name, char *regex)
|
||||
|
@ -284,7 +319,13 @@ new_translation_section(char *name, char *regex)
|
|||
ret->name = alloc_strcat(name, NULL);
|
||||
if (regex == NULL || *regex == '\0') {
|
||||
ret->is_default = 1;
|
||||
default_translation = ret;
|
||||
if (!strcmp(name, "MIDI"))
|
||||
default_midi_translation[0] = ret;
|
||||
else if (!strcmp(name, "MIDI2")) {
|
||||
default_midi_translation[1] = ret;
|
||||
ret->portno = 1;
|
||||
} else
|
||||
default_translation = ret;
|
||||
} else {
|
||||
ret->is_default = 0;
|
||||
err = regcomp(&ret->regex, regex, REG_NOSUB);
|
||||
|
@ -361,6 +402,8 @@ free_all_translations(void)
|
|||
}
|
||||
first_translation_section = NULL;
|
||||
last_translation_section = NULL;
|
||||
default_translation = default_midi_translation[0] =
|
||||
default_midi_translation[1] = NULL;
|
||||
}
|
||||
|
||||
char *config_file_name = NULL;
|
||||
|
@ -656,12 +699,12 @@ re_press_temp_modifiers(void)
|
|||
msg ::= "ch" | "pb" | "pc" | "cc"
|
||||
incr ::= "-" | "+" | "=" | "<" | ">" | "~"
|
||||
|
||||
Numbers are always in decimal. The meaning of the first number depends on
|
||||
the context (octave number for notes, the actual data byte for other
|
||||
messages). This can optionally be followed by a number in brackets,
|
||||
denoting a step size. Also optionally, the suffix with the third number
|
||||
(after the dash) denotes the MIDI channel; otherwise the default MIDI
|
||||
channel is used.
|
||||
Case is insignificant. Numbers are always in decimal. The meaning of
|
||||
the first number depends on the context (octave number for notes, the
|
||||
actual data byte for other messages). This can optionally be followed
|
||||
by a number in brackets, denoting a step size. Also optionally, the
|
||||
suffix with the third number (after the dash) denotes the MIDI
|
||||
channel; otherwise the default MIDI channel is used.
|
||||
|
||||
Note that not all combinations are possible -- "pb" has no data byte; only
|
||||
"cc" and "pb" may be followed by a step size in brackets; and "ch" must
|
||||
|
@ -885,6 +928,7 @@ start_translation(translation *tr, char *which_key)
|
|||
if (!dir) {
|
||||
is_bidirectional = 1;
|
||||
release_first_stroke = &(tr->pbs[chan][1]);
|
||||
tr->pb_step[chan][1] = step;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1115,6 +1159,7 @@ read_config_file(void)
|
|||
}
|
||||
|
||||
free_all_translations();
|
||||
reload_callback();
|
||||
debug_regex = default_debug_regex;
|
||||
debug_strokes = default_debug_strokes;
|
||||
debug_keys = default_debug_keys;
|
||||
|
@ -1235,18 +1280,15 @@ get_translation(char *win_title, char *win_class)
|
|||
read_config_file();
|
||||
tr = first_translation_section;
|
||||
while (tr != NULL) {
|
||||
extern int enable_jack_output;
|
||||
if (tr->is_default &&
|
||||
(strcmp(tr->name, "MIDI") || enable_jack_output)) {
|
||||
return tr;
|
||||
} else if (!tr->is_default) {
|
||||
if (!tr->is_default) {
|
||||
// AG: We first try to match the class name, since it usually provides
|
||||
// better identification clues.
|
||||
if (win_class && *win_class &&
|
||||
regexec(&tr->regex, win_class, 0, NULL, 0) == 0) {
|
||||
return tr;
|
||||
}
|
||||
if (regexec(&tr->regex, win_title, 0, NULL, 0) == 0) {
|
||||
if (win_title && *win_title &&
|
||||
regexec(&tr->regex, win_title, 0, NULL, 0) == 0) {
|
||||
return tr;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue