mloop/src/jack.cpp

230 lines
4.6 KiB
C++

/* $Id$ */
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <jack/jack.h>
#include <jack/midiport.h>
#include "jack.h"
Jack::Jack()
{
m_connected = false;
m_recording = false;
m_loop_buffer = new RingBuffer(2048);
m_notecache.Reset();
m_client_name = "mloop";
}
Jack::~Jack()
{
Disconnect();
delete m_loop_buffer;
}
bool Jack::Connect()
{
if (m_connected) return true;
jack_status_t status;
m_client = jack_client_open(m_client_name, 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_client_name = jack_get_client_name(m_client);
m_sample_rate = jack_get_sample_rate(m_client);
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);
m_connected = true;
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);
m_notecache.HandleEvent(ev);
if (m_recording) {
if (m_delay_record) m_recording_time = -ev.time;
ev.time += m_recording_time;
if (m_loop_buffer->PushEvent(ev)) {
m_delay_record = false;
} else {
fprintf(stderr, "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_delay_record) {
m_recording_time += nframes;
m_loops[m_recording_loop].SetLength(m_recording_time);
}
return 0;
}
void Jack::ToggleRecording(int loop, int bpm, bool delay)
{
if (m_recording) {
m_recording = false;
if (bpm > 0) {
jack_nframes_t chunk = 60 * m_sample_rate / bpm;
m_recording_time -= m_recording_time % chunk;
}
m_loops[m_recording_loop].SetLength(m_recording_time);
m_loops[m_recording_loop].SetState(LS_IDLE);
m_loops[m_recording_loop].Finalise();
} else {
if (m_loops[loop].State() == LS_IDLE) {
m_recording_loop = loop;
m_loops[m_recording_loop].SetState(LS_RECORDING);
m_loops[m_recording_loop].StartFromNoteCache(m_notecache);
m_recording_time = 0;
m_recording = true;
m_delay_record = delay;
}
}
}
void Jack::StartLoop(int loop, bool repeat, bool sync)
{
m_loops[loop].Start(repeat, sync);
}
void Jack::StopLoop(int loop)
{
m_loops[loop].Stop();
}
void Jack::EraseLoop(int loop)
{
m_loops[loop].Empty();
}
bool Jack::Run()
{
jack_midi_event_t ev;
if (m_loop_buffer->PopEvent(ev)) {
if (m_recording) {
m_loops[m_recording_loop].AddEvent(&ev);
} else {
free(ev.buffer);
}
}
return false;
}
void Jack::StopAll()
{
for (int i = 0; i < NUM_LOOPS; i++) {
StopLoop(i);
}
}
void Jack::EraseAll()
{
for (int i = 0; i < NUM_LOOPS; i++) {
EraseLoop(i);
}
}
void Jack::Save() const
{
FILE *f = fopen("test.txt", "w");
fprintf(f, "sample_rate:%d bpm:%u\n", m_sample_rate);
for (int i = 0; i < NUM_LOOPS; i++) {
fprintf(f, "loop:%d\n", i);
m_loops[i].Save(f);
}
fclose(f);
}
void Jack::Load()
{
StopAll();
EraseAll();
int ret;
FILE *f = fopen("test.txt", "r");
int sample_rate;
ret = fscanf(f, "sample_rate:%d\n", &sample_rate);
if (ret != 1) return;
while (!feof(f)) {
int i;
ret = fscanf(f, "loop:%d\n", &i);
if (ret != 1) return;
m_loops[i].Load(f, sample_rate, m_sample_rate);
}
}