jack-tools

A handful of JACK audio tools
git clone git://henryandlizzy.uk/jack-tools
Log | Files | Refs

jack-cc-map.cpp (3410B)


      1 #include <jack/midiport.h>
      2 
      3 #include "jack.hpp"
      4 
      5 #include <memory>
      6 #include <cassert>
      7 
      8 #include <cmath>
      9 #include <cstring>
     10 
     11 #include <regex.h>
     12 //#include <assert.h>
     13 //#include <stdio.h>
     14 //#include <string.h>
     15 
     16 #define ARRAY_SIZE(a)   (sizeof(a) / sizeof(*a))
     17 //#define min(a,b) \
     18 //   ({ __typeof__ (a) _a = (a); \
     19 //       __typeof__ (b) _b = (b); \
     20 //     _a < _b ? _a : _b; })
     21 
     22 
     23 #include <thread>
     24 #include <chrono>
     25 #include <iostream>
     26 #include <iomanip>
     27 
     28 using namespace std::literals::chrono_literals;
     29 
     30 jack::client client("CC-map", JackNoStartServer);
     31 jack::port p1(client, "in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
     32 jack::port p2(client, "out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
     33 
     34 unsigned char remap[128];
     35 
     36 int cb_process(jack_nframes_t nframes, void* arg)
     37 {
     38 	void* in = jack_port_get_buffer(*p1, nframes);
     39 	void* out = jack_port_get_buffer(*p2, nframes);
     40 
     41 	jack_midi_clear_buffer(out);
     42 
     43 	jack_midi_event_t event;
     44 	for (uint32_t i = 0; not jack_midi_event_get(&event, in, i); ++i)
     45 	{
     46 		jack_midi_data_t* buf = jack_midi_event_reserve(out, event.time, event.size);
     47 		memcpy(buf, event.buffer, event.size);
     48 
     49 		for (unsigned i = 0; i < event.size; ++i)
     50 		{
     51 			if (~buf[0] & 0x80)
     52 				continue;
     53 
     54 			if ((buf[0] >> 4 & 0x7) != 3) // Controller change
     55 				continue;
     56 
     57 			buf[1] = remap[buf[1]];
     58 		}
     59 	}
     60 
     61 	return 0;
     62 }
     63 
     64 extern char const* names[98];
     65 
     66 unsigned find_cc(char const* str)
     67 {
     68 	for (unsigned i = 0; i < ARRAY_SIZE(names); ++i)
     69 		if (names[i] && !strcmp(str, names[i]))
     70 			return i;
     71 
     72 	printf("'%s' is not a known MIDI CC\n", str);
     73 	exit(1);
     74 }
     75 
     76 unsigned check_cc_in_range(char const* str)
     77 {
     78 	unsigned long val = strtoul(str, NULL, 10);
     79 
     80 	if (val < 128)
     81 		return val;
     82 
     83 	printf("%s is too large to be a MIDI CC", str);
     84 	exit(1);
     85 }
     86 
     87 void check_regerror(int err, regex_t* preg)
     88 {
     89 	if (!err)
     90 		return;
     91 
     92 	char buf[256];
     93 	regerror(err, preg, buf, sizeof(buf));
     94 	std::cerr << buf << '\n';
     95 	exit(1);
     96 }
     97 
     98 int main(int argc, char* argv[])
     99 {
    100 	for (unsigned i = 0; i < 128; ++i)
    101 		remap[i] = i;
    102 
    103 	char const re[] = "^(([[:digit:]]+)|([[:alnum:]]+))=(([[:digit:]]+)|([[:alnum:]]+))$";
    104 	regex_t preg;
    105 
    106 	check_regerror(regcomp(&preg, re, REG_EXTENDED), &preg);
    107 
    108 	if (argc < 2)
    109 	{
    110 		std::cerr << "Usage:\n"
    111 		"    jack-cc-map IN=OUT ...\n\n"
    112 		"IN and OUT can either be:\n"
    113 		"    a number from 0 to 127\n"
    114 		"  OR\n"
    115 		"    a symbolic name\n\n"
    116 		"CC messages will be routed from IN to OUT\n";
    117 		return 1;
    118 	}
    119 
    120 	while (*++argv)
    121 	{
    122 		unsigned in, out;
    123 		char const* const str = *argv;
    124 		regmatch_t pmatch[7];
    125 
    126 		int res = regexec(&preg, str, ARRAY_SIZE(pmatch), pmatch, 0);
    127 		if (res == REG_NOMATCH)
    128 		{
    129 			std::cerr << "Bad mapping '" << str << "' is not in format IN=OUT\n";
    130 			return 1;
    131 		}
    132 
    133 		check_regerror(res, &preg);
    134 
    135 		for (unsigned j = 0; j < ARRAY_SIZE(pmatch); ++j)
    136 		{
    137 			if (pmatch[j].rm_so == -1)
    138 				continue;
    139 
    140 			char buf[128];
    141 			size_t len = pmatch[j].rm_eo - pmatch[j].rm_so;
    142 			len = std::min(len, ARRAY_SIZE(buf) - 1);
    143 
    144 			strncpy(buf, str + pmatch[j].rm_so, len)[len] = '\0';
    145 
    146 			switch (j)
    147 			{
    148 			case 2:
    149 				in = check_cc_in_range(buf);
    150 				break;
    151 			case 5:
    152 				out = check_cc_in_range(buf);
    153 				break;
    154 			case 3:
    155 				in = find_cc(buf);
    156 				break;
    157 			case 6:
    158 				out = find_cc(buf);
    159 				break;
    160 			}
    161 		}
    162 
    163 		remap[in] = out;
    164 	}
    165 
    166 	regfree(&preg);
    167 
    168 	client.set_process_callback(cb_process, nullptr);
    169 	client.activate();
    170 
    171 	for (;;)
    172 		std::this_thread::sleep_for(100ms);
    173 
    174 	return 0;
    175 }