From 4bd1bef24c4b4bc0722fa92f7aa3ab07f49a0db8 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Fri, 18 Mar 2011 22:42:34 -0700 Subject: [PATCH 01/58] Added MIDI queues, FFADO objects, etc. - see 'http://trac.jackaudio.org/ticket/187' for more details --- common/JackMidiAsyncQueue.cpp | 94 +++ common/JackMidiAsyncQueue.h | 92 +++ common/JackMidiAsyncWaitQueue.cpp | 85 +++ common/JackMidiAsyncWaitQueue.h | 97 +++ common/JackMidiBufferReadQueue.cpp | 57 ++ common/JackMidiBufferReadQueue.h | 60 ++ common/JackMidiBufferWriteQueue.cpp | 57 ++ common/JackMidiBufferWriteQueue.h | 62 ++ common/JackMidiRawInputWriteQueue.cpp | 300 +++++++++ common/JackMidiRawInputWriteQueue.h | 170 +++++ common/JackMidiRawOutputWriteQueue.cpp | 228 +++++++ common/JackMidiRawOutputWriteQueue.h | 147 +++++ common/JackMidiReadQueue.cpp | 27 + common/JackMidiReadQueue.h | 55 ++ common/JackMidiReceiveQueue.cpp | 27 + common/JackMidiReceiveQueue.h | 42 ++ common/JackMidiSendQueue.cpp | 34 + common/JackMidiSendQueue.h | 52 ++ common/JackMidiUtil.cpp | 120 ++++ common/JackMidiUtil.h | 102 +++ common/JackMidiWriteQueue.cpp | 27 + common/JackMidiWriteQueue.h | 79 +++ common/JackPhysicalMidiInput.cpp | 287 --------- common/JackPhysicalMidiInput.h | 146 ----- common/JackPhysicalMidiOutput.cpp | 320 ---------- common/JackPhysicalMidiOutput.h | 118 ---- common/wscript | 17 +- example-clients/midi_latency_test.c | 588 ++++++++++++++++++ example-clients/wscript | 1 + linux/firewire/JackFFADODriver.cpp | 38 +- linux/firewire/JackFFADOMidiInput.cpp | 59 -- linux/firewire/JackFFADOMidiInputPort.cpp | 94 +++ linux/firewire/JackFFADOMidiInputPort.h | 51 ++ linux/firewire/JackFFADOMidiOutput.cpp | 60 -- linux/firewire/JackFFADOMidiOutputPort.cpp | 98 +++ linux/firewire/JackFFADOMidiOutputPort.h | 53 ++ linux/firewire/JackFFADOMidiReceiveQueue.cpp | 55 ++ ...idiInput.h => JackFFADOMidiReceiveQueue.h} | 34 +- linux/firewire/JackFFADOMidiSendQueue.cpp | 64 ++ ...OMidiOutput.h => JackFFADOMidiSendQueue.h} | 37 +- linux/wscript | 8 +- 41 files changed, 3082 insertions(+), 1060 deletions(-) create mode 100644 common/JackMidiAsyncQueue.cpp create mode 100644 common/JackMidiAsyncQueue.h create mode 100644 common/JackMidiAsyncWaitQueue.cpp create mode 100644 common/JackMidiAsyncWaitQueue.h create mode 100644 common/JackMidiBufferReadQueue.cpp create mode 100644 common/JackMidiBufferReadQueue.h create mode 100644 common/JackMidiBufferWriteQueue.cpp create mode 100644 common/JackMidiBufferWriteQueue.h create mode 100644 common/JackMidiRawInputWriteQueue.cpp create mode 100644 common/JackMidiRawInputWriteQueue.h create mode 100644 common/JackMidiRawOutputWriteQueue.cpp create mode 100644 common/JackMidiRawOutputWriteQueue.h create mode 100644 common/JackMidiReadQueue.cpp create mode 100644 common/JackMidiReadQueue.h create mode 100644 common/JackMidiReceiveQueue.cpp create mode 100644 common/JackMidiReceiveQueue.h create mode 100644 common/JackMidiSendQueue.cpp create mode 100644 common/JackMidiSendQueue.h create mode 100644 common/JackMidiUtil.cpp create mode 100644 common/JackMidiUtil.h create mode 100644 common/JackMidiWriteQueue.cpp create mode 100644 common/JackMidiWriteQueue.h delete mode 100644 common/JackPhysicalMidiInput.cpp delete mode 100644 common/JackPhysicalMidiInput.h delete mode 100644 common/JackPhysicalMidiOutput.cpp delete mode 100644 common/JackPhysicalMidiOutput.h create mode 100644 example-clients/midi_latency_test.c delete mode 100644 linux/firewire/JackFFADOMidiInput.cpp create mode 100644 linux/firewire/JackFFADOMidiInputPort.cpp create mode 100644 linux/firewire/JackFFADOMidiInputPort.h delete mode 100644 linux/firewire/JackFFADOMidiOutput.cpp create mode 100644 linux/firewire/JackFFADOMidiOutputPort.cpp create mode 100644 linux/firewire/JackFFADOMidiOutputPort.h create mode 100644 linux/firewire/JackFFADOMidiReceiveQueue.cpp rename linux/firewire/{JackFFADOMidiInput.h => JackFFADOMidiReceiveQueue.h} (59%) create mode 100644 linux/firewire/JackFFADOMidiSendQueue.cpp rename linux/firewire/{JackFFADOMidiOutput.h => JackFFADOMidiSendQueue.h} (58%) diff --git a/common/JackMidiAsyncQueue.cpp b/common/JackMidiAsyncQueue.cpp new file mode 100644 index 00000000..28018b56 --- /dev/null +++ b/common/JackMidiAsyncQueue.cpp @@ -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 + +#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; +} diff --git a/common/JackMidiAsyncQueue.h b/common/JackMidiAsyncQueue.h new file mode 100644 index 00000000..630b5ab1 --- /dev/null +++ b/common/JackMidiAsyncQueue.h @@ -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 diff --git a/common/JackMidiAsyncWaitQueue.cpp b/common/JackMidiAsyncWaitQueue.cpp new file mode 100644 index 00000000..016737cb --- /dev/null +++ b/common/JackMidiAsyncWaitQueue.cpp @@ -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 + +#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; +} diff --git a/common/JackMidiAsyncWaitQueue.h b/common/JackMidiAsyncWaitQueue.h new file mode 100644 index 00000000..d35dd8b6 --- /dev/null +++ b/common/JackMidiAsyncWaitQueue.h @@ -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 diff --git a/common/JackMidiBufferReadQueue.cpp b/common/JackMidiBufferReadQueue.cpp new file mode 100644 index 00000000..66777eb0 --- /dev/null +++ b/common/JackMidiBufferReadQueue.cpp @@ -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; + } +} diff --git a/common/JackMidiBufferReadQueue.h b/common/JackMidiBufferReadQueue.h new file mode 100644 index 00000000..84337e42 --- /dev/null +++ b/common/JackMidiBufferReadQueue.h @@ -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 diff --git a/common/JackMidiBufferWriteQueue.cpp b/common/JackMidiBufferWriteQueue.cpp new file mode 100644 index 00000000..5caab49d --- /dev/null +++ b/common/JackMidiBufferWriteQueue.cpp @@ -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; +} diff --git a/common/JackMidiBufferWriteQueue.h b/common/JackMidiBufferWriteQueue.h new file mode 100644 index 00000000..90d5cbf1 --- /dev/null +++ b/common/JackMidiBufferWriteQueue.h @@ -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 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 +#include +#include + +#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 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; +} diff --git a/common/JackMidiRawInputWriteQueue.h b/common/JackMidiRawInputWriteQueue.h new file mode 100644 index 00000000..2feff527 --- /dev/null +++ b/common/JackMidiRawInputWriteQueue.h @@ -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 diff --git a/common/JackMidiRawOutputWriteQueue.cpp b/common/JackMidiRawOutputWriteQueue.cpp new file mode 100644 index 00000000..6624c47c --- /dev/null +++ b/common/JackMidiRawOutputWriteQueue.cpp @@ -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 +#include + +#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 non_rt_ptr(non_rt_queue); + rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages); + std::auto_ptr 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; + } + } +} diff --git a/common/JackMidiRawOutputWriteQueue.h b/common/JackMidiRawOutputWriteQueue.h new file mode 100644 index 00000000..b5e336dc --- /dev/null +++ b/common/JackMidiRawOutputWriteQueue.h @@ -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 diff --git a/common/JackMidiReadQueue.cpp b/common/JackMidiReadQueue.cpp new file mode 100644 index 00000000..a6869691 --- /dev/null +++ b/common/JackMidiReadQueue.cpp @@ -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 +} diff --git a/common/JackMidiReadQueue.h b/common/JackMidiReadQueue.h new file mode 100644 index 00000000..0f4ca692 --- /dev/null +++ b/common/JackMidiReadQueue.h @@ -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 diff --git a/common/JackMidiReceiveQueue.cpp b/common/JackMidiReceiveQueue.cpp new file mode 100644 index 00000000..3eb3573e --- /dev/null +++ b/common/JackMidiReceiveQueue.cpp @@ -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 +} diff --git a/common/JackMidiReceiveQueue.h b/common/JackMidiReceiveQueue.h new file mode 100644 index 00000000..1d19c3c1 --- /dev/null +++ b/common/JackMidiReceiveQueue.h @@ -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 diff --git a/common/JackMidiSendQueue.cpp b/common/JackMidiSendQueue.cpp new file mode 100644 index 00000000..ac66d812 --- /dev/null +++ b/common/JackMidiSendQueue.cpp @@ -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(); +} diff --git a/common/JackMidiSendQueue.h b/common/JackMidiSendQueue.h new file mode 100644 index 00000000..0cb8df44 --- /dev/null +++ b/common/JackMidiSendQueue.h @@ -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 diff --git a/common/JackMidiUtil.cpp b/common/JackMidiUtil.cpp new file mode 100644 index 00000000..94871ce5 --- /dev/null +++ b/common/JackMidiUtil.cpp @@ -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); +} diff --git a/common/JackMidiUtil.h b/common/JackMidiUtil.h new file mode 100644 index 00000000..52db64c8 --- /dev/null +++ b/common/JackMidiUtil.h @@ -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 diff --git a/common/JackMidiWriteQueue.cpp b/common/JackMidiWriteQueue.cpp new file mode 100644 index 00000000..37fd9067 --- /dev/null +++ b/common/JackMidiWriteQueue.cpp @@ -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 +} diff --git a/common/JackMidiWriteQueue.h b/common/JackMidiWriteQueue.h new file mode 100644 index 00000000..8a51bafa --- /dev/null +++ b/common/JackMidiWriteQueue.h @@ -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 diff --git a/common/JackPhysicalMidiInput.cpp b/common/JackPhysicalMidiInput.cpp deleted file mode 100644 index 311dad0b..00000000 --- a/common/JackPhysicalMidiInput.cpp +++ /dev/null @@ -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 -#include -#include - -#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) -{ - jack_error("Discarding %d MIDI byte(s) - received sysex end without sysex " - "start (cable unplugged?)", bytes); -} - -void -JackPhysicalMidiInput::HandleWriteFailure(size_t bytes) -{ - jack_error("Failed to write a %d byte MIDI message to the port buffer", - bytes); -} - -void -JackPhysicalMidiInput::Process(jack_nframes_t frames) -{ - assert(port_buffer); - port_buffer->Reset(frames); - jack_nframes_t current_frame = 0; - size_t datum_size = sizeof(jack_midi_data_t); - for (;;) { - jack_midi_data_t datum; - current_frame = Receive(&datum, current_frame, frames); - if (current_frame >= frames) { - break; - } - - jack_log("JackPhysicalMidiInput::Process (%d) - Received '%x' byte", - current_frame, (unsigned int) datum); - - if (datum >= 0xf8) { - // Realtime - if (datum == 0xfd) { - HandleInvalidStatusByte(datum); - } else { - - jack_log("JackPhysicalMidiInput::Process - Writing realtime " - "event."); - - WriteByteEvent(current_frame, datum); - } - continue; - } - if (datum == 0xf7) { - // Sysex end - if (status_byte != 0xf0) { - HandleUnexpectedSysexEnd(buffered_bytes + unbuffered_bytes); - Clear(); - expected_data_bytes = 0; - status_byte = 0; - } else { - - jack_log("JackPhysicalMidiInput::Process - Writing sysex " - "event."); - - WriteBufferedSysexEvent(current_frame); - } - continue; - } - if (datum >= 0x80) { - - // We're handling a non-realtime status byte - - jack_log("JackPhysicalMidiInput::Process - Handling non-realtime " - "status byte."); - - if (buffered_bytes || unbuffered_bytes) { - HandleIncompleteMessage(buffered_bytes + unbuffered_bytes + 1); - Clear(); - } - status_byte = datum; - switch (datum & 0xf0) { - case 0x80: - case 0x90: - case 0xa0: - case 0xb0: - case 0xe0: - // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel - expected_data_bytes = 2; - break; - case 0xc0: - case 0xd0: - // Program Change, Channel Pressure - expected_data_bytes = 1; - break; - case 0xf0: - switch (datum) { - case 0xf0: - // Sysex message - expected_data_bytes = 0; - break; - case 0xf1: - case 0xf3: - // MTC Quarter frame, Song Select - expected_data_bytes = 1; - break; - case 0xf2: - // Song Position - expected_data_bytes = 2; - break; - case 0xf4: - case 0xf5: - // Undefined - HandleInvalidStatusByte(datum); - expected_data_bytes = 0; - status_byte = 0; - break; - case 0xf6: - // Tune Request - WriteByteEvent(current_frame, datum); - expected_data_bytes = 0; - status_byte = 0; - } - break; - } - continue; - } - - // We're handling a data byte - - jack_log("JackPhysicalMidiInput::Process - Buffering data byte."); - - if (jack_ringbuffer_write(input_ring, (const char *) &datum, - datum_size) == datum_size) { - buffered_bytes++; - } else { - unbuffered_bytes++; - } - unsigned long total_bytes = buffered_bytes + unbuffered_bytes; - assert((! expected_data_bytes) || - (total_bytes <= expected_data_bytes)); - if (total_bytes == expected_data_bytes) { - if (! unbuffered_bytes) { - - jack_log("JackPhysicalMidiInput::Process - Writing buffered " - "event."); - - WriteBufferedEvent(current_frame); - } else { - HandleBufferFailure(unbuffered_bytes, total_bytes); - Clear(); - } - if (status_byte >= 0xf0) { - expected_data_bytes = 0; - status_byte = 0; - } - } - } -} - -void -JackPhysicalMidiInput::WriteBufferedEvent(jack_nframes_t frame) -{ - assert(port_buffer && port_buffer->IsValid()); - size_t space = jack_ringbuffer_read_space(input_ring); - jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 1); - if (event) { - jack_ringbuffer_data_t vector[2]; - jack_ringbuffer_get_read_vector(input_ring, vector); - event[0] = status_byte; - size_t data_length_1 = vector[0].len; - memcpy(event + 1, vector[0].buf, data_length_1); - size_t data_length_2 = vector[1].len; - if (data_length_2) { - memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); - } - } else { - HandleWriteFailure(space + 1); - } - Clear(); -} - -void -JackPhysicalMidiInput::WriteBufferedSysexEvent(jack_nframes_t frame) -{ - assert(port_buffer && port_buffer->IsValid()); - size_t space = jack_ringbuffer_read_space(input_ring); - jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 2); - if (event) { - jack_ringbuffer_data_t vector[2]; - jack_ringbuffer_get_read_vector(input_ring, vector); - event[0] = status_byte; - size_t data_length_1 = vector[0].len; - memcpy(event + 1, vector[0].buf, data_length_1); - size_t data_length_2 = vector[1].len; - if (data_length_2) { - memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); - } - event[data_length_1 + data_length_2 + 1] = 0xf7; - } else { - HandleWriteFailure(space + 2); - } - Clear(); -} - -void -JackPhysicalMidiInput::WriteByteEvent(jack_nframes_t frame, - jack_midi_data_t datum) -{ - assert(port_buffer && port_buffer->IsValid()); - jack_midi_data_t *event = port_buffer->ReserveEvent(frame, 1); - if (event) { - event[0] = datum; - } else { - HandleWriteFailure(1); - } -} - -} diff --git a/common/JackPhysicalMidiInput.h b/common/JackPhysicalMidiInput.h deleted file mode 100644 index a05de6d0..00000000 --- a/common/JackPhysicalMidiInput.h +++ /dev/null @@ -1,146 +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. - -*/ - -#ifndef __JackPhysicalMidiInput__ -#define __JackPhysicalMidiInput__ - -#include "JackMidiPort.h" -#include "ringbuffer.h" - -namespace Jack { - - class JackPhysicalMidiInput { - - private: - - size_t buffered_bytes; - size_t expected_data_bytes; - jack_ringbuffer_t *input_ring; - JackMidiBuffer *port_buffer; - jack_midi_data_t status_byte; - size_t unbuffered_bytes; - - void - Clear(); - - void - WriteBufferedEvent(jack_nframes_t); - - void - WriteBufferedSysexEvent(jack_nframes_t); - - void - WriteByteEvent(jack_nframes_t, jack_midi_data_t); - - protected: - - /** - * Override to specify how to react when 1 or more bytes of a MIDI - * message are lost because there wasn't enough room in the input - * buffer. The first argument is the amount of bytes that couldn't be - * buffered, and the second argument is the total amount of bytes in - * the MIDI message. The default implementation calls 'jack_error' - * with a basic error message. - */ - - virtual void - HandleBufferFailure(size_t, size_t); - - /** - * Override to specify how to react when a new status byte is received - * before all of the data bytes in a message are received. The - * argument is the number of bytes being discarded. The default - * implementation calls 'jack_error' with a basic error message. - */ - - virtual void - HandleIncompleteMessage(size_t); - - /** - * Override to specify how to react when an invalid status byte (0xf4, - * 0xf5, 0xfd) is received. The argument contains the invalid status - * byte. The default implementation calls 'jack_error' with a basic - * error message. - */ - - virtual void - HandleInvalidStatusByte(jack_midi_data_t); - - /** - * Override to specify how to react when a sysex end byte (0xf7) is - * received without first receiving a sysex start byte (0xf0). The - * argument contains the amount of bytes that will be discarded. The - * default implementation calls 'jack_error' with a basic error - * message. - */ - - virtual void - HandleUnexpectedSysexEnd(size_t); - - /** - * Override to specify how to react when a MIDI message can not be - * written to the port buffer. The argument specifies the length of - * the MIDI message. The default implementation calls 'jack_error' - * with a basic error message. - */ - - virtual void - HandleWriteFailure(size_t); - - /** - * This method *must* be overridden to handle receiving MIDI bytes. - * The first argument is a pointer to the memory location at which the - * MIDI byte should be stored. The second argument is the last frame - * at which a MIDI byte was received, except at the beginning of the - * period when the value is 0. The third argument is the total number - * of frames in the period. The return value is the frame at which the - * MIDI byte is received at, or the value of the third argument is no - * more MIDI bytes can be received in this period. - */ - - virtual jack_nframes_t - Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t) = 0; - - public: - - JackPhysicalMidiInput(size_t buffer_size=1024); - virtual ~JackPhysicalMidiInput(); - - /** - * Called to process MIDI data during a period. - */ - - void - Process(jack_nframes_t); - - /** - * Set the MIDI buffer that will receive incoming messages. - */ - - inline void - SetPortBuffer(JackMidiBuffer *port_buffer) - { - this->port_buffer = port_buffer; - } - - }; - -} - -#endif diff --git a/common/JackPhysicalMidiOutput.cpp b/common/JackPhysicalMidiOutput.cpp deleted file mode 100644 index 8f41d443..00000000 --- a/common/JackPhysicalMidiOutput.cpp +++ /dev/null @@ -1,320 +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 - -#include "JackError.h" -#include "JackPhysicalMidiOutput.h" - -namespace Jack { - -JackPhysicalMidiOutput::JackPhysicalMidiOutput(size_t non_rt_buffer_size, - size_t rt_buffer_size) -{ - size_t datum_size = sizeof(jack_midi_data_t); - assert(non_rt_buffer_size > 0); - assert(rt_buffer_size > 0); - output_ring = jack_ringbuffer_create((non_rt_buffer_size + 1) * - datum_size); - if (! output_ring) { - throw std::bad_alloc(); - } - rt_output_ring = jack_ringbuffer_create((rt_buffer_size + 1) * - datum_size); - if (! rt_output_ring) { - jack_ringbuffer_free(output_ring); - throw std::bad_alloc(); - } - jack_ringbuffer_mlock(output_ring); - jack_ringbuffer_mlock(rt_output_ring); - running_status = 0; -} - -JackPhysicalMidiOutput::~JackPhysicalMidiOutput() -{ - jack_ringbuffer_free(output_ring); - jack_ringbuffer_free(rt_output_ring); -} - -jack_nframes_t -JackPhysicalMidiOutput::Advance(jack_nframes_t frame) -{ - return frame; -} - -inline jack_midi_data_t -JackPhysicalMidiOutput::ApplyRunningStatus(jack_midi_data_t **buffer, - size_t *size) -{ - - // Stolen and modified from alsa/midi_pack.h - - jack_midi_data_t status = (*buffer)[0]; - if ((status >= 0x80) && (status < 0xf0)) { - if (status == running_status) { - (*buffer)++; - (*size)--; - } else { - running_status = status; - } - } else if (status < 0xf8) { - running_status = 0; - } - return status; -} - -void -JackPhysicalMidiOutput::HandleEventLoss(JackMidiEvent *event) -{ - jack_error("%d byte MIDI event lost", event->size); -} - -void -JackPhysicalMidiOutput::Process(jack_nframes_t frames) -{ - assert(port_buffer); - jack_nframes_t current_frame = Advance(0); - jack_nframes_t current_midi_event = 0; - jack_midi_data_t datum; - size_t datum_size = sizeof(jack_midi_data_t); - JackMidiEvent *midi_event; - jack_midi_data_t *midi_event_buffer; - size_t midi_event_size; - jack_nframes_t midi_events = port_buffer->event_count; - - // First, send any realtime MIDI data that's left from last cycle. - - if ((current_frame < frames) && - jack_ringbuffer_read_space(rt_output_ring)) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sending buffered " - "realtime data from last period.", current_frame); - - current_frame = SendBufferedData(rt_output_ring, current_frame, - frames); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", current_frame); - - } - - // Iterate through the events in this cycle. - - for (; (current_midi_event < midi_events) && (current_frame < frames); - current_midi_event++) { - - // Once we're inside this loop, we know that the realtime buffer - // is empty. As long as we don't find a realtime message, we can - // concentrate on sending non-realtime data. - - midi_event = &(port_buffer->events[current_midi_event]); - jack_nframes_t midi_event_time = midi_event->time; - midi_event_buffer = midi_event->GetData(port_buffer); - midi_event_size = midi_event->size; - datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); - if (current_frame < midi_event_time) { - - // We have time before this event is scheduled to be sent. - // Send data in the non-realtime buffer. - - if (jack_ringbuffer_read_space(output_ring)) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " - "buffered non-realtime data from last period.", - current_frame); - - current_frame = SendBufferedData(output_ring, current_frame, - midi_event_time); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", - current_frame); - - } - if (current_frame < midi_event_time) { - - // We _still_ have time before this event is scheduled to - // be sent. Let's send as much of this event as we can - // (save for one byte, which will need to be sent at or - // after its scheduled time). First though, we need to - // make sure that we can buffer this data if we need to. - // Otherwise, we might start sending a message that we - // can't finish. - - if (midi_event_size > 1) { - if (jack_ringbuffer_write_space(output_ring) < - ((midi_event_size - 1) * datum_size)) { - HandleEventLoss(midi_event); - continue; - } - - // Send as much of the event as possible (save for one - // byte). - - do { - - jack_log("JackPhysicalMidiOutput::Process (%d) - " - "Sending unbuffered event byte early.", - current_frame); - - current_frame = Send(current_frame, - *midi_event_buffer); - - jack_log("JackPhysicalMidiOutput::Process (%d) - " - "Sent.", current_frame); - - midi_event_buffer++; - midi_event_size--; - if (current_frame >= midi_event_time) { - - // The event we're processing must be a - // non-realtime event. It has more than one - // byte. - - goto buffer_non_realtime_data; - } - } while (midi_event_size > 1); - } - - jack_log("JackPhysicalMidiOutput::Process (%d) - Advancing to " - ">= %d", current_frame, midi_event_time); - - current_frame = Advance(midi_event_time); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Advanced.", - current_frame); - - } - } - - // If the event is realtime, then we'll send the event now. - // Otherwise, we attempt to put the rest of the event bytes in the - // non-realtime buffer. - - if (datum >= 0xf8) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " - "unbuffered realtime event.", current_frame); - - current_frame = Send(current_frame, datum); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", - current_frame); - - } else if (jack_ringbuffer_write_space(output_ring) >= - (midi_event_size * datum_size)) { - buffer_non_realtime_data: - - jack_log("JackPhysicalMidiOutput::Process (%d) - Buffering %d " - "byte(s) of non-realtime data.", current_frame, - midi_event_size); - - jack_ringbuffer_write(output_ring, - (const char *) midi_event_buffer, - midi_event_size); - } else { - HandleEventLoss(midi_event); - } - } - - if (current_frame < frames) { - - // If we have time left to send data, then we know that all of the - // data in the realtime buffer has been sent, and that all of the - // non-realtime messages have either been sent, or buffered. We - // use whatever time is left to send data in the non-realtime - // buffer. - - if (jack_ringbuffer_read_space(output_ring)) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - All events " - "processed. Sending buffered non-realtime data.", - current_frame); - - current_frame = SendBufferedData(output_ring, current_frame, - frames); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", - current_frame); - - } - } else { - - // Since we have no time left, we need to put all remaining midi - // events in their appropriate buffers, and send them next period. - - for (; current_midi_event < midi_events; current_midi_event++) { - midi_event = &(port_buffer->events[current_midi_event]); - midi_event_buffer = midi_event->GetData(port_buffer); - midi_event_size = midi_event->size; - datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); - if (datum >= 0xf8) { - - // Realtime. - - if (jack_ringbuffer_write_space(rt_output_ring) >= - datum_size) { - - jack_log("JackPhysicalMidiOutput::Process - Buffering " - "realtime event for next period."); - - jack_ringbuffer_write(rt_output_ring, - (const char *) &datum, datum_size); - continue; - } - } else { - - // Non-realtime. - - if (jack_ringbuffer_write_space(output_ring) >= - (midi_event_size * datum_size)) { - - jack_log("JackPhysicalMidiOutput::Process - Buffering " - "non-realtime event for next period."); - - jack_ringbuffer_write(output_ring, - (const char *) midi_event_buffer, - midi_event_size * datum_size); - continue; - } - } - HandleEventLoss(midi_event); - } - } -} - -jack_nframes_t -JackPhysicalMidiOutput::SendBufferedData(jack_ringbuffer_t *buffer, - jack_nframes_t current_frame, - jack_nframes_t boundary) -{ - assert(buffer); - assert(current_frame < boundary); - size_t datum_size = sizeof(jack_midi_data_t); - size_t data_length = jack_ringbuffer_read_space(buffer) / datum_size; - for (size_t i = 0; i < data_length; i++) { - jack_midi_data_t datum; - jack_ringbuffer_read(buffer, (char *) &datum, datum_size); - current_frame = Send(current_frame, datum); - if (current_frame >= boundary) { - break; - } - } - return current_frame; -} - -} diff --git a/common/JackPhysicalMidiOutput.h b/common/JackPhysicalMidiOutput.h deleted file mode 100644 index 9b4804b7..00000000 --- a/common/JackPhysicalMidiOutput.h +++ /dev/null @@ -1,118 +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. - -*/ - -#ifndef __JackPhysicalMidiOutput__ -#define __JackPhysicalMidiOutput__ - -#include "JackMidiPort.h" -#include "ringbuffer.h" - -namespace Jack { - - class JackPhysicalMidiOutput { - - private: - - jack_midi_data_t - ApplyRunningStatus(jack_midi_data_t **, size_t *); - - jack_ringbuffer_t *output_ring; - JackMidiBuffer *port_buffer; - jack_ringbuffer_t *rt_output_ring; - jack_midi_data_t running_status; - - protected: - - /** - * Override to specify the next frame at which a midi byte can be sent. - * The returned frame must be greater than or equal to the frame - * argument. The default returns the frame passed to it. - */ - - virtual jack_nframes_t - Advance(jack_nframes_t); - - /** - * Override to customize how to react when a MIDI event can't be - * buffered and can't be sent immediately. The default calls - * 'jack_error' and specifies the number of bytes lost. - */ - - virtual void - HandleEventLoss(JackMidiEvent *); - - /** - * This method *must* be overridden to specify what happens when a MIDI - * byte is sent at the specfied frame. The frame argument specifies - * the frame at which the MIDI byte should be sent, and the second - * argument specifies the byte itself. The return value is the next - * frame at which a MIDI byte can be sent, and must be greater than or - * equal to the frame argument. - */ - - virtual jack_nframes_t - Send(jack_nframes_t, jack_midi_data_t) = 0; - - /** - * Override to optimize behavior when sending MIDI data that's in the - * ringbuffer. The first frame argument is the current frame, and the - * second frame argument is the boundary frame. The function returns - * the next frame at which MIDI data can be sent, regardless of whether - * or not the boundary is reached. The default implementation calls - * 'Send' with each byte in the ringbuffer until either the ringbuffer - * is empty, or a frame beyond the boundary frame is returned by - * 'Send'. - */ - - virtual jack_nframes_t - SendBufferedData(jack_ringbuffer_t *, jack_nframes_t, jack_nframes_t); - - public: - - /** - * The non-realtime buffer size and the realtime buffer size are both - * optional arguments. - */ - - JackPhysicalMidiOutput(size_t non_rt_buffer_size=1024, - size_t rt_buffer_size=64); - virtual ~JackPhysicalMidiOutput(); - - /** - * Called to process MIDI data during a period. - */ - - void - Process(jack_nframes_t); - - /** - * Set the MIDI buffer that will contain the outgoing MIDI messages. - */ - - inline void - SetPortBuffer(JackMidiBuffer *port_buffer) - { - this->port_buffer = port_buffer; - } - - }; - -} - -#endif diff --git a/common/wscript b/common/wscript index 31542b17..8b10f1b7 100644 --- a/common/wscript +++ b/common/wscript @@ -130,8 +130,21 @@ def build(bld): 'JackNetTool.cpp', 'JackNetInterface.cpp', 'JackArgParser.cpp', - 'JackPhysicalMidiInput.cpp', - 'JackPhysicalMidiOutput.cpp', + + #'JackPhysicalMidiInput.cpp', + #'JackPhysicalMidiOutput.cpp', + + 'JackMidiAsyncQueue.cpp', + 'JackMidiAsyncWaitQueue.cpp', + 'JackMidiBufferReadQueue.cpp', + 'JackMidiBufferWriteQueue.cpp', + 'JackMidiRawInputWriteQueue.cpp', + 'JackMidiRawOutputWriteQueue.cpp', + 'JackMidiReadQueue.cpp', + 'JackMidiReceiveQueue.cpp', + 'JackMidiSendQueue.cpp', + 'JackMidiUtil.cpp', + 'JackMidiWriteQueue.cpp' ] if bld.env['IS_LINUX']: diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c new file mode 100644 index 00000000..2d9e1686 --- /dev/null +++ b/example-clients/midi_latency_test.c @@ -0,0 +1,588 @@ +/* +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. + +*/ + +/* + * This program is used to measure MIDI latency and jitter. It writes MIDI + * messages to one port and calculates how long it takes before it reads the + * same MIDI message over another port. It was written to calculate the + * latency and jitter of hardware and JACK hardware drivers, but might have + * other practical applications. + * + * The latency results of the program include the latency introduced by the + * JACK system. Because JACK has sample accurate MIDI, the same latency + * imposed on audio is also imposed on MIDI going through the system. Make + * sure you take this into account before complaining to me or (*especially*) + * other JACK developers about reported MIDI latency. + * + * The jitter results are a little more interesting. The program attempts to + * calculate 'average jitter' and 'peak jitter', as defined here: + * + * http://openmuse.org/transport/fidelity.html + * + * It also outputs a jitter plot, which gives you a more specific idea about + * the MIDI jitter for the ports you're testing. This is useful for catching + * extreme jitter values, and for analyzing the amount of truth in the + * technical specifications for your MIDI interface(s). :) + * + * This program is loosely based on 'alsa-midi-latency-test' in the ALSA test + * suite. + * + * To port this program to non-POSIX platforms, you'll have to include + * implementations for mutexes, semaphores, and command-line argument handling. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define ABS(x) (((x) >= 0) ? (x) : (-(x))) + +const char *ERROR_UNEXPECTED = "in port received unexpected MIDI message"; +const char *ERROR_UNEXPECTED_EXTRA = "received more than one MIDI message"; +const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer"; +const char *ERROR_TIMEOUT = "timed out while waiting for MIDI message"; +const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve"; +const char *SOURCE_PROCESS = "handle_process"; +const char *SOURCE_TRYLOCK = "pthread_mutex_trylock"; +const char *SOURCE_UNLOCK = "pthread_mutex_unlock"; + +jack_client_t *client; +const char *error_message; +const char *error_source; +jack_nframes_t highest_latency; +jack_time_t highest_latency_time; +jack_latency_range_t in_latency_range; +jack_port_t *in_port; +jack_nframes_t last_activity; +jack_time_t last_activity_time; +jack_time_t *latency_time_values; +jack_nframes_t *latency_values; +jack_nframes_t lowest_latency; +jack_time_t lowest_latency_time; +jack_midi_data_t *message_1; +jack_midi_data_t *message_2; +size_t messages_processed; +size_t message_size; +jack_latency_range_t out_latency_range; +jack_port_t *out_port; +int process_state; +char *program_name; +jack_port_t *remote_in_port; +jack_port_t *remote_out_port; +size_t samples; +sem_t semaphore; +pthread_mutex_t start_mutex; +int timeout; +jack_nframes_t total_latency; +jack_time_t total_latency_time; +size_t unexpected_messages; + +static void +output_error(const char *source, const char *message); + +static void +output_usage(); + +static void +set_process_error(const char *source, const char *message); + +static void +die(char *source, char *error_message) +{ + output_error(source, error_message); + output_usage(); + exit(EXIT_FAILURE); +} + +static void +handle_info(const char *message) +{ + /* Suppress info */ +} + +static int +handle_process(jack_nframes_t frames, void *arg) +{ + jack_midi_data_t *buffer; + int code; + jack_midi_event_t event; + jack_nframes_t event_count; + jack_nframes_t event_time; + jack_nframes_t frame; + size_t i; + jack_nframes_t last_frame_time; + jack_midi_data_t *message; + void *port_buffer; + jack_time_t time; + switch (process_state) { + + case 0: + /* State: initializing */ + code = pthread_mutex_trylock(&start_mutex); + if (code) { + if (code != EBUSY) { + set_process_error(SOURCE_TRYLOCK, strerror(code)); + } + break; + } + code = pthread_mutex_unlock(&start_mutex); + if (code) { + set_process_error(SOURCE_UNLOCK, strerror(code)); + break; + } + highest_latency = 0; + lowest_latency = 0; + messages_processed = 0; + process_state = 1; + total_latency = 0; + total_latency_time = 0; + unexpected_messages = 0; + jack_port_get_latency_range(remote_in_port, JackCaptureLatency, + &in_latency_range); + jack_port_get_latency_range(remote_out_port, JackPlaybackLatency, + &out_latency_range); + goto send_message; + + case 1: + /* State: processing */ + jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames)); + port_buffer = jack_port_get_buffer(in_port, frames); + event_count = jack_midi_get_event_count(port_buffer); + last_frame_time = jack_last_frame_time(client); + for (i = 0; i < event_count; i++) { + jack_midi_event_get(&event, port_buffer, i); + message = (messages_processed % 2) ? message_2 : message_1; + if ((event.size == message_size) && + (! memcmp(message, event.buffer, + message_size * sizeof(jack_midi_data_t)))) { + goto found_message; + } + unexpected_messages++; + } + jack_time_t microseconds = + jack_frames_to_time(client, last_frame_time) - last_activity_time; + if ((microseconds / 1000000) >= timeout) { + set_process_error(SOURCE_PROCESS, ERROR_TIMEOUT); + } + break; + found_message: + event_time = last_frame_time + event.time; + frame = event_time - last_activity; + time = jack_frames_to_time(client, event_time) - last_activity_time; + if ((! highest_latency) || (frame > highest_latency)) { + highest_latency = frame; + highest_latency_time = time; + } + if ((! lowest_latency) || (frame < lowest_latency)) { + lowest_latency = frame; + lowest_latency_time = time; + } + latency_time_values[messages_processed] = time; + latency_values[messages_processed] = frame; + total_latency += frame; + total_latency_time += time; + messages_processed++; + if (messages_processed == samples) { + process_state = 2; + sem_post(&semaphore); + break; + } + send_message: + frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames); + if (frame == frames) { + frame--; + } + port_buffer = jack_port_get_buffer(out_port, frames); + jack_midi_clear_buffer(port_buffer); + buffer = jack_midi_event_reserve(port_buffer, frame, message_size); + if (buffer == NULL) { + set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE); + break; + } + message = (messages_processed % 2) ? message_2 : message_1; + memcpy(buffer, message, message_size * sizeof(jack_midi_data_t)); + last_activity = jack_last_frame_time(client) + frame; + last_activity_time = jack_frames_to_time(client, last_activity); + + case 2: + /* State: finished - do nothing */ + + case -1: + /* State: error - do nothing */ + ; + + } + return 0; +} + +static void +handle_shutdown(void *arg) +{ + set_process_error("handle_shutdown", "The JACK server has been shutdown"); +} + +static void +output_error(const char *source, const char *message) +{ + fprintf(stderr, "%s: %s: %s\n", program_name, source, message); +} + +static void +output_usage() +{ + fprintf(stderr, "Usage: %s [options] out-port-name in-port-name\n\n" + "\t-h, --help print program usage\n" + "\t-m, --message-size=size set size of MIDI messages to send\n" + "\t-s, --samples=n number of MIDI messages to send\n" + "\t-t, --timeout=seconds wait time before giving up on message\n" + "\n", program_name); +} + +static unsigned long +parse_positive_number_arg(char *s, char *name) +{ + char *end_ptr; + unsigned long result; + errno = 0; + result = strtoul(s, &end_ptr, 10); + if (errno) { + die(name, strerror(errno)); + } + if (*s == '\0') { + die(name, "argument value cannot be empty"); + } + if (*end_ptr != '\0') { + die(name, "invalid value"); + } + if (! result) { + die(name, "must be a positive number"); + } + return result; +} + +static void +set_process_error(const char *source, const char *message) +{ + error_source = source; + error_message = message; + process_state = -1; + sem_post(&semaphore); +} + +int +main(int argc, char **argv) +{ + int code; + size_t jitter_plot[101]; + int long_index = 0; + struct option long_options[] = { + {"help", 0, NULL, 'h'}, + {"message-size", 1, NULL, 'm'}, + {"samples", 1, NULL, 's'}, + {"timeout", 1, NULL, 't'} + }; + char *option_string = "hm:s:t:"; + int show_usage = 0; + error_message = NULL; + message_size = 3; + program_name = argv[0]; + samples = 1024; + timeout = 5; + for (;;) { + char c = getopt_long(argc, argv, option_string, long_options, + &long_index); + switch (c) { + case 'h': + show_usage = 1; + break; + case 'm': + message_size = parse_positive_number_arg(optarg, "message-size"); + break; + case 's': + samples = parse_positive_number_arg(optarg, "samples"); + break; + case 't': + timeout = parse_positive_number_arg(optarg, "timeout"); + break; + default: + { + char *s = "'- '"; + s[2] = c; + die(s, "invalid switch"); + } + case -1: + if (show_usage) { + output_usage(); + exit(EXIT_SUCCESS); + } + goto parse_port_names; + case 1: + /* end of switch :) */ + ; + } + } + parse_port_names: + if ((argc - optind) != 2) { + output_usage(); + return EXIT_FAILURE; + } + latency_values = malloc(sizeof(jack_nframes_t) * samples); + if (latency_values == NULL) { + error_message = strerror(errno); + error_source = "malloc"; + goto show_error; + } + latency_time_values = malloc(sizeof(jack_time_t) * samples); + if (latency_time_values == NULL) { + error_message = strerror(errno); + error_source = "malloc"; + goto free_latency_values; + } + message_1 = malloc(message_size * sizeof(jack_midi_data_t)); + if (message_1 == NULL) { + error_message = strerror(errno); + error_source = "malloc"; + goto free_latency_time_values; + } + message_2 = malloc(message_size * sizeof(jack_midi_data_t)); + if (message_2 == NULL) { + error_message = strerror(errno); + error_source = "malloc"; + goto free_message_1; + } + switch (message_size) { + case 1: + message_1[0] = 0xf6; + message_2[0] = 0xf8; + break; + case 2: + message_1[0] = 0xc0; + message_1[1] = 0x00; + message_2[0] = 0xd0; + message_2[1] = 0x7f; + break; + case 3: + message_1[0] = 0x80; + message_1[1] = 0x00; + message_1[2] = 0x00; + message_2[0] = 0x90; + message_2[1] = 0x7f; + message_2[2] = 0x7f; + break; + default: + message_1[0] = 0xf0; + memset(message_1 + 1, 0, + (message_size - 2) * sizeof(jack_midi_data_t)); + message_1[message_size - 1] = 0xf7; + message_2[0] = 0xf0; + memset(message_2 + 1, 0x7f, + (message_size - 2) * sizeof(jack_midi_data_t)); + message_2[message_size - 1] = 0xf7; + } + client = jack_client_open(program_name, JackNullOption, NULL); + if (client == NULL) { + error_message = "failed to open JACK client"; + error_source = "jack_client_open"; + goto free_message_2; + } + remote_in_port = jack_port_by_name(client, argv[optind + 1]); + if (remote_in_port == NULL) { + error_message = "invalid port name"; + error_source = argv[optind + 1]; + goto close_client; + } + remote_out_port = jack_port_by_name(client, argv[optind]); + if (remote_out_port == NULL) { + error_message = "invalid port name"; + error_source = argv[optind]; + goto close_client; + } + in_port = jack_port_register(client, "in", JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput, 0); + if (in_port == NULL) { + error_message = "failed to register MIDI-in port"; + error_source = "jack_port_register"; + goto close_client; + } + out_port = jack_port_register(client, "out", JACK_DEFAULT_MIDI_TYPE, + JackPortIsOutput, 0); + if (out_port == NULL) { + error_message = "failed to register MIDI-out port"; + error_source = "jack_port_register"; + goto unregister_in_port; + } + if (jack_set_process_callback(client, handle_process, NULL)) { + error_message = "failed to set process callback"; + error_source = "jack_set_process_callback"; + goto unregister_out_port; + } + jack_on_shutdown(client, handle_shutdown, NULL); + jack_set_info_function(handle_info); + process_state = 0; + if (sem_init(&semaphore, 0, 0)) { + error_message = strerror(errno); + error_source = "sem_init"; + goto unregister_out_port; + } + code = pthread_mutex_init(&start_mutex, NULL); + if (code) { + error_message = strerror(errno); + error_source = "pthread_mutex_init"; + goto destroy_semaphore; + } + code = pthread_mutex_trylock(&start_mutex); + if (code) { + error_message = strerror(code); + error_source = "pthread_mutex_trylock"; + goto destroy_mutex; + } + if (jack_activate(client)) { + error_message = "could not activate client"; + error_source = "jack_activate"; + goto destroy_mutex; + } + if (jack_connect(client, jack_port_name(out_port), + jack_port_name(remote_out_port))) { + error_message = "could not connect MIDI out port"; + error_source = "jack_connect"; + goto deactivate_client; + } + if (jack_connect(client, jack_port_name(remote_in_port), + jack_port_name(in_port))) { + error_message = "could not connect MIDI in port"; + error_source = "jack_connect"; + goto deactivate_client; + } + jack_port_get_latency_range(remote_in_port, JackCaptureLatency, + &in_latency_range); + jack_port_get_latency_range(remote_out_port, JackPlaybackLatency, + &out_latency_range); + code = pthread_mutex_unlock(&start_mutex); + if (code) { + error_message = strerror(code); + error_source = "pthread_mutex_unlock"; + goto deactivate_client; + } + if (sem_wait(&semaphore)) { + error_message = strerror(errno); + error_source = "sem_wait"; + goto deactivate_client; + } + if (process_state == 2) { + double average_latency = ((double) total_latency) / samples; + double average_latency_time = total_latency_time / samples; + double high_jitter = highest_latency - average_latency; + size_t i; + double low_jitter = average_latency - lowest_latency; + double peak_jitter; + double peak_jitter_time; + double sample_rate = (double) jack_get_sample_rate(client); + jack_nframes_t total_jitter = 0; + jack_time_t total_jitter_time = 0; + if (high_jitter > low_jitter) { + peak_jitter = high_jitter; + peak_jitter_time = highest_latency_time - average_latency_time; + } else { + peak_jitter = low_jitter; + peak_jitter_time = average_latency_time - lowest_latency_time; + } + for (i = 0; i <= 100; i++) { + jitter_plot[i] = 0; + } + for (i = 0; i < samples; i++) { + double jitter_time = ABS(average_latency_time - + ((double) latency_time_values[i])); + if (jitter_time >= 10000.0) { + (jitter_plot[100])++; + } else { + (jitter_plot[(int) (jitter_time / 100.0)])++; + } + total_jitter += ABS(average_latency - + ((double) latency_values[i])); + total_jitter_time += jitter_time; + } + printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n" + "Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n" + "Average latency: %.2f ms (%.2f frames)\n" + "Best latency: %.2f ms (%u frames)\n" + "Worst latency: %.2f ms (%u frames)\n" + "Peak MIDI jitter: %.2f ms (%.2f frames)\n" + "Average MIDI jitter: %.2f ms (%.2f frames)\n", + (out_latency_range.min / sample_rate) * 1000.0, + (out_latency_range.max / sample_rate) * 1000.0, + out_latency_range.min, out_latency_range.max, + (in_latency_range.min / sample_rate) * 1000.0, + (in_latency_range.max / sample_rate) * 1000.0, + in_latency_range.min, in_latency_range.max, + average_latency_time / 1000.0, average_latency, + lowest_latency_time / 1000.0, lowest_latency, + highest_latency_time / 1000.0, highest_latency, + peak_jitter_time / 1000.0, peak_jitter, + (total_jitter_time / 1000.0) / samples, + ((double) total_jitter) / samples); + printf("\nJitter Plot:\n"); + for (i = 0; i < 100; i++) { + if (jitter_plot[i]) { + printf("%.1f - %.1f ms: %u\n", ((float) i) / 10.0, + ((float) (i + 1)) / 10.0, jitter_plot[i]); + } + } + if (jitter_plot[100]) { + printf(" > 10 ms: %u\n", jitter_plot[100]); + } + if (unexpected_messages) { + printf("\nUnexpected messages received: %d\n", + unexpected_messages); + } + } + deactivate_client: + jack_deactivate(client); + destroy_mutex: + pthread_mutex_destroy(&start_mutex); + destroy_semaphore: + sem_destroy(&semaphore); + unregister_out_port: + jack_port_unregister(client, out_port); + unregister_in_port: + jack_port_unregister(client, in_port); + close_client: + jack_client_close(client); + free_message_2: + free(message_2); + free_message_1: + free(message_1); + free_latency_time_values: + free(latency_time_values); + free_latency_values: + free(latency_values); + if (error_message != NULL) { + show_error: + output_error(error_source, error_message); + exit(EXIT_FAILURE); + } + return EXIT_SUCCESS; +} diff --git a/example-clients/wscript b/example-clients/wscript index 9d477e35..8f6dae62 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -27,6 +27,7 @@ example_programs = { 'jack_server_control' : 'server_control.cpp', 'jack_latent_client' : 'latent_client.c', 'jack_midi_dump' : 'midi_dump.c', + 'jack_midi_latency_test' : 'midi_latency_test.c' } example_libs = { diff --git a/linux/firewire/JackFFADODriver.cpp b/linux/firewire/JackFFADODriver.cpp index 0966b1da..e0a2e556 100644 --- a/linux/firewire/JackFFADODriver.cpp +++ b/linux/firewire/JackFFADODriver.cpp @@ -36,8 +36,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include "JackFFADODriver.h" -#include "JackFFADOMidiInput.h" -#include "JackFFADOMidiOutput.h" +#include "JackFFADOMidiInputPort.h" +#include "JackFFADOMidiOutputPort.h" #include "JackEngineControl.h" #include "JackClientControl.h" #include "JackPort.h" @@ -94,14 +94,9 @@ JackFFADODriver::ffado_driver_read (ffado_driver_t * driver, jack_nframes_t nfra /* process the midi data */ for (chn = 0; chn < driver->capture_nchannels; chn++) { if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { - JackFFADOMidiInput *midi_input = (JackFFADOMidiInput *) driver->capture_channels[chn].midi_input; + JackFFADOMidiInputPort *midi_input = (JackFFADOMidiInputPort *) driver->capture_channels[chn].midi_input; JackMidiBuffer *buffer = (JackMidiBuffer *) fGraphManager->GetBuffer(fCapturePortList[chn], nframes); - if (! buffer) { - continue; - } - midi_input->SetInputBuffer(driver->capture_channels[chn].midi_buffer); - midi_input->SetPortBuffer(buffer); - midi_input->Process(nframes); + midi_input->Process(buffer, driver->capture_channels[chn].midi_buffer, nframes); } } @@ -138,16 +133,9 @@ JackFFADODriver::ffado_driver_write (ffado_driver_t * driver, jack_nframes_t nfr memset(midi_buffer, 0, nframes * sizeof(uint32_t)); buf = (jack_default_audio_sample_t *) fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes); ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(midi_buffer)); - /* if the returned buffer is invalid, continue */ - if (!buf) { - ffado_streaming_playback_stream_onoff(driver->dev, chn, 0); - continue; - } - ffado_streaming_playback_stream_onoff(driver->dev, chn, 1); - JackFFADOMidiOutput *midi_output = (JackFFADOMidiOutput *) driver->playback_channels[chn].midi_output; - midi_output->SetPortBuffer((JackMidiBuffer *) buf); - midi_output->SetOutputBuffer(midi_buffer); - midi_output->Process(nframes); + ffado_streaming_playback_stream_onoff(driver->dev, chn, buf ? 1 : 0); + JackFFADOMidiOutputPort *midi_output = (JackFFADOMidiOutputPort *) driver->playback_channels[chn].midi_output; + midi_output->Process((JackMidiBuffer *) buf, midi_buffer, nframes); } else { // always have a valid buffer ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(driver->nullbuffer)); @@ -155,9 +143,7 @@ JackFFADODriver::ffado_driver_write (ffado_driver_t * driver, jack_nframes_t nfr } } } - ffado_streaming_transfer_playback_buffers(driver->dev); - printExit(); return 0; } @@ -476,7 +462,7 @@ int JackFFADODriver::Attach() printError(" cannot enable port %s", buf); } - driver->capture_channels[chn].midi_input = new JackFFADOMidiInput(); + driver->capture_channels[chn].midi_input = new JackFFADOMidiInputPort(); // setup the midi buffer driver->capture_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); @@ -557,12 +543,12 @@ int JackFFADODriver::Attach() // This constructor optionally accepts arguments for the // non-realtime buffer size and the realtime buffer size. Ideally, // these would become command-line options for the FFADO driver. - driver->playback_channels[chn].midi_output = new JackFFADOMidiOutput(); + driver->playback_channels[chn].midi_output = new JackFFADOMidiOutputPort(); driver->playback_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); port = fGraphManager->GetPort(port_index); - range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)) + driver->playback_frame_latency; + range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)) + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + driver->playback_frame_latency; port->SetLatencyRange(JackPlaybackLatency, &range); fPlaybackPortList[chn] = port_index; jack_log("JackFFADODriver::Attach fPlaybackPortList[i] %ld ", port_index); @@ -600,7 +586,7 @@ int JackFFADODriver::Detach() if (driver->capture_channels[chn].midi_buffer) free(driver->capture_channels[chn].midi_buffer); if (driver->capture_channels[chn].midi_input) - delete ((JackFFADOMidiInput *) (driver->capture_channels[chn].midi_input)); + delete ((JackFFADOMidiInputPort *) (driver->capture_channels[chn].midi_input)); } free(driver->capture_channels); @@ -608,7 +594,7 @@ int JackFFADODriver::Detach() if (driver->playback_channels[chn].midi_buffer) free(driver->playback_channels[chn].midi_buffer); if (driver->playback_channels[chn].midi_output) - delete ((JackFFADOMidiOutput *) (driver->playback_channels[chn].midi_output)); + delete ((JackFFADOMidiOutputPort *) (driver->playback_channels[chn].midi_output)); } free(driver->playback_channels); diff --git a/linux/firewire/JackFFADOMidiInput.cpp b/linux/firewire/JackFFADOMidiInput.cpp deleted file mode 100644 index 38ed539b..00000000 --- a/linux/firewire/JackFFADOMidiInput.cpp +++ /dev/null @@ -1,59 +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 - -#include "JackFFADOMidiInput.h" - -namespace Jack { - -JackFFADOMidiInput::JackFFADOMidiInput(size_t buffer_size): - JackPhysicalMidiInput(buffer_size) -{ - new_period = true; -} - -JackFFADOMidiInput::~JackFFADOMidiInput() -{ - // Empty -} - -jack_nframes_t -JackFFADOMidiInput::Receive(jack_midi_data_t *datum, - jack_nframes_t current_frame, - jack_nframes_t total_frames) -{ - assert(input_buffer); - if (! new_period) { - current_frame += 8; - } else { - new_period = false; - } - for (; current_frame < total_frames; current_frame += 8) { - uint32_t data = input_buffer[current_frame]; - if (data & 0xff000000) { - *datum = (jack_midi_data_t) (data & 0xff); - return current_frame; - } - } - new_period = true; - return total_frames; -} - -} diff --git a/linux/firewire/JackFFADOMidiInputPort.cpp b/linux/firewire/JackFFADOMidiInputPort.cpp new file mode 100644 index 00000000..3923bbe0 --- /dev/null +++ b/linux/firewire/JackFFADOMidiInputPort.cpp @@ -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 + +#include "JackFFADOMidiInputPort.h" +#include "JackMidiUtil.h" + +using Jack::JackFFADOMidiInputPort; + +JackFFADOMidiInputPort::JackFFADOMidiInputPort(size_t max_bytes) +{ + event = 0; + receive_queue = new JackFFADOMidiReceiveQueue(); + std::auto_ptr receive_queue_ptr(receive_queue); + write_queue = new JackMidiBufferWriteQueue(); + std::auto_ptr write_queue_ptr(write_queue); + raw_queue = new JackMidiRawInputWriteQueue(write_queue, max_bytes, + max_bytes); + write_queue_ptr.release(); + receive_queue_ptr.release(); +} + +JackFFADOMidiInputPort::~JackFFADOMidiInputPort() +{ + delete raw_queue; + delete receive_queue; + delete write_queue; +} + +void +JackFFADOMidiInputPort::Process(JackMidiBuffer *port_buffer, + uint32_t *input_buffer, jack_nframes_t frames) +{ + receive_queue->ResetInputBuffer(input_buffer, frames); + write_queue->ResetMidiBuffer(port_buffer, frames); + jack_nframes_t boundary_frame = GetLastFrame() + frames; + if (! event) { + event = receive_queue->DequeueEvent(); + } + for (; event; event = receive_queue->DequeueEvent()) { + switch (raw_queue->EnqueueEvent(event)) { + case JackMidiWriteQueue::BUFFER_FULL: + + // Processing events early might free up some space in the raw + // input queue. + + raw_queue->Process(boundary_frame); + switch (raw_queue->EnqueueEvent(event)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + // This shouldn't really happen. It indicates a bug if it + // does. + jack_error("JackFFADOMidiInputPort::Process - **BUG** " + "JackMidiRawInputWriteQueue::EnqueueEvent returned " + "`BUFFER_FULL`, and then returned " + "`BUFFER_TOO_SMALL` after a `Process()` call."); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + return; + } + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackFFADOMidiInputPort::Process - The write queue " + "couldn't enqueue a %d-byte event. Dropping event.", + event->size); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + // This is here to stop compliers from warning us about not + // handling enumeration values. + ; + } + break; + } + raw_queue->Process(boundary_frame); +} diff --git a/linux/firewire/JackFFADOMidiInputPort.h b/linux/firewire/JackFFADOMidiInputPort.h new file mode 100644 index 00000000..f4c70a80 --- /dev/null +++ b/linux/firewire/JackFFADOMidiInputPort.h @@ -0,0 +1,51 @@ +/* +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 __JackFFADOMidiInputPort__ +#define __JackFFADOMidiInputPort__ + +#include "JackFFADOMidiReceiveQueue.h" +#include "JackMidiBufferWriteQueue.h" +#include "JackMidiRawInputWriteQueue.h" + +namespace Jack { + + class JackFFADOMidiInputPort { + + private: + + jack_midi_event_t *event; + JackMidiRawInputWriteQueue *raw_queue; + JackFFADOMidiReceiveQueue *receive_queue; + JackMidiBufferWriteQueue *write_queue; + + public: + + JackFFADOMidiInputPort(size_t max_bytes=4096); + ~JackFFADOMidiInputPort(); + + void + Process(JackMidiBuffer *port_buffer, uint32_t *input_buffer, + jack_nframes_t frames); + + }; + +} + +#endif diff --git a/linux/firewire/JackFFADOMidiOutput.cpp b/linux/firewire/JackFFADOMidiOutput.cpp deleted file mode 100644 index 995f2d28..00000000 --- a/linux/firewire/JackFFADOMidiOutput.cpp +++ /dev/null @@ -1,60 +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 - -#include "JackError.h" -#include "JackFFADOMidiOutput.h" - -namespace Jack { - -JackFFADOMidiOutput::JackFFADOMidiOutput(size_t non_rt_buffer_size, - size_t rt_buffer_size): - JackPhysicalMidiOutput(non_rt_buffer_size, rt_buffer_size) -{ - // Empty -} - -JackFFADOMidiOutput::~JackFFADOMidiOutput() -{ - // Empty -} - -jack_nframes_t -JackFFADOMidiOutput::Advance(jack_nframes_t current_frame) -{ - if (current_frame % 8) { - current_frame = (current_frame & (~ ((jack_nframes_t) 7))) + 8; - } - return current_frame; -} - -jack_nframes_t -JackFFADOMidiOutput::Send(jack_nframes_t current_frame, jack_midi_data_t datum) -{ - assert(output_buffer); - - jack_log("JackFFADOMidiOutput::Send (%d) - Sending '%x' byte.", - current_frame, (unsigned int) datum); - - output_buffer[current_frame] = 0x01000000 | ((uint32_t) datum); - return current_frame + 8; -} - -} diff --git a/linux/firewire/JackFFADOMidiOutputPort.cpp b/linux/firewire/JackFFADOMidiOutputPort.cpp new file mode 100644 index 00000000..3a7c21f5 --- /dev/null +++ b/linux/firewire/JackFFADOMidiOutputPort.cpp @@ -0,0 +1,98 @@ +/* +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 + +#include "JackFFADOMidiOutputPort.h" +#include "JackMidiUtil.h" + +using Jack::JackFFADOMidiOutputPort; + +JackFFADOMidiOutputPort::JackFFADOMidiOutputPort(size_t non_rt_size, + size_t max_non_rt_messages, + size_t max_rt_messages) +{ + event = 0; + read_queue = new JackMidiBufferReadQueue(); + std::auto_ptr read_queue_ptr(read_queue); + send_queue = new JackFFADOMidiSendQueue(); + std::auto_ptr send_queue_ptr(send_queue); + raw_queue = new JackMidiRawOutputWriteQueue(send_queue, max_rt_messages, + max_non_rt_messages, + non_rt_size); + send_queue_ptr.release(); + read_queue_ptr.release(); +} + +JackFFADOMidiOutputPort::~JackFFADOMidiOutputPort() +{ + delete raw_queue; + delete read_queue; + delete send_queue; +} + +void +JackFFADOMidiOutputPort::Process(JackMidiBuffer *port_buffer, + uint32_t *output_buffer, + jack_nframes_t frames) +{ + read_queue->ResetMidiBuffer(port_buffer); + send_queue->ResetOutputBuffer(output_buffer, frames); + jack_nframes_t boundary_frame = GetLastFrame() + frames; + if (! event) { + event = read_queue->DequeueEvent(); + } + for (; event; event = read_queue->DequeueEvent()) { + switch (raw_queue->EnqueueEvent(event)) { + case JackMidiWriteQueue::BUFFER_FULL: + + // Processing events early might free up some space in the raw + // output queue. + + raw_queue->Process(boundary_frame); + switch (raw_queue->EnqueueEvent(event)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + // This shouldn't really happen. It indicates a bug if it + // does. + jack_error("JackFFADOMidiOutputPort::Process - **BUG** " + "JackMidiRawOutputWriteQueue::EnqueueEvent " + "returned `BUFFER_FULL`, and then returned " + "`BUFFER_TOO_SMALL` after a `Process()` call."); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + return; + } + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackFFADOMidiOutputPort::Process - The write queue " + "couldn't enqueue a %d-byte event. Dropping event.", + event->size); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + // This is here to stop compliers from warning us about not + // handling enumeration values. + ; + } + break; + } + raw_queue->Process(boundary_frame); +} diff --git a/linux/firewire/JackFFADOMidiOutputPort.h b/linux/firewire/JackFFADOMidiOutputPort.h new file mode 100644 index 00000000..4ca309bb --- /dev/null +++ b/linux/firewire/JackFFADOMidiOutputPort.h @@ -0,0 +1,53 @@ +/* +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 __JackFFADOMidiOutputPort__ +#define __JackFFADOMidiOutputPort__ + +#include "JackFFADOMidiSendQueue.h" +#include "JackMidiBufferReadQueue.h" +#include "JackMidiRawOutputWriteQueue.h" + +namespace Jack { + + class JackFFADOMidiOutputPort { + + private: + + jack_midi_event_t *event; + JackMidiRawOutputWriteQueue *raw_queue; + JackMidiBufferReadQueue *read_queue; + JackFFADOMidiSendQueue *send_queue; + + public: + + JackFFADOMidiOutputPort(size_t non_rt_size=4096, + size_t max_non_rt_messages=1024, + size_t max_rt_messages=128); + ~JackFFADOMidiOutputPort(); + + void + Process(JackMidiBuffer *port_buffer, uint32_t *output_buffer, + jack_nframes_t frames); + + }; + +} + +#endif diff --git a/linux/firewire/JackFFADOMidiReceiveQueue.cpp b/linux/firewire/JackFFADOMidiReceiveQueue.cpp new file mode 100644 index 00000000..4b67f329 --- /dev/null +++ b/linux/firewire/JackFFADOMidiReceiveQueue.cpp @@ -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. + +*/ + +#include "JackFFADOMidiReceiveQueue.h" +#include "JackMidiUtil.h" + +using Jack::JackFFADOMidiReceiveQueue; + +JackFFADOMidiReceiveQueue::JackFFADOMidiReceiveQueue() +{ + // Empty +} + +jack_midi_event_t * +JackFFADOMidiReceiveQueue::DequeueEvent() +{ + for (; index < length; index += 8) { + uint32_t data = input_buffer[index]; + if (data & 0xff000000) { + byte = (jack_midi_data_t) (data & 0xff); + event.buffer = &byte; + event.size = 1; + event.time = last_frame + index; + index += 8; + return &event; + } + } + return 0; +} + +void +JackFFADOMidiReceiveQueue::ResetInputBuffer(uint32_t *input_buffer, + jack_nframes_t length) +{ + this->input_buffer = input_buffer; + index = 0; + last_frame = GetLastFrame(); + this->length = length; +} diff --git a/linux/firewire/JackFFADOMidiInput.h b/linux/firewire/JackFFADOMidiReceiveQueue.h similarity index 59% rename from linux/firewire/JackFFADOMidiInput.h rename to linux/firewire/JackFFADOMidiReceiveQueue.h index 12dc043c..7647869d 100644 --- a/linux/firewire/JackFFADOMidiInput.h +++ b/linux/firewire/JackFFADOMidiReceiveQueue.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2009 Devin Anderson +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 @@ -17,35 +17,33 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifndef __JackFFADOMidiInput__ -#define __JackFFADOMidiInput__ +#ifndef __JackFFADOMidiReceiveQueue__ +#define __JackFFADOMidiReceiveQueue__ -#include "JackPhysicalMidiInput.h" +#include "JackMidiReceiveQueue.h" namespace Jack { - class JackFFADOMidiInput: public JackPhysicalMidiInput { + class JackFFADOMidiReceiveQueue: public JackMidiReceiveQueue { private: + jack_midi_data_t byte; + jack_midi_event_t event; + jack_nframes_t index; uint32_t *input_buffer; - bool new_period; - - protected: - - jack_nframes_t - Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t); + jack_nframes_t last_frame; + jack_nframes_t length; public: - JackFFADOMidiInput(size_t buffer_size=1024); - ~JackFFADOMidiInput(); + JackFFADOMidiReceiveQueue(); + + jack_midi_event_t * + DequeueEvent(); - inline void - SetInputBuffer(uint32_t *input_buffer) - { - this->input_buffer = input_buffer; - } + void + ResetInputBuffer(uint32_t *input_buffer, jack_nframes_t length); }; diff --git a/linux/firewire/JackFFADOMidiSendQueue.cpp b/linux/firewire/JackFFADOMidiSendQueue.cpp new file mode 100644 index 00000000..97fdf17d --- /dev/null +++ b/linux/firewire/JackFFADOMidiSendQueue.cpp @@ -0,0 +1,64 @@ +/* +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 + +#include "JackFFADOMidiSendQueue.h" +#include "JackMidiUtil.h" + +using Jack::JackFFADOMidiSendQueue; + +JackFFADOMidiSendQueue::JackFFADOMidiSendQueue() +{ + // Empty +} + +Jack::JackMidiWriteQueue::EnqueueResult +JackFFADOMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer) +{ + assert(size == 1); + jack_nframes_t relative_time = (time < last_frame) ? 0 : time - last_frame; + if (index < relative_time) { + index = (relative_time % 8) ? + (relative_time & (~ ((jack_nframes_t) 7))) + 8 : relative_time; + } + if (index >= length) { + return BUFFER_FULL; + } + output_buffer[index] = 0x01000000 | ((uint32_t) *buffer); + index += 8; + return OK; +} + +jack_nframes_t +JackFFADOMidiSendQueue::GetNextScheduleFrame() +{ + return last_frame + index; +} + +void +JackFFADOMidiSendQueue::ResetOutputBuffer(uint32_t *output_buffer, + jack_nframes_t length) +{ + index = 0; + last_frame = GetLastFrame(); + this->length = length; + this->output_buffer = output_buffer; +} diff --git a/linux/firewire/JackFFADOMidiOutput.h b/linux/firewire/JackFFADOMidiSendQueue.h similarity index 58% rename from linux/firewire/JackFFADOMidiOutput.h rename to linux/firewire/JackFFADOMidiSendQueue.h index 309e7f61..f395f13d 100644 --- a/linux/firewire/JackFFADOMidiOutput.h +++ b/linux/firewire/JackFFADOMidiSendQueue.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2009 Devin Anderson +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 @@ -17,38 +17,35 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifndef __JackFFADOMidiOutput__ -#define __JackFFADOMidiOutput__ +#ifndef __JackFFADOMidiSendQueue__ +#define __JackFFADOMidiSendQueue__ -#include "JackPhysicalMidiOutput.h" +#include "JackMidiSendQueue.h" namespace Jack { - class JackFFADOMidiOutput: public JackPhysicalMidiOutput { + class JackFFADOMidiSendQueue: public JackMidiSendQueue { private: + jack_nframes_t index; + jack_nframes_t last_frame; + jack_nframes_t length; uint32_t *output_buffer; - protected: - - jack_nframes_t - Advance(jack_nframes_t); + public: - jack_nframes_t - Send(jack_nframes_t, jack_midi_data_t); + JackFFADOMidiSendQueue(); - public: + EnqueueResult + EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer); - JackFFADOMidiOutput(size_t non_rt_buffer_size=1024, - size_t rt_buffer_size=64); - ~JackFFADOMidiOutput(); + jack_nframes_t + GetNextScheduleFrame(); - inline void - SetOutputBuffer(uint32_t *output_buffer) - { - this->output_buffer = output_buffer; - } + void + ResetOutputBuffer(uint32_t *output_buffer, jack_nframes_t length); }; diff --git a/linux/wscript b/linux/wscript index 545d9628..c28da170 100644 --- a/linux/wscript +++ b/linux/wscript @@ -58,10 +58,10 @@ def build(bld): ] ffado_driver_src = ['firewire/JackFFADODriver.cpp', - 'firewire/JackFFADOMidiInput.cpp', - 'firewire/JackFFADOMidiOutput.cpp', - '../common/JackPhysicalMidiInput.cpp', - '../common/JackPhysicalMidiOutput.cpp' + 'firewire/JackFFADOMidiInputPort.cpp', + 'firewire/JackFFADOMidiOutputPort.cpp', + 'firewire/JackFFADOMidiReceiveQueue.cpp', + 'firewire/JackFFADOMidiSendQueue.cpp' ] if bld.env['BUILD_DRIVER_ALSA'] == True: From b426ca5e5df9bc71148685795f55c4c69bd16bbb Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Fri, 18 Mar 2011 22:54:31 -0700 Subject: [PATCH 02/58] Fix minor syntax error --- linux/firewire/JackFFADODriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/firewire/JackFFADODriver.cpp b/linux/firewire/JackFFADODriver.cpp index e0a2e556..814ac357 100644 --- a/linux/firewire/JackFFADODriver.cpp +++ b/linux/firewire/JackFFADODriver.cpp @@ -512,7 +512,7 @@ int JackFFADODriver::Attach() port = fGraphManager->GetPort(port_index); // Add one buffer more latency if "async" mode is used... - range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)) + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + driver->playback_frame_latency + range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)) + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + driver->playback_frame_latency; port->SetLatencyRange(JackPlaybackLatency, &range); // playback port aliases (jackd1 style port names) snprintf(buf, sizeof(buf) - 1, "%s:playback_%i", fClientControl.fName, (int) chn + 1); From 61ae5bc2ad715126a63fcbe33c89ff1bee1e110f Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Sat, 19 Mar 2011 14:18:25 -0700 Subject: [PATCH 03/58] Fix MIDI queue bugs. Make latency test output unexpected message count even if an error occurs. --- common/JackMidiAsyncQueue.cpp | 32 +++++++++++--------- common/JackMidiAsyncQueue.h | 1 - example-clients/midi_latency_test.c | 7 ++--- linux/firewire/JackFFADOMidiOutputPort.cpp | 4 +-- linux/firewire/JackFFADOMidiReceiveQueue.cpp | 7 ++++- linux/firewire/JackFFADOMidiReceiveQueue.h | 2 ++ linux/firewire/JackFFADOMidiSendQueue.cpp | 6 +++- linux/firewire/JackFFADOMidiSendQueue.h | 2 ++ 8 files changed, 37 insertions(+), 24 deletions(-) diff --git a/common/JackMidiAsyncQueue.cpp b/common/JackMidiAsyncQueue.cpp index 28018b56..85a93b18 100644 --- a/common/JackMidiAsyncQueue.cpp +++ b/common/JackMidiAsyncQueue.cpp @@ -26,13 +26,13 @@ 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); + byte_ring = jack_ringbuffer_create((max_bytes * sizeof(jack_midi_data_t)) + + 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; } @@ -54,26 +54,23 @@ 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->buffer = data_buffer; 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); + memcpy(data_buffer, vector[0].buf, size1 * sizeof(jack_midi_data_t)); + if (size1 < size) { + memcpy(data_buffer + size1, vector[1].buf, + (size - size1) * sizeof(jack_midi_data_t)); } - advance_space = size; + jack_ringbuffer_read_advance(byte_ring, + size * sizeof(jack_midi_data_t)); } return event; } @@ -82,11 +79,16 @@ Jack::JackMidiWriteQueue::EnqueueResult JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer) { + if (size > max_bytes) { + return BUFFER_TOO_SMALL; + } 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_space(byte_ring) >= + (size * sizeof(jack_midi_data_t))))) { + return BUFFER_FULL; } - jack_ringbuffer_write(byte_ring, (const char *) buffer, size); + jack_ringbuffer_write(byte_ring, (const char *) buffer, + size * sizeof(jack_midi_data_t)); jack_ringbuffer_write(info_ring, (const char *) (&time), sizeof(jack_nframes_t)); jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t)); diff --git a/common/JackMidiAsyncQueue.h b/common/JackMidiAsyncQueue.h index 630b5ab1..22948cc9 100644 --- a/common/JackMidiAsyncQueue.h +++ b/common/JackMidiAsyncQueue.h @@ -46,7 +46,6 @@ namespace Jack { 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; diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c index 2d9e1686..bcf54641 100644 --- a/example-clients/midi_latency_test.c +++ b/example-clients/midi_latency_test.c @@ -554,10 +554,9 @@ main(int argc, char **argv) if (jitter_plot[100]) { printf(" > 10 ms: %u\n", jitter_plot[100]); } - if (unexpected_messages) { - printf("\nUnexpected messages received: %d\n", - unexpected_messages); - } + } + if (unexpected_messages) { + printf("\nUnexpected messages received: %d\n", unexpected_messages); } deactivate_client: jack_deactivate(client); diff --git a/linux/firewire/JackFFADOMidiOutputPort.cpp b/linux/firewire/JackFFADOMidiOutputPort.cpp index 3a7c21f5..b916c8c5 100644 --- a/linux/firewire/JackFFADOMidiOutputPort.cpp +++ b/linux/firewire/JackFFADOMidiOutputPort.cpp @@ -33,9 +33,9 @@ JackFFADOMidiOutputPort::JackFFADOMidiOutputPort(size_t non_rt_size, std::auto_ptr read_queue_ptr(read_queue); send_queue = new JackFFADOMidiSendQueue(); std::auto_ptr send_queue_ptr(send_queue); - raw_queue = new JackMidiRawOutputWriteQueue(send_queue, max_rt_messages, + raw_queue = new JackMidiRawOutputWriteQueue(send_queue, non_rt_size, max_non_rt_messages, - non_rt_size); + max_rt_messages); send_queue_ptr.release(); read_queue_ptr.release(); } diff --git a/linux/firewire/JackFFADOMidiReceiveQueue.cpp b/linux/firewire/JackFFADOMidiReceiveQueue.cpp index 4b67f329..16f24636 100644 --- a/linux/firewire/JackFFADOMidiReceiveQueue.cpp +++ b/linux/firewire/JackFFADOMidiReceiveQueue.cpp @@ -24,7 +24,7 @@ using Jack::JackFFADOMidiReceiveQueue; JackFFADOMidiReceiveQueue::JackFFADOMidiReceiveQueue() { - // Empty + bytes_received = 0; } jack_midi_event_t * @@ -38,6 +38,11 @@ JackFFADOMidiReceiveQueue::DequeueEvent() event.size = 1; event.time = last_frame + index; index += 8; + bytes_received++; + + jack_info("Jack::JackFFADOMidiReceiveQueue: %d bytes received", + bytes_received); + return &event; } } diff --git a/linux/firewire/JackFFADOMidiReceiveQueue.h b/linux/firewire/JackFFADOMidiReceiveQueue.h index 7647869d..04c35f7f 100644 --- a/linux/firewire/JackFFADOMidiReceiveQueue.h +++ b/linux/firewire/JackFFADOMidiReceiveQueue.h @@ -28,6 +28,8 @@ namespace Jack { private: + unsigned long bytes_received; + jack_midi_data_t byte; jack_midi_event_t event; jack_nframes_t index; diff --git a/linux/firewire/JackFFADOMidiSendQueue.cpp b/linux/firewire/JackFFADOMidiSendQueue.cpp index 97fdf17d..a20bde54 100644 --- a/linux/firewire/JackFFADOMidiSendQueue.cpp +++ b/linux/firewire/JackFFADOMidiSendQueue.cpp @@ -26,7 +26,7 @@ using Jack::JackFFADOMidiSendQueue; JackFFADOMidiSendQueue::JackFFADOMidiSendQueue() { - // Empty + bytes_sent = 0; } Jack::JackMidiWriteQueue::EnqueueResult @@ -44,6 +44,10 @@ JackFFADOMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size, } output_buffer[index] = 0x01000000 | ((uint32_t) *buffer); index += 8; + + bytes_sent++; + jack_info("Jack::JackFFADOMidiSendQueue: %d bytes sent", bytes_sent); + return OK; } diff --git a/linux/firewire/JackFFADOMidiSendQueue.h b/linux/firewire/JackFFADOMidiSendQueue.h index f395f13d..25fe7c0c 100644 --- a/linux/firewire/JackFFADOMidiSendQueue.h +++ b/linux/firewire/JackFFADOMidiSendQueue.h @@ -28,6 +28,8 @@ namespace Jack { private: + unsigned long bytes_sent; + jack_nframes_t index; jack_nframes_t last_frame; jack_nframes_t length; From 725c7c4ff29d95b50228d823730ab404ef75d88f Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Sat, 19 Mar 2011 14:25:45 -0700 Subject: [PATCH 04/58] Kill temporary FFADO debug code. --- linux/firewire/JackFFADOMidiReceiveQueue.cpp | 7 +------ linux/firewire/JackFFADOMidiReceiveQueue.h | 2 -- linux/firewire/JackFFADOMidiSendQueue.cpp | 6 +----- linux/firewire/JackFFADOMidiSendQueue.h | 2 -- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/linux/firewire/JackFFADOMidiReceiveQueue.cpp b/linux/firewire/JackFFADOMidiReceiveQueue.cpp index 16f24636..4b67f329 100644 --- a/linux/firewire/JackFFADOMidiReceiveQueue.cpp +++ b/linux/firewire/JackFFADOMidiReceiveQueue.cpp @@ -24,7 +24,7 @@ using Jack::JackFFADOMidiReceiveQueue; JackFFADOMidiReceiveQueue::JackFFADOMidiReceiveQueue() { - bytes_received = 0; + // Empty } jack_midi_event_t * @@ -38,11 +38,6 @@ JackFFADOMidiReceiveQueue::DequeueEvent() event.size = 1; event.time = last_frame + index; index += 8; - bytes_received++; - - jack_info("Jack::JackFFADOMidiReceiveQueue: %d bytes received", - bytes_received); - return &event; } } diff --git a/linux/firewire/JackFFADOMidiReceiveQueue.h b/linux/firewire/JackFFADOMidiReceiveQueue.h index 04c35f7f..7647869d 100644 --- a/linux/firewire/JackFFADOMidiReceiveQueue.h +++ b/linux/firewire/JackFFADOMidiReceiveQueue.h @@ -28,8 +28,6 @@ namespace Jack { private: - unsigned long bytes_received; - jack_midi_data_t byte; jack_midi_event_t event; jack_nframes_t index; diff --git a/linux/firewire/JackFFADOMidiSendQueue.cpp b/linux/firewire/JackFFADOMidiSendQueue.cpp index a20bde54..97fdf17d 100644 --- a/linux/firewire/JackFFADOMidiSendQueue.cpp +++ b/linux/firewire/JackFFADOMidiSendQueue.cpp @@ -26,7 +26,7 @@ using Jack::JackFFADOMidiSendQueue; JackFFADOMidiSendQueue::JackFFADOMidiSendQueue() { - bytes_sent = 0; + // Empty } Jack::JackMidiWriteQueue::EnqueueResult @@ -44,10 +44,6 @@ JackFFADOMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size, } output_buffer[index] = 0x01000000 | ((uint32_t) *buffer); index += 8; - - bytes_sent++; - jack_info("Jack::JackFFADOMidiSendQueue: %d bytes sent", bytes_sent); - return OK; } diff --git a/linux/firewire/JackFFADOMidiSendQueue.h b/linux/firewire/JackFFADOMidiSendQueue.h index 25fe7c0c..f395f13d 100644 --- a/linux/firewire/JackFFADOMidiSendQueue.h +++ b/linux/firewire/JackFFADOMidiSendQueue.h @@ -28,8 +28,6 @@ namespace Jack { private: - unsigned long bytes_sent; - jack_nframes_t index; jack_nframes_t last_frame; jack_nframes_t length; From 4916a78906451b6260817f98d1888a0b9936b292 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Mon, 21 Mar 2011 12:16:24 +0100 Subject: [PATCH 05/58] Update XCode project. --- macosx/Jackdmp.xcodeproj/project.pbxproj | 32 ------------------------ 1 file changed, 32 deletions(-) diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index 96daca3a..705de7c9 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -754,18 +754,6 @@ 4BC3B6A50E703B2E0066E42F /* JackPosixThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC3B6A30E703B2E0066E42F /* JackPosixThread.h */; }; 4BC3B6A60E703B2E0066E42F /* JackPosixThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B6A20E703B2E0066E42F /* JackPosixThread.cpp */; }; 4BC3B6A70E703B2E0066E42F /* JackPosixThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC3B6A30E703B2E0066E42F /* JackPosixThread.h */; }; - 4BCBCE5D10C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCBCE5910C4FE3F00450FFE /* JackPhysicalMidiInput.cpp */; }; - 4BCBCE5E10C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BCBCE5A10C4FE3F00450FFE /* JackPhysicalMidiInput.h */; }; - 4BCBCE5F10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCBCE5B10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp */; }; - 4BCBCE6010C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BCBCE5C10C4FE3F00450FFE /* JackPhysicalMidiOutput.h */; }; - 4BCBCE6110C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCBCE5910C4FE3F00450FFE /* JackPhysicalMidiInput.cpp */; }; - 4BCBCE6210C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BCBCE5A10C4FE3F00450FFE /* JackPhysicalMidiInput.h */; }; - 4BCBCE6310C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCBCE5B10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp */; }; - 4BCBCE6410C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BCBCE5C10C4FE3F00450FFE /* JackPhysicalMidiOutput.h */; }; - 4BCBCE6510C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCBCE5910C4FE3F00450FFE /* JackPhysicalMidiInput.cpp */; }; - 4BCBCE6610C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BCBCE5A10C4FE3F00450FFE /* JackPhysicalMidiInput.h */; }; - 4BCBCE6710C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCBCE5B10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp */; }; - 4BCBCE6810C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BCBCE5C10C4FE3F00450FFE /* JackPhysicalMidiOutput.h */; }; 4BCC87960D57168300A7FEB1 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BCC87950D57168300A7FEB1 /* Accelerate.framework */; }; 4BCC87970D57168300A7FEB1 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BCC87950D57168300A7FEB1 /* Accelerate.framework */; }; 4BCC87980D57168300A7FEB1 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BCC87950D57168300A7FEB1 /* Accelerate.framework */; }; @@ -1642,10 +1630,6 @@ 4BC3B6B90E703BCC0066E42F /* JackNetUnixSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackNetUnixSocket.cpp; path = ../posix/JackNetUnixSocket.cpp; sourceTree = SOURCE_ROOT; }; 4BC3B6BA0E703BCC0066E42F /* JackNetUnixSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackNetUnixSocket.h; path = ../posix/JackNetUnixSocket.h; sourceTree = SOURCE_ROOT; }; 4BC8326D0DF42C7D00DD1C93 /* JackMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMutex.h; path = ../common/JackMutex.h; sourceTree = SOURCE_ROOT; }; - 4BCBCE5910C4FE3F00450FFE /* JackPhysicalMidiInput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackPhysicalMidiInput.cpp; path = ../common/JackPhysicalMidiInput.cpp; sourceTree = SOURCE_ROOT; }; - 4BCBCE5A10C4FE3F00450FFE /* JackPhysicalMidiInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackPhysicalMidiInput.h; path = ../common/JackPhysicalMidiInput.h; sourceTree = SOURCE_ROOT; }; - 4BCBCE5B10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackPhysicalMidiOutput.cpp; path = ../common/JackPhysicalMidiOutput.cpp; sourceTree = SOURCE_ROOT; }; - 4BCBCE5C10C4FE3F00450FFE /* JackPhysicalMidiOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackPhysicalMidiOutput.h; path = ../common/JackPhysicalMidiOutput.h; sourceTree = SOURCE_ROOT; }; 4BCC87950D57168300A7FEB1 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = /System/Library/Frameworks/Accelerate.framework; sourceTree = ""; }; 4BD4B4D409BACD9600750C0F /* JackTransportEngine.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackTransportEngine.h; path = ../common/JackTransportEngine.h; sourceTree = SOURCE_ROOT; }; 4BD4B4D509BACD9600750C0F /* JackTransportEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackTransportEngine.cpp; path = ../common/JackTransportEngine.cpp; sourceTree = SOURCE_ROOT; }; @@ -2994,10 +2978,6 @@ 4BF3390D0F8B86AF0080FB5B /* MIDI */ = { isa = PBXGroup; children = ( - 4BCBCE5910C4FE3F00450FFE /* JackPhysicalMidiInput.cpp */, - 4BCBCE5A10C4FE3F00450FFE /* JackPhysicalMidiInput.h */, - 4BCBCE5B10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp */, - 4BCBCE5C10C4FE3F00450FFE /* JackPhysicalMidiOutput.h */, 4BF3391F0F8B873E0080FB5B /* JackMidiDriver.cpp */, 4BF339200F8B873E0080FB5B /* JackMidiDriver.h */, 4BF339140F8B86DC0080FB5B /* JackCoreMidiDriver.h */, @@ -3243,8 +3223,6 @@ 4BF339220F8B873E0080FB5B /* JackMidiDriver.h in Headers */, 4BDCDBD21001FD0200B15929 /* JackWaitThreadedDriver.h in Headers */, 4BDCDC0A1001FDA800B15929 /* JackArgParser.h in Headers */, - 4BCBCE6210C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */, - 4BCBCE6410C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */, 4B88D04311298BEE007A87C1 /* weakjack.h in Headers */, 4B88D04411298BEE007A87C1 /* weakmacros.h in Headers */, 4BC2CA5A113C6CB80076717C /* JackNetInterface.h in Headers */, @@ -3706,8 +3684,6 @@ 4BECB2F60F4451C10091B70A /* JackProcessSync.h in Headers */, 4BF339240F8B873E0080FB5B /* JackMidiDriver.h in Headers */, 4B94334B10A5E666002A187F /* systemdeps.h in Headers */, - 4BCBCE5E10C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */, - 4BCBCE6010C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */, 4B88D03D11298BEE007A87C1 /* weakjack.h in Headers */, 4B88D03E11298BEE007A87C1 /* weakmacros.h in Headers */, 4BC2CA56113C6C940076717C /* JackNetInterface.h in Headers */, @@ -3901,8 +3877,6 @@ 4BA3396D10B2E36800190E3B /* JackMidiDriver.h in Headers */, 4BA3396E10B2E36800190E3B /* JackWaitThreadedDriver.h in Headers */, 4BA3396F10B2E36800190E3B /* JackArgParser.h in Headers */, - 4BCBCE6610C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */, - 4BCBCE6810C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */, 4B88D04511298BEE007A87C1 /* weakjack.h in Headers */, 4B88D04611298BEE007A87C1 /* weakmacros.h in Headers */, 4BC2CA5E113C6CCA0076717C /* JackNetInterface.h in Headers */, @@ -6610,8 +6584,6 @@ 4BF339210F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, 4BDCDBD11001FD0100B15929 /* JackWaitThreadedDriver.cpp in Sources */, 4BDCDC091001FDA800B15929 /* JackArgParser.cpp in Sources */, - 4BCBCE6110C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */, - 4BCBCE6310C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */, 4BC2CA59113C6CB60076717C /* JackNetInterface.cpp in Sources */, 4BC2CA5B113C6CBE0076717C /* JackNetUnixSocket.cpp in Sources */, 4B8A38A7117B80D300664E07 /* JackSocket.cpp in Sources */, @@ -7064,8 +7036,6 @@ 4BBAE4110F42FA6100B8BD3F /* JackEngineProfiling.cpp in Sources */, 4BECB2F50F4451C10091B70A /* JackProcessSync.cpp in Sources */, 4BF339230F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, - 4BCBCE5D10C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */, - 4BCBCE5F10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */, 4BC2CA55113C6C930076717C /* JackNetInterface.cpp in Sources */, 4BC2CA57113C6C9B0076717C /* JackNetUnixSocket.cpp in Sources */, 4B2209E112F6BBF300E5DC26 /* JackSocketServerChannel.cpp in Sources */, @@ -7260,8 +7230,6 @@ 4BA339A210B2E36800190E3B /* JackMidiDriver.cpp in Sources */, 4BA339A310B2E36800190E3B /* JackWaitThreadedDriver.cpp in Sources */, 4BA339A410B2E36800190E3B /* JackArgParser.cpp in Sources */, - 4BCBCE6510C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */, - 4BCBCE6710C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */, 4BC2CA5D113C6CC90076717C /* JackNetInterface.cpp in Sources */, 4BC2CA5F113C6CD10076717C /* JackNetUnixSocket.cpp in Sources */, ); From 6e92bd8020ecf88fc8ccb6c3bec8b7ef6727480c Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 22 Mar 2011 15:10:59 -0700 Subject: [PATCH 06/58] Add 'alsarawmidi' driver, which is a slave MIDI driver that makes ALSA MIDI ports available to JACK. The driver uses the rawmidi devices, and uses the raw MIDI queues to optimize output. --- common/JackMidiRawInputWriteQueue.h | 2 +- common/JackMidiRawOutputWriteQueue.h | 2 +- example-clients/midi_latency_test.c | 4 - linux/alsarawmidi/JackALSARawMidiDriver.cpp | 586 ++++++++++++++++++ linux/alsarawmidi/JackALSARawMidiDriver.h | 79 +++ .../alsarawmidi/JackALSARawMidiInputPort.cpp | 121 ++++ linux/alsarawmidi/JackALSARawMidiInputPort.h | 43 ++ .../alsarawmidi/JackALSARawMidiOutputPort.cpp | 146 +++++ linux/alsarawmidi/JackALSARawMidiOutputPort.h | 44 ++ linux/alsarawmidi/JackALSARawMidiPort.cpp | 158 +++++ linux/alsarawmidi/JackALSARawMidiPort.h | 51 ++ .../JackALSARawMidiReceiveQueue.cpp | 35 ++ .../alsarawmidi/JackALSARawMidiReceiveQueue.h | 32 + .../alsarawmidi/JackALSARawMidiSendQueue.cpp | 40 ++ linux/alsarawmidi/JackALSARawMidiSendQueue.h | 32 + linux/wscript | 10 + 16 files changed, 1379 insertions(+), 6 deletions(-) create mode 100755 linux/alsarawmidi/JackALSARawMidiDriver.cpp create mode 100755 linux/alsarawmidi/JackALSARawMidiDriver.h create mode 100755 linux/alsarawmidi/JackALSARawMidiInputPort.cpp create mode 100755 linux/alsarawmidi/JackALSARawMidiInputPort.h create mode 100755 linux/alsarawmidi/JackALSARawMidiOutputPort.cpp create mode 100755 linux/alsarawmidi/JackALSARawMidiOutputPort.h create mode 100755 linux/alsarawmidi/JackALSARawMidiPort.cpp create mode 100755 linux/alsarawmidi/JackALSARawMidiPort.h create mode 100755 linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp create mode 100755 linux/alsarawmidi/JackALSARawMidiReceiveQueue.h create mode 100755 linux/alsarawmidi/JackALSARawMidiSendQueue.cpp create mode 100755 linux/alsarawmidi/JackALSARawMidiSendQueue.h diff --git a/common/JackMidiRawInputWriteQueue.h b/common/JackMidiRawInputWriteQueue.h index 2feff527..4639107f 100644 --- a/common/JackMidiRawInputWriteQueue.h +++ b/common/JackMidiRawInputWriteQueue.h @@ -161,7 +161,7 @@ namespace Jack { */ jack_nframes_t - Process(jack_nframes_t boundary_frame); + Process(jack_nframes_t boundary_frame=0); }; diff --git a/common/JackMidiRawOutputWriteQueue.h b/common/JackMidiRawOutputWriteQueue.h index b5e336dc..0437ec93 100644 --- a/common/JackMidiRawOutputWriteQueue.h +++ b/common/JackMidiRawOutputWriteQueue.h @@ -138,7 +138,7 @@ namespace Jack { */ jack_nframes_t - Process(jack_nframes_t boundary_frame); + Process(jack_nframes_t boundary_frame=0); }; diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c index bcf54641..27c651c8 100644 --- a/example-clients/midi_latency_test.c +++ b/example-clients/midi_latency_test.c @@ -477,10 +477,6 @@ main(int argc, char **argv) error_source = "jack_connect"; goto deactivate_client; } - jack_port_get_latency_range(remote_in_port, JackCaptureLatency, - &in_latency_range); - jack_port_get_latency_range(remote_out_port, JackPlaybackLatency, - &out_latency_range); code = pthread_mutex_unlock(&start_mutex); if (code) { error_message = strerror(code); diff --git a/linux/alsarawmidi/JackALSARawMidiDriver.cpp b/linux/alsarawmidi/JackALSARawMidiDriver.cpp new file mode 100755 index 00000000..adbb89fb --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiDriver.cpp @@ -0,0 +1,586 @@ +#include +#include +#include + +#include + +#include "JackALSARawMidiDriver.h" +#include "JackEngineControl.h" +#include "JackError.h" +#include "JackMidiUtil.h" + +using Jack::JackALSARawMidiDriver; + +JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name, + const char *alias, + JackLockedEngine *engine, + JackSynchro *table): + JackMidiDriver(name, alias, engine, table) +{ + thread = new JackThread(this); + fCaptureChannels = 0; + fds[0] = -1; + fds[1] = -1; + fPlaybackChannels = 0; + input_ports = 0; + output_ports = 0; + poll_fds = 0; +} + +JackALSARawMidiDriver::~JackALSARawMidiDriver() +{ + Stop(); + delete thread; + Close(); +} + +int +JackALSARawMidiDriver::Attach() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + jack_port_id_t index; + const char *name; + JackPort *port; + for (int i = 0; i < fCaptureChannels; i++) { + JackALSARawMidiInputPort *input_port = input_ports[i]; + name = input_port->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackALSARawMidiDriver::Attach - cannot register input " + "port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(input_port->GetAlias()); + port->SetLatency(buffer_size); + fCapturePortList[i] = index; + } + for (int i = 0; i < fPlaybackChannels; i++) { + JackALSARawMidiOutputPort *output_port = output_ports[i]; + name = output_port->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackALSARawMidiDriver::Attach - cannot register " + "output port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(output_port->GetAlias()); + port->SetLatency(buffer_size); + fPlaybackPortList[i] = index; + } + return 0; +} + +int +JackALSARawMidiDriver::Close() +{ + if (input_ports) { + for (int i = 0; i < fCaptureChannels; i++) { + delete input_ports[i]; + } + delete[] input_ports; + input_ports = 0; + } + if (output_ports) { + for (int i = 0; i < fPlaybackChannels; i++) { + delete output_ports[i]; + } + delete[] output_ports; + output_ports = 0; + } + return 0; +} + +bool +JackALSARawMidiDriver::Execute() +{ + jack_nframes_t timeout_frame = 0; + for (;;) { + jack_time_t wait_time; + unsigned short revents; + if (! timeout_frame) { + wait_time = 0; + } else { + jack_time_t next_time = GetTimeFromFrames(timeout_frame); + jack_time_t now = GetMicroSeconds(); + + if (next_time <= now) { + goto handle_ports; + } + wait_time = next_time - now; + } + + jack_info("Calling 'Poll' with wait time '%d'.", wait_time); + + if (Poll(wait_time) == -1) { + if (errno == EINTR) { + continue; + } + jack_error("JackALSARawMidiDriver::Execute - poll error: %s", + strerror(errno)); + break; + } + revents = poll_fds[0].revents; + if (revents & POLLHUP) { + close(fds[0]); + fds[0] = -1; + break; + } + if (revents & (~(POLLHUP | POLLIN))) { + jack_error("JackALSARawMidiDriver::Execute - unexpected poll " + "event on pipe file descriptor."); + break; + } + handle_ports: + jack_nframes_t process_frame; + jack_nframes_t timeout_frame = 0; + for (int i = 0; i < fCaptureChannels; i++) { + process_frame = input_ports[i]->ProcessALSA(); + if (process_frame && ((! timeout_frame) || + (process_frame < timeout_frame))) { + timeout_frame = process_frame; + } + } + for (int i = 0; i < fPlaybackChannels; i++) { + process_frame = output_ports[i]->ProcessALSA(fds[0]); + if (process_frame && ((! timeout_frame) || + (process_frame < timeout_frame))) { + timeout_frame = process_frame; + } + } + } + + jack_info("ALSA thread is exiting."); + + return false; +} + +void +JackALSARawMidiDriver:: +GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info, + std::vector *info_list) +{ + snd_rawmidi_info_set_subdevice(info, 0); + int code = snd_ctl_rawmidi_info(control, info); + if (code) { + if (code != -ENOENT) { + HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code); + } + return; + } + unsigned int count = snd_rawmidi_info_get_subdevices_count(info); + for (unsigned int i = 0; i < count; i++) { + snd_rawmidi_info_set_subdevice(info, i); + int code = snd_ctl_rawmidi_info(control, info); + if (code) { + HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code); + continue; + } + snd_rawmidi_info_t *info_copy; + code = snd_rawmidi_info_malloc(&info_copy); + if (code) { + HandleALSAError("GetDeviceInfo", "snd_rawmidi_info_malloc", code); + continue; + } + snd_rawmidi_info_copy(info_copy, info); + try { + info_list->push_back(info_copy); + } catch (std::bad_alloc &e) { + snd_rawmidi_info_free(info_copy); + jack_error("JackALSARawMidiDriver::GetDeviceInfo - " + "std::vector::push_back: %s", e.what()); + } + } +} + +void +JackALSARawMidiDriver::HandleALSAError(const char *driver_func, + const char *alsa_func, int code) +{ + jack_error("JackALSARawMidiDriver::%s - %s: %s", driver_func, alsa_func, + snd_strerror(code)); +} + +bool +JackALSARawMidiDriver::Init() +{ + set_threaded_log_function(); + if (thread->AcquireSelfRealTime(fEngineControl->fServerPriority + 1)) { + jack_error("JackALSARawMidiDriver::Init - could not acquire realtime " + "scheduling. Continuing anyway."); + } + return true; +} + +int +JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels, + int out_channels, bool monitor, + const char *capture_driver_name, + const char *playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + snd_rawmidi_info_t *info; + int code = snd_rawmidi_info_malloc(&info); + if (code) { + HandleALSAError("Open", "snd_rawmidi_info_malloc", code); + return -1; + } + std::vector in_info_list; + std::vector out_info_list; + for (int card = -1;;) { + int code = snd_card_next(&card); + if (code) { + HandleALSAError("Open", "snd_card_next", code); + continue; + } + if (card == -1) { + break; + } + char name[32]; + snprintf(name, sizeof(name), "hw:%d", card); + snd_ctl_t *control; + code = snd_ctl_open(&control, name, SND_CTL_NONBLOCK); + if (code) { + HandleALSAError("Open", "snd_ctl_open", code); + continue; + } + for (int device = -1;;) { + code = snd_ctl_rawmidi_next_device(control, &device); + if (code) { + HandleALSAError("Open", "snd_ctl_rawmidi_next_device", code); + continue; + } + if (device == -1) { + break; + } + snd_rawmidi_info_set_device(info, device); + snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); + GetDeviceInfo(control, info, &in_info_list); + snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); + GetDeviceInfo(control, info, &out_info_list); + } + snd_ctl_close(control); + } + snd_rawmidi_info_free(info); + size_t potential_inputs = in_info_list.size(); + size_t potential_outputs = out_info_list.size(); + if (! (potential_inputs || potential_outputs)) { + jack_error("JackALSARawMidiDriver::Open - no ALSA raw MIDI input or " + "output ports found."); + return -1; + } + + // XXX: Can't use auto_ptr here. These are arrays, and require the + // delete[] operator. + std::auto_ptr input_ptr; + if (potential_inputs) { + input_ports = new JackALSARawMidiInputPort *[potential_inputs]; + input_ptr.reset(input_ports); + } + std::auto_ptr output_ptr; + if (potential_outputs) { + output_ports = new JackALSARawMidiOutputPort *[potential_outputs]; + output_ptr.reset(output_ports); + } + + size_t num_inputs = 0; + size_t num_outputs = 0; + for (size_t i = 0; i < potential_inputs; i++) { + snd_rawmidi_info_t *info = in_info_list.at(i); + try { + input_ports[num_inputs] = new JackALSARawMidiInputPort(info, i); + + jack_info("JackALSARawMidiDriver::Open - Input port: card=%d, " + "device=%d, subdevice=%d, id=%s, name=%s, subdevice " + "name=%s", + snd_rawmidi_info_get_card(info), + snd_rawmidi_info_get_device(info), + snd_rawmidi_info_get_subdevice(info), + snd_rawmidi_info_get_id(info), + snd_rawmidi_info_get_name(info), + snd_rawmidi_info_get_subdevice_name(info)); + + num_inputs++; + } catch (std::exception e) { + jack_error("JackALSARawMidiDriver::Open - while creating new " + "JackALSARawMidiInputPort: %s", e.what()); + } + snd_rawmidi_info_free(info); + } + for (size_t i = 0; i < potential_outputs; i++) { + snd_rawmidi_info_t *info = out_info_list.at(i); + try { + output_ports[num_outputs] = new JackALSARawMidiOutputPort(info, i); + + jack_info("JackALSARawMidiDriver::Open - Output port: card=%d, " + "device=%d, subdevice=%d, id=%s, name=%s, subdevice " + "name=%s", + snd_rawmidi_info_get_card(info), + snd_rawmidi_info_get_device(info), + snd_rawmidi_info_get_subdevice(info), + snd_rawmidi_info_get_id(info), + snd_rawmidi_info_get_name(info), + snd_rawmidi_info_get_subdevice_name(info)); + + num_outputs++; + } catch (std::exception e) { + jack_error("JackALSARawMidiDriver::Open - while creating new " + "JackALSARawMidiOutputPort: %s", e.what()); + } + snd_rawmidi_info_free(info); + } + if (num_inputs || num_outputs) { + if (! JackMidiDriver::Open(capturing, playing, num_inputs, num_outputs, + monitor, capture_driver_name, + playback_driver_name, capture_latency, + playback_latency)) { + if (potential_inputs) { + input_ptr.release(); + } + if (potential_outputs) { + output_ptr.release(); + } + return 0; + } + jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error"); + } else { + jack_error("JackALSARawMidiDriver::Open - none of the potential " + "inputs or outputs were successfully opened."); + } + Close(); + return -1; +} + +#ifdef HAVE_PPOLL + +int +JackALSARawMidiDriver::Poll(jack_time_t wait_time) +{ + struct timespec timeout; + struct timespec *timeout_ptr; + if (! wait_time) { + timeout_ptr = 0; + } else { + timeout.tv_sec = wait_time / 1000000; + timeout.tv_nsec = (wait_time % 1000000) * 1000; + timeout_ptr = &timeout; + } + return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0); +} + +#else + +int +JackALSARawMidiDriver::Poll(jack_time_t wait_time) +{ + int result = poll(poll_fds, poll_fd_count, + ! wait_time ? -1 : (int) (wait_time / 1000)); + if ((! result) && wait_time) { + wait_time %= 1000; + if (wait_time) { + // Cheap hack. + usleep(wait_time); + result = poll(poll_fds, poll_fd_count, 0); + } + } + return result; +} + +#endif + +int +JackALSARawMidiDriver::Read() +{ + for (int i = 0; i < fCaptureChannels; i++) { + input_ports[i]->ProcessJack(GetInputBuffer(i), + fEngineControl->fBufferSize); + } + return 0; +} + +int +JackALSARawMidiDriver::Start() +{ + + jack_info("JackALSARawMidiDriver::Start - Starting 'alsarawmidi' driver."); + + // JackMidiDriver::Start(); + + poll_fd_count = 1; + for (int i = 0; i < fCaptureChannels; i++) { + poll_fd_count += input_ports[i]->GetPollDescriptorCount(); + } + for (int i = 0; i < fPlaybackChannels; i++) { + poll_fd_count += output_ports[i]->GetPollDescriptorCount(); + } + try { + poll_fds = new pollfd[poll_fd_count]; + } catch (std::bad_alloc e) { + jack_error("JackALSARawMidiDriver::Start - creating poll descriptor " + "structures failed: %s", e.what()); + return -1; + } + int flags; + struct pollfd *poll_fd_iter; + if (pipe(fds) == -1) { + jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: " + "%s", strerror(errno)); + goto free_poll_descriptors; + } + flags = fcntl(fds[0], F_GETFL); + if (flags == -1) { + jack_error("JackALSARawMidiDriver::Start = while getting flags for " + "read file descriptor: %s", strerror(errno)); + goto close_fds; + } + if (fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) == -1) { + jack_error("JackALSARawMidiDriver::Start - while setting non-blocking " + "mode for read file descriptor: %s", strerror(errno)); + goto close_fds; + } + flags = fcntl(fds[1], F_GETFL); + if (flags == -1) { + jack_error("JackALSARawMidiDriver::Start = while getting flags for " + "write file descriptor: %s", strerror(errno)); + goto close_fds; + } + if (fcntl(fds[1], F_SETFL, flags | O_NONBLOCK) == -1) { + jack_error("JackALSARawMidiDriver::Start - while setting non-blocking " + "mode for write file descriptor: %s", strerror(errno)); + goto close_fds; + } + poll_fds[0].events = POLLERR | POLLIN | POLLNVAL; + poll_fds[0].fd = fds[0]; + poll_fd_iter = poll_fds + 1; + for (int i = 0; i < fCaptureChannels; i++) { + JackALSARawMidiInputPort *input_port = input_ports[i]; + input_port->PopulatePollDescriptors(poll_fd_iter); + poll_fd_iter += input_port->GetPollDescriptorCount(); + } + for (int i = 0; i < fPlaybackChannels; i++) { + JackALSARawMidiOutputPort *output_port = output_ports[i]; + output_port->PopulatePollDescriptors(poll_fd_iter); + poll_fd_iter += output_port->GetPollDescriptorCount(); + } + + jack_info("Starting ALSA thread ..."); + + if (! thread->StartSync()) { + + jack_info("Started ALSA thread."); + + return 0; + } + jack_error("JackALSARawMidiDriver::Start - failed to start MIDI " + "processing thread."); + close_fds: + close(fds[1]); + fds[1] = -1; + close(fds[0]); + fds[0] = -1; + free_poll_descriptors: + delete[] poll_fds; + poll_fds = 0; + return -1; +} + +int +JackALSARawMidiDriver::Stop() +{ + + jack_info("Stopping 'alsarawmidi' driver."); + + if (fds[1] != -1) { + close(fds[1]); + fds[1] = -1; + } + int result; + const char *verb; + switch (thread->GetStatus()) { + case JackThread::kIniting: + case JackThread::kStarting: + result = thread->Kill(); + verb = "kill"; + break; + case JackThread::kRunning: + result = thread->Stop(); + verb = "stop"; + break; + default: + result = 0; + verb = 0; + } + if (fds[0] != -1) { + close(fds[0]); + fds[0] = -1; + } + if (poll_fds) { + delete[] poll_fds; + poll_fds = 0; + } + if (result) { + jack_error("JackALSARawMidiDriver::Stop - could not %s MIDI " + "processing thread.", verb); + } + return result; +} + +int +JackALSARawMidiDriver::Write() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + int write_fd = fds[1]; + for (int i = 0; i < fPlaybackChannels; i++) { + output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size, + write_fd); + } + return 0; +} + +#ifdef __cplusplus +extern "C" { +#endif + + SERVER_EXPORT jack_driver_desc_t * + driver_get_descriptor() + { + jack_driver_desc_t *desc = + (jack_driver_desc_t *) malloc(sizeof(jack_driver_desc_t)); + if (desc) { + strcpy(desc->desc, "Alternative ALSA raw MIDI backend."); + strcpy(desc->name, "alsarawmidi"); + + // X: There could be parameters here regarding setting I/O buffer + // sizes. I don't think MIDI drivers can accept parameters right + // now without being set as the main driver. + desc->nparams = 0; + desc->params = 0; + } + return desc; + } + + SERVER_EXPORT Jack::JackDriverClientInterface * + driver_initialize(Jack::JackLockedEngine *engine, Jack::JackSynchro *table, + const JSList *params) + { + Jack::JackDriverClientInterface *driver = + new Jack::JackALSARawMidiDriver("system", "alsarawmidi", engine, + table); + if (driver->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0)) { + delete driver; + driver = 0; + } + return driver; + } + +#ifdef __cplusplus +} +#endif diff --git a/linux/alsarawmidi/JackALSARawMidiDriver.h b/linux/alsarawmidi/JackALSARawMidiDriver.h new file mode 100755 index 00000000..5a793ebb --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiDriver.h @@ -0,0 +1,79 @@ +#ifndef __JackALSARawMidiDriver__ +#define __JackALSARawMidiDriver__ + +#include + +#include +#include + +#include "JackALSARawMidiInputPort.h" +#include "JackALSARawMidiOutputPort.h" +#include "JackMidiDriver.h" +#include "JackThread.h" + +namespace Jack { + + class JackALSARawMidiDriver: + public JackMidiDriver, public JackRunnableInterface { + + private: + + int fds[2]; + JackALSARawMidiInputPort **input_ports; + JackALSARawMidiOutputPort **output_ports; + nfds_t poll_fd_count; + struct pollfd *poll_fds; + JackThread *thread; + + void + GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info, + std::vector *info_list); + + void + HandleALSAError(const char *driver_func, const char *alsa_func, + int code); + + int + Poll(jack_time_t wait_time); + + public: + + JackALSARawMidiDriver(const char *name, const char *alias, + JackLockedEngine *engine, JackSynchro *table); + ~JackALSARawMidiDriver(); + + int + Attach(); + + int + Close(); + + bool + Execute(); + + bool + Init(); + + int + Open(bool capturing, bool playing, int in_channels, int out_channels, + bool monitoring, const char *capture_driver_name, + const char *playback_driver_name, jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + + int + Read(); + + int + Start(); + + int + Stop(); + + int + Write(); + + }; + +} + +#endif diff --git a/linux/alsarawmidi/JackALSARawMidiInputPort.cpp b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp new file mode 100755 index 00000000..fe0d7911 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp @@ -0,0 +1,121 @@ +#include + +#include "JackALSARawMidiInputPort.h" +#include "JackMidiUtil.h" + +using Jack::JackALSARawMidiInputPort; + +JackALSARawMidiInputPort::JackALSARawMidiInputPort(snd_rawmidi_info_t *info, + size_t index, + size_t max_bytes, + size_t max_messages): + JackALSARawMidiPort(info, index) +{ + alsa_event = 0; + jack_event = 0; + receive_queue = new JackALSARawMidiReceiveQueue(rawmidi, max_bytes); + std::auto_ptr receive_ptr(receive_queue); + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_ptr(thread_queue); + write_queue = new JackMidiBufferWriteQueue(); + std::auto_ptr write_ptr(write_queue); + raw_queue = new JackMidiRawInputWriteQueue(thread_queue, max_bytes, + max_messages); + write_ptr.release(); + thread_ptr.release(); + receive_ptr.release(); +} + +JackALSARawMidiInputPort::~JackALSARawMidiInputPort() +{ + delete raw_queue; + delete receive_queue; + delete thread_queue; + delete write_queue; +} + +jack_nframes_t +JackALSARawMidiInputPort::EnqueueALSAEvent() +{ + switch (raw_queue->EnqueueEvent(alsa_event)) { + case JackMidiWriteQueue::BUFFER_FULL: + // Processing events early might free up some space in the raw queue. + raw_queue->Process(); + switch (raw_queue->EnqueueEvent(alsa_event)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackALSARawMidiInputPort::Process - **BUG** " + "JackMidiRawInputWriteQueue::EnqueueEvent returned " + "`BUFFER_FULL` and then returned `BUFFER_TOO_SMALL` " + "after a `Process()` call."); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + return 0; + default: + ; + } + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackALSARawMidiInputPort::Execute - The thread queue " + "couldn't enqueue a %d-byte packet. Dropping event.", + alsa_event->size); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + return 0; + default: + ; + } + jack_nframes_t alsa_time = alsa_event->time; + jack_nframes_t next_time = raw_queue->Process(); + return (next_time < alsa_time) ? next_time : alsa_time; +} + +jack_nframes_t +JackALSARawMidiInputPort::ProcessALSA() +{ + unsigned short revents = ProcessPollEvents(); + jack_nframes_t frame; + if (alsa_event) { + frame = EnqueueALSAEvent(); + if (frame) { + return frame; + } + } + if (revents & POLLIN) { + for (alsa_event = receive_queue->DequeueEvent(); alsa_event; + alsa_event = receive_queue->DequeueEvent()) { + frame = EnqueueALSAEvent(); + if (frame) { + return frame; + } + } + } + return raw_queue->Process(); +} + +void +JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) +{ + write_queue->ResetMidiBuffer(port_buffer, frames); + if (! jack_event) { + jack_event = thread_queue->DequeueEvent(); + } + for (; jack_event; jack_event = thread_queue->DequeueEvent()) { + // We add `frames` so that MIDI events align with audio as closely as + // possible. + switch (write_queue->EnqueueEvent(jack_event->time + frames, + jack_event->size, + jack_event->buffer)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackALSARawMidiInputPort::Process - The write queue " + "couldn't enqueue a %d-byte event. Dropping event.", + jack_event->size); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + ; + } + break; + } +} diff --git a/linux/alsarawmidi/JackALSARawMidiInputPort.h b/linux/alsarawmidi/JackALSARawMidiInputPort.h new file mode 100755 index 00000000..a5d5a2da --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiInputPort.h @@ -0,0 +1,43 @@ +#ifndef __JackALSARawMidiInputPort__ +#define __JackALSARawMidiInputPort__ + +#include "JackALSARawMidiPort.h" +#include "JackALSARawMidiReceiveQueue.h" +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferWriteQueue.h" +#include "JackMidiRawInputWriteQueue.h" + +namespace Jack { + + class JackALSARawMidiInputPort: public JackALSARawMidiPort { + + private: + + jack_midi_event_t *alsa_event; + jack_midi_event_t *jack_event; + JackMidiRawInputWriteQueue *raw_queue; + JackALSARawMidiReceiveQueue *receive_queue; + JackMidiAsyncQueue *thread_queue; + JackMidiBufferWriteQueue *write_queue; + + jack_nframes_t + EnqueueALSAEvent(); + + public: + + JackALSARawMidiInputPort(snd_rawmidi_info_t *info, size_t index, + size_t max_bytes=4096, + size_t max_messages=1024); + ~JackALSARawMidiInputPort(); + + jack_nframes_t + ProcessALSA(); + + void + ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); + + }; + +} + +#endif diff --git a/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp b/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp new file mode 100755 index 00000000..640d13f6 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp @@ -0,0 +1,146 @@ +#include + +#include "JackALSARawMidiOutputPort.h" + +using Jack::JackALSARawMidiOutputPort; + +JackALSARawMidiOutputPort::JackALSARawMidiOutputPort(snd_rawmidi_info_t *info, + size_t index, + size_t max_bytes, + size_t max_messages): + JackALSARawMidiPort(info, index) +{ + alsa_event = 0; + blocked = false; + read_queue = new JackMidiBufferReadQueue(); + std::auto_ptr read_ptr(read_queue); + send_queue = new JackALSARawMidiSendQueue(rawmidi); + std::auto_ptr send_ptr(send_queue); + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_ptr(thread_queue); + raw_queue = new JackMidiRawOutputWriteQueue(send_queue, max_bytes, + max_messages, max_messages); + thread_ptr.release(); + send_ptr.release(); + read_ptr.release(); +} + +JackALSARawMidiOutputPort::~JackALSARawMidiOutputPort() +{ + delete raw_queue; + delete read_queue; + delete send_queue; + delete thread_queue; +} + +jack_midi_event_t * +JackALSARawMidiOutputPort::DequeueALSAEvent(int read_fd) +{ + jack_midi_event_t *event = thread_queue->DequeueEvent(); + if (event) { + char c; + ssize_t result = read(read_fd, &c, 1); + if (! result) { + jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - **BUG** " + "An event was dequeued from the thread queue, but no " + "byte was available for reading from the pipe file " + "descriptor."); + } else if (result < 0) { + jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - error " + "reading a byte from the pipe file descriptor: %s", + strerror(errno)); + } + } + return event; +} + +jack_nframes_t +JackALSARawMidiOutputPort::ProcessALSA(int read_fd) +{ + unsigned short revents = ProcessPollEvents(); + if (blocked) { + if (! (revents & POLLOUT)) { + return 0; + } + blocked = false; + } + if (! alsa_event) { + alsa_event = DequeueALSAEvent(read_fd); + } + for (; alsa_event; alsa_event = DequeueALSAEvent(read_fd)) { + switch (raw_queue->EnqueueEvent(alsa_event)) { + case JackMidiWriteQueue::BUFFER_FULL: + // Try to free up some space by processing events early. + raw_queue->Process(); + switch (raw_queue->EnqueueEvent(alsa_event)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackALSARawMidiOutputPort::ProcessALSA - **BUG** " + "JackMidiRawOutputWriteQueue::EnqueueEvent " + "returned `BUFFER_FULL`, and then returned " + "`BUFFER_TOO_SMALL` after a Process() call."); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + ; + } + goto process_events; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackALSARawMidiOutputPort::ProcessALSA - The raw " + "output queue couldn't enqueue a %d-byte event. " + "Dropping event.", alsa_event->size); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + ; + } + break; + } + process_events: + jack_nframes_t next_frame = raw_queue->Process(); + blocked = send_queue->IsBlocked(); + if (blocked) { + SetPollEventMask(POLLERR | POLLNVAL | POLLOUT); + return 0; + } + SetPollEventMask(POLLERR | POLLNVAL); + return next_frame; +} + +void +JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames, int write_fd) +{ + read_queue->ResetMidiBuffer(port_buffer); + for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; + event = read_queue->DequeueEvent()) { + if (event->size > thread_queue->GetAvailableSpace()) { + jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread " + "queue doesn't have enough room to enqueue a %d-byte " + "event. Dropping event.", event->size); + continue; + } + char c = 1; + + jack_info("Attempting to write to file descriptor '%d'", write_fd); + + ssize_t result = write(write_fd, &c, 1); + assert(result <= 1); + if (! result) { + jack_error("JackALSARawMidiOutputPort::ProcessJack - Couldn't " + "write a byte to the pipe file descriptor. Dropping " + "event."); + } else if (result < 0) { + jack_error("JackALSARawMidiOutputPort::ProcessJack - error " + "writing a byte to the pipe file descriptor: %s", + strerror(errno)); + } else if (thread_queue->EnqueueEvent(event->time + frames, + event->size, event->buffer) != + JackMidiWriteQueue::OK) { + jack_error("JackALSARawMidiOutputPort::ProcessJack - **BUG** The " + "thread queue said it had enough space to enqueue a " + "%d-byte event, but failed to enqueue the event."); + } + } +} diff --git a/linux/alsarawmidi/JackALSARawMidiOutputPort.h b/linux/alsarawmidi/JackALSARawMidiOutputPort.h new file mode 100755 index 00000000..aea8f2ea --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiOutputPort.h @@ -0,0 +1,44 @@ +#ifndef __JackALSARawMidiOutputPort__ +#define __JackALSARawMidiOutputPort__ + +#include "JackALSARawMidiPort.h" +#include "JackALSARawMidiSendQueue.h" +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferReadQueue.h" +#include "JackMidiRawOutputWriteQueue.h" + +namespace Jack { + + class JackALSARawMidiOutputPort: public JackALSARawMidiPort { + + private: + + jack_midi_event_t *alsa_event; + bool blocked; + JackMidiRawOutputWriteQueue *raw_queue; + JackMidiBufferReadQueue *read_queue; + JackALSARawMidiSendQueue *send_queue; + JackMidiAsyncQueue *thread_queue; + + jack_midi_event_t * + DequeueALSAEvent(int read_fd); + + public: + + JackALSARawMidiOutputPort(snd_rawmidi_info_t *info, size_t index, + size_t max_bytes=4096, + size_t max_messages=1024); + ~JackALSARawMidiOutputPort(); + + jack_nframes_t + ProcessALSA(int read_fd); + + void + ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames, + int write_fd); + + }; + +} + +#endif diff --git a/linux/alsarawmidi/JackALSARawMidiPort.cpp b/linux/alsarawmidi/JackALSARawMidiPort.cpp new file mode 100755 index 00000000..db476003 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiPort.cpp @@ -0,0 +1,158 @@ +#include +#include + +#include "JackALSARawMidiPort.h" +#include "JackError.h" + +using Jack::JackALSARawMidiPort; + +JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, + size_t index) +{ + char device_id[32]; + snprintf(device_id, sizeof(device_id), "hw:%d,%d,%d", + snd_rawmidi_info_get_card(info), + snd_rawmidi_info_get_device(info), + snd_rawmidi_info_get_subdevice(info)); + const char *alias_prefix; + const char *error_message; + snd_rawmidi_t **in; + snd_rawmidi_t **out; + const char *name_suffix; + if (snd_rawmidi_info_get_stream(info) == SND_RAWMIDI_STREAM_OUTPUT) { + alias_prefix = "system:midi_playback_"; + in = 0; + name_suffix = "out"; + out = &rawmidi; + } else { + alias_prefix = "system:midi_capture_"; + in = &rawmidi; + name_suffix = "in"; + out = 0; + } + const char *device_name; + const char *func; + int code = snd_rawmidi_open(in, out, device_id, SND_RAWMIDI_NONBLOCK); + if (code) { + error_message = snd_strerror(code); + func = "snd_rawmidi_open"; + goto handle_error; + } + snd_rawmidi_params_t *params; + code = snd_rawmidi_params_malloc(¶ms); + if (code) { + error_message = snd_strerror(code); + func = "snd_rawmidi_params_malloc"; + goto close; + } + code = snd_rawmidi_params_current(rawmidi, params); + if (code) { + error_message = snd_strerror(code); + func = "snd_rawmidi_params_current"; + goto close; + } + code = snd_rawmidi_params_set_avail_min(rawmidi, params, 1); + if (code) { + error_message = snd_strerror(code); + func = "snd_rawmidi_params_set_avail_min"; + goto free_params; + } + code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1); + if (code) { + error_message = snd_strerror(code); + func = "snd_rawmidi_params_set_no_active_sensing"; + goto free_params; + } + snd_rawmidi_params_free(params); + num_fds = snd_rawmidi_poll_descriptors_count(rawmidi); + if (! num_fds) { + error_message = "returned '0' count for poll descriptors"; + func = "snd_rawmidi_poll_descriptors_count"; + goto close; + } + snprintf(alias, sizeof(alias), "%s%d", alias_prefix, index); + device_name = snd_rawmidi_info_get_subdevice_name(info); + if (! strlen(device_name)) { + device_name = snd_rawmidi_info_get_name(info); + } + snprintf(name, sizeof(name), "system:%s %s", device_name, name_suffix); + return; + free_params: + snd_rawmidi_params_free(params); + close: + snd_rawmidi_close(rawmidi); + handle_error: + throw std::runtime_error(std::string(func) + ": " + error_message); +} + +JackALSARawMidiPort::~JackALSARawMidiPort() +{ + if (rawmidi) { + int code = snd_rawmidi_close(rawmidi); + if (code) { + jack_error("JackALSARawMidiPort::~JackALSARawMidiPort - " + "snd_rawmidi_close: %s", snd_strerror(code)); + } + rawmidi = 0; + } +} + +const char * +JackALSARawMidiPort::GetAlias() +{ + return alias; +} + +const char * +JackALSARawMidiPort::GetName() +{ + return name; +} + +int +JackALSARawMidiPort::GetPollDescriptorCount() +{ + return num_fds; +} + +bool +JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd) +{ + bool result = snd_rawmidi_poll_descriptors(rawmidi, poll_fd, num_fds) == + num_fds; + if (result) { + poll_fds = poll_fd; + } + return result; +} + +int +JackALSARawMidiPort::ProcessPollEvents() +{ + unsigned short revents; + int code = snd_rawmidi_poll_descriptors_revents(rawmidi, poll_fds, num_fds, + &revents); + if (code) { + jack_error("JackALSARawMidiInputPort::ProcessPollEvents - " + "snd_rawmidi_poll_descriptors_revents: %s", + snd_strerror(code)); + return 0; + } + if (revents & POLLNVAL) { + jack_error("JackALSARawMidiInputPort::ProcessPollEvents - the file " + "descriptor is invalid."); + } + if (revents & POLLERR) { + jack_error("JackALSARawMidiInputPort::ProcessPollEvents - an error " + "has occurred on the device or stream."); + } + return revents; +} + +void +JackALSARawMidiPort::SetPollEventMask(unsigned short events) +{ + for (int i = 0; i < num_fds; i++) { + (poll_fds + i)->events = events; + } +} diff --git a/linux/alsarawmidi/JackALSARawMidiPort.h b/linux/alsarawmidi/JackALSARawMidiPort.h new file mode 100755 index 00000000..31ffd078 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiPort.h @@ -0,0 +1,51 @@ +#ifndef __JackALSARawMidiPort__ +#define __JackALSARawMidiPort__ + +#include +#include + +#include "JackConstants.h" + +namespace Jack { + + class JackALSARawMidiPort { + + private: + + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + int num_fds; + struct pollfd *poll_fds; + + protected: + + snd_rawmidi_t *rawmidi; + + int + ProcessPollEvents(); + + void + SetPollEventMask(unsigned short events); + + public: + + JackALSARawMidiPort(snd_rawmidi_info_t *info, size_t index); + virtual ~JackALSARawMidiPort(); + + const char * + GetAlias(); + + const char * + GetName(); + + int + GetPollDescriptorCount(); + + bool + PopulatePollDescriptors(struct pollfd *poll_fd); + + }; + +} + +#endif diff --git a/linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp b/linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp new file mode 100755 index 00000000..a4b24bef --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp @@ -0,0 +1,35 @@ +#include "JackALSARawMidiReceiveQueue.h" +#include "JackError.h" +#include "JackMidiUtil.h" + +using Jack::JackALSARawMidiReceiveQueue; + +JackALSARawMidiReceiveQueue:: +JackALSARawMidiReceiveQueue(snd_rawmidi_t *rawmidi, size_t buffer_size) +{ + buffer = new jack_midi_data_t[buffer_size]; + this->buffer_size = buffer_size; + this->rawmidi = rawmidi; +} + +JackALSARawMidiReceiveQueue::~JackALSARawMidiReceiveQueue() +{ + delete[] buffer; +} + +jack_midi_event_t * +JackALSARawMidiReceiveQueue::DequeueEvent() +{ + ssize_t result = snd_rawmidi_read(rawmidi, buffer, buffer_size); + if (result > 0) { + event.buffer = buffer; + event.size = (size_t) result; + event.time = GetCurrentFrame(); + return &event; + } + if (result && (result != -EWOULDBLOCK)) { + jack_error("JackALSARawMidiReceiveQueue::DequeueEvent - " + "snd_rawmidi_read: %s", snd_strerror(result)); + } + return 0; +} diff --git a/linux/alsarawmidi/JackALSARawMidiReceiveQueue.h b/linux/alsarawmidi/JackALSARawMidiReceiveQueue.h new file mode 100755 index 00000000..a76c1e54 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiReceiveQueue.h @@ -0,0 +1,32 @@ +#ifndef __JackALSARawMidiReceiveQueue__ +#define __JackALSARawMidiReceiveQueue__ + +#include + +#include "JackMidiReceiveQueue.h" + +namespace Jack { + + class JackALSARawMidiReceiveQueue: public JackMidiReceiveQueue { + + private: + + jack_midi_data_t *buffer; + size_t buffer_size; + jack_midi_event_t event; + snd_rawmidi_t *rawmidi; + + public: + + JackALSARawMidiReceiveQueue(snd_rawmidi_t *rawmidi, + size_t buffer_size=4096); + ~JackALSARawMidiReceiveQueue(); + + jack_midi_event_t * + DequeueEvent(); + + }; + +} + +#endif diff --git a/linux/alsarawmidi/JackALSARawMidiSendQueue.cpp b/linux/alsarawmidi/JackALSARawMidiSendQueue.cpp new file mode 100755 index 00000000..5f314263 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiSendQueue.cpp @@ -0,0 +1,40 @@ +#include + +#include "JackALSARawMidiSendQueue.h" +#include "JackMidiUtil.h" + +using Jack::JackALSARawMidiSendQueue; + +JackALSARawMidiSendQueue::JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi) +{ + this->rawmidi = rawmidi; + blocked = false; +} + +Jack::JackMidiWriteQueue::EnqueueResult +JackALSARawMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer) +{ + assert(size == 1); + if (time > GetCurrentFrame()) { + return EVENT_EARLY; + } + ssize_t result = snd_rawmidi_write(rawmidi, buffer, 1); + switch (result) { + case 1: + blocked = false; + return OK; + case -EWOULDBLOCK: + blocked = true; + return BUFFER_FULL; + } + jack_error("JackALSARawMidiSendQueue::EnqueueEvent - snd_rawmidi_write: " + "%s", snd_strerror(result)); + return ERROR; +} + +bool +JackALSARawMidiSendQueue::IsBlocked() +{ + return blocked; +} diff --git a/linux/alsarawmidi/JackALSARawMidiSendQueue.h b/linux/alsarawmidi/JackALSARawMidiSendQueue.h new file mode 100755 index 00000000..f3f6542c --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiSendQueue.h @@ -0,0 +1,32 @@ +#ifndef __JackALSARawMidiSendQueue__ +#define __JackALSARawMidiSendQueue__ + +#include + +#include "JackMidiSendQueue.h" + +namespace Jack { + + class JackALSARawMidiSendQueue: public JackMidiSendQueue { + + private: + + bool blocked; + snd_rawmidi_t *rawmidi; + + public: + + JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi); + + JackMidiWriteQueue::EnqueueResult + EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer); + + bool + IsBlocked(); + + }; + +} + +#endif diff --git a/linux/wscript b/linux/wscript index c28da170..9c45817b 100644 --- a/linux/wscript +++ b/linux/wscript @@ -57,6 +57,14 @@ def build(bld): 'alsa/ice1712.c' ] + alsarawmidi_driver_src = ['alsarawmidi/JackALSARawMidiDriver.cpp', + 'alsarawmidi/JackALSARawMidiInputPort.cpp', + 'alsarawmidi/JackALSARawMidiOutputPort.cpp', + 'alsarawmidi/JackALSARawMidiPort.cpp', + 'alsarawmidi/JackALSARawMidiReceiveQueue.cpp', + 'alsarawmidi/JackALSARawMidiSendQueue.cpp' + ] + ffado_driver_src = ['firewire/JackFFADODriver.cpp', 'firewire/JackFFADOMidiInputPort.cpp', 'firewire/JackFFADOMidiOutputPort.cpp', @@ -66,6 +74,8 @@ def build(bld): if bld.env['BUILD_DRIVER_ALSA'] == True: create_jack_driver_obj(bld, 'alsa', alsa_driver_src, "ALSA") + create_jack_driver_obj(bld, 'alsarawmidi', alsarawmidi_driver_src, + "ALSA") if bld.env['BUILD_DRIVER_FREEBOB'] == True: create_jack_driver_obj(bld, 'freebob', 'freebob/JackFreebobDriver.cpp', "LIBFREEBOB") From 4ced89b41c7bb0d67d074439044e3fcd29e51d00 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 22 Mar 2011 15:13:10 -0700 Subject: [PATCH 07/58] Minor library change that I forgot to add to the last commit. Adds a method to asynchronous queues that checks to available write space in the queue. --- common/JackMidiAsyncQueue.cpp | 7 +++++++ common/JackMidiAsyncQueue.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/common/JackMidiAsyncQueue.cpp b/common/JackMidiAsyncQueue.cpp index 85a93b18..0cfa42dc 100644 --- a/common/JackMidiAsyncQueue.cpp +++ b/common/JackMidiAsyncQueue.cpp @@ -94,3 +94,10 @@ JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size, jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t)); return OK; } + +size_t +JackMidiAsyncQueue::GetAvailableSpace() +{ + return jack_ringbuffer_write_space(info_ring) < INFO_SIZE ? 0 : + max_bytes - jack_ringbuffer_read_space(byte_ring); +} diff --git a/common/JackMidiAsyncQueue.h b/common/JackMidiAsyncQueue.h index 22948cc9..a9ac84ce 100644 --- a/common/JackMidiAsyncQueue.h +++ b/common/JackMidiAsyncQueue.h @@ -84,6 +84,13 @@ namespace Jack { EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer); + /** + * Returns the maximum size event that can be enqueued right *now*. + */ + + size_t + GetAvailableSpace(); + }; } From e2683cb02435b91f43dfbc943c31831a55881bf5 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Wed, 23 Mar 2011 02:44:24 -0700 Subject: [PATCH 08/58] Fix 'alsarawmidi' driver so that it actually works. Add functionality to 'midi_latency_test'. Fix bug in raw write queue implementation. Output error message when a source MIDI port isn't valid during mixdown. Output error messages for error conditions detected in buffer read and write queues. --- common/JackMidiBufferReadQueue.cpp | 16 ++++- common/JackMidiBufferWriteQueue.cpp | 18 ++++-- common/JackMidiPort.cpp | 7 +- common/JackMidiRawInputWriteQueue.cpp | 2 +- example-clients/midi_latency_test.c | 39 +++++++---- linux/alsarawmidi/JackALSARawMidiDriver.cpp | 64 +++++++++---------- linux/alsarawmidi/JackALSARawMidiDriver.h | 2 +- .../alsarawmidi/JackALSARawMidiInputPort.cpp | 1 + .../alsarawmidi/JackALSARawMidiOutputPort.cpp | 7 +- linux/alsarawmidi/JackALSARawMidiPort.cpp | 12 ++-- linux/alsarawmidi/JackALSARawMidiPort.h | 4 +- 11 files changed, 106 insertions(+), 66 deletions(-) diff --git a/common/JackMidiBufferReadQueue.cpp b/common/JackMidiBufferReadQueue.cpp index 66777eb0..c53e596c 100644 --- a/common/JackMidiBufferReadQueue.cpp +++ b/common/JackMidiBufferReadQueue.cpp @@ -46,12 +46,22 @@ JackMidiBufferReadQueue::DequeueEvent() void JackMidiBufferReadQueue::ResetMidiBuffer(JackMidiBuffer *buffer) { + event_count = 0; index = 0; - if (buffer) { + if (! buffer) { + jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " + "to NULL"); + } else if (! buffer->IsValid()) { + jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " + "to invalid buffer"); + } else { + uint32_t lost_events = buffer->lost_events; + if (lost_events) { + jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - %d events " + "lost during mixdown", lost_events); + } this->buffer = buffer; event_count = buffer->event_count; last_frame_time = GetLastFrame(); - } else { - event_count = 0; } } diff --git a/common/JackMidiBufferWriteQueue.cpp b/common/JackMidiBufferWriteQueue.cpp index 5caab49d..7e6ae67f 100644 --- a/common/JackMidiBufferWriteQueue.cpp +++ b/common/JackMidiBufferWriteQueue.cpp @@ -49,9 +49,17 @@ 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; + if (! buffer) { + jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " + "to NULL"); + } else if (! buffer->IsValid()) { + jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " + "to invalid buffer"); + } else { + this->buffer = buffer; + buffer->Reset(frames); + last_frame_time = GetLastFrame(); + max_bytes = buffer->MaxEventSize(); + next_frame_time = last_frame_time + frames; + } } diff --git a/common/JackMidiPort.cpp b/common/JackMidiPort.cpp index 42ee245c..5bd82341 100644 --- a/common/JackMidiPort.cpp +++ b/common/JackMidiPort.cpp @@ -55,7 +55,6 @@ SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time lost_events++; return 0; } - JackMidiEvent* event = &events[event_count++]; event->time = time; event->size = size; @@ -90,7 +89,7 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count { JackMidiBuffer* mix = static_cast(mixbuffer); if (!mix->IsValid()) { - jack_error("MIDI: invalid mix buffer"); + jack_error("Jack::MidiBufferMixdown - invalid mix buffer"); return; } mix->Reset(nframes); @@ -98,8 +97,10 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count int event_count = 0; for (int i = 0; i < src_count; ++i) { JackMidiBuffer* buf = static_cast(src_buffers[i]); - if (!buf->IsValid()) + if (!buf->IsValid()) { + jack_error("Jack::MidiBufferMixdown - invalid source buffer"); return; + } buf->mix_index = 0; event_count += buf->event_count; mix->lost_events += buf->lost_events; diff --git a/common/JackMidiRawInputWriteQueue.cpp b/common/JackMidiRawInputWriteQueue.cpp index e2ff4453..f2853160 100644 --- a/common/JackMidiRawInputWriteQueue.cpp +++ b/common/JackMidiRawInputWriteQueue.cpp @@ -282,7 +282,7 @@ JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte) bool JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame) { - if (event.time < boundary_frame) { + if ((! boundary_frame) || (event.time < boundary_frame)) { switch (write_queue->EnqueueEvent(&event)) { case BUFFER_TOO_SMALL: HandleEventLoss(&event); diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c index 27c651c8..732fe2ab 100644 --- a/example-clients/midi_latency_test.c +++ b/example-clients/midi_latency_test.c @@ -85,7 +85,8 @@ jack_nframes_t lowest_latency; jack_time_t lowest_latency_time; jack_midi_data_t *message_1; jack_midi_data_t *message_2; -size_t messages_processed; +size_t messages_received; +size_t messages_sent; size_t message_size; jack_latency_range_t out_latency_range; jack_port_t *out_port; @@ -100,6 +101,7 @@ int timeout; jack_nframes_t total_latency; jack_time_t total_latency_time; size_t unexpected_messages; +size_t xrun_count; static void output_error(const char *source, const char *message); @@ -156,11 +158,13 @@ handle_process(jack_nframes_t frames, void *arg) } highest_latency = 0; lowest_latency = 0; - messages_processed = 0; + messages_received = 0; + messages_sent = 0; process_state = 1; total_latency = 0; total_latency_time = 0; unexpected_messages = 0; + xrun_count = 0; jack_port_get_latency_range(remote_in_port, JackCaptureLatency, &in_latency_range); jack_port_get_latency_range(remote_out_port, JackPlaybackLatency, @@ -175,7 +179,7 @@ handle_process(jack_nframes_t frames, void *arg) last_frame_time = jack_last_frame_time(client); for (i = 0; i < event_count; i++) { jack_midi_event_get(&event, port_buffer, i); - message = (messages_processed % 2) ? message_2 : message_1; + message = (messages_received % 2) ? message_2 : message_1; if ((event.size == message_size) && (! memcmp(message, event.buffer, message_size * sizeof(jack_midi_data_t)))) { @@ -201,20 +205,20 @@ handle_process(jack_nframes_t frames, void *arg) lowest_latency = frame; lowest_latency_time = time; } - latency_time_values[messages_processed] = time; - latency_values[messages_processed] = frame; + latency_time_values[messages_received] = time; + latency_values[messages_received] = frame; total_latency += frame; total_latency_time += time; - messages_processed++; - if (messages_processed == samples) { + messages_received++; + if (messages_received == samples) { process_state = 2; sem_post(&semaphore); break; } send_message: frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames); - if (frame == frames) { - frame--; + if (frame >= frames) { + frame = frames - 1; } port_buffer = jack_port_get_buffer(out_port, frames); jack_midi_clear_buffer(port_buffer); @@ -223,10 +227,11 @@ handle_process(jack_nframes_t frames, void *arg) set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE); break; } - message = (messages_processed % 2) ? message_2 : message_1; + message = (messages_sent % 2) ? message_2 : message_1; memcpy(buffer, message, message_size * sizeof(jack_midi_data_t)); last_activity = jack_last_frame_time(client) + frame; last_activity_time = jack_frames_to_time(client, last_activity); + messages_sent++; case 2: /* State: finished - do nothing */ @@ -245,6 +250,12 @@ handle_shutdown(void *arg) set_process_error("handle_shutdown", "The JACK server has been shutdown"); } +static void +handle_xrun(void *arg) +{ + xrun_count++; +} + static void output_error(const char *source, const char *message) { @@ -551,8 +562,14 @@ main(int argc, char **argv) printf(" > 10 ms: %u\n", jitter_plot[100]); } } + printf("\nMessages sent: %d\n" + "Messages received: %d\n", + messages_sent, messages_received); if (unexpected_messages) { - printf("\nUnexpected messages received: %d\n", unexpected_messages); + printf("Unexpected messages received: %d\n", unexpected_messages); + } + if (xrun_count) { + printf("Xruns: %d (messages may have been lost)", xrun_count); } deactivate_client: jack_deactivate(client); diff --git a/linux/alsarawmidi/JackALSARawMidiDriver.cpp b/linux/alsarawmidi/JackALSARawMidiDriver.cpp index adbb89fb..8e564c76 100755 --- a/linux/alsarawmidi/JackALSARawMidiDriver.cpp +++ b/linux/alsarawmidi/JackALSARawMidiDriver.cpp @@ -39,8 +39,12 @@ JackALSARawMidiDriver::Attach() { jack_nframes_t buffer_size = fEngineControl->fBufferSize; jack_port_id_t index; + jack_nframes_t latency = buffer_size; + jack_latency_range_t latency_range; const char *name; JackPort *port; + latency_range.max = latency; + latency_range.min = latency; for (int i = 0; i < fCaptureChannels; i++) { JackALSARawMidiInputPort *input_port = input_ports[i]; name = input_port->GetName(); @@ -55,9 +59,14 @@ JackALSARawMidiDriver::Attach() } port = fGraphManager->GetPort(index); port->SetAlias(input_port->GetAlias()); - port->SetLatency(buffer_size); + port->SetLatencyRange(JackCaptureLatency, &latency_range); fCapturePortList[i] = index; } + if (! fEngineControl->fSyncMode) { + latency += buffer_size; + latency_range.max = latency; + latency_range.min = latency; + } for (int i = 0; i < fPlaybackChannels; i++) { JackALSARawMidiOutputPort *output_port = output_ports[i]; name = output_port->GetName(); @@ -72,7 +81,7 @@ JackALSARawMidiDriver::Attach() } port = fGraphManager->GetPort(index); port->SetAlias(output_port->GetAlias()); - port->SetLatency(buffer_size); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); fPlaybackPortList[i] = index; } return 0; @@ -103,23 +112,19 @@ JackALSARawMidiDriver::Execute() { jack_nframes_t timeout_frame = 0; for (;;) { + jack_nframes_t process_frame; jack_time_t wait_time; + jack_time_t *wait_time_ptr; unsigned short revents; if (! timeout_frame) { - wait_time = 0; + wait_time_ptr = 0; } else { jack_time_t next_time = GetTimeFromFrames(timeout_frame); jack_time_t now = GetMicroSeconds(); - - if (next_time <= now) { - goto handle_ports; - } - wait_time = next_time - now; + wait_time = next_time <= now ? 0 : next_time - now; + wait_time_ptr = &wait_time; } - - jack_info("Calling 'Poll' with wait time '%d'.", wait_time); - - if (Poll(wait_time) == -1) { + if (Poll(wait_time_ptr) == -1) { if (errno == EINTR) { continue; } @@ -139,8 +144,7 @@ JackALSARawMidiDriver::Execute() break; } handle_ports: - jack_nframes_t process_frame; - jack_nframes_t timeout_frame = 0; + timeout_frame = 0; for (int i = 0; i < fCaptureChannels; i++) { process_frame = input_ports[i]->ProcessALSA(); if (process_frame && ((! timeout_frame) || @@ -156,9 +160,6 @@ JackALSARawMidiDriver::Execute() } } } - - jack_info("ALSA thread is exiting."); - return false; } @@ -362,15 +363,15 @@ JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels, #ifdef HAVE_PPOLL int -JackALSARawMidiDriver::Poll(jack_time_t wait_time) +JackALSARawMidiDriver::Poll(const jack_time_t *wait_time) { struct timespec timeout; struct timespec *timeout_ptr; if (! wait_time) { timeout_ptr = 0; } else { - timeout.tv_sec = wait_time / 1000000; - timeout.tv_nsec = (wait_time % 1000000) * 1000; + timeout.tv_sec = (*wait_time) / 1000000; + timeout.tv_nsec = ((*wait_time) % 1000000) * 1000; timeout_ptr = &timeout; } return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0); @@ -379,15 +380,15 @@ JackALSARawMidiDriver::Poll(jack_time_t wait_time) #else int -JackALSARawMidiDriver::Poll(jack_time_t wait_time) +JackALSARawMidiDriver::Poll(const jack_time_t *wait_time) { int result = poll(poll_fds, poll_fd_count, - ! wait_time ? -1 : (int) (wait_time / 1000)); + ! wait_time ? -1 : (int) ((*wait_time) / 1000)); if ((! result) && wait_time) { - wait_time %= 1000; - if (wait_time) { + jack_time_t time_left = (*wait_time) % 1000; + if (time_left) { // Cheap hack. - usleep(wait_time); + usleep(time_left); result = poll(poll_fds, poll_fd_count, 0); } } @@ -399,9 +400,9 @@ JackALSARawMidiDriver::Poll(jack_time_t wait_time) int JackALSARawMidiDriver::Read() { + jack_nframes_t buffer_size = fEngineControl->fBufferSize; for (int i = 0; i < fCaptureChannels; i++) { - input_ports[i]->ProcessJack(GetInputBuffer(i), - fEngineControl->fBufferSize); + input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); } return 0; } @@ -412,8 +413,7 @@ JackALSARawMidiDriver::Start() jack_info("JackALSARawMidiDriver::Start - Starting 'alsarawmidi' driver."); - // JackMidiDriver::Start(); - + JackMidiDriver::Start(); poll_fd_count = 1; for (int i = 0; i < fCaptureChannels; i++) { poll_fd_count += input_ports[i]->GetPollDescriptorCount(); @@ -471,11 +471,11 @@ JackALSARawMidiDriver::Start() poll_fd_iter += output_port->GetPollDescriptorCount(); } - jack_info("Starting ALSA thread ..."); + jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ..."); if (! thread->StartSync()) { - jack_info("Started ALSA thread."); + jack_info("JackALSARawMidiDriver::Start - started ALSA thread."); return 0; } @@ -496,7 +496,7 @@ int JackALSARawMidiDriver::Stop() { - jack_info("Stopping 'alsarawmidi' driver."); + jack_info("JackALSARawMidiDriver::Stop - stopping 'alsarawmidi' driver."); if (fds[1] != -1) { close(fds[1]); diff --git a/linux/alsarawmidi/JackALSARawMidiDriver.h b/linux/alsarawmidi/JackALSARawMidiDriver.h index 5a793ebb..0ffc4e52 100755 --- a/linux/alsarawmidi/JackALSARawMidiDriver.h +++ b/linux/alsarawmidi/JackALSARawMidiDriver.h @@ -34,7 +34,7 @@ namespace Jack { int code); int - Poll(jack_time_t wait_time); + Poll(const jack_time_t *wait_time); public: diff --git a/linux/alsarawmidi/JackALSARawMidiInputPort.cpp b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp index fe0d7911..94ae8c4a 100755 --- a/linux/alsarawmidi/JackALSARawMidiInputPort.cpp +++ b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp @@ -101,6 +101,7 @@ JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_event = thread_queue->DequeueEvent(); } for (; jack_event; jack_event = thread_queue->DequeueEvent()) { + // We add `frames` so that MIDI events align with audio as closely as // possible. switch (write_queue->EnqueueEvent(jack_event->time + frames, diff --git a/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp b/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp index 640d13f6..df1fc268 100755 --- a/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp +++ b/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp @@ -101,6 +101,10 @@ JackALSARawMidiOutputPort::ProcessALSA(int read_fd) jack_nframes_t next_frame = raw_queue->Process(); blocked = send_queue->IsBlocked(); if (blocked) { + + jack_info("JackALSARawMidiOutputPort::ProcessALSA - MIDI port is " + "blocked"); + SetPollEventMask(POLLERR | POLLNVAL | POLLOUT); return 0; } @@ -122,9 +126,6 @@ JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, continue; } char c = 1; - - jack_info("Attempting to write to file descriptor '%d'", write_fd); - ssize_t result = write(write_fd, &c, 1); assert(result <= 1); if (! result) { diff --git a/linux/alsarawmidi/JackALSARawMidiPort.cpp b/linux/alsarawmidi/JackALSARawMidiPort.cpp index db476003..a2d3d8fc 100755 --- a/linux/alsarawmidi/JackALSARawMidiPort.cpp +++ b/linux/alsarawmidi/JackALSARawMidiPort.cpp @@ -49,7 +49,7 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info, if (code) { error_message = snd_strerror(code); func = "snd_rawmidi_params_current"; - goto close; + goto free_params; } code = snd_rawmidi_params_set_avail_min(rawmidi, params, 1); if (code) { @@ -129,22 +129,22 @@ JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd) int JackALSARawMidiPort::ProcessPollEvents() { - unsigned short revents; + unsigned short revents = 0; int code = snd_rawmidi_poll_descriptors_revents(rawmidi, poll_fds, num_fds, &revents); if (code) { - jack_error("JackALSARawMidiInputPort::ProcessPollEvents - " + jack_error("JackALSARawMidiPort::ProcessPollEvents - " "snd_rawmidi_poll_descriptors_revents: %s", snd_strerror(code)); return 0; } if (revents & POLLNVAL) { - jack_error("JackALSARawMidiInputPort::ProcessPollEvents - the file " + jack_error("JackALSARawMidiPort::ProcessPollEvents - the file " "descriptor is invalid."); } if (revents & POLLERR) { - jack_error("JackALSARawMidiInputPort::ProcessPollEvents - an error " - "has occurred on the device or stream."); + jack_error("JackALSARawMidiPort::ProcessPollEvents - an error has " + "occurred on the device or stream."); } return revents; } diff --git a/linux/alsarawmidi/JackALSARawMidiPort.h b/linux/alsarawmidi/JackALSARawMidiPort.h index 31ffd078..564e5b7b 100755 --- a/linux/alsarawmidi/JackALSARawMidiPort.h +++ b/linux/alsarawmidi/JackALSARawMidiPort.h @@ -30,7 +30,9 @@ namespace Jack { public: JackALSARawMidiPort(snd_rawmidi_info_t *info, size_t index); - virtual ~JackALSARawMidiPort(); + + virtual + ~JackALSARawMidiPort(); const char * GetAlias(); From 6c5a4ceb84139d56d58850705a4541c4045a04b0 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Wed, 23 Mar 2011 02:48:34 -0700 Subject: [PATCH 09/58] Strangeness concerning the 'Process()' implementation in the JackMidiDriver class: In async mode, if the Write method is run before ResumeRefNum, then events are occasionally lost. This also happens in sync mode, though it shouldn't. I'm committing what I *think* should be the proper implementation, but it's still buggy in sync mode (works in async mode), and I'm not sure why. Someone that's more familiar with JACK 2 threading should take a look at this, remembering that the MIDI drivers are slave drivers. --- common/JackMidiDriver.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 15d507b1..82a3e29a 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -140,26 +140,36 @@ int JackMidiDriver::ProcessNull() int JackMidiDriver::Process() { - // Read input buffers for the current cycle if (Read() < 0) { jack_error("JackMidiDriver::Process: read error, skip cycle"); return 0; // Skip cycle, but continue processing... } - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); if (fEngineControl->fSyncMode) { - if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); - return -1; + int res = 0; + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackMidiDriver::Process - ResumeRefNum error"); + res = -1; + } + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, + DRIVER_TIMEOUT_FACTOR * + fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackMidiDriver::Process - SuspendRefNum error"); + res = -1; + } + if (Write() < 0) { + jack_error("JackMidiDriver::Process - Write error"); } + return res; } - // Write output buffers for the current cycle + // Not in sync mode + if (Write() < 0) { - jack_error("JackMidiDriver::Process: write error, skip cycle"); - return 0; // Skip cycle, but continue processing... + jack_error("JackMidiDriver::Process - Write error"); + } else { + fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); } - return 0; } From ea20f7f4b81f4a37e4590cc202c961794bf2b589 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Wed, 23 Mar 2011 14:54:06 +0100 Subject: [PATCH 10/58] JackMidiDriver::Process in progress. --- common/JackMidiDriver.cpp | 60 +++++++++++++++++++++++++++++++++++++++ common/JackMidiDriver.h | 14 +++++---- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 82a3e29a..b8993e7a 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -138,6 +138,7 @@ int JackMidiDriver::ProcessNull() return 0; } +/* int JackMidiDriver::Process() { if (Read() < 0) { @@ -172,6 +173,65 @@ int JackMidiDriver::Process() } return 0; } +*/ + +int JackMidiDriver::Process() +{ + return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); +} + +int JackMidiDriver::ProcessSync() +{ + int res = 0; + + // Read input buffers for the current cycle + if (Read() < 0) { + jack_error("JackMidiDriver::ProcessSync: read error, skip cycle"); + return 0; // Skip cycle, but continue processing... + } + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackMidiDriver::ProcessSync - ResumeRefNum error"); + res = -1; + } + + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, + DRIVER_TIMEOUT_FACTOR * + fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackMidiDriver::ProcessSync - SuspendRefNum error"); + res = -1; + } + + // Write output buffers from the current cycle + if (Write() < 0) { + jack_error("JackMidiDriver::ProcessSync - Write error"); + } + + return res; +} + +int JackMidiDriver::ProcessAsync() +{ + int res = 0; + + // Read input buffers for the current cycle + if (Read() < 0) { + jack_error("JackMidiDriver::ProcessAsync: read error, skip cycle"); + return 0; // Skip cycle, but continue processing... + } + + // Write output buffers from the previous cycle + if (Write() < 0) { + jack_error("JackMidiDriver::ProcessAsync - Write error"); + } + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackMidiDriver::ProcessAsync - ResumeRefNum error"); + res = -1; + } + + return res; +} JackMidiBuffer* JackMidiDriver::GetInputBuffer(int port_index) { diff --git a/common/JackMidiDriver.h b/common/JackMidiDriver.h index 7c5bc5e0..1ce627d3 100644 --- a/common/JackMidiDriver.h +++ b/common/JackMidiDriver.h @@ -39,15 +39,15 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver int fCaptureChannels; int fPlaybackChannels; - + jack_ringbuffer_t* fRingBuffer[DRIVER_PORT_NUM]; jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; - + JackMidiBuffer* GetInputBuffer(int port_index); JackMidiBuffer* GetOutputBuffer(int port_index); - + public: JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); @@ -62,16 +62,18 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); - + virtual int Process(); + virtual int ProcessSync(); + virtual int ProcessAsync(); virtual int ProcessNull(); virtual int Attach(); virtual int Detach(); - + virtual int Read(); virtual int Write(); - + }; } // end of namespace From 6b57fcbc7461790406fff38675b8f9bd9a7cbe0d Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Wed, 23 Mar 2011 18:23:17 -0700 Subject: [PATCH 11/58] Change system to system_midi to avoid simple semaphore name clashes. --- linux/alsarawmidi/JackALSARawMidiDriver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/alsarawmidi/JackALSARawMidiDriver.cpp b/linux/alsarawmidi/JackALSARawMidiDriver.cpp index 8e564c76..7bbae0a1 100755 --- a/linux/alsarawmidi/JackALSARawMidiDriver.cpp +++ b/linux/alsarawmidi/JackALSARawMidiDriver.cpp @@ -572,8 +572,8 @@ extern "C" { const JSList *params) { Jack::JackDriverClientInterface *driver = - new Jack::JackALSARawMidiDriver("system", "alsarawmidi", engine, - table); + new Jack::JackALSARawMidiDriver("system_midi", "alsarawmidi", + engine, table); if (driver->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0)) { delete driver; driver = 0; From 625af9c67718def913e3f9886c8a9023b50be928 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Wed, 23 Mar 2011 23:09:52 -0700 Subject: [PATCH 12/58] Correct peak jitter measurement to be the difference between the highest latency and lowest latency events instead of the difference between the latency of the event with latency farthest from the average latency and the average latency itself. Actually use the xrun callback. --- example-clients/midi_latency_test.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c index 732fe2ab..1bece0d3 100644 --- a/example-clients/midi_latency_test.c +++ b/example-clients/midi_latency_test.c @@ -250,10 +250,11 @@ handle_shutdown(void *arg) set_process_error("handle_shutdown", "The JACK server has been shutdown"); } -static void +static int handle_xrun(void *arg) { xrun_count++; + return 0; } static void @@ -451,6 +452,11 @@ main(int argc, char **argv) error_source = "jack_set_process_callback"; goto unregister_out_port; } + if (jack_set_xrun_callback(client, handle_xrun, NULL)) { + error_message = "failed to set xrun callback"; + error_source = "jack_set_xrun_callback"; + goto unregister_out_port; + } jack_on_shutdown(client, handle_shutdown, NULL); jack_set_info_function(handle_info); process_state = 0; @@ -502,21 +508,10 @@ main(int argc, char **argv) if (process_state == 2) { double average_latency = ((double) total_latency) / samples; double average_latency_time = total_latency_time / samples; - double high_jitter = highest_latency - average_latency; size_t i; - double low_jitter = average_latency - lowest_latency; - double peak_jitter; - double peak_jitter_time; double sample_rate = (double) jack_get_sample_rate(client); jack_nframes_t total_jitter = 0; jack_time_t total_jitter_time = 0; - if (high_jitter > low_jitter) { - peak_jitter = high_jitter; - peak_jitter_time = highest_latency_time - average_latency_time; - } else { - peak_jitter = low_jitter; - peak_jitter_time = average_latency_time - lowest_latency_time; - } for (i = 0; i <= 100; i++) { jitter_plot[i] = 0; } @@ -537,7 +532,7 @@ main(int argc, char **argv) "Average latency: %.2f ms (%.2f frames)\n" "Best latency: %.2f ms (%u frames)\n" "Worst latency: %.2f ms (%u frames)\n" - "Peak MIDI jitter: %.2f ms (%.2f frames)\n" + "Peak MIDI jitter: %.2f ms (%u frames)\n" "Average MIDI jitter: %.2f ms (%.2f frames)\n", (out_latency_range.min / sample_rate) * 1000.0, (out_latency_range.max / sample_rate) * 1000.0, @@ -548,7 +543,8 @@ main(int argc, char **argv) average_latency_time / 1000.0, average_latency, lowest_latency_time / 1000.0, lowest_latency, highest_latency_time / 1000.0, highest_latency, - peak_jitter_time / 1000.0, peak_jitter, + (highest_latency_time - lowest_latency_time) / 1000.0, + highest_latency - lowest_latency, (total_jitter_time / 1000.0) / samples, ((double) total_jitter) / samples); printf("\nJitter Plot:\n"); @@ -569,7 +565,7 @@ main(int argc, char **argv) printf("Unexpected messages received: %d\n", unexpected_messages); } if (xrun_count) { - printf("Xruns: %d (messages may have been lost)", xrun_count); + printf("Xruns: %d (messages may have been lost)\n", xrun_count); } deactivate_client: jack_deactivate(client); From 916203bcca5359cfbec782ba61dea562179d151d Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 24 Mar 2011 11:28:24 +0100 Subject: [PATCH 13/58] Update XCode project. --- macosx/Jackdmp.xcodeproj/project.pbxproj | 251 +++++++++++++++++++++-- 1 file changed, 236 insertions(+), 15 deletions(-) diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index 705de7c9..efe5e9eb 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 4B66550E127C356E00753A79 /* PBXTargetDependency */, 4B38120313269CCB00C61B14 /* PBXTargetDependency */, 4B8F16FC1329169F0002AD73 /* PBXTargetDependency */, + 4B20220C133A9C370019E213 /* PBXTargetDependency */, ); name = "All Universal 32/64 bits"; productName = All; @@ -114,6 +115,7 @@ 4B19B31B0E2362E800DD4A82 /* JackLibSampleRateResampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B30E0E2362E700DD4A82 /* JackLibSampleRateResampler.cpp */; }; 4B19B31C0E2362E800DD4A82 /* JackLibSampleRateResampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19B30F0E2362E700DD4A82 /* JackLibSampleRateResampler.h */; }; 4B19B31F0E2362E800DD4A82 /* JackResampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B3120E2362E700DD4A82 /* JackResampler.cpp */; }; + 4B20220A133A9C1C0019E213 /* midi_latency_test.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B202209133A9C1C0019E213 /* midi_latency_test.c */; }; 4B2209E112F6BBF300E5DC26 /* JackSocketServerChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B6B30E703B8D0066E42F /* JackSocketServerChannel.cpp */; }; 4B2209E212F6BBF400E5DC26 /* JackSocketServerChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC3B6B40E703B8D0066E42F /* JackSocketServerChannel.h */; }; 4B2209E312F6BBF500E5DC26 /* JackSocketServerNotifyChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B6B50E703B8D0066E42F /* JackSocketServerNotifyChannel.cpp */; }; @@ -865,6 +867,13 @@ remoteGlobalIDString = 4B19B2F60E23620F00DD4A82; remoteInfo = audioadapter; }; + 4B20220B133A9C370019E213 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4B2021DC133A9BA40019E213 /* jack_midi_latency 64 bits */; + remoteInfo = "jack_midi_latency 64 bits"; + }; 4B224B330E65BA330066BE5B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -1418,9 +1427,6 @@ 4B05A0640DF72BC000840F4C /* usx2y.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = usx2y.c; path = ../linux/alsa/usx2y.c; sourceTree = SOURCE_ROOT; }; 4B05A0650DF72BC000840F4C /* usx2y.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = usx2y.h; path = ../linux/alsa/usx2y.h; sourceTree = SOURCE_ROOT; }; 4B05A07D0DF72BC000840F4C /* driver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = driver.h; path = ../linux/driver.h; sourceTree = SOURCE_ROOT; }; - 4B05A07F0DF72BC000840F4C /* ffado_driver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ffado_driver.h; path = ../linux/firewire/ffado_driver.h; sourceTree = SOURCE_ROOT; }; - 4B05A0800DF72BC000840F4C /* JackFFADODriver.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackFFADODriver.cpp; path = ../linux/firewire/JackFFADODriver.cpp; sourceTree = SOURCE_ROOT; }; - 4B05A0810DF72BC000840F4C /* JackFFADODriver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackFFADODriver.h; path = ../linux/firewire/JackFFADODriver.h; sourceTree = SOURCE_ROOT; }; 4B05A0830DF72BC000840F4C /* freebob_driver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = freebob_driver.h; path = ../linux/freebob/freebob_driver.h; sourceTree = SOURCE_ROOT; }; 4B05A0840DF72BC000840F4C /* JackFreebobDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackFreebobDriver.cpp; path = ../linux/freebob/JackFreebobDriver.cpp; sourceTree = SOURCE_ROOT; }; 4B05A0850DF72BC000840F4C /* JackFreebobDriver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackFreebobDriver.h; path = ../linux/freebob/JackFreebobDriver.h; sourceTree = SOURCE_ROOT; }; @@ -1454,6 +1460,8 @@ 4B19B30E0E2362E700DD4A82 /* JackLibSampleRateResampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackLibSampleRateResampler.cpp; path = ../common/JackLibSampleRateResampler.cpp; sourceTree = SOURCE_ROOT; }; 4B19B30F0E2362E700DD4A82 /* JackLibSampleRateResampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackLibSampleRateResampler.h; path = ../common/JackLibSampleRateResampler.h; sourceTree = SOURCE_ROOT; }; 4B19B3120E2362E700DD4A82 /* JackResampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackResampler.cpp; path = ../common/JackResampler.cpp; sourceTree = SOURCE_ROOT; }; + 4B2021E6133A9BA40019E213 /* jack_midi_latency_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_midi_latency_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B202209133A9C1C0019E213 /* midi_latency_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = midi_latency_test.c; path = "../example-clients/midi_latency_test.c"; sourceTree = SOURCE_ROOT; }; 4B2C28F908DAD01E00249230 /* JackGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackGlobals.cpp; path = ../common/JackGlobals.cpp; sourceTree = SOURCE_ROOT; }; 4B3224E510A3156800838A8E /* jack_netone.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = jack_netone.so; sourceTree = BUILT_PRODUCTS_DIR; }; 4B3224E810A315B100838A8E /* JackNetOneDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackNetOneDriver.cpp; path = ../common/JackNetOneDriver.cpp; sourceTree = SOURCE_ROOT; }; @@ -1466,6 +1474,29 @@ 4B32256110A3187800838A8E /* jack_netsource */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_netsource; sourceTree = BUILT_PRODUCTS_DIR; }; 4B32256310A318E300838A8E /* netsource.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = netsource.c; path = "../example-clients/netsource.c"; sourceTree = SOURCE_ROOT; }; 4B32257B10A3190C00838A8E /* jack_netsource */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_netsource; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B349826133A6AF500D130AB /* JackALSARawMidiDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackALSARawMidiDriver.cpp; path = ../linux/alsarawmidi/JackALSARawMidiDriver.cpp; sourceTree = SOURCE_ROOT; }; + 4B349827133A6AF500D130AB /* JackALSARawMidiDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackALSARawMidiDriver.h; path = ../linux/alsarawmidi/JackALSARawMidiDriver.h; sourceTree = SOURCE_ROOT; }; + 4B349828133A6AF500D130AB /* JackALSARawMidiInputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackALSARawMidiInputPort.cpp; path = ../linux/alsarawmidi/JackALSARawMidiInputPort.cpp; sourceTree = SOURCE_ROOT; }; + 4B349829133A6AF500D130AB /* JackALSARawMidiInputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackALSARawMidiInputPort.h; path = ../linux/alsarawmidi/JackALSARawMidiInputPort.h; sourceTree = SOURCE_ROOT; }; + 4B34982A133A6AF500D130AB /* JackALSARawMidiOutputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackALSARawMidiOutputPort.cpp; path = ../linux/alsarawmidi/JackALSARawMidiOutputPort.cpp; sourceTree = SOURCE_ROOT; }; + 4B34982B133A6AF500D130AB /* JackALSARawMidiOutputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackALSARawMidiOutputPort.h; path = ../linux/alsarawmidi/JackALSARawMidiOutputPort.h; sourceTree = SOURCE_ROOT; }; + 4B34982C133A6AF500D130AB /* JackALSARawMidiPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackALSARawMidiPort.cpp; path = ../linux/alsarawmidi/JackALSARawMidiPort.cpp; sourceTree = SOURCE_ROOT; }; + 4B34982D133A6AF500D130AB /* JackALSARawMidiPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackALSARawMidiPort.h; path = ../linux/alsarawmidi/JackALSARawMidiPort.h; sourceTree = SOURCE_ROOT; }; + 4B34982E133A6AF500D130AB /* JackALSARawMidiReceiveQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackALSARawMidiReceiveQueue.cpp; path = ../linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B34982F133A6AF500D130AB /* JackALSARawMidiReceiveQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackALSARawMidiReceiveQueue.h; path = ../linux/alsarawmidi/JackALSARawMidiReceiveQueue.h; sourceTree = SOURCE_ROOT; }; + 4B349830133A6AF500D130AB /* JackALSARawMidiSendQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackALSARawMidiSendQueue.cpp; path = ../linux/alsarawmidi/JackALSARawMidiSendQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B349831133A6AF500D130AB /* JackALSARawMidiSendQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackALSARawMidiSendQueue.h; path = ../linux/alsarawmidi/JackALSARawMidiSendQueue.h; sourceTree = SOURCE_ROOT; }; + 4B349838133A6B6F00D130AB /* ffado_driver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ffado_driver.h; path = ../linux/firewire/ffado_driver.h; sourceTree = SOURCE_ROOT; }; + 4B349839133A6B6F00D130AB /* JackFFADODriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackFFADODriver.cpp; path = ../linux/firewire/JackFFADODriver.cpp; sourceTree = SOURCE_ROOT; }; + 4B34983A133A6B6F00D130AB /* JackFFADODriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackFFADODriver.h; path = ../linux/firewire/JackFFADODriver.h; sourceTree = SOURCE_ROOT; }; + 4B34983B133A6B6F00D130AB /* JackFFADOMidiInputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackFFADOMidiInputPort.cpp; path = ../linux/firewire/JackFFADOMidiInputPort.cpp; sourceTree = SOURCE_ROOT; }; + 4B34983C133A6B6F00D130AB /* JackFFADOMidiInputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackFFADOMidiInputPort.h; path = ../linux/firewire/JackFFADOMidiInputPort.h; sourceTree = SOURCE_ROOT; }; + 4B34983D133A6B6F00D130AB /* JackFFADOMidiOutputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackFFADOMidiOutputPort.cpp; path = ../linux/firewire/JackFFADOMidiOutputPort.cpp; sourceTree = SOURCE_ROOT; }; + 4B34983E133A6B6F00D130AB /* JackFFADOMidiOutputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackFFADOMidiOutputPort.h; path = ../linux/firewire/JackFFADOMidiOutputPort.h; sourceTree = SOURCE_ROOT; }; + 4B34983F133A6B6F00D130AB /* JackFFADOMidiReceiveQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackFFADOMidiReceiveQueue.cpp; path = ../linux/firewire/JackFFADOMidiReceiveQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B349840133A6B6F00D130AB /* JackFFADOMidiReceiveQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackFFADOMidiReceiveQueue.h; path = ../linux/firewire/JackFFADOMidiReceiveQueue.h; sourceTree = SOURCE_ROOT; }; + 4B349841133A6B6F00D130AB /* JackFFADOMidiSendQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackFFADOMidiSendQueue.cpp; path = ../linux/firewire/JackFFADOMidiSendQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B349842133A6B6F00D130AB /* JackFFADOMidiSendQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackFFADOMidiSendQueue.h; path = ../linux/firewire/JackFFADOMidiSendQueue.h; sourceTree = SOURCE_ROOT; }; 4B35C4250D4731D1000DE7AE /* jackdmp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jackdmp; sourceTree = BUILT_PRODUCTS_DIR; }; 4B35C4830D4731D1000DE7AE /* Jackmp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Jackmp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4B35C4FC0D4731D1000DE7AE /* Jackservermp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Jackservermp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1767,6 +1798,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B2021E0133A9BA40019E213 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B3224E010A3156800838A8E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2543,6 +2581,7 @@ 4B3811971326884E00C61B14 /* jack_latent_client */, 4B8F16E513290DC80002AD73 /* jack_midi_dump */, 4B8F16F213290E0E0002AD73 /* jack_midi_dump */, + 4B2021E6133A9BA40019E213 /* jack_midi_latency_test */, ); name = Products; sourceTree = ""; @@ -2550,6 +2589,7 @@ 4B03383E0797E19900686131 /* Simple clients */ = { isa = PBXGroup; children = ( + 4B202209133A9C1C0019E213 /* midi_latency_test.c */, 4B8F16F41329161E0002AD73 /* midi_dump.c */, 4B3811FA13269C8300C61B14 /* latent_client.c */, 4B6654FB127C350100753A79 /* server_control.cpp */, @@ -2579,9 +2619,10 @@ 4B05A0420DF72B8500840F4C /* Linux */ = { isa = PBXGroup; children = ( + 4B349837133A6B6F00D130AB /* firewire */, + 4B349825133A6AF500D130AB /* alsarawmidi */, 4B05A04C0DF72BC000840F4C /* alsa */, 4B05A07D0DF72BC000840F4C /* driver.h */, - 4B05A07E0DF72BC000840F4C /* firewire */, 4B05A0820DF72BC000840F4C /* freebob */, ); name = Linux; @@ -2618,17 +2659,6 @@ path = ../linux/alsa; sourceTree = SOURCE_ROOT; }; - 4B05A07E0DF72BC000840F4C /* firewire */ = { - isa = PBXGroup; - children = ( - 4B05A07F0DF72BC000840F4C /* ffado_driver.h */, - 4B05A0800DF72BC000840F4C /* JackFFADODriver.cpp */, - 4B05A0810DF72BC000840F4C /* JackFFADODriver.h */, - ); - name = firewire; - path = ../linux/firewire; - sourceTree = SOURCE_ROOT; - }; 4B05A0820DF72BC000840F4C /* freebob */ = { isa = PBXGroup; children = ( @@ -2693,6 +2723,45 @@ name = Adapter; sourceTree = ""; }; + 4B349825133A6AF500D130AB /* alsarawmidi */ = { + isa = PBXGroup; + children = ( + 4B349826133A6AF500D130AB /* JackALSARawMidiDriver.cpp */, + 4B349827133A6AF500D130AB /* JackALSARawMidiDriver.h */, + 4B349828133A6AF500D130AB /* JackALSARawMidiInputPort.cpp */, + 4B349829133A6AF500D130AB /* JackALSARawMidiInputPort.h */, + 4B34982A133A6AF500D130AB /* JackALSARawMidiOutputPort.cpp */, + 4B34982B133A6AF500D130AB /* JackALSARawMidiOutputPort.h */, + 4B34982C133A6AF500D130AB /* JackALSARawMidiPort.cpp */, + 4B34982D133A6AF500D130AB /* JackALSARawMidiPort.h */, + 4B34982E133A6AF500D130AB /* JackALSARawMidiReceiveQueue.cpp */, + 4B34982F133A6AF500D130AB /* JackALSARawMidiReceiveQueue.h */, + 4B349830133A6AF500D130AB /* JackALSARawMidiSendQueue.cpp */, + 4B349831133A6AF500D130AB /* JackALSARawMidiSendQueue.h */, + ); + name = alsarawmidi; + path = ../linux/alsarawmidi; + sourceTree = SOURCE_ROOT; + }; + 4B349837133A6B6F00D130AB /* firewire */ = { + isa = PBXGroup; + children = ( + 4B349838133A6B6F00D130AB /* ffado_driver.h */, + 4B349839133A6B6F00D130AB /* JackFFADODriver.cpp */, + 4B34983A133A6B6F00D130AB /* JackFFADODriver.h */, + 4B34983B133A6B6F00D130AB /* JackFFADOMidiInputPort.cpp */, + 4B34983C133A6B6F00D130AB /* JackFFADOMidiInputPort.h */, + 4B34983D133A6B6F00D130AB /* JackFFADOMidiOutputPort.cpp */, + 4B34983E133A6B6F00D130AB /* JackFFADOMidiOutputPort.h */, + 4B34983F133A6B6F00D130AB /* JackFFADOMidiReceiveQueue.cpp */, + 4B349840133A6B6F00D130AB /* JackFFADOMidiReceiveQueue.h */, + 4B349841133A6B6F00D130AB /* JackFFADOMidiSendQueue.cpp */, + 4B349842133A6B6F00D130AB /* JackFFADOMidiSendQueue.h */, + ); + name = firewire; + path = ../linux/firewire; + sourceTree = SOURCE_ROOT; + }; 4B37C20006DF1F900016E567 /* Latency */ = { isa = PBXGroup; children = ( @@ -3066,6 +3135,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B2021DD133A9BA40019E213 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B3224D810A3156800838A8E /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -4111,6 +4187,25 @@ productReference = 4B19B3000E23620F00DD4A82 /* audioadapter.so */; productType = "com.apple.product-type.library.dynamic"; }; + 4B2021DC133A9BA40019E213 /* jack_midi_latency 64 bits */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4B2021E2133A9BA40019E213 /* Build configuration list for PBXNativeTarget "jack_midi_latency 64 bits" */; + buildPhases = ( + 4B2021DD133A9BA40019E213 /* Headers */, + 4B2021DE133A9BA40019E213 /* Sources */, + 4B2021E0133A9BA40019E213 /* Frameworks */, + 4B2021E1133A9BA40019E213 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "jack_midi_latency 64 bits"; + productInstallPath = /usr/local/bin; + productName = jack_metro; + productReference = 4B2021E6133A9BA40019E213 /* jack_midi_latency_test */; + productType = "com.apple.product-type.tool"; + }; 4B3224D710A3156800838A8E /* jack_netone Universal */ = { isa = PBXNativeTarget; buildConfigurationList = 4B3224E110A3156800838A8E /* Build configuration list for PBXNativeTarget "jack_netone Universal" */; @@ -5838,6 +5933,7 @@ 4B35C50A0D4731D1000DE7AE /* jack_midiseq 64 bits */, 4B35C5160D4731D1000DE7AE /* jack_midisine 64 bits */, 4B8F16E813290E0E0002AD73 /* jack_midi_dump 64 bits */, + 4B2021DC133A9BA40019E213 /* jack_midi_latency 64 bits */, 4B35C5220D4731D1000DE7AE /* jack_metro 64 bits */, 4B35C52E0D4731D1000DE7AE /* jack_lsp 64 bits */, 4B35C53A0D4731D1000DE7AE /* jack_connect 64 bits */, @@ -5940,6 +6036,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B2021E1133A9BA40019E213 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B32255C10A3187800838A8E /* Rez */ = { isa = PBXRezBuildPhase; buildActionMask = 2147483647; @@ -6448,6 +6551,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B2021DE133A9BA40019E213 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4B20220A133A9C1C0019E213 /* midi_latency_test.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B3224DC10A3156800838A8E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -7438,6 +7549,11 @@ target = 4B19B2F60E23620F00DD4A82 /* audioadapter Universal */; targetProxy = 4B19B32B0E23636E00DD4A82 /* PBXContainerItemProxy */; }; + 4B20220C133A9C370019E213 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4B2021DC133A9BA40019E213 /* jack_midi_latency 64 bits */; + targetProxy = 4B20220B133A9C370019E213 /* PBXContainerItemProxy */; + }; 4B224B340E65BA330066BE5B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 4B5E08BF0E5B66EE00BEE4E0 /* netadapter Universal */; @@ -8141,6 +8257,101 @@ }; name = Default; }; + 4B2021E3133A9BA40019E213 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ../common; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ( + "-framework", + Jackmp, + "-framework", + CoreFoundation, + ); + OTHER_REZFLAGS = ""; + PRODUCT_NAME = jack_midi_latency_test; + REZ_EXECUTABLE = YES; + SDKROOT = ""; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + ZERO_LINK = YES; + }; + name = Development; + }; + 4B2021E4133A9BA40019E213 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + COPY_PHASE_STRIP = YES; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + HEADER_SEARCH_PATHS = ../common; + MACOSX_DEPLOYMENT_TARGET = 10.4; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ( + "-framework", + Jackmp, + "-framework", + CoreFoundation, + ); + OTHER_REZFLAGS = ""; + PRODUCT_NAME = jack_midi_latency_test; + REZ_EXECUTABLE = YES; + SDKROOT = ""; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + ZERO_LINK = NO; + }; + name = Deployment; + }; + 4B2021E5133A9BA40019E213 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc64, + ppc, + i386, + x86_64, + ); + FRAMEWORK_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = ../common; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ( + "-framework", + Jackmp, + "-framework", + CoreFoundation, + ); + OTHER_REZFLAGS = ""; + PRODUCT_NAME = jack_midisine; + REZ_EXECUTABLE = YES; + SDKROOT = ""; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Default; + }; 4B3224E210A3156800838A8E /* Development */ = { isa = XCBuildConfiguration; buildSettings = { @@ -18735,6 +18946,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Default; }; + 4B2021E2133A9BA40019E213 /* Build configuration list for PBXNativeTarget "jack_midi_latency 64 bits" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4B2021E3133A9BA40019E213 /* Development */, + 4B2021E4133A9BA40019E213 /* Deployment */, + 4B2021E5133A9BA40019E213 /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; 4B3224E110A3156800838A8E /* Build configuration list for PBXNativeTarget "jack_netone Universal" */ = { isa = XCConfigurationList; buildConfigurations = ( From 5360ec7522b28d8e89ef1f7431f85c63b6e5ff68 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 24 Mar 2011 12:41:20 +0100 Subject: [PATCH 14/58] Fix midi_latency_test.c for OSX. --- example-clients/midi_latency_test.c | 34 ++++++++++++++++++++++++ macosx/Jackdmp.xcodeproj/project.pbxproj | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c index 1bece0d3..d9e6fb08 100644 --- a/example-clients/midi_latency_test.c +++ b/example-clients/midi_latency_test.c @@ -95,7 +95,11 @@ char *program_name; jack_port_t *remote_in_port; jack_port_t *remote_out_port; size_t samples; +#ifdef __APPLE__ +sem_t* semaphore; +#else sem_t semaphore; +#endif pthread_mutex_t start_mutex; int timeout; jack_nframes_t total_latency; @@ -212,7 +216,11 @@ handle_process(jack_nframes_t frames, void *arg) messages_received++; if (messages_received == samples) { process_state = 2; + #ifdef __APPLE__ + sem_post(semaphore); + #else sem_post(&semaphore); + #endif break; } send_message: @@ -302,7 +310,11 @@ set_process_error(const char *source, const char *message) error_source = source; error_message = message; process_state = -1; + #ifdef __APPLE__ + sem_post(semaphore); +#else sem_post(&semaphore); +#endif } int @@ -460,11 +472,22 @@ main(int argc, char **argv) jack_on_shutdown(client, handle_shutdown, NULL); jack_set_info_function(handle_info); process_state = 0; +#ifdef __APPLE__ + // sem_init is not implemented on OSX + char name[128]; + sprintf(name, "midi_sem_%p", client); + if ((semaphore = sem_open(name, O_CREAT, 0777, 0)) == (sem_t*)SEM_FAILED) { + error_message = strerror(errno); + error_source = "sem_open"; + goto unregister_out_port; + } +#else if (sem_init(&semaphore, 0, 0)) { error_message = strerror(errno); error_source = "sem_init"; goto unregister_out_port; } +#endif code = pthread_mutex_init(&start_mutex, NULL); if (code) { error_message = strerror(errno); @@ -500,11 +523,15 @@ main(int argc, char **argv) error_source = "pthread_mutex_unlock"; goto deactivate_client; } +#ifdef __APPLE__ + while (sem_wait(semaphore) != 0) {} +#else if (sem_wait(&semaphore)) { error_message = strerror(errno); error_source = "sem_wait"; goto deactivate_client; } +#endif if (process_state == 2) { double average_latency = ((double) total_latency) / samples; double average_latency_time = total_latency_time / samples; @@ -572,7 +599,11 @@ main(int argc, char **argv) destroy_mutex: pthread_mutex_destroy(&start_mutex); destroy_semaphore: +#ifdef __APPLE__ + sem_destroy(semaphore); +#else sem_destroy(&semaphore); +#endif unregister_out_port: jack_port_unregister(client, out_port); unregister_in_port: @@ -592,5 +623,8 @@ main(int argc, char **argv) output_error(error_source, error_message); exit(EXIT_FAILURE); } +#ifdef __APPLE__ + sem_unlink(name); +#endif return EXIT_SUCCESS; } diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index efe5e9eb..edfbae28 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -871,7 +871,7 @@ isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; - remoteGlobalIDString = 4B2021DC133A9BA40019E213 /* jack_midi_latency 64 bits */; + remoteGlobalIDString = 4B2021DC133A9BA40019E213; remoteInfo = "jack_midi_latency 64 bits"; }; 4B224B330E65BA330066BE5B /* PBXContainerItemProxy */ = { From 885381fc9764e4ae327d9f52392f8dedc0177d6c Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Thu, 24 Mar 2011 12:12:16 -0700 Subject: [PATCH 15/58] Add error handling to 'alsarawmidi' driver to make driver fail more gracefully if an ALSA error is detected (i.e. device is unplugged, etc.). Add optional frame_offset parameter to JackMidiWriteQueue::EnqueueEvent. --- common/JackMidiWriteQueue.h | 9 ++-- linux/alsarawmidi/JackALSARawMidiDriver.cpp | 34 ++++++++++---- .../alsarawmidi/JackALSARawMidiInputPort.cpp | 28 +++++++----- linux/alsarawmidi/JackALSARawMidiInputPort.h | 6 +-- .../alsarawmidi/JackALSARawMidiOutputPort.cpp | 44 +++++++++++-------- linux/alsarawmidi/JackALSARawMidiOutputPort.h | 6 +-- linux/alsarawmidi/JackALSARawMidiPort.cpp | 17 +++---- linux/alsarawmidi/JackALSARawMidiPort.h | 4 +- 8 files changed, 89 insertions(+), 59 deletions(-) diff --git a/common/JackMidiWriteQueue.h b/common/JackMidiWriteQueue.h index 8a51bafa..a39776c9 100644 --- a/common/JackMidiWriteQueue.h +++ b/common/JackMidiWriteQueue.h @@ -63,13 +63,16 @@ namespace Jack { jack_midi_data_t *buffer) = 0; /** - * A wrapper method for the `EnqueueEvent` method above. + * A wrapper method for the `EnqueueEvent` method above. The optional + * 'frame_offset' argument is an amount of frames to add to the event's + * time. */ inline EnqueueResult - EnqueueEvent(jack_midi_event_t *event) + EnqueueEvent(jack_midi_event_t *event, jack_nframes_t frame_offset=0) { - return EnqueueEvent(event->time, event->size, event->buffer); + return EnqueueEvent(event->time + frame_offset, event->size, + event->buffer); } }; diff --git a/linux/alsarawmidi/JackALSARawMidiDriver.cpp b/linux/alsarawmidi/JackALSARawMidiDriver.cpp index 7bbae0a1..3f4761e0 100755 --- a/linux/alsarawmidi/JackALSARawMidiDriver.cpp +++ b/linux/alsarawmidi/JackALSARawMidiDriver.cpp @@ -134,32 +134,44 @@ JackALSARawMidiDriver::Execute() } revents = poll_fds[0].revents; if (revents & POLLHUP) { - close(fds[0]); - fds[0] = -1; + // Driver is being stopped. break; } - if (revents & (~(POLLHUP | POLLIN))) { + if (revents & (~ POLLIN)) { jack_error("JackALSARawMidiDriver::Execute - unexpected poll " "event on pipe file descriptor."); break; } - handle_ports: timeout_frame = 0; for (int i = 0; i < fCaptureChannels; i++) { - process_frame = input_ports[i]->ProcessALSA(); + if (! input_ports[i]->ProcessALSA(&process_frame)) { + jack_error("JackALSARawMidiDriver::Execute - a fatal error " + "occurred while processing ALSA input events."); + goto cleanup; + } if (process_frame && ((! timeout_frame) || (process_frame < timeout_frame))) { timeout_frame = process_frame; } } for (int i = 0; i < fPlaybackChannels; i++) { - process_frame = output_ports[i]->ProcessALSA(fds[0]); + if (! output_ports[i]->ProcessALSA(fds[0], &process_frame)) { + jack_error("JackALSARawMidiDriver::Execute - a fatal error " + "occurred while processing ALSA output events."); + goto cleanup; + } if (process_frame && ((! timeout_frame) || (process_frame < timeout_frame))) { timeout_frame = process_frame; } } } + cleanup: + close(fds[0]); + fds[0] = -1; + + jack_info("JackALSARawMidiDriver::Execute - ALSA thread exiting."); + return false; } @@ -402,7 +414,9 @@ JackALSARawMidiDriver::Read() { jack_nframes_t buffer_size = fEngineControl->fBufferSize; for (int i = 0; i < fCaptureChannels; i++) { - input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); + if (! input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size)) { + return -1; + } } return 0; } @@ -539,8 +553,10 @@ JackALSARawMidiDriver::Write() jack_nframes_t buffer_size = fEngineControl->fBufferSize; int write_fd = fds[1]; for (int i = 0; i < fPlaybackChannels; i++) { - output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size, - write_fd); + if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size, + write_fd)) { + return -1; + } } return 0; } diff --git a/linux/alsarawmidi/JackALSARawMidiInputPort.cpp b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp index 94ae8c4a..7167c8b8 100755 --- a/linux/alsarawmidi/JackALSARawMidiInputPort.cpp +++ b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp @@ -69,30 +69,33 @@ JackALSARawMidiInputPort::EnqueueALSAEvent() return (next_time < alsa_time) ? next_time : alsa_time; } -jack_nframes_t -JackALSARawMidiInputPort::ProcessALSA() +bool +JackALSARawMidiInputPort::ProcessALSA(jack_nframes_t *frame) { - unsigned short revents = ProcessPollEvents(); - jack_nframes_t frame; + unsigned short revents; + if (! ProcessPollEvents(&revents)) { + return false; + } if (alsa_event) { - frame = EnqueueALSAEvent(); - if (frame) { - return frame; + *frame = EnqueueALSAEvent(); + if (*frame) { + return true; } } if (revents & POLLIN) { for (alsa_event = receive_queue->DequeueEvent(); alsa_event; alsa_event = receive_queue->DequeueEvent()) { - frame = EnqueueALSAEvent(); - if (frame) { - return frame; + *frame = EnqueueALSAEvent(); + if (*frame) { + return true; } } } - return raw_queue->Process(); + *frame = raw_queue->Process(); + return true; } -void +bool JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames) { @@ -119,4 +122,5 @@ JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, } break; } + return true; } diff --git a/linux/alsarawmidi/JackALSARawMidiInputPort.h b/linux/alsarawmidi/JackALSARawMidiInputPort.h index a5d5a2da..38b8985b 100755 --- a/linux/alsarawmidi/JackALSARawMidiInputPort.h +++ b/linux/alsarawmidi/JackALSARawMidiInputPort.h @@ -30,10 +30,10 @@ namespace Jack { size_t max_messages=1024); ~JackALSARawMidiInputPort(); - jack_nframes_t - ProcessALSA(); + bool + ProcessALSA(jack_nframes_t *frame); - void + bool ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); }; diff --git a/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp b/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp index df1fc268..c1a2bfb8 100755 --- a/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp +++ b/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp @@ -54,13 +54,17 @@ JackALSARawMidiOutputPort::DequeueALSAEvent(int read_fd) return event; } -jack_nframes_t -JackALSARawMidiOutputPort::ProcessALSA(int read_fd) +bool +JackALSARawMidiOutputPort::ProcessALSA(int read_fd, jack_nframes_t *frame) { - unsigned short revents = ProcessPollEvents(); + unsigned short revents; + if (! ProcessPollEvents(&revents)) { + return false; + } if (blocked) { if (! (revents & POLLOUT)) { - return 0; + *frame = 0; + return true; } blocked = false; } @@ -98,7 +102,7 @@ JackALSARawMidiOutputPort::ProcessALSA(int read_fd) break; } process_events: - jack_nframes_t next_frame = raw_queue->Process(); + *frame = raw_queue->Process(); blocked = send_queue->IsBlocked(); if (blocked) { @@ -106,13 +110,14 @@ JackALSARawMidiOutputPort::ProcessALSA(int read_fd) "blocked"); SetPollEventMask(POLLERR | POLLNVAL | POLLOUT); - return 0; + *frame = 0; + } else { + SetPollEventMask(POLLERR | POLLNVAL); } - SetPollEventMask(POLLERR | POLLNVAL); - return next_frame; + return true; } -void +bool JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames, int write_fd) { @@ -128,20 +133,21 @@ JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, char c = 1; ssize_t result = write(write_fd, &c, 1); assert(result <= 1); + if (result < 0) { + jack_error("JackALSARawMidiOutputPort::ProcessJack - error " + "writing a byte to the pipe file descriptor: %s", + strerror(errno)); + return false; + } if (! result) { + // Recoverable. jack_error("JackALSARawMidiOutputPort::ProcessJack - Couldn't " "write a byte to the pipe file descriptor. Dropping " "event."); - } else if (result < 0) { - jack_error("JackALSARawMidiOutputPort::ProcessJack - error " - "writing a byte to the pipe file descriptor: %s", - strerror(errno)); - } else if (thread_queue->EnqueueEvent(event->time + frames, - event->size, event->buffer) != - JackMidiWriteQueue::OK) { - jack_error("JackALSARawMidiOutputPort::ProcessJack - **BUG** The " - "thread queue said it had enough space to enqueue a " - "%d-byte event, but failed to enqueue the event."); + } else { + assert(thread_queue->EnqueueEvent(event, frames) == + JackMidiWriteQueue::OK); } } + return true; } diff --git a/linux/alsarawmidi/JackALSARawMidiOutputPort.h b/linux/alsarawmidi/JackALSARawMidiOutputPort.h index aea8f2ea..816ab6a2 100755 --- a/linux/alsarawmidi/JackALSARawMidiOutputPort.h +++ b/linux/alsarawmidi/JackALSARawMidiOutputPort.h @@ -30,10 +30,10 @@ namespace Jack { size_t max_messages=1024); ~JackALSARawMidiOutputPort(); - jack_nframes_t - ProcessALSA(int read_fd); + bool + ProcessALSA(int read_fd, jack_nframes_t *frame); - void + bool ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames, int write_fd); diff --git a/linux/alsarawmidi/JackALSARawMidiPort.cpp b/linux/alsarawmidi/JackALSARawMidiPort.cpp index a2d3d8fc..a37f9d93 100755 --- a/linux/alsarawmidi/JackALSARawMidiPort.cpp +++ b/linux/alsarawmidi/JackALSARawMidiPort.cpp @@ -126,27 +126,28 @@ JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd) return result; } -int -JackALSARawMidiPort::ProcessPollEvents() +bool +JackALSARawMidiPort::ProcessPollEvents(unsigned short *revents) { - unsigned short revents = 0; int code = snd_rawmidi_poll_descriptors_revents(rawmidi, poll_fds, num_fds, - &revents); + revents); if (code) { jack_error("JackALSARawMidiPort::ProcessPollEvents - " "snd_rawmidi_poll_descriptors_revents: %s", snd_strerror(code)); - return 0; + return false; } - if (revents & POLLNVAL) { + if ((*revents) & POLLNVAL) { jack_error("JackALSARawMidiPort::ProcessPollEvents - the file " "descriptor is invalid."); + return false; } - if (revents & POLLERR) { + if ((*revents) & POLLERR) { jack_error("JackALSARawMidiPort::ProcessPollEvents - an error has " "occurred on the device or stream."); + return false; } - return revents; + return true; } void diff --git a/linux/alsarawmidi/JackALSARawMidiPort.h b/linux/alsarawmidi/JackALSARawMidiPort.h index 564e5b7b..07fb4c0f 100755 --- a/linux/alsarawmidi/JackALSARawMidiPort.h +++ b/linux/alsarawmidi/JackALSARawMidiPort.h @@ -21,8 +21,8 @@ namespace Jack { snd_rawmidi_t *rawmidi; - int - ProcessPollEvents(); + bool + ProcessPollEvents(unsigned short *revents); void SetPollEventMask(unsigned short events); From 5e19eacbeab3365f6f5639421abdafbfc9c33fa0 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Fri, 25 Mar 2011 15:06:45 +0100 Subject: [PATCH 16/58] Correct latency for MIDI backend. --- common/JackMidiDriver.cpp | 10 ++++++++++ macosx/coremidi/JackCoreMidiDriver.cpp | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index b8993e7a..454f046d 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -74,9 +74,12 @@ int JackMidiDriver::Attach() jack_port_id_t port_index; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + jack_latency_range_t latency_range; + jack_nframes_t latency = fEngineControl->fBufferSize; int i; jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + latency_range.max = latency_range.min = latency; for (i = 0; i < fCaptureChannels; i++) { snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); @@ -87,10 +90,16 @@ int JackMidiDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); + port->SetLatencyRange(JackCaptureLatency, &latency_range); fCapturePortList[i] = port_index; jack_log("JackMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } + if (!fEngineControl->fSyncMode) { + latency += fEngineControl->fBufferSize;; + latency_range.max = latency_range.min = latency; + } + for (i = 0; i < fPlaybackChannels; i++) { snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); @@ -100,6 +109,7 @@ int JackMidiDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); fPlaybackPortList[i] = port_index; jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index 1cb7176b..233ac8dd 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -227,9 +227,12 @@ int JackCoreMidiDriver::Attach() char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char endpoint_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + jack_latency_range_t latency_range; + jack_nframes_t latency = fEngineControl->fBufferSize; int i; jack_log("JackCoreMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + latency_range.max = latency_range.min = latency; for (i = 0; i < fCaptureChannels; i++) { @@ -249,10 +252,16 @@ int JackCoreMidiDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); + port->SetLatencyRange(JackCaptureLatency, &latency_range); fCapturePortList[i] = port_index; jack_log("JackCoreMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } + if (!fEngineControl->fSyncMode) { + latency += fEngineControl->fBufferSize;; + latency_range.max = latency_range.min = latency; + } + for (i = 0; i < fPlaybackChannels; i++) { err = MIDIObjectGetStringProperty(fMidiSource[i], kMIDIPropertyName, &pname); @@ -271,6 +280,7 @@ int JackCoreMidiDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); fPlaybackPortList[i] = port_index; jack_log("JackCoreMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } From 981ff8cf459ff4adb7c7c14a45f99bc7497e4355 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Fri, 25 Mar 2011 17:18:37 +0100 Subject: [PATCH 17/58] Major redesign of driver activation (master and salve mode). --- common/JackAudioDriver.cpp | 58 +++++++++++++++++++------- common/JackAudioDriver.h | 8 +++- common/JackDriver.cpp | 27 ++++++++++++- common/JackDriver.h | 34 ++++++++++++++-- common/JackFreewheelDriver.cpp | 74 +++++++++++++++++++++++++++------- common/JackFreewheelDriver.h | 10 +++++ common/JackMidiDriver.cpp | 62 ++++++++++------------------ common/JackMidiDriver.h | 12 ++++-- common/JackThreadedDriver.cpp | 19 ++++++++- common/JackThreadedDriver.h | 9 ++++- 10 files changed, 231 insertions(+), 82 deletions(-) diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index 272c1e66..dedf02b0 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -193,9 +193,9 @@ int JackAudioDriver::ProcessNull() JackDriver::CycleTakeBeginTime(); if (fEngineControl->fSyncMode) { - ProcessGraphSync(); + ProcessGraphSyncMaster(); } else { - ProcessGraphAsync(); + ProcessGraphAsyncMaster(); } // Keep end cycle time @@ -230,9 +230,9 @@ int JackAudioDriver::ProcessAsync() // Process graph if (fIsMaster) { - ProcessGraphAsync(); + ProcessGraphAsyncMaster(); } else { - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); + ProcessGraphAsyncSlave(); } // Keep end cycle time @@ -255,12 +255,12 @@ int JackAudioDriver::ProcessSync() // Process graph if (fIsMaster) { - if (ProcessGraphSync() < 0) { + if (ProcessGraphSyncMaster() < 0) { jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); goto end; } } else { - if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + if (ProcessGraphSyncSlave() < 0) { jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); goto end; } @@ -279,27 +279,50 @@ end: return 0; } -void JackAudioDriver::ProcessGraphAsync() +void JackAudioDriver::ProcessGraphAsyncMaster() { // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle if (!fEngine->Process(fBeginDateUst, fEndDateUst)) - jack_error("JackAudioDriver::ProcessGraphAsync: Process error"); - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); - if (ProcessSlaves() < 0) - jack_error("JackAudioDriver::ProcessGraphAsync: ProcessSlaves error"); + jack_error("JackAudioDriver::ProcessGraphAsyncMaster: Process error"); + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) + jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ResumeRefNum error"); + + if (ProcessReadSlaves() < 0) + jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessReadSlaves error"); + + if (ProcessWriteSlaves() < 0) + jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessWriteSlaves error"); +} + +void JackAudioDriver::ProcessGraphAsyncSlave() +{ + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) + jack_error("JackAudioDriver::ProcessGraphAsyncSlave: ResumeRefNum error"); } -int JackAudioDriver::ProcessGraphSync() +int JackAudioDriver::ProcessGraphSyncMaster() { int res = 0; // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle if (fEngine->Process(fBeginDateUst, fEndDateUst)) { - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); - if (ProcessSlaves() < 0) { - jack_error("JackAudioDriver::ProcessGraphSync: ProcessSlaves error, engine may now behave abnormally!!"); + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackAudioDriver::ProcessGraphSyncMaster: ResumeRefNum error"); res = -1; } + + if (ProcessReadSlaves() < 0) { + jack_error("JackAudioDriver::ProcessGraphSync: ProcessReadSlaves error, engine may now behave abnormally!!"); + res = -1; + } + + if (ProcessWriteSlaves() < 0) { + jack_error("JackAudioDriver::ProcessGraphSync: ProcessWriteSlaves error, engine may now behave abnormally!!"); + res = -1; + } + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { jack_error("JackAudioDriver::ProcessGraphSync: SuspendRefNum error, engine may now behave abnormally!!"); res = -1; @@ -312,6 +335,11 @@ int JackAudioDriver::ProcessGraphSync() return res; } +int JackAudioDriver::ProcessGraphSyncSlave() +{ + return fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); +} + int JackAudioDriver::Start() { int res = JackDriver::Start(); diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index 8eaca692..cb0ebefe 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -35,8 +35,12 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver protected: - void ProcessGraphAsync(); - int ProcessGraphSync(); + void ProcessGraphAsyncMaster(); + void ProcessGraphAsyncSlave(); + + int ProcessGraphSyncMaster(); + int ProcessGraphSyncSlave(); + void WaitUntilNextCycle(); virtual int ProcessAsync(); diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index 657524bd..77b3850e 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -282,19 +282,42 @@ void JackDriver::RemoveSlave(JackDriverInterface* slave) fSlaveList.remove(slave); } -int JackDriver::ProcessSlaves() +int JackDriver::ProcessReadSlaves() { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; - if (slave->Process() < 0) + if (slave->ProcessRead() < 0) res = -1; } return res; } +int JackDriver::ProcessWriteSlaves() +{ + int res = 0; + list::const_iterator it; + for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { + JackDriverInterface* slave = *it; + if (slave->ProcessWrite() < 0) + res = -1; + + } + return res; +} + +int JackDriver::ProcessRead() +{ + return 0; +} + +int JackDriver::ProcessWrite() +{ + return 0; +} + int JackDriver::Process() { return 0; diff --git a/common/JackDriver.h b/common/JackDriver.h index 68f937c2..e619f6e4 100644 --- a/common/JackDriver.h +++ b/common/JackDriver.h @@ -34,6 +34,7 @@ namespace Jack class JackLockedEngine; class JackGraphManager; struct JackEngineControl; +class JackSlaveDriverInterface; /*! \brief The base interface for drivers. @@ -91,10 +92,17 @@ class SERVER_EXPORT JackDriverInterface virtual void SetMaster(bool onoff) = 0; virtual bool GetMaster() = 0; + virtual void AddSlave(JackDriverInterface* slave) = 0; virtual void RemoveSlave(JackDriverInterface* slave) = 0; + virtual std::list GetSlaves() = 0; - virtual int ProcessSlaves() = 0; + + virtual int ProcessReadSlaves() = 0; + virtual int ProcessWriteSlaves() = 0; + + virtual int ProcessRead() = 0; + virtual int ProcessWrite() = 0; virtual bool IsRealTime() const = 0; virtual bool IsRunning() const = 0; @@ -159,11 +167,11 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface void AddSlave(JackDriverInterface* slave); void RemoveSlave(JackDriverInterface* slave); + std::list GetSlaves() { return fSlaveList; } - int ProcessSlaves(); virtual int Open(); @@ -200,10 +208,17 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface virtual int Write(); virtual int Start(); - virtual int StartSlaves(); virtual int Stop(); + + virtual int StartSlaves(); virtual int StopSlaves(); + int ProcessReadSlaves(); + int ProcessWriteSlaves(); + + int ProcessRead(); + int ProcessWrite(); + virtual bool IsFixedBufferSize(); virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetSampleRate(jack_nframes_t sample_rate); @@ -217,6 +232,19 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface }; +/* +class SERVER_EXPORT JackSlaveDriverInterface +{ + + public: + + virtual int ProcessRead() { return 0; } + virtual int ProcessWrite() { return 0; } + +}; + +*/ + } // end of namespace #endif diff --git a/common/JackFreewheelDriver.cpp b/common/JackFreewheelDriver.cpp index c28e30c3..2928b211 100644 --- a/common/JackFreewheelDriver.cpp +++ b/common/JackFreewheelDriver.cpp @@ -28,26 +28,72 @@ namespace Jack int JackFreewheelDriver::Process() { - if (fIsMaster) { - jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); - JackDriver::CycleTakeBeginTime(); - fEngine->Process(fBeginDateUst, fEndDateUst); - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients + int res = 0; + + jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); + JackDriver::CycleTakeBeginTime(); + + if (fEngine->Process(fBeginDateUst, fEndDateUst)) { + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable)) { // Signal all clients + jack_error("JackFreewheelDriver::Process: ResumeRefNum error"); + res = -1; + } + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, FREEWHEEL_DRIVER_TIMEOUT * 1000000) < 0) { // Wait for all clients to finish for 10 sec - jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); + jack_error("JackFreewheelDriver::ProcessSync: SuspendRefNum error"); /* We have a client time-out error, but still continue to process, until a better recovery strategy is chosen */ return 0; } - } else { - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients - if (fEngineControl->fSyncMode) { - if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); - return -1; - } - } + + } else { // Graph not finished: do not activate it + jack_error("JackFreewheelDriver::Process: Process error"); + res = -1; + } + + return res; +} + +int JackFreewheelDriver::ProcessRead() +{ + return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); +} + +int JackFreewheelDriver::ProcessWrite() +{ + return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); +} + +int JackFreewheelDriver::ProcessReadSync() +{ + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { // Signal all clients + jack_error("JackFreewheelDriver::ProcessReadSync: ResumeRefNum error"); + return -1; + } + return 0; +} + +int JackFreewheelDriver::ProcessWriteSync() +{ + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); + return -1; + } + return 0; +} + +int JackFreewheelDriver::ProcessReadAsync() +{ + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { // Signal all clients + jack_error("JackFreewheelDriver::ProcessReadAsync: ResumeRefNum error"); + return -1; } return 0; } +int JackFreewheelDriver::ProcessWriteAsync() +{ + return 0; +} + } // end of namespace diff --git a/common/JackFreewheelDriver.h b/common/JackFreewheelDriver.h index b988ffb0..02a9cf76 100644 --- a/common/JackFreewheelDriver.h +++ b/common/JackFreewheelDriver.h @@ -46,6 +46,16 @@ class JackFreewheelDriver : public JackDriver } int Process(); + + int ProcessRead(); + int ProcessWrite(); + + int ProcessReadSync(); + int ProcessWriteSync(); + + int ProcessReadAsync(); + int ProcessWriteAsync(); + }; } // end of namespace diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 454f046d..a2fffa83 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -148,56 +148,24 @@ int JackMidiDriver::ProcessNull() return 0; } -/* -int JackMidiDriver::Process() +int JackMidiDriver::ProcessRead() { - if (Read() < 0) { - jack_error("JackMidiDriver::Process: read error, skip cycle"); - return 0; // Skip cycle, but continue processing... - } - - if (fEngineControl->fSyncMode) { - int res = 0; - if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { - jack_error("JackMidiDriver::Process - ResumeRefNum error"); - res = -1; - } - if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, - DRIVER_TIMEOUT_FACTOR * - fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackMidiDriver::Process - SuspendRefNum error"); - res = -1; - } - if (Write() < 0) { - jack_error("JackMidiDriver::Process - Write error"); - } - return res; - } - - // Not in sync mode - - if (Write() < 0) { - jack_error("JackMidiDriver::Process - Write error"); - } else { - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); - } - return 0; + return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); } -*/ -int JackMidiDriver::Process() +int JackMidiDriver::ProcessWrite() { - return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); + return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); } -int JackMidiDriver::ProcessSync() +int JackMidiDriver::ProcessReadSync() { int res = 0; // Read input buffers for the current cycle if (Read() < 0) { jack_error("JackMidiDriver::ProcessSync: read error, skip cycle"); - return 0; // Skip cycle, but continue processing... + res = -1; } if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { @@ -205,6 +173,13 @@ int JackMidiDriver::ProcessSync() res = -1; } + return res; +} + +int JackMidiDriver::ProcessWriteSync() +{ + int res = 0; + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { @@ -215,24 +190,26 @@ int JackMidiDriver::ProcessSync() // Write output buffers from the current cycle if (Write() < 0) { jack_error("JackMidiDriver::ProcessSync - Write error"); + res = -1; } return res; } -int JackMidiDriver::ProcessAsync() +int JackMidiDriver::ProcessReadAsync() { int res = 0; // Read input buffers for the current cycle if (Read() < 0) { jack_error("JackMidiDriver::ProcessAsync: read error, skip cycle"); - return 0; // Skip cycle, but continue processing... + res = -1; } // Write output buffers from the previous cycle if (Write() < 0) { jack_error("JackMidiDriver::ProcessAsync - Write error"); + res = -1; } if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { @@ -243,6 +220,11 @@ int JackMidiDriver::ProcessAsync() return res; } +int JackMidiDriver::ProcessWriteAsync() +{ + return 0; +} + JackMidiBuffer* JackMidiDriver::GetInputBuffer(int port_index) { assert(fCapturePortList[port_index]); diff --git a/common/JackMidiDriver.h b/common/JackMidiDriver.h index 1ce627d3..45fe484c 100644 --- a/common/JackMidiDriver.h +++ b/common/JackMidiDriver.h @@ -48,6 +48,12 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver JackMidiBuffer* GetInputBuffer(int port_index); JackMidiBuffer* GetOutputBuffer(int port_index); + virtual int ProcessReadSync(); + virtual int ProcessWriteSync(); + + virtual int ProcessReadAsync(); + virtual int ProcessWriteAsync(); + public: JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); @@ -63,9 +69,9 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver jack_nframes_t capture_latency, jack_nframes_t playback_latency); - virtual int Process(); - virtual int ProcessSync(); - virtual int ProcessAsync(); + virtual int ProcessRead(); + virtual int ProcessWrite(); + virtual int ProcessNull(); virtual int Attach(); diff --git a/common/JackThreadedDriver.cpp b/common/JackThreadedDriver.cpp index 88323fe5..62260e59 100644 --- a/common/JackThreadedDriver.cpp +++ b/common/JackThreadedDriver.cpp @@ -127,9 +127,24 @@ void JackThreadedDriver::RemoveSlave(JackDriverInterface* slave) fDriver->RemoveSlave(slave); } -int JackThreadedDriver::ProcessSlaves() +int JackThreadedDriver::ProcessReadSlaves() { - return fDriver->ProcessSlaves(); + return fDriver->ProcessReadSlaves(); +} + +int JackThreadedDriver::ProcessWriteSlaves() +{ + return fDriver->ProcessWriteSlaves(); +} + +int JackThreadedDriver::ProcessRead() +{ + return fDriver->ProcessRead(); +} + +int JackThreadedDriver::ProcessWrite() +{ + return fDriver->ProcessWrite(); } std::list JackThreadedDriver::GetSlaves() diff --git a/common/JackThreadedDriver.h b/common/JackThreadedDriver.h index 92ec1d26..9d4876b1 100644 --- a/common/JackThreadedDriver.h +++ b/common/JackThreadedDriver.h @@ -89,10 +89,17 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi virtual void SetMaster(bool onoff); virtual bool GetMaster(); + virtual void AddSlave(JackDriverInterface* slave); virtual void RemoveSlave(JackDriverInterface* slave); + virtual std::list GetSlaves(); - virtual int ProcessSlaves(); + + virtual int ProcessReadSlaves(); + virtual int ProcessWriteSlaves(); + + virtual int ProcessRead(); + virtual int ProcessWrite(); virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); virtual JackClientControl* GetClientControl() const; From faf29128cf7925735ac671ae50dfe55fff89639b Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Fri, 25 Mar 2011 18:40:14 +0100 Subject: [PATCH 18/58] Cleanup JackCoreAudioAdapter. --- macosx/coreaudio/JackCoreAudioAdapter.cpp | 67 +++++++++++------------ macosx/coreaudio/JackCoreAudioDriver.cpp | 11 ++-- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/macosx/coreaudio/JackCoreAudioAdapter.cpp b/macosx/coreaudio/JackCoreAudioAdapter.cpp index c1e9dc5e..d086a877 100644 --- a/macosx/coreaudio/JackCoreAudioAdapter.cpp +++ b/macosx/coreaudio/JackCoreAudioAdapter.cpp @@ -168,7 +168,7 @@ OSStatus JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice, switch (inPropertyID) { case kAudioDevicePropertyNominalSampleRate: { - jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); + jack_log("JackCoreAudioAdapter::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); driver->fState = true; break; } @@ -430,12 +430,15 @@ OSStatus JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID* id) jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); // Get the device only if default input and output are the same - if (inDefault == outDefault) { - *id = inDefault; - return noErr; - } else { + if (inDefault != outDefault) { jack_error("Default input and output devices are not the same !!"); return kAudioHardwareBadDeviceError; + } else if (inDefault == 0) { + jack_error("Default input and output devices are null !!"); + return kAudioHardwareBadDeviceError; + } else { + *id = inDefault; + return noErr; } } @@ -444,20 +447,16 @@ OSStatus JackCoreAudioAdapter::GetTotalChannels(AudioDeviceID device, int& chann OSStatus err = noErr; UInt32 outSize; Boolean outWritable; - AudioBufferList* bufferList = 0; channelCount = 0; err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); if (err == noErr) { - bufferList = (AudioBufferList*)malloc(outSize); + AudioBufferList bufferList[outSize]; err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); if (err == noErr) { for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) channelCount += bufferList->mBuffers[i].mNumberChannels; } - - if (bufferList) - free(bufferList); } return err; @@ -604,7 +603,7 @@ int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, // Use default driver in duplex mode } else { - jack_log("JackCoreAudioDriver::Open default driver"); + jack_log("JackCoreAudioAdapter::Open default driver"); if (GetDefaultDevice(&fDeviceID) != noErr) { jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); @@ -1030,14 +1029,14 @@ OSStatus JackCoreAudioAdapter::DestroyAggregateDevice() osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); + jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); printError(osErr); return osErr; } osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error"); + jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyData error"); printError(osErr); return osErr; } @@ -1115,18 +1114,18 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca for (UInt32 i = 0; i < captureDeviceID.size(); i++) { if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of input device"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of input device"); } else { // Check clock domain osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); if (osErr != 0) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); printError(osErr); } else { keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; - jack_log("JackCoreAudioDriver::CreateAggregateDevice : input clockdomain = %d", clockdomain); + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : input clockdomain = %d", clockdomain); if (clockdomain != 0 && clockdomain != keptclockdomain) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); need_clock_drift_compensation = true; } } @@ -1135,18 +1134,18 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of output device"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of output device"); } else { // Check clock domain osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); if (osErr != 0) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); printError(osErr); } else { keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; - jack_log("JackCoreAudioDriver::CreateAggregateDevice : output clockdomain = %d", clockdomain); + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : output clockdomain = %d", clockdomain); if (clockdomain != 0 && clockdomain != keptclockdomain) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); need_clock_drift_compensation = true; } } @@ -1175,7 +1174,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); printError(osErr); return osErr; } @@ -1191,7 +1190,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); printError(osErr); return osErr; } @@ -1218,13 +1217,13 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca SInt32 system; Gestalt(gestaltSystemVersion, &system); - jack_log("JackCoreAudioDriver::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054); + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054); // Starting with 10.5.4 systems, the AD can be internal... (better) if (system < 0x00001054) { - jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device...."); + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : public aggregate device...."); } else { - jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device...."); + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : private aggregate device...."); CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); } @@ -1306,14 +1305,14 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); printError(osErr); goto error; } osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyData error"); printError(osErr); goto error; } @@ -1332,7 +1331,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca outDataSize = sizeof(CFMutableArrayRef); osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); printError(osErr); goto error; } @@ -1352,7 +1351,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca outDataSize = sizeof(CFStringRef); osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First apture is master... if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); printError(osErr); goto error; } @@ -1370,19 +1369,19 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca // Get the property data size osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); printError(osErr); } // Calculate the number of object IDs subDevicesNum = outSize / sizeof(AudioObjectID); - jack_info("JackCoreAudioDriver::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum); + jack_info("JackCoreAudioAdapter::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum); AudioObjectID subDevices[subDevicesNum]; outSize = sizeof(subDevices); osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); printError(osErr); } @@ -1391,7 +1390,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector ca UInt32 theDriftCompensationValue = 1; osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); if (osErr != noErr) { - jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); + jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); printError(osErr); } } diff --git a/macosx/coreaudio/JackCoreAudioDriver.cpp b/macosx/coreaudio/JackCoreAudioDriver.cpp index 83806646..383babf5 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.cpp +++ b/macosx/coreaudio/JackCoreAudioDriver.cpp @@ -386,12 +386,15 @@ OSStatus JackCoreAudioDriver::GetDefaultDevice(AudioDeviceID* id) jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); // Get the device only if default input and output are the same - if (inDefault == outDefault) { - *id = inDefault; - return noErr; - } else { + if (inDefault != outDefault) { jack_error("Default input and output devices are not the same !!"); return kAudioHardwareBadDeviceError; + } else if (inDefault == 0) { + jack_error("Default input and output devices are null !!"); + return kAudioHardwareBadDeviceError; + } else { + *id = inDefault; + return noErr; } } From 280bf48a5ff9e78cef92a3563909146cd1a00165 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Fri, 25 Mar 2011 18:45:30 -0700 Subject: [PATCH 19/58] Have JackALSARawMidiInputPort use inline 'EnqueueEvent' equivalent. --- linux/alsarawmidi/JackALSARawMidiInputPort.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/linux/alsarawmidi/JackALSARawMidiInputPort.cpp b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp index 7167c8b8..5ed14882 100755 --- a/linux/alsarawmidi/JackALSARawMidiInputPort.cpp +++ b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp @@ -107,13 +107,11 @@ JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, // We add `frames` so that MIDI events align with audio as closely as // possible. - switch (write_queue->EnqueueEvent(jack_event->time + frames, - jack_event->size, - jack_event->buffer)) { + switch (write_queue->EnqueueEvent(jack_event, frames)) { case JackMidiWriteQueue::BUFFER_TOO_SMALL: - jack_error("JackALSARawMidiInputPort::Process - The write queue " - "couldn't enqueue a %d-byte event. Dropping event.", - jack_event->size); + jack_error("JackALSARawMidiInputPort::ProcessJack - The write " + "queue couldn't enqueue a %d-byte event. Dropping " + "event.", jack_event->size); // Fallthrough on purpose case JackMidiWriteQueue::OK: continue; From 0429665581710d47f01560be597562386c137010 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Fri, 25 Mar 2011 20:59:22 -0700 Subject: [PATCH 20/58] Overhaulled the CoreMIDI driver. Added queue objects. Added proper event times (I think). WARNING: I've never programmed for Mac. I don't have a Mac development environment. I haven't tested or compiled the code I'm committing (Stephane said he'd take care of that). Don't expect this code to run without a thorough debug and fix session. That said, I hope this code can be turned into something workable. --- macosx/coremidi/JackCoreMidiDriver.cpp | 787 ++++++++++++------ macosx/coremidi/JackCoreMidiDriver.h | 94 ++- macosx/coremidi/JackCoreMidiInputPort.cpp | 181 ++++ macosx/coremidi/JackCoreMidiInputPort.h | 73 ++ macosx/coremidi/JackCoreMidiOutputPort.cpp | 203 +++++ macosx/coremidi/JackCoreMidiOutputPort.h | 83 ++ .../JackCoreMidiPhysicalInputPort.cpp | 53 ++ .../coremidi/JackCoreMidiPhysicalInputPort.h | 45 + .../JackCoreMidiPhysicalOutputPort.cpp | 65 ++ .../coremidi/JackCoreMidiPhysicalOutputPort.h | 56 ++ macosx/coremidi/JackCoreMidiPort.cpp | 90 ++ macosx/coremidi/JackCoreMidiPort.h | 67 ++ macosx/coremidi/JackCoreMidiUtil.cpp | 44 + macosx/coremidi/JackCoreMidiUtil.h | 38 + .../coremidi/JackCoreMidiVirtualInputPort.cpp | 75 ++ .../coremidi/JackCoreMidiVirtualInputPort.h | 50 ++ .../JackCoreMidiVirtualOutputPort.cpp | 72 ++ .../coremidi/JackCoreMidiVirtualOutputPort.h | 50 ++ 18 files changed, 1828 insertions(+), 298 deletions(-) create mode 100644 macosx/coremidi/JackCoreMidiInputPort.cpp create mode 100644 macosx/coremidi/JackCoreMidiInputPort.h create mode 100644 macosx/coremidi/JackCoreMidiOutputPort.cpp create mode 100644 macosx/coremidi/JackCoreMidiOutputPort.h create mode 100644 macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp create mode 100644 macosx/coremidi/JackCoreMidiPhysicalInputPort.h create mode 100644 macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp create mode 100644 macosx/coremidi/JackCoreMidiPhysicalOutputPort.h create mode 100644 macosx/coremidi/JackCoreMidiPort.cpp create mode 100644 macosx/coremidi/JackCoreMidiPort.h create mode 100644 macosx/coremidi/JackCoreMidiUtil.cpp create mode 100644 macosx/coremidi/JackCoreMidiUtil.h create mode 100644 macosx/coremidi/JackCoreMidiVirtualInputPort.cpp create mode 100644 macosx/coremidi/JackCoreMidiVirtualInputPort.h create mode 100644 macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp create mode 100644 macosx/coremidi/JackCoreMidiVirtualOutputPort.h diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index 1cb7176b..4be05c2d 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2009 Grame +Copyright (C) 2011 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,345 +18,616 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + +#include + #include "JackCoreMidiDriver.h" -#include "JackGraphManager.h" -#include "JackServer.h" +#include "JackCoreMidiUtil.h" #include "JackEngineControl.h" -#include "JackDriverLoader.h" -#include -#include -#include -#include -#include +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// -namespace Jack +void +JackCoreMidiDriver::HandleInputEvent(const MIDIPacketList *packet_list, + void *driver, void *port) { + ((JackCoreMidiPhysicalInputPort *) port)->ProcessCoreMidi(packet_list); +} -static MIDITimeStamp MIDIGetCurrentHostTime() +void +JackCoreMidiDriver::HandleNotificationEvent(const MIDINotification *message, + void *driver) { - return mach_absolute_time(); + ((JackCoreMidiDriver *) driver)->HandleNotification(message); } -void JackCoreMidiDriver::ReadProcAux(const MIDIPacketList *pktlist, jack_ringbuffer_t* ringbuffer) +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackCoreMidiDriver::JackCoreMidiDriver(const char *name, const char *alias, + JackLockedEngine *engine, + JackSynchro *table): + JackMidiDriver(name, alias, engine, table) { - // Write the number of packets - size_t size = jack_ringbuffer_write_space(ringbuffer); - if (size < sizeof(UInt32)) { - jack_error("ReadProc : ring buffer is full, skip events..."); - return; + mach_timebase_info_data_t info; + kern_return_t result = mach_timebase_info(&info); + if (result != KERN_SUCCESS) { + throw std::runtime_error(mach_error_string(result)); } + client = 0; + fCaptureChannels = 0; + fPlaybackChannels = 0; + num_physical_inputs = 0; + num_physical_outputs = 0; + num_virtual_inputs = 0; + num_virtual_outputs = 0; + physical_input_ports = 0; + physical_output_ports = 0; + time_ratio = (((double) info.numer) / info.denom) / 1000.0; + virtual_input_ports = 0; + virtual_output_ports = 0; +} - jack_ringbuffer_write(ringbuffer, (char*)&pktlist->numPackets, sizeof(UInt32)); +JackCoreMidiDriver::~JackCoreMidiDriver() +{ + Stop(); + Close(); +} - for (unsigned int i = 0; i < pktlist->numPackets; ++i) { +int +JackCoreMidiDriver::Attach() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + jack_port_id_t index; + jack_nframes_t latency = buffer_size; + jack_latency_range_t latency_range; + const char *name; + JackPort *port; + JackCoreMidiPort *port_obj; + latency_range.max = latency; + latency_range.min = latency; + + // Physical inputs + for (int i = 0; i < num_physical_inputs; i++) { + port_obj = physical_input_ports[i]; + name = port_obj->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackCoreMidiDriver::Attach - cannot register physical " + "input port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackCaptureLatency, &latency_range); + fCapturePortList[i] = index; + } - MIDIPacket *packet = (MIDIPacket *)pktlist->packet; + // Virtual inputs + for (int i = 0; i < num_virtual_inputs; i++) { + port_obj = virtual_input_ports[i]; + name = port_obj->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackCoreMidiDriver::Attach - cannot register virtual " + "input port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackCaptureLatency, &latency_range); + fCapturePortList[num_physical_inputs + i] = index; + } - // TODO : use timestamp + if (! fEngineControl->fSyncMode) { + latency += buffer_size; + latency_range.max = latency; + latency_range.min = latency; + } - // Check available size first.. - size = jack_ringbuffer_write_space(ringbuffer); - if (size < (sizeof(UInt16) + packet->length)) { - jack_error("ReadProc : ring buffer is full, skip events..."); - return; + // Physical outputs + for (int i = 0; i < num_physical_outputs; i++) { + port_obj = physical_output_ports[i]; + name = port_obj->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackCoreMidiDriver::Attach - cannot register physical " + "output port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; } - // Write length of each packet first - jack_ringbuffer_write(ringbuffer, (char*)&packet->length, sizeof(UInt16)); - // Write event actual data - jack_ringbuffer_write(ringbuffer, (char*)packet->data, packet->length); + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); + fPlaybackPortList[i] = index; + } - packet = MIDIPacketNext(packet); + // Virtual outputs + for (int i = 0; i < num_virtual_outputs; i++) { + port_obj = virtual_output_ports[i]; + name = port_obj->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackCoreMidiDriver::Attach - cannot register virtual " + "output port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); + fPlaybackPortList[num_physical_outputs + i] = index; } -} -void JackCoreMidiDriver::ReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) -{ - jack_ringbuffer_t* ringbuffer = (jack_ringbuffer_t*)connRefCon; - ReadProcAux(pktlist, ringbuffer); + return 0; } -void JackCoreMidiDriver::ReadVirtualProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) +int +JackCoreMidiDriver::Close() { - jack_ringbuffer_t* ringbuffer = (jack_ringbuffer_t*)refCon; - ReadProcAux(pktlist, ringbuffer); + int result = 0; + OSStatus status; + if (physical_input_ports) { + for (int i = 0; i < num_physical_inputs; i++) { + delete physical_input_ports[i]; + } + delete[] physical_input_ports; + num_physical_inputs = 0; + physical_input_ports = 0; + status = MIDIPortDispose(internal_input); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose", + status); + result = -1; + } + } + if (physical_output_ports) { + for (int i = 0; i < num_physical_outputs; i++) { + delete physical_output_ports[i]; + } + delete[] physical_output_ports; + num_physical_outputs = 0; + physical_output_ports = 0; + status = MIDIPortDispose(internal_output); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose", + status); + result = -1; + } + } + if (virtual_input_ports) { + for (int i = 0; i < num_virtual_inputs; i++) { + delete virtual_input_ports[i]; + } + delete[] virtual_input_ports; + num_virtual_inputs = 0; + virtual_input_ports = 0; + } + if (virtual_output_ports) { + for (int i = 0; i < num_virtual_outputs; i++) { + delete virtual_output_ports[i]; + } + delete[] virtual_output_ports; + num_virtual_outputs = 0; + virtual_output_ports = 0; + } + if (client) { + status = MIDIClientDispose(client); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientDispose", + status); + result = -1; + } + client = 0; + } + return result; } -void JackCoreMidiDriver::NotifyProc(const MIDINotification *message, void *refCon) +void +JackCoreMidiDriver::HandleNotification(const MIDINotification *message) { - jack_log("NotifyProc %d", message->messageID); + // Empty } -JackCoreMidiDriver::JackCoreMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) - : JackMidiDriver(name, alias, engine, table), fMidiClient(NULL), fInputPort(NULL), fOutputPort(NULL), fRealCaptureChannels(0), fRealPlaybackChannels(0) -{} - -JackCoreMidiDriver::~JackCoreMidiDriver() -{} - -int JackCoreMidiDriver::Open(bool capturing, - bool playing, - int inchannels, - int outchannels, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency) - { - OSStatus err; - CFStringRef coutputStr; - std::string str; - - // Get real input/output number - fRealCaptureChannels = MIDIGetNumberOfSources(); - fRealPlaybackChannels = MIDIGetNumberOfDestinations(); - - // Generic JackMidiDriver Open - if (JackMidiDriver::Open(capturing, playing, inchannels + fRealCaptureChannels, outchannels + fRealPlaybackChannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency) != 0) +int +JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, + int out_channels, bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + int pi_count = 0; + int po_count = 0; + int vi_count = 0; + int vo_count = 0; + CFStringRef name = CFStringCreateWithCString(0, "JackMidi", + CFStringGetSystemEncoding()); + if (! name) { + jack_error("JackCoreMidiDriver::Open - failed to allocate memory for " + "client name string"); return -1; + } + OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this, + &client); + CFRelease(name); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientCreate", + status); + return -1; + } + char *client_name = fClientControl.fName; + int port_rt_priority = fEngineControl->fServerPriority + 1; + + // Allocate and connect virtual inputs + if (in_channels) { + try { + virtual_input_ports = + new JackCoreMidiVirtualInputPort[in_channels]; + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "input port array: %s", e.what()); + goto destroy_client; + } + for (vi_count = 0; vi_count < in_channels; vi_count++) { + try { + virtual_input_ports[vi_count] = + new JackCoreMidiVirtualInputPort(fAliasName, client_name, + capture_driver_name, + vi_count, client, + time_ratio); + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "input port: %s", e.what()); + goto destroy_virtual_input_ports; + } + } + } - coutputStr = CFStringCreateWithCString(0, "JackMidi", CFStringGetSystemEncoding()); - err = MIDIClientCreate(coutputStr, NotifyProc, this, &fMidiClient); - CFRelease(coutputStr); - if (!fMidiClient) { - jack_error("Cannot create CoreMidi client"); - goto error; - } - - err = MIDIInputPortCreate(fMidiClient, CFSTR("Input port"), ReadProc, this, &fInputPort); - if (!fInputPort) { - jack_error("Cannot open CoreMidi in port\n"); - goto error; - } - - err = MIDIOutputPortCreate(fMidiClient, CFSTR("Output port"), &fOutputPort); - if (!fOutputPort) { - jack_error("Cannot open CoreMidi out port\n"); - goto error; - } - - fMidiDestination = new MIDIEndpointRef[inchannels + fRealCaptureChannels]; - assert(fMidiDestination); + // Allocate and connect virtual outputs + if (out_channels) { + try { + virtual_output_ports = + new JackCoreMidiVirtualOutputPort[out_channels]; + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "output port array: %s", e.what()); + goto destroy_virtual_input_ports; + } + for (vo_count = 0; vo_count < out_channels; vo_count++) { + try { + virtual_output_ports[vo_count] = + new JackCoreMidiVirtualOutputPort(fAliasName, client_name, + playback_driver_name, + vo_count, client, + time_ratio, + port_rt_priority); + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "output port: %s", e.what()); + goto destroy_virtual_output_ports; + } + } + } - // Virtual input - for (int i = 0; i < inchannels; i++) { - std::stringstream num; - num << i; - str = "JackMidi" + num.str(); - coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); - err = MIDIDestinationCreate(fMidiClient, coutputStr, ReadVirtualProc, fRingBuffer[i], &fMidiDestination[i]); - CFRelease(coutputStr); - if (!fMidiDestination[i]) { - jack_error("Cannot create CoreMidi destination"); - goto error; + // Allocate and connect physical inputs + ItemCount potential_pi_count = MIDIGetNumberOfSources(); + if (potential_pi_count) { + status = MIDIInputPortCreate(client, CFSTR("Physical Input Port"), + HandleInputEvent, this, &internal_input); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIInputPortCreate", + status); + goto destroy_virtual_output_ports; + } + try { + physical_input_ports = + new JackCoreMidiPhysicalInputPort[potential_pi_count]; + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating physical " + "input port array: %s", e.what()); + goto destroy_internal_input_port; + } + for (ItemCount i = 0; i < potential_pi_count; i++) { + try { + physical_input_ports[pi_count] = + new JackCoreMidiPhysicalInputPort(fAliasName, client_name, + capture_driver_name, i, + client, internal_input, + time_ratio); + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating " + "physical input port: %s", e.what()); + continue; + } + pi_count++; } } - // Real input - for (int i = 0; i < fRealCaptureChannels; i++) { - fMidiDestination[i + inchannels] = MIDIGetSource(i); - MIDIPortConnectSource(fInputPort, fMidiDestination[i + inchannels], fRingBuffer[i + inchannels]); + // Allocate and connect physical outputs + ItemCount potential_po_count = MIDIGetNumberOfDestinations(); + if (potential_po_count) { + status = MIDIOutputPortCreate(client, CFSTR("Physical Output Port"), + &internal_output); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIOutputPortCreate", + status); + goto destroy_physical_input_ports; + } + try { + physical_output_ports = + new JackCoreMidiPhysicalOutputPort[potential_po_count]; + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating physical " + "output port array: %s", e.what()); + goto destroy_internal_output_port; + } + for (ItemCount i = 0; i < potential_po_count; i++) { + try { + physical_output_ports[po_count] = + new JackCoreMidiPhysicalOutputPort(fAliasName, client_name, + playback_driver_name, i, + client, internal_output, + time_ratio, + port_rt_priority); + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating " + "physical output port: %s", e.what()); + continue; + } + po_count++; + } } - fMidiSource = new MIDIEndpointRef[outchannels + fRealPlaybackChannels]; - assert(fMidiSource); + if (! (pi_count || po_count || in_channels || out_channels)) { + jack_error("JackCoreMidiDriver::Open - no CoreMIDI inputs or outputs " + "found, and no virtual ports allocated."); + } else if (! JackMidiDriver::Open(capturing, playing, + in_channels + pi_count, + out_channels + po_count, monitor, + capture_driver_name, + playback_driver_name, capture_latency, + playback_latency)) { + num_physical_inputs = pi_count; + num_physical_outputs = po_count; + num_virtual_inputs = in_channels; + num_virtual_outputs = out_channels; + return 0; + } - // Virtual output - for (int i = 0; i < outchannels; i++) { - std::stringstream num; - num << i; - str = "JackMidi" + num.str(); - coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); - err = MIDISourceCreate(fMidiClient, coutputStr, &fMidiSource[i]); - CFRelease(coutputStr); - if (!fMidiSource[i]) { - jack_error("Cannot create CoreMidi source"); - goto error; + // Cleanup + if (physical_output_ports) { + for (int i = 0; i < po_count; i++) { + delete physical_output_ports[i]; } + delete[] physical_output_ports; + physical_output_ports = 0; } - - // Real output - for (int i = 0; i < fRealPlaybackChannels; i++) { - fMidiSource[i + outchannels] = MIDIGetDestination(i); + destroy_internal_output_port: + status = MIDIPortDispose(internal_output); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status); } + destroy_physical_input_ports: + if (physical_input_ports) { + for (int i = 0; i < pi_count; i++) { + delete physical_input_ports[i]; + } + delete[] physical_input_ports; + physical_input_ports = 0; + } + destroy_internal_input_port: + status = MIDIPortDispose(internal_input); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status); + } + destroy_virtual_output_ports: + if (virtual_output_ports) { + for (int i = 0; i < vo_count; i++) { + delete virtual_output_ports[i]; + } + delete[] virtual_output_ports; + virtual_output_ports = 0; + } + destroy_virtual_input_ports: + if (virtual_input_ports) { + for (int i = 0; i < vi_count; i++) { + delete virtual_input_ports[i]; + } + delete[] virtual_input_ports; + virtual_input_ports = 0; + } + destroy_client: + status = MIDIClientDispose(client); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientDispose", + status); + } + client = 0; + return -1; +} +int +JackCoreMidiDriver::Read() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < num_physical_inputs; i++) { + physical_input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); + } + for (int i = 0; i < num_virtual_inputs; i++) { + virtual_input_ports[i]-> + ProcessJack(GetInputBuffer(num_physical_inputs + i), buffer_size); + } return 0; - -error: - Close(); - return -1; } -int JackCoreMidiDriver::Close() +int +JackCoreMidiDriver::Start() { - // Generic midi driver close - int res = JackMidiDriver::Close(); + jack_info("JackCoreMidiDriver::Start - Starting driver."); - if (fInputPort) - MIDIPortDispose(fInputPort); + JackMidiDriver::Start(); - if (fOutputPort) - MIDIPortDispose(fOutputPort); + int pi_count = 0; + int po_count = 0; + int vi_count = 0; + int vo_count = 0; - // Only dispose "virtual" endpoints - for (int i = 0; i < fCaptureChannels - fRealCaptureChannels; i++) { - if (fMidiDestination) - MIDIEndpointDispose(fMidiDestination[i]); - } - delete[] fMidiDestination; + jack_info("JackCoreMidiDriver::Start - Enabling physical input ports."); - // Only dispose "virtual" endpoints - for (int i = 0; i < fPlaybackChannels - fRealPlaybackChannels; i++) { - if (fMidiSource[i]) - MIDIEndpointDispose(fMidiSource[i]); + for (; pi_count < num_physical_inputs; pi_count++) { + if (physical_input_ports[pi_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable physical " + "input port."); + goto stop_physical_input_ports; + } } - delete[] fMidiSource; - if (fMidiClient) - MIDIClientDispose(fMidiClient); + jack_info("JackCoreMidiDriver::Start - Enabling physical output ports."); - return res; -} - -int JackCoreMidiDriver::Attach() -{ - OSStatus err; - JackPort* port; - CFStringRef pname; - jack_port_id_t port_index; - char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char endpoint_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - int i; - - jack_log("JackCoreMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); - - for (i = 0; i < fCaptureChannels; i++) { - - err = MIDIObjectGetStringProperty(fMidiDestination[i], kMIDIPropertyName, &pname); - if (err == noErr) { - CFStringGetCString(pname, endpoint_name, sizeof(endpoint_name), 0); - CFRelease(pname); - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, endpoint_name, i + 1); - } else { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); + for (; po_count < num_physical_outputs; po_count++) { + if (physical_output_ports[po_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable physical " + "output port."); + goto stop_physical_output_ports; } - - snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { - jack_error("driver: cannot register port for %s", name); - return -1; - } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fCapturePortList[i] = port_index; - jack_log("JackCoreMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } - for (i = 0; i < fPlaybackChannels; i++) { + jack_info("JackCoreMidiDriver::Start - Enabling virtual input ports."); - err = MIDIObjectGetStringProperty(fMidiSource[i], kMIDIPropertyName, &pname); - if (err == noErr) { - CFStringGetCString(pname, endpoint_name, sizeof(endpoint_name), 0); - CFRelease(pname); - snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, endpoint_name, i + 1); - } else { - snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); + for (; vi_count < num_virtual_inputs; vi_count++) { + if (virtual_input_ports[vi_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable virtual " + "input port."); + goto stop_virtual_input_ports; } + } - snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { - jack_error("driver: cannot register port for %s", name); - return -1; + jack_info("JackCoreMidiDriver::Start - Enabling virtual output ports."); + + for (; vo_count < num_virtual_outputs; vo_count++) { + if (virtual_output_ports[vo_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable virtual " + "output port."); + goto stop_virtual_output_ports; } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fPlaybackPortList[i] = port_index; - jack_log("JackCoreMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } - return 0; -} -int JackCoreMidiDriver::Read() -{ - for (int chan = 0; chan < fCaptureChannels; chan++) { - - if (fGraphManager->GetConnectionsNum(fCapturePortList[chan]) > 0) { + jack_info("JackCoreMidiDriver::Start - Driver started."); - // Get JACK port - JackMidiBuffer* midi_buffer = GetInputBuffer(chan); + return 0; - if (jack_ringbuffer_read_space(fRingBuffer[chan]) == 0) { - // Reset buffer - midi_buffer->Reset(midi_buffer->nframes); - } else { + stop_virtual_output_ports: + for (int i = 0; i < vo_count; i++) { + if (virtual_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable virtual " + "output port."); + } + } + stop_virtual_input_ports: + for (int i = 0; i < vi_count; i++) { + if (virtual_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable virtual " + "input port."); + } + } + stop_physical_output_ports: + for (int i = 0; i < po_count; i++) { + if (physical_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable " + "physical output port."); + } + } + stop_physical_input_ports: + for (int i = 0; i < pi_count; i++) { + if (physical_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable " + "physical input port."); + } + } - while (jack_ringbuffer_read_space(fRingBuffer[chan]) > 0) { + return -1; +} - // Read event number - int ev_count = 0; - jack_ringbuffer_read(fRingBuffer[chan], (char*)&ev_count, sizeof(int)); +int +JackCoreMidiDriver::Stop() +{ + int result = 0; - for (int j = 0; j < ev_count; j++) { - // Read event length - UInt16 event_len; - jack_ringbuffer_read(fRingBuffer[chan], (char*)&event_len, sizeof(UInt16)); - // Read event actual data - jack_midi_data_t* dest = midi_buffer->ReserveEvent(0, event_len); - jack_ringbuffer_read(fRingBuffer[chan], (char*)dest, event_len); - } - } - } + jack_info("JackCoreMidiDriver::Stop - disabling physical input ports."); - } else { - // Consume ring buffer - jack_ringbuffer_read_advance(fRingBuffer[chan], jack_ringbuffer_read_space(fRingBuffer[chan])); + for (int i = 0; i < num_physical_inputs; i++) { + if (physical_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable physical " + "input port."); + result = -1; } } - return 0; -} - -int JackCoreMidiDriver::Write() -{ - MIDIPacketList* pktlist = (MIDIPacketList*)fMIDIBuffer; - for (int chan = 0; chan < fPlaybackChannels; chan++) { + jack_info("JackCoreMidiDriver::Stop - disabling physical output ports."); - if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chan]) > 0) { + for (int i = 0; i < num_physical_outputs; i++) { + if (physical_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable physical " + "output port."); + result = -1; + } + } - MIDIPacket* packet = MIDIPacketListInit(pktlist); - JackMidiBuffer* midi_buffer = GetOutputBuffer(chan); + jack_info("JackCoreMidiDriver::Stop - disabling virtual input ports."); - // TODO : use timestamp + for (int i = 0; i < num_virtual_inputs; i++) { + if (virtual_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual " + "input port."); + result = -1; + } + } - for (unsigned int j = 0; j < midi_buffer->event_count; j++) { - JackMidiEvent* ev = &midi_buffer->events[j]; - packet = MIDIPacketListAdd(pktlist, sizeof(fMIDIBuffer), packet, MIDIGetCurrentHostTime(), ev->size, ev->GetData(midi_buffer)); - } + jack_info("JackCoreMidiDriver::Stop - disabling virtual output ports."); - if (packet) { - if (chan < fPlaybackChannels - fRealPlaybackChannels) { - OSStatus err = MIDIReceived(fMidiSource[chan], pktlist); - if (err != noErr) - jack_error("MIDIReceived error"); - } else { - OSStatus err = MIDISend(fOutputPort, fMidiSource[chan], pktlist); - if (err != noErr) - jack_error("MIDISend error"); - } - } + for (int i = 0; i < num_virtual_outputs; i++) { + if (virtual_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual " + "output port."); + result = -1; } } - return 0; + return result; } -} // end of namespace +int +JackCoreMidiDriver::Write() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < num_physical_outputs; i++) { + physical_output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size); + } + for (int i = 0; i < num_virtual_outputs; i++) { + virtual_output_ports[i]-> + ProcessJack(GetOutputBuffer(num_physical_outputs + i), + buffer_size); + } + return 0; +} #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() @@ -423,4 +695,3 @@ extern "C" #ifdef __cplusplus } #endif - diff --git a/macosx/coremidi/JackCoreMidiDriver.h b/macosx/coremidi/JackCoreMidiDriver.h index a99b35a0..34056c33 100644 --- a/macosx/coremidi/JackCoreMidiDriver.h +++ b/macosx/coremidi/JackCoreMidiDriver.h @@ -1,5 +1,6 @@ /* Copyright (C) 2009 Grame +Copyright (C) 2011 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,61 +21,74 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackCoreMidiDriver__ #define __JackCoreMidiDriver__ -#include +#include "JackCoreMidiPhysicalInputPort.h" +#include "JackCoreMidiPhysicalOutputPort.h" +#include "JackCoreMidiVirtualInputPort.h" +#include "JackCoreMidiVirtualOutputPort.h" #include "JackMidiDriver.h" -#include "JackTime.h" -namespace Jack -{ +namespace Jack { -/*! -\brief The CoreMidi driver. -*/ - -class JackCoreMidiDriver : public JackMidiDriver -{ + class JackCoreMidiDriver: public JackMidiDriver { private: - MIDIClientRef fMidiClient; - MIDIPortRef fInputPort; - MIDIPortRef fOutputPort; - MIDIEndpointRef* fMidiDestination; - MIDIEndpointRef* fMidiSource; + static void + HandleInputEvent(const MIDIPacketList *packet_list, void *driver, + void *port); + + static void + HandleNotificationEvent(const MIDINotification *message, void *driver); + + void + HandleNotification(const MIDINotification *message); + + MIDIClientRef client; + MIDIPortRef internal_input; + MIDIPortRef internal_output; + int num_physical_inputs; + int num_physical_outputs; + int num_virtual_inputs; + int num_virtual_outputs; + JackCoreMidiPhysicalInputPort *physical_input_ports; + JackCoreMidiPhysicalOutputPort *physical_output_ports; + double time_ratio; + JackCoreMidiVirtualInputPort *virtual_input_ports; + JackCoreMidiVirtualOutputPort *virtual_output_ports; + + public: - char fMIDIBuffer[BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t)]; + JackCoreMidiDriver(const char* name, const char* alias, + JackLockedEngine* engine, JackSynchro* table); - int fRealCaptureChannels; - int fRealPlaybackChannels; + ~JackCoreMidiDriver(); - static void ReadProcAux(const MIDIPacketList *pktlist, jack_ringbuffer_t* ringbuffer); - static void ReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon); - static void ReadVirtualProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon); - static void NotifyProc(const MIDINotification *message, void *refCon); + int + Attach(); - public: + int + Close(); + + int + Open(bool capturing, bool playing, int num_inputs, int num_outputs, + bool monitor, const char* capture_driver_name, + const char* playback_driver_name, jack_nframes_t capture_latency, + jack_nframes_t playback_latency); - JackCoreMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); - virtual ~JackCoreMidiDriver(); + int + Read(); - int Open( bool capturing, - bool playing, - int chan_in, - int chan_out, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency); - int Close(); + int + Start(); - int Attach(); + int + Stop(); - int Read(); - int Write(); + int + Write(); -}; + }; -} // end of namespace +} #endif diff --git a/macosx/coremidi/JackCoreMidiInputPort.cpp b/macosx/coremidi/JackCoreMidiInputPort.cpp new file mode 100644 index 00000000..20d2af0c --- /dev/null +++ b/macosx/coremidi/JackCoreMidiInputPort.cpp @@ -0,0 +1,181 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiInputPort.h" +#include "JackMidiUtil.h" + +using Jack::JackCoreMidiInputPort; + +JackCoreMidiInputPort::JackCoreMidiInputPort(double time_ratio, + size_t max_bytes, + size_t max_messages): + JackCoreMidiPort(time_ratio) +{ + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); + write_queue = new JackMidiBufferWriteQueue(); + std::auto_ptr write_queue_ptr(write_queue); + sysex_buffer = new jack_midi_data_t[max_bytes]; + write_queue_ptr.release(); + thread_queue_ptr.release(); + jack_event = 0; +} + +JackCoreMidiInputPort::~JackCoreMidiInputPort() +{ + delete thread_queue; + delete write_queue; + delete[] sysex_buffer; +} + +jack_frames_t +JackCoreMidiPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp) +{ + return GetFramesFromTime((jack_time_t) (timestamp * time_ratio)); +} + +void +JackCoreMidiInputPort::Initialize(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint) +{ + Initialize(alias_name, client_name, driver_name, index, endpoint, false); +} + +void +JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list) +{ + unsigned int packet_count = packet_list->numPackets; + assert(packet_count); + MIDIPacket *packet = (MIDIPacket *) packet_list->packet; + for (unsigned int i = 0; i < packet_count; i++) { + jack_midi_data_t *data = packet->data; + size_t size = packet->length; + assert(size); + jack_midi_event_t event; + + // XX: There might be dragons in my spaghetti. This code is begging + // for a rewrite. + + if (sysex_bytes_sent) { + if (data[0] & 0x80) { + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - System " + "exclusive message aborted."); + sysex_bytes_sent = 0; + goto parse_event; + } + buffer_sysex_bytes: + if ((sysex_bytes_sent + size) <= sizeof(sysex_buffer)) { + memcpy(sysex_buffer + sysex_bytes_sent, packet, + size * sizeof(jack_midi_data_t)); + } + sysex_bytes_sent += size; + if (data[size - 1] == 0xf7) { + if (sysex_bytes_sent > sizeof(sysex_buffer)) { + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - " + "Could not buffer a %d-byte system exclusive " + "message. Discarding message.", + sysex_bytes_sent); + sysex_bytes_sent = 0; + goto get_next_packet; + } + event.data = sysex_buffer; + event.size = sysex_bytes_sent; + event.time = ; + sysex_bytes_sent = 0; + goto send_event; + } + goto get_next_packet; + } + + parse_event: + if (data[0] == 0xf0) { + if (data[size - 1] != 0xf7) { + goto buffer_sysex_bytes; + } + } + event.data = data; + event.size = size; + + send_event: + event.time = GetFramesFromTimeStamp(packet->timeStamp); + switch (thread_queue->EnqueueEvent(&event)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread " + "queue buffer is full. Dropping event."); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread " + "queue couldn't enqueue a %d-byte packet. Dropping " + "event.", event->size); + break; + default: + ; + } + + get_next_packet: + packet = MIDIPacketNext(packet); + assert(packet); + } +} + +void +JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) +{ + write_queue->ResetMidiBuffer(port_buffer); + if (! jack_event) { + jack_event = thread_queue->DequeueEvent(); + } + for (; jack_event; jack_event = thread_queue->DequeueEvent()) { + // Add 'frames' to MIDI events to align with audio. + switch (write_queue->EnqueueEvent(jack_event, frames)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackCoreMidiInputPort::ProcessJack - The write queue " + "couldn't enqueue a %d-byte event. Dropping event.", + jack_event->size); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + ; + } + break; + } +} + +bool +JackCoreMidiInputPort::Start() +{ + // Hack: Get rid of any messages that might have come in before starting + // the engine. + while (thread_queue->DequeueEvent()); + sysex_bytes_sent = 0; + return true; +} + +bool +JackCoreMidiInputPort::Stop() +{ + return true; +} diff --git a/macosx/coremidi/JackCoreMidiInputPort.h b/macosx/coremidi/JackCoreMidiInputPort.h new file mode 100644 index 00000000..3950c70c --- /dev/null +++ b/macosx/coremidi/JackCoreMidiInputPort.h @@ -0,0 +1,73 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiInputPort__ +#define __JackCoreMidiInputPort__ + +#include "JackCoreMidiPort.h" +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferWriteQueue.h" + +namespace Jack { + + class JackCoreMidiInputPort: public JackCoreMidiPort { + + private: + + jack_nframes_t + GetFramesFromTimeStamp(MIDITimeStamp timestamp); + + jack_midi_event_t *jack_event; + jack_midi_data_t *sysex_buffer; + size_t sysex_bytes_sent; + JackMidiAsyncQueue *thread_queue; + JackMidiBufferWriteQueue *write_queue; + + protected: + + void + Initialize(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint); + + public: + + JackCoreMidiInputPort(double time_ratio, size_t max_bytes=4096, + size_t max_messages=1024); + + virtual + ~JackCoreMidiInputPort(); + + void + ProcessCoreMidi(const MIDIPacketList *packet_list); + + void + ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); + + bool + Start(); + + bool + Stop(); + + }; + +} + +#endif diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp new file mode 100644 index 00000000..dced4fab --- /dev/null +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -0,0 +1,203 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiOutputPort.h" + +using Jack::JackCoreMidiOutputPort; + +JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, + int realtime_priority, + size_t max_bytes, + size_t max_messages): + JackCoreMidiPort(time_ratio) +{ + read_queue = new JackMidiBufferReadQueue(); + std::auto_ptr read_ptr(read_queue); + thread_queue = new JackMidiAsyncWaitQueue(max_bytes, max_messages); + std::auto_ptr thread_ptr(thread_queue); + thread = new JackThread(this); + this->realtime_priority = realtime_priority; + thread_ptr.release(); + read_ptr.release(); +} + +JackCoreMidiOutputPort::~JackCoreMidiOutputPort() +{ + Stop(); + delete thread; + delete read_queue; + delete thread_queue; +} + +bool +JackCoreMidiOutputPort::Execute() +{ + jack_midi_event_t *event = 0; + MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer; + for (;;) { + MIDIPacket *packet = MIDIPacketListInit(packet_list); + assert(packet); + if (! event) { + event = thread_queue->DequeueEvent((long) 0); + } + jack_midi_data_t *data = event->data; + + // This is the latest time that the packet list can be sent out. We + // may want to consider subtracting some frames to leave room for the + // CoreMIDI driver/client to handle all of the events. There's a + // property called 'kMIDIPropertyAdvanceScheduleTimeMuSec' that might + // be useful in this case. + jack_nframes_t send_time = event->time; + + size_t size = event->size; + MIDITimeStamp timestamp = GetTimeStampFromFrames(send_time); + packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet, + timestamp, size, data); + if (packet) { + while (GetCurrentFrame() < send_time) { + event = thread_queue->DequeueEvent(); + if (! event) { + break; + } + packet = MIDIPacketListAdd(packet_list, midi_buffer_size, + packet, + GetTimeStampFromFrames(event->time), + event->size, event->data); + if (! packet) { + break; + } + } + SendPacketList(packet_list); + } else { + + // We have a large system exclusive event. We'll have to send it + // out in multiple packets. + size_t bytes_sent = 0; + do { + packet = MIDIPacketListInit(packet_list); + assert(packet); + size_t num_bytes = 0; + for (; bytes_sent < size; bytes_sent += num_bytes) { + size_t num_bytes = size - bytes_sent; + + // We use 256 because the MIDIPacket struct defines the + // size of the 'data' member to be 256 bytes. I believe + // this prevents packets from being dynamically allocated + // by 'MIDIPacketListAdd', but I might be wrong. + if (num_bytes > 256) { + num_bytes = 256; + } + packet = MIDIPacketListAdd(packet_list, midi_buffer_size, + packet, timestamp, num_bytes, + data + bytes_sent); + if (! packet) { + break; + } + } + if (! SendPacketList(packet_list)) { + // An error occurred. The error message has already been + // output. We lick our wounds and move along. + break; + } + } while (bytes_sent < size); + event = 0; + } + } + return false; +} + +MIDITimeStamp +JackCoreMidiPort::GetTimeStampFromFrames(jack_nframes_t frames) +{ + return GetTimeFromFrames(frames) / time_ratio; +} + +bool +JackCoreMidiOutputPort::Init() +{ + set_threaded_log_function(); + if (thread->AcquireSelfRealTime(realtime_priority)) { + jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime " + "scheduling. Continuing anyway."); + } + return true; +} + +void +JackCoreMidiOutputPort::Initialize(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint) +{ + Initialize(alias_name, client_name, driver_name, index, endpoint, true); +} + +void +JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) +{ + read_queue->ResetMidiBuffer(port_buffer); + for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; + event = read_queue->DequeueEvent()) { + switch (thread_queue->EnqueueEvent(event, frames)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " + "queue buffer is full. Dropping event."); + continue; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " + "queue couldn't enqueue a %d-byte event. Dropping " + "event.", event->size); + // Fallthrough on purpose + default: + ; + } + } +} + +bool +JackCoreMidiOutputPort::Start() +{ + bool result = thread->GetStatus() != JackThread::kIdle; + if (! result) { + result = ! thread->StartSync(); + if (! result) { + jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI " + "processing thread."); + } + } + return result; +} + +bool +JackCoreMidiOutputPort::Stop() +{ + bool result = thread->GetStatus() == JackThread::kIdle; + if (! result) { + result = ! thread->Kill(); + if (! result) { + jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI " + "processing thread."); + } + } + return result; +} diff --git a/macosx/coremidi/JackCoreMidiOutputPort.h b/macosx/coremidi/JackCoreMidiOutputPort.h new file mode 100644 index 00000000..0108770e --- /dev/null +++ b/macosx/coremidi/JackCoreMidiOutputPort.h @@ -0,0 +1,83 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiOutputPort__ +#define __JackCoreMidiOutputPort__ + +#include "JackCoreMidiPort.h" +#include "JackMidiAsyncWaitQueue.h" +#include "JackMidiBufferReadQueue.h" +#include "JackThread.h" + +namespace Jack { + + class JackCoreMidiOutputPort: + public JackCoreMidiPort, public JackRunnableInterface { + + private: + + MIDITimeStamp + GetTimeStampFromFrames(jack_nframes_t frames); + + void + Initialize(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint); + + static const size_t PACKET_BUFFER_SIZE = 65536; + + char packet_buffer[PACKET_BUFFER_SIZE]; + JackMidiBufferReadQueue *read_queue; + int realtime_priority; + JackThread *thread; + JackMidiAsyncWaitQueue *thread_queue; + + protected: + + virtual void + SendPacketList(MIDIPacketList *packet_list) = 0; + + public: + + JackCoreMidiOutputPort(double time_ratio, size_t max_bytes=4096, + size_t max_messages=1024); + + virtual + ~JackCoreMidiOutputPort(); + + bool + Execute(); + + bool + Init(); + + void + ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); + + bool + Start(); + + bool + Stop(); + + }; + +} + +#endif diff --git a/macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp b/macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp new file mode 100644 index 00000000..748dbc17 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiPhysicalInputPort.h" +#include "JackCoreMidiUtil.h" + +using Jack::JackCoreMidiPhysicalInputPort; + +JackCoreMidiPhysicalInputPort:: +JackCoreMidiPhysicalInputPort(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, MIDIPortRef internal_input, + double time_ratio, size_t max_bytes, + size_t max_messages): + JackCoreMidiInputPort(time_ratio, max_bytes, max_messages) +{ + MIDIEndpointRef source = MIDIGetSource(index); + if (! source) { + // X: Is there a way to get a better error message? + std::stringstream stream; + stream << "The source at index '" << index << "' is not available"; + throw std::runtime_error(stream.str().c_str()); + } + OSStatus status = MIDIPortConnectSource(internal_input, source, this); + if (status != noErr) { + throw std::runtime_error(GetMacOSErrorString(status)); + } + Initialize(alias_name, client_name, driver_name, index, source); +} + +JackCoreMidiPhysicalInputPort::~JackCoreMidiPhysicalInputPort() +{ + // Empty +} diff --git a/macosx/coremidi/JackCoreMidiPhysicalInputPort.h b/macosx/coremidi/JackCoreMidiPhysicalInputPort.h new file mode 100644 index 00000000..83f0f2dd --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPhysicalInputPort.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiPhysicalInputPort__ +#define __JackCoreMidiPhysicalInputPort__ + +#include "JackCoreMidiInputPort.h" + +namespace Jack { + + class JackCoreMidiPhysicalInputPort: public JackCoreMidiInputPort { + + public: + + JackCoreMidiPhysicalInputPort(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, + MIDIPortRef internal_input, + double time_ratio, size_t max_bytes=4096, + size_t max_messages=1024); + + ~JackCoreMidiPhysicalInputPort(); + + }; + +} + +#endif diff --git a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp new file mode 100644 index 00000000..1af6b2c6 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp @@ -0,0 +1,65 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiPhysicalOutputPort.h" +#include "JackCoreMidiUtil.h" + +using Jack::JackCoreMidiPhysicalOutputPort; + +JackCoreMidiPhysicalOutputPort:: +JackCoreMidiPhysicalOutputPort(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, + MIDIPortRef internal_output, double time_ratio, + int realtime_priority, size_t max_bytes, + size_t max_messages): + JackCoreMidiOutputPort(time_ratio, realtime_priority, max_bytes, + max_messages) +{ + MIDIEndpointRef destination = MIDIGetDestination(index); + if (! destination) { + // X: Can we get a better error message? + std::stringstream stream; + stream << "The destination at index '" << index + << "' is not available"; + throw std::runtime_error(stream.str().c_str()); + } + Initialize(alias_name, client_name, driver_name, index, destination); + this->internal_output = internal_output; +} + +JackCoreMidiPhysicalOutputPort::~JackCoreMidiPhysicalOutputPort() +{ + // Empty +} + +bool +JackCoreMidiPhysicalOutputPort::SendPacketList(MIDIPacketList *packet_list) +{ + OSStatus status = MIDISend(internal_output, source, packet_list); + bool result = status == noErr; + if (! result) { + WriteMacOSError("JackCoreMidiPhysicalOutputPort::SendPacketList", + "MIDISend", status); + } + return result; +} diff --git a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h new file mode 100644 index 00000000..fb922f84 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h @@ -0,0 +1,56 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiPhysicalOutputPort__ +#define __JackCoreMidiPhysicalOutputPort__ + +#include "JackCoreMidiOutputPort.h" + +namespace Jack { + + class JackCoreMidiPhysicalOutputPort: public JackCoreMidiOutputPort { + + private: + + MIDIPortRef internal_output; + + protected: + + bool + SendPacketList(MIDIPacketList *packet_list); + + public: + + JackCoreMidiPhysicalOutputPort(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, + MIDIPortRef internal_output, + double time_ratio, + int realtime_priority, + size_t max_bytes=4096, + size_t max_messages=1024); + + ~JackCoreMidiPhysicalOutputPort(); + + }; + +} + +#endif diff --git a/macosx/coremidi/JackCoreMidiPort.cpp b/macosx/coremidi/JackCoreMidiPort.cpp new file mode 100644 index 00000000..1783f34e --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPort.cpp @@ -0,0 +1,90 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include "JackCoreMidiPort.h" + +using Jack::JackCoreMidiPort; + +JackCoreMidiPort::JackCoreMidiPort(double time_ratio) +{ + initialized = false; + this->time_ratio = time_ratio; +} + +JackCoreMidiPort::~JackCoreMidiPort() +{ + // Empty +} + +const char * +JackCoreMidiPort::GetAlias() +{ + assert(initialized); + return alias; +} + +MIDIEndpointRef +JackCoreMidiPort::GetEndpoint() +{ + assert(initialized); + return endpoint; +} + +const char * +JackCoreMidiPort::GetName() +{ + assert(initialized); + return name; +} + +void +JackCoreMidiPort::Initialize(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint, bool is_output) +{ + char endpoint_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + CFStringRef endpoint_name_ref; + int num = index + 1; + OSStatus result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, + &endpoint_name_ref); + if (result != noErr) { + WriteMacOSError("JackCoreMidiPort::Initialize", + "MIDIObjectGetStringProperty", result); + goto get_basic_alias; + } + result = CFStringGetCString(endpoint_name_ref, endpoint_name, + sizeof(endpoint_name), 0); + CFRelease(endpoint_name_ref); + if (result != noErr) { + jack_error("JackCoreMidiPort::Initialize - failed to allocate memory " + "for endpoint name."); + get_basic_alias: + snprintf(alias, sizeof(alias) - 1, "%s:%s:%s%d", alias_name, + driver_name, is_output ? "out" : "in", num); + } else { + snprintf(alias, sizeof(alias) - 1, "%s:%s:%s%d", alias_name, + endpoint_name, is_output ? "out" : "in", num); + } + snprintf(name, sizeof(name) - 1, "%s:%s_%d", client_name, + is_output ? "playback" : "capture", num); + this->endpoint = endpoint; + initialized = true; +} diff --git a/macosx/coremidi/JackCoreMidiPort.h b/macosx/coremidi/JackCoreMidiPort.h new file mode 100644 index 00000000..e8dd64e4 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPort.h @@ -0,0 +1,67 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiPort__ +#define __JackCoreMidiPort__ + +#include + +#include "JackConstants.h" + +namespace Jack { + + class JackCoreMidiPort { + + private: + + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + MIDIEndpointRef endpoint; + bool initialized; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + + protected: + + MIDIEndpointRef + GetEndpoint(); + + void + Initialize(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint, bool is_output); + + double time_ratio; + + public: + + JackCoreMidiPort(double time_ratio); + + virtual + ~JackCoreMidiPort(); + + const char * + GetAlias(); + + const char * + GetName(); + + }; + +} + +#endif diff --git a/macosx/coremidi/JackCoreMidiUtil.cpp b/macosx/coremidi/JackCoreMidiUtil.cpp new file mode 100644 index 00000000..80b8746e --- /dev/null +++ b/macosx/coremidi/JackCoreMidiUtil.cpp @@ -0,0 +1,44 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include "JackError.h" + +std::string +Jack::GetMacOSErrorString(OSStatus status) +{ + char message[kMaxErrorLength + 1]; + OSStatus result = GetErrorLongString(error, message, + (long) sizeof(message)); + if (result != noErr) { + std::stringstream stream; + stream << "error (code: '" << status << "')"; + return stream.str(); + } + return std::string(message); +} + +void +Jack::WriteMacOSError(const char *jack_function, const char *mac_function, + OSStatus status) +{ + jack_error("%s - %s: %s", jack_function, mac_function, + GetMacOSStatusString(status).c_str()); +} diff --git a/macosx/coremidi/JackCoreMidiUtil.h b/macosx/coremidi/JackCoreMidiUtil.h new file mode 100644 index 00000000..5c9aac00 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiUtil.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiUtil__ +#define __JackCoreMidiUtil__ + +#include + +#include + +namespace Jack { + + std::string + GetMacOSErrorString(OSStatus status); + + void + WriteMacOSError(const char *jack_function, const char *mac_function, + OSStatus status); + +} + +#endif diff --git a/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp new file mode 100644 index 00000000..6c6ec015 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp @@ -0,0 +1,75 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiUtil.h" +#include "JackCoreMidiVirtualInputPort.h" + +using Jack::JackCoreMidiVirtualInputPort; + +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +void +JackCoreMidiVirtualInputPort:: +HandleInputEvent(const MIDIPacketList *packet_list, void *port, + void */*src_ref*/) +{ + ((JackCoreMidiVirtualInputPort *) port)->ProcessCoreMidi(packet_list); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackCoreMidiVirtualInputPort:: +JackCoreMidiVirtualInputPort(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, double time_ratio, + size_t max_bytes, size_t max_messages): + JackCoreMidiInputPort(time_ratio, max_bytes, max_messages) +{ + std::stringstream stream; + stream << "JackMidi" << (index + 1); + CFStringRef name = CFStringCreateWithString(0, stream.str().c_str(), + CFStringGetSystemEncoding()); + if (! name) { + throw std::bad_alloc(); + } + MIDIEndpointRef destination; + OSStatus status = MIDIDestinationCreate(client, name, HandleInputEvent, + this, &destination); + CFRelease(name); + if (status != noErr) { + throw std::runtime_error(GetMacOSErrorString(status)); + } + Initialize(alias_name, client_name, driver_name, index, destination); +} + +JackCoreMidiVirtualInputPort::~JackCoreMidiVirtualInputPort() +{ + OSStatus status = MIDIEndpointDispose(GetEndpoint()); + if (status != noErr) { + WriteMacOSError("JackCoreMidiVirtualInputPort [destructor]", + "MIDIEndpointDispose", status); + } +} diff --git a/macosx/coremidi/JackCoreMidiVirtualInputPort.h b/macosx/coremidi/JackCoreMidiVirtualInputPort.h new file mode 100644 index 00000000..62c970da --- /dev/null +++ b/macosx/coremidi/JackCoreMidiVirtualInputPort.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiVirtualInputPort__ +#define __JackCoreMidiVirtualInputPort__ + +#include "JackCoreMidiInputPort.h" + +namespace Jack { + + class JackCoreMidiVirtualInputPort: public JackCoreMidiInputPort { + + private: + + static void + HandleInputEvent(const MIDIPacketList *packet_list, void *port, + void *src_ref) + + public: + + JackCoreMidiVirtualInputPort(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, double time_ratio, + size_t max_bytes=4096, + size_t max_messages=1024); + + ~JackCoreMidiVirtualInputPort(); + + }; + +} + +#endif diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp new file mode 100644 index 00000000..2fa3f2cf --- /dev/null +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp @@ -0,0 +1,72 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiUtil.h" +#include "JackCoreMidiVirtualOutputPort.h" + +using Jack::JackCoreMidiVirtualOutputPort; + +JackCoreMidiVirtualOutputPort:: +JackCoreMidiVirtualOutputPort(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, double time_ratio, + int realtime_priority, size_t max_bytes, + size_t max_messages): + JackCoreMidiOutputPort(time_ratio, realtime_priority, max_bytes, + max_messages) +{ + std::stringstream stream; + stream << "JackMidi" << (index + 1); + CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), + CFStringGetSystemEncoding()); + if (! name) { + throw std::bad_alloc(); + } + MIDIEndpointRef source; + OSStatus status = MIDISourceCreate(client, name, &source); + CFRelease(name); + if (status != noErr) { + throw std::runtime_error(GetMacOSErrorString(status)); + } + Initialize(alias_name, client_name, driver_name, index, source); +} + +JackCoreMidiVirtualOutputPort::~JackCoreMidiVirtualOutputPort() +{ + OSStatus status = MIDIEndpointDispose(GetEndpoint()); + if (status != noErr) { + WriteMacOSError("JackCoreMidiVirtualOutputPort [destructor]", + "MIDIEndpointDispose", status); + } +} + +bool +JackCoreMidiVirtualOutputPort::SendPacketList(MIDIPacketList *packet_list) +{ + OSStatus status = MIDIReceived(source, packet_list); + bool result = status == noErr; + if (! result) { + WriteMacOSError("JackCoreMidiVirtualOutputPort::SendPacketList", + "MIDIReceived" status); + } + return result; +} diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.h b/macosx/coremidi/JackCoreMidiVirtualOutputPort.h new file mode 100644 index 00000000..2b828553 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackCoreMidiVirtualOutputPort__ +#define __JackCoreMidiVirtualOutputPort__ + +#include "JackCoreMidiOutputPort.h" + +namespace Jack { + + class JackCoreMidiVirtualOutputPort: public JackCoreMidiOutputPort { + + protected: + + bool + SendPacketList(MIDIPacketList *packet_list); + + public: + + JackCoreMidiVirtualOutputPort(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, double time_ratio, + int realtime_priority, + size_t max_bytes=4096, + size_t max_messages=1024); + + ~JackCoreMidiVirtualOutputPort(); + + }; + +} + +#endif From 955c051f72741e727e05bd25a7438bbc1a2ed5f3 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Sat, 26 Mar 2011 11:18:22 +0100 Subject: [PATCH 21/58] Compilation on OSX in progress. --- macosx/Jackdmp.xcodeproj/project.pbxproj | 96 +++++++++++++++++++ macosx/coremidi/JackCoreMidiDriver.cpp | 20 ++-- macosx/coremidi/JackCoreMidiDriver.h | 8 +- macosx/coremidi/JackCoreMidiInputPort.cpp | 11 ++- macosx/coremidi/JackCoreMidiOutputPort.cpp | 5 +- macosx/coremidi/JackCoreMidiOutputPort.h | 14 +-- .../JackCoreMidiPhysicalOutputPort.cpp | 2 +- macosx/coremidi/JackCoreMidiPort.cpp | 2 + macosx/coremidi/JackCoreMidiPort.h | 2 +- macosx/coremidi/JackCoreMidiUtil.cpp | 3 + macosx/coremidi/JackCoreMidiUtil.h | 3 +- .../coremidi/JackCoreMidiVirtualInputPort.cpp | 2 +- .../coremidi/JackCoreMidiVirtualInputPort.h | 2 +- .../JackCoreMidiVirtualOutputPort.cpp | 5 +- 14 files changed, 143 insertions(+), 32 deletions(-) diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index edfbae28..3ba3fc40 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -319,6 +319,38 @@ 4B363F230DEB0AB0001F72D9 /* monitor_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B363F220DEB0AB0001F72D9 /* monitor_client.c */; }; 4B363F3E0DEB0C31001F72D9 /* showtime.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B363F3D0DEB0C31001F72D9 /* showtime.c */; }; 4B363F760DEB0D7D001F72D9 /* impulse_grabber.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B363F750DEB0D7D001F72D9 /* impulse_grabber.c */; }; + 4B370A24133DD7E300237B68 /* JackCoreMidiInputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A14133DD7E200237B68 /* JackCoreMidiInputPort.cpp */; }; + 4B370A25133DD7E300237B68 /* JackCoreMidiInputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A15133DD7E200237B68 /* JackCoreMidiInputPort.h */; }; + 4B370A26133DD7E300237B68 /* JackCoreMidiOutputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A16133DD7E200237B68 /* JackCoreMidiOutputPort.cpp */; }; + 4B370A27133DD7E300237B68 /* JackCoreMidiOutputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A17133DD7E200237B68 /* JackCoreMidiOutputPort.h */; }; + 4B370A28133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A18133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.cpp */; }; + 4B370A29133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A19133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.h */; }; + 4B370A2A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A1A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp */; }; + 4B370A2B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A1B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h */; }; + 4B370A2C133DD7E300237B68 /* JackCoreMidiPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A1C133DD7E300237B68 /* JackCoreMidiPort.cpp */; }; + 4B370A2D133DD7E300237B68 /* JackCoreMidiPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A1D133DD7E300237B68 /* JackCoreMidiPort.h */; }; + 4B370A2E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A1E133DD7E300237B68 /* JackCoreMidiUtil.cpp */; }; + 4B370A2F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A1F133DD7E300237B68 /* JackCoreMidiUtil.h */; }; + 4B370A30133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A20133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp */; }; + 4B370A31133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A21133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h */; }; + 4B370A32133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A22133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp */; }; + 4B370A33133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A23133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h */; }; + 4B370A34133DD7E300237B68 /* JackCoreMidiInputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A14133DD7E200237B68 /* JackCoreMidiInputPort.cpp */; }; + 4B370A35133DD7E300237B68 /* JackCoreMidiInputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A15133DD7E200237B68 /* JackCoreMidiInputPort.h */; }; + 4B370A36133DD7E300237B68 /* JackCoreMidiOutputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A16133DD7E200237B68 /* JackCoreMidiOutputPort.cpp */; }; + 4B370A37133DD7E300237B68 /* JackCoreMidiOutputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A17133DD7E200237B68 /* JackCoreMidiOutputPort.h */; }; + 4B370A38133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A18133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.cpp */; }; + 4B370A39133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A19133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.h */; }; + 4B370A3A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A1A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp */; }; + 4B370A3B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A1B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h */; }; + 4B370A3C133DD7E300237B68 /* JackCoreMidiPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A1C133DD7E300237B68 /* JackCoreMidiPort.cpp */; }; + 4B370A3D133DD7E300237B68 /* JackCoreMidiPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A1D133DD7E300237B68 /* JackCoreMidiPort.h */; }; + 4B370A3E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A1E133DD7E300237B68 /* JackCoreMidiUtil.cpp */; }; + 4B370A3F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A1F133DD7E300237B68 /* JackCoreMidiUtil.h */; }; + 4B370A40133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A20133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp */; }; + 4B370A41133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A21133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h */; }; + 4B370A42133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B370A22133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp */; }; + 4B370A43133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B370A23133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h */; }; 4B3811FB13269C8300C61B14 /* latent_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B3811FA13269C8300C61B14 /* latent_client.c */; }; 4B3811FC13269C8300C61B14 /* latent_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B3811FA13269C8300C61B14 /* latent_client.c */; }; 4B3814201327AA6800C61B14 /* iodelay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B38141F1327AA6800C61B14 /* iodelay.cpp */; }; @@ -1537,6 +1569,22 @@ 4B363F3D0DEB0C31001F72D9 /* showtime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = showtime.c; path = "../example-clients/showtime.c"; sourceTree = SOURCE_ROOT; }; 4B363F720DEB0D4E001F72D9 /* jack_impulse_grabber */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_impulse_grabber; sourceTree = BUILT_PRODUCTS_DIR; }; 4B363F750DEB0D7D001F72D9 /* impulse_grabber.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = impulse_grabber.c; path = "../example-clients/impulse_grabber.c"; sourceTree = SOURCE_ROOT; }; + 4B370A14133DD7E200237B68 /* JackCoreMidiInputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiInputPort.cpp; path = ../../coremidi/JackCoreMidiInputPort.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A15133DD7E200237B68 /* JackCoreMidiInputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiInputPort.h; path = ../../coremidi/JackCoreMidiInputPort.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A16133DD7E200237B68 /* JackCoreMidiOutputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiOutputPort.cpp; path = ../../coremidi/JackCoreMidiOutputPort.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A17133DD7E200237B68 /* JackCoreMidiOutputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiOutputPort.h; path = ../../coremidi/JackCoreMidiOutputPort.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A18133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiPhysicalInputPort.cpp; path = ../../coremidi/JackCoreMidiPhysicalInputPort.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A19133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiPhysicalInputPort.h; path = ../../coremidi/JackCoreMidiPhysicalInputPort.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A1A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiPhysicalOutputPort.cpp; path = ../../coremidi/JackCoreMidiPhysicalOutputPort.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A1B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiPhysicalOutputPort.h; path = ../../coremidi/JackCoreMidiPhysicalOutputPort.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A1C133DD7E300237B68 /* JackCoreMidiPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiPort.cpp; path = ../../coremidi/JackCoreMidiPort.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A1D133DD7E300237B68 /* JackCoreMidiPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiPort.h; path = ../../coremidi/JackCoreMidiPort.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A1E133DD7E300237B68 /* JackCoreMidiUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiUtil.cpp; path = ../../coremidi/JackCoreMidiUtil.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A1F133DD7E300237B68 /* JackCoreMidiUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiUtil.h; path = ../../coremidi/JackCoreMidiUtil.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A20133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiVirtualInputPort.cpp; path = ../../coremidi/JackCoreMidiVirtualInputPort.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A21133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiVirtualInputPort.h; path = ../../coremidi/JackCoreMidiVirtualInputPort.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A22133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackCoreMidiVirtualOutputPort.cpp; path = ../../coremidi/JackCoreMidiVirtualOutputPort.cpp; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B370A23133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackCoreMidiVirtualOutputPort.h; path = ../../coremidi/JackCoreMidiVirtualOutputPort.h; sourceTree = BUILT_PRODUCTS_DIR; }; 4B37C20306DF1FBE0016E567 /* CALatencyLog.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CALatencyLog.cpp; path = /Developer/Examples/CoreAudio/PublicUtility/CALatencyLog.cpp; sourceTree = ""; }; 4B37C20406DF1FBE0016E567 /* CALatencyLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CALatencyLog.h; path = /Developer/Examples/CoreAudio/PublicUtility/CALatencyLog.h; sourceTree = ""; }; 4B37C20906DF1FE20016E567 /* latency.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = latency.c; path = /Developer/Examples/CoreAudio/PublicUtility/latency.c; sourceTree = ""; }; @@ -3047,6 +3095,22 @@ 4BF3390D0F8B86AF0080FB5B /* MIDI */ = { isa = PBXGroup; children = ( + 4B370A14133DD7E200237B68 /* JackCoreMidiInputPort.cpp */, + 4B370A15133DD7E200237B68 /* JackCoreMidiInputPort.h */, + 4B370A16133DD7E200237B68 /* JackCoreMidiOutputPort.cpp */, + 4B370A17133DD7E200237B68 /* JackCoreMidiOutputPort.h */, + 4B370A18133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.cpp */, + 4B370A19133DD7E200237B68 /* JackCoreMidiPhysicalInputPort.h */, + 4B370A1A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp */, + 4B370A1B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h */, + 4B370A1C133DD7E300237B68 /* JackCoreMidiPort.cpp */, + 4B370A1D133DD7E300237B68 /* JackCoreMidiPort.h */, + 4B370A1E133DD7E300237B68 /* JackCoreMidiUtil.cpp */, + 4B370A1F133DD7E300237B68 /* JackCoreMidiUtil.h */, + 4B370A20133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp */, + 4B370A21133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h */, + 4B370A22133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp */, + 4B370A23133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h */, 4BF3391F0F8B873E0080FB5B /* JackMidiDriver.cpp */, 4BF339200F8B873E0080FB5B /* JackMidiDriver.h */, 4BF339140F8B86DC0080FB5B /* JackCoreMidiDriver.h */, @@ -3994,6 +4058,14 @@ buildActionMask = 2147483647; files = ( 4BDCDB951001FB9C00B15929 /* JackCoreMidiDriver.h in Headers */, + 4B370A35133DD7E300237B68 /* JackCoreMidiInputPort.h in Headers */, + 4B370A37133DD7E300237B68 /* JackCoreMidiOutputPort.h in Headers */, + 4B370A39133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.h in Headers */, + 4B370A3B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h in Headers */, + 4B370A3D133DD7E300237B68 /* JackCoreMidiPort.h in Headers */, + 4B370A3F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */, + 4B370A41133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */, + 4B370A43133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4054,6 +4126,14 @@ buildActionMask = 2147483647; files = ( 4BF3391A0F8B86DC0080FB5B /* JackCoreMidiDriver.h in Headers */, + 4B370A25133DD7E300237B68 /* JackCoreMidiInputPort.h in Headers */, + 4B370A27133DD7E300237B68 /* JackCoreMidiOutputPort.h in Headers */, + 4B370A29133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.h in Headers */, + 4B370A2B133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.h in Headers */, + 4B370A2D133DD7E300237B68 /* JackCoreMidiPort.h in Headers */, + 4B370A2F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */, + 4B370A31133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */, + 4B370A33133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7383,6 +7463,14 @@ buildActionMask = 2147483647; files = ( 4BDCDB971001FB9C00B15929 /* JackCoreMidiDriver.cpp in Sources */, + 4B370A34133DD7E300237B68 /* JackCoreMidiInputPort.cpp in Sources */, + 4B370A36133DD7E300237B68 /* JackCoreMidiOutputPort.cpp in Sources */, + 4B370A38133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.cpp in Sources */, + 4B370A3A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp in Sources */, + 4B370A3C133DD7E300237B68 /* JackCoreMidiPort.cpp in Sources */, + 4B370A3E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */, + 4B370A40133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */, + 4B370A42133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7448,6 +7536,14 @@ buildActionMask = 2147483647; files = ( 4BF3391B0F8B86DC0080FB5B /* JackCoreMidiDriver.cpp in Sources */, + 4B370A24133DD7E300237B68 /* JackCoreMidiInputPort.cpp in Sources */, + 4B370A26133DD7E300237B68 /* JackCoreMidiOutputPort.cpp in Sources */, + 4B370A28133DD7E300237B68 /* JackCoreMidiPhysicalInputPort.cpp in Sources */, + 4B370A2A133DD7E300237B68 /* JackCoreMidiPhysicalOutputPort.cpp in Sources */, + 4B370A2C133DD7E300237B68 /* JackCoreMidiPort.cpp in Sources */, + 4B370A2E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */, + 4B370A30133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */, + 4B370A32133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index 4be05c2d..b006c333 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -26,6 +26,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackCoreMidiUtil.h" #include "JackEngineControl.h" +namespace Jack +{ + /////////////////////////////////////////////////////////////////////////////// // Static callbacks /////////////////////////////////////////////////////////////////////////////// @@ -255,6 +258,9 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, int po_count = 0; int vi_count = 0; int vo_count = 0; + ItemCount potential_po_count; + ItemCount potential_pi_count; + CFStringRef name = CFStringCreateWithCString(0, "JackMidi", CFStringGetSystemEncoding()); if (! name) { @@ -277,7 +283,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, if (in_channels) { try { virtual_input_ports = - new JackCoreMidiVirtualInputPort[in_channels]; + new JackCoreMidiVirtualInputPort*[in_channels]; } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating virtual " "input port array: %s", e.what()); @@ -302,7 +308,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, if (out_channels) { try { virtual_output_ports = - new JackCoreMidiVirtualOutputPort[out_channels]; + new JackCoreMidiVirtualOutputPort*[out_channels]; } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating virtual " "output port array: %s", e.what()); @@ -325,7 +331,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } // Allocate and connect physical inputs - ItemCount potential_pi_count = MIDIGetNumberOfSources(); + potential_pi_count = MIDIGetNumberOfSources(); if (potential_pi_count) { status = MIDIInputPortCreate(client, CFSTR("Physical Input Port"), HandleInputEvent, this, &internal_input); @@ -336,7 +342,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } try { physical_input_ports = - new JackCoreMidiPhysicalInputPort[potential_pi_count]; + new JackCoreMidiPhysicalInputPort*[potential_pi_count]; } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating physical " "input port array: %s", e.what()); @@ -359,7 +365,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } // Allocate and connect physical outputs - ItemCount potential_po_count = MIDIGetNumberOfDestinations(); + potential_po_count = MIDIGetNumberOfDestinations(); if (potential_po_count) { status = MIDIOutputPortCreate(client, CFSTR("Physical Output Port"), &internal_output); @@ -370,7 +376,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } try { physical_output_ports = - new JackCoreMidiPhysicalOutputPort[potential_po_count]; + new JackCoreMidiPhysicalOutputPort*[potential_po_count]; } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating physical " "output port array: %s", e.what()); @@ -626,6 +632,8 @@ JackCoreMidiDriver::Write() return 0; } +} // end of namespace + #ifdef __cplusplus extern "C" { #endif diff --git a/macosx/coremidi/JackCoreMidiDriver.h b/macosx/coremidi/JackCoreMidiDriver.h index 34056c33..c5a26f5b 100644 --- a/macosx/coremidi/JackCoreMidiDriver.h +++ b/macosx/coremidi/JackCoreMidiDriver.h @@ -50,11 +50,11 @@ namespace Jack { int num_physical_outputs; int num_virtual_inputs; int num_virtual_outputs; - JackCoreMidiPhysicalInputPort *physical_input_ports; - JackCoreMidiPhysicalOutputPort *physical_output_ports; + JackCoreMidiPhysicalInputPort **physical_input_ports; + JackCoreMidiPhysicalOutputPort **physical_output_ports; double time_ratio; - JackCoreMidiVirtualInputPort *virtual_input_ports; - JackCoreMidiVirtualOutputPort *virtual_output_ports; + JackCoreMidiVirtualInputPort **virtual_input_ports; + JackCoreMidiVirtualOutputPort **virtual_output_ports; public: diff --git a/macosx/coremidi/JackCoreMidiInputPort.cpp b/macosx/coremidi/JackCoreMidiInputPort.cpp index 20d2af0c..e28d62b2 100644 --- a/macosx/coremidi/JackCoreMidiInputPort.cpp +++ b/macosx/coremidi/JackCoreMidiInputPort.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackCoreMidiInputPort.h" #include "JackMidiUtil.h" +//#include "types.h" using Jack::JackCoreMidiInputPort; @@ -47,8 +48,8 @@ JackCoreMidiInputPort::~JackCoreMidiInputPort() delete[] sysex_buffer; } -jack_frames_t -JackCoreMidiPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp) +jack_nframes_t +JackCoreMidiInputPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp) { return GetFramesFromTime((jack_time_t) (timestamp * time_ratio)); } @@ -59,7 +60,7 @@ JackCoreMidiInputPort::Initialize(const char *alias_name, const char *driver_name, int index, MIDIEndpointRef endpoint) { - Initialize(alias_name, client_name, driver_name, index, endpoint, false); + JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false); } void @@ -127,7 +128,7 @@ JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list) case JackMidiWriteQueue::BUFFER_TOO_SMALL: jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread " "queue couldn't enqueue a %d-byte packet. Dropping " - "event.", event->size); + "event.", event.size); break; default: ; @@ -143,7 +144,7 @@ void JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames) { - write_queue->ResetMidiBuffer(port_buffer); + write_queue->ResetMidiBuffer(port_buffer, frames); if (! jack_event) { jack_event = thread_queue->DequeueEvent(); } diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index dced4fab..9bf9a5a3 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -21,6 +21,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include "JackCoreMidiOutputPort.h" +#include "JackMidiUtil.h" using Jack::JackCoreMidiOutputPort; @@ -126,7 +127,7 @@ JackCoreMidiOutputPort::Execute() } MIDITimeStamp -JackCoreMidiPort::GetTimeStampFromFrames(jack_nframes_t frames) +JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames) { return GetTimeFromFrames(frames) / time_ratio; } @@ -148,7 +149,7 @@ JackCoreMidiOutputPort::Initialize(const char *alias_name, const char *driver_name, int index, MIDIEndpointRef endpoint) { - Initialize(alias_name, client_name, driver_name, index, endpoint, true); + JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, true); } void diff --git a/macosx/coremidi/JackCoreMidiOutputPort.h b/macosx/coremidi/JackCoreMidiOutputPort.h index 0108770e..cddda999 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.h +++ b/macosx/coremidi/JackCoreMidiOutputPort.h @@ -35,11 +35,6 @@ namespace Jack { MIDITimeStamp GetTimeStampFromFrames(jack_nframes_t frames); - void - Initialize(const char *alias_name, const char *client_name, - const char *driver_name, int index, - MIDIEndpointRef endpoint); - static const size_t PACKET_BUFFER_SIZE = 65536; char packet_buffer[PACKET_BUFFER_SIZE]; @@ -50,12 +45,17 @@ namespace Jack { protected: - virtual void + virtual bool SendPacketList(MIDIPacketList *packet_list) = 0; + void + Initialize(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint); + public: - JackCoreMidiOutputPort(double time_ratio, size_t max_bytes=4096, + JackCoreMidiOutputPort(double time_ratio, int realtime_priority, size_t max_bytes=4096, size_t max_messages=1024); virtual diff --git a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp index 1af6b2c6..9716359a 100644 --- a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp @@ -55,7 +55,7 @@ JackCoreMidiPhysicalOutputPort::~JackCoreMidiPhysicalOutputPort() bool JackCoreMidiPhysicalOutputPort::SendPacketList(MIDIPacketList *packet_list) { - OSStatus status = MIDISend(internal_output, source, packet_list); + OSStatus status = MIDISend(internal_output, endpoint, packet_list); bool result = status == noErr; if (! result) { WriteMacOSError("JackCoreMidiPhysicalOutputPort::SendPacketList", diff --git a/macosx/coremidi/JackCoreMidiPort.cpp b/macosx/coremidi/JackCoreMidiPort.cpp index 1783f34e..e3c027c3 100644 --- a/macosx/coremidi/JackCoreMidiPort.cpp +++ b/macosx/coremidi/JackCoreMidiPort.cpp @@ -20,6 +20,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include "JackCoreMidiPort.h" +#include "JackCoreMidiUtil.h" +#include "JackError.h" using Jack::JackCoreMidiPort; diff --git a/macosx/coremidi/JackCoreMidiPort.h b/macosx/coremidi/JackCoreMidiPort.h index e8dd64e4..8a5641bb 100644 --- a/macosx/coremidi/JackCoreMidiPort.h +++ b/macosx/coremidi/JackCoreMidiPort.h @@ -31,7 +31,6 @@ namespace Jack { private: char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - MIDIEndpointRef endpoint; bool initialized; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; @@ -46,6 +45,7 @@ namespace Jack { MIDIEndpointRef endpoint, bool is_output); double time_ratio; + MIDIEndpointRef endpoint; public: diff --git a/macosx/coremidi/JackCoreMidiUtil.cpp b/macosx/coremidi/JackCoreMidiUtil.cpp index 80b8746e..1652036a 100644 --- a/macosx/coremidi/JackCoreMidiUtil.cpp +++ b/macosx/coremidi/JackCoreMidiUtil.cpp @@ -20,6 +20,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include "JackError.h" +#include "JackCoreMidiUtil.h" +#import std::string Jack::GetMacOSErrorString(OSStatus status) @@ -42,3 +44,4 @@ Jack::WriteMacOSError(const char *jack_function, const char *mac_function, jack_error("%s - %s: %s", jack_function, mac_function, GetMacOSStatusString(status).c_str()); } + diff --git a/macosx/coremidi/JackCoreMidiUtil.h b/macosx/coremidi/JackCoreMidiUtil.h index 5c9aac00..291cabe6 100644 --- a/macosx/coremidi/JackCoreMidiUtil.h +++ b/macosx/coremidi/JackCoreMidiUtil.h @@ -20,8 +20,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackCoreMidiUtil__ #define __JackCoreMidiUtil__ -#include - +#import #include namespace Jack { diff --git a/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp index 6c6ec015..42d9b52f 100644 --- a/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp +++ b/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp @@ -50,7 +50,7 @@ JackCoreMidiVirtualInputPort(const char *alias_name, const char *client_name, { std::stringstream stream; stream << "JackMidi" << (index + 1); - CFStringRef name = CFStringCreateWithString(0, stream.str().c_str(), + CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), CFStringGetSystemEncoding()); if (! name) { throw std::bad_alloc(); diff --git a/macosx/coremidi/JackCoreMidiVirtualInputPort.h b/macosx/coremidi/JackCoreMidiVirtualInputPort.h index 62c970da..e48126c4 100644 --- a/macosx/coremidi/JackCoreMidiVirtualInputPort.h +++ b/macosx/coremidi/JackCoreMidiVirtualInputPort.h @@ -30,7 +30,7 @@ namespace Jack { static void HandleInputEvent(const MIDIPacketList *packet_list, void *port, - void *src_ref) + void *src_ref); public: diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp index 2fa3f2cf..f295449d 100644 --- a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp @@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include +#import #include "JackCoreMidiUtil.h" #include "JackCoreMidiVirtualOutputPort.h" @@ -62,11 +63,11 @@ JackCoreMidiVirtualOutputPort::~JackCoreMidiVirtualOutputPort() bool JackCoreMidiVirtualOutputPort::SendPacketList(MIDIPacketList *packet_list) { - OSStatus status = MIDIReceived(source, packet_list); + OSStatus status = MIDIReceived(endpoint, packet_list); bool result = status == noErr; if (! result) { WriteMacOSError("JackCoreMidiVirtualOutputPort::SendPacketList", - "MIDIReceived" status); + "MIDIReceived", status); } return result; } From 03e695ba1e3af55d685f58b534f4ada1cc592185 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Sat, 26 Mar 2011 12:52:52 +0100 Subject: [PATCH 22/58] Correct loopback driver for new activation model. --- common/JackDriver.h | 13 --------- common/JackLoopbackDriver.cpp | 55 ++++++++++++++++++++++++++++++----- common/JackLoopbackDriver.h | 11 ++++++- common/JackMidiDriver.cpp | 14 ++++----- 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/common/JackDriver.h b/common/JackDriver.h index e619f6e4..45d695af 100644 --- a/common/JackDriver.h +++ b/common/JackDriver.h @@ -232,19 +232,6 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface }; -/* -class SERVER_EXPORT JackSlaveDriverInterface -{ - - public: - - virtual int ProcessRead() { return 0; } - virtual int ProcessWrite() { return 0; } - -}; - -*/ - } // end of namespace #endif diff --git a/common/JackLoopbackDriver.cpp b/common/JackLoopbackDriver.cpp index e91cce1e..07778417 100644 --- a/common/JackLoopbackDriver.cpp +++ b/common/JackLoopbackDriver.cpp @@ -30,20 +30,61 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { -int JackLoopbackDriver::Process() +int JackLoopbackDriver::ProcessRead() { + return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); +} + +int JackLoopbackDriver::ProcessWrite() +{ + return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); +} + +int JackLoopbackDriver::ProcessReadSync() +{ + int res = 0; + // Loopback copy for (int i = 0; i < fCaptureChannels; i++) { memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); } - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients - if (fEngineControl->fSyncMode) { - if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackLoopbackDriver::ProcessSync SuspendRefNum error"); - return -1; - } + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackLoopbackDriver::ProcessReadSync - ResumeRefNum error"); + res = -1; + } + + return res; +} + +int JackLoopbackDriver::ProcessWriteSync() +{ + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackLoopbackDriver::ProcessWriteSync SuspendRefNum error"); + return -1; + } + return 0; +} + +int JackLoopbackDriver::ProcessReadAsync() +{ + int res = 0; + + // Loopback copy + for (int i = 0; i < fCaptureChannels; i++) { + memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); + } + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackLoopbackDriver::ProcessReadAsync - ResumeRefNum error"); + res = -1; } + + return res; +} + +int JackLoopbackDriver::ProcessWriteAsync() +{ return 0; } diff --git a/common/JackLoopbackDriver.h b/common/JackLoopbackDriver.h index 5c542e3a..8fe22266 100644 --- a/common/JackLoopbackDriver.h +++ b/common/JackLoopbackDriver.h @@ -33,6 +33,14 @@ namespace Jack class JackLoopbackDriver : public JackAudioDriver { + private: + + virtual int ProcessReadSync(); + virtual int ProcessWriteSync(); + + virtual int ProcessReadAsync(); + virtual int ProcessWriteAsync(); + public: JackLoopbackDriver(JackLockedEngine* engine, JackSynchro* table) @@ -41,7 +49,8 @@ class JackLoopbackDriver : public JackAudioDriver virtual ~JackLoopbackDriver() {} - int Process(); + virtual int ProcessRead(); + virtual int ProcessWrite(); }; } // end of namespace diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index a2fffa83..4de11989 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -164,12 +164,12 @@ int JackMidiDriver::ProcessReadSync() // Read input buffers for the current cycle if (Read() < 0) { - jack_error("JackMidiDriver::ProcessSync: read error, skip cycle"); + jack_error("JackMidiDriver::ProcessReadSync: read error, skip cycle"); res = -1; } if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { - jack_error("JackMidiDriver::ProcessSync - ResumeRefNum error"); + jack_error("JackMidiDriver::ProcessReadSync - ResumeRefNum error"); res = -1; } @@ -183,13 +183,13 @@ int JackMidiDriver::ProcessWriteSync() if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackMidiDriver::ProcessSync - SuspendRefNum error"); + jack_error("JackMidiDriver::ProcessWriteSync - SuspendRefNum error"); res = -1; } // Write output buffers from the current cycle if (Write() < 0) { - jack_error("JackMidiDriver::ProcessSync - Write error"); + jack_error("JackMidiDriver::ProcessWriteSync - Write error"); res = -1; } @@ -202,18 +202,18 @@ int JackMidiDriver::ProcessReadAsync() // Read input buffers for the current cycle if (Read() < 0) { - jack_error("JackMidiDriver::ProcessAsync: read error, skip cycle"); + jack_error("JackMidiDriver::ProcessReadAsync: read error, skip cycle"); res = -1; } // Write output buffers from the previous cycle if (Write() < 0) { - jack_error("JackMidiDriver::ProcessAsync - Write error"); + jack_error("JackMidiDriver::ProcessReadAsync - Write error"); res = -1; } if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { - jack_error("JackMidiDriver::ProcessAsync - ResumeRefNum error"); + jack_error("JackMidiDriver::ProcessReadAsync - ResumeRefNum error"); res = -1; } From 94cd8d0be595999a5b53303e2e0463c4bad4b569 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Sat, 26 Mar 2011 14:59:31 -0700 Subject: [PATCH 23/58] Fix compilation errors sent from Stephane. --- common/JackMidiAsyncWaitQueue.h | 2 ++ macosx/coremidi/JackCoreMidiDriver.cpp | 5 +---- macosx/coremidi/JackCoreMidiInputPort.cpp | 5 ++--- macosx/coremidi/JackCoreMidiOutputPort.cpp | 11 ++++++----- macosx/coremidi/JackCoreMidiUtil.cpp | 10 +++------- macosx/coremidi/JackCoreMidiUtil.h | 7 ++++++- macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp | 1 - 7 files changed, 20 insertions(+), 21 deletions(-) diff --git a/common/JackMidiAsyncWaitQueue.h b/common/JackMidiAsyncWaitQueue.h index d35dd8b6..8499f688 100644 --- a/common/JackMidiAsyncWaitQueue.h +++ b/common/JackMidiAsyncWaitQueue.h @@ -44,6 +44,8 @@ namespace Jack { public: + using JackMidiAsyncQueue::EnqueueEvent; + /** * Creates a new asynchronous MIDI wait message queue. The queue can * store up to `max_messages` MIDI messages and up to `max_bytes` of diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index b006c333..4be27180 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -26,8 +26,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackCoreMidiUtil.h" #include "JackEngineControl.h" -namespace Jack -{ +using Jack::JackCoreMidiDriver; /////////////////////////////////////////////////////////////////////////////// // Static callbacks @@ -632,8 +631,6 @@ JackCoreMidiDriver::Write() return 0; } -} // end of namespace - #ifdef __cplusplus extern "C" { #endif diff --git a/macosx/coremidi/JackCoreMidiInputPort.cpp b/macosx/coremidi/JackCoreMidiInputPort.cpp index e28d62b2..6bf19a1e 100644 --- a/macosx/coremidi/JackCoreMidiInputPort.cpp +++ b/macosx/coremidi/JackCoreMidiInputPort.cpp @@ -100,9 +100,8 @@ JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list) sysex_bytes_sent = 0; goto get_next_packet; } - event.data = sysex_buffer; + event.buffer = sysex_buffer; event.size = sysex_bytes_sent; - event.time = ; sysex_bytes_sent = 0; goto send_event; } @@ -115,7 +114,7 @@ JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list) goto buffer_sysex_bytes; } } - event.data = data; + event.buffer = data; event.size = size; send_event: diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index 9bf9a5a3..fcc4102e 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -60,7 +60,7 @@ JackCoreMidiOutputPort::Execute() if (! event) { event = thread_queue->DequeueEvent((long) 0); } - jack_midi_data_t *data = event->data; + jack_midi_data_t *data = event->buffer; // This is the latest time that the packet list can be sent out. We // may want to consider subtracting some frames to leave room for the @@ -79,10 +79,10 @@ JackCoreMidiOutputPort::Execute() if (! event) { break; } - packet = MIDIPacketListAdd(packet_list, midi_buffer_size, + packet = MIDIPacketListAdd(packet_list, sizeof(packet_buffer), packet, GetTimeStampFromFrames(event->time), - event->size, event->data); + event->size, event->buffer); if (! packet) { break; } @@ -107,8 +107,9 @@ JackCoreMidiOutputPort::Execute() if (num_bytes > 256) { num_bytes = 256; } - packet = MIDIPacketListAdd(packet_list, midi_buffer_size, - packet, timestamp, num_bytes, + packet = MIDIPacketListAdd(packet_list, + sizeof(packet_buffer), packet, + timestamp, num_bytes, data + bytes_sent); if (! packet) { break; diff --git a/macosx/coremidi/JackCoreMidiUtil.cpp b/macosx/coremidi/JackCoreMidiUtil.cpp index 1652036a..d976e936 100644 --- a/macosx/coremidi/JackCoreMidiUtil.cpp +++ b/macosx/coremidi/JackCoreMidiUtil.cpp @@ -21,15 +21,12 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackError.h" #include "JackCoreMidiUtil.h" -#import std::string Jack::GetMacOSErrorString(OSStatus status) { - char message[kMaxErrorLength + 1]; - OSStatus result = GetErrorLongString(error, message, - (long) sizeof(message)); - if (result != noErr) { + const char *message = GetMacOSStatusErrorString(status); + if (! message) { std::stringstream stream; stream << "error (code: '" << status << "')"; return stream.str(); @@ -42,6 +39,5 @@ Jack::WriteMacOSError(const char *jack_function, const char *mac_function, OSStatus status) { jack_error("%s - %s: %s", jack_function, mac_function, - GetMacOSStatusString(status).c_str()); + GetMacOSErrorString(status).c_str()); } - diff --git a/macosx/coremidi/JackCoreMidiUtil.h b/macosx/coremidi/JackCoreMidiUtil.h index 291cabe6..2b8fe6fe 100644 --- a/macosx/coremidi/JackCoreMidiUtil.h +++ b/macosx/coremidi/JackCoreMidiUtil.h @@ -20,9 +20,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackCoreMidiUtil__ #define __JackCoreMidiUtil__ -#import #include +#include + +// Import? What does this line do? Is it necessary now that I'm not including +// the same library I was including? +#import + namespace Jack { std::string diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp index f295449d..51e3f69c 100644 --- a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp @@ -19,7 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include -#import #include "JackCoreMidiUtil.h" #include "JackCoreMidiVirtualOutputPort.h" From 62496d8ba8fafa22d2fb8a2c2908648ea2f2972f Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Sun, 27 Mar 2011 10:52:48 +0200 Subject: [PATCH 24/58] Now compiles on OSX. --- macosx/JackMachThread.h | 2 +- macosx/Jackdmp.xcodeproj/project.pbxproj | 122 +++++++++++++++++++++++ macosx/coremidi/JackCoreMidiUtil.h | 7 +- 3 files changed, 125 insertions(+), 6 deletions(-) diff --git a/macosx/JackMachThread.h b/macosx/JackMachThread.h index c22ce20a..92f77d9e 100644 --- a/macosx/JackMachThread.h +++ b/macosx/JackMachThread.h @@ -62,7 +62,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define __JackMachThread__ #include "JackPosixThread.h" -#import +#include #include #include diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index 3ba3fc40..d7041d38 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -108,6 +108,48 @@ /* Begin PBXBuildFile section */ 4B0A28ED0D520852002EFF74 /* tw.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A28EC0D520852002EFF74 /* tw.c */; }; 4B0A29260D52108E002EFF74 /* tw.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A28EC0D520852002EFF74 /* tw.c */; }; + 4B193933133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */; }; + 4B193934133F311400547810 /* JackMidiAsyncQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193932133F311400547810 /* JackMidiAsyncQueue.h */; }; + 4B193935133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */; }; + 4B193936133F311400547810 /* JackMidiAsyncQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193932133F311400547810 /* JackMidiAsyncQueue.h */; }; + 4B19393D133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */; }; + 4B19393E133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */; }; + 4B19393F133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */; }; + 4B193940133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */; }; + 4B193947133F315300547810 /* JackMidiReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193945133F315200547810 /* JackMidiReadQueue.cpp */; }; + 4B193948133F315300547810 /* JackMidiReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193946133F315200547810 /* JackMidiReadQueue.h */; }; + 4B193949133F315300547810 /* JackMidiReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193945133F315200547810 /* JackMidiReadQueue.cpp */; }; + 4B19394A133F315300547810 /* JackMidiReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193946133F315200547810 /* JackMidiReadQueue.h */; }; + 4B19395F133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */; }; + 4B193960133F317300547810 /* JackMidiBufferReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */; }; + 4B193961133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */; }; + 4B193962133F317300547810 /* JackMidiBufferReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */; }; + 4B19396A133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */; }; + 4B19396B133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */; }; + 4B19396C133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */; }; + 4B19396D133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */; }; + 4B19397B133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */; }; + 4B19397C133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */; }; + 4B19397D133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */; }; + 4B19397E133F31CB00547810 /* JackMidiSendQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193976133F31CB00547810 /* JackMidiSendQueue.h */; }; + 4B19397F133F31CB00547810 /* JackMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193977133F31CB00547810 /* JackMidiUtil.cpp */; }; + 4B193980133F31CB00547810 /* JackMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193978133F31CB00547810 /* JackMidiUtil.h */; }; + 4B193981133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */; }; + 4B193982133F31CB00547810 /* JackMidiWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */; }; + 4B193983133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */; }; + 4B193984133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */; }; + 4B193985133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */; }; + 4B193986133F31CB00547810 /* JackMidiSendQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193976133F31CB00547810 /* JackMidiSendQueue.h */; }; + 4B193987133F31CB00547810 /* JackMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193977133F31CB00547810 /* JackMidiUtil.cpp */; }; + 4B193988133F31CB00547810 /* JackMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193978133F31CB00547810 /* JackMidiUtil.h */; }; + 4B193989133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */; }; + 4B19398A133F31CB00547810 /* JackMidiWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */; }; + 4B193991133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; + 4B193992133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; + 4B193993133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; + 4B193994133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; + 4B193995133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; + 4B193996133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; 4B19B3130E2362E800DD4A82 /* JackAudioAdapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B3060E2362E700DD4A82 /* JackAudioAdapter.cpp */; }; 4B19B3140E2362E800DD4A82 /* JackAudioAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19B3070E2362E700DD4A82 /* JackAudioAdapter.h */; }; 4B19B3150E2362E800DD4A82 /* JackAudioAdapterInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B3080E2362E700DD4A82 /* JackAudioAdapterInterface.cpp */; }; @@ -1483,6 +1525,25 @@ 4B0A28E60D52073D002EFF74 /* jack_thread_wait */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_thread_wait; sourceTree = BUILT_PRODUCTS_DIR; }; 4B0A28EC0D520852002EFF74 /* tw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tw.c; path = "../example-clients/tw.c"; sourceTree = SOURCE_ROOT; }; 4B0A292D0D52108E002EFF74 /* jack_thread_wait */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_thread_wait; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiAsyncQueue.cpp; path = ../common/JackMidiAsyncQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B193932133F311400547810 /* JackMidiAsyncQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiAsyncQueue.h; path = ../common/JackMidiAsyncQueue.h; sourceTree = SOURCE_ROOT; }; + 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiAsyncWaitQueue.cpp; path = ../common/JackMidiAsyncWaitQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiAsyncWaitQueue.h; path = ../common/JackMidiAsyncWaitQueue.h; sourceTree = SOURCE_ROOT; }; + 4B193945133F315200547810 /* JackMidiReadQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiReadQueue.cpp; path = ../common/JackMidiReadQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B193946133F315200547810 /* JackMidiReadQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiReadQueue.h; path = ../common/JackMidiReadQueue.h; sourceTree = SOURCE_ROOT; }; + 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiBufferReadQueue.cpp; path = ../common/JackMidiBufferReadQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiBufferReadQueue.h; path = ../common/JackMidiBufferReadQueue.h; sourceTree = SOURCE_ROOT; }; + 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiBufferWriteQueue.cpp; path = ../common/JackMidiBufferWriteQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiBufferWriteQueue.h; path = ../common/JackMidiBufferWriteQueue.h; sourceTree = SOURCE_ROOT; }; + 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiReceiveQueue.cpp; path = ../common/JackMidiReceiveQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiReceiveQueue.h; path = ../common/JackMidiReceiveQueue.h; sourceTree = SOURCE_ROOT; }; + 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiSendQueue.cpp; path = ../common/JackMidiSendQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B193976133F31CB00547810 /* JackMidiSendQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiSendQueue.h; path = ../common/JackMidiSendQueue.h; sourceTree = SOURCE_ROOT; }; + 4B193977133F31CB00547810 /* JackMidiUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiUtil.cpp; path = ../common/JackMidiUtil.cpp; sourceTree = SOURCE_ROOT; }; + 4B193978133F31CB00547810 /* JackMidiUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiUtil.h; path = ../common/JackMidiUtil.h; sourceTree = SOURCE_ROOT; }; + 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackMidiWriteQueue.cpp; path = ../common/JackMidiWriteQueue.cpp; sourceTree = SOURCE_ROOT; }; + 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMidiWriteQueue.h; path = ../common/JackMidiWriteQueue.h; sourceTree = SOURCE_ROOT; }; + 4B193990133F321500547810 /* JackFilters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackFilters.h; path = ../common/JackFilters.h; sourceTree = SOURCE_ROOT; }; 4B19B3000E23620F00DD4A82 /* audioadapter.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = audioadapter.so; sourceTree = BUILT_PRODUCTS_DIR; }; 4B19B3060E2362E700DD4A82 /* JackAudioAdapter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackAudioAdapter.cpp; path = ../common/JackAudioAdapter.cpp; sourceTree = SOURCE_ROOT; }; 4B19B3070E2362E700DD4A82 /* JackAudioAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackAudioAdapter.h; path = ../common/JackAudioAdapter.h; sourceTree = SOURCE_ROOT; }; @@ -2990,6 +3051,7 @@ 4BA550FB05E2420000569492 /* Engine */ = { isa = PBXGroup; children = ( + 4B193990133F321500547810 /* JackFilters.h */, 4B5F253D0DEE9B8F0041E486 /* JackLockedEngine.h */, 4BF8D2130834F02800C94B91 /* JackEngine.h */, 4BF8D2140834F02800C94B91 /* JackEngine.cpp */, @@ -3095,6 +3157,24 @@ 4BF3390D0F8B86AF0080FB5B /* MIDI */ = { isa = PBXGroup; children = ( + 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */, + 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */, + 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */, + 4B193976133F31CB00547810 /* JackMidiSendQueue.h */, + 4B193977133F31CB00547810 /* JackMidiUtil.cpp */, + 4B193978133F31CB00547810 /* JackMidiUtil.h */, + 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */, + 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */, + 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */, + 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */, + 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */, + 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */, + 4B193945133F315200547810 /* JackMidiReadQueue.cpp */, + 4B193946133F315200547810 /* JackMidiReadQueue.h */, + 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */, + 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */, + 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */, + 4B193932133F311400547810 /* JackMidiAsyncQueue.h */, 4B370A14133DD7E200237B68 /* JackCoreMidiInputPort.cpp */, 4B370A15133DD7E200237B68 /* JackCoreMidiInputPort.h */, 4B370A16133DD7E200237B68 /* JackCoreMidiOutputPort.cpp */, @@ -3301,6 +3381,7 @@ 4B8A38F0117B827900664E07 /* JackSocket.h in Headers */, 4B8A38F7117B82B200664E07 /* JackSocketClientChannel.h in Headers */, 4B5160A813215E8B00BB7DCB /* systemdeps.h in Headers */, + 4B193993133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3372,6 +3453,7 @@ 4B8A38B0117B812500664E07 /* JackSocketServerChannel.h in Headers */, 4B8A38C4117B814000664E07 /* JackSocketServerNotifyChannel.h in Headers */, 4B5160AA13215ED900BB7DCB /* systemdeps.h in Headers */, + 4B193995133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3667,6 +3749,7 @@ 4B88D04111298BEE007A87C1 /* weakjack.h in Headers */, 4B88D04211298BEE007A87C1 /* weakmacros.h in Headers */, 4B5160A913215EBF00BB7DCB /* systemdeps.h in Headers */, + 4B193994133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3758,6 +3841,7 @@ 4B88D03C11298BEE007A87C1 /* weakmacros.h in Headers */, 4B2209ED12F6BC2200E5DC26 /* JackSocket.h in Headers */, 4B2209EF12F6BC2500E5DC26 /* JackSocketClientChannel.h in Headers */, + 4B193991133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3832,6 +3916,7 @@ 4B2209E412F6BBF600E5DC26 /* JackSocketServerNotifyChannel.h in Headers */, 4B2209E712F6BC0300E5DC26 /* JackSocket.h in Headers */, 4B2209EA12F6BC1600E5DC26 /* JackSocketNotifyChannel.h in Headers */, + 4B193992133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4022,6 +4107,7 @@ 4BC2CA5E113C6CCA0076717C /* JackNetInterface.h in Headers */, 4BC2CA60113C6CD20076717C /* JackNetUnixSocket.h in Headers */, 4B5160AE13215EF900BB7DCB /* systemdeps.h in Headers */, + 4B193996133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4066,6 +4152,15 @@ 4B370A3F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */, 4B370A41133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */, 4B370A43133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */, + 4B193936133F311400547810 /* JackMidiAsyncQueue.h in Headers */, + 4B193940133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */, + 4B19394A133F315300547810 /* JackMidiReadQueue.h in Headers */, + 4B193962133F317300547810 /* JackMidiBufferReadQueue.h in Headers */, + 4B19396D133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */, + 4B193984133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */, + 4B193986133F31CB00547810 /* JackMidiSendQueue.h in Headers */, + 4B193988133F31CB00547810 /* JackMidiUtil.h in Headers */, + 4B19398A133F31CB00547810 /* JackMidiWriteQueue.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4134,6 +4229,15 @@ 4B370A2F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */, 4B370A31133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */, 4B370A33133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */, + 4B193934133F311400547810 /* JackMidiAsyncQueue.h in Headers */, + 4B19393E133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */, + 4B193948133F315300547810 /* JackMidiReadQueue.h in Headers */, + 4B193960133F317300547810 /* JackMidiBufferReadQueue.h in Headers */, + 4B19396B133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */, + 4B19397C133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */, + 4B19397E133F31CB00547810 /* JackMidiSendQueue.h in Headers */, + 4B193980133F31CB00547810 /* JackMidiUtil.h in Headers */, + 4B193982133F31CB00547810 /* JackMidiWriteQueue.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7471,6 +7575,15 @@ 4B370A3E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */, 4B370A40133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */, 4B370A42133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */, + 4B193935133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */, + 4B19393F133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */, + 4B193949133F315300547810 /* JackMidiReadQueue.cpp in Sources */, + 4B193961133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */, + 4B19396C133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */, + 4B193983133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */, + 4B193985133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */, + 4B193987133F31CB00547810 /* JackMidiUtil.cpp in Sources */, + 4B193989133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7544,6 +7657,15 @@ 4B370A2E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */, 4B370A30133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */, 4B370A32133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */, + 4B193933133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */, + 4B19393D133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */, + 4B193947133F315300547810 /* JackMidiReadQueue.cpp in Sources */, + 4B19395F133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */, + 4B19396A133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */, + 4B19397B133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */, + 4B19397D133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */, + 4B19397F133F31CB00547810 /* JackMidiUtil.cpp in Sources */, + 4B193981133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macosx/coremidi/JackCoreMidiUtil.h b/macosx/coremidi/JackCoreMidiUtil.h index 2b8fe6fe..4f24fc5b 100644 --- a/macosx/coremidi/JackCoreMidiUtil.h +++ b/macosx/coremidi/JackCoreMidiUtil.h @@ -22,11 +22,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include -#include - -// Import? What does this line do? Is it necessary now that I'm not including -// the same library I was including? -#import +#include +#include namespace Jack { From c8e5d8d3ffffdd470614b26c0c05622cd83ef4a8 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Sun, 27 Mar 2011 11:10:27 +0200 Subject: [PATCH 25/58] Update XCode project. --- macosx/Jackdmp.xcodeproj/project.pbxproj | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index d7041d38..4d002151 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -1861,7 +1861,7 @@ 4BF8D2470834F20600C94B91 /* testSem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = testSem.cpp; path = ../tests/testSem.cpp; sourceTree = SOURCE_ROOT; }; 4BF8FB0D08AC88EF00D1A344 /* JackFrameTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackFrameTimer.cpp; path = ../common/JackFrameTimer.cpp; sourceTree = SOURCE_ROOT; }; 4BF8FB0E08AC88EF00D1A344 /* JackFrameTimer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackFrameTimer.h; path = ../common/JackFrameTimer.h; sourceTree = SOURCE_ROOT; }; - 4BFA5E980DEC4D9C00FA4CDB /* testMutex */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testMutex; sourceTree = BUILT_PRODUCTS_DIR; }; + 4BFA5E980DEC4D9C00FA4CDB /* testSem */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testSem; sourceTree = BUILT_PRODUCTS_DIR; }; 4BFA5E9E0DEC4DD900FA4CDB /* testMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testMutex.cpp; path = ../tests/testMutex.cpp; sourceTree = SOURCE_ROOT; }; 4BFA828C0DF6A9E40087B4E1 /* jack_evmon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_evmon; sourceTree = BUILT_PRODUCTS_DIR; }; 4BFA829F0DF6A9E40087B4E1 /* jack_bufsize */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_bufsize; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2662,7 +2662,7 @@ 4B363F1E0DEB0A6A001F72D9 /* jack_monitor_client */, 4B363F350DEB0BD1001F72D9 /* jack_showtime */, 4B363F720DEB0D4E001F72D9 /* jack_impulse_grabber */, - 4BFA5E980DEC4D9C00FA4CDB /* testMutex */, + 4BFA5E980DEC4D9C00FA4CDB /* testSem */, 4BFA828C0DF6A9E40087B4E1 /* jack_evmon */, 4BFA829F0DF6A9E40087B4E1 /* jack_bufsize */, 4BFA82AB0DF6A9E40087B4E1 /* jack_rec */, @@ -5871,7 +5871,7 @@ name = "testMutex Universal"; productInstallPath = /usr/local/bin; productName = testSem; - productReference = 4BFA5E980DEC4D9C00FA4CDB /* testMutex */; + productReference = 4BFA5E980DEC4D9C00FA4CDB /* testSem */; productType = "com.apple.product-type.tool"; }; 4BFA82820DF6A9E40087B4E1 /* jack_evmon 64 bits */ = { @@ -8356,7 +8356,7 @@ OTHER_CFLAGS = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = ( - /usr/local/lib/libsamplerate.a, + /opt/local/lib/libsamplerate.a, "-framework", Jackservermp, "-framework", @@ -9131,7 +9131,7 @@ ); OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -9511,7 +9511,7 @@ CoreAudio, ); OTHER_REZFLAGS = ""; - PRODUCT_NAME = Jackdmp; + PRODUCT_NAME = Jackservermp; REZ_EXECUTABLE = NO; SDKROOT = ""; SECTORDER_FLAGS = ""; @@ -11508,7 +11508,7 @@ OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -11658,7 +11658,7 @@ OTHER_LDFLAGS = ( libportaudio.a, "-framework", - Jackdmp, + Jackservermp, "-framework", AudioToolbox, "-framework", @@ -11807,7 +11807,7 @@ OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -11945,7 +11945,7 @@ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -16457,7 +16457,7 @@ CoreAudio, ); OTHER_REZFLAGS = ""; - PRODUCT_NAME = Jackdmp; + PRODUCT_NAME = Jackservermp; REZ_EXECUTABLE = NO; SDKROOT = ""; SECTORDER_FLAGS = ""; @@ -17468,7 +17468,7 @@ OTHER_CFLAGS = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = ( - /usr/local/lib/libsamplerate.a, + /opt/local/lib/libsamplerate.a, "-framework", Jackservermp, "-framework", @@ -17617,7 +17617,7 @@ OTHER_CFLAGS = ""; OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; OTHER_LDFLAGS = ( - /usr/local/lib/libsamplerate.a, + /opt/local/lib/libsamplerate.a, "-framework", Jackservermp, "-framework", From e843289af872b1c21730faea9b3b9020d3e31bae Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Sun, 27 Mar 2011 11:46:09 +0200 Subject: [PATCH 26/58] MIDI thread RT priority reworked a bit on OSX. --- macosx/Jackdmp.xcodeproj/project.pbxproj | 7 ++++--- macosx/coremidi/JackCoreMidiDriver.cpp | 9 ++++----- macosx/coremidi/JackCoreMidiOutputPort.cpp | 12 +++++++++--- macosx/coremidi/JackCoreMidiOutputPort.h | 3 +-- macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp | 4 ++-- macosx/coremidi/JackCoreMidiPhysicalOutputPort.h | 1 - macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp | 4 ++-- macosx/coremidi/JackCoreMidiVirtualOutputPort.h | 1 - 8 files changed, 22 insertions(+), 19 deletions(-) diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index 4d002151..195dd976 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -1861,7 +1861,7 @@ 4BF8D2470834F20600C94B91 /* testSem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = testSem.cpp; path = ../tests/testSem.cpp; sourceTree = SOURCE_ROOT; }; 4BF8FB0D08AC88EF00D1A344 /* JackFrameTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackFrameTimer.cpp; path = ../common/JackFrameTimer.cpp; sourceTree = SOURCE_ROOT; }; 4BF8FB0E08AC88EF00D1A344 /* JackFrameTimer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackFrameTimer.h; path = ../common/JackFrameTimer.h; sourceTree = SOURCE_ROOT; }; - 4BFA5E980DEC4D9C00FA4CDB /* testSem */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testSem; sourceTree = BUILT_PRODUCTS_DIR; }; + 4BFA5E980DEC4D9C00FA4CDB /* testMutex */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testMutex; sourceTree = BUILT_PRODUCTS_DIR; }; 4BFA5E9E0DEC4DD900FA4CDB /* testMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testMutex.cpp; path = ../tests/testMutex.cpp; sourceTree = SOURCE_ROOT; }; 4BFA828C0DF6A9E40087B4E1 /* jack_evmon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_evmon; sourceTree = BUILT_PRODUCTS_DIR; }; 4BFA829F0DF6A9E40087B4E1 /* jack_bufsize */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_bufsize; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2662,7 +2662,7 @@ 4B363F1E0DEB0A6A001F72D9 /* jack_monitor_client */, 4B363F350DEB0BD1001F72D9 /* jack_showtime */, 4B363F720DEB0D4E001F72D9 /* jack_impulse_grabber */, - 4BFA5E980DEC4D9C00FA4CDB /* testSem */, + 4BFA5E980DEC4D9C00FA4CDB /* testMutex */, 4BFA828C0DF6A9E40087B4E1 /* jack_evmon */, 4BFA829F0DF6A9E40087B4E1 /* jack_bufsize */, 4BFA82AB0DF6A9E40087B4E1 /* jack_rec */, @@ -5871,7 +5871,7 @@ name = "testMutex Universal"; productInstallPath = /usr/local/bin; productName = testSem; - productReference = 4BFA5E980DEC4D9C00FA4CDB /* testSem */; + productReference = 4BFA5E980DEC4D9C00FA4CDB /* testMutex */; productType = "com.apple.product-type.tool"; }; 4BFA82820DF6A9E40087B4E1 /* jack_evmon 64 bits */ = { @@ -9502,6 +9502,7 @@ "-D__SMP__", "-DMACH_RPC_MACH_SEMA", "$(OTHER_CPLUSPLUSFLAGS_QUOTED_FOR_TARGET_1)", + "$(OTHER_CPLUSPLUSFLAGS_QUOTED_FOR_TARGET_2)", ); OTHER_CPLUSPLUSFLAGS_QUOTED_FOR_TARGET_1 = "-DADDON_DIR=\\\"/usr/local/lib/jackmp\\\""; OTHER_LDFLAGS = ( diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index 4be27180..85d01fcf 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -267,8 +267,10 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, "client name string"); return -1; } + OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this, &client); + CFRelease(name); if (status != noErr) { WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientCreate", @@ -276,7 +278,6 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, return -1; } char *client_name = fClientControl.fName; - int port_rt_priority = fEngineControl->fServerPriority + 1; // Allocate and connect virtual inputs if (in_channels) { @@ -319,8 +320,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, new JackCoreMidiVirtualOutputPort(fAliasName, client_name, playback_driver_name, vo_count, client, - time_ratio, - port_rt_priority); + time_ratio); } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating virtual " "output port: %s", e.what()); @@ -387,8 +387,7 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, new JackCoreMidiPhysicalOutputPort(fAliasName, client_name, playback_driver_name, i, client, internal_output, - time_ratio, - port_rt_priority); + time_ratio); } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating " "physical output port: %s", e.what()); diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index fcc4102e..fe1f2d6f 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -26,7 +26,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. using Jack::JackCoreMidiOutputPort; JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, - int realtime_priority, size_t max_bytes, size_t max_messages): JackCoreMidiPort(time_ratio) @@ -36,7 +35,6 @@ JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, thread_queue = new JackMidiAsyncWaitQueue(max_bytes, max_messages); std::auto_ptr thread_ptr(thread_queue); thread = new JackThread(this); - this->realtime_priority = realtime_priority; thread_ptr.release(); read_ptr.release(); } @@ -137,7 +135,15 @@ bool JackCoreMidiOutputPort::Init() { set_threaded_log_function(); - if (thread->AcquireSelfRealTime(realtime_priority)) { + + // OSX only... + UInt64 period = 0; + UInt64 computation = 500 * 1000; + UInt64 constraint = 500 * 1000; + thread->SetParams(period, computation, constraint); + + // Use the server priority : y + if (thread->AcquireSelfRealTime()) { jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime " "scheduling. Continuing anyway."); } diff --git a/macosx/coremidi/JackCoreMidiOutputPort.h b/macosx/coremidi/JackCoreMidiOutputPort.h index cddda999..54df1562 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.h +++ b/macosx/coremidi/JackCoreMidiOutputPort.h @@ -39,7 +39,6 @@ namespace Jack { char packet_buffer[PACKET_BUFFER_SIZE]; JackMidiBufferReadQueue *read_queue; - int realtime_priority; JackThread *thread; JackMidiAsyncWaitQueue *thread_queue; @@ -55,7 +54,7 @@ namespace Jack { public: - JackCoreMidiOutputPort(double time_ratio, int realtime_priority, size_t max_bytes=4096, + JackCoreMidiOutputPort(double time_ratio, size_t max_bytes=4096, size_t max_messages=1024); virtual diff --git a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp index 9716359a..e5b19d59 100644 --- a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp @@ -30,9 +30,9 @@ JackCoreMidiPhysicalOutputPort(const char *alias_name, const char *client_name, const char *driver_name, int index, MIDIClientRef client, MIDIPortRef internal_output, double time_ratio, - int realtime_priority, size_t max_bytes, + size_t max_bytes, size_t max_messages): - JackCoreMidiOutputPort(time_ratio, realtime_priority, max_bytes, + JackCoreMidiOutputPort(time_ratio, max_bytes, max_messages) { MIDIEndpointRef destination = MIDIGetDestination(index); diff --git a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h index fb922f84..9d641180 100644 --- a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h @@ -43,7 +43,6 @@ namespace Jack { MIDIClientRef client, MIDIPortRef internal_output, double time_ratio, - int realtime_priority, size_t max_bytes=4096, size_t max_messages=1024); diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp index 51e3f69c..5e69a170 100644 --- a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp @@ -29,9 +29,9 @@ JackCoreMidiVirtualOutputPort:: JackCoreMidiVirtualOutputPort(const char *alias_name, const char *client_name, const char *driver_name, int index, MIDIClientRef client, double time_ratio, - int realtime_priority, size_t max_bytes, + size_t max_bytes, size_t max_messages): - JackCoreMidiOutputPort(time_ratio, realtime_priority, max_bytes, + JackCoreMidiOutputPort(time_ratio, max_bytes, max_messages) { std::stringstream stream; diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.h b/macosx/coremidi/JackCoreMidiVirtualOutputPort.h index 2b828553..61529a98 100644 --- a/macosx/coremidi/JackCoreMidiVirtualOutputPort.h +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.h @@ -37,7 +37,6 @@ namespace Jack { const char *client_name, const char *driver_name, int index, MIDIClientRef client, double time_ratio, - int realtime_priority, size_t max_bytes=4096, size_t max_messages=1024); From 8ff7bfe6f73ce332fb49463ddca35d8863af75c1 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Sun, 27 Mar 2011 11:49:11 +0200 Subject: [PATCH 27/58] MIDI thread RT priority reworked a bit on OSX. --- macosx/coremidi/JackCoreMidiOutputPort.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index fe1f2d6f..72eea5d4 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -55,6 +55,7 @@ JackCoreMidiOutputPort::Execute() for (;;) { MIDIPacket *packet = MIDIPacketListInit(packet_list); assert(packet); + assert(thread_queue); if (! event) { event = thread_queue->DequeueEvent((long) 0); } From 810ec14d11f1ba8f17ad65a7e2e562c4b3aac633 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Sun, 27 Mar 2011 12:34:40 +0200 Subject: [PATCH 28/58] Improve error handling. --- common/JackMidiAsyncWaitQueue.cpp | 2 ++ common/Jackdmp.cpp | 12 +++++++++--- macosx/coremidi/JackCoreMidiDriver.cpp | 8 ++++++-- macosx/coremidi/JackCoreMidiPort.cpp | 5 +++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/common/JackMidiAsyncWaitQueue.cpp b/common/JackMidiAsyncWaitQueue.cpp index 016737cb..7eea9fb1 100644 --- a/common/JackMidiAsyncWaitQueue.cpp +++ b/common/JackMidiAsyncWaitQueue.cpp @@ -68,6 +68,8 @@ JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame) jack_midi_event_t * JackMidiAsyncWaitQueue::DequeueEvent(long usec) { +printf("JackMidiAsyncWaitQueue::DequeueEvent 0 %d\n", semaphore); + return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ? JackMidiAsyncQueue::DequeueEvent() : 0; } diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp index 602aca31..1cd5968f 100644 --- a/common/Jackdmp.cpp +++ b/common/Jackdmp.cpp @@ -476,7 +476,9 @@ int main(int argc, char* argv[]) fprintf(stderr, "Unknown driver \"%s\"\n", *it); goto close_server; } - jackctl_server_add_slave(server_ctl, slave_driver_ctl); + if (!jackctl_server_add_slave(server_ctl, slave_driver_ctl)) { + fprintf(stderr, "Driver \"%s\" cannot be loaded\n", *it); + } } // Loopback driver @@ -491,7 +493,9 @@ int main(int argc, char* argv[]) value.ui = loopback; jackctl_parameter_set_value(param, &value); } - jackctl_server_add_slave(server_ctl, loopback_driver_ctl); + if (!jackctl_server_add_slave(server_ctl, loopback_driver_ctl)) { + fprintf(stderr, "Driver \"loopback\" cannot be loaded\n"); + } } } @@ -509,7 +513,9 @@ int main(int argc, char* argv[]) fprintf(stderr, "Unknown internal \"%s\"\n", *it); goto stop_server; } - jackctl_server_load_internal(server_ctl, internal_driver_ctl); + if (!jackctl_server_load_internal(server_ctl, internal_driver_ctl)) { + fprintf(stderr, "Internal client \"%s\" cannot be loaded\n", *it); + } } notify_server_start(server_name); diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index 85d01fcf..c7d8d07a 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -357,7 +357,9 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating " "physical input port: %s", e.what()); - continue; + //continue; + // SL : seems safer to fail here? + goto destroy_internal_input_port; } pi_count++; } @@ -391,7 +393,9 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating " "physical output port: %s", e.what()); - continue; + //continue; + // SL : seems safer to fail here? + goto destroy_internal_output_port; } po_count++; } diff --git a/macosx/coremidi/JackCoreMidiPort.cpp b/macosx/coremidi/JackCoreMidiPort.cpp index e3c027c3..82a92fa1 100644 --- a/macosx/coremidi/JackCoreMidiPort.cpp +++ b/macosx/coremidi/JackCoreMidiPort.cpp @@ -65,6 +65,7 @@ JackCoreMidiPort::Initialize(const char *alias_name, const char *client_name, char endpoint_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; CFStringRef endpoint_name_ref; int num = index + 1; + Boolean res; OSStatus result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpoint_name_ref); if (result != noErr) { @@ -72,10 +73,10 @@ JackCoreMidiPort::Initialize(const char *alias_name, const char *client_name, "MIDIObjectGetStringProperty", result); goto get_basic_alias; } - result = CFStringGetCString(endpoint_name_ref, endpoint_name, + res = CFStringGetCString(endpoint_name_ref, endpoint_name, sizeof(endpoint_name), 0); CFRelease(endpoint_name_ref); - if (result != noErr) { + if (!res) { jack_error("JackCoreMidiPort::Initialize - failed to allocate memory " "for endpoint name."); get_basic_alias: From 9a538cbddc453f959f59692f67414e52ffae3c15 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Sun, 27 Mar 2011 12:36:11 -0700 Subject: [PATCH 29/58] Make JackCoreMidiOutputPort use the standard JackMidiAsyncQueue with native semaphores. --- macosx/coremidi/JackCoreMidiOutputPort.cpp | 60 ++++++++++++++++++---- macosx/coremidi/JackCoreMidiOutputPort.h | 11 +++- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index 72eea5d4..c1761075 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -18,7 +18,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include +#include +#include #include +#include #include "JackCoreMidiOutputPort.h" #include "JackMidiUtil.h" @@ -31,18 +34,27 @@ JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, JackCoreMidiPort(time_ratio) { read_queue = new JackMidiBufferReadQueue(); - std::auto_ptr read_ptr(read_queue); - thread_queue = new JackMidiAsyncWaitQueue(max_bytes, max_messages); - std::auto_ptr thread_ptr(thread_queue); + std::auto_ptr read_queue_ptr(read_queue); + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); thread = new JackThread(this); + std::auto_ptr thread_ptr(thread); + sprintf(semaphore_name, "coremidi_thread_queue_semaphore_%p", this); + thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0); + if (thread_queue_semaphore == (sem_t *) SEM_FAILED) { + throw std::runtime_error(strerror(errno)); + } thread_ptr.release(); - read_ptr.release(); + thread_queue_ptr.release(); + read_queue_ptr.release(); } JackCoreMidiOutputPort::~JackCoreMidiOutputPort() { Stop(); delete thread; + sem_destroy(thread_queue_semaphore); + sem_unlink(semaphore_name); delete read_queue; delete thread_queue; } @@ -57,7 +69,7 @@ JackCoreMidiOutputPort::Execute() assert(packet); assert(thread_queue); if (! event) { - event = thread_queue->DequeueEvent((long) 0); + event = GetCoreMidiEvent(true); } jack_midi_data_t *data = event->buffer; @@ -74,7 +86,7 @@ JackCoreMidiOutputPort::Execute() timestamp, size, data); if (packet) { while (GetCurrentFrame() < send_time) { - event = thread_queue->DequeueEvent(); + event = GetCoreMidiEvent(false); if (! event) { break; } @@ -126,6 +138,29 @@ JackCoreMidiOutputPort::Execute() return false; } +jack_midi_event_t * +JackCoreMidiOutputPort::GetCoreMidiEvent(bool block) +{ + if (! block) { + if (sem_trywait(thread_queue_semaphore)) { + if (errno != ETIMEDOUT) { + jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s", + strerror(errno)); + } + return 0; + } + } else { + while (sem_wait(thread_queue_semaphore)) { + if (errno != EINTR) { + jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s", + strerror(errno)); + return 0; + } + } + } + return thread_queue->DequeueEvent(); +} + MIDITimeStamp JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames) { @@ -157,7 +192,8 @@ JackCoreMidiOutputPort::Initialize(const char *alias_name, const char *driver_name, int index, MIDIEndpointRef endpoint) { - JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, true); + JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, + endpoint, true); } void @@ -171,14 +207,18 @@ JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, case JackMidiWriteQueue::BUFFER_FULL: jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " "queue buffer is full. Dropping event."); - continue; + break; case JackMidiWriteQueue::BUFFER_TOO_SMALL: jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " "queue couldn't enqueue a %d-byte event. Dropping " "event.", event->size); - // Fallthrough on purpose + break; default: - ; + if (sem_post(thread_queue_semaphore)) { + jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected " + "error while posting to thread queue semaphore: %s", + strerror(errno)); + } } } } diff --git a/macosx/coremidi/JackCoreMidiOutputPort.h b/macosx/coremidi/JackCoreMidiOutputPort.h index 54df1562..d713c314 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.h +++ b/macosx/coremidi/JackCoreMidiOutputPort.h @@ -20,8 +20,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackCoreMidiOutputPort__ #define __JackCoreMidiOutputPort__ +#include + #include "JackCoreMidiPort.h" -#include "JackMidiAsyncWaitQueue.h" +#include "JackMidiAsyncQueue.h" #include "JackMidiBufferReadQueue.h" #include "JackThread.h" @@ -32,6 +34,9 @@ namespace Jack { private: + jack_midi_event_t * + GetCoreMidiEvent(bool block); + MIDITimeStamp GetTimeStampFromFrames(jack_nframes_t frames); @@ -39,8 +44,10 @@ namespace Jack { char packet_buffer[PACKET_BUFFER_SIZE]; JackMidiBufferReadQueue *read_queue; + char semaphore_name[128]; JackThread *thread; - JackMidiAsyncWaitQueue *thread_queue; + JackMidiAsyncQueue *thread_queue; + sem_t *thread_queue_semaphore; protected: From dd4012c6caf11a7fa2d912902afb733a83a4ec5c Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Sun, 27 Mar 2011 22:24:09 -0700 Subject: [PATCH 30/58] Minor cleanup for 'midi_latency_test.c'. --- example-clients/midi_latency_test.c | 38 +++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c index d9e6fb08..1b14b13c 100644 --- a/example-clients/midi_latency_test.c +++ b/example-clients/midi_latency_test.c @@ -95,11 +95,13 @@ char *program_name; jack_port_t *remote_in_port; jack_port_t *remote_out_port; size_t samples; + #ifdef __APPLE__ sem_t* semaphore; #else sem_t semaphore; #endif + pthread_mutex_t start_mutex; int timeout; jack_nframes_t total_latency; @@ -216,11 +218,13 @@ handle_process(jack_nframes_t frames, void *arg) messages_received++; if (messages_received == samples) { process_state = 2; - #ifdef __APPLE__ + +#ifdef __APPLE__ sem_post(semaphore); - #else +#else sem_post(&semaphore); - #endif +#endif + break; } send_message: @@ -310,11 +314,13 @@ set_process_error(const char *source, const char *message) error_source = source; error_message = message; process_state = -1; - #ifdef __APPLE__ + +#ifdef __APPLE__ sem_post(semaphore); #else sem_post(&semaphore); #endif + } int @@ -472,6 +478,7 @@ main(int argc, char **argv) jack_on_shutdown(client, handle_shutdown, NULL); jack_set_info_function(handle_info); process_state = 0; + #ifdef __APPLE__ // sem_init is not implemented on OSX char name[128]; @@ -488,6 +495,7 @@ main(int argc, char **argv) goto unregister_out_port; } #endif + code = pthread_mutex_init(&start_mutex, NULL); if (code) { error_message = strerror(errno); @@ -523,15 +531,19 @@ main(int argc, char **argv) error_source = "pthread_mutex_unlock"; goto deactivate_client; } + #ifdef __APPLE__ - while (sem_wait(semaphore) != 0) {} + while (sem_wait(semaphore) != 0) { #else - if (sem_wait(&semaphore)) { - error_message = strerror(errno); - error_source = "sem_wait"; - goto deactivate_client; - } + while (sem_wait(&semaphore) != 0) { #endif + + if (errno != EINTR) { + error_message = strerror(errno); + error_source = "sem_wait"; + goto deactivate_client; + } + } if (process_state == 2) { double average_latency = ((double) total_latency) / samples; double average_latency_time = total_latency_time / samples; @@ -599,11 +611,14 @@ main(int argc, char **argv) destroy_mutex: pthread_mutex_destroy(&start_mutex); destroy_semaphore: + #ifdef __APPLE__ sem_destroy(semaphore); + sem_unlink(name); #else sem_destroy(&semaphore); #endif + unregister_out_port: jack_port_unregister(client, out_port); unregister_in_port: @@ -623,8 +638,5 @@ main(int argc, char **argv) output_error(error_source, error_message); exit(EXIT_FAILURE); } -#ifdef __APPLE__ - sem_unlink(name); -#endif return EXIT_SUCCESS; } From 950945cd7df5c6e6e4102576cfc54be0b12e5480 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Sun, 27 Mar 2011 23:18:21 -0700 Subject: [PATCH 31/58] Make midi_latency_test.c use an 'active sense' message instead of a 'midi clock' message when testing one byte messages. Some MIDI interfaces eat up sync messages. Of course, they could very well eat up 'active sense' messages too ... --- example-clients/midi_latency_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-clients/midi_latency_test.c b/example-clients/midi_latency_test.c index 1b14b13c..42e7f1ed 100644 --- a/example-clients/midi_latency_test.c +++ b/example-clients/midi_latency_test.c @@ -407,7 +407,7 @@ main(int argc, char **argv) switch (message_size) { case 1: message_1[0] = 0xf6; - message_2[0] = 0xf8; + message_2[0] = 0xfe; break; case 2: message_1[0] = 0xc0; From bdde2d110beaa2a5400cf02d4e6792d1bd618b05 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Mon, 28 Mar 2011 17:05:03 +0200 Subject: [PATCH 32/58] CoreMidi driver starting to work. --- common/JackMidiAsyncWaitQueue.cpp | 2 -- common/Jackdmp.cpp | 3 +++ macosx/coremidi/JackCoreMidiInputPort.cpp | 1 + macosx/coremidi/JackCoreMidiOutputPort.cpp | 6 +++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/JackMidiAsyncWaitQueue.cpp b/common/JackMidiAsyncWaitQueue.cpp index 7eea9fb1..016737cb 100644 --- a/common/JackMidiAsyncWaitQueue.cpp +++ b/common/JackMidiAsyncWaitQueue.cpp @@ -68,8 +68,6 @@ JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame) jack_midi_event_t * JackMidiAsyncWaitQueue::DequeueEvent(long usec) { -printf("JackMidiAsyncWaitQueue::DequeueEvent 0 %d\n", semaphore); - return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ? JackMidiAsyncQueue::DequeueEvent() : 0; } diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp index 1cd5968f..9123b3c7 100644 --- a/common/Jackdmp.cpp +++ b/common/Jackdmp.cpp @@ -478,6 +478,7 @@ int main(int argc, char* argv[]) } if (!jackctl_server_add_slave(server_ctl, slave_driver_ctl)) { fprintf(stderr, "Driver \"%s\" cannot be loaded\n", *it); + goto close_server; } } @@ -495,6 +496,7 @@ int main(int argc, char* argv[]) } if (!jackctl_server_add_slave(server_ctl, loopback_driver_ctl)) { fprintf(stderr, "Driver \"loopback\" cannot be loaded\n"); + goto close_server; } } @@ -515,6 +517,7 @@ int main(int argc, char* argv[]) } if (!jackctl_server_load_internal(server_ctl, internal_driver_ctl)) { fprintf(stderr, "Internal client \"%s\" cannot be loaded\n", *it); + goto stop_server; } } diff --git a/macosx/coremidi/JackCoreMidiInputPort.cpp b/macosx/coremidi/JackCoreMidiInputPort.cpp index 6bf19a1e..86fa9bf0 100644 --- a/macosx/coremidi/JackCoreMidiInputPort.cpp +++ b/macosx/coremidi/JackCoreMidiInputPort.cpp @@ -147,6 +147,7 @@ JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, if (! jack_event) { jack_event = thread_queue->DequeueEvent(); } + for (; jack_event; jack_event = thread_queue->DequeueEvent()) { // Add 'frames' to MIDI events to align with audio. switch (write_queue->EnqueueEvent(jack_event, frames)) { diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index c1761075..05e1786f 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -36,10 +36,10 @@ JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, read_queue = new JackMidiBufferReadQueue(); std::auto_ptr read_queue_ptr(read_queue); thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); - std::auto_ptr thread_queue_ptr(thread_queue); + std::auto_ptr thread_queue_ptr(thread_queue); thread = new JackThread(this); std::auto_ptr thread_ptr(thread); - sprintf(semaphore_name, "coremidi_thread_queue_semaphore_%p", this); + sprintf(semaphore_name, "coremidi_%p", this); thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0); if (thread_queue_semaphore == (sem_t *) SEM_FAILED) { throw std::runtime_error(strerror(errno)); @@ -202,7 +202,7 @@ JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, { read_queue->ResetMidiBuffer(port_buffer); for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; - event = read_queue->DequeueEvent()) { + event = read_queue->DequeueEvent()) { switch (thread_queue->EnqueueEvent(event, frames)) { case JackMidiWriteQueue::BUFFER_FULL: jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " From c1c9a7c4cd2137f9cca8d19b15afcc4f6b4315f3 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Mon, 28 Mar 2011 17:52:25 +0200 Subject: [PATCH 33/58] Correct JackCoreAudioDriver::Close. --- macosx/coreaudio/JackCoreAudioDriver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/macosx/coreaudio/JackCoreAudioDriver.cpp b/macosx/coreaudio/JackCoreAudioDriver.cpp index 383babf5..6fed74be 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.cpp +++ b/macosx/coreaudio/JackCoreAudioDriver.cpp @@ -1552,7 +1552,6 @@ error: int JackCoreAudioDriver::Close() { jack_log("JackCoreAudioDriver::Close"); - Stop(); // Generic audio driver close int res = JackAudioDriver::Close(); From 33534bd8a73279774c4db04c4091a7a92a3795ab Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Mon, 28 Mar 2011 18:02:41 +0200 Subject: [PATCH 34/58] Cleanup. --- macosx/coremidi/JackCoreMidiDriver.cpp | 4 ---- macosx/coremidi/JackCoreMidiOutputPort.cpp | 1 - 2 files changed, 5 deletions(-) diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index c7d8d07a..acdd5136 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -357,8 +357,6 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating " "physical input port: %s", e.what()); - //continue; - // SL : seems safer to fail here? goto destroy_internal_input_port; } pi_count++; @@ -393,8 +391,6 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } catch (std::exception e) { jack_error("JackCoreMidiDriver::Open - while creating " "physical output port: %s", e.what()); - //continue; - // SL : seems safer to fail here? goto destroy_internal_output_port; } po_count++; diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index 05e1786f..796d40bf 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -67,7 +67,6 @@ JackCoreMidiOutputPort::Execute() for (;;) { MIDIPacket *packet = MIDIPacketListInit(packet_list); assert(packet); - assert(thread_queue); if (! event) { event = GetCoreMidiEvent(true); } From 691694696132bac4da671a4c9e12c9ef798aeb51 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Mon, 28 Mar 2011 09:35:57 -0700 Subject: [PATCH 35/58] Use EAGAIN with 'sem_trywait' instead of ETIMEDOUT. --- macosx/coremidi/JackCoreMidiOutputPort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index c1761075..ced499e4 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -143,7 +143,7 @@ JackCoreMidiOutputPort::GetCoreMidiEvent(bool block) { if (! block) { if (sem_trywait(thread_queue_semaphore)) { - if (errno != ETIMEDOUT) { + if (errno != EAGAIN) { jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s", strerror(errno)); } From 6341e378598ef830714832c221bc7a045136bbec Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Mon, 28 Mar 2011 18:50:43 +0200 Subject: [PATCH 36/58] Correct some naming stuff. --- common/JackLoopbackDriver.h | 2 +- macosx/coremidi/JackCoreMidiPort.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/JackLoopbackDriver.h b/common/JackLoopbackDriver.h index 8fe22266..247f852e 100644 --- a/common/JackLoopbackDriver.h +++ b/common/JackLoopbackDriver.h @@ -44,7 +44,7 @@ class JackLoopbackDriver : public JackAudioDriver public: JackLoopbackDriver(JackLockedEngine* engine, JackSynchro* table) - : JackAudioDriver("loopback", "", engine, table) + : JackAudioDriver("loopback", "loopback", engine, table) {} virtual ~JackLoopbackDriver() {} diff --git a/macosx/coremidi/JackCoreMidiPort.cpp b/macosx/coremidi/JackCoreMidiPort.cpp index 82a92fa1..7b2fd6db 100644 --- a/macosx/coremidi/JackCoreMidiPort.cpp +++ b/macosx/coremidi/JackCoreMidiPort.cpp @@ -81,10 +81,10 @@ JackCoreMidiPort::Initialize(const char *alias_name, const char *client_name, "for endpoint name."); get_basic_alias: snprintf(alias, sizeof(alias) - 1, "%s:%s:%s%d", alias_name, - driver_name, is_output ? "out" : "in", num); + driver_name, is_output ? "in" : "out", num); } else { snprintf(alias, sizeof(alias) - 1, "%s:%s:%s%d", alias_name, - endpoint_name, is_output ? "out" : "in", num); + endpoint_name, is_output ? "in" : "out", num); } snprintf(name, sizeof(name) - 1, "%s:%s_%d", client_name, is_output ? "playback" : "capture", num); From f21223cf60078db56464c67760cf5bdcecb8ec75 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Mon, 28 Mar 2011 21:20:18 +0200 Subject: [PATCH 37/58] Correct MIDI threads priority. --- macosx/coremidi/JackCoreMidiOutputPort.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index ad333504..15a04f76 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -171,13 +171,12 @@ JackCoreMidiOutputPort::Init() { set_threaded_log_function(); - // OSX only... + // OSX only, values read in RT CoreMidi thread UInt64 period = 0; - UInt64 computation = 500 * 1000; + UInt64 computation = 250 * 1000; UInt64 constraint = 500 * 1000; thread->SetParams(period, computation, constraint); - // Use the server priority : y if (thread->AcquireSelfRealTime()) { jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime " "scheduling. Continuing anyway."); From c362b8b0bb29f3e15e9e8294be4091e993393868 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Tue, 29 Mar 2011 09:17:52 +0200 Subject: [PATCH 38/58] Add set_threaded_log_function in CoreMidi RT thread. --- macosx/coreaudio/JackCoreAudioDriver.cpp | 2 +- macosx/coremidi/JackCoreMidiInputPort.cpp | 2 ++ macosx/coremidi/JackCoreMidiPort.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/macosx/coreaudio/JackCoreAudioDriver.cpp b/macosx/coreaudio/JackCoreAudioDriver.cpp index 6fed74be..bf4e3195 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.cpp +++ b/macosx/coreaudio/JackCoreAudioDriver.cpp @@ -195,7 +195,7 @@ OSStatus JackCoreAudioDriver::Render(void *inRefCon, driver->fCurrentTime = (AudioTimeStamp *)inTimeStamp; driver->fDriverOutputData = ioData; - // Setup threadded based log function once... + // Setup threaded based log function et get RT thread parameters once... if (set_threaded_log_function()) { jack_log("set_threaded_log_function"); diff --git a/macosx/coremidi/JackCoreMidiInputPort.cpp b/macosx/coremidi/JackCoreMidiInputPort.cpp index 86fa9bf0..fb4bc961 100644 --- a/macosx/coremidi/JackCoreMidiInputPort.cpp +++ b/macosx/coremidi/JackCoreMidiInputPort.cpp @@ -66,6 +66,8 @@ JackCoreMidiInputPort::Initialize(const char *alias_name, void JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list) { + set_threaded_log_function(); + unsigned int packet_count = packet_list->numPackets; assert(packet_count); MIDIPacket *packet = (MIDIPacket *) packet_list->packet; diff --git a/macosx/coremidi/JackCoreMidiPort.h b/macosx/coremidi/JackCoreMidiPort.h index 8a5641bb..21055c31 100644 --- a/macosx/coremidi/JackCoreMidiPort.h +++ b/macosx/coremidi/JackCoreMidiPort.h @@ -45,7 +45,7 @@ namespace Jack { MIDIEndpointRef endpoint, bool is_output); double time_ratio; - MIDIEndpointRef endpoint; + MIDIEndpointRef endpoint; public: From 20831f45ea19094d2d12d4f00faba62d9e1169dd Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 29 Mar 2011 08:55:11 -0700 Subject: [PATCH 39/58] Overhaul WinMME driver to use MIDI queue system. WARNING: I don't have a Windows development environment, and haven't compiled/tested this code. --- windows/winmme/JackWinMMEDriver.cpp | 549 ++++++++++-------------- windows/winmme/JackWinMMEDriver.h | 77 ++-- windows/winmme/JackWinMMEInputPort.cpp | 287 +++++++++++++ windows/winmme/JackWinMMEInputPort.h | 89 ++++ windows/winmme/JackWinMMEOutputPort.cpp | 352 +++++++++++++++ windows/winmme/JackWinMMEOutputPort.h | 93 ++++ 6 files changed, 1069 insertions(+), 378 deletions(-) create mode 100644 windows/winmme/JackWinMMEInputPort.cpp create mode 100644 windows/winmme/JackWinMMEInputPort.h create mode 100644 windows/winmme/JackWinMMEOutputPort.cpp create mode 100644 windows/winmme/JackWinMMEOutputPort.h diff --git a/windows/winmme/JackWinMMEDriver.cpp b/windows/winmme/JackWinMMEDriver.cpp index d3635c64..455118a2 100644 --- a/windows/winmme/JackWinMMEDriver.cpp +++ b/windows/winmme/JackWinMMEDriver.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2009 Grame +Copyright (C) 2011 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,401 +18,285 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "JackWinMMEDriver.h" -#include "JackGraphManager.h" #include "JackEngineControl.h" -#include "JackDriverLoader.h" - -#include -#include -#include -#include - -#include -#include -#include +#include "JackWinMMEDriver.h" -namespace Jack -{ +using Jack::JackWinMMEDriver; -static bool InitHeaders(MidiSlot* slot) +JackWinMMEDriver::JackWinMMEDriver(const char *name, const char *alias, + JackLockedEngine *engine, + JackSynchro *table): + JackMidiDriver(name, alias, engine, table) { - slot->fHeader = (LPMIDIHDR)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT, sizeof(MIDIHDR) + kBuffSize); - if (!slot->fHeader) - return false; - - slot->fHeader->lpData = (LPSTR)((LPBYTE)slot->fHeader + sizeof(MIDIHDR)); - slot->fHeader->dwBufferLength = kBuffSize; - slot->fHeader->dwFlags = 0; - slot->fHeader->dwUser = 0; - slot->fHeader->lpNext = 0; - slot->fHeader->dwBytesRecorded = 0; - return true; + fCaptureChannels = 0; + fPlaybackChannels = 0; + input_ports = 0; + output_ports = 0; } -void CALLBACK JackWinMMEDriver::MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD userData, DWORD dwParam1, DWORD dwParam2) +JackWinMMEDriver::~JackWinMMEDriver() { - jack_ringbuffer_t* ringbuffer = (jack_ringbuffer_t*)userData; - //jack_info("JackWinMMEDriver::MidiInProc 0\n"); - - switch (wMsg) { - case MIM_OPEN: - break; - - case MIM_ERROR: - case MIM_DATA: { - - //jack_info("JackWinMMEDriver::MidiInProc"); - - // One event - unsigned int num_packet = 1; - jack_ringbuffer_write(ringbuffer, (char*)&num_packet, sizeof(unsigned int)); - - // Write event actual data - jack_ringbuffer_write(ringbuffer, (char*)&dwParam1, 3); - break; - } - - case MIM_LONGERROR: - case MIM_LONGDATA: - /* - Nothing for now - */ - break; - } + Stop(); + Close(); } -JackWinMMEDriver::JackWinMMEDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) - : JackMidiDriver(name, alias, engine, table), - fRealCaptureChannels(0), - fRealPlaybackChannels(0), - fMidiSource(NULL), - fMidiDestination(NULL) -{} - -JackWinMMEDriver::~JackWinMMEDriver() -{} - -int JackWinMMEDriver::Open(bool capturing, - bool playing, - int inchannels, - int outchannels, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency) +int +JackWinMMEDriver::Attach() { + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + jack_port_id_t index; + jack_nframes_t latency = buffer_size; + jack_latency_range_t latency_range; + const char *name; + JackPort *port; + latency_range.max = latency; + latency_range.min = latency; + + // Inputs + for (int i = 0; i < fCaptureChannels; i++) { + JackWinMMEInputPort *input_port = input_ports[i]; + name = input_port->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackWinMMEDriver::Attach - cannot register input port " + "with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(input_port->GetAlias()); + port->SetLatencyRange(JackCaptureLatency, &latency_range); + fCapturePortList[i] = index; + } - jack_log("JackWinMMEDriver::Open"); - - fRealCaptureChannels = midiInGetNumDevs(); - fRealPlaybackChannels = midiOutGetNumDevs(); - - // Generic JackMidiDriver Open - if (JackMidiDriver::Open(capturing, playing, fRealCaptureChannels, fRealPlaybackChannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency) != 0) - return -1; - - fMidiDestination = new MidiSlot[fRealCaptureChannels]; - assert(fMidiDestination); + if (! fEngineControl->fSyncMode) { + latency += buffer_size; + latency_range.max = latency; + latency_range.min = latency; + } - // Real input - int devindex = 0; - for (int i = 0; i < fRealCaptureChannels; i++) { + // Outputs + for (int i = 0; i < fPlaybackChannels; i++) { + JackWinMMEOutputPort *output_port = output_ports[i]; + name = output_port->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackWinMMEDriver::Attach - cannot register output " + "port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(output_port->GetAlias()); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); + fPlaybackPortList[i] = index; + } - HMIDIIN handle; - fMidiDestination[devindex].fIndex = i; - MMRESULT ret = midiInOpen(&handle, fMidiDestination[devindex].fIndex, (DWORD)MidiInProc, (DWORD)fRingBuffer[devindex], CALLBACK_FUNCTION); + return 0; +} - if (ret == MMSYSERR_NOERROR) { - fMidiDestination[devindex].fHandle = handle; - if (!InitHeaders(&fMidiDestination[devindex])) { - jack_error("memory allocation failed"); - midiInClose(handle); - continue; - } - ret = midiInPrepareHeader(handle, fMidiDestination[devindex].fHeader, sizeof(MIDIHDR)); - - if (ret == MMSYSERR_NOERROR) { - fMidiDestination[devindex].fHeader->dwUser = 1; - ret = midiInAddBuffer(handle, fMidiDestination[devindex].fHeader, sizeof(MIDIHDR)); - if (ret == MMSYSERR_NOERROR) { - ret = midiInStart(handle); - if (ret != MMSYSERR_NOERROR) { - jack_error("midiInStart error"); - CloseInput(&fMidiDestination[devindex]); - continue; - } - } else { - jack_error ("midiInAddBuffer error"); - CloseInput(&fMidiDestination[devindex]); - continue; - } - } else { - jack_error("midiInPrepareHeader error"); - midiInClose(handle); - continue; - } - } else { - jack_error ("midiInOpen error"); - continue; +int +JackWinMMEDriver::Close() +{ + int result = JackMidiDriver::Close(); + if (input_ports) { + for (int i = 0; i < fCaptureChannels; i++) { + delete input_ports[i]; } - devindex += 1; + delete[] input_ports; + input_ports = 0; } - fRealCaptureChannels = devindex; - fCaptureChannels = devindex; - - fMidiSource = new MidiSlot[fRealPlaybackChannels]; - assert(fMidiSource); - - // Real output - devindex = 0; - for (int i = 0; i < fRealPlaybackChannels; i++) { - MMRESULT res; - HMIDIOUT handle; - fMidiSource[devindex].fIndex = i; - UINT ret = midiOutOpen(&handle, fMidiSource[devindex].fIndex, 0L, 0L, CALLBACK_NULL); - if (ret == MMSYSERR_NOERROR) { - fMidiSource[devindex].fHandle = handle; - if (!InitHeaders(&fMidiSource[devindex])) { - jack_error("memory allocation failed"); - midiOutClose(handle); - continue; - } - res = midiOutPrepareHeader(handle, fMidiSource[devindex].fHeader, sizeof(MIDIHDR)); - if (res != MMSYSERR_NOERROR) { - jack_error("midiOutPrepareHeader error %d %d %d", i, handle, res); - continue; - } else { - fMidiSource[devindex].fHeader->dwUser = 1; - } - } else { - jack_error("midiOutOpen error"); - continue; + if (output_ports) { + for (int i = 0; i < fPlaybackChannels; i++) { + delete output_ports[i]; } - devindex += 1; + delete[] output_ports; + output_ports = 0; } - fRealPlaybackChannels = devindex; - fPlaybackChannels = devindex; - return 0; + return result; } -void JackWinMMEDriver::CloseInput(MidiSlot* slot) +int +JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels, + int out_channels, bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) { - MMRESULT res; - int retry = 0; - - if (slot->fHandle == 0) - return; - - HMIDIIN handle = (HMIDIIN)slot->fHandle; - slot->fHeader->dwUser = 0; - res = midiInStop(handle); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInStop error"); + const char *client_name = fClientControl.fName; + int input_count = 0; + int num_potential_inputs = midiInGetNumDevs(); + int num_potential_outputs = midiOutGetNumDevs(); + int output_count = 0; + if (num_potential_inputs) { + try { + input_ports = new JackWinMMEInputPort *[num_potential_inputs]; + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating input port " + "array: %s", e.what()); + return -1; + } + for (int i = 0; i < num_potential_inputs; i++) { + try { + input_ports[input_count] = + new JackWinMMEInputPort(fAliasName, client_name, + capture_driver_name, i); + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating input " + "port: %s", e.what()); + continue; + } + input_count++; + } } - res = midiInReset(handle); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInReset error"); + if (num_potential_outputs) { + try { + output_ports = new JackWinMMEOutputPort *[num_potential_outputs]; + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating output port " + "array: %s", e.what()); + goto destroy_input_ports; + } + for (int i = 0; i < num_potential_outputs; i++) { + try { + output_ports[output_count] = + new JackWinMMEOutputPort(fAliasName, client_name, + playback_driver_name, i); + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating output " + "port: %s", e.what()); + continue; + } + output_count++; + } } - res = midiInUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInUnprepareHeader error"); + if (! (input_count || output_count)) { + jack_error("JackWinMMEDriver::Open - no WinMME inputs or outputs " + "allocated."); + } else if (! JackMidiDriver::Open(capturing, playing, input_count, + output_count, monitor, + capture_driver_name, + playback_driver_name, capture_latency, + playback_latency)) { + return 0; } - do { - res = midiInClose(handle); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInClose error"); + + destroy_input_ports: + if (input_ports) { + for (int i = 0; i < input_count; i++) { + delete input_ports[i]; } - if (res == MIDIERR_STILLPLAYING) - midiInReset(handle); - Sleep (10); - retry++; - } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); - - if (slot->fHeader) { - GlobalFreePtr(slot->fHeader); + delete[] input_ports; + input_ports = 0; } + return -1; } -void JackWinMMEDriver::CloseOutput(MidiSlot* slot) +int +JackWinMMEDriver::Read() { - MMRESULT res; - int retry = 0; - - if (slot->fHandle == 0) - return; - - HMIDIOUT handle = (HMIDIOUT)slot->fHandle; - res = midiOutReset(handle); - if (res != MMSYSERR_NOERROR) - jack_error("midiOutReset error"); - midiOutUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); - do { - res = midiOutClose(handle); - if (res != MMSYSERR_NOERROR) - jack_error("midiOutClose error"); - Sleep(10); - retry++; - } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); - - if (slot->fHeader) { - GlobalFreePtr(slot->fHeader); + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < fCaptureChannels; i++) { + input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); } + return 0; } -int JackWinMMEDriver::Close() +int +JackWinMMEDriver::Start() { - jack_log("JackWinMMEDriver::Close"); + jack_info("JackWinMMEDriver::Start - Starting driver."); + + JackMidiDriver::Start(); + + int input_count = 0; + int output_count = 0; - // Generic midi driver close - int res = JackMidiDriver::Close(); + jack_info("JackWinMMEDriver::Start - Enabling input ports."); - // Close input - if (fMidiDestination) { - for (int i = 0; i < fRealCaptureChannels; i++) { - CloseInput(&fMidiDestination[i]); + for (; input_count < fCaptureChannels; input_count++) { + if (input_ports[input_count]->Start() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to enable input " + "port."); + goto stop_input_ports; } - delete[] fMidiDestination; } - // Close output - if (fMidiSource) { - for (int i = 0; i < fRealPlaybackChannels; i++) { - CloseOutput(&fMidiSource[i]); + jack_info("JackWinMMEDriver::Start - Enabling output ports."); + + for (; output_count < fPlaybackChannels; output_count++) { + if (output_ports[output_count]->Start() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to enable output " + "port."); + goto stop_output_ports; } - delete[] fMidiSource; } - return res; -} + jack_info("JackWinMMEDriver::Start - Driver started."); -int JackWinMMEDriver::Attach() -{ - JackPort* port; - jack_port_id_t port_index; - char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - MMRESULT res; - int i; - - jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); - - for (i = 0; i < fCaptureChannels; i++) { - MIDIINCAPS caps; - res = midiInGetDevCaps(fMidiDestination[i].fIndex, &caps, sizeof(caps)); - if (res == MMSYSERR_NOERROR) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, caps.szPname, i + 1); - } else { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); - } - snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); - - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { - jack_error("driver: cannot register port for %s", name); - return -1; + return 0; + + stop_output_ports: + for (int i = 0; i < output_count; i++) { + if (output_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to disable output " + "port."); } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fCapturePortList[i] = port_index; - jack_log("JackMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } - - for (i = 0; i < fPlaybackChannels; i++) { - MIDIOUTCAPS caps; - res = midiOutGetDevCaps(fMidiSource[i].fIndex, &caps, sizeof(caps)); - if (res == MMSYSERR_NOERROR) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, caps.szPname, i + 1); - } else { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fPlaybackDriverName, i + 1); - } - snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); - - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { - jack_error("driver: cannot register port for %s", name); - return -1; + stop_input_ports: + for (int i = 0; i < input_count; i++) { + if (input_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to disable input " + "port."); } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fPlaybackPortList[i] = port_index; - jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } - return 0; + return -1; } -int JackWinMMEDriver::Read() +int +JackWinMMEDriver::Stop() { - size_t size; + int result = 0; - for (int chan = 0; chan < fCaptureChannels; chan++) { + jack_info("JackWinMMEDriver::Stop - disabling input ports."); - if (fGraphManager->GetConnectionsNum(fCapturePortList[chan]) > 0) { - - JackMidiBuffer* midi_buffer = GetInputBuffer(chan); - - if (jack_ringbuffer_read_space (fRingBuffer[chan]) == 0) { - // Reset buffer - midi_buffer->Reset(midi_buffer->nframes); - } else { - - while ((size = jack_ringbuffer_read_space (fRingBuffer[chan])) > 0) { + for (int i = 0; i < fCaptureChannels; i++) { + if (input_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Stop - Failed to disable input " + "port."); + result = -1; + } + } - //jack_info("jack_ringbuffer_read_space %d", size); - int ev_count = 0; - jack_ringbuffer_read(fRingBuffer[chan], (char*)&ev_count, sizeof(int)); + jack_info("JackWinMMEDriver::Stop - disabling output ports."); - if (ev_count > 0) { - for (int j = 0; j < ev_count; j++) { - unsigned int event_len = 3; - // Read event actual data - jack_midi_data_t* dest = midi_buffer->ReserveEvent(0, event_len); - jack_ringbuffer_read(fRingBuffer[chan], (char*)dest, event_len); - } - } - } - } - } else { - //jack_info("Consume ring buffer"); - jack_ringbuffer_read_advance(fRingBuffer[chan], jack_ringbuffer_read_space(fRingBuffer[chan])); + for (int i = 0; i < fPlaybackChannels; i++) { + if (output_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Stop - Failed to disable output " + "port."); + result = -1; } } - return 0; + + return result; } -int JackWinMMEDriver::Write() +int +JackWinMMEDriver::Write() { - for (int chan = 0; chan < fPlaybackChannels; chan++) { - - if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chan]) > 0) { - - JackMidiBuffer* midi_buffer = GetOutputBuffer(chan); - - // TODO : use timestamp - - for (unsigned int j = 0; j < midi_buffer->event_count; j++) { - JackMidiEvent* ev = &midi_buffer->events[j]; - if (ev->size <= 3) { - jack_midi_data_t *d = ev->GetData(midi_buffer); - DWORD winev = 0; - if (ev->size > 0) winev |= d[0]; - if (ev->size > 1) winev |= (d[1] << 8); - if (ev->size > 2) winev |= (d[2] << 16); - MMRESULT res = midiOutShortMsg((HMIDIOUT)fMidiSource[chan].fHandle, winev); - if (res != MMSYSERR_NOERROR) - jack_error ("midiOutShortMsg error res %d", res); - } else { - - } - } - } + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < fPlaybackChannels; i++) { + output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size); } - return 0; } -} // end of namespace - #ifdef __cplusplus extern "C" { diff --git a/windows/winmme/JackWinMMEDriver.h b/windows/winmme/JackWinMMEDriver.h index 6c28a0ef..52ae5f2b 100644 --- a/windows/winmme/JackWinMMEDriver.h +++ b/windows/winmme/JackWinMMEDriver.h @@ -1,5 +1,6 @@ /* Copyright (C) 2009 Grame +Copyright (C) 2011 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,67 +22,51 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #define __JackWinMMEDriver__ #include "JackMidiDriver.h" -#include "JackTime.h" +#include "JackWinMMEInputPort.h" +#include "JackWinMMEOutputPort.h" -namespace Jack -{ +namespace Jack { -/*! -\brief The WinMME driver. -*/ - -#define kBuffSize 512 - -struct MidiSlot { - - LPVOID fHandle; // MMSystem handler - short fIndex; // MMSystem dev index - LPMIDIHDR fHeader; // for long msg output - - MidiSlot():fHandle(0),fIndex(0) - {} + class JackWinMMEDriver : public JackMidiDriver { -}; + private: -class JackWinMMEDriver : public JackMidiDriver -{ + JackWinMMEInputPort **input_ports; + JackWinMMEOutputPort **output_ports; - private: + public: - int fRealCaptureChannels; - int fRealPlaybackChannels; + JackWinMMEDriver(const char* name, const char* alias, + JackLockedEngine* engine, JackSynchro* table); - MidiSlot* fMidiSource; - MidiSlot* fMidiDestination; + ~JackWinMMEDriver(); - void CloseInput(MidiSlot* slot); - void CloseOutput(MidiSlot* slot); + int + Attach(); - static void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); + int + Close(); - public: + int + Open(bool capturing, bool playing, int num_inputs, int num_outputs, + bool monitor, const char* capture_driver_name, + const char* playback_driver_name, jack_nframes_t capture_latency, + jack_nframes_t playback_latency); - JackWinMMEDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); - virtual ~JackWinMMEDriver(); + int + Read(); - int Open(bool capturing, - bool playing, - int chan_in, - int chan_out, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency); - int Close(); + int + Start(); - int Attach(); + int + Stop(); - int Read(); - int Write(); + int + Write(); -}; + }; -} // end of namespace +} #endif diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp new file mode 100644 index 00000000..f5c74023 --- /dev/null +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -0,0 +1,287 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include + +#include "JackError.h" +#include "JackMidiUtil.h" +#include "JackWinMMEInputPort.h" + +using Jack::JackWinMMEInputPort; + +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +void CALLBACK +JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle, UINT message, + DWORD port, DWORD param1, + DWORD param2) +{ + ((JackWinMMEInputPort *) port)->ProcessWinMME(message, param1, param2); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, + const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes, size_t max_messages) +{ + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); + write_queue = new JackMidiBufferWriteQueue(); + std::auto_ptr write_queue_ptr(write_queue); + sysex_buffer = new jack_midi_data_t[max_bytes]; + char error_message[MAXERRORLENGTH]; + MMRESULT result = midiInOpen(&handle, index, HandleMidiInputEvent, this, + CALLBACK_FUNCTION); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto delete_sysex_buffer; + } + sysex_header.dwBufferLength = max_bytes; + sysex_header.dwBytesRecorded = 0; + sysex_header.dwFlags = 0; + sysex_header.dwUser = 0; + sysex_header.lpData = (((LPBYTE) sysex_header) + sizeof(MIDIHDR)); + sysex_header.lpNext = 0; + result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto close_handle; + } + result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto unprepare_header; + } + + jack_event = 0; + started = false; + write_queue_ptr.release(); + thread_queue_ptr.release(); + return; + + unprepare_header: + result = midiInUnprepareHeader(handle, sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [constructor]", + "midiInUnprepareHeader", result); + } + close_handle: + result = midiInClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [constructor]", "midiInClose", result); + } + delete_sysex_buffer: + delete[] sysex_buffer; + throw std::runtime_error(error_message); +} + +JackWinMMEInputPort::~JackWinMMEInputPort() +{ + Stop(); + MMRESULT result = midiInReset(handle); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [destructor]", "midiInReset", result); + } + result = midiInUnprepareHeader(handle, sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [destructor]", "midiInUnprepareHeader", + result); + } + result = midiInClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [destructor]", "midiInClose", result); + } + delete[] sysex_buffer; + delete thread_queue; + delete write_queue; +} + +void +JackWinMMEInputPort::EnqueueMessage(jack_nframes_t time, size_t length, + jack_midi_data_t *data) +{ + switch (thread_queue->EnqueueEvent(time, length, data)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " + "cannot currently accept a %d-byte event. Dropping event.", + size); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " + "buffer is too small to enqueue a %d-byte event. Dropping " + "event.", size); + break; + default: + ; + } +} + +const char * +JackWinMMEInputPort::GetAlias() +{ + return alias; +} + +void +JackWinMMEInputPort::GetErrorString(MMRESULT error, LPTSTR text) +{ + MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); + if (result != MMSYSERR_NOERROR) { + snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); + } +} + +const char * +JackWinMMEInputPort::GetName() +{ + return name; +} + +void +JackWinMMEInputPort::ProcessJack() +{ + write_queue->ResetMidiBuffer(port_buffer, frames); + if (! jack_event) { + jack_event = thread_queue->DequeueEvent(); + } + for (; jack_event; jack_event = thread_queue->DequeueEvent()) { + switch (write_queue->EnqueueEvent(event)) { + case BUFFER_TOO_SMALL: + jack_error("JackWinMMEMidiInputPort::Process - The buffer write " + "queue couldn't enqueue a %d-byte event. Dropping " + "event.", event->size); + // Fallthrough on purpose + case OK: + continue; + } + break; + } +} + +void +JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) +{ + jack_nframes_t current_frame = GetCurrentFrame(); + switch (message) { + case MIM_CLOSE: + jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed."); + break; + case MIM_MOREDATA: + jack_info("JackWinMMEInputPort::ProcessWinMME - The MIDI input device " + "driver thinks that JACK is not processing messages fast " + "enough."); + // Fallthrough on purpose. + case MIM_DATA: + jack_midi_data_t message_buffer[3]; + jack_midi_data_t status = param1 & 0xff; + int length = GetMessageLength(status); + switch (length) { + case 3: + message_buffer[2] = param1 & 0xff0000; + // Fallthrough on purpose. + case 2: + message_buffer[1] = param1 & 0xff00; + // Fallthrough on purpose. + case 1: + message_buffer[0] = status; + break; + case 0: + jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI " + "input driver sent an MIM_DATA message with a sysex " + "status byte."); + return; + case -1: + jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI " + "input driver sent an MIM_DATA message with an invalid " + "status byte."); + return; + } + EnqueueMessage(current_frame, (size_t) length, message_buffer); + break; + case MIM_LONGDATA: + LPMIDIHDR header = (LPMIDIHDR) dwParam1; + jack_midi_data_t *data = (jack_midi_data_t *) header->lpData; + size_t length = header->dwBytesRecorded; + if ((data[0] != 0xf0) || (data[length - 1] != 0xf7)) { + jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding " + "%d-byte sysex chunk.", length); + } else { + EnqueueMessage(current_frame, length, data); + } + // Is this realtime-safe? This function isn't run in the JACK thread, + // but we still want it to perform as quickly as possible. Even if + // this isn't realtime safe, it may not be avoidable. + MMRESULT result = midiInAddBuffer(handle, &sysex_header, + sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort::ProcessWinMME", "midiInAddBuffer", + result); + } + break; + case MIM_LONGERROR: + jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or " + "incomplete sysex message received."); + break; + case MIM_OPEN: + jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device opened."); + } +} + +bool +JackWinMMEInputPort::Start() +{ + if (! started) { + MMRESULT result = midiInStart(handle); + started = result == MMSYSERR_NOERROR; + if (! started) { + WriteError("JackWinMMEInputPort::Start", "midiInStart", result); + } + } + return started; +} + +bool +JackWinMMEInputPort::Stop() +{ + if (started) { + MMRESULT result = midiInStop(handle); + started = result != MMSYSERR_NOERROR; + if (started) { + WriteError("JackWinMMEInputPort::Stop", "midiInStop", result); + } + } + return ! started; +} + +void +JackWinMMEInputPort::WriteError(const char *jack_func, const char *mm_func, + MMRESULT result) +{ + const char error_message[MAXERRORLENGTH]; + GetErrorString(result, error_message); + jack_error("%s - %s: %s", jack_func, mm_func, error_message); +} diff --git a/windows/winmme/JackWinMMEInputPort.h b/windows/winmme/JackWinMMEInputPort.h new file mode 100644 index 00000000..5ce042b9 --- /dev/null +++ b/windows/winmme/JackWinMMEInputPort.h @@ -0,0 +1,89 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackWinMMEInputPort__ +#define __JackWinMMEInputPort__ + +#include + +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferWriteQueue.h" + +namespace Jack { + + class JackWinMMEInputPort { + + private: + + static void CALLBACK + HandleMidiInputEvent(HMIDIIN handle, UINT message, DWORD port, + DWORD param1, DWORD param2); + + void + EnqueueMessage(jack_nframes_t time, size_t length, + jack_midi_data_t *data); + + void + GetErrorString(MMRESULT error, LPTSTR text); + + void + ProcessWinMME(UINT message, DWORD param1, DWORD param2); + + void + WriteError(const char *jack_func, const char *mm_func, + MMRESULT result); + + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + HMIDIIN handle; + jack_midi_event_t *jack_event; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + bool started; + jack_midi_data_t *sysex_buffer; + MIDIHDR sysex_header + JackMidiAsyncQueue *thread_queue; + JackMidiBufferWriteQueue *write_queue; + + public: + + JackWinMMEInputPort(const char *alias_name, const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes=4096, size_t max_messages=1024); + + ~JackWinMMEInputPort(); + + const char * + GetAlias(); + + const char * + GetName(); + + void + ProcessJack(); + + bool + Start(); + + bool + Stop(); + + }; + +} + +#endif diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp new file mode 100644 index 00000000..8f25feaf --- /dev/null +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -0,0 +1,352 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackMidiUtil.h" +#include "JackTime.h" +#include "JackWinMMEOutputPort.h" + +using Jack::JackWinMMEOutputPort; + +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +void CALLBACK +JackWinMMEOutputPort::HandleMessageEvent(HMIDIOUT handle, UINT message, + DWORD_PTR port, DWORD_PTR param1, + DWORD_PTR param2) +{ + ((JackWinMMEOutputPort *) port)->HandleMessage(message, param1, param2); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, + const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes, + size_t max_messages) +{ + read_queue = new JackMidiBufferReadQueue(); + std::auto_ptr read_queue_ptr(read_queue); + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); + char error_message[MAXERRORLENGTH]; + MMRESULT result = midiOutOpen(&handle, index, HandleMessageEvent, this, + CALLBACK_FUNCTION); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto raise_exception; + } + thread_queue_semaphore = CreateSemaphore(NULL, 0, max_messages, NULL); + if (thread_queue_semaphore == NULL) { + GetOSErrorString(error_message); + goto close_handle; + } + sysex_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + if (sysex_semaphore == NULL) { + GetOSErrorString(error_message); + goto destroy_thread_queue_semaphore; + } + MIDIINCAPS capabilities; + char *name; + result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", + result); + name = driver_name; + } else { + name = capabilities.szPname; + } + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, driver_name, + index + 1); + snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1); + write_queue_ptr.release(); + thread_queue_ptr.release(); + return; + + destroy_thread_queue_semaphore: + if (! CloseHandle(thread_queue_semaphore)) { + WriteOSError("JackWinMMEOutputPort [constructor]", "CloseHandle"); + } + close_handle: + result = midiOutClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutClose", + result); + } + raise_exception: + throw std::runtime_error(error_message); +} + +JackWinMMEOutputPort::~JackWinMMEOutputPort() +{ + Stop(); + MMRESULT result = midiOutReset(handle); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [destructor]", "midiOutReset", + result); + } + result = midiOutClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [destructor]", "midiOutClose", + result); + } + if (! CloseHandle(sysex_semaphore)) { + WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle"); + } + if (! CloseHandle(thread_queue_semaphore)) { + WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle"); + } + delete read_queue; + delete thread_queue; +} + +bool +JackWinMMEOutputPort::Execute() +{ + for (;;) { + if (! Wait(thread_queue_semaphore)) { + break; + } + jack_midi_event_t *event = thread_queue->DequeueEvent(); + if (! event) { + break; + } + jack_time_t frame_time = GetTimeFromFrames(event->time); + for (jack_time_t current_time = GetMicroSeconds(); + frame_time > current_time; current_time = GetMicroSeconds()) { + jack_time_t sleep_time = frame_time - current_time; + + // Windows has a millisecond sleep resolution for its Sleep calls. + // This is unfortunate, as MIDI timing often requires a higher + // resolution. For now, we attempt to compensate by letting an + // event be sent if we're less than 500 microseconds from sending + // the event. We assume that it's better to let an event go out + // 499 microseconds early than let an event go out 501 microseconds + // late. Of course, that's assuming optimal sleep times, which is + // a whole different Windows issue ... + if (sleep_time < 500) { + break; + } + + if (sleep_time < 1000) { + sleep_time = 1000; + } + JackSleep(sleep_time); + } + jack_midi_data_t *data = event->buffer; + DWORD message = 0; + MMRESULT result; + size_t size = event->size; + switch (size) { + case 3: + message |= (((DWORD) data[2]) << 16); + // Fallthrough on purpose. + case 2: + message |= (((DWORD) data[1]) << 8); + // Fallthrough on purpose. + case 1: + message |= (DWORD) data[0]; + result = midiOutShortMsg(handle, message); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", + "midiOutShortMsg", result); + } + continue; + } + MIDIHDR header; + header.dwFlags = 0; + header.dwLength = size; + header.lpData = data; + result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", + "midiOutPrepareHeader", result); + continue; + } + result = midiOutLongMsg(handle, &header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", "midiOutLongMsg", + result); + continue; + } + + // System exclusive messages may be sent synchronously or + // asynchronously. The choice is up to the WinMME driver. So, we wait + // until the message is sent, regardless of the driver's choice. + if (! Wait(sysex_semaphore)) { + break; + } + + result = midiOutUnprepareHeader(handle, &header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", + "midiOutUnprepareHeader", result); + break; + } + } + stop_execution: + return false; +} + +void +JackWinMMEOutputPort::GetMMErrorString(MMRESULT error, LPTSTR text) +{ + MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH); + if (result != MMSYSERR_NOERROR) { + snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error); + } +} + +void +JackWinMMEOutputPort::GetOSErrorString(LPTSTR text) +{ + DWORD error = GetLastError(); + if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &text, + MAXERRORLENGTH, NULL)) { + snprintf(text, MAXERRORLENGTH, "Unknown OS error code '%d'", error); + } +} + +void +JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, + DWORD_PTR param2) +{ + switch (message) { + case MOM_CLOSE: + jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device closed."); + break; + case MOM_DONE: + if (! ReleaseSemaphore(sysex_semaphore, 1, NULL)) { + WriteOSError("JackWinMMEOutputPort::HandleMessage", + "ReleaseSemaphore"); + } + break; + case MOM_OPEN: + jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened."); + } +} + +bool +JackWinMMEOutputPort::Init() +{ + set_threaded_log_function(); + // XX: Can more be done? Ideally, this thread should have the JACK server + // thread priority + 1. + if (thread->AcquireSelfRealTime()) { + jack_error("JackWinMMEOutputPort::Init - could not acquire realtime " + "scheduling. Continuing anyway."); + } + return true; +} + +void +JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) +{ + read_queue->ResetMidiBuffer(port_buffer); + for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; + event = read_queue->DequeueEvent()) { + switch (thread_queue->EnqueueEvent(event, frames)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue " + "buffer is full. Dropping event."); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue " + "couldn't enqueue a %d-byte event. Dropping event.", + event->size); + break; + default: + if (! ReleaseSemaphore(thread_queue_semaphore, 1, NULL)) { + WriteOSError("JackWinMMEOutputPort::ProcessJack", + "ReleaseSemaphore"); + } + } + } +} + +bool +JackWinMMEOutputPort::Start() +{ + bool result = thread->GetStatus() != JackThread::kIdle; + if (! result) { + result = ! thread->StartSync(); + if (! result) { + jack_error("JackWinMMEOutputPort::Start - failed to start MIDI " + "processing thread."); + } + } + return result; +} + +bool +JackWinMMEOutputPort::Stop() +{ + bool result = thread->GetStatus() == JackThread::kIdle; + if (! result) { + result = ! thread->Kill(); + if (! result) { + jack_error("JackWinMMEOutputPort::Stop - failed to stop MIDI " + "processing thread."); + } + } + return result; +} + +bool +JackWinMMEOutputPort::Wait(Handle semaphore) +{ + DWORD result = WaitForSingleObject(semaphore, INFINITE); + switch (result) { + case WAIT_FAILED: + WriteOSError("JackWinMMEOutputPort::Wait", "WaitForSingleObject"); + break; + case WAIT_OBJECT_0: + return true; + default: + jack_error("JackWinMMEOutputPort::Wait - unexpected result from " + "'WaitForSingleObject'."); + } + return false; +} + +void +JackWinMMEOutputPort::WriteMMError(const char *jack_func, const char *mm_func, + MMRESULT result) +{ + const char error_message[MAXERRORLENGTH]; + GetMMErrorString(result, error_message); + jack_error("%s - %s: %s", jack_func, mm_func, error_message); +} + +void +JackWinMMEOutputPort::WriteOSError(const char *jack_func, const char *os_func) +{ + const char error_message[MAXERRORLENGTH]; + GetOSErrorString(result, error_message); + jack_error("%s - %s: %s", jack_func, os_func, error_message); +} diff --git a/windows/winmme/JackWinMMEOutputPort.h b/windows/winmme/JackWinMMEOutputPort.h new file mode 100644 index 00000000..8da32066 --- /dev/null +++ b/windows/winmme/JackWinMMEOutputPort.h @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackWinMMEOutputPort__ +#define __JackWinMMEOutputPort__ + +#include +#include + +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferReadQueue.h" + +namespace Jack { + + class JackWinMMEOutputPort { + + private: + + static void CALLBACK + HandleMessageEvent(HMIDIOUT handle, UINT message, DWORD_PTR port, + DWORD_PTR param1, DWORD_PTR param2); + + void + GetMMErrorString(MMRESULT error, LPTSTR text); + + void + GetOSErrorString(LPTSTR text); + + void + HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2); + + bool + Wait(Handle semaphore); + + void + WriteMMError(const char *jack_func, const char *mm_func, + MMRESULT result); + + void + WriteOSError(const char *jack_func, const char *os_func); + + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + HMIDIOUT handle; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + JackMidiBufferReadQueue *read_queue; + HANDLE sysex_semaphore; + JackMidiAsyncQueue *thread_queue; + HANDLE thread_queue_semaphore; + + public: + + JackWinMMEOutputPort(const char *alias_name, const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes=4096, size_t max_messages=1024); + + ~JackWinMMEOutputPort(); + + bool + Execute(); + + bool + Init(); + + void + ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); + + bool + Start(); + + bool + Stop(); + + }; + +} + +#endif From 8c7cabfe3dd13df72a2ed6fc1f39f606d3c1b64d Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 29 Mar 2011 09:41:28 -0700 Subject: [PATCH 40/58] Add missing thread functionality to JackWinMMEOutputPort. --- windows/winmme/JackWinMMEOutputPort.cpp | 3 +++ windows/winmme/JackWinMMEOutputPort.h | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index 8f25feaf..7c6789f8 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -52,6 +52,8 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, std::auto_ptr read_queue_ptr(read_queue); thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); std::auto_ptr thread_queue_ptr(thread_queue); + thread = new JackThread(this); + std::auto_ptr thread_ptr(thread); char error_message[MAXERRORLENGTH]; MMRESULT result = midiOutOpen(&handle, index, HandleMessageEvent, this, CALLBACK_FUNCTION); @@ -82,6 +84,7 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, driver_name, index + 1); snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1); + thread_ptr.release(); write_queue_ptr.release(); thread_queue_ptr.release(); return; diff --git a/windows/winmme/JackWinMMEOutputPort.h b/windows/winmme/JackWinMMEOutputPort.h index 8da32066..ef1bd65d 100644 --- a/windows/winmme/JackWinMMEOutputPort.h +++ b/windows/winmme/JackWinMMEOutputPort.h @@ -25,10 +25,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackMidiAsyncQueue.h" #include "JackMidiBufferReadQueue.h" +#include "JackThread.h" namespace Jack { - class JackWinMMEOutputPort { + class JackWinMMEOutputPort: public JackRunnableInterface { private: @@ -60,6 +61,7 @@ namespace Jack { char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; JackMidiBufferReadQueue *read_queue; HANDLE sysex_semaphore; + JackThread *thread; JackMidiAsyncQueue *thread_queue; HANDLE thread_queue_semaphore; From 194ce1cd57d30a92669159fd5bf59fcd9a900cd0 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 29 Mar 2011 11:15:39 -0700 Subject: [PATCH 41/58] Make sure threaded log functions are set. Add MOM_POSITIONCB info. --- windows/winmme/JackWinMMEInputPort.cpp | 3 ++- windows/winmme/JackWinMMEOutputPort.cpp | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp index f5c74023..c9b9cbb1 100644 --- a/windows/winmme/JackWinMMEInputPort.cpp +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -55,7 +55,7 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, sysex_buffer = new jack_midi_data_t[max_bytes]; char error_message[MAXERRORLENGTH]; MMRESULT result = midiInOpen(&handle, index, HandleMidiInputEvent, this, - CALLBACK_FUNCTION); + CALLBACK_FUNCTION | MIDI_IO_STATUS); if (result != MMSYSERR_NOERROR) { GetErrorString(result, error_message); goto delete_sysex_buffer; @@ -185,6 +185,7 @@ JackWinMMEInputPort::ProcessJack() void JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) { + set_threaded_log_function(); jack_nframes_t current_frame = GetCurrentFrame(); switch (message) { case MIM_CLOSE: diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index 7c6789f8..3e615742 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -180,8 +180,11 @@ JackWinMMEOutputPort::Execute() continue; } MIDIHDR header; + header.dwBufferLength = size; + header.dwBytesRecorded = size; header.dwFlags = 0; - header.dwLength = size; + header.dwOffset = 0; + header.dwUser = 0; header.lpData = data; result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { @@ -238,6 +241,7 @@ void JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2) { + set_threaded_log_function(); switch (message) { case MOM_CLOSE: jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device closed."); @@ -250,6 +254,12 @@ JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, break; case MOM_OPEN: jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened."); + break; + case MOM_POSITIONCB: + LPMIDIHDR header = (LPMIDIHDR) param2; + jack_info("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d " + "bytes of the current sysex message have been sent.", + header->dwOffset, header->dwBytesRecorded); } } From 8e4ee25c7f9e6a8a18b25fbe6e9c5c2f43c40a93 Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 29 Mar 2011 11:27:21 -0700 Subject: [PATCH 42/58] Make thread termination more elegant. --- windows/winmme/JackWinMMEOutputPort.cpp | 52 +++++++++++++++++-------- windows/winmme/JackWinMMEOutputPort.h | 3 ++ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index 3e615742..aa87de0f 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -247,10 +247,7 @@ JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device closed."); break; case MOM_DONE: - if (! ReleaseSemaphore(sysex_semaphore, 1, NULL)) { - WriteOSError("JackWinMMEOutputPort::HandleMessage", - "ReleaseSemaphore"); - } + Signal(sysex_semaphore); break; case MOM_OPEN: jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened."); @@ -294,14 +291,21 @@ JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer, event->size); break; default: - if (! ReleaseSemaphore(thread_queue_semaphore, 1, NULL)) { - WriteOSError("JackWinMMEOutputPort::ProcessJack", - "ReleaseSemaphore"); - } + Signal(thread_queue_semaphore); } } } +bool +JackWinMMEOutputPort::Signal(Handle semaphore) +{ + bool result = (bool) ReleaseSemaphore(semaphore, 1, NULL); + if (! result) { + WriteOSError("JackWinMMEOutputPort::Signal", "ReleaseSemaphore"); + } + return result; +} + bool JackWinMMEOutputPort::Start() { @@ -319,15 +323,31 @@ JackWinMMEOutputPort::Start() bool JackWinMMEOutputPort::Stop() { - bool result = thread->GetStatus() == JackThread::kIdle; - if (! result) { - result = ! thread->Kill(); - if (! result) { - jack_error("JackWinMMEOutputPort::Stop - failed to stop MIDI " - "processing thread."); - } + + jack_info("JackWinMMEOutputPort::Stop - stopping MIDI output port " + "processing thread."); + + int result; + const char *verb; + switch (thread->GetStatus()) { + case JackThread::kIniting: + case JackThread::kStarting: + result = thread->Kill(); + verb = "kill"; + break; + case JackThread::kRunning: + Signal(thread_queue_semaphore); + result = thread->Stop(); + verb = "stop"; + break; + default: + return true; } - return result; + if (result) { + jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing " + "thread.", verb); + } + return ! result; } bool diff --git a/windows/winmme/JackWinMMEOutputPort.h b/windows/winmme/JackWinMMEOutputPort.h index ef1bd65d..1033ec3e 100644 --- a/windows/winmme/JackWinMMEOutputPort.h +++ b/windows/winmme/JackWinMMEOutputPort.h @@ -46,6 +46,9 @@ namespace Jack { void HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2); + bool + Signal(Handle semaphore); + bool Wait(Handle semaphore); From 66200176dc62008e87f69fd7c2837840b351589a Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 29 Mar 2011 12:24:26 -0700 Subject: [PATCH 43/58] Add support for advance scheduling via driver preferences. Hope this doesn't break anything. --- macosx/coremidi/JackCoreMidiOutputPort.cpp | 25 +++++++++---------- macosx/coremidi/JackCoreMidiOutputPort.h | 3 ++- .../JackCoreMidiPhysicalOutputPort.cpp | 15 ++++++++++- .../JackCoreMidiVirtualOutputPort.cpp | 2 +- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index 15a04f76..48058c92 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -44,6 +44,7 @@ JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, if (thread_queue_semaphore == (sem_t *) SEM_FAILED) { throw std::runtime_error(strerror(errno)); } + advance_schedule_time = 0; thread_ptr.release(); thread_queue_ptr.release(); read_queue_ptr.release(); @@ -71,20 +72,15 @@ JackCoreMidiOutputPort::Execute() event = GetCoreMidiEvent(true); } jack_midi_data_t *data = event->buffer; - - // This is the latest time that the packet list can be sent out. We - // may want to consider subtracting some frames to leave room for the - // CoreMIDI driver/client to handle all of the events. There's a - // property called 'kMIDIPropertyAdvanceScheduleTimeMuSec' that might - // be useful in this case. - jack_nframes_t send_time = event->time; - + jack_nframes_t send_frame = event->time; + jack_time_t send_time = + GetTimeFromFrames(send_frame) - advance_schedule_time; size_t size = event->size; - MIDITimeStamp timestamp = GetTimeStampFromFrames(send_time); + MIDITimeStamp timestamp = GetTimeStampFromFrames(send_frame); packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet, timestamp, size, data); if (packet) { - while (GetCurrentFrame() < send_time) { + while (GetMicroSeconds() < send_time) { event = GetCoreMidiEvent(false); if (! event) { break; @@ -186,12 +182,15 @@ JackCoreMidiOutputPort::Init() void JackCoreMidiOutputPort::Initialize(const char *alias_name, - const char *client_name, - const char *driver_name, int index, - MIDIEndpointRef endpoint) + const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint, + SInt32 advance_schedule_time) { JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, true); + assert(advance_schedule_time >= 0); + this->advance_schedule_time = advance_schedule_time; } void diff --git a/macosx/coremidi/JackCoreMidiOutputPort.h b/macosx/coremidi/JackCoreMidiOutputPort.h index d713c314..a04d98b3 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.h +++ b/macosx/coremidi/JackCoreMidiOutputPort.h @@ -42,6 +42,7 @@ namespace Jack { static const size_t PACKET_BUFFER_SIZE = 65536; + SInt32 advance_schedule_time; char packet_buffer[PACKET_BUFFER_SIZE]; JackMidiBufferReadQueue *read_queue; char semaphore_name[128]; @@ -57,7 +58,7 @@ namespace Jack { void Initialize(const char *alias_name, const char *client_name, const char *driver_name, int index, - MIDIEndpointRef endpoint); + MIDIEndpointRef endpoint, SInt32 advance_schedule_time); public: diff --git a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp index e5b19d59..d8a4af25 100644 --- a/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp @@ -43,7 +43,20 @@ JackCoreMidiPhysicalOutputPort(const char *alias_name, const char *client_name, << "' is not available"; throw std::runtime_error(stream.str().c_str()); } - Initialize(alias_name, client_name, driver_name, index, destination); + SInt32 advance_schedule_time; + OSStatus status = + MIDIObjectGetIntegerProperty(destination, + kMIDIPropertyAdvanceScheduleTimeMuSec, + &advance_schedule_time); + if (status != noErr) { + WriteMacOSError("JackCoreMidiPhysicalOutputPort [constructor]", + "MIDIObjectGetIntegerProperty", status); + advance_schedule_time = 0; + } else if (advance_schedule_time < 0) { + advance_schedule_time = 0; + } + Initialize(alias_name, client_name, driver_name, index, destination, + advance_schedule_time); this->internal_output = internal_output; } diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp index 5e69a170..88b46d06 100644 --- a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp @@ -47,7 +47,7 @@ JackCoreMidiVirtualOutputPort(const char *alias_name, const char *client_name, if (status != noErr) { throw std::runtime_error(GetMacOSErrorString(status)); } - Initialize(alias_name, client_name, driver_name, index, source); + Initialize(alias_name, client_name, driver_name, index, source, 0); } JackCoreMidiVirtualOutputPort::~JackCoreMidiVirtualOutputPort() From e75eea7e67ff79d8ca69d79bffbf1623954fb978 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Tue, 29 Mar 2011 23:01:56 +0200 Subject: [PATCH 44/58] Add missing include. --- macosx/coremidi/JackCoreMidiOutputPort.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/macosx/coremidi/JackCoreMidiOutputPort.cpp b/macosx/coremidi/JackCoreMidiOutputPort.cpp index 48058c92..f014d478 100644 --- a/macosx/coremidi/JackCoreMidiOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -25,6 +25,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackCoreMidiOutputPort.h" #include "JackMidiUtil.h" +#include "JackTime.h" using Jack::JackCoreMidiOutputPort; From c9342fc058e01ad5df90b46a961b03bbd9a939ce Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Tue, 29 Mar 2011 16:54:37 -0700 Subject: [PATCH 45/58] Add default 'SetBufferSize' implementation for 'JackMidiDriver'. --- common/JackMidiDriver.cpp | 21 +++++++++++++++++++++ common/JackMidiDriver.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 4de11989..6b3f2935 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -143,6 +143,27 @@ int JackMidiDriver::Write() return 0; } +int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) +{ + jack_latency_range_t latency_range; + latency_range.max = buffer_size; + latency_range.min = buffer_size; + for (int i = 0; i < fCaptureChannels; i++) { + fGraphManager->GetPort(fCapturePortList[i])-> + SetLatencyRange(JackCaptureLatency, &latency_range); + } + if (! fEngineControl->fSyncMode) { + buffer_size *= 2; + latency_range.max = buffer_size; + latency_range.min = buffer_size; + } + for (int i = 0; i < fPlaybackChannels; i++) { + fGraphManager->GetPort(fPlaybackPortList[i])-> + SetLatencyRange(JackPlaybackLatency, &latency_range); + } + return 0; +} + int JackMidiDriver::ProcessNull() { return 0; diff --git a/common/JackMidiDriver.h b/common/JackMidiDriver.h index 45fe484c..044e8d23 100644 --- a/common/JackMidiDriver.h +++ b/common/JackMidiDriver.h @@ -69,6 +69,8 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver jack_nframes_t capture_latency, jack_nframes_t playback_latency); + virtual int SetBufferSize(jack_nframes_t buffer_size); + virtual int ProcessRead(); virtual int ProcessWrite(); From cf98c33ef8678e948667235f0061494ba0a627cd Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Wed, 30 Mar 2011 11:41:00 +0200 Subject: [PATCH 46/58] Correct SetBufferSize for drivers. --- common/JackAudioDriver.cpp | 43 ++++++++---- common/JackAudioDriver.h | 1 + common/JackDriver.cpp | 20 +++++- common/JackDummyDriver.cpp | 1 + common/JackMidiDriver.cpp | 1 + common/JackNetManager.cpp | 2 +- common/JackServer.cpp | 2 - linux/alsa/JackAlsaDriver.cpp | 40 +++++++---- linux/alsa/JackAlsaDriver.h | 2 + macosx/coreaudio/JackCoreAudioDriver.cpp | 86 ++++++++++++++--------- macosx/coreaudio/JackCoreAudioDriver.h | 16 +++-- windows/portaudio/JackPortAudioDriver.cpp | 39 ++++------ windows/portaudio/JackPortAudioDriver.h | 2 + 13 files changed, 163 insertions(+), 92 deletions(-) diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index dedf02b0..f284a8cf 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -44,12 +44,17 @@ JackAudioDriver::~JackAudioDriver() int JackAudioDriver::SetBufferSize(jack_nframes_t buffer_size) { + // Update engine and graph manager state fEngineControl->fBufferSize = buffer_size; fGraphManager->SetBufferSize(buffer_size); fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec if (!fEngineControl->fTimeOut) fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); - return 0; + + UpdateLatencies(); + + // Redirect on slaves drivers... + return JackDriver::SetBufferSize(buffer_size); } int JackAudioDriver::SetSampleRate(jack_nframes_t sample_rate) @@ -58,7 +63,8 @@ int JackAudioDriver::SetSampleRate(jack_nframes_t sample_rate) fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec if (!fEngineControl->fTimeOut) fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); - return 0; + + return JackDriver::SetSampleRate(sample_rate); } int JackAudioDriver::Open(jack_nframes_t buffer_size, @@ -95,13 +101,33 @@ int JackAudioDriver::Open(bool capturing, return JackDriver::Open(capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } +void JackAudioDriver::UpdateLatencies() +{ + jack_latency_range_t range; + + for (int i = 0; i < fCaptureChannels; i++) { + range.max = range.min = fEngineControl->fBufferSize; + fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); + } + + for (int i = 0; i < fPlaybackChannels; i++) { + if (! fEngineControl->fSyncMode) { + range.max = range.min = fEngineControl->fBufferSize * 2; + } + fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); + if (fWithMonitorPorts) { + range.min = range.max = fEngineControl->fBufferSize; + fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &range); + } + } +} + int JackAudioDriver::Attach() { JackPort* port; jack_port_id_t port_index; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - jack_latency_range_t range; int i; jack_log("JackAudioDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); @@ -115,8 +141,6 @@ int JackAudioDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - range.min = range.max = fEngineControl->fBufferSize + fCaptureLatency; - port->SetLatencyRange(JackCaptureLatency, &range); fCapturePortList[i] = port_index; jack_log("JackAudioDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } @@ -130,9 +154,6 @@ int JackAudioDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - // Add more latency if "async" mode is used... - range.min = range.max = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + fPlaybackLatency; - port->SetLatencyRange(JackPlaybackLatency, &range); fPlaybackPortList[i] = port_index; jack_log("JackAudioDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); @@ -144,14 +165,12 @@ int JackAudioDriver::Attach() jack_error("Cannot register monitor port for %s", name); return -1; } else { - port = fGraphManager->GetPort(port_index); - range.min = range.max = fEngineControl->fBufferSize; - port->SetLatencyRange(JackCaptureLatency, &range); - fMonitorPortList[i] = port_index; + fMonitorPortList[i] = port_index; } } } + UpdateLatencies(); return 0; } diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index cb0ebefe..83b0ba53 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -62,6 +62,7 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver jack_default_audio_sample_t* GetMonitorBuffer(int port_index); void HandleLatencyCallback(int status); + void UpdateLatencies(); public: diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index e94b1a35..5fab3f4f 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -418,12 +418,28 @@ bool JackDriver::IsFixedBufferSize() int JackDriver::SetBufferSize(jack_nframes_t buffer_size) { - return 0; + int res = 0; + + list::const_iterator it; + for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { + JackDriverInterface* slave = *it; + if (slave->SetBufferSize(buffer_size) < 0) + res = -1; + } + + return res; } int JackDriver::SetSampleRate(jack_nframes_t sample_rate) { - return 0; + int res = 0; + list::const_iterator it; + for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { + JackDriverInterface* slave = *it; + if (slave->SetSampleRate(sample_rate) < 0) + res = -1; + } + return res; } bool JackDriver::Initialize() diff --git a/common/JackDummyDriver.cpp b/common/JackDummyDriver.cpp index fb996a11..4c084f06 100644 --- a/common/JackDummyDriver.cpp +++ b/common/JackDummyDriver.cpp @@ -78,6 +78,7 @@ int JackDummyDriver::Process() int JackDummyDriver::SetBufferSize(jack_nframes_t buffer_size) { + // Generic change, never fails JackAudioDriver::SetBufferSize(buffer_size); fWaitTime = (unsigned long)((((float)buffer_size) / ((float)fEngineControl->fSampleRate)) * 1000000.0f); return 0; diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 6b3f2935..5c0604d5 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -145,6 +145,7 @@ int JackMidiDriver::Write() int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) { + // Update latencies jack_latency_range_t latency_range; latency_range.max = buffer_size; latency_range.min = buffer_size; diff --git a/common/JackNetManager.cpp b/common/JackNetManager.cpp index e882cb6e..ab97637b 100644 --- a/common/JackNetManager.cpp +++ b/common/JackNetManager.cpp @@ -392,7 +392,7 @@ namespace Jack { JackNetMaster* obj = static_cast(arg); if (nframes != obj->fParams.fPeriodSize) { - jack_error("Cannot handle bufer size change, so JackNetMaster proxy will be removed..."); + jack_error("Cannot handle buffer size change, so JackNetMaster proxy will be removed..."); obj->Exit(); } return 0; diff --git a/common/JackServer.cpp b/common/JackServer.cpp index cd1915fb..e1bcbb8a 100644 --- a/common/JackServer.cpp +++ b/common/JackServer.cpp @@ -223,13 +223,11 @@ int JackServer::SetBufferSize(jack_nframes_t buffer_size) } if (fAudioDriver->SetBufferSize(buffer_size) == 0) { - fFreewheelDriver->SetBufferSize(buffer_size); fEngine->NotifyBufferSize(buffer_size); return fAudioDriver->Start(); } else { // Failure: try to restore current value jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size); fAudioDriver->SetBufferSize(current_buffer_size); - fFreewheelDriver->SetBufferSize(current_buffer_size); fAudioDriver->Start(); // SetBufferSize actually failed, so return an error... return -1; diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index b42e9693..f8eaf22b 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -55,8 +55,11 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) ((alsa_driver_t *)fDriver)->frame_rate); if (res == 0) { // update fEngineControl and fGraphManager - JackAudioDriver::SetBufferSize(buffer_size); // never fails + JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails + // ALSA specific + UpdateLatencies(); } else { + // Restore old values alsa_driver_reset_parameters((alsa_driver_t *)fDriver, fEngineControl->fBufferSize, ((alsa_driver_t *)fDriver)->user_nperiods, ((alsa_driver_t *)fDriver)->frame_rate); @@ -65,6 +68,28 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) return res; } +void JackAlsaDriver::UpdateLatencies() +{ + jack_latency_range_t range; + + for (int i = 0; i < fCaptureChannels; i++) { + range.min = range.max = alsa_driver->frames_per_cycle + alsa_driver->capture_frame_latency; + fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); + } + + for (int i = 0; i < fPlaybackChannels; i++) { + // Add one buffer more latency if "async" mode is used... + range.min = range.max = (alsa_driver->frames_per_cycle * (alsa_driver->user_nperiods - 1)) + + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + alsa_driver->playback_frame_latency; + fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); + // Monitor port + if (fWithMonitorPorts) { + range.min = range.max = alsa_driver->frames_per_cycle; + fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &range); + } + } +} + int JackAlsaDriver::Attach() { JackPort* port; @@ -72,7 +97,6 @@ int JackAlsaDriver::Attach() unsigned long port_flags = (unsigned long)CaptureDriverFlags; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - jack_latency_range_t range; assert(fCaptureChannels < DRIVER_PORT_NUM); assert(fPlaybackChannels < DRIVER_PORT_NUM); @@ -97,8 +121,6 @@ int JackAlsaDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - range.min = range.max = alsa_driver->frames_per_cycle + alsa_driver->capture_frame_latency; - port->SetLatencyRange(JackCaptureLatency, &range); fCapturePortList[i] = port_index; jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_index); } @@ -114,11 +136,6 @@ int JackAlsaDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - // Add one buffer more latency if "async" mode is used... - range.min = range.max = (alsa_driver->frames_per_cycle * (alsa_driver->user_nperiods - 1)) + - ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + alsa_driver->playback_frame_latency; - - port->SetLatencyRange(JackPlaybackLatency, &range); fPlaybackPortList[i] = port_index; jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", port_index); @@ -129,14 +146,13 @@ int JackAlsaDriver::Attach() if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { jack_error ("ALSA: cannot register monitor port for %s", name); } else { - port = fGraphManager->GetPort(port_index); - range.min = range.max = alsa_driver->frames_per_cycle; - port->SetLatencyRange(JackCaptureLatency, &range); fMonitorPortList[i] = port_index; } } } + UpdateLatencies(); + if (alsa_driver->midi) { int err = (alsa_driver->midi->attach)(alsa_driver->midi); if (err) diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index 6d42b91a..a88d4a01 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -42,6 +42,8 @@ class JackAlsaDriver : public JackAudioDriver int fReservedCaptureDevice; int fReservedPlaybackDevice; + void UpdateLatencies(); + public: JackAlsaDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) diff --git a/macosx/coreaudio/JackCoreAudioDriver.cpp b/macosx/coreaudio/JackCoreAudioDriver.cpp index bf4e3195..97352bca 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.cpp +++ b/macosx/coreaudio/JackCoreAudioDriver.cpp @@ -1563,6 +1563,53 @@ int JackCoreAudioDriver::Close() return res; } +void JackCoreAudioDriver::UpdateLatencies() +{ + UInt32 size; + OSStatus err; + jack_latency_range_t range; + range.max = fEngineControl->fBufferSize; + range.min = fEngineControl->fBufferSize; + + for (int i = 0; i < fCaptureChannels; i++) { + size = sizeof(UInt32); + UInt32 value1 = 0; + UInt32 value2 = 0; + err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertyLatency, &size, &value1); + if (err != noErr) + jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); + err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertySafetyOffset, &size, &value2); + if (err != noErr) + jack_log("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); + + range.min = range.max = fEngineControl->fBufferSize + value1 + value2 + fCaptureLatency; + fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); + } + + for (int i = 0; i < fPlaybackChannels; i++) { + size = sizeof(UInt32); + UInt32 value1 = 0; + UInt32 value2 = 0; + err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertyLatency, &size, &value1); + if (err != noErr) + jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); + err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertySafetyOffset, &size, &value2); + if (err != noErr) + jack_log("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); + + // Add more latency if "async" mode is used... + range.min = range.max + = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize * fIOUsage) + value1 + value2 + fPlaybackLatency; + fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); + + // Monitor port + if (fWithMonitorPorts) { + range.min = range.max = fEngineControl->fBufferSize; + fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &range); + } + } +} + int JackCoreAudioDriver::Attach() { OSStatus err; @@ -1573,7 +1620,6 @@ int JackCoreAudioDriver::Attach() char channel_name[64]; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - jack_latency_range_t range; jack_log("JackCoreAudioDriver::Attach fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); @@ -1598,20 +1644,8 @@ int JackCoreAudioDriver::Attach() return -1; } - size = sizeof(UInt32); - UInt32 value1 = 0; - UInt32 value2 = 0; - err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertyLatency, &size, &value1); - if (err != noErr) - jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); - err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertySafetyOffset, &size, &value2); - if (err != noErr) - jack_log("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); - port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - range.min = range.max = fEngineControl->fBufferSize + value1 + value2 + fCaptureLatency; - port->SetLatencyRange(JackCaptureLatency, &range); fCapturePortList[i] = port_index; } @@ -1636,21 +1670,8 @@ int JackCoreAudioDriver::Attach() return -1; } - size = sizeof(UInt32); - UInt32 value1 = 0; - UInt32 value2 = 0; - err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertyLatency, &size, &value1); - if (err != noErr) - jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); - err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertySafetyOffset, &size, &value2); - if (err != noErr) - jack_log("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); - port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - // Add more latency if "async" mode is used... - range.min = range.max = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize * fIOUsage) + value1 + value2 + fPlaybackLatency; - port->SetLatencyRange(JackPlaybackLatency, &range); fPlaybackPortList[i] = port_index; // Monitor ports @@ -1661,14 +1682,13 @@ int JackCoreAudioDriver::Attach() jack_error("Cannot register monitor port for %s", name); return -1; } else { - port = fGraphManager->GetPort(port_index); - range.min = range.max = fEngineControl->fBufferSize; - port->SetLatencyRange(JackCaptureLatency, &range); fMonitorPortList[i] = port_index; } } } + UpdateLatencies(); + // Input buffers do no change : prepare them only once for (int i = 0; i < fCaptureChannels; i++) { fJackInputData->mBuffers[i].mData = GetInputBuffer(i); @@ -1716,17 +1736,19 @@ int JackCoreAudioDriver::Stop() int JackCoreAudioDriver::SetBufferSize(jack_nframes_t buffer_size) { - OSStatus err; UInt32 outSize = sizeof(UInt32); - err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &buffer_size); + OSStatus err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &buffer_size); if (err != noErr) { jack_error("Cannot set buffer size %ld", buffer_size); printError(err); return -1; } - JackAudioDriver::SetBufferSize(buffer_size); // never fails + JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails + + // CoreAudio specific + UpdateLatencies(); // Input buffers do no change : prepare them only once for (int i = 0; i < fCaptureChannels; i++) { diff --git a/macosx/coreaudio/JackCoreAudioDriver.h b/macosx/coreaudio/JackCoreAudioDriver.h index b0ccbe10..c0513609 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.h +++ b/macosx/coreaudio/JackCoreAudioDriver.h @@ -40,7 +40,7 @@ typedef UInt8 CAAudioHardwareDeviceSectionID; #define kAudioDeviceSectionOutput ((CAAudioHardwareDeviceSectionID)0x00) #define kAudioDeviceSectionGlobal ((CAAudioHardwareDeviceSectionID)0x00) #define kAudioDeviceSectionWildcard ((CAAudioHardwareDeviceSectionID)0xFF) - + #define WAIT_COUNTER 60 /*! @@ -74,13 +74,13 @@ class JackCoreAudioDriver : public JackAudioDriver float fIOUsage; float fComputationGrain; bool fClockDriftCompensate; - - /* + + /* #ifdef MAC_OS_X_VERSION_10_5 AudioDeviceIOProcID fMesureCallbackID; #endif */ - + static OSStatus Render(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, @@ -106,13 +106,13 @@ class JackCoreAudioDriver : public JackAudioDriver OSStatus GetDefaultOutputDevice(AudioDeviceID* id); OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name); OSStatus GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput); - + // Setup OSStatus CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice); OSStatus CreateAggregateDeviceAux(vector captureDeviceID, vector playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice); OSStatus DestroyAggregateDevice(); bool IsAggregateDevice(AudioDeviceID device); - + int SetupDevices(const char* capture_driver_uid, const char* playback_driver_uid, char* capture_driver_name, @@ -146,10 +146,12 @@ class JackCoreAudioDriver : public JackAudioDriver int AddListeners(); void RemoveListeners(); - + bool TakeHogAux(AudioDeviceID deviceID, bool isInput); bool TakeHog(); + void UpdateLatencies(); + public: JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); diff --git a/windows/portaudio/JackPortAudioDriver.cpp b/windows/portaudio/JackPortAudioDriver.cpp index 41a3deb2..790bcda1 100644 --- a/windows/portaudio/JackPortAudioDriver.cpp +++ b/windows/portaudio/JackPortAudioDriver.cpp @@ -86,13 +86,11 @@ namespace Jack return -1; //get devices - if (capturing) - { + if (capturing) { if (fPaDevices->GetInputDeviceFromName(capture_driver_uid, fInputDevice, in_max) < 0) goto error; } - if (playing) - { + if (playing) { if (fPaDevices->GetOutputDeviceFromName(playback_driver_uid, fOutputDevice, out_max) < 0) goto error; } @@ -100,25 +98,21 @@ namespace Jack jack_log("JackPortAudioDriver::Open fInputDevice = %d, fOutputDevice %d", fInputDevice, fOutputDevice); //default channels number required - if (inchannels == 0) - { + if (inchannels == 0) { jack_log("JackPortAudioDriver::Open setup max in channels = %ld", in_max); inchannels = in_max; } - if (outchannels == 0) - { + if (outchannels == 0) { jack_log("JackPortAudioDriver::Open setup max out channels = %ld", out_max); outchannels = out_max; } //too many channels required, take max available - if (inchannels > in_max) - { + if (inchannels > in_max) { jack_error("This device has only %d available input channels.", in_max); inchannels = in_max; } - if (outchannels > out_max) - { + if (outchannels > out_max) { jack_error("This device has only %d available output channels.", out_max); outchannels = out_max; } @@ -148,8 +142,7 @@ namespace Jack paNoFlag, // Clipping is on... Render, this); - if (err != paNoError) - { + if (err != paNoError) { jack_error("Pa_OpenStream error = %s", Pa_GetErrorText(err)); goto error; } @@ -218,13 +211,12 @@ error: PaStreamParameters inputParameters; PaStreamParameters outputParameters; - if ((err = Pa_CloseStream(fStream)) != paNoError) - { + if ((err = Pa_CloseStream(fStream)) != paNoError) { jack_error("Pa_CloseStream error = %s", Pa_GetErrorText(err)); return -1; } - //change parametering + // Update parameters inputParameters.device = fInputDevice; inputParameters.channelCount = fCaptureChannels; inputParameters.sampleFormat = paFloat32 | paNonInterleaved; // 32 bit floating point output @@ -250,15 +242,14 @@ error: Render, this); - if (err != paNoError) - { + if (err != paNoError) { jack_error("Pa_OpenStream error = %s", Pa_GetErrorText(err)); return -1; - } - else - { - // Only done when success - return JackAudioDriver::SetBufferSize(buffer_size); // never fails + } else { + JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails + // PortAudio specific + UpdateLatencies(); + return 0; } } diff --git a/windows/portaudio/JackPortAudioDriver.h b/windows/portaudio/JackPortAudioDriver.h index c4945f44..564d5a4e 100644 --- a/windows/portaudio/JackPortAudioDriver.h +++ b/windows/portaudio/JackPortAudioDriver.h @@ -48,6 +48,8 @@ class JackPortAudioDriver : public JackAudioDriver PaStreamCallbackFlags statusFlags, void* userData); + void UpdateLatencies(); + public: JackPortAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, PortAudioDevices* pa_devices) From 3240743a8c6a4b0d70120984bbf9b7cb582d0294 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Wed, 30 Mar 2011 11:51:32 +0200 Subject: [PATCH 47/58] Cleanup SetBufferSize for JackMidiDriver. --- common/JackMidiDriver.cpp | 40 ++++++++++++++++----------------------- common/JackMidiDriver.h | 2 ++ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 5c0604d5..b98b8daa 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -74,12 +74,9 @@ int JackMidiDriver::Attach() jack_port_id_t port_index; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - jack_latency_range_t latency_range; - jack_nframes_t latency = fEngineControl->fBufferSize; int i; jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); - latency_range.max = latency_range.min = latency; for (i = 0; i < fCaptureChannels; i++) { snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); @@ -90,16 +87,10 @@ int JackMidiDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - port->SetLatencyRange(JackCaptureLatency, &latency_range); fCapturePortList[i] = port_index; jack_log("JackMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } - if (!fEngineControl->fSyncMode) { - latency += fEngineControl->fBufferSize;; - latency_range.max = latency_range.min = latency; - } - for (i = 0; i < fPlaybackChannels; i++) { snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); @@ -109,11 +100,11 @@ int JackMidiDriver::Attach() } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); - port->SetLatencyRange(JackPlaybackLatency, &latency_range); fPlaybackPortList[i] = port_index; jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } + UpdateLatencies(); return 0; } @@ -143,25 +134,26 @@ int JackMidiDriver::Write() return 0; } -int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) +void JackMidiDriver::UpdateLatencies() { - // Update latencies - jack_latency_range_t latency_range; - latency_range.max = buffer_size; - latency_range.min = buffer_size; + jack_latency_range_t range; + for (int i = 0; i < fCaptureChannels; i++) { - fGraphManager->GetPort(fCapturePortList[i])-> - SetLatencyRange(JackCaptureLatency, &latency_range); - } - if (! fEngineControl->fSyncMode) { - buffer_size *= 2; - latency_range.max = buffer_size; - latency_range.min = buffer_size; + range.max = range.min = fEngineControl->fBufferSize; + fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); } + for (int i = 0; i < fPlaybackChannels; i++) { - fGraphManager->GetPort(fPlaybackPortList[i])-> - SetLatencyRange(JackPlaybackLatency, &latency_range); + if (! fEngineControl->fSyncMode) { + range.max = range.min = fEngineControl->fBufferSize * 2; + } + fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); } +} + +int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) +{ + UpdateLatencies(); return 0; } diff --git a/common/JackMidiDriver.h b/common/JackMidiDriver.h index 044e8d23..fa471627 100644 --- a/common/JackMidiDriver.h +++ b/common/JackMidiDriver.h @@ -54,6 +54,8 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver virtual int ProcessReadAsync(); virtual int ProcessWriteAsync(); + virtual void UpdateLatencies(); + public: JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); From 51e07561ad3541f26e88923e67c66b46740f6112 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Wed, 30 Mar 2011 11:44:27 +0200 Subject: [PATCH 48/58] Correct JackAlsaDriver::UpdateLatencies. --- linux/alsa/JackAlsaDriver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index f8eaf22b..fb816453 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -71,6 +71,7 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) void JackAlsaDriver::UpdateLatencies() { jack_latency_range_t range; + alsa_driver_t* alsa_driver = (alsa_driver_t*)fDriver; for (int i = 0; i < fCaptureChannels; i++) { range.min = range.max = alsa_driver->frames_per_cycle + alsa_driver->capture_frame_latency; From ceb7c50ec4240ba1bbb333b4c3e1656a8dcd2a81 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Wed, 30 Mar 2011 23:40:47 +0200 Subject: [PATCH 49/58] Correct virtual sources/destinations handling. --- macosx/coremidi/JackCoreMidiDriver.cpp | 101 +++++++++--------- .../coremidi/JackCoreMidiVirtualInputPort.cpp | 2 +- .../JackCoreMidiVirtualOutputPort.cpp | 2 +- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index acdd5136..234f2afe 100644 --- a/macosx/coremidi/JackCoreMidiDriver.cpp +++ b/macosx/coremidi/JackCoreMidiDriver.cpp @@ -279,56 +279,6 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } char *client_name = fClientControl.fName; - // Allocate and connect virtual inputs - if (in_channels) { - try { - virtual_input_ports = - new JackCoreMidiVirtualInputPort*[in_channels]; - } catch (std::exception e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "input port array: %s", e.what()); - goto destroy_client; - } - for (vi_count = 0; vi_count < in_channels; vi_count++) { - try { - virtual_input_ports[vi_count] = - new JackCoreMidiVirtualInputPort(fAliasName, client_name, - capture_driver_name, - vi_count, client, - time_ratio); - } catch (std::exception e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "input port: %s", e.what()); - goto destroy_virtual_input_ports; - } - } - } - - // Allocate and connect virtual outputs - if (out_channels) { - try { - virtual_output_ports = - new JackCoreMidiVirtualOutputPort*[out_channels]; - } catch (std::exception e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "output port array: %s", e.what()); - goto destroy_virtual_input_ports; - } - for (vo_count = 0; vo_count < out_channels; vo_count++) { - try { - virtual_output_ports[vo_count] = - new JackCoreMidiVirtualOutputPort(fAliasName, client_name, - playback_driver_name, - vo_count, client, - time_ratio); - } catch (std::exception e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "output port: %s", e.what()); - goto destroy_virtual_output_ports; - } - } - } - // Allocate and connect physical inputs potential_pi_count = MIDIGetNumberOfSources(); if (potential_pi_count) { @@ -397,6 +347,57 @@ JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels, } } + // Allocate and connect virtual inputs + if (in_channels) { + try { + virtual_input_ports = + new JackCoreMidiVirtualInputPort*[in_channels]; + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "input port array: %s", e.what()); + goto destroy_client; + } + for (vi_count = 0; vi_count < in_channels; vi_count++) { + try { + virtual_input_ports[vi_count] = + new JackCoreMidiVirtualInputPort(fAliasName, client_name, + capture_driver_name, + vi_count + pi_count, client, + time_ratio); + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "input port: %s", e.what()); + goto destroy_virtual_input_ports; + } + } + } + + // Allocate and connect virtual outputs + if (out_channels) { + try { + virtual_output_ports = + new JackCoreMidiVirtualOutputPort*[out_channels]; + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "output port array: %s", e.what()); + goto destroy_virtual_input_ports; + } + for (vo_count = 0; vo_count < out_channels; vo_count++) { + try { + virtual_output_ports[vo_count] = + new JackCoreMidiVirtualOutputPort(fAliasName, client_name, + playback_driver_name, + vo_count + po_count, client, + time_ratio); + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "output port: %s", e.what()); + goto destroy_virtual_output_ports; + } + } + } + + if (! (pi_count || po_count || in_channels || out_channels)) { jack_error("JackCoreMidiDriver::Open - no CoreMIDI inputs or outputs " "found, and no virtual ports allocated."); diff --git a/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp index 42d9b52f..874a644d 100644 --- a/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp +++ b/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp @@ -49,7 +49,7 @@ JackCoreMidiVirtualInputPort(const char *alias_name, const char *client_name, JackCoreMidiInputPort(time_ratio, max_bytes, max_messages) { std::stringstream stream; - stream << "JackMidi" << (index + 1); + stream << "virtual" << (index + 1); CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), CFStringGetSystemEncoding()); if (! name) { diff --git a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp index 88b46d06..0d8bc173 100644 --- a/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp @@ -35,7 +35,7 @@ JackCoreMidiVirtualOutputPort(const char *alias_name, const char *client_name, max_messages) { std::stringstream stream; - stream << "JackMidi" << (index + 1); + stream << "virtual" << (index + 1); CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), CFStringGetSystemEncoding()); if (! name) { From 69c59f4423ce79f41444479f5dde8dbc8ba977db Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 31 Mar 2011 11:34:58 +0200 Subject: [PATCH 50/58] Renaming for Windows compatibility. --- common/JackMidiWriteQueue.h | 4 ++-- linux/alsarawmidi/JackALSARawMidiSendQueue.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/JackMidiWriteQueue.h b/common/JackMidiWriteQueue.h index a39776c9..f21a58ff 100644 --- a/common/JackMidiWriteQueue.h +++ b/common/JackMidiWriteQueue.h @@ -37,7 +37,7 @@ namespace Jack { BUFFER_FULL, BUFFER_TOO_SMALL, EVENT_EARLY, - ERROR, + EN_ERROR, OK }; @@ -54,7 +54,7 @@ namespace Jack { * 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 + * queue cannot schedule events ahead of time, and `EN_ERROR` if an error * occurs that cannot be specified by another return code. */ diff --git a/linux/alsarawmidi/JackALSARawMidiSendQueue.cpp b/linux/alsarawmidi/JackALSARawMidiSendQueue.cpp index 5f314263..80a11b56 100755 --- a/linux/alsarawmidi/JackALSARawMidiSendQueue.cpp +++ b/linux/alsarawmidi/JackALSARawMidiSendQueue.cpp @@ -30,7 +30,7 @@ JackALSARawMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size, } jack_error("JackALSARawMidiSendQueue::EnqueueEvent - snd_rawmidi_write: " "%s", snd_strerror(result)); - return ERROR; + return EN_ERROR; } bool From 07f7895991345ac8a0b66d271052d9c145e8cb6f Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 31 Mar 2011 13:16:19 +0200 Subject: [PATCH 51/58] New driver compiles. --- windows/jack_winmme.cbp | 11 +++++ windows/jackd.workspace | 4 +- windows/libjackserver.cbp | 2 - windows/portaudio/JackPortAudioDriver.cpp | 2 +- windows/winmme/JackWinMMEInputPort.cpp | 33 +++++++------- windows/winmme/JackWinMMEInputPort.h | 4 +- windows/winmme/JackWinMMEOutputPort.cpp | 52 +++++++++++++++++------ windows/winmme/JackWinMMEOutputPort.h | 16 +++++-- 8 files changed, 85 insertions(+), 39 deletions(-) diff --git a/windows/jack_winmme.cbp b/windows/jack_winmme.cbp index 2c6d8bbe..7de61fa8 100644 --- a/windows/jack_winmme.cbp +++ b/windows/jack_winmme.cbp @@ -102,10 +102,21 @@ + + + + + + + + + + + diff --git a/windows/jackd.workspace b/windows/jackd.workspace index 05f5b3d1..4384140e 100644 --- a/windows/jackd.workspace +++ b/windows/jackd.workspace @@ -5,7 +5,7 @@ - + @@ -57,6 +57,6 @@ - + diff --git a/windows/libjackserver.cbp b/windows/libjackserver.cbp index e4db2136..89cd2dd3 100644 --- a/windows/libjackserver.cbp +++ b/windows/libjackserver.cbp @@ -154,8 +154,6 @@ - - diff --git a/windows/portaudio/JackPortAudioDriver.cpp b/windows/portaudio/JackPortAudioDriver.cpp index 790bcda1..f7611add 100644 --- a/windows/portaudio/JackPortAudioDriver.cpp +++ b/windows/portaudio/JackPortAudioDriver.cpp @@ -248,7 +248,7 @@ error: } else { JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails // PortAudio specific - UpdateLatencies(); + JackAudioDriver::UpdateLatencies(); return 0; } } diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp index c9b9cbb1..0adf3bec 100644 --- a/windows/winmme/JackWinMMEInputPort.cpp +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -24,6 +24,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackError.h" #include "JackMidiUtil.h" #include "JackWinMMEInputPort.h" +#include "JackMidiWriteQueue.h" using Jack::JackWinMMEInputPort; @@ -54,7 +55,7 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, std::auto_ptr write_queue_ptr(write_queue); sysex_buffer = new jack_midi_data_t[max_bytes]; char error_message[MAXERRORLENGTH]; - MMRESULT result = midiInOpen(&handle, index, HandleMidiInputEvent, this, + MMRESULT result = midiInOpen(&handle, index, (DWORD)HandleMidiInputEvent, (DWORD)this, CALLBACK_FUNCTION | MIDI_IO_STATUS); if (result != MMSYSERR_NOERROR) { GetErrorString(result, error_message); @@ -64,7 +65,7 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, sysex_header.dwBytesRecorded = 0; sysex_header.dwFlags = 0; sysex_header.dwUser = 0; - sysex_header.lpData = (((LPBYTE) sysex_header) + sizeof(MIDIHDR)); + sysex_header.lpData = (LPSTR)(((LPBYTE) &sysex_header) + sizeof(MIDIHDR)); sysex_header.lpNext = 0; result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { @@ -84,7 +85,7 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, return; unprepare_header: - result = midiInUnprepareHeader(handle, sysex_header, sizeof(MIDIHDR)); + result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { WriteError("JackWinMMEInputPort [constructor]", "midiInUnprepareHeader", result); @@ -106,7 +107,7 @@ JackWinMMEInputPort::~JackWinMMEInputPort() if (result != MMSYSERR_NOERROR) { WriteError("JackWinMMEInputPort [destructor]", "midiInReset", result); } - result = midiInUnprepareHeader(handle, sysex_header, sizeof(MIDIHDR)); + result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { WriteError("JackWinMMEInputPort [destructor]", "midiInUnprepareHeader", result); @@ -128,12 +129,12 @@ JackWinMMEInputPort::EnqueueMessage(jack_nframes_t time, size_t length, case JackMidiWriteQueue::BUFFER_FULL: jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " "cannot currently accept a %d-byte event. Dropping event.", - size); + length); break; case JackMidiWriteQueue::BUFFER_TOO_SMALL: jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " "buffer is too small to enqueue a %d-byte event. Dropping " - "event.", size); + "event.", length); break; default: ; @@ -162,20 +163,20 @@ JackWinMMEInputPort::GetName() } void -JackWinMMEInputPort::ProcessJack() +JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames) { write_queue->ResetMidiBuffer(port_buffer, frames); if (! jack_event) { jack_event = thread_queue->DequeueEvent(); } for (; jack_event; jack_event = thread_queue->DequeueEvent()) { - switch (write_queue->EnqueueEvent(event)) { - case BUFFER_TOO_SMALL: + switch (write_queue->EnqueueEvent(jack_event)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: jack_error("JackWinMMEMidiInputPort::Process - The buffer write " "queue couldn't enqueue a %d-byte event. Dropping " - "event.", event->size); + "event.", jack_event->size); // Fallthrough on purpose - case OK: + case JackMidiWriteQueue::OK: continue; } break; @@ -224,14 +225,14 @@ JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) EnqueueMessage(current_frame, (size_t) length, message_buffer); break; case MIM_LONGDATA: - LPMIDIHDR header = (LPMIDIHDR) dwParam1; + LPMIDIHDR header = (LPMIDIHDR) param1; jack_midi_data_t *data = (jack_midi_data_t *) header->lpData; - size_t length = header->dwBytesRecorded; - if ((data[0] != 0xf0) || (data[length - 1] != 0xf7)) { + size_t length1 = header->dwBytesRecorded; + if ((data[0] != 0xf0) || (data[length1 - 1] != 0xf7)) { jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding " "%d-byte sysex chunk.", length); } else { - EnqueueMessage(current_frame, length, data); + EnqueueMessage(current_frame, length1, data); } // Is this realtime-safe? This function isn't run in the JACK thread, // but we still want it to perform as quickly as possible. Even if @@ -282,7 +283,7 @@ void JackWinMMEInputPort::WriteError(const char *jack_func, const char *mm_func, MMRESULT result) { - const char error_message[MAXERRORLENGTH]; + char error_message[MAXERRORLENGTH]; GetErrorString(result, error_message); jack_error("%s - %s: %s", jack_func, mm_func, error_message); } diff --git a/windows/winmme/JackWinMMEInputPort.h b/windows/winmme/JackWinMMEInputPort.h index 5ce042b9..dd4c53f1 100644 --- a/windows/winmme/JackWinMMEInputPort.h +++ b/windows/winmme/JackWinMMEInputPort.h @@ -55,7 +55,7 @@ namespace Jack { char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; bool started; jack_midi_data_t *sysex_buffer; - MIDIHDR sysex_header + MIDIHDR sysex_header; JackMidiAsyncQueue *thread_queue; JackMidiBufferWriteQueue *write_queue; @@ -74,7 +74,7 @@ namespace Jack { GetName(); void - ProcessJack(); + ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); bool Start(); diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index aa87de0f..eb9ba49e 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -55,7 +55,7 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, thread = new JackThread(this); std::auto_ptr thread_ptr(thread); char error_message[MAXERRORLENGTH]; - MMRESULT result = midiOutOpen(&handle, index, HandleMessageEvent, this, + MMRESULT result = midiOutOpen(&handle, index, (DWORD)HandleMessageEvent, (DWORD)this, CALLBACK_FUNCTION); if (result != MMSYSERR_NOERROR) { GetErrorString(result, error_message); @@ -71,22 +71,24 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, GetOSErrorString(error_message); goto destroy_thread_queue_semaphore; } - MIDIINCAPS capabilities; - char *name; + MIDIOUTCAPS capabilities; + char *name_tmp; result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities)); + /* + Devin : FIXME if (result != MMSYSERR_NOERROR) { WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", result); - name = driver_name; + name_tmp = driver_name; } else { - name = capabilities.szPname; + name_tmp = capabilities.szPname; } + */ snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, driver_name, index + 1); snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1); thread_ptr.release(); - write_queue_ptr.release(); - thread_queue_ptr.release(); + thread_queue_ptr.release(); return; destroy_thread_queue_semaphore: @@ -185,7 +187,7 @@ JackWinMMEOutputPort::Execute() header.dwFlags = 0; header.dwOffset = 0; header.dwUser = 0; - header.lpData = data; + header.lpData = (LPSTR)data; result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { WriteMMError("JackWinMMEOutputPort::Execute", @@ -230,11 +232,14 @@ void JackWinMMEOutputPort::GetOSErrorString(LPTSTR text) { DWORD error = GetLastError(); + /* + Devin: FIXME if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &text, MAXERRORLENGTH, NULL)) { snprintf(text, MAXERRORLENGTH, "Unknown OS error code '%d'", error); } + */ } void @@ -297,7 +302,7 @@ JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer, } bool -JackWinMMEOutputPort::Signal(Handle semaphore) +JackWinMMEOutputPort::Signal(HANDLE semaphore) { bool result = (bool) ReleaseSemaphore(semaphore, 1, NULL); if (! result) { @@ -351,7 +356,7 @@ JackWinMMEOutputPort::Stop() } bool -JackWinMMEOutputPort::Wait(Handle semaphore) +JackWinMMEOutputPort::Wait(HANDLE semaphore) { DWORD result = WaitForSingleObject(semaphore, INFINITE); switch (result) { @@ -371,7 +376,7 @@ void JackWinMMEOutputPort::WriteMMError(const char *jack_func, const char *mm_func, MMRESULT result) { - const char error_message[MAXERRORLENGTH]; + char error_message[MAXERRORLENGTH]; GetMMErrorString(result, error_message); jack_error("%s - %s: %s", jack_func, mm_func, error_message); } @@ -379,7 +384,28 @@ JackWinMMEOutputPort::WriteMMError(const char *jack_func, const char *mm_func, void JackWinMMEOutputPort::WriteOSError(const char *jack_func, const char *os_func) { - const char error_message[MAXERRORLENGTH]; - GetOSErrorString(result, error_message); + char error_message[MAXERRORLENGTH]; + // Devin : FIXME + //GetOSErrorString(result, error_message); jack_error("%s - %s: %s", jack_func, os_func, error_message); } + +const char * +JackWinMMEOutputPort::GetAlias() +{ + return alias; +} +const char * +JackWinMMEOutputPort::GetName() +{ + return name; +} + +void +JackWinMMEOutputPort::GetErrorString(MMRESULT error, LPTSTR text) +{ + MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); + if (result != MMSYSERR_NOERROR) { + snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); + } +} diff --git a/windows/winmme/JackWinMMEOutputPort.h b/windows/winmme/JackWinMMEOutputPort.h index 1033ec3e..c343ff55 100644 --- a/windows/winmme/JackWinMMEOutputPort.h +++ b/windows/winmme/JackWinMMEOutputPort.h @@ -47,10 +47,10 @@ namespace Jack { HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2); bool - Signal(Handle semaphore); + Signal(HANDLE semaphore); bool - Wait(Handle semaphore); + Wait(HANDLE semaphore); void WriteMMError(const char *jack_func, const char *mm_func, @@ -60,14 +60,18 @@ namespace Jack { WriteOSError(const char *jack_func, const char *os_func); char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - HMIDIOUT handle; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + + HMIDIOUT handle; JackMidiBufferReadQueue *read_queue; HANDLE sysex_semaphore; JackThread *thread; JackMidiAsyncQueue *thread_queue; HANDLE thread_queue_semaphore; + void + GetErrorString(MMRESULT error, LPTSTR text); + public: JackWinMMEOutputPort(const char *alias_name, const char *client_name, @@ -91,6 +95,12 @@ namespace Jack { bool Stop(); + const char * + GetAlias(); + + const char * + GetName(); + }; } From 2997da1f23403a84501d229d951e8808026aea79 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 31 Mar 2011 15:53:55 +0200 Subject: [PATCH 52/58] Move MIDI common files in server library on OSX. --- macosx/Jackdmp.xcodeproj/project.pbxproj | 144 +++++++++++------------ 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index 195dd976..eb5eb758 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -108,42 +108,6 @@ /* Begin PBXBuildFile section */ 4B0A28ED0D520852002EFF74 /* tw.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A28EC0D520852002EFF74 /* tw.c */; }; 4B0A29260D52108E002EFF74 /* tw.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A28EC0D520852002EFF74 /* tw.c */; }; - 4B193933133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */; }; - 4B193934133F311400547810 /* JackMidiAsyncQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193932133F311400547810 /* JackMidiAsyncQueue.h */; }; - 4B193935133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */; }; - 4B193936133F311400547810 /* JackMidiAsyncQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193932133F311400547810 /* JackMidiAsyncQueue.h */; }; - 4B19393D133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */; }; - 4B19393E133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */; }; - 4B19393F133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */; }; - 4B193940133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */; }; - 4B193947133F315300547810 /* JackMidiReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193945133F315200547810 /* JackMidiReadQueue.cpp */; }; - 4B193948133F315300547810 /* JackMidiReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193946133F315200547810 /* JackMidiReadQueue.h */; }; - 4B193949133F315300547810 /* JackMidiReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193945133F315200547810 /* JackMidiReadQueue.cpp */; }; - 4B19394A133F315300547810 /* JackMidiReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193946133F315200547810 /* JackMidiReadQueue.h */; }; - 4B19395F133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */; }; - 4B193960133F317300547810 /* JackMidiBufferReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */; }; - 4B193961133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */; }; - 4B193962133F317300547810 /* JackMidiBufferReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */; }; - 4B19396A133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */; }; - 4B19396B133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */; }; - 4B19396C133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */; }; - 4B19396D133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */; }; - 4B19397B133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */; }; - 4B19397C133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */; }; - 4B19397D133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */; }; - 4B19397E133F31CB00547810 /* JackMidiSendQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193976133F31CB00547810 /* JackMidiSendQueue.h */; }; - 4B19397F133F31CB00547810 /* JackMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193977133F31CB00547810 /* JackMidiUtil.cpp */; }; - 4B193980133F31CB00547810 /* JackMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193978133F31CB00547810 /* JackMidiUtil.h */; }; - 4B193981133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */; }; - 4B193982133F31CB00547810 /* JackMidiWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */; }; - 4B193983133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */; }; - 4B193984133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */; }; - 4B193985133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */; }; - 4B193986133F31CB00547810 /* JackMidiSendQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193976133F31CB00547810 /* JackMidiSendQueue.h */; }; - 4B193987133F31CB00547810 /* JackMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193977133F31CB00547810 /* JackMidiUtil.cpp */; }; - 4B193988133F31CB00547810 /* JackMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193978133F31CB00547810 /* JackMidiUtil.h */; }; - 4B193989133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */; }; - 4B19398A133F31CB00547810 /* JackMidiWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */; }; 4B193991133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; 4B193992133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; 4B193993133F321500547810 /* JackFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193990133F321500547810 /* JackFilters.h */; }; @@ -671,6 +635,42 @@ 4B94334A10A5E666002A187F /* systemdeps.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B94334910A5E666002A187F /* systemdeps.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B94334B10A5E666002A187F /* systemdeps.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B94334910A5E666002A187F /* systemdeps.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B95BCAE0D913073000F7695 /* control.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B95BCAD0D913073000F7695 /* control.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B97B6381344B3C100794F57 /* JackMidiAsyncQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */; }; + 4B97B6391344B3C300794F57 /* JackMidiAsyncQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193932133F311400547810 /* JackMidiAsyncQueue.h */; }; + 4B97B63A1344B3C700794F57 /* JackMidiAsyncWaitQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */; }; + 4B97B63D1344B3EC00794F57 /* JackMidiAsyncWaitQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */; }; + 4B97B63E1344B3F100794F57 /* JackMidiBufferReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */; }; + 4B97B6411344B40C00794F57 /* JackMidiBufferReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */; }; + 4B97B6531344B41E00794F57 /* JackMidiBufferWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */; }; + 4B97B6541344B42400794F57 /* JackMidiBufferWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */; }; + 4B97B6561344B43600794F57 /* JackMidiReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193946133F315200547810 /* JackMidiReadQueue.h */; }; + 4B97B6571344B43A00794F57 /* JackMidiReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193945133F315200547810 /* JackMidiReadQueue.cpp */; }; + 4B97B6581344B43F00794F57 /* JackMidiReceiveQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */; }; + 4B97B6591344B44800794F57 /* JackMidiReceiveQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */; }; + 4B97B65A1344B44F00794F57 /* JackMidiSendQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */; }; + 4B97B65B1344B45600794F57 /* JackMidiSendQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193976133F31CB00547810 /* JackMidiSendQueue.h */; }; + 4B97B65C1344B45D00794F57 /* JackMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193977133F31CB00547810 /* JackMidiUtil.cpp */; }; + 4B97B65D1344B46400794F57 /* JackMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193978133F31CB00547810 /* JackMidiUtil.h */; }; + 4B97B65E1344B46B00794F57 /* JackMidiWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */; }; + 4B97B65F1344B47100794F57 /* JackMidiWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */; }; + 4B97B6601344B48F00794F57 /* JackMidiAsyncQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193931133F311400547810 /* JackMidiAsyncQueue.cpp */; }; + 4B97B6611344B49500794F57 /* JackMidiAsyncQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193932133F311400547810 /* JackMidiAsyncQueue.h */; }; + 4B97B6621344B49C00794F57 /* JackMidiAsyncWaitQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19393B133F313000547810 /* JackMidiAsyncWaitQueue.cpp */; }; + 4B97B6631344B4A800794F57 /* JackMidiAsyncWaitQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19393C133F313000547810 /* JackMidiAsyncWaitQueue.h */; }; + 4B97B6641344B4AE00794F57 /* JackMidiBufferReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19395D133F317300547810 /* JackMidiBufferReadQueue.cpp */; }; + 4B97B6651344B4B500794F57 /* JackMidiBufferReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19395E133F317300547810 /* JackMidiBufferReadQueue.h */; }; + 4B97B6671344B4C700794F57 /* JackMidiBufferWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193969133F319000547810 /* JackMidiBufferWriteQueue.h */; }; + 4B97B6691344B4CE00794F57 /* JackMidiBufferWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193968133F319000547810 /* JackMidiBufferWriteQueue.cpp */; }; + 4B97B66E1344B4D500794F57 /* JackMidiReadQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193945133F315200547810 /* JackMidiReadQueue.cpp */; }; + 4B97B66F1344B4DC00794F57 /* JackMidiReadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193946133F315200547810 /* JackMidiReadQueue.h */; }; + 4B97B6701344B4E300794F57 /* JackMidiReceiveQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193973133F31CB00547810 /* JackMidiReceiveQueue.cpp */; }; + 4B97B6711344B4EA00794F57 /* JackMidiReceiveQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193974133F31CB00547810 /* JackMidiReceiveQueue.h */; }; + 4B97B6721344B4F000794F57 /* JackMidiSendQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193975133F31CB00547810 /* JackMidiSendQueue.cpp */; }; + 4B97B6781344B50800794F57 /* JackMidiSendQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193976133F31CB00547810 /* JackMidiSendQueue.h */; }; + 4B97B6791344B50F00794F57 /* JackMidiUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193977133F31CB00547810 /* JackMidiUtil.cpp */; }; + 4B97B67A1344B51600794F57 /* JackMidiUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B193978133F31CB00547810 /* JackMidiUtil.h */; }; + 4B97B67B1344B51D00794F57 /* JackMidiWriteQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B193979133F31CB00547810 /* JackMidiWriteQueue.cpp */; }; + 4B97B67C1344B52800794F57 /* JackMidiWriteQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19397A133F31CB00547810 /* JackMidiWriteQueue.h */; }; 4B9A25B50DBF8330006E9FBC /* JackError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9A25B30DBF8330006E9FBC /* JackError.cpp */; }; 4B9A25B60DBF8330006E9FBC /* JackError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9A25B30DBF8330006E9FBC /* JackError.cpp */; }; 4B9A26010DBF8584006E9FBC /* jslist.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B9A26000DBF8584006E9FBC /* jslist.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -3454,6 +3454,15 @@ 4B8A38C4117B814000664E07 /* JackSocketServerNotifyChannel.h in Headers */, 4B5160AA13215ED900BB7DCB /* systemdeps.h in Headers */, 4B193995133F321500547810 /* JackFilters.h in Headers */, + 4B97B6611344B49500794F57 /* JackMidiAsyncQueue.h in Headers */, + 4B97B6631344B4A800794F57 /* JackMidiAsyncWaitQueue.h in Headers */, + 4B97B6651344B4B500794F57 /* JackMidiBufferReadQueue.h in Headers */, + 4B97B6671344B4C700794F57 /* JackMidiBufferWriteQueue.h in Headers */, + 4B97B66F1344B4DC00794F57 /* JackMidiReadQueue.h in Headers */, + 4B97B6711344B4EA00794F57 /* JackMidiReceiveQueue.h in Headers */, + 4B97B6781344B50800794F57 /* JackMidiSendQueue.h in Headers */, + 4B97B67A1344B51600794F57 /* JackMidiUtil.h in Headers */, + 4B97B67C1344B52800794F57 /* JackMidiWriteQueue.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3917,6 +3926,15 @@ 4B2209E712F6BC0300E5DC26 /* JackSocket.h in Headers */, 4B2209EA12F6BC1600E5DC26 /* JackSocketNotifyChannel.h in Headers */, 4B193992133F321500547810 /* JackFilters.h in Headers */, + 4B97B6391344B3C300794F57 /* JackMidiAsyncQueue.h in Headers */, + 4B97B63D1344B3EC00794F57 /* JackMidiAsyncWaitQueue.h in Headers */, + 4B97B6411344B40C00794F57 /* JackMidiBufferReadQueue.h in Headers */, + 4B97B6541344B42400794F57 /* JackMidiBufferWriteQueue.h in Headers */, + 4B97B6561344B43600794F57 /* JackMidiReadQueue.h in Headers */, + 4B97B6591344B44800794F57 /* JackMidiReceiveQueue.h in Headers */, + 4B97B65B1344B45600794F57 /* JackMidiSendQueue.h in Headers */, + 4B97B65D1344B46400794F57 /* JackMidiUtil.h in Headers */, + 4B97B65F1344B47100794F57 /* JackMidiWriteQueue.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4152,15 +4170,6 @@ 4B370A3F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */, 4B370A41133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */, 4B370A43133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */, - 4B193936133F311400547810 /* JackMidiAsyncQueue.h in Headers */, - 4B193940133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */, - 4B19394A133F315300547810 /* JackMidiReadQueue.h in Headers */, - 4B193962133F317300547810 /* JackMidiBufferReadQueue.h in Headers */, - 4B19396D133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */, - 4B193984133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */, - 4B193986133F31CB00547810 /* JackMidiSendQueue.h in Headers */, - 4B193988133F31CB00547810 /* JackMidiUtil.h in Headers */, - 4B19398A133F31CB00547810 /* JackMidiWriteQueue.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4229,15 +4238,6 @@ 4B370A2F133DD7E300237B68 /* JackCoreMidiUtil.h in Headers */, 4B370A31133DD7E300237B68 /* JackCoreMidiVirtualInputPort.h in Headers */, 4B370A33133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.h in Headers */, - 4B193934133F311400547810 /* JackMidiAsyncQueue.h in Headers */, - 4B19393E133F313000547810 /* JackMidiAsyncWaitQueue.h in Headers */, - 4B193948133F315300547810 /* JackMidiReadQueue.h in Headers */, - 4B193960133F317300547810 /* JackMidiBufferReadQueue.h in Headers */, - 4B19396B133F319000547810 /* JackMidiBufferWriteQueue.h in Headers */, - 4B19397C133F31CB00547810 /* JackMidiReceiveQueue.h in Headers */, - 4B19397E133F31CB00547810 /* JackMidiSendQueue.h in Headers */, - 4B193980133F31CB00547810 /* JackMidiUtil.h in Headers */, - 4B193982133F31CB00547810 /* JackMidiWriteQueue.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6885,6 +6885,15 @@ 4B8A38AE117B811100664E07 /* JackSocketNotifyChannel.cpp in Sources */, 4B8A38B1117B812D00664E07 /* JackSocketServerChannel.cpp in Sources */, 4B8A38B2117B813400664E07 /* JackSocketServerNotifyChannel.cpp in Sources */, + 4B97B6601344B48F00794F57 /* JackMidiAsyncQueue.cpp in Sources */, + 4B97B6621344B49C00794F57 /* JackMidiAsyncWaitQueue.cpp in Sources */, + 4B97B6641344B4AE00794F57 /* JackMidiBufferReadQueue.cpp in Sources */, + 4B97B6691344B4CE00794F57 /* JackMidiBufferWriteQueue.cpp in Sources */, + 4B97B66E1344B4D500794F57 /* JackMidiReadQueue.cpp in Sources */, + 4B97B6701344B4E300794F57 /* JackMidiReceiveQueue.cpp in Sources */, + 4B97B6721344B4F000794F57 /* JackMidiSendQueue.cpp in Sources */, + 4B97B6791344B50F00794F57 /* JackMidiUtil.cpp in Sources */, + 4B97B67B1344B51D00794F57 /* JackMidiWriteQueue.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7337,6 +7346,15 @@ 4B2209E312F6BBF500E5DC26 /* JackSocketServerNotifyChannel.cpp in Sources */, 4B2209E612F6BC0200E5DC26 /* JackSocket.cpp in Sources */, 4B2209E912F6BC1500E5DC26 /* JackSocketNotifyChannel.cpp in Sources */, + 4B97B6381344B3C100794F57 /* JackMidiAsyncQueue.cpp in Sources */, + 4B97B63A1344B3C700794F57 /* JackMidiAsyncWaitQueue.cpp in Sources */, + 4B97B63E1344B3F100794F57 /* JackMidiBufferReadQueue.cpp in Sources */, + 4B97B6531344B41E00794F57 /* JackMidiBufferWriteQueue.cpp in Sources */, + 4B97B6571344B43A00794F57 /* JackMidiReadQueue.cpp in Sources */, + 4B97B6581344B43F00794F57 /* JackMidiReceiveQueue.cpp in Sources */, + 4B97B65A1344B44F00794F57 /* JackMidiSendQueue.cpp in Sources */, + 4B97B65C1344B45D00794F57 /* JackMidiUtil.cpp in Sources */, + 4B97B65E1344B46B00794F57 /* JackMidiWriteQueue.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7575,15 +7593,6 @@ 4B370A3E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */, 4B370A40133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */, 4B370A42133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */, - 4B193935133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */, - 4B19393F133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */, - 4B193949133F315300547810 /* JackMidiReadQueue.cpp in Sources */, - 4B193961133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */, - 4B19396C133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */, - 4B193983133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */, - 4B193985133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */, - 4B193987133F31CB00547810 /* JackMidiUtil.cpp in Sources */, - 4B193989133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7657,15 +7666,6 @@ 4B370A2E133DD7E300237B68 /* JackCoreMidiUtil.cpp in Sources */, 4B370A30133DD7E300237B68 /* JackCoreMidiVirtualInputPort.cpp in Sources */, 4B370A32133DD7E300237B68 /* JackCoreMidiVirtualOutputPort.cpp in Sources */, - 4B193933133F311400547810 /* JackMidiAsyncQueue.cpp in Sources */, - 4B19393D133F313000547810 /* JackMidiAsyncWaitQueue.cpp in Sources */, - 4B193947133F315300547810 /* JackMidiReadQueue.cpp in Sources */, - 4B19395F133F317300547810 /* JackMidiBufferReadQueue.cpp in Sources */, - 4B19396A133F319000547810 /* JackMidiBufferWriteQueue.cpp in Sources */, - 4B19397B133F31CB00547810 /* JackMidiReceiveQueue.cpp in Sources */, - 4B19397D133F31CB00547810 /* JackMidiSendQueue.cpp in Sources */, - 4B19397F133F31CB00547810 /* JackMidiUtil.cpp in Sources */, - 4B193981133F31CB00547810 /* JackMidiWriteQueue.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From a871bb2496c14c125c3c7007441874a1dd60101e Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 31 Mar 2011 18:22:52 +0200 Subject: [PATCH 53/58] Correct JackWinMMEInputPort.cpp. --- common/shm.h | 2 +- windows/jackd.workspace | 4 +-- windows/winmme/JackWinMMEDriver.cpp | 39 ++++++++++++++++++------- windows/winmme/JackWinMMEInputPort.cpp | 29 ++++++++++++++---- windows/winmme/JackWinMMEInputPort.h | 6 ++-- windows/winmme/JackWinMMEOutputPort.cpp | 11 ++++++- 6 files changed, 68 insertions(+), 23 deletions(-) diff --git a/common/shm.h b/common/shm.h index ed5a953f..f1edc8ed 100644 --- a/common/shm.h +++ b/common/shm.h @@ -14,7 +14,7 @@ extern "C" { #endif -#define MAX_SERVERS 8 /* maximum concurrent servers */ +#define MAX_SERVERS 64 /* maximum concurrent servers */ #define MAX_SHM_ID 256 /* generally about 16 per server */ #define JACK_SERVER_NAME_SIZE 256 /* maximum length of server name */ #define JACK_SHM_MAGIC 0x4a41434b /* shm magic number: "JACK" */ diff --git a/windows/jackd.workspace b/windows/jackd.workspace index 4384140e..c424a7b4 100644 --- a/windows/jackd.workspace +++ b/windows/jackd.workspace @@ -5,7 +5,7 @@ - + @@ -56,7 +56,7 @@ - + diff --git a/windows/winmme/JackWinMMEDriver.cpp b/windows/winmme/JackWinMMEDriver.cpp index 455118a2..96bebda1 100644 --- a/windows/winmme/JackWinMMEDriver.cpp +++ b/windows/winmme/JackWinMMEDriver.cpp @@ -52,6 +52,9 @@ JackWinMMEDriver::Attach() latency_range.max = latency; latency_range.min = latency; + jack_info("JackWinMMEDriver::Attach - fCaptureChannels %d", fCaptureChannels); + jack_info("JackWinMMEDriver::Attach - fPlaybackChannels %d", fPlaybackChannels); + // Inputs for (int i = 0; i < fCaptureChannels; i++) { JackWinMMEInputPort *input_port = input_ports[i]; @@ -130,9 +133,13 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels, { const char *client_name = fClientControl.fName; int input_count = 0; + int output_count = 0; int num_potential_inputs = midiInGetNumDevs(); int num_potential_outputs = midiOutGetNumDevs(); - int output_count = 0; + + jack_info("JackWinMMEDriver::Open - num_potential_inputs %d", num_potential_inputs); + jack_info("JackWinMMEDriver::Open - num_potential_outputs %d", num_potential_outputs); + if (num_potential_inputs) { try { input_ports = new JackWinMMEInputPort *[num_potential_inputs]; @@ -175,6 +182,11 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels, output_count++; } } + + jack_info("JackWinMMEDriver::Open - input_count %d", input_count); + jack_info("JackWinMMEDriver::Open - output_count %d", output_count); + + if (! (input_count || output_count)) { jack_error("JackWinMMEDriver::Open - no WinMME inputs or outputs " "allocated."); @@ -200,10 +212,25 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels, int JackWinMMEDriver::Read() { + jack_nframes_t buffer_size = fEngineControl->fBufferSize; for (int i = 0; i < fCaptureChannels; i++) { input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); } + + return 0; +} + + +int +JackWinMMEDriver::Write() +{ + /* + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < fPlaybackChannels; i++) { + output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size); + } + */ return 0; } @@ -287,16 +314,6 @@ JackWinMMEDriver::Stop() return result; } -int -JackWinMMEDriver::Write() -{ - jack_nframes_t buffer_size = fEngineControl->fBufferSize; - for (int i = 0; i < fPlaybackChannels; i++) { - output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size); - } - return 0; -} - #ifdef __cplusplus extern "C" { diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp index 0adf3bec..6c573cf0 100644 --- a/windows/winmme/JackWinMMEInputPort.cpp +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -78,6 +78,23 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, goto unprepare_header; } + MIDIINCAPS capabilities; + char *name_tmp; + result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities)); + /* + Devin : FIXME + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", + result); + name_tmp = driver_name; + } else { + name_tmp = capabilities.szPname; + } + */ + snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", alias_name, driver_name, + index + 1); + snprintf(name, sizeof(name) - 1, "%s:capture_%d", client_name, index + 1); + jack_event = 0; started = false; write_queue_ptr.release(); @@ -147,6 +164,12 @@ JackWinMMEInputPort::GetAlias() return alias; } +const char * +JackWinMMEInputPort::GetName() +{ + return name; +} + void JackWinMMEInputPort::GetErrorString(MMRESULT error, LPTSTR text) { @@ -156,12 +179,6 @@ JackWinMMEInputPort::GetErrorString(MMRESULT error, LPTSTR text) } } -const char * -JackWinMMEInputPort::GetName() -{ - return name; -} - void JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames) { diff --git a/windows/winmme/JackWinMMEInputPort.h b/windows/winmme/JackWinMMEInputPort.h index dd4c53f1..568bd9f5 100644 --- a/windows/winmme/JackWinMMEInputPort.h +++ b/windows/winmme/JackWinMMEInputPort.h @@ -50,15 +50,17 @@ namespace Jack { MMRESULT result); char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + HMIDIIN handle; jack_midi_event_t *jack_event; - char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - bool started; jack_midi_data_t *sysex_buffer; MIDIHDR sysex_header; JackMidiAsyncQueue *thread_queue; JackMidiBufferWriteQueue *write_queue; + bool started; + public: JackWinMMEInputPort(const char *alias_name, const char *client_name, diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index eb9ba49e..0b05cb2c 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -88,7 +88,7 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, index + 1); snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1); thread_ptr.release(); - thread_queue_ptr.release(); + thread_queue_ptr.release(); return; destroy_thread_queue_semaphore: @@ -132,9 +132,15 @@ bool JackWinMMEOutputPort::Execute() { for (;;) { + jack_log("JackWinMMEOutputPort::Execute TOTO"); + JackSleep(100000); + if (! Wait(thread_queue_semaphore)) { + jack_log("JackWinMMEOutputPort::Execute BREAK"); + break; } + /* jack_midi_event_t *event = thread_queue->DequeueEvent(); if (! event) { break; @@ -214,6 +220,7 @@ JackWinMMEOutputPort::Execute() "midiOutUnprepareHeader", result); break; } + */ } stop_execution: return false; @@ -358,6 +365,8 @@ JackWinMMEOutputPort::Stop() bool JackWinMMEOutputPort::Wait(HANDLE semaphore) { + jack_log("JackWinMMEOutputPort::Wait %d", semaphore); + DWORD result = WaitForSingleObject(semaphore, INFINITE); switch (result) { case WAIT_FAILED: From 173f1629a95298dfb0692044dd9b932245685d3d Mon Sep 17 00:00:00 2001 From: Devin Anderson Date: Thu, 31 Mar 2011 10:37:17 -0700 Subject: [PATCH 54/58] Fix problems sent by Stephane. --- windows/winmme/JackWinMMEInputPort.cpp | 20 ++++++++-- windows/winmme/JackWinMMEOutputPort.cpp | 53 +++++++++---------------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp index 0adf3bec..79d1c8af 100644 --- a/windows/winmme/JackWinMMEInputPort.cpp +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -55,7 +55,8 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, std::auto_ptr write_queue_ptr(write_queue); sysex_buffer = new jack_midi_data_t[max_bytes]; char error_message[MAXERRORLENGTH]; - MMRESULT result = midiInOpen(&handle, index, (DWORD)HandleMidiInputEvent, (DWORD)this, + MMRESULT result = midiInOpen(&handle, index, (DWORD)HandleMidiInputEvent, + (DWORD)this, CALLBACK_FUNCTION | MIDI_IO_STATUS); if (result != MMSYSERR_NOERROR) { GetErrorString(result, error_message); @@ -77,7 +78,19 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, GetErrorString(result, error_message); goto unprepare_header; } - + MIDIINCAPS capabilities; + char *name_tmp; + result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities)); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [constructor]", "midiInGetDevCaps", + result); + name_tmp = driver_name; + } else { + name_tmp = capabilities.szPname; + } + snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", alias_name, name_tmp, + index + 1); + snprintf(name, sizeof(name) - 1, "%s:capture_%d", client_name, index + 1); jack_event = 0; started = false; write_queue_ptr.release(); @@ -163,7 +176,8 @@ JackWinMMEInputPort::GetName() } void -JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames) +JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) { write_queue->ResetMidiBuffer(port_buffer, frames); if (! jack_event) { diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index eb9ba49e..ff001d96 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -55,10 +55,10 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, thread = new JackThread(this); std::auto_ptr thread_ptr(thread); char error_message[MAXERRORLENGTH]; - MMRESULT result = midiOutOpen(&handle, index, (DWORD)HandleMessageEvent, (DWORD)this, - CALLBACK_FUNCTION); + MMRESULT result = midiOutOpen(&handle, index, (DWORD)HandleMessageEvent, + (DWORD)this, CALLBACK_FUNCTION); if (result != MMSYSERR_NOERROR) { - GetErrorString(result, error_message); + GetMMErrorString(result, error_message); goto raise_exception; } thread_queue_semaphore = CreateSemaphore(NULL, 0, max_messages, NULL); @@ -74,8 +74,6 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, MIDIOUTCAPS capabilities; char *name_tmp; result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities)); - /* - Devin : FIXME if (result != MMSYSERR_NOERROR) { WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", result); @@ -83,12 +81,11 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, } else { name_tmp = capabilities.szPname; } - */ - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, driver_name, + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, name_tmp, index + 1); snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1); thread_ptr.release(); - thread_queue_ptr.release(); + thread_queue_ptr.release(); return; destroy_thread_queue_semaphore: @@ -219,6 +216,12 @@ JackWinMMEOutputPort::Execute() return false; } +const char * +JackWinMMEOutputPort::GetAlias() +{ + return alias; +} + void JackWinMMEOutputPort::GetMMErrorString(MMRESULT error, LPTSTR text) { @@ -228,18 +231,21 @@ JackWinMMEOutputPort::GetMMErrorString(MMRESULT error, LPTSTR text) } } +const char * +JackWinMMEOutputPort::GetName() +{ + return name; +} + void JackWinMMEOutputPort::GetOSErrorString(LPTSTR text) { DWORD error = GetLastError(); - /* - Devin: FIXME if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &text, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), text, MAXERRORLENGTH, NULL)) { snprintf(text, MAXERRORLENGTH, "Unknown OS error code '%d'", error); } - */ } void @@ -385,27 +391,6 @@ void JackWinMMEOutputPort::WriteOSError(const char *jack_func, const char *os_func) { char error_message[MAXERRORLENGTH]; - // Devin : FIXME - //GetOSErrorString(result, error_message); + GetOSErrorString(error_message); jack_error("%s - %s: %s", jack_func, os_func, error_message); } - -const char * -JackWinMMEOutputPort::GetAlias() -{ - return alias; -} -const char * -JackWinMMEOutputPort::GetName() -{ - return name; -} - -void -JackWinMMEOutputPort::GetErrorString(MMRESULT error, LPTSTR text) -{ - MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); - if (result != MMSYSERR_NOERROR) { - snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); - } -} From a3318119b3df546061b6960d7d6a4a8752265383 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 31 Mar 2011 19:52:14 +0200 Subject: [PATCH 55/58] Correct MIDI ports memory management in JackNetDriver. --- common/JackNetDriver.cpp | 52 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/common/JackNetDriver.cpp b/common/JackNetDriver.cpp index d2327eb1..66e4f7dd 100644 --- a/common/JackNetDriver.cpp +++ b/common/JackNetDriver.cpp @@ -151,8 +151,16 @@ namespace Jack //allocate midi ports lists fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels]; fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels]; - assert ( fMidiCapturePortList ); - assert ( fMidiPlaybackPortList ); + + assert(fMidiCapturePortList); + assert(fMidiPlaybackPortList); + + for (uint midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { + fMidiCapturePortList[midi_port_index] = NULL; + } + for (uint midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { + fMidiPlaybackPortList[midi_port_index] = NULL; + } //register jack ports if ( AllocPorts() != 0 ) @@ -362,20 +370,32 @@ namespace Jack { jack_log ( "JackNetDriver::FreePorts" ); - int audio_port_index; - uint midi_port_index; - for ( audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++ ) - if (fCapturePortList[audio_port_index] > 0) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fCapturePortList[audio_port_index] ); - for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) - if (fPlaybackPortList[audio_port_index] > 0) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fPlaybackPortList[audio_port_index] ); - for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) - if (fMidiCapturePortList[midi_port_index] > 0) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiCapturePortList[midi_port_index] ); - for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) - if (fMidiPlaybackPortList[midi_port_index] > 0) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index] ); + for (int audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++) { + if (fCapturePortList[audio_port_index] > 0) { + fGraphManager->ReleasePort ( fClientControl.fRefNum, fCapturePortList[audio_port_index]); + } + } + + for (int audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) { + if (fPlaybackPortList[audio_port_index] > 0) { + fGraphManager->ReleasePort ( fClientControl.fRefNum, fPlaybackPortList[audio_port_index]); + } + } + + for (uint midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { + if (fMidiCapturePortList && fMidiCapturePortList[midi_port_index] > 0) { + fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiCapturePortList[midi_port_index]); + } + } + + for (uint midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { + if (fMidiPlaybackPortList && fMidiPlaybackPortList[midi_port_index] > 0) { + fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index]); + } + } + // Clear MIDI channels + fParams.fSendMidiChannels = 0; + fParams.fReturnMidiChannels = 0; return 0; } From 9ce3e4fc5224eda16c677497e070150f9af8b918 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 31 Mar 2011 21:15:42 +0200 Subject: [PATCH 56/58] Correct test.cpp. --- tests/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test.cpp b/tests/test.cpp index 1a6e4a05..7c3f5b5e 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -972,14 +972,14 @@ int main (int argc, char *argv[]) float factor = 0.5f; old_buffer_size = jack_get_buffer_size(client1); - Log("Testing BufferSize change & Callback...\n--> Current buffer size : %i.\n", old_buffer_size); + Log("Testing BufferSize change & Callback...\n--> Current buffer size : %d.\n", old_buffer_size); linebuf = linecount; if (jack_set_buffer_size(client1, (jack_nframes_t)(old_buffer_size * factor)) < 0) { printf("!!! ERROR !!! jack_set_buffer_size fails !\n"); } jack_sleep(1 * 1000); cur_buffer_size = jack_get_buffer_size(client1); - if ((old_buffer_size * factor) != cur_buffer_size) { + if (abs((old_buffer_size * factor) - cur_buffer_size) > 5) { // Tolerance needed for dummy driver... printf("!!! ERROR !!! Buffer size has not been changed !\n"); printf("!!! Maybe jack was compiled without the '--enable-resize' flag...\n"); } else { From eb24bdac93a1871785ca4db191f5817267f0cc46 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 31 Mar 2011 22:03:34 +0200 Subject: [PATCH 57/58] New JackWinMMEPort class to factorize some code. --- windows/winmme/JackWinMMEInputPort.cpp | 59 +++++++++------------ windows/winmme/JackWinMMEInputPort.h | 25 ++++----- windows/winmme/JackWinMMEOutputPort.cpp | 67 +++++++----------------- windows/winmme/JackWinMMEOutputPort.h | 31 +++-------- windows/winmme/JackWinMMEPort.cpp | 69 +++++++++++++++++++++++++ windows/winmme/JackWinMMEPort.h | 58 +++++++++++++++++++++ 6 files changed, 185 insertions(+), 124 deletions(-) create mode 100644 windows/winmme/JackWinMMEPort.cpp create mode 100644 windows/winmme/JackWinMMEPort.h diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp index ae526cbc..9d72cc42 100644 --- a/windows/winmme/JackWinMMEInputPort.cpp +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -59,7 +59,7 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, (DWORD)this, CALLBACK_FUNCTION | MIDI_IO_STATUS); if (result != MMSYSERR_NOERROR) { - GetErrorString(result, error_message); + GetInErrorString(result, error_message); goto delete_sysex_buffer; } sysex_header.dwBufferLength = max_bytes; @@ -70,19 +70,19 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, sysex_header.lpNext = 0; result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - GetErrorString(result, error_message); + GetInErrorString(result, error_message); goto close_handle; } result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - GetErrorString(result, error_message); + GetInErrorString(result, error_message); goto unprepare_header; } MIDIINCAPS capabilities; char *name_tmp; result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities)); if (result != MMSYSERR_NOERROR) { - WriteError("JackWinMMEInputPort [constructor]", "midiInGetDevCaps", + WriteInError("JackWinMMEInputPort [constructor]", "midiInGetDevCaps", result); name_tmp = driver_name; } else { @@ -100,13 +100,13 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, unprepare_header: result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - WriteError("JackWinMMEInputPort [constructor]", + WriteInError("JackWinMMEInputPort [constructor]", "midiInUnprepareHeader", result); } close_handle: result = midiInClose(handle); if (result != MMSYSERR_NOERROR) { - WriteError("JackWinMMEInputPort [constructor]", "midiInClose", result); + WriteInError("JackWinMMEInputPort [constructor]", "midiInClose", result); } delete_sysex_buffer: delete[] sysex_buffer; @@ -118,16 +118,16 @@ JackWinMMEInputPort::~JackWinMMEInputPort() Stop(); MMRESULT result = midiInReset(handle); if (result != MMSYSERR_NOERROR) { - WriteError("JackWinMMEInputPort [destructor]", "midiInReset", result); + WriteInError("JackWinMMEInputPort [destructor]", "midiInReset", result); } result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - WriteError("JackWinMMEInputPort [destructor]", "midiInUnprepareHeader", + WriteInError("JackWinMMEInputPort [destructor]", "midiInUnprepareHeader", result); } result = midiInClose(handle); if (result != MMSYSERR_NOERROR) { - WriteError("JackWinMMEInputPort [destructor]", "midiInClose", result); + WriteInError("JackWinMMEInputPort [destructor]", "midiInClose", result); } delete[] sysex_buffer; delete thread_queue; @@ -154,27 +154,6 @@ JackWinMMEInputPort::EnqueueMessage(jack_nframes_t time, size_t length, } } -const char * -JackWinMMEInputPort::GetAlias() -{ - return alias; -} - -const char * -JackWinMMEInputPort::GetName() -{ - return name; -} - -void -JackWinMMEInputPort::GetErrorString(MMRESULT error, LPTSTR text) -{ - MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); - if (result != MMSYSERR_NOERROR) { - snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); - } -} - void JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames) @@ -254,7 +233,7 @@ JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) MMRESULT result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - WriteError("JackWinMMEInputPort::ProcessWinMME", "midiInAddBuffer", + WriteInError("JackWinMMEInputPort::ProcessWinMME", "midiInAddBuffer", result); } break; @@ -274,7 +253,7 @@ JackWinMMEInputPort::Start() MMRESULT result = midiInStart(handle); started = result == MMSYSERR_NOERROR; if (! started) { - WriteError("JackWinMMEInputPort::Start", "midiInStart", result); + WriteInError("JackWinMMEInputPort::Start", "midiInStart", result); } } return started; @@ -287,17 +266,27 @@ JackWinMMEInputPort::Stop() MMRESULT result = midiInStop(handle); started = result != MMSYSERR_NOERROR; if (started) { - WriteError("JackWinMMEInputPort::Stop", "midiInStop", result); + WriteInError("JackWinMMEInputPort::Stop", "midiInStop", result); } } return ! started; } void -JackWinMMEInputPort::WriteError(const char *jack_func, const char *mm_func, +JackWinMMEInputPort::GetInErrorString(MMRESULT error, LPTSTR text) +{ + MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); + if (result != MMSYSERR_NOERROR) { + snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); + } +} + +void +JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func, MMRESULT result) { char error_message[MAXERRORLENGTH]; - GetErrorString(result, error_message); + GetInErrorString(result, error_message); jack_error("%s - %s: %s", jack_func, mm_func, error_message); } + diff --git a/windows/winmme/JackWinMMEInputPort.h b/windows/winmme/JackWinMMEInputPort.h index 568bd9f5..a7305340 100644 --- a/windows/winmme/JackWinMMEInputPort.h +++ b/windows/winmme/JackWinMMEInputPort.h @@ -24,10 +24,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackMidiAsyncQueue.h" #include "JackMidiBufferWriteQueue.h" +#include "JackWinMMEPort.h" namespace Jack { - class JackWinMMEInputPort { + class JackWinMMEInputPort : public JackRunnableInterface { private: @@ -39,19 +40,10 @@ namespace Jack { EnqueueMessage(jack_nframes_t time, size_t length, jack_midi_data_t *data); - void - GetErrorString(MMRESULT error, LPTSTR text); void ProcessWinMME(UINT message, DWORD param1, DWORD param2); - void - WriteError(const char *jack_func, const char *mm_func, - MMRESULT result); - - char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - HMIDIIN handle; jack_midi_event_t *jack_event; jack_midi_data_t *sysex_buffer; @@ -61,6 +53,13 @@ namespace Jack { bool started; + void + WriteOSError(const char *jack_func, const char *os_func); + + void + WriteInError(const char *jack_func, const char *mm_func, + MMRESULT result); + public: JackWinMMEInputPort(const char *alias_name, const char *client_name, @@ -69,12 +68,6 @@ namespace Jack { ~JackWinMMEInputPort(); - const char * - GetAlias(); - - const char * - GetName(); - void ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index 7af41ca7..dcc1dd79 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -58,7 +58,7 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, MMRESULT result = midiOutOpen(&handle, index, (DWORD)HandleMessageEvent, (DWORD)this, CALLBACK_FUNCTION); if (result != MMSYSERR_NOERROR) { - GetMMErrorString(result, error_message); + GetOutErrorString(result, error_message); goto raise_exception; } thread_queue_semaphore = CreateSemaphore(NULL, 0, max_messages, NULL); @@ -75,7 +75,7 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, char *name_tmp; result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities)); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", + WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", result); name_tmp = driver_name; } else { @@ -95,7 +95,7 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, close_handle: result = midiOutClose(handle); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutClose", + WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutClose", result); } raise_exception: @@ -107,12 +107,12 @@ JackWinMMEOutputPort::~JackWinMMEOutputPort() Stop(); MMRESULT result = midiOutReset(handle); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort [destructor]", "midiOutReset", + WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutReset", result); } result = midiOutClose(handle); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort [destructor]", "midiOutClose", + WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutClose", result); } if (! CloseHandle(sysex_semaphore)) { @@ -179,7 +179,7 @@ JackWinMMEOutputPort::Execute() message |= (DWORD) data[0]; result = midiOutShortMsg(handle, message); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort::Execute", + WriteOutError("JackWinMMEOutputPort::Execute", "midiOutShortMsg", result); } continue; @@ -193,13 +193,13 @@ JackWinMMEOutputPort::Execute() header.lpData = (LPSTR)data; result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort::Execute", + WriteOutError("JackWinMMEOutputPort::Execute", "midiOutPrepareHeader", result); continue; } result = midiOutLongMsg(handle, &header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort::Execute", "midiOutLongMsg", + WriteOutError("JackWinMMEOutputPort::Execute", "midiOutLongMsg", result); continue; } @@ -213,7 +213,7 @@ JackWinMMEOutputPort::Execute() result = midiOutUnprepareHeader(handle, &header, sizeof(MIDIHDR)); if (result != MMSYSERR_NOERROR) { - WriteMMError("JackWinMMEOutputPort::Execute", + WriteOutError("JackWinMMEOutputPort::Execute", "midiOutUnprepareHeader", result); break; } @@ -223,38 +223,6 @@ JackWinMMEOutputPort::Execute() return false; } -const char * -JackWinMMEOutputPort::GetAlias() -{ - return alias; -} - -void -JackWinMMEOutputPort::GetMMErrorString(MMRESULT error, LPTSTR text) -{ - MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH); - if (result != MMSYSERR_NOERROR) { - snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error); - } -} - -const char * -JackWinMMEOutputPort::GetName() -{ - return name; -} - -void -JackWinMMEOutputPort::GetOSErrorString(LPTSTR text) -{ - DWORD error = GetLastError(); - if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), text, - MAXERRORLENGTH, NULL)) { - snprintf(text, MAXERRORLENGTH, "Unknown OS error code '%d'", error); - } -} - void JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2) @@ -388,18 +356,19 @@ JackWinMMEOutputPort::Wait(HANDLE semaphore) } void -JackWinMMEOutputPort::WriteMMError(const char *jack_func, const char *mm_func, - MMRESULT result) +JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text) { - char error_message[MAXERRORLENGTH]; - GetMMErrorString(result, error_message); - jack_error("%s - %s: %s", jack_func, mm_func, error_message); + MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH); + if (result != MMSYSERR_NOERROR) { + snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error); + } } void -JackWinMMEOutputPort::WriteOSError(const char *jack_func, const char *os_func) +JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func, + MMRESULT result) { char error_message[MAXERRORLENGTH]; - GetOSErrorString(error_message); - jack_error("%s - %s: %s", jack_func, os_func, error_message); + GetMMErrorString(result, error_message); + jack_error("%s - %s: %s", jack_func, mm_func, error_message); } diff --git a/windows/winmme/JackWinMMEOutputPort.h b/windows/winmme/JackWinMMEOutputPort.h index c343ff55..36fa28b1 100644 --- a/windows/winmme/JackWinMMEOutputPort.h +++ b/windows/winmme/JackWinMMEOutputPort.h @@ -29,7 +29,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { - class JackWinMMEOutputPort: public JackRunnableInterface { + class JackWinMMEOutputPort : + public JackWinMMEPort, public JackRunnableInterface { private: @@ -37,12 +38,6 @@ namespace Jack { HandleMessageEvent(HMIDIOUT handle, UINT message, DWORD_PTR port, DWORD_PTR param1, DWORD_PTR param2); - void - GetMMErrorString(MMRESULT error, LPTSTR text); - - void - GetOSErrorString(LPTSTR text); - void HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2); @@ -52,16 +47,6 @@ namespace Jack { bool Wait(HANDLE semaphore); - void - WriteMMError(const char *jack_func, const char *mm_func, - MMRESULT result); - - void - WriteOSError(const char *jack_func, const char *os_func); - - char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - HMIDIOUT handle; JackMidiBufferReadQueue *read_queue; HANDLE sysex_semaphore; @@ -70,7 +55,11 @@ namespace Jack { HANDLE thread_queue_semaphore; void - GetErrorString(MMRESULT error, LPTSTR text); + GetOutErrorString(MMRESULT error, LPTSTR text); + + void + WriteOutError(const char *jack_func, const char *mm_func, + MMRESULT result); public: @@ -95,12 +84,6 @@ namespace Jack { bool Stop(); - const char * - GetAlias(); - - const char * - GetName(); - }; } diff --git a/windows/winmme/JackWinMMEPort.cpp b/windows/winmme/JackWinMMEPort.cpp new file mode 100644 index 00000000..577cfbeb --- /dev/null +++ b/windows/winmme/JackWinMMEPort.cpp @@ -0,0 +1,69 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackWinMMEPort.h" + +using Jack::JackWinMMEPort; + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackWinMMEPort::JackWinMMEPort(const char *alias_name, + const char *client_name, + const char *driver_name) +{} + +JackWinMMEPort::~JackWinMMEPort() +{} + +const JackWinMMEPort * +JackWinMMEOutputPort::GetAlias() +{ + return alias; +} + +const char * +JackWinMMEPort::GetName() +{ + return name; +} + +void +JackWinMMEPort::GetOSErrorString(LPTSTR text) +{ + DWORD error = GetLastError(); + if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), text, + MAXERRORLENGTH, NULL)) { + snprintf(text, MAXERRORLENGTH, "Unknown OS error code '%d'", error); + } +} + +void +JackWinMMEPort::WriteOSError(const char *jack_func, const char *os_func) +{ + char error_message[MAXERRORLENGTH]; + GetOSErrorString(error_message); + jack_error("%s - %s: %s", jack_func, os_func, error_message); +} + diff --git a/windows/winmme/JackWinMMEPort.h b/windows/winmme/JackWinMMEPort.h new file mode 100644 index 00000000..3b53fd5f --- /dev/null +++ b/windows/winmme/JackWinMMEPort.h @@ -0,0 +1,58 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackWinMMEPort__ +#define __JackWinMMEPort__ + +#include +#include + +namespace Jack { + + class JackWinMMEPort { + + protected: + + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + + public: + + JackWinMMEPort(const char *alias_name, const char *client_name, + const char *driver_name); + + ~JackWinMMEPort(); + + const char * + GetAlias(); + + const char * + GetName(); + + void + GetOSErrorString(LPTSTR text); + + void + GetInErrorString(MMRESULT error, LPTSTR text); + + }; + +} + +#endif From 8e55718c3834d458690315c4dba55c93ef6e6145 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Fri, 1 Apr 2011 10:45:55 +0200 Subject: [PATCH 58/58] Winmme driver compiles again. --- windows/jack_winmme.cbp | 7 +------ windows/libjackserver.cbp | 6 ++++++ windows/winmme/JackWinMMEInputPort.cpp | 4 +++- windows/winmme/JackWinMMEInputPort.h | 7 ++++--- windows/winmme/JackWinMMEOutputPort.cpp | 4 ++-- windows/winmme/JackWinMMEOutputPort.h | 4 +--- windows/winmme/JackWinMMEPort.cpp | 9 ++++----- windows/winmme/JackWinMMEPort.h | 9 +++++---- 8 files changed, 26 insertions(+), 24 deletions(-) diff --git a/windows/jack_winmme.cbp b/windows/jack_winmme.cbp index 7de61fa8..7fe93703 100644 --- a/windows/jack_winmme.cbp +++ b/windows/jack_winmme.cbp @@ -102,12 +102,6 @@ - - - - - - @@ -117,6 +111,7 @@ + diff --git a/windows/libjackserver.cbp b/windows/libjackserver.cbp index 89cd2dd3..84b8985d 100644 --- a/windows/libjackserver.cbp +++ b/windows/libjackserver.cbp @@ -150,8 +150,14 @@ + + + + + + diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp index 9d72cc42..c3b04453 100644 --- a/windows/winmme/JackWinMMEInputPort.cpp +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -78,16 +78,18 @@ JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, GetInErrorString(result, error_message); goto unprepare_header; } + MIDIINCAPS capabilities; char *name_tmp; result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities)); if (result != MMSYSERR_NOERROR) { WriteInError("JackWinMMEInputPort [constructor]", "midiInGetDevCaps", result); - name_tmp = driver_name; + name_tmp = (char*) driver_name; } else { name_tmp = capabilities.szPname; } + snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", alias_name, name_tmp, index + 1); snprintf(name, sizeof(name) - 1, "%s:capture_%d", client_name, index + 1); diff --git a/windows/winmme/JackWinMMEInputPort.h b/windows/winmme/JackWinMMEInputPort.h index a7305340..13eb32a6 100644 --- a/windows/winmme/JackWinMMEInputPort.h +++ b/windows/winmme/JackWinMMEInputPort.h @@ -28,7 +28,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { - class JackWinMMEInputPort : public JackRunnableInterface { + class JackWinMMEInputPort : public JackWinMMEPort { private: @@ -53,13 +53,14 @@ namespace Jack { bool started; - void - WriteOSError(const char *jack_func, const char *os_func); void WriteInError(const char *jack_func, const char *mm_func, MMRESULT result); + void + GetInErrorString(MMRESULT error, LPTSTR text); + public: JackWinMMEInputPort(const char *alias_name, const char *client_name, diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp index dcc1dd79..51a9ddbd 100644 --- a/windows/winmme/JackWinMMEOutputPort.cpp +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -77,7 +77,7 @@ JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, if (result != MMSYSERR_NOERROR) { WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", result); - name_tmp = driver_name; + name_tmp = (char*)driver_name; } else { name_tmp = capabilities.szPname; } @@ -369,6 +369,6 @@ JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func, MMRESULT result) { char error_message[MAXERRORLENGTH]; - GetMMErrorString(result, error_message); + GetOutErrorString(result, error_message); jack_error("%s - %s: %s", jack_func, mm_func, error_message); } diff --git a/windows/winmme/JackWinMMEOutputPort.h b/windows/winmme/JackWinMMEOutputPort.h index 36fa28b1..d43eae35 100644 --- a/windows/winmme/JackWinMMEOutputPort.h +++ b/windows/winmme/JackWinMMEOutputPort.h @@ -20,12 +20,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackWinMMEOutputPort__ #define __JackWinMMEOutputPort__ -#include -#include - #include "JackMidiAsyncQueue.h" #include "JackMidiBufferReadQueue.h" #include "JackThread.h" +#include "JackWinMMEPort.h" namespace Jack { diff --git a/windows/winmme/JackWinMMEPort.cpp b/windows/winmme/JackWinMMEPort.cpp index 577cfbeb..46c4546b 100644 --- a/windows/winmme/JackWinMMEPort.cpp +++ b/windows/winmme/JackWinMMEPort.cpp @@ -21,6 +21,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include "JackWinMMEPort.h" +#include "JackError.h" using Jack::JackWinMMEPort; @@ -28,16 +29,14 @@ using Jack::JackWinMMEPort; // Class /////////////////////////////////////////////////////////////////////////////// -JackWinMMEPort::JackWinMMEPort(const char *alias_name, - const char *client_name, - const char *driver_name) +JackWinMMEPort::JackWinMMEPort() {} JackWinMMEPort::~JackWinMMEPort() {} -const JackWinMMEPort * -JackWinMMEOutputPort::GetAlias() +const char * +JackWinMMEPort::GetAlias() { return alias; } diff --git a/windows/winmme/JackWinMMEPort.h b/windows/winmme/JackWinMMEPort.h index 3b53fd5f..a544c4a6 100644 --- a/windows/winmme/JackWinMMEPort.h +++ b/windows/winmme/JackWinMMEPort.h @@ -20,8 +20,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackWinMMEPort__ #define __JackWinMMEPort__ -#include #include +#include + +#include "JackConstants.h" namespace Jack { @@ -34,8 +36,7 @@ namespace Jack { public: - JackWinMMEPort(const char *alias_name, const char *client_name, - const char *driver_name); + JackWinMMEPort(); ~JackWinMMEPort(); @@ -49,7 +50,7 @@ namespace Jack { GetOSErrorString(LPTSTR text); void - GetInErrorString(MMRESULT error, LPTSTR text); + WriteOSError(const char *jack_func, const char *os_func); };