12-command-art #2
BIN
src/channel.o
Normal file
BIN
src/channel.o
Normal file
Binary file not shown.
66
src/looper.c
66
src/looper.c
@@ -1,7 +1,9 @@
|
|||||||
// cppcheck-suppress missingIncludeSystem
|
// cppcheck-suppress missingIncludeSystem
|
||||||
#include "looper.h"
|
#include "looper.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
|
#include "command.h"
|
||||||
#include "midi.h"
|
#include "midi.h"
|
||||||
|
#include "queue.h"
|
||||||
#include <jack/jack.h>
|
#include <jack/jack.h>
|
||||||
#include <jack/midiport.h>
|
#include <jack/midiport.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@@ -9,8 +11,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "command.h"
|
|
||||||
#include "queue.h"
|
|
||||||
|
|
||||||
/* Global state (shared across files) */
|
/* Global state (shared across files) */
|
||||||
struct channel_t channels[MAX_CHANNELS];
|
struct channel_t channels[MAX_CHANNELS];
|
||||||
@@ -30,38 +30,48 @@ static int pending_unregister_idx = -1;
|
|||||||
static int pending_unregister_cycle = 0;
|
static int pending_unregister_cycle = 0;
|
||||||
|
|
||||||
static void apply_command(command_t cmd) {
|
static void apply_command(command_t cmd) {
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case CMD_CYCLE:
|
case CMD_CYCLE:
|
||||||
if (cmd.channel >= 0 && cmd.channel < MAX_CHANNELS) {
|
if (cmd.channel >= 0 && cmd.channel < MAX_CHANNELS) {
|
||||||
int cur = atomic_load(&channels[cmd.channel].state);
|
int cur = atomic_load(&channels[cmd.channel].state);
|
||||||
int next;
|
int next;
|
||||||
switch (cur) {
|
switch (cur) {
|
||||||
case STATE_IDLE: next = STATE_RECORD; break;
|
case STATE_IDLE:
|
||||||
case STATE_RECORD: next = STATE_LOOPING; break;
|
next = STATE_RECORD;
|
||||||
case STATE_LOOPING: next = STATE_PAUSED; break;
|
|
||||||
case STATE_PAUSED: next = STATE_LOOPING; break;
|
|
||||||
default: next = STATE_IDLE; break;
|
|
||||||
}
|
|
||||||
atomic_store(&channels[cmd.channel].state, next);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CMD_STOP:
|
case STATE_RECORD:
|
||||||
if (cmd.channel >= 0 && cmd.channel < MAX_CHANNELS)
|
next = STATE_LOOPING;
|
||||||
atomic_store(&channels[cmd.channel].state, STATE_IDLE);
|
|
||||||
else {
|
|
||||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
|
||||||
atomic_store(&channels[i].state, STATE_IDLE);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CMD_BIND_CHANNEL:
|
case STATE_LOOPING:
|
||||||
atomic_store(&bind_channel, cmd.data);
|
next = STATE_PAUSED;
|
||||||
break;
|
break;
|
||||||
case CMD_UNBIND:
|
case STATE_PAUSED:
|
||||||
atomic_store(&bind_channel, 0);
|
next = STATE_LOOPING;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
next = STATE_IDLE;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
atomic_store(&channels[cmd.channel].state, next);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case CMD_STOP:
|
||||||
|
if (cmd.channel >= 0 && cmd.channel < MAX_CHANNELS)
|
||||||
|
atomic_store(&channels[cmd.channel].state, STATE_IDLE);
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||||
|
atomic_store(&channels[i].state, STATE_IDLE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CMD_BIND_CHANNEL:
|
||||||
|
atomic_store(&bind_channel, cmd.data);
|
||||||
|
break;
|
||||||
|
case CMD_UNBIND:
|
||||||
|
atomic_store(&bind_channel, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
|||||||
BIN
src/looper.o
Normal file
BIN
src/looper.o
Normal file
Binary file not shown.
BIN
src/main.o
Normal file
BIN
src/main.o
Normal file
Binary file not shown.
50
src/midi.c
50
src/midi.c
@@ -36,36 +36,34 @@ void midi_handle_events(void *port_buffer, jack_nframes_t nframes) {
|
|||||||
if (ck) {
|
if (ck) {
|
||||||
atomic_store(&control_key_active, 0);
|
atomic_store(&control_key_active, 0);
|
||||||
if (note < 16) {
|
if (note < 16) {
|
||||||
command_t cmd = { .type = CMD_BIND_CHANNEL, .channel = -1, .data = note };
|
command_t cmd = {
|
||||||
|
.type = CMD_BIND_CHANNEL, .channel = -1, .data = note};
|
||||||
queue_push(&cmd_queue, cmd);
|
queue_push(&cmd_queue, cmd);
|
||||||
} else {
|
} else {
|
||||||
switch (note) {
|
switch (note) {
|
||||||
case 60:
|
case 60: {
|
||||||
{
|
command_t cmd = {
|
||||||
command_t cmd = { .type = CMD_ADD_CHANNEL, .channel = -1, .data = 0 };
|
.type = CMD_ADD_CHANNEL, .channel = -1, .data = 0};
|
||||||
queue_push(&cmd_queue_main_midi, cmd);
|
queue_push(&cmd_queue_main_midi, cmd);
|
||||||
} break;
|
} break;
|
||||||
case 61:
|
case 61: {
|
||||||
{
|
command_t cmd = {
|
||||||
command_t cmd = { .type = CMD_REMOVE_CHANNEL, .channel = -1, .data = 0 };
|
.type = CMD_REMOVE_CHANNEL, .channel = -1, .data = 0};
|
||||||
queue_push(&cmd_queue_main_midi, cmd);
|
queue_push(&cmd_queue_main_midi, cmd);
|
||||||
} break;
|
} break;
|
||||||
case 62:
|
case 62: {
|
||||||
{
|
|
||||||
int bch = atomic_load(&bind_channel);
|
int bch = atomic_load(&bind_channel);
|
||||||
if (bch >= 0 && bch < MAX_CHANNELS) {
|
if (bch >= 0 && bch < MAX_CHANNELS) {
|
||||||
command_t cmd = { .type = CMD_CYCLE, .channel = bch, .data = 0 };
|
command_t cmd = {.type = CMD_CYCLE, .channel = bch, .data = 0};
|
||||||
queue_push(&cmd_queue, cmd);
|
queue_push(&cmd_queue, cmd);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case 63:
|
case 63: {
|
||||||
{
|
command_t cmd = {.type = CMD_UNBIND, .channel = -1, .data = 0};
|
||||||
command_t cmd = { .type = CMD_UNBIND, .channel = -1, .data = 0 };
|
|
||||||
queue_push(&cmd_queue, cmd);
|
queue_push(&cmd_queue, cmd);
|
||||||
} break;
|
} break;
|
||||||
case 65:
|
case 65: {
|
||||||
{
|
command_t cmd = {.type = CMD_STOP, .channel = -1, .data = 0};
|
||||||
command_t cmd = { .type = CMD_STOP, .channel = -1, .data = 0 };
|
|
||||||
queue_push(&cmd_queue, cmd);
|
queue_push(&cmd_queue, cmd);
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
@@ -75,19 +73,17 @@ void midi_handle_events(void *port_buffer, jack_nframes_t nframes) {
|
|||||||
} else {
|
} else {
|
||||||
/* direct mapping */
|
/* direct mapping */
|
||||||
switch (note) {
|
switch (note) {
|
||||||
case 1:
|
case 1: {
|
||||||
{
|
command_t cmd = {.type = CMD_CYCLE, .channel = 0, .data = 0};
|
||||||
command_t cmd = { .type = CMD_CYCLE, .channel = 0, .data = 0 };
|
queue_push(&cmd_queue, cmd);
|
||||||
queue_push(&cmd_queue, cmd);
|
} break;
|
||||||
} break;
|
case 60: {
|
||||||
case 60:
|
command_t cmd = {.type = CMD_ADD_CHANNEL, .channel = -1, .data = 0};
|
||||||
{
|
|
||||||
command_t cmd = { .type = CMD_ADD_CHANNEL, .channel = -1, .data = 0 };
|
|
||||||
queue_push(&cmd_queue_main_midi, cmd);
|
queue_push(&cmd_queue_main_midi, cmd);
|
||||||
} break;
|
} break;
|
||||||
case 61:
|
case 61: {
|
||||||
{
|
command_t cmd = {
|
||||||
command_t cmd = { .type = CMD_REMOVE_CHANNEL, .channel = -1, .data = 0 };
|
.type = CMD_REMOVE_CHANNEL, .channel = -1, .data = 0};
|
||||||
queue_push(&cmd_queue_main_midi, cmd);
|
queue_push(&cmd_queue_main_midi, cmd);
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
BIN
src/midi.o
Normal file
BIN
src/midi.o
Normal file
Binary file not shown.
108
src/pipe.c
108
src/pipe.c
@@ -1,14 +1,14 @@
|
|||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "queue.h"
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#define FIFO_PATH "/tmp/looper_cmd"
|
#define FIFO_PATH "/tmp/looper_cmd"
|
||||||
#define LINE_MAX 256
|
#define LINE_MAX 256
|
||||||
@@ -18,57 +18,57 @@ extern spsc_queue_t cmd_queue;
|
|||||||
extern spsc_queue_t cmd_queue_main_fifo;
|
extern spsc_queue_t cmd_queue_main_fifo;
|
||||||
|
|
||||||
static void *pipe_thread_func(void *arg) {
|
static void *pipe_thread_func(void *arg) {
|
||||||
(void)arg;
|
(void)arg;
|
||||||
FILE *fifo = fopen(FIFO_PATH, "r");
|
FILE *fifo = fopen(FIFO_PATH, "r");
|
||||||
if (!fifo) {
|
if (!fifo) {
|
||||||
perror("fopen fifo");
|
perror("fopen fifo");
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
char line[LINE_MAX];
|
|
||||||
while (fgets(line, sizeof(line), fifo)) {
|
|
||||||
/* strip newline */
|
|
||||||
size_t len = strlen(line);
|
|
||||||
if (len > 0 && line[len-1] == '\n')
|
|
||||||
line[len-1] = '\0';
|
|
||||||
|
|
||||||
if (strcmp(line, "add") == 0) {
|
|
||||||
command_t cmd = { .type = CMD_ADD_CHANNEL, .channel = -1, .data = 0 };
|
|
||||||
queue_push(&cmd_queue_main_fifo, cmd);
|
|
||||||
} else if (strcmp(line, "remove") == 0) {
|
|
||||||
command_t cmd = { .type = CMD_REMOVE_CHANNEL, .channel = -1, .data = 0 };
|
|
||||||
queue_push(&cmd_queue_main_fifo, cmd);
|
|
||||||
} else if (strncmp(line, "record ", 7) == 0) {
|
|
||||||
int ch = atoi(line + 7);
|
|
||||||
command_t cmd = { .type = CMD_CYCLE, .channel = ch, .data = 0 };
|
|
||||||
queue_push(&cmd_queue, cmd);
|
|
||||||
} else if (strcmp(line, "stop") == 0) {
|
|
||||||
command_t cmd = { .type = CMD_STOP, .channel = -1, .data = 0 };
|
|
||||||
queue_push(&cmd_queue, cmd);
|
|
||||||
} else if (strncmp(line, "bind ", 5) == 0) {
|
|
||||||
int ch = atoi(line + 5);
|
|
||||||
command_t cmd = { .type = CMD_BIND_CHANNEL, .channel = -1, .data = ch };
|
|
||||||
queue_push(&cmd_queue, cmd);
|
|
||||||
} else if (strcmp(line, "unbind") == 0) {
|
|
||||||
command_t cmd = { .type = CMD_UNBIND, .channel = -1, .data = 0 };
|
|
||||||
queue_push(&cmd_queue, cmd);
|
|
||||||
}
|
|
||||||
/* ignore unknown lines */
|
|
||||||
}
|
|
||||||
fclose(fifo);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
char line[LINE_MAX];
|
||||||
|
while (fgets(line, sizeof(line), fifo)) {
|
||||||
|
/* strip newline */
|
||||||
|
size_t len = strlen(line);
|
||||||
|
if (len > 0 && line[len - 1] == '\n')
|
||||||
|
line[len - 1] = '\0';
|
||||||
|
|
||||||
|
if (strcmp(line, "add") == 0) {
|
||||||
|
command_t cmd = {.type = CMD_ADD_CHANNEL, .channel = -1, .data = 0};
|
||||||
|
queue_push(&cmd_queue_main_fifo, cmd);
|
||||||
|
} else if (strcmp(line, "remove") == 0) {
|
||||||
|
command_t cmd = {.type = CMD_REMOVE_CHANNEL, .channel = -1, .data = 0};
|
||||||
|
queue_push(&cmd_queue_main_fifo, cmd);
|
||||||
|
} else if (strncmp(line, "record ", 7) == 0) {
|
||||||
|
int ch = atoi(line + 7);
|
||||||
|
command_t cmd = {.type = CMD_CYCLE, .channel = ch, .data = 0};
|
||||||
|
queue_push(&cmd_queue, cmd);
|
||||||
|
} else if (strcmp(line, "stop") == 0) {
|
||||||
|
command_t cmd = {.type = CMD_STOP, .channel = -1, .data = 0};
|
||||||
|
queue_push(&cmd_queue, cmd);
|
||||||
|
} else if (strncmp(line, "bind ", 5) == 0) {
|
||||||
|
int ch = atoi(line + 5);
|
||||||
|
command_t cmd = {.type = CMD_BIND_CHANNEL, .channel = -1, .data = ch};
|
||||||
|
queue_push(&cmd_queue, cmd);
|
||||||
|
} else if (strcmp(line, "unbind") == 0) {
|
||||||
|
command_t cmd = {.type = CMD_UNBIND, .channel = -1, .data = 0};
|
||||||
|
queue_push(&cmd_queue, cmd);
|
||||||
|
}
|
||||||
|
/* ignore unknown lines */
|
||||||
|
}
|
||||||
|
fclose(fifo);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pipe_start_reader(void) {
|
int pipe_start_reader(void) {
|
||||||
/* create FIFO if it doesn't exist */
|
/* create FIFO if it doesn't exist */
|
||||||
if (mkfifo(FIFO_PATH, 0666) != 0 && errno != EEXIST) {
|
if (mkfifo(FIFO_PATH, 0666) != 0 && errno != EEXIST) {
|
||||||
perror("mkfifo");
|
perror("mkfifo");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
if (pthread_create(&tid, NULL, pipe_thread_func, NULL) != 0) {
|
if (pthread_create(&tid, NULL, pipe_thread_func, NULL) != 0) {
|
||||||
perror("pthread_create");
|
perror("pthread_create");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pthread_detach(tid); /* we don't need to join */
|
pthread_detach(tid); /* we don't need to join */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/pipe.o
Normal file
BIN
src/pipe.o
Normal file
Binary file not shown.
37
src/queue.c
37
src/queue.c
@@ -3,28 +3,29 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
void queue_init(spsc_queue_t *q) {
|
void queue_init(spsc_queue_t *q) {
|
||||||
/* nothing to allocate, just ensure head/tail start at 0 */
|
/* nothing to allocate, just ensure head/tail start at 0 */
|
||||||
q->head = 0;
|
q->head = 0;
|
||||||
q->tail = 0;
|
q->tail = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool queue_push(spsc_queue_t *q, command_t cmd) {
|
bool queue_push(spsc_queue_t *q, command_t cmd) {
|
||||||
int h = atomic_load_explicit(&q->head, memory_order_relaxed);
|
int h = atomic_load_explicit(&q->head, memory_order_relaxed);
|
||||||
int t = atomic_load_explicit(&q->tail, memory_order_acquire);
|
int t = atomic_load_explicit(&q->tail, memory_order_acquire);
|
||||||
int next = (h + 1) % QUEUE_CAPACITY;
|
int next = (h + 1) % QUEUE_CAPACITY;
|
||||||
if (next == t)
|
if (next == t)
|
||||||
return false; /* queue full */
|
return false; /* queue full */
|
||||||
q->buffer[h] = cmd;
|
q->buffer[h] = cmd;
|
||||||
atomic_store_explicit(&q->head, next, memory_order_release);
|
atomic_store_explicit(&q->head, next, memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool queue_pop(spsc_queue_t *q, command_t *cmd) {
|
bool queue_pop(spsc_queue_t *q, command_t *cmd) {
|
||||||
int t = atomic_load_explicit(&q->tail, memory_order_relaxed);
|
int t = atomic_load_explicit(&q->tail, memory_order_relaxed);
|
||||||
int h = atomic_load_explicit(&q->head, memory_order_acquire);
|
int h = atomic_load_explicit(&q->head, memory_order_acquire);
|
||||||
if (t == h)
|
if (t == h)
|
||||||
return false; /* queue empty */
|
return false; /* queue empty */
|
||||||
*cmd = q->buffer[t];
|
*cmd = q->buffer[t];
|
||||||
atomic_store_explicit(&q->tail, (t + 1) % QUEUE_CAPACITY, memory_order_release);
|
atomic_store_explicit(&q->tail, (t + 1) % QUEUE_CAPACITY,
|
||||||
return true;
|
memory_order_release);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/queue.o
Normal file
BIN
src/queue.o
Normal file
Binary file not shown.
Reference in New Issue
Block a user