@@ -0,0 +1,94 @@ | |||
/* | |||
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; | |||
} |
@@ -0,0 +1,92 @@ | |||
/* | |||
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 |
@@ -0,0 +1,85 @@ | |||
/* | |||
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; | |||
} |
@@ -0,0 +1,97 @@ | |||
/* | |||
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 |
@@ -0,0 +1,57 @@ | |||
/* | |||
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; | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
/* | |||
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 |
@@ -0,0 +1,57 @@ | |||
/* | |||
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; | |||
} |
@@ -0,0 +1,62 @@ | |||
/* | |||
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 |
@@ -0,0 +1,300 @@ | |||
/* | |||
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 <cassert> | |||
#include <memory> | |||
#include <new> | |||
#include "JackMidiRawInputWriteQueue.h" | |||
using Jack::JackMidiRawInputWriteQueue; | |||
JackMidiRawInputWriteQueue:: | |||
JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, | |||
size_t max_packet_data, size_t max_packets) | |||
{ | |||
packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets); | |||
std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue); | |||
input_ring = jack_ringbuffer_create(max_packet_data + 1); | |||
if (! input_ring) { | |||
throw std::bad_alloc(); | |||
} | |||
jack_ringbuffer_mlock(input_ring); | |||
Clear(); | |||
expected_bytes = 0; | |||
event_pending = false; | |||
packet = 0; | |||
status_byte = 0; | |||
this->write_queue = write_queue; | |||
packet_queue_ptr.release(); | |||
} | |||
JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue() | |||
{ | |||
jack_ringbuffer_free(input_ring); | |||
delete packet_queue; | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::Clear() | |||
{ | |||
jack_ringbuffer_reset(input_ring); | |||
total_bytes = 0; | |||
unbuffered_bytes = 0; | |||
} | |||
Jack::JackMidiWriteQueue::EnqueueResult | |||
JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
return packet_queue->EnqueueEvent(time, size, buffer); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes, | |||
size_t total_bytes) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI " | |||
"byte(s) of a %d byte message could not be buffered. The " | |||
"message has been dropped.", unbuffered_bytes, total_bytes); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleEventLoss(jack_midi_event_t *event) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI " | |||
"event scheduled for frame '%d' could not be processed because " | |||
"the write queue cannot accomodate an event of that size. The " | |||
"event has been discarded.", event->size, event->time); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleIncompleteMessage(size_t total_bytes) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - " | |||
"Discarding %d MIDI byte(s) of an incomplete message. The " | |||
"MIDI cable may have been unplugged.", total_bytes); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleInvalidStatusByte(jack_midi_data_t byte) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - " | |||
"Dropping invalid MIDI status byte '%x'.", (unsigned int) byte); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd(size_t total_bytes) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - " | |||
"Received a sysex end byte without first receiving a sysex " | |||
"start byte. Discarding %d MIDI byte(s). The cable may have " | |||
"been unplugged.", total_bytes); | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time) | |||
{ | |||
bool result = ! unbuffered_bytes; | |||
if (! result) { | |||
HandleBufferFailure(unbuffered_bytes, total_bytes); | |||
} else { | |||
size_t size = jack_ringbuffer_read_space(input_ring); | |||
jack_ringbuffer_data_t vector[2]; | |||
jack_ringbuffer_get_read_vector(input_ring, vector); | |||
// We don't worry about the second part of the vector, as we reset the | |||
// ringbuffer after each parsed message. | |||
PrepareEvent(time, size, (jack_midi_data_t *) vector[0].buf); | |||
} | |||
Clear(); | |||
if (status_byte >= 0xf0) { | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
} | |||
return result; | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
event_byte = byte; | |||
PrepareEvent(time, 1, &event_byte); | |||
return true; | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
event.buffer = buffer; | |||
event.size = size; | |||
event.time = time; | |||
event_pending = true; | |||
} | |||
jack_nframes_t | |||
JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame) | |||
{ | |||
if (event_pending) { | |||
if (! WriteEvent(boundary_frame)) { | |||
return event.time; | |||
} | |||
} | |||
if (! packet) { | |||
packet = packet_queue->DequeueEvent(); | |||
} | |||
for (; packet; packet = packet_queue->DequeueEvent()) { | |||
for (; packet->size; (packet->buffer)++, (packet->size)--) { | |||
if (ProcessByte(packet->time, *(packet->buffer))) { | |||
if (! WriteEvent(boundary_frame)) { | |||
(packet->buffer)++; | |||
(packet->size)--; | |||
return event.time; | |||
} | |||
} | |||
} | |||
} | |||
return 0; | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
if (byte >= 0xf8) { | |||
// Realtime | |||
if (byte == 0xfd) { | |||
HandleInvalidStatusByte(byte); | |||
return false; | |||
} | |||
return PrepareByteEvent(time, byte); | |||
} | |||
if (byte == 0xf7) { | |||
// Sysex end | |||
if (status_byte == 0xf0) { | |||
RecordByte(byte); | |||
return PrepareBufferedEvent(time); | |||
} | |||
HandleUnexpectedSysexEnd(total_bytes); | |||
Clear(); | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
return false; | |||
} | |||
if (byte >= 0x80) { | |||
// Non-realtime status byte | |||
if (total_bytes) { | |||
HandleIncompleteMessage(total_bytes); | |||
Clear(); | |||
} | |||
status_byte = byte; | |||
switch (byte & 0xf0) { | |||
case 0x80: | |||
case 0x90: | |||
case 0xa0: | |||
case 0xb0: | |||
case 0xe0: | |||
// Note On, Note Off, Aftertouch, Control Change, Pitch Wheel | |||
expected_bytes = 3; | |||
break; | |||
case 0xc0: | |||
case 0xd0: | |||
// Program Change, Channel Pressure | |||
expected_bytes = 2; | |||
break; | |||
case 0xf0: | |||
switch (byte) { | |||
case 0xf0: | |||
// Sysex | |||
expected_bytes = 0; | |||
break; | |||
case 0xf1: | |||
case 0xf3: | |||
// MTC Quarter Frame, Song Select | |||
expected_bytes = 2; | |||
break; | |||
case 0xf2: | |||
// Song Position | |||
expected_bytes = 3; | |||
break; | |||
case 0xf4: | |||
case 0xf5: | |||
// Undefined | |||
HandleInvalidStatusByte(byte); | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
return false; | |||
case 0xf6: | |||
// Tune Request | |||
bool result = PrepareByteEvent(time, byte); | |||
if (result) { | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
} | |||
return result; | |||
} | |||
} | |||
RecordByte(byte); | |||
return false; | |||
} | |||
// Data byte | |||
if (! status_byte) { | |||
// Data bytes without a status will be discarded. | |||
total_bytes++; | |||
unbuffered_bytes++; | |||
return false; | |||
} | |||
if (! total_bytes) { | |||
// Apply running status. | |||
RecordByte(status_byte); | |||
} | |||
RecordByte(byte); | |||
return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) : | |||
false; | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte) | |||
{ | |||
if (jack_ringbuffer_write(input_ring, (const char *) &byte, 1) != 1) { | |||
unbuffered_bytes++; | |||
} | |||
total_bytes++; | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame) | |||
{ | |||
if (event.time < boundary_frame) { | |||
switch (write_queue->EnqueueEvent(&event)) { | |||
case BUFFER_TOO_SMALL: | |||
HandleEventLoss(&event); | |||
// Fallthrough on purpose | |||
case OK: | |||
event_pending = false; | |||
return true; | |||
default: | |||
// This is here to stop compilers from warning us about not | |||
// handling enumeration values. | |||
; | |||
} | |||
} | |||
return false; | |||
} |
@@ -0,0 +1,170 @@ | |||
/* | |||
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 __JackMidiRawInputWriteQueue__ | |||
#define __JackMidiRawInputWriteQueue__ | |||
#include "JackMidiAsyncQueue.h" | |||
#include "JackMidiWriteQueue.h" | |||
#include "ringbuffer.h" | |||
namespace Jack { | |||
/** | |||
* This queue enqueues raw, unparsed MIDI packets, and outputs complete | |||
* MIDI messages to a write queue. | |||
* | |||
* Use this queue if the MIDI API you're interfacing with gives you raw | |||
* MIDI bytes that must be parsed. | |||
*/ | |||
class SERVER_EXPORT JackMidiRawInputWriteQueue: public JackMidiWriteQueue { | |||
private: | |||
jack_midi_event_t event; | |||
jack_midi_data_t event_byte; | |||
bool event_pending; | |||
size_t expected_bytes; | |||
jack_ringbuffer_t *input_ring; | |||
jack_midi_event_t *packet; | |||
JackMidiAsyncQueue *packet_queue; | |||
jack_midi_data_t status_byte; | |||
size_t total_bytes; | |||
size_t unbuffered_bytes; | |||
JackMidiWriteQueue *write_queue; | |||
void | |||
Clear(); | |||
bool | |||
PrepareBufferedEvent(jack_nframes_t time); | |||
bool | |||
PrepareByteEvent(jack_nframes_t time, jack_midi_data_t byte); | |||
void | |||
PrepareEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
bool | |||
ProcessByte(jack_nframes_t time, jack_midi_data_t byte); | |||
void | |||
RecordByte(jack_midi_data_t byte); | |||
bool | |||
WriteEvent(jack_nframes_t boundary_frame); | |||
protected: | |||
/** | |||
* Override this method to specify what happens when there isn't enough | |||
* room in the ringbuffer to contain a parsed event. The default | |||
* method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes); | |||
/** | |||
* Override this method to specify what happens when a parsed event | |||
* can't be written to the write queue because the event's size exceeds | |||
* the total possible space in the write queue. The default method | |||
* outputs an error message. | |||
*/ | |||
virtual void | |||
HandleEventLoss(jack_midi_event_t *event); | |||
/** | |||
* Override this method to specify what happens when an incomplete MIDI | |||
* message is parsed. The default method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleIncompleteMessage(size_t total_bytes); | |||
/** | |||
* Override this method to specify what happens when an invalid MIDI | |||
* status byte is parsed. The default method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleInvalidStatusByte(jack_midi_data_t byte); | |||
/** | |||
* Override this method to specify what happens when a sysex end byte | |||
* is parsed without first parsing a sysex begin byte. The default | |||
* method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleUnexpectedSysexEnd(size_t total_bytes); | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
/** | |||
* Called to create a new raw input write queue. The `write_queue` | |||
* argument is the queue to write parsed messages to. The optional | |||
* `max_packets` argument specifies the number of packets that can be | |||
* enqueued in the internal queue. The optional `max_packet_data` | |||
* argument specifies the total number of MIDI bytes that can be put in | |||
* the internal queue, AND the maximum size for an event that can be | |||
* written to the write queue. | |||
*/ | |||
JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, | |||
size_t max_packet_data=4096, | |||
size_t max_packets=1024); | |||
~JackMidiRawInputWriteQueue(); | |||
EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
/** | |||
* The `Process()` method should be called each time the | |||
* `EnqueueEvent()` method returns `OK`. The `Process()` method will | |||
* return the next frame at which an event should be sent. The return | |||
* value from `Process()` depends upon the result of writing bytes to | |||
* the write queue: | |||
* | |||
* -If the return value is '0', then all *complete* events have been | |||
* sent successfully to the write queue. Don't call `Process()` again | |||
* until another event has been enqueued. | |||
* | |||
* -If the return value is a non-zero value, then it specifies the | |||
* frame that a pending event is scheduled to sent at. If the frame is | |||
* in the future, then `Process()` should be called again at that time; | |||
* otherwise, `Process()` should be called as soon as the write queue | |||
* will accept events again. | |||
*/ | |||
jack_nframes_t | |||
Process(jack_nframes_t boundary_frame); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,228 @@ | |||
/* | |||
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 <memory> | |||
#include <new> | |||
#include "JackError.h" | |||
#include "JackMidiRawOutputWriteQueue.h" | |||
#include "JackMidiUtil.h" | |||
using Jack::JackMidiRawOutputWriteQueue; | |||
#define STILL_TIME(c, b) ((! (b)) || ((c) < (b))) | |||
JackMidiRawOutputWriteQueue:: | |||
JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size, | |||
size_t max_non_rt_messages, size_t max_rt_messages) | |||
{ | |||
non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages); | |||
std::auto_ptr<JackMidiAsyncQueue> non_rt_ptr(non_rt_queue); | |||
rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages); | |||
std::auto_ptr<JackMidiAsyncQueue> rt_ptr(rt_queue); | |||
non_rt_event = 0; | |||
rt_event = 0; | |||
running_status = 0; | |||
this->send_queue = send_queue; | |||
rt_ptr.release(); | |||
non_rt_ptr.release(); | |||
} | |||
JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue() | |||
{ | |||
delete non_rt_queue; | |||
delete rt_queue; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent() | |||
{ | |||
non_rt_event = non_rt_queue->DequeueEvent(); | |||
bool result = non_rt_event != 0; | |||
if (result) { | |||
non_rt_event_time = non_rt_event->time; | |||
running_status = ApplyRunningStatus(non_rt_event, running_status); | |||
} | |||
return result; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::DequeueRealtimeEvent() | |||
{ | |||
rt_event = rt_queue->DequeueEvent(); | |||
bool result = rt_event != 0; | |||
if (result) { | |||
rt_event_time = rt_event->time; | |||
} | |||
return result; | |||
} | |||
Jack::JackMidiWriteQueue::EnqueueResult | |||
JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue : | |||
non_rt_queue; | |||
EnqueueResult result = queue->EnqueueEvent(time, size, buffer); | |||
if (result == OK) { | |||
last_enqueued_message_time = time; | |||
} | |||
return result; | |||
} | |||
void | |||
JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
jack_error("JackMidiRawOutputWriteQueue::HandleWriteQueueBug - **BUG** " | |||
"The write queue told us that it couldn't enqueue a 1-byte " | |||
"MIDI event scheduled for frame '%d'. This is probably a bug " | |||
"in the write queue implementation.", time); | |||
} | |||
jack_nframes_t | |||
JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame) | |||
{ | |||
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
while (STILL_TIME(current_frame, boundary_frame)) { | |||
if (! non_rt_event) { | |||
DequeueNonRealtimeEvent(); | |||
} | |||
if (! rt_event) { | |||
DequeueRealtimeEvent(); | |||
} | |||
if (! (non_rt_event || rt_event)) { | |||
return 0; | |||
} | |||
if (! WriteRealtimeEvents(boundary_frame)) { | |||
break; | |||
} | |||
jack_nframes_t non_rt_boundary = | |||
rt_event && STILL_TIME(rt_event_time, boundary_frame) ? | |||
rt_event_time : boundary_frame; | |||
if (! WriteNonRealtimeEvents(non_rt_boundary)) { | |||
break; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
} | |||
// If we get here, that means there is some sort of message available, and | |||
// that either we can't currently write to the write queue or we have | |||
// reached the boundary frame. Return the earliest time that a message is | |||
// scheduled to be sent. | |||
return ! non_rt_event ? rt_event_time : | |||
non_rt_event->size > 1 ? current_frame : | |||
! rt_event ? non_rt_event_time : | |||
non_rt_event_time < rt_event_time ? non_rt_event_time : rt_event_time; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
switch (send_queue->EnqueueEvent(time, 1, &byte)) { | |||
case BUFFER_TOO_SMALL: | |||
HandleWriteQueueBug(time, byte); | |||
case OK: | |||
return true; | |||
default: | |||
// This is here to stop compilers from warning us about not handling | |||
// enumeration values. | |||
; | |||
} | |||
return false; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue:: | |||
WriteNonRealtimeEvents(jack_nframes_t boundary_frame) | |||
{ | |||
if (! non_rt_event) { | |||
if (! DequeueNonRealtimeEvent()) { | |||
return true; | |||
} | |||
} | |||
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
do { | |||
// Send out as much of the non-realtime buffer as we can, save for one | |||
// byte which we will send out when the message is supposed to arrive. | |||
for (; non_rt_event->size > 1; | |||
(non_rt_event->size)--, (non_rt_event->buffer)++) { | |||
if (! STILL_TIME(current_frame, boundary_frame)) { | |||
return true; | |||
} | |||
if (! SendByte(current_frame, *(non_rt_event->buffer))) { | |||
return false; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
} | |||
if (! (STILL_TIME(current_frame, boundary_frame) && | |||
STILL_TIME(non_rt_event_time, boundary_frame))) { | |||
return true; | |||
} | |||
// There's still time. Try to send the byte. | |||
if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) { | |||
return false; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
if (! DequeueNonRealtimeEvent()) { | |||
break; | |||
} | |||
} while (STILL_TIME(current_frame, boundary_frame)); | |||
return true; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::WriteRealtimeEvents(jack_nframes_t boundary_frame) | |||
{ | |||
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
if (! rt_event) { | |||
if (! DequeueRealtimeEvent()) { | |||
return true; | |||
} | |||
} | |||
for (;;) { | |||
if (! STILL_TIME(current_frame, boundary_frame)) { | |||
return false; | |||
} | |||
// If: | |||
// -there's still time before we need to send the realtime event | |||
// -there's a non-realtime event available for sending | |||
// -non-realtime data can be scheduled before this event | |||
if ((rt_event_time > current_frame) && non_rt_event && | |||
((non_rt_event->size > 1) || | |||
(non_rt_event_time < rt_event_time))) { | |||
return true; | |||
} | |||
if (! SendByte(rt_event_time, *(rt_event->buffer))) { | |||
return false; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
if (! DequeueRealtimeEvent()) { | |||
return true; | |||
} | |||
} | |||
} |
@@ -0,0 +1,147 @@ | |||
/* | |||
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 __JackMidiRawOutputWriteQueue__ | |||
#define __JackMidiRawOutputWriteQueue__ | |||
#include "JackMidiAsyncQueue.h" | |||
#include "JackMidiSendQueue.h" | |||
namespace Jack { | |||
/** | |||
* This queue enqueues valid MIDI events and modifies them for raw output | |||
* to a write queue. It has a number of advantages over straight MIDI | |||
* event copying: | |||
* | |||
* -Running status: Status bytes can be omitted when the status byte of the | |||
* current MIDI message is the same as the status byte of the last sent | |||
* MIDI message. | |||
* | |||
* -Realtime messages: Realtime messages are given priority over | |||
* non-realtime messages. Realtime bytes are interspersed with | |||
* non-realtime bytes so that realtime messages can be sent as close as | |||
* possible to the time they're scheduled for sending. | |||
* | |||
* -Time optimization: Bytes in non-realtime messages are sent out early | |||
* when possible, with the last byte of the message being sent out as close | |||
* to the specified event time as possible. | |||
* | |||
* Use this queue if the MIDI API you're interfacing with allows you to | |||
* send raw MIDI bytes. | |||
*/ | |||
class SERVER_EXPORT JackMidiRawOutputWriteQueue: | |||
public JackMidiWriteQueue { | |||
private: | |||
jack_nframes_t last_enqueued_message_time; | |||
jack_midi_event_t *non_rt_event; | |||
jack_nframes_t non_rt_event_time; | |||
JackMidiAsyncQueue *non_rt_queue; | |||
jack_midi_event_t *rt_event; | |||
jack_nframes_t rt_event_time; | |||
JackMidiAsyncQueue *rt_queue; | |||
jack_midi_data_t running_status; | |||
JackMidiSendQueue *send_queue; | |||
bool | |||
DequeueNonRealtimeEvent(); | |||
bool | |||
DequeueRealtimeEvent(); | |||
bool | |||
SendByte(jack_nframes_t time, jack_midi_data_t byte); | |||
bool | |||
WriteNonRealtimeEvents(jack_nframes_t boundary_frame); | |||
bool | |||
WriteRealtimeEvents(jack_nframes_t boundary_frame); | |||
protected: | |||
/** | |||
* Override this method to specify what happens when the write queue | |||
* says that a 1-byte event is too large for its buffer. Basically, | |||
* this should never happen. | |||
*/ | |||
virtual void | |||
HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte); | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
/** | |||
* Called to create a new raw write queue. The `send_queue` argument | |||
* is the queue to write raw bytes to. The optional `max_rt_messages` | |||
* argument specifies the number of messages that can be enqueued in | |||
* the internal realtime queue. The optional `max_non_rt_messages` | |||
* argument specifies the number of messages that can be enqueued in | |||
* the internal non-realtime queue. The optional `non_rt_size` | |||
* argument specifies the total number of MIDI bytes that can be put in | |||
* the non-realtime queue. | |||
*/ | |||
JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, | |||
size_t non_rt_size=4096, | |||
size_t max_non_rt_messages=1024, | |||
size_t max_rt_messages=128); | |||
~JackMidiRawOutputWriteQueue(); | |||
EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
/** | |||
* The `Process()` method should be called each time the | |||
* `EnqueueEvent()` method returns 'OK'. The `Process()` method will | |||
* return the next frame at which an event should be sent. The return | |||
* value from `Process()` depends upon the result of writing bytes to | |||
* the write queue: | |||
* | |||
* -If the return value is '0', then all events that have been enqueued | |||
* in this queue have been sent successfully to the write queue. Don't | |||
* call `Process()` again until another event has been enqueued. | |||
* | |||
* -If the return value is an earlier frame or the current frame, it | |||
* means that the write queue returned 'BUFFER_FULL', 'ERROR', or | |||
* 'EVENT_EARLY' when this queue attempted to send the next byte, and | |||
* that the byte should have already been sent, or is scheduled to be | |||
* sent *now*. `Process()` should be called again when the write queue | |||
* can enqueue events again successfully. How to determine when this | |||
* will happen is left up to the caller. | |||
* | |||
* -If the return value is in the future, then `Process()` should be | |||
* called again at that time, or after another event is enqueued. | |||
*/ | |||
jack_nframes_t | |||
Process(jack_nframes_t boundary_frame); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,27 @@ | |||
/* | |||
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 "JackMidiReadQueue.h" | |||
using Jack::JackMidiReadQueue; | |||
JackMidiReadQueue::~JackMidiReadQueue() | |||
{ | |||
// Empty | |||
} |
@@ -0,0 +1,55 @@ | |||
/* | |||
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 __JackMidiReadQueue__ | |||
#define __JackMidiReadQueue__ | |||
#include "JackMidiPort.h" | |||
namespace Jack { | |||
/** | |||
* Interface for objects that MIDI events can be read from. | |||
*/ | |||
class SERVER_EXPORT JackMidiReadQueue { | |||
public: | |||
virtual | |||
~JackMidiReadQueue(); | |||
/** | |||
* Dequeues an event from the queue. Returns the event, or 0 if no | |||
* events are available for reading. | |||
* | |||
* An event dequeued from the read queue is guaranteed to be valid up | |||
* until another event is dequeued, at which all bets are off. Make | |||
* sure that you handle each event you dequeue before dequeueing the | |||
* next event. | |||
*/ | |||
virtual jack_midi_event_t * | |||
DequeueEvent() = 0; | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,27 @@ | |||
/* | |||
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 "JackMidiReceiveQueue.h" | |||
using Jack::JackMidiReceiveQueue; | |||
JackMidiReceiveQueue::~JackMidiReceiveQueue() | |||
{ | |||
// Empty | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
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 __JackMidiReceiveQueue__ | |||
#define __JackMidiReceiveQueue__ | |||
#include "JackMidiReadQueue.h" | |||
namespace Jack { | |||
/** | |||
* Implemented by MIDI input connections. | |||
*/ | |||
class SERVER_EXPORT JackMidiReceiveQueue: public JackMidiReadQueue { | |||
public: | |||
virtual | |||
~JackMidiReceiveQueue(); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,34 @@ | |||
/* | |||
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 "JackMidiSendQueue.h" | |||
#include "JackMidiUtil.h" | |||
using Jack::JackMidiSendQueue; | |||
JackMidiSendQueue::~JackMidiSendQueue() | |||
{ | |||
// Empty | |||
} | |||
jack_nframes_t | |||
JackMidiSendQueue::GetNextScheduleFrame() | |||
{ | |||
return GetCurrentFrame(); | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
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 __JackMidiSendQueue__ | |||
#define __JackMidiSendQueue__ | |||
#include "JackMidiWriteQueue.h" | |||
namespace Jack { | |||
/** | |||
* Implemented by MIDI output connections. | |||
*/ | |||
class SERVER_EXPORT JackMidiSendQueue: public JackMidiWriteQueue { | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
virtual | |||
~JackMidiSendQueue(); | |||
/** | |||
* Returns the next frame that a MIDI message can be sent at. The | |||
* default method returns the current frame. | |||
*/ | |||
virtual jack_nframes_t | |||
GetNextScheduleFrame(); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,120 @@ | |||
/* | |||
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 "JackEngineControl.h" | |||
#include "JackFrameTimer.h" | |||
#include "JackGlobals.h" | |||
#include "JackMidiUtil.h" | |||
#include "JackTime.h" | |||
jack_midi_data_t | |||
Jack::ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, | |||
jack_midi_data_t running_status) | |||
{ | |||
// Stolen and modified from alsa/midi_pack.h | |||
jack_midi_data_t status = **buffer; | |||
if ((status >= 0x80) && (status < 0xf0)) { | |||
if (status == running_status) { | |||
(*buffer)++; | |||
(*size)--; | |||
} else { | |||
running_status = status; | |||
} | |||
} else if (status < 0xf8) { | |||
running_status = 0; | |||
} | |||
return running_status; | |||
} | |||
jack_midi_data_t | |||
Jack::ApplyRunningStatus(jack_midi_event_t *event, | |||
jack_midi_data_t running_status) | |||
{ | |||
return ApplyRunningStatus(&(event->size), &(event->buffer), | |||
running_status); | |||
} | |||
jack_nframes_t | |||
Jack::GetCurrentFrame() | |||
{ | |||
JackEngineControl *control = GetEngineControl(); | |||
JackTimer timer; | |||
control->ReadFrameTime(&timer); | |||
return timer.Time2Frames(GetMicroSeconds(), control->fBufferSize); | |||
} | |||
jack_nframes_t | |||
Jack::GetFramesFromTime(jack_time_t time) | |||
{ | |||
JackEngineControl* control = GetEngineControl(); | |||
JackTimer timer; | |||
control->ReadFrameTime(&timer); | |||
return timer.Time2Frames(time, control->fBufferSize); | |||
} | |||
jack_nframes_t | |||
Jack::GetLastFrame() | |||
{ | |||
return GetEngineControl()->fFrameTimer.ReadCurrentState()->CurFrame(); | |||
} | |||
int | |||
Jack::GetMessageLength(jack_midi_data_t status_byte) | |||
{ | |||
switch (status_byte & 0xf0) { | |||
case 0x80: | |||
case 0x90: | |||
case 0xa0: | |||
case 0xb0: | |||
case 0xe0: | |||
return 3; | |||
case 0xc0: | |||
case 0xd0: | |||
return 2; | |||
case 0xf0: | |||
switch (status_byte) { | |||
case 0xf0: | |||
return 0; | |||
case 0xf1: | |||
case 0xf3: | |||
return 2; | |||
case 0xf2: | |||
return 3; | |||
case 0xf4: | |||
case 0xf5: | |||
case 0xf7: | |||
case 0xfd: | |||
break; | |||
default: | |||
return 1; | |||
} | |||
} | |||
return -1; | |||
} | |||
jack_time_t | |||
Jack::GetTimeFromFrames(jack_nframes_t frames) | |||
{ | |||
JackEngineControl* control = GetEngineControl(); | |||
JackTimer timer; | |||
control->ReadFrameTime(&timer); | |||
return timer.Frames2Time(frames, control->fBufferSize); | |||
} |
@@ -0,0 +1,102 @@ | |||
/* | |||
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 __JackMidiUtil__ | |||
#define __JackMidiUtil__ | |||
#include "JackMidiPort.h" | |||
namespace Jack { | |||
/** | |||
* Use this function to optimize MIDI output by omitting unnecessary status | |||
* bytes. This can't be used with all MIDI APIs, so before using this | |||
* function, make sure that your MIDI API doesn't require complete MIDI | |||
* messages to be sent. | |||
* | |||
* To start using this function, call this method with pointers to the | |||
* `size` and `buffer` arguments of the MIDI message you want to send, and | |||
* set the `running_status` argument to '0'. For each subsequent MIDI | |||
* message, call this method with pointers to its `size` and `buffer` | |||
* arguments, and set the `running_status` argument to the return value of | |||
* the previous call to this function. | |||
* | |||
* Note: This function will alter the `size` and `buffer` of your MIDI | |||
* message for each message that can be optimized. | |||
*/ | |||
SERVER_EXPORT jack_midi_data_t | |||
ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, | |||
jack_midi_data_t running_status=0); | |||
/** | |||
* A wrapper function for the above `ApplyRunningStatus` function. | |||
*/ | |||
SERVER_EXPORT jack_midi_data_t | |||
ApplyRunningStatus(jack_midi_event_t *event, | |||
jack_midi_data_t running_status); | |||
/** | |||
* Gets the estimated current time in frames. This function has the same | |||
* functionality as the JACK client API function `jack_frame_time`. | |||
*/ | |||
SERVER_EXPORT jack_nframes_t | |||
GetCurrentFrame(); | |||
/** | |||
* Gets the estimated frame that will be occurring at the given time. This | |||
* function has the same functionality as the JACK client API function | |||
* `jack_time_to_frames`. | |||
*/ | |||
SERVER_EXPORT jack_nframes_t | |||
GetFramesFromTime(jack_time_t time); | |||
/** | |||
* Gets the precise time at the start of the current process cycle. This | |||
* function has the same functionality as the JACK client API function | |||
* `jack_last_frame_time`. | |||
*/ | |||
SERVER_EXPORT jack_nframes_t | |||
GetLastFrame(); | |||
/** | |||
* Returns the expected message length for the status byte. Returns 0 if | |||
* the status byte is a system exclusive status byte, or -1 if the status | |||
* byte is invalid. | |||
*/ | |||
SERVER_EXPORT int | |||
GetMessageLength(jack_midi_data_t status_byte); | |||
/** | |||
* Gets the estimated time at which the given frame will occur. This | |||
* function has the same functionality as the JACK client API function | |||
* `jack_frames_to_time`. | |||
*/ | |||
SERVER_EXPORT jack_time_t | |||
GetTimeFromFrames(jack_nframes_t frames); | |||
}; | |||
#endif |
@@ -0,0 +1,27 @@ | |||
/* | |||
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 "JackMidiWriteQueue.h" | |||
using Jack::JackMidiWriteQueue; | |||
JackMidiWriteQueue::~JackMidiWriteQueue() | |||
{ | |||
// Empty | |||
} |
@@ -0,0 +1,79 @@ | |||
/* | |||
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 __JackMidiWriteQueue__ | |||
#define __JackMidiWriteQueue__ | |||
#include "JackMidiPort.h" | |||
namespace Jack { | |||
/** | |||
* Interface for classes that act as write queues for MIDI messages. Write | |||
* queues are used by processors to transfer data to the next processor. | |||
*/ | |||
class SERVER_EXPORT JackMidiWriteQueue { | |||
public: | |||
enum EnqueueResult { | |||
BUFFER_FULL, | |||
BUFFER_TOO_SMALL, | |||
EVENT_EARLY, | |||
ERROR, | |||
OK | |||
}; | |||
virtual ~JackMidiWriteQueue(); | |||
/** | |||
* Enqueues a data packet in the write queue of `size` bytes contained | |||
* in `buffer` that will be sent the absolute time specified by `time`. | |||
* This method should not block unless 1.) this write queue represents | |||
* the actual outbound MIDI connection, 2.) the MIDI event is being | |||
* sent *now*, meaning that `time` is less than or equal to *now*, and | |||
* 3.) the method is *not* being called in the process thread. The | |||
* method should return `OK` if the event was enqueued, `BUFFER_FULL` | |||
* if the write queue isn't able to accept the event right now, | |||
* `BUFFER_TOO_SMALL` if this write queue will never be able to accept | |||
* the event because the event is too large, `EVENT_EARLY` if this | |||
* queue cannot schedule events ahead of time, and `ERROR` if an error | |||
* occurs that cannot be specified by another return code. | |||
*/ | |||
virtual EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) = 0; | |||
/** | |||
* A wrapper method for the `EnqueueEvent` method above. | |||
*/ | |||
inline EnqueueResult | |||
EnqueueEvent(jack_midi_event_t *event) | |||
{ | |||
return EnqueueEvent(event->time, event->size, event->buffer); | |||
} | |||
}; | |||
} | |||
#endif |
@@ -1,287 +0,0 @@ | |||
/* | |||
Copyright (C) 2009 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 <cassert> | |||
#include <cstring> | |||
#include <new> | |||
#include "JackError.h" | |||
#include "JackPhysicalMidiInput.h" | |||
namespace Jack { | |||
JackPhysicalMidiInput::JackPhysicalMidiInput(size_t buffer_size) | |||
{ | |||
size_t datum_size = sizeof(jack_midi_data_t); | |||
assert(buffer_size > 0); | |||
input_ring = jack_ringbuffer_create((buffer_size + 1) * datum_size); | |||
if (! input_ring) { | |||
throw std::bad_alloc(); | |||
} | |||
jack_ringbuffer_mlock(input_ring); | |||
Clear(); | |||
expected_data_bytes = 0; | |||
status_byte = 0; | |||
} | |||
JackPhysicalMidiInput::~JackPhysicalMidiInput() | |||
{ | |||
jack_ringbuffer_free(input_ring); | |||
} | |||
void | |||
JackPhysicalMidiInput::Clear() | |||
{ | |||
jack_ringbuffer_reset(input_ring); | |||
buffered_bytes = 0; | |||
unbuffered_bytes = 0; | |||
} | |||
void | |||
JackPhysicalMidiInput::HandleBufferFailure(size_t unbuffered_bytes, | |||
size_t total_bytes) | |||
{ | |||
jack_error("%d MIDI byte(s) of a %d byte message could not be buffered - " | |||
"message dropped", unbuffered_bytes, total_bytes); | |||
} | |||
void | |||
JackPhysicalMidiInput::HandleIncompleteMessage(size_t bytes) | |||
{ | |||
jack_error("Discarding %d MIDI byte(s) - incomplete message (cable " | |||
"unplugged?)", bytes); | |||
} | |||
void | |||
JackPhysicalMidiInput::HandleInvalidStatusByte(jack_midi_data_t status) | |||
{ | |||
jack_error("Dropping invalid MIDI status byte '%x'", | |||
(unsigned int) status); | |||
} | |||
void | |||
JackPhysicalMidiInput::HandleUnexpectedSysexEnd(size_t bytes) | |||