251 lines
5.5 KiB
C++
251 lines
5.5 KiB
C++
/* $Id$ */
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <sys/select.h>
|
|
#include <termios.h>
|
|
#include <jack/jack.h>
|
|
#include <jack/midiport.h>
|
|
#include "jack.h"
|
|
|
|
Jack::Jack()
|
|
{
|
|
m_connected = false;
|
|
m_recording = false;
|
|
m_buffer = new RingBuffer(2048);
|
|
}
|
|
|
|
Jack::~Jack()
|
|
{
|
|
Disconnect();
|
|
delete m_buffer;
|
|
}
|
|
|
|
bool Jack::Connect()
|
|
{
|
|
if (m_connected) return true;
|
|
|
|
jack_status_t status;
|
|
m_client = jack_client_open("mloop", JackNoStartServer, &status);
|
|
if (m_client == NULL) {
|
|
if (status & JackServerFailed) {
|
|
fprintf(stderr, "JACK server not running\n");
|
|
} else {
|
|
fprintf(stderr, "jack_client_open() failed, status = 0x%2.0x\n", status);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
m_connected = true;
|
|
|
|
jack_on_shutdown(m_client, &ShutdownCallbackHandler, this);
|
|
jack_set_process_callback(m_client, &ProcessCallbackHandler, this);
|
|
|
|
m_input = jack_port_register(m_client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
|
|
m_output = jack_port_register(m_client, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
|
|
|
|
m_control = jack_port_register(m_client, "control", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput|JackPortIsTerminal, 0);
|
|
|
|
jack_activate(m_client);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Jack::Disconnect()
|
|
{
|
|
if (!m_connected) return;
|
|
|
|
m_connected = false;
|
|
|
|
jack_deactivate(m_client);
|
|
jack_client_close(m_client);
|
|
}
|
|
|
|
void Jack::ShutdownCallback()
|
|
{
|
|
m_connected = false;
|
|
printf("shutdowncallback\n");
|
|
}
|
|
|
|
int Jack::ProcessCallback(jack_nframes_t nframes)
|
|
{
|
|
void *input = jack_port_get_buffer(m_input, nframes);
|
|
void *output = jack_port_get_buffer(m_output, nframes);
|
|
|
|
void *control = jack_port_get_buffer(m_control, nframes);
|
|
|
|
jack_midi_clear_buffer(output);
|
|
|
|
/* Copy from input to output */
|
|
jack_nframes_t event_count = jack_midi_get_event_count(input);
|
|
jack_nframes_t event_index = 0;
|
|
|
|
jack_midi_event_t ev;
|
|
if (event_index < event_count) {
|
|
jack_midi_event_get(&ev, input, event_index++);
|
|
} else {
|
|
ev.time = UINT_MAX;
|
|
}
|
|
|
|
for (jack_nframes_t frame = 0; frame < nframes; frame++) {
|
|
while (ev.time == frame) {
|
|
jack_midi_event_write(output, ev.time, ev.buffer, ev.size);
|
|
|
|
if (m_recording) {
|
|
/* Don't add the event to the buffer if it will become full.
|
|
* This includes the case where the event would actually fit,
|
|
* but would cause the buffer to be full. This prevents the
|
|
* need for extra logic to determine if the buffer is full
|
|
* or empty.
|
|
*/
|
|
if (m_buffer->Free() > sizeof ev.time + sizeof ev.size + ev.size) {
|
|
m_buffer->Write((uint8_t *)&m_recording_time, sizeof m_recording_time);
|
|
m_buffer->Write((uint8_t *)&ev.time, sizeof ev.time);
|
|
m_buffer->Write((uint8_t *)&ev.size, sizeof ev.size);
|
|
m_buffer->Write((uint8_t *)ev.buffer, ev.size);
|
|
} else {
|
|
printf("Buffer full, dropping input!\n");
|
|
}
|
|
}
|
|
|
|
if (event_index < event_count) {
|
|
jack_midi_event_get(&ev, input, event_index++);
|
|
} else {
|
|
ev.time = UINT_MAX;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < NUM_LOOPS; i++) {
|
|
m_loops[i].PlayFrame(output, frame);
|
|
}
|
|
}
|
|
|
|
if (m_recording) {
|
|
m_recording_time += nframes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct termios orig_termios;
|
|
|
|
void reset_terminal_mode()
|
|
{
|
|
tcsetattr(0, TCSANOW, &orig_termios);
|
|
}
|
|
|
|
void set_conio_terminal_mode()
|
|
{
|
|
struct termios new_termios;
|
|
|
|
/* take two copies - one for now, one for later */
|
|
tcgetattr(0, &orig_termios);
|
|
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
|
|
|
|
/* register cleanup handler, and set the new terminal mode */
|
|
atexit(reset_terminal_mode);
|
|
cfmakeraw(&new_termios);
|
|
tcsetattr(0, TCSANOW, &new_termios);
|
|
}
|
|
|
|
int kbhit()
|
|
{
|
|
struct timeval tv = { 0L, 0L };
|
|
fd_set fds;
|
|
FD_SET(0, &fds);
|
|
return select(1, &fds, NULL, NULL, &tv);
|
|
}
|
|
|
|
int getch()
|
|
{
|
|
int r;
|
|
unsigned char c;
|
|
if ((r = read(0, &c, sizeof(c))) < 0) {
|
|
return r;
|
|
} else {
|
|
return c;
|
|
}
|
|
}
|
|
|
|
void Jack::Run()
|
|
{
|
|
set_conio_terminal_mode();
|
|
|
|
jack_nframes_t recording_time;
|
|
jack_midi_event_t ev;
|
|
ev.time = UINT_MAX;
|
|
|
|
m_recording = false;
|
|
|
|
for (;;) {
|
|
usleep(10000);
|
|
if (ev.time == UINT_MAX) {
|
|
if (m_buffer->Size() >= sizeof recording_time + sizeof ev.time + sizeof ev.size) {
|
|
m_buffer->Read((uint8_t *)&recording_time, sizeof recording_time);
|
|
m_buffer->Read((uint8_t *)&ev.time, sizeof ev.time);
|
|
m_buffer->Read((uint8_t *)&ev.size, sizeof ev.size);
|
|
}
|
|
} else {
|
|
if (m_buffer->Size() >= ev.size) {
|
|
ev.buffer = (jack_midi_data_t *)malloc(ev.size);
|
|
m_buffer->Read((uint8_t *)ev.buffer, ev.size);
|
|
}
|
|
|
|
if (m_recording) {
|
|
printf("Recording event for loop %d\n", m_recording_loop);
|
|
m_loops[m_recording_loop].AddEvent(recording_time, &ev);
|
|
}
|
|
ev.time = UINT_MAX;
|
|
}
|
|
|
|
if (kbhit()) {
|
|
char c = getch();
|
|
|
|
switch (c) {
|
|
case 3:
|
|
case 'q': return;
|
|
|
|
case 'r':
|
|
if (m_recording) {
|
|
m_recording = false;
|
|
m_loops[m_recording_loop].SetLength(m_recording_time);
|
|
printf("Finished recording loop %d\n", m_recording_loop);
|
|
} else {
|
|
m_recording_time = 0;
|
|
m_recording = true;
|
|
printf("Started recording loop %d\n", m_recording_loop);
|
|
}
|
|
break;
|
|
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
if (!m_recording) {
|
|
m_recording_loop = c - '1';
|
|
printf("Selected recording loop %d\n", m_recording_loop);
|
|
}
|
|
break;
|
|
|
|
case 'z':
|
|
case 'x':
|
|
printf("Starting loop %d (%s)\n", m_recording_loop, c == 'x' ? "loop" : "once");
|
|
m_loops[m_recording_loop].Start(c == 'x');
|
|
break;
|
|
|
|
case 'c':
|
|
printf("Stopping loop %d\n", m_recording_loop);
|
|
m_loops[m_recording_loop].Stop();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|