feat: add initial JACK audio looper with MIDI control
Co-authored-by: aider (deepseek/deepseek-reasoner) <aider@aider.chat>
This commit is contained in:
131
src/main.c
131
src/main.c
@@ -0,0 +1,131 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <jack/jack.h>
|
||||
|
||||
typedef enum {
|
||||
STATE_IDLE,
|
||||
STATE_RECORD,
|
||||
STATE_LOOPING,
|
||||
STATE_PAUSED
|
||||
} looper_state;
|
||||
|
||||
static volatile looper_state current_state = STATE_IDLE;
|
||||
|
||||
static jack_port_t *input_port;
|
||||
static jack_port_t *output_port;
|
||||
static jack_port_t *midi_control_port;
|
||||
static jack_port_t *midi_clock_port;
|
||||
|
||||
static jack_client_t *client;
|
||||
|
||||
static int process(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer(input_port, nframes);
|
||||
jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_port, nframes);
|
||||
|
||||
if (in && out) {
|
||||
memcpy(out, in, sizeof(jack_default_audio_sample_t) * nframes);
|
||||
}
|
||||
|
||||
void *midi_ctrl_buf = jack_port_get_buffer(midi_control_port, nframes);
|
||||
if (midi_ctrl_buf) {
|
||||
jack_nframes_t nevents = jack_midi_get_event_count(midi_ctrl_buf);
|
||||
jack_midi_event_t ev;
|
||||
for (jack_nframes_t i = 0; i < nevents; i++) {
|
||||
if (jack_midi_event_get(&ev, midi_ctrl_buf, i) == 0) {
|
||||
// note on with note number 1
|
||||
if ((ev.size >= 3) && ((ev.buffer[0] & 0xf0) == 0x90)) {
|
||||
unsigned char note = ev.buffer[1];
|
||||
if (note == 1) {
|
||||
switch (current_state) {
|
||||
case STATE_IDLE:
|
||||
current_state = STATE_RECORD;
|
||||
fprintf(stderr, "[looper] -> record\n");
|
||||
break;
|
||||
case STATE_RECORD:
|
||||
current_state = STATE_LOOPING;
|
||||
fprintf(stderr, "[looper] -> looping\n");
|
||||
break;
|
||||
case STATE_LOOPING:
|
||||
current_state = STATE_PAUSED;
|
||||
fprintf(stderr, "[looper] -> paused\n");
|
||||
break;
|
||||
case STATE_PAUSED:
|
||||
current_state = STATE_LOOPING;
|
||||
fprintf(stderr, "[looper] -> looping\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// midi clock port is registered but not processed here
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jack_shutdown(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
fprintf(stderr, "JACK shutdown\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *client_name = "looper";
|
||||
jack_options_t options = JackNullOption;
|
||||
jack_status_t status;
|
||||
|
||||
client = jack_client_open(client_name, options, &status);
|
||||
if (client == NULL) {
|
||||
fprintf(stderr, "jack_client_open() failed, status = 0x%2.0x\n", status);
|
||||
if (status & JackServerFailed) {
|
||||
fprintf(stderr, "Unable to connect to JACK server\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (status & JackNameNotUnique) {
|
||||
client_name = jack_get_client_name(client);
|
||||
}
|
||||
|
||||
jack_set_process_callback(client, process, NULL);
|
||||
jack_on_shutdown(client, jack_shutdown, NULL);
|
||||
|
||||
input_port = jack_port_register(client, "input",
|
||||
JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsInput, 0);
|
||||
output_port = jack_port_register(client, "output",
|
||||
JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsOutput, 0);
|
||||
midi_control_port = jack_port_register(client, "control",
|
||||
JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsInput, 0);
|
||||
midi_clock_port = jack_port_register(client, "clock",
|
||||
JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsInput, 0);
|
||||
|
||||
if ((input_port == NULL) || (output_port == NULL) ||
|
||||
(midi_control_port == NULL) || (midi_clock_port == NULL)) {
|
||||
fprintf(stderr, "Could not create ports\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (jack_activate(client)) {
|
||||
fprintf(stderr, "Cannot activate client\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "looper running (client name '%s')\n", client_name);
|
||||
|
||||
while (1) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
jack_client_close(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user