summaryrefslogtreecommitdiff
path: root/common/JackMidiRawInputWriteQueue.cpp
diff options
context:
space:
mode:
authorDevin Anderson <surfacepatterns (at) gmail (dot) com>2011-03-18 22:42:34 -0700
committerDevin Anderson <surfacepatterns (at) gmail (dot) com>2011-03-18 22:47:45 -0700
commit4bd1bef24c4b4bc0722fa92f7aa3ab07f49a0db8 (patch)
tree8e25142172c5c827a3cfa9a9c069e016472aa163 /common/JackMidiRawInputWriteQueue.cpp
parente97dcccfa863288a22b6b42324aa385edd4f16d6 (diff)
downloadjack2-4bd1bef24c4b4bc0722fa92f7aa3ab07f49a0db8.tar.gz
Added MIDI queues, FFADO objects, etc. - see 'http://trac.jackaudio.org/ticket/187' for more details
Diffstat (limited to 'common/JackMidiRawInputWriteQueue.cpp')
-rw-r--r--common/JackMidiRawInputWriteQueue.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/common/JackMidiRawInputWriteQueue.cpp b/common/JackMidiRawInputWriteQueue.cpp
new file mode 100644
index 00000000..e2ff4453
--- /dev/null
+++ b/common/JackMidiRawInputWriteQueue.cpp
@@ -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;
+}