git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@4243 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.9.8
| @@ -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(); | |||
| @@ -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: | |||
| @@ -300,19 +300,42 @@ void JackDriver::RemoveSlave(JackDriverInterface* slave) | |||
| fSlaveList.remove(slave); | |||
| } | |||
| int JackDriver::ProcessSlaves() | |||
| int JackDriver::ProcessReadSlaves() | |||
| { | |||
| int res = 0; | |||
| list<JackDriverInterface*>::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<JackDriverInterface*>::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<JackDriverInterface*>::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<JackDriverInterface*>::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() | |||
| @@ -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<JackDriverInterface*> 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<JackDriverInterface*> 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); | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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 <new> | |||
| #include "JackMidiAsyncQueue.h" | |||
| using Jack::JackMidiAsyncQueue; | |||
| JackMidiAsyncQueue::JackMidiAsyncQueue(size_t max_bytes, size_t max_messages) | |||
| { | |||
| data_buffer = new jack_midi_data_t[max_bytes]; | |||
| byte_ring = jack_ringbuffer_create((max_bytes * 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); | |||
| } | |||
| @@ -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 | |||
| @@ -0,0 +1,85 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include <new> | |||
| #include "JackMidiAsyncWaitQueue.h" | |||
| #include "JackMidiUtil.h" | |||
| #include "JackTime.h" | |||
| using Jack::JackMidiAsyncWaitQueue; | |||
| JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes, | |||
| size_t max_messages): | |||
| JackMidiAsyncQueue(max_bytes, max_messages) | |||
| { | |||
| if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) { | |||
| throw std::bad_alloc(); | |||
| } | |||
| } | |||
| JackMidiAsyncWaitQueue::~JackMidiAsyncWaitQueue() | |||
| { | |||
| semaphore.Destroy(); | |||
| } | |||
| jack_midi_event_t * | |||
| JackMidiAsyncWaitQueue::DequeueEvent() | |||
| { | |||
| return DequeueEvent((long) 0); | |||
| } | |||
| jack_midi_event_t * | |||
| JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame) | |||
| { | |||
| // XXX: I worry about timer resolution on Solaris and Windows. When the | |||
| // resolution for the `JackSynchro` object is milliseconds, the worst-case | |||
| // scenario for processor objects is that the wait time becomes less than a | |||
| // millisecond, and the processor object continually calls this method, | |||
| // expecting to wait a certain amount of microseconds, and ends up not | |||
| // waiting at all each time, essentially busy-waiting until the current | |||
| // frame is reached. Perhaps there should be a #define that indicates the | |||
| // wait time resolution for `JackSynchro` objects so that we can wait a | |||
| // little longer if necessary. | |||
| jack_time_t frame_time = GetTimeFromFrames(frame); | |||
| jack_time_t current_time = GetMicroSeconds(); | |||
| return DequeueEvent((frame_time < current_time) ? 0 : | |||
| (long) (frame_time - current_time)); | |||
| } | |||
| jack_midi_event_t * | |||
| JackMidiAsyncWaitQueue::DequeueEvent(long usec) | |||
| { | |||
| return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ? | |||
| JackMidiAsyncQueue::DequeueEvent() : 0; | |||
| } | |||
| Jack::JackMidiWriteQueue::EnqueueResult | |||
| JackMidiAsyncWaitQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
| jack_midi_data_t *buffer) | |||
| { | |||
| EnqueueResult result = JackMidiAsyncQueue::EnqueueEvent(time, size, | |||
| buffer); | |||
| if (result == OK) { | |||
| semaphore.Signal(); | |||
| } | |||
| return result; | |||
| } | |||
| @@ -0,0 +1,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 | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef __JackMidiBufferReadQueue__ | |||
| #define __JackMidiBufferReadQueue__ | |||
| #include "JackMidiReadQueue.h" | |||
| namespace Jack { | |||
| /** | |||
| * Wrapper class to present a JackMidiBuffer in a read queue interface. | |||
| */ | |||
| class SERVER_EXPORT JackMidiBufferReadQueue: public JackMidiReadQueue { | |||
| private: | |||
| JackMidiBuffer *buffer; | |||
| jack_nframes_t event_count; | |||
| jack_nframes_t index; | |||
| jack_nframes_t last_frame_time; | |||
| jack_midi_event_t midi_event; | |||
| public: | |||
| JackMidiBufferReadQueue(); | |||
| jack_midi_event_t * | |||
| DequeueEvent(); | |||
| /** | |||
| * This method must be called each period to reset the MIDI buffer for | |||
| * processing. | |||
| */ | |||
| void | |||
| ResetMidiBuffer(JackMidiBuffer *buffer); | |||
| }; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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<JackMidiBuffer*>(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<JackMidiBuffer*>(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; | |||
| @@ -0,0 +1,300 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include <cassert> | |||
| #include <memory> | |||
| #include <new> | |||
| #include "JackMidiRawInputWriteQueue.h" | |||
| using Jack::JackMidiRawInputWriteQueue; | |||
| JackMidiRawInputWriteQueue:: | |||
| JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, | |||
| size_t max_packet_data, size_t max_packets) | |||
| { | |||
| packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets); | |||
| std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue); | |||
| input_ring = jack_ringbuffer_create(max_packet_data + 1); | |||
| if (! input_ring) { | |||
| throw std::bad_alloc(); | |||
| } | |||
| jack_ringbuffer_mlock(input_ring); | |||
| Clear(); | |||
| expected_bytes = 0; | |||
| event_pending = false; | |||
| packet = 0; | |||
| status_byte = 0; | |||
| this->write_queue = write_queue; | |||
| packet_queue_ptr.release(); | |||
| } | |||
| JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue() | |||
| { | |||
| jack_ringbuffer_free(input_ring); | |||
| delete packet_queue; | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::Clear() | |||
| { | |||
| jack_ringbuffer_reset(input_ring); | |||
| total_bytes = 0; | |||
| unbuffered_bytes = 0; | |||
| } | |||
| Jack::JackMidiWriteQueue::EnqueueResult | |||
| JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
| jack_midi_data_t *buffer) | |||
| { | |||
| return packet_queue->EnqueueEvent(time, size, buffer); | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes, | |||
| size_t total_bytes) | |||
| { | |||
| jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI " | |||
| "byte(s) of a %d byte message could not be buffered. The " | |||
| "message has been dropped.", unbuffered_bytes, total_bytes); | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::HandleEventLoss(jack_midi_event_t *event) | |||
| { | |||
| jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI " | |||
| "event scheduled for frame '%d' could not be processed because " | |||
| "the write queue cannot accomodate an event of that size. The " | |||
| "event has been discarded.", event->size, event->time); | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::HandleIncompleteMessage(size_t total_bytes) | |||
| { | |||
| jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - " | |||
| "Discarding %d MIDI byte(s) of an incomplete message. The " | |||
| "MIDI cable may have been unplugged.", total_bytes); | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::HandleInvalidStatusByte(jack_midi_data_t byte) | |||
| { | |||
| jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - " | |||
| "Dropping invalid MIDI status byte '%x'.", (unsigned int) byte); | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd(size_t total_bytes) | |||
| { | |||
| jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - " | |||
| "Received a sysex end byte without first receiving a sysex " | |||
| "start byte. Discarding %d MIDI byte(s). The cable may have " | |||
| "been unplugged.", total_bytes); | |||
| } | |||
| bool | |||
| JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time) | |||
| { | |||
| bool result = ! unbuffered_bytes; | |||
| if (! result) { | |||
| HandleBufferFailure(unbuffered_bytes, total_bytes); | |||
| } else { | |||
| size_t size = jack_ringbuffer_read_space(input_ring); | |||
| jack_ringbuffer_data_t vector[2]; | |||
| jack_ringbuffer_get_read_vector(input_ring, vector); | |||
| // We don't worry about the second part of the vector, as we reset the | |||
| // ringbuffer after each parsed message. | |||
| PrepareEvent(time, size, (jack_midi_data_t *) vector[0].buf); | |||
| } | |||
| Clear(); | |||
| if (status_byte >= 0xf0) { | |||
| expected_bytes = 0; | |||
| status_byte = 0; | |||
| } | |||
| return result; | |||
| } | |||
| bool | |||
| JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time, | |||
| jack_midi_data_t byte) | |||
| { | |||
| event_byte = byte; | |||
| PrepareEvent(time, 1, &event_byte); | |||
| return true; | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size, | |||
| jack_midi_data_t *buffer) | |||
| { | |||
| event.buffer = buffer; | |||
| event.size = size; | |||
| event.time = time; | |||
| event_pending = true; | |||
| } | |||
| jack_nframes_t | |||
| JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame) | |||
| { | |||
| if (event_pending) { | |||
| if (! WriteEvent(boundary_frame)) { | |||
| return event.time; | |||
| } | |||
| } | |||
| if (! packet) { | |||
| packet = packet_queue->DequeueEvent(); | |||
| } | |||
| for (; packet; packet = packet_queue->DequeueEvent()) { | |||
| for (; packet->size; (packet->buffer)++, (packet->size)--) { | |||
| if (ProcessByte(packet->time, *(packet->buffer))) { | |||
| if (! WriteEvent(boundary_frame)) { | |||
| (packet->buffer)++; | |||
| (packet->size)--; | |||
| return event.time; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| bool | |||
| JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time, | |||
| jack_midi_data_t byte) | |||
| { | |||
| if (byte >= 0xf8) { | |||
| // Realtime | |||
| if (byte == 0xfd) { | |||
| HandleInvalidStatusByte(byte); | |||
| return false; | |||
| } | |||
| return PrepareByteEvent(time, byte); | |||
| } | |||
| if (byte == 0xf7) { | |||
| // Sysex end | |||
| if (status_byte == 0xf0) { | |||
| RecordByte(byte); | |||
| return PrepareBufferedEvent(time); | |||
| } | |||
| HandleUnexpectedSysexEnd(total_bytes); | |||
| Clear(); | |||
| expected_bytes = 0; | |||
| status_byte = 0; | |||
| return false; | |||
| } | |||
| if (byte >= 0x80) { | |||
| // Non-realtime status byte | |||
| if (total_bytes) { | |||
| HandleIncompleteMessage(total_bytes); | |||
| Clear(); | |||
| } | |||
| status_byte = byte; | |||
| switch (byte & 0xf0) { | |||
| case 0x80: | |||
| case 0x90: | |||
| case 0xa0: | |||
| case 0xb0: | |||
| case 0xe0: | |||
| // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel | |||
| expected_bytes = 3; | |||
| break; | |||
| case 0xc0: | |||
| case 0xd0: | |||
| // Program Change, Channel Pressure | |||
| expected_bytes = 2; | |||
| break; | |||
| case 0xf0: | |||
| switch (byte) { | |||
| case 0xf0: | |||
| // Sysex | |||
| expected_bytes = 0; | |||
| break; | |||
| case 0xf1: | |||
| case 0xf3: | |||
| // MTC Quarter Frame, Song Select | |||
| expected_bytes = 2; | |||
| break; | |||
| case 0xf2: | |||
| // Song Position | |||
| expected_bytes = 3; | |||
| break; | |||
| case 0xf4: | |||
| case 0xf5: | |||
| // Undefined | |||
| HandleInvalidStatusByte(byte); | |||
| expected_bytes = 0; | |||
| status_byte = 0; | |||
| return false; | |||
| case 0xf6: | |||
| // Tune Request | |||
| bool result = PrepareByteEvent(time, byte); | |||
| if (result) { | |||
| expected_bytes = 0; | |||
| status_byte = 0; | |||
| } | |||
| return result; | |||
| } | |||
| } | |||
| RecordByte(byte); | |||
| return false; | |||
| } | |||
| // Data byte | |||
| if (! status_byte) { | |||
| // Data bytes without a status will be discarded. | |||
| total_bytes++; | |||
| unbuffered_bytes++; | |||
| return false; | |||
| } | |||
| if (! total_bytes) { | |||
| // Apply running status. | |||
| RecordByte(status_byte); | |||
| } | |||
| RecordByte(byte); | |||
| return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) : | |||
| false; | |||
| } | |||
| void | |||
| JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte) | |||
| { | |||
| if (jack_ringbuffer_write(input_ring, (const char *) &byte, 1) != 1) { | |||
| unbuffered_bytes++; | |||
| } | |||
| total_bytes++; | |||
| } | |||
| bool | |||
| JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame) | |||
| { | |||
| if ((! 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; | |||
| } | |||
| @@ -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 | |||
| @@ -0,0 +1,228 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include <memory> | |||
| #include <new> | |||
| #include "JackError.h" | |||
| #include "JackMidiRawOutputWriteQueue.h" | |||
| #include "JackMidiUtil.h" | |||
| using Jack::JackMidiRawOutputWriteQueue; | |||
| #define STILL_TIME(c, b) ((! (b)) || ((c) < (b))) | |||
| JackMidiRawOutputWriteQueue:: | |||
| JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size, | |||
| size_t max_non_rt_messages, size_t max_rt_messages) | |||
| { | |||
| non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages); | |||
| std::auto_ptr<JackMidiAsyncQueue> non_rt_ptr(non_rt_queue); | |||
| rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages); | |||
| std::auto_ptr<JackMidiAsyncQueue> rt_ptr(rt_queue); | |||
| non_rt_event = 0; | |||
| rt_event = 0; | |||
| running_status = 0; | |||
| this->send_queue = send_queue; | |||
| rt_ptr.release(); | |||
| non_rt_ptr.release(); | |||
| } | |||
| JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue() | |||
| { | |||
| delete non_rt_queue; | |||
| delete rt_queue; | |||
| } | |||
| bool | |||
| JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent() | |||
| { | |||
| non_rt_event = non_rt_queue->DequeueEvent(); | |||
| bool result = non_rt_event != 0; | |||
| if (result) { | |||
| non_rt_event_time = non_rt_event->time; | |||
| running_status = ApplyRunningStatus(non_rt_event, running_status); | |||
| } | |||
| return result; | |||
| } | |||
| bool | |||
| JackMidiRawOutputWriteQueue::DequeueRealtimeEvent() | |||
| { | |||
| rt_event = rt_queue->DequeueEvent(); | |||
| bool result = rt_event != 0; | |||
| if (result) { | |||
| rt_event_time = rt_event->time; | |||
| } | |||
| return result; | |||
| } | |||
| Jack::JackMidiWriteQueue::EnqueueResult | |||
| JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
| jack_midi_data_t *buffer) | |||
| { | |||
| JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue : | |||
| non_rt_queue; | |||
| EnqueueResult result = queue->EnqueueEvent(time, size, buffer); | |||
| if (result == OK) { | |||
| last_enqueued_message_time = time; | |||
| } | |||
| return result; | |||
| } | |||
| void | |||
| JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time, | |||
| jack_midi_data_t byte) | |||
| { | |||
| jack_error("JackMidiRawOutputWriteQueue::HandleWriteQueueBug - **BUG** " | |||
| "The write queue told us that it couldn't enqueue a 1-byte " | |||
| "MIDI event scheduled for frame '%d'. This is probably a bug " | |||
| "in the write queue implementation.", time); | |||
| } | |||
| jack_nframes_t | |||
| JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame) | |||
| { | |||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
| while (STILL_TIME(current_frame, boundary_frame)) { | |||
| if (! non_rt_event) { | |||
| DequeueNonRealtimeEvent(); | |||
| } | |||
| if (! rt_event) { | |||
| DequeueRealtimeEvent(); | |||
| } | |||
| if (! (non_rt_event || rt_event)) { | |||
| return 0; | |||
| } | |||
| if (! WriteRealtimeEvents(boundary_frame)) { | |||
| break; | |||
| } | |||
| jack_nframes_t non_rt_boundary = | |||
| rt_event && STILL_TIME(rt_event_time, boundary_frame) ? | |||
| rt_event_time : boundary_frame; | |||
| if (! WriteNonRealtimeEvents(non_rt_boundary)) { | |||
| break; | |||
| } | |||
| current_frame = send_queue->GetNextScheduleFrame(); | |||
| } | |||
| // If we get here, that means there is some sort of message available, and | |||
| // that either we can't currently write to the write queue or we have | |||
| // reached the boundary frame. Return the earliest time that a message is | |||
| // scheduled to be sent. | |||
| return ! non_rt_event ? rt_event_time : | |||
| non_rt_event->size > 1 ? current_frame : | |||
| ! rt_event ? non_rt_event_time : | |||
| non_rt_event_time < rt_event_time ? non_rt_event_time : rt_event_time; | |||
| } | |||
| bool | |||
| JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time, | |||
| jack_midi_data_t byte) | |||
| { | |||
| switch (send_queue->EnqueueEvent(time, 1, &byte)) { | |||
| case BUFFER_TOO_SMALL: | |||
| HandleWriteQueueBug(time, byte); | |||
| case OK: | |||
| return true; | |||
| default: | |||
| // This is here to stop compilers from warning us about not handling | |||
| // enumeration values. | |||
| ; | |||
| } | |||
| return false; | |||
| } | |||
| bool | |||
| JackMidiRawOutputWriteQueue:: | |||
| WriteNonRealtimeEvents(jack_nframes_t boundary_frame) | |||
| { | |||
| if (! non_rt_event) { | |||
| if (! DequeueNonRealtimeEvent()) { | |||
| return true; | |||
| } | |||
| } | |||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
| do { | |||
| // Send out as much of the non-realtime buffer as we can, save for one | |||
| // byte which we will send out when the message is supposed to arrive. | |||
| for (; non_rt_event->size > 1; | |||
| (non_rt_event->size)--, (non_rt_event->buffer)++) { | |||
| if (! STILL_TIME(current_frame, boundary_frame)) { | |||
| return true; | |||
| } | |||
| if (! SendByte(current_frame, *(non_rt_event->buffer))) { | |||
| return false; | |||
| } | |||
| current_frame = send_queue->GetNextScheduleFrame(); | |||
| } | |||
| if (! (STILL_TIME(current_frame, boundary_frame) && | |||
| STILL_TIME(non_rt_event_time, boundary_frame))) { | |||
| return true; | |||
| } | |||
| // There's still time. Try to send the byte. | |||
| if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) { | |||
| return false; | |||
| } | |||
| current_frame = send_queue->GetNextScheduleFrame(); | |||
| if (! DequeueNonRealtimeEvent()) { | |||
| break; | |||
| } | |||
| } while (STILL_TIME(current_frame, boundary_frame)); | |||
| return true; | |||
| } | |||
| bool | |||
| JackMidiRawOutputWriteQueue::WriteRealtimeEvents(jack_nframes_t boundary_frame) | |||
| { | |||
| jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
| if (! rt_event) { | |||
| if (! DequeueRealtimeEvent()) { | |||
| return true; | |||
| } | |||
| } | |||
| for (;;) { | |||
| if (! STILL_TIME(current_frame, boundary_frame)) { | |||
| return false; | |||
| } | |||
| // If: | |||
| // -there's still time before we need to send the realtime event | |||
| // -there's a non-realtime event available for sending | |||
| // -non-realtime data can be scheduled before this event | |||
| if ((rt_event_time > current_frame) && non_rt_event && | |||
| ((non_rt_event->size > 1) || | |||
| (non_rt_event_time < rt_event_time))) { | |||
| return true; | |||
| } | |||
| if (! SendByte(rt_event_time, *(rt_event->buffer))) { | |||
| return false; | |||
| } | |||
| current_frame = send_queue->GetNextScheduleFrame(); | |||
| if (! DequeueRealtimeEvent()) { | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,147 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef __JackMidiRawOutputWriteQueue__ | |||
| #define __JackMidiRawOutputWriteQueue__ | |||
| #include "JackMidiAsyncQueue.h" | |||
| #include "JackMidiSendQueue.h" | |||
| namespace Jack { | |||
| /** | |||
| * This queue enqueues valid MIDI events and modifies them for raw output | |||
| * to a write queue. It has a number of advantages over straight MIDI | |||
| * event copying: | |||
| * | |||
| * -Running status: Status bytes can be omitted when the status byte of the | |||
| * current MIDI message is the same as the status byte of the last sent | |||
| * MIDI message. | |||
| * | |||
| * -Realtime messages: Realtime messages are given priority over | |||
| * non-realtime messages. Realtime bytes are interspersed with | |||
| * non-realtime bytes so that realtime messages can be sent as close as | |||
| * possible to the time they're scheduled for sending. | |||
| * | |||
| * -Time optimization: Bytes in non-realtime messages are sent out early | |||
| * when possible, with the last byte of the message being sent out as close | |||
| * to the specified event time as possible. | |||
| * | |||
| * Use this queue if the MIDI API you're interfacing with allows you to | |||
| * send raw MIDI bytes. | |||
| */ | |||
| class SERVER_EXPORT JackMidiRawOutputWriteQueue: | |||
| public JackMidiWriteQueue { | |||
| private: | |||
| jack_nframes_t last_enqueued_message_time; | |||
| jack_midi_event_t *non_rt_event; | |||
| jack_nframes_t non_rt_event_time; | |||
| JackMidiAsyncQueue *non_rt_queue; | |||
| jack_midi_event_t *rt_event; | |||
| jack_nframes_t rt_event_time; | |||
| JackMidiAsyncQueue *rt_queue; | |||
| jack_midi_data_t running_status; | |||
| JackMidiSendQueue *send_queue; | |||
| bool | |||
| DequeueNonRealtimeEvent(); | |||
| bool | |||
| DequeueRealtimeEvent(); | |||
| bool | |||
| SendByte(jack_nframes_t time, jack_midi_data_t byte); | |||
| bool | |||
| WriteNonRealtimeEvents(jack_nframes_t boundary_frame); | |||
| bool | |||
| WriteRealtimeEvents(jack_nframes_t boundary_frame); | |||
| protected: | |||
| /** | |||
| * Override this method to specify what happens when the write queue | |||
| * says that a 1-byte event is too large for its buffer. Basically, | |||
| * this should never happen. | |||
| */ | |||
| virtual void | |||
| HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte); | |||
| public: | |||
| using JackMidiWriteQueue::EnqueueEvent; | |||
| /** | |||
| * Called to create a new raw write queue. The `send_queue` argument | |||
| * is the queue to write raw bytes to. The optional `max_rt_messages` | |||
| * argument specifies the number of messages that can be enqueued in | |||
| * the internal realtime queue. The optional `max_non_rt_messages` | |||
| * argument specifies the number of messages that can be enqueued in | |||
| * the internal non-realtime queue. The optional `non_rt_size` | |||
| * argument specifies the total number of MIDI bytes that can be put in | |||
| * the non-realtime queue. | |||
| */ | |||
| JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, | |||
| size_t non_rt_size=4096, | |||
| size_t max_non_rt_messages=1024, | |||
| size_t max_rt_messages=128); | |||
| ~JackMidiRawOutputWriteQueue(); | |||
| EnqueueResult | |||
| EnqueueEvent(jack_nframes_t time, size_t size, | |||
| jack_midi_data_t *buffer); | |||
| /** | |||
| * The `Process()` method should be called each time the | |||
| * `EnqueueEvent()` method returns 'OK'. The `Process()` method will | |||
| * return the next frame at which an event should be sent. The return | |||
| * value from `Process()` depends upon the result of writing bytes to | |||
| * the write queue: | |||
| * | |||
| * -If the return value is '0', then all events that have been enqueued | |||
| * in this queue have been sent successfully to the write queue. Don't | |||
| * call `Process()` again until another event has been enqueued. | |||
| * | |||
| * -If the return value is an earlier frame or the current frame, it | |||
| * means that the write queue returned 'BUFFER_FULL', 'ERROR', or | |||
| * 'EVENT_EARLY' when this queue attempted to send the next byte, and | |||
| * that the byte should have already been sent, or is scheduled to be | |||
| * sent *now*. `Process()` should be called again when the write queue | |||
| * can enqueue events again successfully. How to determine when this | |||
| * will happen is left up to the caller. | |||
| * | |||
| * -If the return value is in the future, then `Process()` should be | |||
| * called again at that time, or after another event is enqueued. | |||
| */ | |||
| jack_nframes_t | |||
| Process(jack_nframes_t boundary_frame=0); | |||
| }; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,27 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include "JackMidiReadQueue.h" | |||
| using Jack::JackMidiReadQueue; | |||
| JackMidiReadQueue::~JackMidiReadQueue() | |||
| { | |||
| // Empty | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef __JackMidiReadQueue__ | |||
| #define __JackMidiReadQueue__ | |||
| #include "JackMidiPort.h" | |||
| namespace Jack { | |||
| /** | |||
| * Interface for objects that MIDI events can be read from. | |||
| */ | |||
| class SERVER_EXPORT JackMidiReadQueue { | |||
| public: | |||
| virtual | |||
| ~JackMidiReadQueue(); | |||
| /** | |||
| * Dequeues an event from the queue. Returns the event, or 0 if no | |||
| * events are available for reading. | |||
| * | |||
| * An event dequeued from the read queue is guaranteed to be valid up | |||
| * until another event is dequeued, at which all bets are off. Make | |||
| * sure that you handle each event you dequeue before dequeueing the | |||
| * next event. | |||
| */ | |||
| virtual jack_midi_event_t * | |||
| DequeueEvent() = 0; | |||
| }; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,27 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include "JackMidiReceiveQueue.h" | |||
| using Jack::JackMidiReceiveQueue; | |||
| JackMidiReceiveQueue::~JackMidiReceiveQueue() | |||
| { | |||
| // Empty | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef __JackMidiReceiveQueue__ | |||
| #define __JackMidiReceiveQueue__ | |||
| #include "JackMidiReadQueue.h" | |||
| namespace Jack { | |||
| /** | |||
| * Implemented by MIDI input connections. | |||
| */ | |||
| class SERVER_EXPORT JackMidiReceiveQueue: public JackMidiReadQueue { | |||
| public: | |||
| virtual | |||
| ~JackMidiReceiveQueue(); | |||
| }; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,34 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include "JackMidiSendQueue.h" | |||
| #include "JackMidiUtil.h" | |||
| using Jack::JackMidiSendQueue; | |||
| JackMidiSendQueue::~JackMidiSendQueue() | |||
| { | |||
| // Empty | |||
| } | |||
| jack_nframes_t | |||
| JackMidiSendQueue::GetNextScheduleFrame() | |||
| { | |||
| return GetCurrentFrame(); | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef __JackMidiSendQueue__ | |||
| #define __JackMidiSendQueue__ | |||
| #include "JackMidiWriteQueue.h" | |||
| namespace Jack { | |||
| /** | |||
| * Implemented by MIDI output connections. | |||
| */ | |||
| class SERVER_EXPORT JackMidiSendQueue: public JackMidiWriteQueue { | |||
| public: | |||
| using JackMidiWriteQueue::EnqueueEvent; | |||
| virtual | |||
| ~JackMidiSendQueue(); | |||
| /** | |||
| * Returns the next frame that a MIDI message can be sent at. The | |||
| * default method returns the current frame. | |||
| */ | |||
| virtual jack_nframes_t | |||
| GetNextScheduleFrame(); | |||
| }; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,120 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include "JackEngineControl.h" | |||
| #include "JackFrameTimer.h" | |||
| #include "JackGlobals.h" | |||
| #include "JackMidiUtil.h" | |||
| #include "JackTime.h" | |||
| jack_midi_data_t | |||
| Jack::ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, | |||
| jack_midi_data_t running_status) | |||
| { | |||
| // Stolen and modified from alsa/midi_pack.h | |||
| jack_midi_data_t status = **buffer; | |||
| if ((status >= 0x80) && (status < 0xf0)) { | |||
| if (status == running_status) { | |||
| (*buffer)++; | |||
| (*size)--; | |||
| } else { | |||
| running_status = status; | |||
| } | |||
| } else if (status < 0xf8) { | |||
| running_status = 0; | |||
| } | |||
| return running_status; | |||
| } | |||
| jack_midi_data_t | |||
| Jack::ApplyRunningStatus(jack_midi_event_t *event, | |||
| jack_midi_data_t running_status) | |||
| { | |||
| return ApplyRunningStatus(&(event->size), &(event->buffer), | |||
| running_status); | |||
| } | |||
| jack_nframes_t | |||
| Jack::GetCurrentFrame() | |||
| { | |||
| JackEngineControl *control = GetEngineControl(); | |||
| JackTimer timer; | |||
| control->ReadFrameTime(&timer); | |||
| return timer.Time2Frames(GetMicroSeconds(), control->fBufferSize); | |||
| } | |||
| jack_nframes_t | |||
| Jack::GetFramesFromTime(jack_time_t time) | |||
| { | |||
| JackEngineControl* control = GetEngineControl(); | |||
| JackTimer timer; | |||
| control->ReadFrameTime(&timer); | |||
| return timer.Time2Frames(time, control->fBufferSize); | |||
| } | |||
| jack_nframes_t | |||
| Jack::GetLastFrame() | |||
| { | |||
| return GetEngineControl()->fFrameTimer.ReadCurrentState()->CurFrame(); | |||
| } | |||
| int | |||
| Jack::GetMessageLength(jack_midi_data_t status_byte) | |||
| { | |||
| switch (status_byte & 0xf0) { | |||
| case 0x80: | |||
| case 0x90: | |||
| case 0xa0: | |||
| case 0xb0: | |||
| case 0xe0: | |||
| return 3; | |||
| case 0xc0: | |||
| case 0xd0: | |||
| return 2; | |||
| case 0xf0: | |||
| switch (status_byte) { | |||
| case 0xf0: | |||
| return 0; | |||
| case 0xf1: | |||
| case 0xf3: | |||
| return 2; | |||
| case 0xf2: | |||
| return 3; | |||
| case 0xf4: | |||
| case 0xf5: | |||
| case 0xf7: | |||
| case 0xfd: | |||
| break; | |||
| default: | |||
| return 1; | |||
| } | |||
| } | |||
| return -1; | |||
| } | |||
| jack_time_t | |||
| Jack::GetTimeFromFrames(jack_nframes_t frames) | |||
| { | |||
| JackEngineControl* control = GetEngineControl(); | |||
| JackTimer timer; | |||
| control->ReadFrameTime(&timer); | |||
| return timer.Frames2Time(frames, control->fBufferSize); | |||
| } | |||
| @@ -0,0 +1,102 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef __JackMidiUtil__ | |||
| #define __JackMidiUtil__ | |||
| #include "JackMidiPort.h" | |||
| namespace Jack { | |||
| /** | |||
| * Use this function to optimize MIDI output by omitting unnecessary status | |||
| * bytes. This can't be used with all MIDI APIs, so before using this | |||
| * function, make sure that your MIDI API doesn't require complete MIDI | |||
| * messages to be sent. | |||
| * | |||
| * To start using this function, call this method with pointers to the | |||
| * `size` and `buffer` arguments of the MIDI message you want to send, and | |||
| * set the `running_status` argument to '0'. For each subsequent MIDI | |||
| * message, call this method with pointers to its `size` and `buffer` | |||
| * arguments, and set the `running_status` argument to the return value of | |||
| * the previous call to this function. | |||
| * | |||
| * Note: This function will alter the `size` and `buffer` of your MIDI | |||
| * message for each message that can be optimized. | |||
| */ | |||
| SERVER_EXPORT jack_midi_data_t | |||
| ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, | |||
| jack_midi_data_t running_status=0); | |||
| /** | |||
| * A wrapper function for the above `ApplyRunningStatus` function. | |||
| */ | |||
| SERVER_EXPORT jack_midi_data_t | |||
| ApplyRunningStatus(jack_midi_event_t *event, | |||
| jack_midi_data_t running_status); | |||
| /** | |||
| * Gets the estimated current time in frames. This function has the same | |||
| * functionality as the JACK client API function `jack_frame_time`. | |||
| */ | |||
| SERVER_EXPORT jack_nframes_t | |||
| GetCurrentFrame(); | |||
| /** | |||
| * Gets the estimated frame that will be occurring at the given time. This | |||
| * function has the same functionality as the JACK client API function | |||
| * `jack_time_to_frames`. | |||
| */ | |||
| SERVER_EXPORT jack_nframes_t | |||
| GetFramesFromTime(jack_time_t time); | |||
| /** | |||
| * Gets the precise time at the start of the current process cycle. This | |||
| * function has the same functionality as the JACK client API function | |||
| * `jack_last_frame_time`. | |||
| */ | |||
| SERVER_EXPORT jack_nframes_t | |||
| GetLastFrame(); | |||
| /** | |||
| * Returns the expected message length for the status byte. Returns 0 if | |||
| * the status byte is a system exclusive status byte, or -1 if the status | |||
| * byte is invalid. | |||
| */ | |||
| SERVER_EXPORT int | |||
| GetMessageLength(jack_midi_data_t status_byte); | |||
| /** | |||
| * Gets the estimated time at which the given frame will occur. This | |||
| * function has the same functionality as the JACK client API function | |||
| * `jack_frames_to_time`. | |||
| */ | |||
| SERVER_EXPORT jack_time_t | |||
| GetTimeFromFrames(jack_nframes_t frames); | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,27 @@ | |||
| /* | |||
| Copyright (C) 2010 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include "JackMidiWriteQueue.h" | |||
| using Jack::JackMidiWriteQueue; | |||
| JackMidiWriteQueue::~JackMidiWriteQueue() | |||
| { | |||
| // Empty | |||
| } | |||
| @@ -0,0 +1,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 | |||
| @@ -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; | |||
| } | |||
| @@ -392,7 +392,7 @@ namespace Jack | |||
| { | |||
| JackNetMaster* obj = static_cast<JackNetMaster*>(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; | |||
| @@ -1,287 +0,0 @@ | |||
| /* | |||
| Copyright (C) 2009 Devin Anderson | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include <cassert> | |||
| #include <cstring> | |||
| #include <new> | |||
| #include "JackError.h" | |||
| #include "JackPhysicalMidiInput.h" | |||
| namespace Jack { | |||
| JackPhysicalMidiInput::JackPhysicalMidiInput(size_t buffer_size) | |||
| { | |||
| size_t datum_size = sizeof(jack_midi_data_t); | |||
| assert(buffer_size > 0); | |||
| input_ring = jack_ringbuffer_create((buffer_size + 1) * datum_size); | |||
| if (! input_ring) { | |||
| throw std::bad_alloc(); | |||
| } | |||
| jack_ringbuffer_mlock(input_ring); | |||
| Clear(); | |||
| expected_data_bytes = 0; | |||
| status_byte = 0; | |||
| } | |||
| JackPhysicalMidiInput::~JackPhysicalMidiInput() | |||
| { | |||
| jack_ringbuffer_free(input_ring); | |||
| } | |||
| void | |||
| JackPhysicalMidiInput::Clear() | |||
| { | |||
| jack_ringbuffer_reset(input_ring); | |||
| buffered_bytes = 0; | |||
| unbuffered_bytes = 0; | |||
| } | |||
| void | |||
| JackPhysicalMidiInput::HandleBufferFailure(size_t unbuffered_bytes, | |||
| size_t total_bytes) | |||
| { | |||
| jack_error("%d MIDI byte(s) of a %d byte message could not be buffered - " | |||
| "message dropped", unbuffered_bytes, total_bytes); | |||
| } | |||
| void | |||
| JackPhysicalMidiInput::HandleIncompleteMessage(size_t bytes) | |||
| { | |||
| jack_error("Discarding %d MIDI byte(s) - incomplete message (cable " | |||
| "unplugged?)", bytes); | |||
| } | |||
| void | |||
| JackPhysicalMidiInput::HandleInvalidStatusByte(jack_midi_data_t status) | |||
| { | |||
| jack_error("Dropping invalid MIDI status byte '%x'", | |||
| (unsigned int) status); | |||
| } | |||
| void | |||
| JackPhysicalMidiInput::HandleUnexpectedSysexEnd(size_t bytes) | |||
| { | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 <cassert> | |||
| #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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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<JackDriverInterface*> JackThreadedDriver::GetSlaves() | |||
| @@ -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<JackDriverInterface*> 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; | |||
| @@ -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); | |||
| @@ -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" */ | |||
| @@ -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']: | |||
| @@ -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 <errno.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <getopt.h> | |||
| #include <pthread.h> | |||
| #include <semaphore.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/midiport.h> | |||
| #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; | |||
| } | |||
| @@ -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 = { | |||
| @@ -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) | |||
| @@ -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) | |||
| @@ -0,0 +1,602 @@ | |||
| #include <memory> | |||
| #include <new> | |||
| #include <stdexcept> | |||
| #include <alsa/asoundlib.h> | |||
| #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<snd_rawmidi_info_t *> *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<snd_rawmidi_info_t *> in_info_list; | |||
| std::vector<snd_rawmidi_info_t *> 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<JackALSARawMidiInputPort *> input_ptr; | |||
| if (potential_inputs) { | |||
| input_ports = new JackALSARawMidiInputPort *[potential_inputs]; | |||
| input_ptr.reset(input_ports); | |||
| } | |||
| std::auto_ptr<JackALSARawMidiOutputPort *> 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 | |||
| @@ -0,0 +1,79 @@ | |||
| #ifndef __JackALSARawMidiDriver__ | |||
| #define __JackALSARawMidiDriver__ | |||
| #include <vector> | |||
| #include <alsa/asoundlib.h> | |||
| #include <poll.h> | |||
| #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<snd_rawmidi_info_t *> *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 | |||
| @@ -0,0 +1,124 @@ | |||
| #include <memory> | |||
| #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<JackALSARawMidiReceiveQueue> receive_ptr(receive_queue); | |||
| thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); | |||
| std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue); | |||
| write_queue = new JackMidiBufferWriteQueue(); | |||
| std::auto_ptr<JackMidiBufferWriteQueue> 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; | |||
| } | |||
| @@ -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 | |||
| @@ -0,0 +1,153 @@ | |||
| #include <memory> | |||
| #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<JackMidiBufferReadQueue> read_ptr(read_queue); | |||
| send_queue = new JackALSARawMidiSendQueue(rawmidi); | |||
| std::auto_ptr<JackALSARawMidiSendQueue> send_ptr(send_queue); | |||
| thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); | |||
| std::auto_ptr<JackMidiAsyncQueue> 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; | |||
| } | |||
| @@ -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 | |||
| @@ -0,0 +1,159 @@ | |||
| #include <stdexcept> | |||
| #include <string> | |||
| #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; | |||
| } | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| #ifndef __JackALSARawMidiPort__ | |||
| #define __JackALSARawMidiPort__ | |||
| #include <alsa/asoundlib.h> | |||
| #include <poll.h> | |||
| #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 | |||
| @@ -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; | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| #ifndef __JackALSARawMidiReceiveQueue__ | |||
| #define __JackALSARawMidiReceiveQueue__ | |||
| #include <alsa/asoundlib.h> | |||
| #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 | |||
| @@ -0,0 +1,40 @@ | |||
| #include <cassert> | |||
| #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; | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| #ifndef __JackALSARawMidiSendQueue__ | |||
| #define __JackALSARawMidiSendQueue__ | |||
| #include <alsa/asoundlib.h> | |||
| #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 | |||
| @@ -36,8 +36,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| #include <string.h> | |||
| #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); | |||
| @@ -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 <cassert> | |||
| #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; | |||
| } | |||
| } | |||
| @@ -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 <memory> | |||
| #include "JackFFADOMidiInputPort.h" | |||
| #include "JackMidiUtil.h" | |||
| using Jack::JackFFADOMidiInputPort; | |||
| JackFFADOMidiInputPort::JackFFADOMidiInputPort(size_t max_bytes) | |||
| { | |||
| event = 0; | |||
| receive_queue = new JackFFADOMidiReceiveQueue(); | |||
| std::auto_ptr<JackFFADOMidiReceiveQueue> receive_queue_ptr(receive_queue); | |||
| write_queue = new JackMidiBufferWriteQueue(); | |||
| std::auto_ptr<JackMidiBufferWriteQueue> 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); | |||
| } | |||
| @@ -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 | |||
| @@ -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 <cassert> | |||
| #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; | |||
| } | |||
| } | |||
| @@ -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 <memory> | |||
| #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<JackMidiBufferReadQueue> read_queue_ptr(read_queue); | |||
| send_queue = new JackFFADOMidiSendQueue(); | |||
| std::auto_ptr<JackFFADOMidiSendQueue> 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); | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| }; | |||
| @@ -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 <cassert> | |||
| #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; | |||
| } | |||
| @@ -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); | |||
| }; | |||
| @@ -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") | |||
| @@ -62,7 +62,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| #define __JackMachThread__ | |||
| #include "JackPosixThread.h" | |||
| #import <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h> | |||
| #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h> | |||
| #include <mach/thread_policy.h> | |||
| #include <mach/thread_act.h> | |||
| @@ -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 = "<absolute>"; }; | |||
| 4B37C20406DF1FBE0016E567 /* CALatencyLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CALatencyLog.h; path = /Developer/Examples/CoreAudio/PublicUtility/CALatencyLog.h; sourceTree = "<absolute>"; }; | |||
| 4B37C20906DF1FE20016E567 /* latency.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = latency.c; path = /Developer/Examples/CoreAudio/PublicUtility/latency.c; sourceTree = "<absolute>"; }; | |||
| @@ -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 = "<absolute>"; }; | |||
| 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 = "<group>"; | |||
| @@ -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 = "<group>"; | |||
| }; | |||
| 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 = ( | |||
| @@ -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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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<AudioDeviceID> 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); | |||
| } | |||
| } | |||
| @@ -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++) { | |||
| @@ -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<AudioDeviceID> captureDeviceID, vector<AudioDeviceID> 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); | |||
| @@ -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 <stdexcept> | |||
| #include <mach/mach_time.h> | |||
| #include "JackCoreMidiDriver.h" | |||
| #include "JackGraphManager.h" | |||
| #include "JackServer.h" | |||
| #include "JackCoreMidiUtil.h" | |||
| #include "JackEngineControl.h" | |||
| #include "JackDriverLoader.h" | |||
| #include <mach/mach_time.h> | |||
| #include <assert.h> | |||
| #include <iostream> | |||
| #include <sstream> | |||
| #include <string> | |||
| 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 | |||
| @@ -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 <CoreMIDI/CoreMIDI.h> | |||
| #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 | |||
| @@ -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 <cassert> | |||
| #include <memory> | |||
| #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<JackMidiAsyncQueue> thread_queue_ptr(thread_queue); | |||
| write_queue = new JackMidiBufferWriteQueue(); | |||
| std::auto_ptr<JackMidiBufferWriteQueue> 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; | |||
| } | |||
| @@ -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 | |||
| @@ -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 <cassert> | |||
| #include <cerrno> | |||
| #include <cstring> | |||
| #include <new> | |||
| #include <stdexcept> | |||
| #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<JackMidiBufferReadQueue> read_queue_ptr(read_queue); | |||
| thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); | |||
| std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue); | |||
| thread = new JackThread(this); | |||
| std::auto_ptr<JackThread> 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; | |||
| } | |||
| @@ -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 <semaphore.h> | |||
| #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 | |||
| @@ -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 <sstream> | |||
| #include <stdexcept> | |||
| #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 | |||
| } | |||
| @@ -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 | |||
| @@ -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 <sstream> | |||
| #include <stdexcept> | |||
| #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; | |||
| } | |||
| @@ -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 | |||
| @@ -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 <cassert> | |||
| #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; | |||
| } | |||
| @@ -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 <CoreMIDI/CoreMIDI.h> | |||
| #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 | |||
| @@ -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 <sstream> | |||
| #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()); | |||
| } | |||
| @@ -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 <string> | |||
| #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h> | |||
| #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/Debugging.h> | |||
| namespace Jack { | |||
| std::string | |||
| GetMacOSErrorString(OSStatus status); | |||
| void | |||
| WriteMacOSError(const char *jack_function, const char *mac_function, | |||
| OSStatus status); | |||
| } | |||
| #endif | |||
| @@ -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 <sstream> | |||
| #include <stdexcept> | |||
| #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); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 <sstream> | |||
| #include <stdexcept> | |||
| #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; | |||
| } | |||
| @@ -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 | |||
| @@ -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 { | |||
| @@ -106,6 +106,12 @@ | |||
| <Option compilerVar="WINDRES" /> | |||
| </Unit> | |||
| <Unit filename="winmme\JackWinMMEDriver.cpp" /> | |||
| <Unit filename="winmme\JackWinMMEDriver.h" /> | |||
| <Unit filename="winmme\JackWinMMEInputPort.cpp" /> | |||
| <Unit filename="winmme\JackWinMMEInputPort.h" /> | |||
| <Unit filename="winmme\JackWinMMEOutputPort.cpp" /> | |||
| <Unit filename="winmme\JackWinMMEOutputPort.h" /> | |||
| <Unit filename="winmme\JackWinMMEPort.cpp" /> | |||
| <Extensions> | |||
| <code_completion /> | |||
| <envvars /> | |||
| @@ -56,7 +56,7 @@ | |||
| <Project filename="multiple_metro.cbp"> | |||
| <Depends filename="libjack.cbp" /> | |||
| </Project> | |||
| <Project filename="jack_winmme.cbp" /> | |||
| <Project filename="jack_loopback.cbp" active="1" /> | |||
| <Project filename="jack_winmme.cbp" active="1" /> | |||
| <Project filename="jack_loopback.cbp" /> | |||
| </Workspace> | |||
| </CodeBlocks_workspace_file> | |||