#include #include #include #include #include #include #include #include #include /* * Raw to seq converter for ALSA sequencer * * Copyright (C) 2014, 2018 R.J. van der Putten, Leiden, Holland, * rob at sput dot nl. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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. */ void wrtseq(snd_seq_t *seqhnd, snd_seq_event_t *evnt, int port) { snd_seq_ev_set_source(evnt, port); snd_seq_ev_set_subs(evnt); snd_seq_event_output(seqhnd, evnt); snd_seq_drain_output(seqhnd); evnt->type = SND_SEQ_EVENT_NONE; } void clrevts(snd_seq_event_t *evnt) { int i; i = 0; while (i < 4) { evnt[i].type = SND_SEQ_EVENT_NONE; i++; } } void wrtevts(snd_seq_t *seqhnd, snd_seq_event_t *evnt, int port) { int i; i = 0; while (i < 4 && evnt[i].type != SND_SEQ_EVENT_NONE) { wrtseq(seqhnd, &(evnt[i]), port); i++; } clrevts(evnt); } void prtout(snd_seq_event_t *evnt) { printf("Convto: Type: %02X Par: %04X Value: %04X Chan: %02X\n", \ evnt->type, evnt->data.control.param, evnt->data.control.value, evnt->data.control.channel); } void hlp(void) { fprintf(stderr, "Converts ALSA 'raw' continuous controllers into snd_seq.\n"); fprintf(stderr, "(N)RPN increment / decrement is not converted\n"); fprintf(stderr, "usage: raw2seq [-b] [-d] [-n] [-r]\n"); fprintf(stderr, "-d: Debug info\n"); fprintf(stderr, "-b: Convert bank select\n"); fprintf(stderr, "-n: Convert NRPN\n"); fprintf(stderr, "-r: Convert RPN\n"); fprintf(stderr, "Other controllers are always converted.\n"); fprintf(stderr, "\n"); } int main(int argc, char **argv) { /* ALSA */ snd_seq_t *inseqhnd, *outseqhnd; snd_seq_event_t *ev, evts[4]; int inport, outport; int EVSIZ; int evptr; int evtyp; /* poll() */ struct pollfd pfd; int ret; /* User interface */ int opt; int debflg, c14min, nrpnflg, rpnflg; ev = NULL; EVSIZ = sizeof(snd_seq_event_t); memset(evts, 0, sizeof(evts)); /* Event array */ clrevts(evts); /* No event */ evptr = 0; evtyp = SND_SEQ_EVENT_NONE; ret = 0; debflg = 0; c14min = 1; nrpnflg = 0; rpnflg = 0; while ((opt = getopt(argc, argv, "bdhnr")) != -1) { switch (opt) { case 'b': c14min = 0; break; case 'd': debflg = 1; case 'n': nrpnflg = 1; break; case 'r': rpnflg = 1; break; case 'h': default: hlp(); exit(1); } } /* In, block (0): Set to SND_SEQ_NONBLOCK (1) for non-block */ if (snd_seq_open(&inseqhnd, "default", SND_SEQ_OPEN_INPUT, 0) < 0) { fprintf(stderr, "Can't open seq device\n"); exit(1); } snd_seq_set_client_name(inseqhnd, "raw2seq"); if ((inport = snd_seq_create_simple_port(inseqhnd, "raw in", \ SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, \ SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_SOFTWARE)) < 0) { fprintf(stderr, "Can't open in port \n"); exit(1); } /* Out */ if (snd_seq_open(&outseqhnd, "default", SND_SEQ_OPEN_OUTPUT, 0) < 0) { fprintf(stderr, "Can't open seq device\n"); exit(1); } snd_seq_set_client_name(outseqhnd, "raw2seq"); if ((outport = snd_seq_create_simple_port(outseqhnd, "seq out", \ SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, \ SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { fprintf(stderr, "Can't create out port\n"); exit(1); } /* Init poll */ snd_seq_poll_descriptors(inseqhnd, &pfd, 1, POLLIN); /* Loop */ while (1) { ret = poll(&pfd, 1, 2); /* 2 ms timeout */ if (ret < 0) { perror("poll failed"); exit(1); } else if (ret == 0) { /* Timed out. Dump all we have */ wrtevts(outseqhnd, evts, outport); } else if (ret > 0) { /* There is data */ do { /* Read until no data */ snd_seq_event_input(inseqhnd, &ev); if (debflg != 0) { printf("Input: Type: %02X Par: %04X Value: %04X Chan: %02X ", \ ev->type, ev->data.control.param, \ ev->data.control.value, ev->data.control.channel); printf("State: Type: %02X Ptr: %d\n", evtyp, evptr); } if (ev->type == SND_SEQ_EVENT_CONTROLLER) { /* Is a continuous controller */ if (evts[0].type == SND_SEQ_EVENT_NONE) { /* First byte in sequence */ if (ev->data.control.param < 32 && \ ev->data.control.param >= c14min && \ ev->data.control.param != 6) { memcpy(evts, ev, EVSIZ); evtyp = SND_SEQ_EVENT_CONTROL14; } else if (nrpnflg != 0 && ev->data.control.param == 99) { memcpy(evts, ev, EVSIZ); evtyp = SND_SEQ_EVENT_NONREGPARAM; evptr = 1; } else if (rpnflg != 0 && ev->data.control.param == 101) { memcpy(evts, ev, EVSIZ); evtyp = SND_SEQ_EVENT_REGPARAM; evptr = 1; } else { /* Not a 14 bit controller */ wrtseq(outseqhnd, ev, outport); /* Just to be safe */ evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } } else { /* Not the first byte in sequence */ if (evtyp == SND_SEQ_EVENT_CONTROL14) { if (evts[0].data.control.channel == ev->data.control.channel && \ evts[0].data.control.param + 32 == ev->data.control.param) { evts[0].type = SND_SEQ_EVENT_CONTROL14; evts[0].data.control.value = 128 * evts[0].data.control.value + \ ev->data.control.value; if (debflg != 0) prtout(evts); wrtseq(outseqhnd, evts, outport); clrevts(evts); evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } else { /* No match */ wrtevts(outseqhnd, evts, outport); wrtseq(outseqhnd, ev, outport); evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } } else if (evptr < 2 && evtyp == SND_SEQ_EVENT_NONREGPARAM) { if (evptr == 1 && ev->data.control.param == 98) { memcpy(&(evts[1]), ev, EVSIZ); evptr = 2; } else { /* No match */ wrtevts(outseqhnd, evts, outport); wrtseq(outseqhnd, ev, outport); evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } } else if (evptr < 2 && evtyp == SND_SEQ_EVENT_REGPARAM) { if (evptr == 1 && ev->data.control.param == 100) { memcpy(&(evts[1]), ev, EVSIZ); evptr = 2; } else { /* No match */ wrtevts(outseqhnd, evts, outport); wrtseq(outseqhnd, ev, outport); evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } } else if (evptr > 1 && \ (evtyp == SND_SEQ_EVENT_NONREGPARAM || evtyp == SND_SEQ_EVENT_REGPARAM)) { if (evptr == 2 && ev->data.control.param == 6) { memcpy(&(evts[2]), ev, EVSIZ); evptr = 3; } else if (evptr == 3 && ev->data.control.param == 38) { evts[0].type = evtyp; evts[0].data.control.param = 128 * evts[0].data.control.value + \ evts[1].data.control.value; evts[0].data.control.value = 128 * evts[2].data.control.value + \ ev->data.control.value; if (debflg != 0) prtout(evts); wrtseq(outseqhnd, evts, outport); clrevts(evts); evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } else { /* No match */ wrtevts(outseqhnd, evts, outport); wrtseq(outseqhnd, ev, outport); evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } } } } else if (ev->type == SND_SEQ_EVENT_CLOCK || ev->type == SND_SEQ_EVENT_TICK || \ ev->type == SND_SEQ_EVENT_START || ev->type == SND_SEQ_EVENT_CONTINUE || \ ev->type == SND_SEQ_EVENT_STOP || \ ev->type == SND_SEQ_EVENT_SENSING || ev->type == SND_SEQ_EVENT_RESET) { /* * System realtime. These are midi commands 0xF8 to 0xFF. 0xFD is not defined. * These do not reset status => Just write. */ wrtseq(outseqhnd, ev, outport); } else { /* Not a controller or system realtime */ wrtevts(outseqhnd, evts, outport); wrtseq(outseqhnd, ev, outport); evptr = 0; evtyp = SND_SEQ_EVENT_NONE; } /* snd_seq_free_event(ev); */ } while (snd_seq_event_input_pending(inseqhnd, 0) > 0); } } return(0); }