jack.cpp
This is a replacement jack.cpp edited by Drumfix, for compiling a libaam.so that uses jack.
Download source code: jack.cpp
As per jorgen, the source is public domain (the original version is here).
This version creates one ALSA MIDI input, which can be routed using qjackctl. If you need more than one, edit this line:
#define NUM_MIDI_INS 1
To compile jack.cpp, you need libasound and libjack installed (libasound2, libasound2-dev, libjack0, libjack-dev). Then compile with this line:
g++ -shared -lasound -ljack jack.cpp -o /_path_to_energyXT2_/libaam.so
// energyXT - JACK audio interface // author: jorgen aase - www.energy-xt.com // compile: g++ -shared -lasound -ljack jack.cpp -o /_path_to_energyXT2_/libaam.so #include <alsa/asoundlib.h> #include <jack/jack.h> #define NUM_MIDI_INS 1 // libaam interface int libaam (int index, int value1, int value2) asm ("libaam"); const int STREAM_INIT = 0; const int STREAM_OPEN = 1; const int STREAM_CLOSE = 2; //const int STREAM_PLAY = 3; //const int STREAM_STOP = 4; const int STREAM_EXIT = 5; const int STREAM_ABOUT = 6; const int STREAM_AUDIO_COUNT = 7; const int STREAM_AUDIO_NAME = 8; const int STREAM_FRAMES = 9; const int STREAM_RATE = 10; const int STREAM_LATENCY = 17; const int STREAM_RELOAD = 18; const int MIDI_IN_COUNT = 30; const int MIDI_IN_NAME = 31; const int MIDI_IN_ENABLE = 32; const int MIDI_IN_ENABLED = 33; const int AUDIO_IN_COUNT = 40; const int AUDIO_IN_NAME = 41; const int AUDIO_IN_ENABLE = 42; const int AUDIO_IN_ENABLED = 43; const int AUDIO_OUT_COUNT = 50; const int AUDIO_OUT_NAME = 51; const int AUDIO_OUT_ENABLE = 52; const int AUDIO_OUT_ENABLED = 53; // create a new alsa sequencer client snd_seq_t *aseq_client; snd_seq_t *open_client() { snd_seq_t *handle; int err; err = snd_seq_open(&handle, "default", SND_SEQ_OPEN_DUPLEX, 0); if (err < 0) return NULL; snd_seq_set_client_name(handle, "EnergyXT2"); return handle; } // create a new port; return the port id // port will be read/writable and accept the read/write-subscription. int new_in_port(snd_seq_t *handle, char *name) { return snd_seq_create_simple_port(handle, name, SND_SEQ_PORT_CAP_WRITE |SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC); } // create a new port; return the port id // port will be read/writable and accept the read/write-subscription. int new_out_port(snd_seq_t *handle, char *name) { return snd_seq_create_simple_port(handle, name, SND_SEQ_PORT_CAP_READ |SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC); } typedef int (CProcess) (float** inputs, float** outputs, int numIns, int numOuts, int frames, void* midiData, int midiDataCount); CProcess* process = 0; struct MidiThread { bool terminated; pthread_t handle; } midithread; void *execute(void *); void start_midi_thread() { midithread.terminated = false; pthread_create(&midithread.handle, 0, execute, 0); } void stop_midi_thread() { midithread.terminated = true; pthread_join(midithread.handle, 0); } struct CMIDIData { int data; // midi bytes char port; // midi port index void* buffer; // pointer to buffer (sysex etc) }; int midiDevices = 0; unsigned int frames = 1024; unsigned int rate = 44100; pthread_mutex_t mutex_midiin = PTHREAD_MUTEX_INITIALIZER; const int maxChs = 32; // jack audio struct CJackPort { char id[255]; int enabled, active; jack_port_t *port; }; int aoutcount = 0, aincount = 0, ainused = 0, aoutused = 0; jack_client_t *client = 0; CJackPort ain[maxChs], aout[maxChs]; // midi device const int maxMIDIDevs = 64; class CMIDIDev { public: snd_rawmidi_t *handle; char device[255], id[255]; int enabled; int index; CMIDIDev(); ~CMIDIDev(); void enable(int enable); }; CMIDIDev* mdevs[maxMIDIDevs]; // midi data buffer CMIDIData midiData[128]; int midiDataCount = 0; // jack process jack_default_audio_sample_t* outputs[maxChs], *inputs[maxChs]; int process_jack (jack_nframes_t samples, void *arg) { int i, tempBufferCount, used; CMIDIData tempBuffer[128]; jack_transport_state_t ts = jack_transport_query(client, 0); // if (ts == JackTransportRolling) // { // intput & output buffers used = 0; for (i = 0; i < maxChs, used < ainused; i++) { if (ain[i].active) inputs[used++] = (jack_default_audio_sample_t*) jack_port_get_buffer (ain[i].port, samples); } used = 0; for (i = 0; i < maxChs, used < aoutused; i++) { if (aout[i].active) outputs[used++] = (jack_default_audio_sample_t*) jack_port_get_buffer (aout[i].port, samples); } // midi in pthread_mutex_lock(&mutex_midiin); memcpy(tempBuffer, midiData, midiDataCount * sizeof(CMIDIData)); tempBufferCount = midiDataCount; midiDataCount = 0; pthread_mutex_unlock(&mutex_midiin); // process process(inputs, outputs, ainused, aoutused, samples, &tempBuffer, tempBufferCount); // todo: midi out // } return 0; } void jack_shutdown (void *arg) { // todo } // jack open device void openJack() { char str[255]; jack_status_t status; jack_options_t options = JackNullOption; ainused = 0; aoutused = 0; // open client aincount = 0; aoutcount = 0; client = jack_client_open ("energyXT2", options, &status, 0); if (client) { // setup process jack_set_process_callback(client, process_jack, 0); jack_on_shutdown(client, jack_shutdown, 0); // get sample rate rate = jack_get_sample_rate (client); // set/get buffer frames frames = jack_get_buffer_size (client); // activate client if (jack_activate (client)) { jack_client_close (client); client = 0; printf("jack_activate failed\n"); } // inputs and outputs else { int i; const char **ports; // audio inputs ports = jack_get_ports (client, 0, 0, JackPortIsPhysical | JackPortIsOutput); if (ports) { i = -1; while (ports[++i]) { strcpy(ain[i].id, ports[i]); aincount++; // connect ain[i].active = 0; if (ain[i].enabled) { sprintf(str, "in%d", ainused); ain[i].port = jack_port_register (client, str, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); if (ain[i].port && jack_connect (client, ain[i].id, jack_port_name (ain[i].port)) == 0) { ainused++; ain[i].active = 1; } } } } free (ports); // audio outputs ports = jack_get_ports (client, 0, 0, JackPortIsPhysical | JackPortIsInput); if (ports) { i = -1; while (ports[++i]) { strcpy(aout[i].id, ports[i]); aoutcount++; // connect aout[i].active = 0; if (aout[i].enabled) { sprintf(str, "out%d", aoutused); aout[i].port = jack_port_register (client, str, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (aout[i].port && jack_connect (client, jack_port_name (aout[i].port), aout[i].id) == 0) { aoutused++; aout[i].active = 1; } } } } free (ports); } } // start jack transporter // todo: check if already started // todo: dont start automatically? // if (client) // jack_transport_start(client); } // jack close device void closeJack() { if (client) { jack_client_close (client); client = 0; } } // init void init() { char id[16]; int i; int in; // disable all channels (by default) for (i = 0; i < maxChs; i++) { ain[i].enabled = false; aout[i].enabled = false; } midiDevices = 0; // ALSA midi aseq_client = open_client(); for (i=0; i<NUM_MIDI_INS; i++) { sprintf(id, "aseq %d", i); if ((in = new_in_port(aseq_client, id)) != -1) { mdevs[i] = new CMIDIDev(); mdevs[i]->index = in; strcpy(mdevs[i]->device, "alsaseq"); strcpy(mdevs[i]->id, id); midiDevices++; } // if } // for if (midiDevices) start_midi_thread(); } // init // deinit void deinit() { stop_midi_thread(); pthread_mutex_destroy(&mutex_midiin); } // dispatcher int libaam (int index, int value1, int value2) { int i, result = -1; switch (index) { // init library case STREAM_INIT: init(); process = (CProcess*)value1; break; // exit library case STREAM_EXIT: deinit(); break; // device count case STREAM_AUDIO_COUNT: result = 1; // jack only break; // device name case STREAM_AUDIO_NAME: strcpy((char*)value2, "JACK Audio"); break; // open device case STREAM_OPEN: openJack(); result = 1; break; // close device case STREAM_CLOSE: closeJack(); result = 1; break; // get/set frames case STREAM_FRAMES: if (value1 > 0) frames = value1; result = frames; break; // get/set rate case STREAM_RATE: if (value1 == 0) *(float*)value2 = rate; else if (value1 > 0) rate = value1; break; // audio in count case AUDIO_IN_COUNT: return aincount; break; // audio out enable case AUDIO_IN_ENABLE: if (value1 >= 0 && value1 < maxChs) ain[value1].enabled = value2; break; // audio in enabled case AUDIO_IN_ENABLED: if (value1 >= 0 && value1 < maxChs) result = ain[value1].enabled; break; // audio in name case AUDIO_IN_NAME: if (value1 >= 0 && value1 < aincount) strcpy((char*)value2, ain[value1].id); break; // audio out count case AUDIO_OUT_COUNT: return aoutcount; break; // audio out enable case AUDIO_OUT_ENABLE: if (value1 >= 0 && value1 < maxChs) aout[value1].enabled = value2; break; // audio out enabled case AUDIO_OUT_ENABLED: if (value1 >= 0 && value1 < maxChs) result = aout[value1].enabled; break; // audio out name case AUDIO_OUT_NAME: if (value1 >= 0 && value1 < aoutcount) strcpy((char*)value2, aout[value1].id); break; // midi in count case MIDI_IN_COUNT: return midiDevices; break; // midi in name case MIDI_IN_NAME: if (value1 >= 0 && value1 < midiDevices) strcpy((char*)value2, mdevs[value1]->id); break; // enable midi in case MIDI_IN_ENABLE: if (value1 >= 0 && value1 < midiDevices) mdevs[value1]->enable(value2); break; // is midi in enabled? case MIDI_IN_ENABLED: if (value1 >= 0 && value1 < midiDevices) return mdevs[value1]->enabled; break; } return result; } CMIDIDev :: CMIDIDev() { enabled = 0; } CMIDIDev::~CMIDIDev() { enable(0); } // midi dev enable void CMIDIDev :: enable(int enable) { enabled = enable; } //CMIDIThread :: CMIDIThread() //{ //}; // handle midi void handleMIDI(snd_seq_event_t *ev) { int i = 0; int data = 0; switch (ev->type) { case SND_SEQ_EVENT_NOTEON: data = (0x90 | ev->data.note.channel) | (ev->data.note.note << 8) | (ev->data.note.velocity << 16); break; case SND_SEQ_EVENT_NOTEOFF: data = (0x80 | ev->data.note.channel) | (ev->data.note.note << 8) | (ev->data.note.off_velocity << 16); break; case SND_SEQ_EVENT_KEYPRESS: data = (0xA0 | ev->data.note.channel) | (ev->data.note.note << 8) | (ev->data.note.velocity << 16); break; case SND_SEQ_EVENT_CONTROLLER: data = (0xB0 | ev->data.control.channel) | (ev->data.control.param << 8) | (ev->data.control.value << 16); break; case SND_SEQ_EVENT_PGMCHANGE: data = (0xC0 | ev->data.control.channel) | (ev->data.control.value << 8); break; case SND_SEQ_EVENT_CHANPRESS: data = (0xD0 | ev->data.control.channel) | (ev->data.control.value << 8); break; case SND_SEQ_EVENT_PITCHBEND: ev->data.control.value += 8192; data = (0xE0 | ev->data.control.channel) | ((ev->data.control.value & 0x7f) << 8)| (((ev->data.control.value >> 7) & 0x7f) << 16); break; case SND_SEQ_EVENT_SONGPOS: data = 0xF2 | ((ev->data.control.value & 0x7f) << 8)| (((ev->data.control.value >> 7) & 0x7f) << 16); break; case SND_SEQ_EVENT_START: data = 0xFA; break; case SND_SEQ_EVENT_STOP: data = 0xFC; break; case SND_SEQ_EVENT_CONTINUE: data = 0xFB; break; default: break; } // switch (ev)->type if (data) { pthread_mutex_lock(&mutex_midiin); midiData[midiDataCount].data = data; midiData[midiDataCount].port = ev->dest.port; midiDataCount++; pthread_mutex_unlock(&mutex_midiin); } } // handleMIDI // midi thread void *execute(void *param) { int count, ms = 1; snd_seq_event_t *ev = 0; while (!midithread.terminated) { count = snd_seq_event_input_pending(aseq_client, 1); for (int i=0; i<count; i++) { snd_seq_event_input(aseq_client, &ev); if (mdevs[ev->dest.port]->enabled) handleMIDI(ev); } // while input pending usleep(1000 * ms); } // while not terminated } // CMIDIThread :: execute()





