diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index 272c1e66..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; } @@ -193,9 +212,9 @@ int JackAudioDriver::ProcessNull() JackDriver::CycleTakeBeginTime(); if (fEngineControl->fSyncMode) { - ProcessGraphSync(); + ProcessGraphSyncMaster(); } else { - ProcessGraphAsync(); + ProcessGraphAsyncMaster(); } // Keep end cycle time @@ -230,9 +249,9 @@ int JackAudioDriver::ProcessAsync() // Process graph if (fIsMaster) { - ProcessGraphAsync(); + ProcessGraphAsyncMaster(); } else { - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); + ProcessGraphAsyncSlave(); } // Keep end cycle time @@ -255,12 +274,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 +298,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 +354,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..83b0ba53 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(); @@ -58,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 ce860468..5fab3f4f 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -300,19 +300,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; @@ -395,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/JackDriver.h b/common/JackDriver.h index 68f937c2..45d695af 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); 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/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/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..247f852e 100644 --- a/common/JackLoopbackDriver.h +++ b/common/JackLoopbackDriver.h @@ -33,15 +33,24 @@ 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) - : JackAudioDriver("loopback", "", engine, table) + : JackAudioDriver("loopback", "loopback", engine, table) {} virtual ~JackLoopbackDriver() {} - int Process(); + virtual int ProcessRead(); + virtual int ProcessWrite(); }; } // end of namespace diff --git a/common/JackMidiAsyncQueue.cpp b/common/JackMidiAsyncQueue.cpp new file mode 100644 index 00000000..0cfa42dc --- /dev/null +++ b/common/JackMidiAsyncQueue.cpp @@ -0,0 +1,103 @@ +/* +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 * 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); + 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) { + 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; + 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)); + } + jack_ringbuffer_read_advance(byte_ring, + size * sizeof(jack_midi_data_t)); + } + return event; +} + +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 * sizeof(jack_midi_data_t))))) { + return BUFFER_FULL; + } + 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)); + 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 new file mode 100644 index 00000000..a9ac84ce --- /dev/null +++ b/common/JackMidiAsyncQueue.h @@ -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. + +*/ + +#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); + + 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); + + /** + * Returns the maximum size event that can be enqueued right *now*. + */ + + size_t + GetAvailableSpace(); + + }; + +} + +#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..8499f688 --- /dev/null +++ b/common/JackMidiAsyncWaitQueue.h @@ -0,0 +1,99 @@ +/* +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: + + 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 + * 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..c53e596c --- /dev/null +++ b/common/JackMidiBufferReadQueue.cpp @@ -0,0 +1,67 @@ +/* +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) +{ + event_count = 0; + index = 0; + 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(); + } +} 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..7e6ae67f --- /dev/null +++ b/common/JackMidiBufferWriteQueue.cpp @@ -0,0 +1,65 @@ +/* +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) +{ + 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/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/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 15d507b1..b98b8daa 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -104,6 +104,7 @@ int JackMidiDriver::Attach() jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } + UpdateLatencies(); return 0; } @@ -133,33 +134,108 @@ int JackMidiDriver::Write() return 0; } +void JackMidiDriver::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); + } +} + +int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) +{ + UpdateLatencies(); + return 0; +} + int JackMidiDriver::ProcessNull() { return 0; } -int JackMidiDriver::Process() +int JackMidiDriver::ProcessRead() +{ + return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); +} + +int JackMidiDriver::ProcessWrite() { + return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); +} + +int JackMidiDriver::ProcessReadSync() +{ + int res = 0; + // 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... + jack_error("JackMidiDriver::ProcessReadSync: read error, skip cycle"); + res = -1; } - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); - if (fEngineControl->fSyncMode) { - if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); - return -1; - } + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackMidiDriver::ProcessReadSync - ResumeRefNum error"); + res = -1; } - // Write output buffers for the current cycle + return res; +} + +int JackMidiDriver::ProcessWriteSync() +{ + int res = 0; + + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, + DRIVER_TIMEOUT_FACTOR * + fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackMidiDriver::ProcessWriteSync - SuspendRefNum error"); + res = -1; + } + + // Write output buffers from the current cycle + if (Write() < 0) { + jack_error("JackMidiDriver::ProcessWriteSync - Write error"); + res = -1; + } + + return res; +} + +int JackMidiDriver::ProcessReadAsync() +{ + int res = 0; + + // Read input buffers for the current cycle + if (Read() < 0) { + jack_error("JackMidiDriver::ProcessReadAsync: read error, skip cycle"); + res = -1; + } + + // Write output buffers from the previous cycle if (Write() < 0) { - jack_error("JackMidiDriver::Process: write error, skip cycle"); - return 0; // Skip cycle, but continue processing... + jack_error("JackMidiDriver::ProcessReadAsync - Write error"); + res = -1; } + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackMidiDriver::ProcessReadAsync - ResumeRefNum error"); + res = -1; + } + + return res; +} + +int JackMidiDriver::ProcessWriteAsync() +{ return 0; } diff --git a/common/JackMidiDriver.h b/common/JackMidiDriver.h index 7c5bc5e0..fa471627 100644 --- a/common/JackMidiDriver.h +++ b/common/JackMidiDriver.h @@ -39,15 +39,23 @@ 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); - + + virtual int ProcessReadSync(); + virtual int ProcessWriteSync(); + + virtual int ProcessReadAsync(); + virtual int ProcessWriteAsync(); + + virtual void UpdateLatencies(); + public: JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); @@ -62,16 +70,20 @@ 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 SetBufferSize(jack_nframes_t buffer_size); + + virtual int ProcessRead(); + virtual int ProcessWrite(); + virtual int ProcessNull(); virtual int Attach(); virtual int Detach(); - + virtual int Read(); virtual int Write(); - + }; } // end of namespace 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 new file mode 100644 index 00000000..f2853160 --- /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 ((! boundary_frame) || (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..4639107f --- /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=0); + + }; + +} + +#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..0437ec93 --- /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=0); + + }; + +} + +#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..f21a58ff --- /dev/null +++ b/common/JackMidiWriteQueue.h @@ -0,0 +1,82 @@ +/* +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, + EN_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 `EN_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. 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, jack_nframes_t frame_offset=0) + { + return EnqueueEvent(event->time + frame_offset, event->size, + event->buffer); + } + + }; + +} + +#endif 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; } 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/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/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/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; diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp index 602aca31..9123b3c7 100644 --- a/common/Jackdmp.cpp +++ b/common/Jackdmp.cpp @@ -476,7 +476,10 @@ 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); + goto close_server; + } } // Loopback driver @@ -491,7 +494,10 @@ 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"); + goto close_server; + } } } @@ -509,7 +515,10 @@ 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); + goto stop_server; + } } notify_server_start(server_name); 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/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..42e7f1ed --- /dev/null +++ b/example-clients/midi_latency_test.c @@ -0,0 +1,642 @@ +/* +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_received; +size_t messages_sent; +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; + +#ifdef __APPLE__ +sem_t* semaphore; +#else +sem_t semaphore; +#endif + +pthread_mutex_t start_mutex; +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); + +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_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, + &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_received % 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_received] = time; + latency_values[messages_received] = frame; + total_latency += frame; + total_latency_time += time; + messages_received++; + if (messages_received == samples) { + process_state = 2; + +#ifdef __APPLE__ + sem_post(semaphore); +#else + sem_post(&semaphore); +#endif + + break; + } + send_message: + frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames); + if (frame >= frames) { + frame = frames - 1; + } + 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_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 */ + + 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 int +handle_xrun(void *arg) +{ + xrun_count++; + return 0; +} + +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; + +#ifdef __APPLE__ + sem_post(semaphore); +#else + sem_post(&semaphore); +#endif + +} + +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] = 0xfe; + 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; + } + 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; + +#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); + 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; + } + code = pthread_mutex_unlock(&start_mutex); + if (code) { + error_message = strerror(code); + error_source = "pthread_mutex_unlock"; + goto deactivate_client; + } + +#ifdef __APPLE__ + while (sem_wait(semaphore) != 0) { +#else + 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; + size_t i; + double sample_rate = (double) jack_get_sample_rate(client); + jack_nframes_t total_jitter = 0; + jack_time_t total_jitter_time = 0; + 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 (%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, + 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, + (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"); + 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]); + } + } + printf("\nMessages sent: %d\n" + "Messages received: %d\n", + messages_sent, messages_received); + if (unexpected_messages) { + printf("Unexpected messages received: %d\n", unexpected_messages); + } + if (xrun_count) { + printf("Xruns: %d (messages may have been lost)\n", xrun_count); + } + deactivate_client: + jack_deactivate(client); + 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: + 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 1e35396e..dcc13d08 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/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index b42e9693..fb816453 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,29 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) return res; } +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; + 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 +98,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 +122,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 +137,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 +147,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/linux/alsarawmidi/JackALSARawMidiDriver.cpp b/linux/alsarawmidi/JackALSARawMidiDriver.cpp new file mode 100755 index 00000000..3f4761e0 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiDriver.cpp @@ -0,0 +1,602 @@ +#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; + 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(); + 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->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(); + 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->SetLatencyRange(JackPlaybackLatency, &latency_range); + 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_nframes_t process_frame; + jack_time_t wait_time; + jack_time_t *wait_time_ptr; + unsigned short revents; + if (! timeout_frame) { + wait_time_ptr = 0; + } else { + jack_time_t next_time = GetTimeFromFrames(timeout_frame); + jack_time_t now = GetMicroSeconds(); + wait_time = next_time <= now ? 0 : next_time - now; + wait_time_ptr = &wait_time; + } + if (Poll(wait_time_ptr) == -1) { + if (errno == EINTR) { + continue; + } + jack_error("JackALSARawMidiDriver::Execute - poll error: %s", + strerror(errno)); + break; + } + revents = poll_fds[0].revents; + if (revents & POLLHUP) { + // Driver is being stopped. + break; + } + if (revents & (~ POLLIN)) { + jack_error("JackALSARawMidiDriver::Execute - unexpected poll " + "event on pipe file descriptor."); + break; + } + timeout_frame = 0; + for (int i = 0; i < fCaptureChannels; i++) { + 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++) { + 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; +} + +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(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_ptr = &timeout; + } + return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0); +} + +#else + +int +JackALSARawMidiDriver::Poll(const jack_time_t *wait_time) +{ + int result = poll(poll_fds, poll_fd_count, + ! wait_time ? -1 : (int) ((*wait_time) / 1000)); + if ((! result) && wait_time) { + jack_time_t time_left = (*wait_time) % 1000; + if (time_left) { + // Cheap hack. + usleep(time_left); + result = poll(poll_fds, poll_fd_count, 0); + } + } + return result; +} + +#endif + +int +JackALSARawMidiDriver::Read() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < fCaptureChannels; i++) { + if (! input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size)) { + return -1; + } + } + 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("JackALSARawMidiDriver::Start - starting ALSA thread ..."); + + if (! thread->StartSync()) { + + jack_info("JackALSARawMidiDriver::Start - 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("JackALSARawMidiDriver::Stop - 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++) { + if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size, + write_fd)) { + return -1; + } + } + 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_midi", "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..0ffc4e52 --- /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(const 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..5ed14882 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiInputPort.cpp @@ -0,0 +1,124 @@ +#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; +} + +bool +JackALSARawMidiInputPort::ProcessALSA(jack_nframes_t *frame) +{ + unsigned short revents; + if (! ProcessPollEvents(&revents)) { + return false; + } + if (alsa_event) { + *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 true; + } + } + } + *frame = raw_queue->Process(); + return true; +} + +bool +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, frames)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + 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; + default: + ; + } + break; + } + return true; +} diff --git a/linux/alsarawmidi/JackALSARawMidiInputPort.h b/linux/alsarawmidi/JackALSARawMidiInputPort.h new file mode 100755 index 00000000..38b8985b --- /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(); + + bool + ProcessALSA(jack_nframes_t *frame); + + bool + 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..c1a2bfb8 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiOutputPort.cpp @@ -0,0 +1,153 @@ +#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; +} + +bool +JackALSARawMidiOutputPort::ProcessALSA(int read_fd, jack_nframes_t *frame) +{ + unsigned short revents; + if (! ProcessPollEvents(&revents)) { + return false; + } + if (blocked) { + if (! (revents & POLLOUT)) { + *frame = 0; + return true; + } + 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: + *frame = raw_queue->Process(); + blocked = send_queue->IsBlocked(); + if (blocked) { + + jack_info("JackALSARawMidiOutputPort::ProcessALSA - MIDI port is " + "blocked"); + + SetPollEventMask(POLLERR | POLLNVAL | POLLOUT); + *frame = 0; + } else { + SetPollEventMask(POLLERR | POLLNVAL); + } + return true; +} + +bool +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; + 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 { + assert(thread_queue->EnqueueEvent(event, frames) == + JackMidiWriteQueue::OK); + } + } + return true; +} diff --git a/linux/alsarawmidi/JackALSARawMidiOutputPort.h b/linux/alsarawmidi/JackALSARawMidiOutputPort.h new file mode 100755 index 00000000..816ab6a2 --- /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(); + + bool + ProcessALSA(int read_fd, jack_nframes_t *frame); + + bool + 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..a37f9d93 --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiPort.cpp @@ -0,0 +1,159 @@ +#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 free_params; + } + 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; +} + +bool +JackALSARawMidiPort::ProcessPollEvents(unsigned short *revents) +{ + int code = snd_rawmidi_poll_descriptors_revents(rawmidi, poll_fds, num_fds, + revents); + if (code) { + jack_error("JackALSARawMidiPort::ProcessPollEvents - " + "snd_rawmidi_poll_descriptors_revents: %s", + snd_strerror(code)); + return false; + } + if ((*revents) & POLLNVAL) { + jack_error("JackALSARawMidiPort::ProcessPollEvents - the file " + "descriptor is invalid."); + return false; + } + if ((*revents) & POLLERR) { + jack_error("JackALSARawMidiPort::ProcessPollEvents - an error has " + "occurred on the device or stream."); + return false; + } + return true; +} + +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..07fb4c0f --- /dev/null +++ b/linux/alsarawmidi/JackALSARawMidiPort.h @@ -0,0 +1,53 @@ +#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; + + bool + ProcessPollEvents(unsigned short *revents); + + 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..80a11b56 --- /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 EN_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/firewire/JackFFADODriver.cpp b/linux/firewire/JackFFADODriver.cpp index 2f855f37..62f10ad1 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..b916c8c5 --- /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, non_rt_size, + max_non_rt_messages, + max_rt_messages); + 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 5675c3dc..3306b880 100644 --- a/linux/wscript +++ b/linux/wscript @@ -57,15 +57,25 @@ 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/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: 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") 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 96daca3a..eb5eb758 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; @@ -107,6 +108,12 @@ /* 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 */; }; + 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 */; }; @@ -114,6 +121,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 */; }; @@ -317,6 +325,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 */; }; @@ -595,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, ); }; }; @@ -754,18 +830,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 */; }; @@ -877,6 +941,13 @@ remoteGlobalIDString = 4B19B2F60E23620F00DD4A82; remoteInfo = audioadapter; }; + 4B20220B133A9C370019E213 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4B2021DC133A9BA40019E213; + remoteInfo = "jack_midi_latency 64 bits"; + }; 4B224B330E65BA330066BE5B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -1430,9 +1501,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; }; @@ -1457,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; }; @@ -1466,6 +1553,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; }; @@ -1478,6 +1567,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; }; @@ -1518,6 +1630,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 = ""; }; @@ -1642,10 +1770,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; }; @@ -1783,6 +1907,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B2021E0133A9BA40019E213 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B3224E010A3156800838A8E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2559,6 +2690,7 @@ 4B3811971326884E00C61B14 /* jack_latent_client */, 4B8F16E513290DC80002AD73 /* jack_midi_dump */, 4B8F16F213290E0E0002AD73 /* jack_midi_dump */, + 4B2021E6133A9BA40019E213 /* jack_midi_latency_test */, ); name = Products; sourceTree = ""; @@ -2566,6 +2698,7 @@ 4B03383E0797E19900686131 /* Simple clients */ = { isa = PBXGroup; children = ( + 4B202209133A9C1C0019E213 /* midi_latency_test.c */, 4B8F16F41329161E0002AD73 /* midi_dump.c */, 4B3811FA13269C8300C61B14 /* latent_client.c */, 4B6654FB127C350100753A79 /* server_control.cpp */, @@ -2595,9 +2728,10 @@ 4B05A0420DF72B8500840F4C /* Linux */ = { isa = PBXGroup; children = ( + 4B349837133A6B6F00D130AB /* firewire */, + 4B349825133A6AF500D130AB /* alsarawmidi */, 4B05A04C0DF72BC000840F4C /* alsa */, 4B05A07D0DF72BC000840F4C /* driver.h */, - 4B05A07E0DF72BC000840F4C /* firewire */, 4B05A0820DF72BC000840F4C /* freebob */, ); name = Linux; @@ -2634,17 +2768,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 = ( @@ -2709,6 +2832,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 = ( @@ -2889,6 +3051,7 @@ 4BA550FB05E2420000569492 /* Engine */ = { isa = PBXGroup; children = ( + 4B193990133F321500547810 /* JackFilters.h */, 4B5F253D0DEE9B8F0041E486 /* JackLockedEngine.h */, 4BF8D2130834F02800C94B91 /* JackEngine.h */, 4BF8D2140834F02800C94B91 /* JackEngine.cpp */, @@ -2994,10 +3157,40 @@ 4BF3390D0F8B86AF0080FB5B /* MIDI */ = { isa = PBXGroup; children = ( - 4BCBCE5910C4FE3F00450FFE /* JackPhysicalMidiInput.cpp */, - 4BCBCE5A10C4FE3F00450FFE /* JackPhysicalMidiInput.h */, - 4BCBCE5B10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp */, - 4BCBCE5C10C4FE3F00450FFE /* JackPhysicalMidiOutput.h */, + 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 */, + 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 */, @@ -3086,6 +3279,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B2021DD133A9BA40019E213 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B3224D810A3156800838A8E /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -3181,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; }; @@ -3243,8 +3444,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 */, @@ -3254,6 +3453,16 @@ 4B8A38B0117B812500664E07 /* JackSocketServerChannel.h in Headers */, 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; }; @@ -3549,6 +3758,7 @@ 4B88D04111298BEE007A87C1 /* weakjack.h in Headers */, 4B88D04211298BEE007A87C1 /* weakmacros.h in Headers */, 4B5160A913215EBF00BB7DCB /* systemdeps.h in Headers */, + 4B193994133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3640,6 +3850,7 @@ 4B88D03C11298BEE007A87C1 /* weakmacros.h in Headers */, 4B2209ED12F6BC2200E5DC26 /* JackSocket.h in Headers */, 4B2209EF12F6BC2500E5DC26 /* JackSocketClientChannel.h in Headers */, + 4B193991133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3706,8 +3917,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 */, @@ -3716,6 +3925,16 @@ 4B2209E412F6BBF600E5DC26 /* JackSocketServerNotifyChannel.h in Headers */, 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; }; @@ -3901,13 +4120,12 @@ 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 */, 4BC2CA60113C6CD20076717C /* JackNetUnixSocket.h in Headers */, 4B5160AE13215EF900BB7DCB /* systemdeps.h in Headers */, + 4B193996133F321500547810 /* JackFilters.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3944,6 +4162,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; }; @@ -4004,6 +4230,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; }; @@ -4137,6 +4371,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" */; @@ -5864,6 +6117,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 */, @@ -5966,6 +6220,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B2021E1133A9BA40019E213 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B32255C10A3187800838A8E /* Rez */ = { isa = PBXRezBuildPhase; buildActionMask = 2147483647; @@ -6474,6 +6735,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; @@ -6610,14 +6879,21 @@ 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 */, 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; }; @@ -7064,14 +7340,21 @@ 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 */, 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; }; @@ -7260,8 +7543,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 */, ); @@ -7304,6 +7585,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; }; @@ -7369,6 +7658,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; }; @@ -7470,6 +7767,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 */; @@ -8054,7 +8356,7 @@ OTHER_CFLAGS = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = ( - /usr/local/lib/libsamplerate.a, + /opt/local/lib/libsamplerate.a, "-framework", Jackservermp, "-framework", @@ -8173,6 +8475,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 = { @@ -8734,7 +9131,7 @@ ); OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -9105,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 = ( @@ -9114,7 +9512,7 @@ CoreAudio, ); OTHER_REZFLAGS = ""; - PRODUCT_NAME = Jackdmp; + PRODUCT_NAME = Jackservermp; REZ_EXECUTABLE = NO; SDKROOT = ""; SECTORDER_FLAGS = ""; @@ -11111,7 +11509,7 @@ OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -11261,7 +11659,7 @@ OTHER_LDFLAGS = ( libportaudio.a, "-framework", - Jackdmp, + Jackservermp, "-framework", AudioToolbox, "-framework", @@ -11410,7 +11808,7 @@ OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -11548,7 +11946,7 @@ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = ( "-framework", - Jackdmp, + Jackservermp, "-framework", CoreAudio, "-framework", @@ -16060,7 +16458,7 @@ CoreAudio, ); OTHER_REZFLAGS = ""; - PRODUCT_NAME = Jackdmp; + PRODUCT_NAME = Jackservermp; REZ_EXECUTABLE = NO; SDKROOT = ""; SECTORDER_FLAGS = ""; @@ -17071,7 +17469,7 @@ OTHER_CFLAGS = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = ( - /usr/local/lib/libsamplerate.a, + /opt/local/lib/libsamplerate.a, "-framework", Jackservermp, "-framework", @@ -17220,7 +17618,7 @@ OTHER_CFLAGS = ""; OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; OTHER_LDFLAGS = ( - /usr/local/lib/libsamplerate.a, + /opt/local/lib/libsamplerate.a, "-framework", Jackservermp, "-framework", @@ -18767,6 +19165,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 = ( 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..97352bca 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"); @@ -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; } } @@ -1549,7 +1552,6 @@ error: int JackCoreAudioDriver::Close() { jack_log("JackCoreAudioDriver::Close"); - Stop(); // Generic audio driver close int res = JackAudioDriver::Close(); @@ -1561,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; @@ -1571,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); @@ -1596,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; } @@ -1634,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 @@ -1659,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); @@ -1714,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/macosx/coremidi/JackCoreMidiDriver.cpp b/macosx/coremidi/JackCoreMidiDriver.cpp index 1cb7176b..234f2afe 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,621 @@ 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 +using Jack::JackCoreMidiDriver; -namespace Jack +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +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; + ItemCount potential_po_count; + ItemCount potential_pi_count; + + CFStringRef name = CFStringCreateWithCString(0, "JackMidi", + CFStringGetSystemEncoding()); + if (! name) { + jack_error("JackCoreMidiDriver::Open - failed to allocate memory for " + "client name string"); return -1; + } - 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; - } + OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this, + &client); - err = MIDIOutputPortCreate(fMidiClient, CFSTR("Output port"), &fOutputPort); - if (!fOutputPort) { - jack_error("Cannot open CoreMidi out port\n"); - goto error; - } + CFRelease(name); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientCreate", + status); + return -1; + } + char *client_name = fClientControl.fName; + + // Allocate and connect physical inputs + 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()); + goto destroy_internal_input_port; + } + pi_count++; + } + } - fMidiDestination = new MIDIEndpointRef[inchannels + fRealCaptureChannels]; - assert(fMidiDestination); + // Allocate and connect physical outputs + 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); + } catch (std::exception e) { + jack_error("JackCoreMidiDriver::Open - while creating " + "physical output port: %s", e.what()); + goto destroy_internal_output_port; + } + po_count++; + } + } - // 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 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; + } } } - // 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 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; + } + } } - fMidiSource = new MIDIEndpointRef[outchannels + fRealPlaybackChannels]; - assert(fMidiSource); - // 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; - } + 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; } - // Real output - for (int i = 0; i < fRealPlaybackChannels; i++) { - fMidiSource[i + outchannels] = MIDIGetDestination(i); + // 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; + } + 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 +700,3 @@ extern "C" #ifdef __cplusplus } #endif - diff --git a/macosx/coremidi/JackCoreMidiDriver.h b/macosx/coremidi/JackCoreMidiDriver.h index a99b35a0..c5a26f5b 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..fb4bc961 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiInputPort.cpp @@ -0,0 +1,184 @@ +/* +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" +//#include "types.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_nframes_t +JackCoreMidiInputPort::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) +{ + JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false); +} + +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; + 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.buffer = sysex_buffer; + event.size = sysex_bytes_sent; + 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.buffer = 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, frames); + 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..f014d478 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiOutputPort.cpp @@ -0,0 +1,250 @@ +/* +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 +#include + +#include "JackCoreMidiOutputPort.h" +#include "JackMidiUtil.h" +#include "JackTime.h" + +using Jack::JackCoreMidiOutputPort; + +JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, + size_t max_bytes, + size_t max_messages): + JackCoreMidiPort(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); + thread = new JackThread(this); + std::auto_ptr thread_ptr(thread); + 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)); + } + advance_schedule_time = 0; + thread_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; +} + +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 = GetCoreMidiEvent(true); + } + jack_midi_data_t *data = event->buffer; + 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_frame); + packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet, + timestamp, size, data); + if (packet) { + while (GetMicroSeconds() < send_time) { + event = GetCoreMidiEvent(false); + if (! event) { + break; + } + packet = MIDIPacketListAdd(packet_list, sizeof(packet_buffer), + packet, + GetTimeStampFromFrames(event->time), + event->size, event->buffer); + 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, + sizeof(packet_buffer), 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; +} + +jack_midi_event_t * +JackCoreMidiOutputPort::GetCoreMidiEvent(bool block) +{ + if (! block) { + if (sem_trywait(thread_queue_semaphore)) { + if (errno != EAGAIN) { + 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) +{ + return GetTimeFromFrames(frames) / time_ratio; +} + +bool +JackCoreMidiOutputPort::Init() +{ + set_threaded_log_function(); + + // OSX only, values read in RT CoreMidi thread + UInt64 period = 0; + UInt64 computation = 250 * 1000; + UInt64 constraint = 500 * 1000; + thread->SetParams(period, computation, constraint); + + if (thread->AcquireSelfRealTime()) { + 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, + 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 +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."); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " + "queue couldn't enqueue a %d-byte event. Dropping " + "event.", event->size); + break; + default: + if (sem_post(thread_queue_semaphore)) { + jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected " + "error while posting to thread queue semaphore: %s", + strerror(errno)); + } + } + } +} + +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..a04d98b3 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiOutputPort.h @@ -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. + +*/ + +#ifndef __JackCoreMidiOutputPort__ +#define __JackCoreMidiOutputPort__ + +#include + +#include "JackCoreMidiPort.h" +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferReadQueue.h" +#include "JackThread.h" + +namespace Jack { + + class JackCoreMidiOutputPort: + public JackCoreMidiPort, public JackRunnableInterface { + + private: + + jack_midi_event_t * + GetCoreMidiEvent(bool block); + + MIDITimeStamp + GetTimeStampFromFrames(jack_nframes_t frames); + + 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]; + JackThread *thread; + JackMidiAsyncQueue *thread_queue; + sem_t *thread_queue_semaphore; + + protected: + + 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, SInt32 advance_schedule_time); + + 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..d8a4af25 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp @@ -0,0 +1,78 @@ +/* +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, + size_t max_bytes, + size_t max_messages): + JackCoreMidiOutputPort(time_ratio, 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()); + } + 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; +} + +JackCoreMidiPhysicalOutputPort::~JackCoreMidiPhysicalOutputPort() +{ + // Empty +} + +bool +JackCoreMidiPhysicalOutputPort::SendPacketList(MIDIPacketList *packet_list) +{ + OSStatus status = MIDISend(internal_output, endpoint, 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..9d641180 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPhysicalOutputPort.h @@ -0,0 +1,55 @@ +/* +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, + 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..7b2fd6db --- /dev/null +++ b/macosx/coremidi/JackCoreMidiPort.cpp @@ -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. + +*/ + +#include + +#include "JackCoreMidiPort.h" +#include "JackCoreMidiUtil.h" +#include "JackError.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; + Boolean res; + OSStatus result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, + &endpoint_name_ref); + if (result != noErr) { + WriteMacOSError("JackCoreMidiPort::Initialize", + "MIDIObjectGetStringProperty", result); + goto get_basic_alias; + } + res = CFStringGetCString(endpoint_name_ref, endpoint_name, + sizeof(endpoint_name), 0); + CFRelease(endpoint_name_ref); + if (!res) { + 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 ? "in" : "out", num); + } else { + snprintf(alias, sizeof(alias) - 1, "%s:%s:%s%d", alias_name, + endpoint_name, is_output ? "in" : "out", 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..21055c31 --- /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]; + 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; + MIDIEndpointRef endpoint; + + 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..d976e936 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiUtil.cpp @@ -0,0 +1,43 @@ +/* +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" +#include "JackCoreMidiUtil.h" + +std::string +Jack::GetMacOSErrorString(OSStatus status) +{ + const char *message = GetMacOSStatusErrorString(status); + if (! message) { + 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, + GetMacOSErrorString(status).c_str()); +} diff --git a/macosx/coremidi/JackCoreMidiUtil.h b/macosx/coremidi/JackCoreMidiUtil.h new file mode 100644 index 00000000..4f24fc5b --- /dev/null +++ b/macosx/coremidi/JackCoreMidiUtil.h @@ -0,0 +1,39 @@ +/* +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 +#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..874a644d --- /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 << "virtual" << (index + 1); + CFStringRef name = CFStringCreateWithCString(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..e48126c4 --- /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..0d8bc173 --- /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, + size_t max_bytes, + size_t max_messages): + JackCoreMidiOutputPort(time_ratio, max_bytes, + max_messages) +{ + std::stringstream stream; + stream << "virtual" << (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, 0); +} + +JackCoreMidiVirtualOutputPort::~JackCoreMidiVirtualOutputPort() +{ + OSStatus status = MIDIEndpointDispose(GetEndpoint()); + if (status != noErr) { + WriteMacOSError("JackCoreMidiVirtualOutputPort [destructor]", + "MIDIEndpointDispose", status); + } +} + +bool +JackCoreMidiVirtualOutputPort::SendPacketList(MIDIPacketList *packet_list) +{ + OSStatus status = MIDIReceived(endpoint, 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..61529a98 --- /dev/null +++ b/macosx/coremidi/JackCoreMidiVirtualOutputPort.h @@ -0,0 +1,49 @@ +/* +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, + size_t max_bytes=4096, + size_t max_messages=1024); + + ~JackCoreMidiVirtualOutputPort(); + + }; + +} + +#endif 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 { diff --git a/windows/jack_winmme.cbp b/windows/jack_winmme.cbp index 2c6d8bbe..7fe93703 100644 --- a/windows/jack_winmme.cbp +++ b/windows/jack_winmme.cbp @@ -106,6 +106,12 @@