Commit 4bd1bef2 authored by Devin Anderson's avatar Devin Anderson
Browse files

Added MIDI queues, FFADO objects, etc. - see...

Added MIDI queues, FFADO objects, etc. - see 'http://trac.jackaudio.org/ticket/187' for more details
parent e97dcccf
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <new>
#include "JackMidiAsyncQueue.h"
using Jack::JackMidiAsyncQueue;
JackMidiAsyncQueue::JackMidiAsyncQueue(size_t max_bytes, size_t max_messages)
{
data_buffer = new jack_midi_data_t[max_bytes];
byte_ring = jack_ringbuffer_create(max_bytes + 1);
if (byte_ring) {
info_ring = jack_ringbuffer_create((max_messages * INFO_SIZE) + 1);
if (info_ring) {
jack_ringbuffer_mlock(byte_ring);
jack_ringbuffer_mlock(info_ring);
advance_space = 0;
this->max_bytes = max_bytes;
return;
}
jack_ringbuffer_free(byte_ring);
}
delete data_buffer;
throw std::bad_alloc();
}
JackMidiAsyncQueue::~JackMidiAsyncQueue()
{
jack_ringbuffer_free(byte_ring);
jack_ringbuffer_free(info_ring);
delete[] data_buffer;
}
jack_midi_event_t *
JackMidiAsyncQueue::DequeueEvent()
{
jack_midi_event_t *event = 0;
if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) {
if (advance_space) {
jack_ringbuffer_read_advance(byte_ring, advance_space);
}
event = &dequeue_event;
jack_ringbuffer_read(info_ring, (char *) &(event->time),
sizeof(jack_nframes_t));
size_t size;
jack_ringbuffer_read(info_ring, (char *) &size, sizeof(size_t));
event->size = size;
jack_ringbuffer_data_t vector[2];
jack_ringbuffer_get_read_vector(byte_ring, vector);
size_t size1 = vector[0].len;
if (size1 >= size) {
event->buffer = (jack_midi_data_t *) vector[0].buf;
} else {
event->buffer = data_buffer;
memcpy(data_buffer, vector[0].buf, size1);
memcpy(data_buffer + size1, vector[1].buf, size - size1);
}
advance_space = size;
}
return event;
}
Jack::JackMidiWriteQueue::EnqueueResult
JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
if (! ((jack_ringbuffer_write_space(info_ring) >= INFO_SIZE) &&
(jack_ringbuffer_write_space(byte_ring) >= size))) {
return size > max_bytes ? BUFFER_TOO_SMALL : BUFFER_FULL;
}
jack_ringbuffer_write(byte_ring, (const char *) buffer, size);
jack_ringbuffer_write(info_ring, (const char *) (&time),
sizeof(jack_nframes_t));
jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t));
return OK;
}
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JackMidiAsyncQueue__
#define __JackMidiAsyncQueue__
#include "JackMidiPort.h"
#include "JackMidiReadQueue.h"
#include "JackMidiWriteQueue.h"
#include "ringbuffer.h"
namespace Jack {
/**
* This is a MIDI message queue designed to allow two threads to pass MIDI
* messages between two threads (though it can also be used to buffer
* events internally). This is especially useful if the MIDI API
* you're attempting to interface with doesn't provide the ability to
* schedule MIDI events ahead of time and/or has blocking send/receive
* calls, as it allows a separate thread to handle input/output while the
* JACK process thread copies events from a `JackMidiBufferReadQueue` to
* this queue, or from this queue to a `JackMidiBufferWriteQueue`.
*/
class SERVER_EXPORT JackMidiAsyncQueue:
public JackMidiReadQueue, public JackMidiWriteQueue {
private:
static const size_t INFO_SIZE =
sizeof(jack_nframes_t) + sizeof(size_t);
size_t advance_space;
jack_ringbuffer_t *byte_ring;
jack_midi_data_t *data_buffer;
jack_midi_event_t dequeue_event;
jack_ringbuffer_t *info_ring;
size_t max_bytes;
public:
using JackMidiWriteQueue::EnqueueEvent;
/**
* Creates a new asynchronous MIDI message queue. The queue can store
* up to `max_messages` MIDI messages and up to `max_bytes` of MIDI
* data before it starts rejecting messages.
*/
JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024);
virtual ~JackMidiAsyncQueue();
/**
* Dequeues and returns a MIDI event. Returns '0' if there are no MIDI
* events available. This method may be overridden.
*/
virtual jack_midi_event_t *
DequeueEvent();
/**
* Enqueues the MIDI event specified by the arguments. The return
* value indiciates whether or not the event was successfully enqueued.
* This method may be overridden.
*/
virtual EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);
};
}
#endif
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <new>
#include "JackMidiAsyncWaitQueue.h"
#include "JackMidiUtil.h"
#include "JackTime.h"
using Jack::JackMidiAsyncWaitQueue;
JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes,
size_t max_messages):
JackMidiAsyncQueue(max_bytes, max_messages)
{
if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) {
throw std::bad_alloc();
}
}
JackMidiAsyncWaitQueue::~JackMidiAsyncWaitQueue()
{
semaphore.Destroy();
}
jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent()
{
return DequeueEvent((long) 0);
}
jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame)
{
// XXX: I worry about timer resolution on Solaris and Windows. When the
// resolution for the `JackSynchro` object is milliseconds, the worst-case
// scenario for processor objects is that the wait time becomes less than a
// millisecond, and the processor object continually calls this method,
// expecting to wait a certain amount of microseconds, and ends up not
// waiting at all each time, essentially busy-waiting until the current
// frame is reached. Perhaps there should be a #define that indicates the
// wait time resolution for `JackSynchro` objects so that we can wait a
// little longer if necessary.
jack_time_t frame_time = GetTimeFromFrames(frame);
jack_time_t current_time = GetMicroSeconds();
return DequeueEvent((frame_time < current_time) ? 0 :
(long) (frame_time - current_time));
}
jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent(long usec)
{
return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ?
JackMidiAsyncQueue::DequeueEvent() : 0;
}
Jack::JackMidiWriteQueue::EnqueueResult
JackMidiAsyncWaitQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
EnqueueResult result = JackMidiAsyncQueue::EnqueueEvent(time, size,
buffer);
if (result == OK) {
semaphore.Signal();
}
return result;
}
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JackMidiAsyncWaitQueue__
#define __JackMidiAsyncWaitQueue__
#include "JackMidiAsyncQueue.h"
namespace Jack {
/**
* This is an asynchronous wait queue that allows a thread to wait for a
* message, either indefinitely or for a specified time. This is one
* example of a way that the `JackMidiAsyncQueue` class can be extended so
* that process threads can interact with non-process threads to send MIDI
* events.
*
* XXX: As of right now, this code hasn't been tested. Also, note the
* warning in the JackMidiAsyncWaitQueue.cpp about semaphore wait
* resolution.
*/
class SERVER_EXPORT JackMidiAsyncWaitQueue: public JackMidiAsyncQueue {
private:
JackSynchro semaphore;
public:
/**
* Creates a new asynchronous MIDI wait message queue. The queue can
* store up to `max_messages` MIDI messages and up to `max_bytes` of
* MIDI data before it starts rejecting messages.
*/
JackMidiAsyncWaitQueue(size_t max_bytes=4096,
size_t max_messages=1024);
~JackMidiAsyncWaitQueue();
/**
* Dequeues and returns a MIDI event. Returns '0' if there are no MIDI
* events available right now.
*/
jack_midi_event_t *
DequeueEvent();
/**
* Waits a specified time for a MIDI event to be available, or
* indefinitely if the time is negative. Returns the MIDI event, or
* '0' if time runs out and no MIDI event is available.
*/
jack_midi_event_t *
DequeueEvent(long usecs);
/**
* Waits until the specified frame for a MIDI event to be available.
* Returns the MIDI event, or '0' if time runs out and no MIDI event is
* available.
*/
jack_midi_event_t *
DequeueEvent(jack_nframes_t frame);
/**
* Enqueues the MIDI event specified by the arguments. The return
* value indiciates whether or not the event was successfully enqueued.
*/
EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);
};
}
#endif
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "JackMidiBufferReadQueue.h"
#include "JackMidiUtil.h"
using Jack::JackMidiBufferReadQueue;
JackMidiBufferReadQueue::JackMidiBufferReadQueue()
{
event_count = 0;
index = 0;
}
jack_midi_event_t *
JackMidiBufferReadQueue::DequeueEvent()
{
jack_midi_event_t *e = 0;
if (index < event_count) {
JackMidiEvent *event = &(buffer->events[index]);
midi_event.buffer = event->GetData(buffer);
midi_event.size = event->size;
midi_event.time = last_frame_time + event->time;
e = &midi_event;
index++;
}
return e;
}
void
JackMidiBufferReadQueue::ResetMidiBuffer(JackMidiBuffer *buffer)
{
index = 0;
if (buffer) {
this->buffer = buffer;
event_count = buffer->event_count;
last_frame_time = GetLastFrame();
} else {
event_count = 0;
}
}
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JackMidiBufferReadQueue__
#define __JackMidiBufferReadQueue__
#include "JackMidiReadQueue.h"
namespace Jack {
/**
* Wrapper class to present a JackMidiBuffer in a read queue interface.
*/
class SERVER_EXPORT JackMidiBufferReadQueue: public JackMidiReadQueue {
private:
JackMidiBuffer *buffer;
jack_nframes_t event_count;
jack_nframes_t index;
jack_nframes_t last_frame_time;
jack_midi_event_t midi_event;
public:
JackMidiBufferReadQueue();
jack_midi_event_t *
DequeueEvent();
/**
* This method must be called each period to reset the MIDI buffer for
* processing.
*/
void
ResetMidiBuffer(JackMidiBuffer *buffer);
};
}
#endif
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "JackMidiBufferWriteQueue.h"
#include "JackMidiUtil.h"
using Jack::JackMidiBufferWriteQueue;
JackMidiBufferWriteQueue::JackMidiBufferWriteQueue()
{
// Empty
}
Jack::JackMidiWriteQueue::EnqueueResult
JackMidiBufferWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *data)
{
if (time >= next_frame_time) {
return EVENT_EARLY;
}
if (time < last_frame_time) {
time = last_frame_time;
}
jack_midi_data_t *dst = buffer->ReserveEvent(time - last_frame_time, size);
if (! dst) {
return size > max_bytes ? BUFFER_TOO_SMALL : BUFFER_FULL;
}
memcpy(dst, data, size);
return OK;
}
void
JackMidiBufferWriteQueue::ResetMidiBuffer(JackMidiBuffer *buffer,
jack_nframes_t frames)
{
this->buffer = buffer;
buffer->Reset(frames);
last_frame_time = GetLastFrame();
max_bytes = buffer->MaxEventSize();
next_frame_time = last_frame_time + frames;
}
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JackMidiBufferWriteQueue__
#define __JackMidiBufferWriteQueue__
#include "JackMidiWriteQueue.h"
namespace Jack {
/**
* Wrapper class to present a JackMidiBuffer in a write queue interface.
*/
class SERVER_EXPORT JackMidiBufferWriteQueue: public JackMidiWriteQueue {
private:
JackMidiBuffer *buffer;
jack_nframes_t last_frame_time;
size_t max_bytes;
jack_nframes_t next_frame_time;
public:
using JackMidiWriteQueue::EnqueueEvent;
JackMidiBufferWriteQueue();
EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);
/**
* This method must be called each period to reset the MIDI buffer for
* processing.
*/
void
ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames);
};
}
#endif
/*
Copyright (C) 2010 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the