-Initial import

git-svn-id: http://svn.fuzzle.org/mloop/mloop/trunk@1 ba049829-c6ef-42ef-81ac-908dd8d2e907
remotes/git-svn@10
petern 2009-07-21 20:23:12 +00:00
commit 90e082d82b
9 changed files with 538 additions and 0 deletions

250
src/jack.cpp 100644
View File

@ -0,0 +1,250 @@
/* $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;
}
}
}
}

48
src/jack.h 100644
View File

@ -0,0 +1,48 @@
/* $Id$ */
#ifndef JACK_H
#define JACK_H
#include <jack/jack.h>
#include "loop.h"
#include "ringbuffer.h"
#define NUM_LOOPS 9
class Jack {
private:
bool m_connected;
jack_client_t *m_client;
jack_port_t *m_input;
jack_port_t *m_output;
jack_port_t *m_control;
Loop m_loops[NUM_LOOPS];
RingBuffer *m_buffer;
bool m_recording;
int m_recording_loop;
jack_nframes_t m_recording_time;
static void ShutdownCallbackHandler(void *arg)
{
((Jack *)arg)->ShutdownCallback();
}
static int ProcessCallbackHandler(jack_nframes_t nframes, void *arg)
{
return ((Jack *)arg)->ProcessCallback(nframes);
}
void ShutdownCallback();
int ProcessCallback(jack_nframes_t nframes);
public:
Jack();
~Jack();
bool Connect();
void Disconnect();
void Run();
};
#endif /* JACK_H */

83
src/loop.cpp 100644
View File

@ -0,0 +1,83 @@
/* $Id$ */
#include <stdio.h>
#include "loop.h"
void Loop::PlayFrame(void *port_buffer, jack_nframes_t frame)
{
if (m_state == LS_IDLE) return;
if (m_state == LS_STOPPING) {
printf("Stopping, so send all notes off!\n");
uint8_t buffer[3];
buffer[1] = 0x78;
buffer[2] = 0;
for (int i = 0; i < 16; i++) {
buffer[0] = 0xB0 + i;
jack_midi_event_write(port_buffer, frame, buffer, sizeof buffer);
}
m_state = LS_IDLE;
return;
}
for (; m_iterator != m_events.end(); ++m_iterator) {
jack_midi_event_t &event = (*m_iterator).first;
jack_nframes_t position = (*m_iterator).second;
if (event.time + position > m_position) break;
jack_midi_event_write(port_buffer, event.time, event.buffer, event.size);
}
m_position++;
if (m_position == m_length) {
if (m_state == LS_PLAY_ONCE) {
m_state = LS_IDLE;
}
printf("Completed %u frames\n", m_position);
m_position = 0;
m_iterator = m_events.begin();
}
}
void Loop::AddEvent(jack_nframes_t position, jack_midi_event_t *event)
{
Event e;
e.first = *event;
e.second = position;
m_events.push_back(e);
}
void Loop::SetLength(jack_nframes_t length)
{
if (m_state != LS_IDLE) return;
m_length = length;
}
void Loop::Start(bool loop)
{
if (m_state != LS_IDLE) return;
m_position = 0;
m_iterator = m_events.begin();
m_state = loop ? LS_PLAY_LOOP : LS_PLAY_ONCE;
}
void Loop::Stop()
{
m_state = LS_STOPPING;
}
/*
void Loop::Reset()
{
m_state = LS_IDLE;
m_position = 0;
}
*/

39
src/loop.h 100644
View File

@ -0,0 +1,39 @@
/* $Id$ */
#ifndef LOOP_H
#define LOOP_H
#include <stdint.h>
#include <list>
#include <jack/jack.h>
#include <jack/midiport.h>
enum LoopState {
LS_IDLE,
LS_PLAY_LOOP,
LS_PLAY_ONCE,
LS_STOPPING,
};
typedef std::pair<jack_midi_event_t, jack_nframes_t> Event;
typedef std::list<Event> EventList;
class Loop {
private:
jack_nframes_t m_length; ///< Length of loop, in samples.
jack_nframes_t m_position; ///< Current position of loop, in samples.
LoopState m_state;
EventList m_events;
EventList::iterator m_iterator;
public:
void PlayFrame(void *port_buffer, jack_nframes_t frame);
void AddEvent(jack_nframes_t position, jack_midi_event_t *event);
void SetLength(jack_nframes_t length);
void Start(bool loop);
void Stop();
};
#endif /* LOOP_H */

15
src/mloop.cpp 100644
View File

@ -0,0 +1,15 @@
/* $Id$ */
#include "jack.h"
int main(int argc, char **argv)
{
Jack *j = new Jack();
j->Connect();
j->Run();
delete j;
return 0;
}

61
src/ringbuffer.h 100644
View File

@ -0,0 +1,61 @@
/* $Id$ */
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
class RingBuffer {
private:
uint8_t *m_buffer;
uint8_t *m_end;
uint8_t *m_write;
uint8_t *m_read;
public:
RingBuffer(size_t length)
{
m_buffer = (uint8_t *)malloc(length);
m_end = m_buffer + length;
m_write = m_buffer;
m_read = m_buffer;
}
~RingBuffer()
{
free(m_buffer);
}
void Write(uint8_t *buffer, size_t length)
{
while (length--) {
if (m_write == m_end) m_write = m_buffer;
*m_write++ = *buffer++;
}
}
void Read(uint8_t *buffer, size_t length)
{
while (length--) {
if (m_read == m_end) m_read = m_buffer;
*buffer++ = *m_read++;
}
}
size_t Size() const
{
if (m_write >= m_read) return m_write - m_read;
return (m_end - m_read) + (m_write - m_buffer);
}
size_t Free() const
{
return (m_end - m_buffer) - Size();
}
void Reset()
{
m_write = m_buffer;
m_read = m_buffer;
}
};
#endif /* RINGBUFFER_H */

12
src/wscript 100644
View File

@ -0,0 +1,12 @@
#! /usr/bin/env python
# encoding: utf-8
def configure(conf):
conf.check_cfg(package='jack', uselib_store='JACK', args='--cflags --libs', atleast_version='1.9.2')
def build(bld):
bld.new_task_gen(
features = 'cxx cprogram',
source = 'jack.cpp loop.cpp mloop.cpp',
target = 'mloop',
uselib = 'JACK')

BIN
waf vendored 100755

Binary file not shown.

30
wscript 100644
View File

@ -0,0 +1,30 @@
#! /usr/bin/env python
# encoding: utf-8
VERSION='0.0.1'
APPNAME='mloop'
srcdir = '.'
blddir = 'build'
def init():
pass
def set_options(opt):
opt.tool_options('compiler_cxx')
def configure(conf):
conf.check_tool('compiler_cxx')
conf.sub_config('src')
env = conf.env.copy()
env.set_variant('debug')
conf.set_env_name('debug', env)
conf.setenv('debug')
conf.env.CXXFLAGS = ['-Wall', '-g']
def build(bld):
bld.add_subdirs('src')
for obj in bld.all_task_gen[:]:
new_obj = obj.clone('debug')