218 lines
7.1 KiB
C
218 lines
7.1 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <jack/jack.h>
|
|
#include <jack/midiport.h>
|
|
#include <stdint.h>
|
|
#include <sys/ioctl.h>
|
|
#include <signal.h>
|
|
#include "tascam_common.h"
|
|
|
|
#define DEVICE_NODE "/dev/tascam_raw"
|
|
#define MIDI_NODE "/dev/tascam_midi"
|
|
#define CHANNELS 4
|
|
#define BYTES_PER_SAMPLE 3
|
|
#define S24_MAX 8388607.0
|
|
#define RAW_BLOCK_SIZE 512
|
|
#define FRAMES_PER_BLOCK 8
|
|
|
|
int fd, midi_fd;
|
|
volatile sig_atomic_t keep_running = 1;
|
|
|
|
jack_client_t *client_pb;
|
|
jack_client_t *client_cap;
|
|
jack_client_t *client_midi;
|
|
|
|
jack_port_t *pb_ports[CHANNELS];
|
|
jack_port_t *cap_ports[CHANNELS];
|
|
jack_port_t *midi_in_port;
|
|
jack_port_t *midi_out_port;
|
|
|
|
const char *pb_port_names[] = {
|
|
"Analog Left", "Analog Right",
|
|
"Digital Left", "Digital Right"
|
|
};
|
|
|
|
const char *cap_port_names[] = {
|
|
"Analog Left", "Analog Right",
|
|
"Digital Left", "Digital Right"
|
|
};
|
|
|
|
void signal_handler(int sig) {
|
|
keep_running = 0;
|
|
}
|
|
|
|
void decode_block(const uint8_t *src, float **dst_ch, int offset) {
|
|
int frame, bit;
|
|
for (frame = 0; frame < FRAMES_PER_BLOCK; ++frame) {
|
|
const uint8_t *p_src = src + frame * 64;
|
|
int32_t ch[4] = {0};
|
|
for (bit = 0; bit < 24; ++bit) {
|
|
uint8_t byte1 = p_src[bit];
|
|
ch[0] = (ch[0] << 1) | (byte1 & 1);
|
|
ch[2] = (ch[2] << 1) | ((byte1 >> 1) & 1);
|
|
}
|
|
for (bit = 0; bit < 24; ++bit) {
|
|
uint8_t byte2 = p_src[bit + 32];
|
|
ch[1] = (ch[1] << 1) | (byte2 & 1);
|
|
ch[3] = (ch[3] << 1) | ((byte2 >> 1) & 1);
|
|
}
|
|
for(int c=0; c<4; c++) {
|
|
int32_t val = ch[c];
|
|
if (val & 0x800000) val |= 0xFF000000;
|
|
dst_ch[c][offset + frame] = (float)val / S24_MAX;
|
|
}
|
|
}
|
|
}
|
|
|
|
int process_pb(jack_nframes_t nframes, void *arg) {
|
|
jack_default_audio_sample_t *pb_in[CHANNELS];
|
|
|
|
for (int i = 0; i < CHANNELS; i++) {
|
|
pb_in[i] = (jack_default_audio_sample_t *)jack_port_get_buffer(pb_ports[i], nframes);
|
|
}
|
|
|
|
size_t pb_bytes = nframes * CHANNELS * BYTES_PER_SAMPLE;
|
|
unsigned char *pb_buf = malloc(pb_bytes);
|
|
if (!pb_buf) return 0;
|
|
|
|
unsigned char *ptr = pb_buf;
|
|
for (int i = 0; i < nframes; i++) {
|
|
for (int c = 0; c < CHANNELS; c++) {
|
|
float s = pb_in[c][i];
|
|
if (s > 1.0f) s = 1.0f; else if (s < -1.0f) s = -1.0f;
|
|
int32_t val = (int32_t)(s * S24_MAX);
|
|
*ptr++ = (val >> 0) & 0xFF;
|
|
*ptr++ = (val >> 8) & 0xFF;
|
|
*ptr++ = (val >> 16) & 0xFF;
|
|
}
|
|
}
|
|
|
|
if (write(fd, pb_buf, pb_bytes)) {};
|
|
free(pb_buf);
|
|
return 0;
|
|
}
|
|
|
|
int process_cap(jack_nframes_t nframes, void *arg) {
|
|
jack_default_audio_sample_t *cap_out[CHANNELS];
|
|
for (int i = 0; i < CHANNELS; i++) {
|
|
cap_out[i] = (jack_default_audio_sample_t *)jack_port_get_buffer(cap_ports[i], nframes);
|
|
}
|
|
|
|
int blocks_needed = nframes / FRAMES_PER_BLOCK;
|
|
if (nframes % FRAMES_PER_BLOCK != 0) blocks_needed++;
|
|
|
|
size_t raw_bytes = blocks_needed * RAW_BLOCK_SIZE;
|
|
unsigned char *raw_buf = malloc(raw_bytes);
|
|
if (!raw_buf) return 0;
|
|
|
|
ssize_t read_len = read(fd, raw_buf, raw_bytes);
|
|
|
|
float *dst_ptrs[4] = { cap_out[0], cap_out[1], cap_out[2], cap_out[3] };
|
|
if (read_len > 0) {
|
|
int blocks_read = read_len / RAW_BLOCK_SIZE;
|
|
for(int b=0; b<blocks_read; b++) {
|
|
if ((b * FRAMES_PER_BLOCK) < nframes) {
|
|
decode_block(raw_buf + (b * RAW_BLOCK_SIZE), dst_ptrs, b * FRAMES_PER_BLOCK);
|
|
}
|
|
}
|
|
} else {
|
|
for(int c=0; c<4; c++) memset(cap_out[c], 0, nframes * sizeof(float));
|
|
}
|
|
free(raw_buf);
|
|
return 0;
|
|
}
|
|
|
|
int process_midi(jack_nframes_t nframes, void *arg) {
|
|
void *midi_in_buf = jack_port_get_buffer(midi_in_port, nframes);
|
|
void *midi_out_buf = jack_port_get_buffer(midi_out_port, nframes);
|
|
jack_midi_clear_buffer(midi_out_buf);
|
|
|
|
jack_nframes_t event_count = jack_midi_get_event_count(midi_in_buf);
|
|
jack_midi_event_t event;
|
|
for(int i=0; i<event_count; i++) {
|
|
jack_midi_event_get(&event, midi_in_buf, i);
|
|
if (event.size > 8) continue;
|
|
uint8_t packet[9];
|
|
memcpy(packet, event.buffer, event.size);
|
|
if (event.size < 8) memset(packet + event.size, 0xFD, 8 - event.size);
|
|
packet[8] = 0xE0;
|
|
if (write(midi_fd, packet, 9)) {};
|
|
}
|
|
|
|
uint8_t raw_midi[9 * 16];
|
|
ssize_t m_read = read(midi_fd, raw_midi, sizeof(raw_midi));
|
|
if (m_read > 0) {
|
|
int packets = m_read / 9;
|
|
for(int p=0; p<packets; p++) {
|
|
uint8_t *pkt = raw_midi + (p*9);
|
|
int len = 0;
|
|
while(len < 8 && pkt[len] != 0xFD) len++;
|
|
if (len > 0) jack_midi_event_write(midi_out_buf, 0, pkt, len);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main() {
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
|
|
fd = open(DEVICE_NODE, O_RDWR);
|
|
if (fd < 0) { fprintf(stderr, "Could not open %s\n", DEVICE_NODE); return 1; }
|
|
|
|
midi_fd = open(MIDI_NODE, O_RDWR | O_NONBLOCK);
|
|
if (midi_fd < 0) { fprintf(stderr, "Could not open %s\n", MIDI_NODE); return 1; }
|
|
|
|
client_pb = jack_client_open("TASCAM Output", JackNullOption, NULL);
|
|
if (!client_pb) { fprintf(stderr, "Failed to create Playback client\n"); return 1; }
|
|
|
|
int rate = jack_get_sample_rate(client_pb);
|
|
printf("JACK Sample Rate: %d Hz\n", rate);
|
|
if (ioctl(fd, TASCAM_IOC_SET_RATE, &rate) < 0) { perror("IOCTL failed"); return 1; }
|
|
|
|
jack_set_process_callback(client_pb, process_pb, NULL);
|
|
for (int i = 0; i < CHANNELS; i++) {
|
|
pb_ports[i] = jack_port_register(client_pb, pb_port_names[i], JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
|
|
}
|
|
|
|
client_cap = jack_client_open("TASCAM Input", JackNullOption, NULL);
|
|
if (!client_cap) { fprintf(stderr, "Failed to create Capture client\n"); return 1; }
|
|
|
|
jack_set_process_callback(client_cap, process_cap, NULL);
|
|
for (int i = 0; i < CHANNELS; i++) {
|
|
cap_ports[i] = jack_port_register(client_cap, cap_port_names[i], JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
|
|
}
|
|
|
|
client_midi = jack_client_open("TASCAM MIDI", JackNullOption, NULL);
|
|
if (!client_midi) { fprintf(stderr, "Failed to create MIDI client\n"); return 1; }
|
|
|
|
jack_set_process_callback(client_midi, process_midi, NULL);
|
|
midi_in_port = jack_port_register(client_midi, "MIDI OUT", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
|
|
midi_out_port = jack_port_register(client_midi, "MIDI IN", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
|
|
|
|
if (jack_activate(client_pb)) return 1;
|
|
if (jack_activate(client_cap)) return 1;
|
|
if (jack_activate(client_midi)) return 1;
|
|
|
|
printf("TASCAM Raw JACK Clients Running.\n");
|
|
printf(" [1] TASCAM Output (Playback)\n");
|
|
printf(" [2] TASCAM Input (Capture)\n");
|
|
printf(" [3] TASCAM MIDI (MIDI)\n");
|
|
printf("Running in background. Send SIGINT (Ctrl+C) to stop.\n");
|
|
|
|
while(keep_running) {
|
|
sleep(1);
|
|
}
|
|
|
|
printf("\nStopping...\n");
|
|
jack_client_close(client_pb);
|
|
jack_client_close(client_cap);
|
|
jack_client_close(client_midi);
|
|
close(fd);
|
|
close(midi_fd);
|
|
return 0;
|
|
}
|