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) | int JackAudioDriver::SetBufferSize(jack_nframes_t buffer_size) | ||||
| { | { | ||||
| // Update engine and graph manager state | |||||
| fEngineControl->fBufferSize = buffer_size; | fEngineControl->fBufferSize = buffer_size; | ||||
| fGraphManager->SetBufferSize(buffer_size); | fGraphManager->SetBufferSize(buffer_size); | ||||
| fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec | fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec | ||||
| if (!fEngineControl->fTimeOut) | if (!fEngineControl->fTimeOut) | ||||
| fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); | 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) | 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 | fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec | ||||
| if (!fEngineControl->fTimeOut) | if (!fEngineControl->fTimeOut) | ||||
| fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); | fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); | ||||
| return 0; | |||||
| return JackDriver::SetSampleRate(sample_rate); | |||||
| } | } | ||||
| int JackAudioDriver::Open(jack_nframes_t buffer_size, | 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); | 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() | int JackAudioDriver::Attach() | ||||
| { | { | ||||
| JackPort* port; | JackPort* port; | ||||
| jack_port_id_t port_index; | jack_port_id_t port_index; | ||||
| char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | ||||
| char alias[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; | int i; | ||||
| jack_log("JackAudioDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); | 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 = fGraphManager->GetPort(port_index); | ||||
| port->SetAlias(alias); | port->SetAlias(alias); | ||||
| range.min = range.max = fEngineControl->fBufferSize + fCaptureLatency; | |||||
| port->SetLatencyRange(JackCaptureLatency, &range); | |||||
| fCapturePortList[i] = port_index; | fCapturePortList[i] = port_index; | ||||
| jack_log("JackAudioDriver::Attach fCapturePortList[i] port_index = %ld", 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 = fGraphManager->GetPort(port_index); | ||||
| port->SetAlias(alias); | 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; | fPlaybackPortList[i] = port_index; | ||||
| jack_log("JackAudioDriver::Attach fPlaybackPortList[i] port_index = %ld", 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); | jack_error("Cannot register monitor port for %s", name); | ||||
| return -1; | return -1; | ||||
| } else { | } 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; | return 0; | ||||
| } | } | ||||
| @@ -193,9 +212,9 @@ int JackAudioDriver::ProcessNull() | |||||
| JackDriver::CycleTakeBeginTime(); | JackDriver::CycleTakeBeginTime(); | ||||
| if (fEngineControl->fSyncMode) { | if (fEngineControl->fSyncMode) { | ||||
| ProcessGraphSync(); | |||||
| ProcessGraphSyncMaster(); | |||||
| } else { | } else { | ||||
| ProcessGraphAsync(); | |||||
| ProcessGraphAsyncMaster(); | |||||
| } | } | ||||
| // Keep end cycle time | // Keep end cycle time | ||||
| @@ -230,9 +249,9 @@ int JackAudioDriver::ProcessAsync() | |||||
| // Process graph | // Process graph | ||||
| if (fIsMaster) { | if (fIsMaster) { | ||||
| ProcessGraphAsync(); | |||||
| ProcessGraphAsyncMaster(); | |||||
| } else { | } else { | ||||
| fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); | |||||
| ProcessGraphAsyncSlave(); | |||||
| } | } | ||||
| // Keep end cycle time | // Keep end cycle time | ||||
| @@ -255,12 +274,12 @@ int JackAudioDriver::ProcessSync() | |||||
| // Process graph | // Process graph | ||||
| if (fIsMaster) { | if (fIsMaster) { | ||||
| if (ProcessGraphSync() < 0) { | |||||
| if (ProcessGraphSyncMaster() < 0) { | |||||
| jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); | jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); | ||||
| goto end; | goto end; | ||||
| } | } | ||||
| } else { | } else { | ||||
| if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { | |||||
| if (ProcessGraphSyncSlave() < 0) { | |||||
| jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); | jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); | ||||
| goto end; | goto end; | ||||
| } | } | ||||
| @@ -279,27 +298,50 @@ end: | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| void JackAudioDriver::ProcessGraphAsync() | |||||
| void JackAudioDriver::ProcessGraphAsyncMaster() | |||||
| { | { | ||||
| // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle | // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle | ||||
| if (!fEngine->Process(fBeginDateUst, fEndDateUst)) | 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; | int res = 0; | ||||
| // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle | // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle | ||||
| if (fEngine->Process(fBeginDateUst, fEndDateUst)) { | 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; | 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) { | if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { | ||||
| jack_error("JackAudioDriver::ProcessGraphSync: SuspendRefNum error, engine may now behave abnormally!!"); | jack_error("JackAudioDriver::ProcessGraphSync: SuspendRefNum error, engine may now behave abnormally!!"); | ||||
| res = -1; | res = -1; | ||||
| @@ -312,6 +354,11 @@ int JackAudioDriver::ProcessGraphSync() | |||||
| return res; | return res; | ||||
| } | } | ||||
| int JackAudioDriver::ProcessGraphSyncSlave() | |||||
| { | |||||
| return fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); | |||||
| } | |||||
| int JackAudioDriver::Start() | int JackAudioDriver::Start() | ||||
| { | { | ||||
| int res = JackDriver::Start(); | int res = JackDriver::Start(); | ||||
| @@ -35,8 +35,12 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver | |||||
| protected: | protected: | ||||
| void ProcessGraphAsync(); | |||||
| int ProcessGraphSync(); | |||||
| void ProcessGraphAsyncMaster(); | |||||
| void ProcessGraphAsyncSlave(); | |||||
| int ProcessGraphSyncMaster(); | |||||
| int ProcessGraphSyncSlave(); | |||||
| void WaitUntilNextCycle(); | void WaitUntilNextCycle(); | ||||
| virtual int ProcessAsync(); | virtual int ProcessAsync(); | ||||
| @@ -58,6 +62,7 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver | |||||
| jack_default_audio_sample_t* GetMonitorBuffer(int port_index); | jack_default_audio_sample_t* GetMonitorBuffer(int port_index); | ||||
| void HandleLatencyCallback(int status); | void HandleLatencyCallback(int status); | ||||
| void UpdateLatencies(); | |||||
| public: | public: | ||||
| @@ -300,19 +300,42 @@ void JackDriver::RemoveSlave(JackDriverInterface* slave) | |||||
| fSlaveList.remove(slave); | fSlaveList.remove(slave); | ||||
| } | } | ||||
| int JackDriver::ProcessSlaves() | |||||
| int JackDriver::ProcessReadSlaves() | |||||
| { | { | ||||
| int res = 0; | int res = 0; | ||||
| list<JackDriverInterface*>::const_iterator it; | list<JackDriverInterface*>::const_iterator it; | ||||
| for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { | for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { | ||||
| JackDriverInterface* slave = *it; | JackDriverInterface* slave = *it; | ||||
| if (slave->Process() < 0) | |||||
| if (slave->ProcessRead() < 0) | |||||
| res = -1; | res = -1; | ||||
| } | } | ||||
| return res; | 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() | int JackDriver::Process() | ||||
| { | { | ||||
| return 0; | return 0; | ||||
| @@ -395,12 +418,28 @@ bool JackDriver::IsFixedBufferSize() | |||||
| int JackDriver::SetBufferSize(jack_nframes_t buffer_size) | 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) | 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() | bool JackDriver::Initialize() | ||||
| @@ -34,6 +34,7 @@ namespace Jack | |||||
| class JackLockedEngine; | class JackLockedEngine; | ||||
| class JackGraphManager; | class JackGraphManager; | ||||
| struct JackEngineControl; | struct JackEngineControl; | ||||
| class JackSlaveDriverInterface; | |||||
| /*! | /*! | ||||
| \brief The base interface for drivers. | \brief The base interface for drivers. | ||||
| @@ -91,10 +92,17 @@ class SERVER_EXPORT JackDriverInterface | |||||
| virtual void SetMaster(bool onoff) = 0; | virtual void SetMaster(bool onoff) = 0; | ||||
| virtual bool GetMaster() = 0; | virtual bool GetMaster() = 0; | ||||
| virtual void AddSlave(JackDriverInterface* slave) = 0; | virtual void AddSlave(JackDriverInterface* slave) = 0; | ||||
| virtual void RemoveSlave(JackDriverInterface* slave) = 0; | virtual void RemoveSlave(JackDriverInterface* slave) = 0; | ||||
| virtual std::list<JackDriverInterface*> GetSlaves() = 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 IsRealTime() const = 0; | ||||
| virtual bool IsRunning() const = 0; | virtual bool IsRunning() const = 0; | ||||
| @@ -159,11 +167,11 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface | |||||
| void AddSlave(JackDriverInterface* slave); | void AddSlave(JackDriverInterface* slave); | ||||
| void RemoveSlave(JackDriverInterface* slave); | void RemoveSlave(JackDriverInterface* slave); | ||||
| std::list<JackDriverInterface*> GetSlaves() | std::list<JackDriverInterface*> GetSlaves() | ||||
| { | { | ||||
| return fSlaveList; | return fSlaveList; | ||||
| } | } | ||||
| int ProcessSlaves(); | |||||
| virtual int Open(); | virtual int Open(); | ||||
| @@ -200,10 +208,17 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface | |||||
| virtual int Write(); | virtual int Write(); | ||||
| virtual int Start(); | virtual int Start(); | ||||
| virtual int StartSlaves(); | |||||
| virtual int Stop(); | virtual int Stop(); | ||||
| virtual int StartSlaves(); | |||||
| virtual int StopSlaves(); | virtual int StopSlaves(); | ||||
| int ProcessReadSlaves(); | |||||
| int ProcessWriteSlaves(); | |||||
| int ProcessRead(); | |||||
| int ProcessWrite(); | |||||
| virtual bool IsFixedBufferSize(); | virtual bool IsFixedBufferSize(); | ||||
| virtual int SetBufferSize(jack_nframes_t buffer_size); | virtual int SetBufferSize(jack_nframes_t buffer_size); | ||||
| virtual int SetSampleRate(jack_nframes_t sample_rate); | virtual int SetSampleRate(jack_nframes_t sample_rate); | ||||
| @@ -78,6 +78,7 @@ int JackDummyDriver::Process() | |||||
| int JackDummyDriver::SetBufferSize(jack_nframes_t buffer_size) | int JackDummyDriver::SetBufferSize(jack_nframes_t buffer_size) | ||||
| { | { | ||||
| // Generic change, never fails | |||||
| JackAudioDriver::SetBufferSize(buffer_size); | JackAudioDriver::SetBufferSize(buffer_size); | ||||
| fWaitTime = (unsigned long)((((float)buffer_size) / ((float)fEngineControl->fSampleRate)) * 1000000.0f); | fWaitTime = (unsigned long)((((float)buffer_size) / ((float)fEngineControl->fSampleRate)) * 1000000.0f); | ||||
| return 0; | return 0; | ||||
| @@ -28,26 +28,72 @@ namespace Jack | |||||
| int JackFreewheelDriver::Process() | 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 | 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 */ | /* We have a client time-out error, but still continue to process, until a better recovery strategy is chosen */ | ||||
| return 0; | 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; | return 0; | ||||
| } | } | ||||
| int JackFreewheelDriver::ProcessWriteAsync() | |||||
| { | |||||
| return 0; | |||||
| } | |||||
| } // end of namespace | } // end of namespace | ||||
| @@ -46,6 +46,16 @@ class JackFreewheelDriver : public JackDriver | |||||
| } | } | ||||
| int Process(); | int Process(); | ||||
| int ProcessRead(); | |||||
| int ProcessWrite(); | |||||
| int ProcessReadSync(); | |||||
| int ProcessWriteSync(); | |||||
| int ProcessReadAsync(); | |||||
| int ProcessWriteAsync(); | |||||
| }; | }; | ||||
| } // end of namespace | } // end of namespace | ||||
| @@ -30,20 +30,61 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| namespace Jack | 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 | // Loopback copy | ||||
| for (int i = 0; i < fCaptureChannels; i++) { | for (int i = 0; i < fCaptureChannels; i++) { | ||||
| memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); | 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; | return 0; | ||||
| } | } | ||||
| @@ -33,15 +33,24 @@ namespace Jack | |||||
| class JackLoopbackDriver : public JackAudioDriver | class JackLoopbackDriver : public JackAudioDriver | ||||
| { | { | ||||
| private: | |||||
| virtual int ProcessReadSync(); | |||||
| virtual int ProcessWriteSync(); | |||||
| virtual int ProcessReadAsync(); | |||||
| virtual int ProcessWriteAsync(); | |||||
| public: | public: | ||||
| JackLoopbackDriver(JackLockedEngine* engine, JackSynchro* table) | JackLoopbackDriver(JackLockedEngine* engine, JackSynchro* table) | ||||
| : JackAudioDriver("loopback", "", engine, table) | |||||
| : JackAudioDriver("loopback", "loopback", engine, table) | |||||
| {} | {} | ||||
| virtual ~JackLoopbackDriver() | virtual ~JackLoopbackDriver() | ||||
| {} | {} | ||||
| int Process(); | |||||
| virtual int ProcessRead(); | |||||
| virtual int ProcessWrite(); | |||||
| }; | }; | ||||
| } // end of namespace | } // 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); | jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); | ||||
| } | } | ||||
| UpdateLatencies(); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -133,33 +134,108 @@ int JackMidiDriver::Write() | |||||
| return 0; | 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() | int JackMidiDriver::ProcessNull() | ||||
| { | { | ||||
| return 0; | 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 | // Read input buffers for the current cycle | ||||
| if (Read() < 0) { | 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) { | 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; | return 0; | ||||
| } | } | ||||
| @@ -39,15 +39,23 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver | |||||
| int fCaptureChannels; | int fCaptureChannels; | ||||
| int fPlaybackChannels; | int fPlaybackChannels; | ||||
| jack_ringbuffer_t* fRingBuffer[DRIVER_PORT_NUM]; | jack_ringbuffer_t* fRingBuffer[DRIVER_PORT_NUM]; | ||||
| jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; | jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; | ||||
| jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; | jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; | ||||
| JackMidiBuffer* GetInputBuffer(int port_index); | JackMidiBuffer* GetInputBuffer(int port_index); | ||||
| JackMidiBuffer* GetOutputBuffer(int port_index); | JackMidiBuffer* GetOutputBuffer(int port_index); | ||||
| virtual int ProcessReadSync(); | |||||
| virtual int ProcessWriteSync(); | |||||
| virtual int ProcessReadAsync(); | |||||
| virtual int ProcessWriteAsync(); | |||||
| virtual void UpdateLatencies(); | |||||
| public: | public: | ||||
| JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); | 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, | const char* playback_driver_name, | ||||
| jack_nframes_t capture_latency, | jack_nframes_t capture_latency, | ||||
| jack_nframes_t playback_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 ProcessNull(); | ||||
| virtual int Attach(); | virtual int Attach(); | ||||
| virtual int Detach(); | virtual int Detach(); | ||||
| virtual int Read(); | virtual int Read(); | ||||
| virtual int Write(); | virtual int Write(); | ||||
| }; | }; | ||||
| } // end of namespace | } // end of namespace | ||||
| @@ -55,7 +55,6 @@ SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time | |||||
| lost_events++; | lost_events++; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| JackMidiEvent* event = &events[event_count++]; | JackMidiEvent* event = &events[event_count++]; | ||||
| event->time = time; | event->time = time; | ||||
| event->size = size; | event->size = size; | ||||
| @@ -90,7 +89,7 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count | |||||
| { | { | ||||
| JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer); | JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer); | ||||
| if (!mix->IsValid()) { | if (!mix->IsValid()) { | ||||
| jack_error("MIDI: invalid mix buffer"); | |||||
| jack_error("Jack::MidiBufferMixdown - invalid mix buffer"); | |||||
| return; | return; | ||||
| } | } | ||||
| mix->Reset(nframes); | mix->Reset(nframes); | ||||
| @@ -98,8 +97,10 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count | |||||
| int event_count = 0; | int event_count = 0; | ||||
| for (int i = 0; i < src_count; ++i) { | for (int i = 0; i < src_count; ++i) { | ||||
| JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]); | JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]); | ||||
| if (!buf->IsValid()) | |||||
| if (!buf->IsValid()) { | |||||
| jack_error("Jack::MidiBufferMixdown - invalid source buffer"); | |||||
| return; | return; | ||||
| } | |||||
| buf->mix_index = 0; | buf->mix_index = 0; | ||||
| event_count += buf->event_count; | event_count += buf->event_count; | ||||
| mix->lost_events += buf->lost_events; | 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 | //allocate midi ports lists | ||||
| fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels]; | fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels]; | ||||
| fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels]; | 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 | //register jack ports | ||||
| if ( AllocPorts() != 0 ) | if ( AllocPorts() != 0 ) | ||||
| @@ -362,20 +370,32 @@ namespace Jack | |||||
| { | { | ||||
| jack_log ( "JackNetDriver::FreePorts" ); | 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; | return 0; | ||||
| } | } | ||||
| @@ -392,7 +392,7 @@ namespace Jack | |||||
| { | { | ||||
| JackNetMaster* obj = static_cast<JackNetMaster*>(arg); | JackNetMaster* obj = static_cast<JackNetMaster*>(arg); | ||||
| if (nframes != obj->fParams.fPeriodSize) { | 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(); | obj->Exit(); | ||||
| } | } | ||||
| return 0; | 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) { | if (fAudioDriver->SetBufferSize(buffer_size) == 0) { | ||||
| fFreewheelDriver->SetBufferSize(buffer_size); | |||||
| fEngine->NotifyBufferSize(buffer_size); | fEngine->NotifyBufferSize(buffer_size); | ||||
| return fAudioDriver->Start(); | return fAudioDriver->Start(); | ||||
| } else { // Failure: try to restore current value | } else { // Failure: try to restore current value | ||||
| jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size); | jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size); | ||||
| fAudioDriver->SetBufferSize(current_buffer_size); | fAudioDriver->SetBufferSize(current_buffer_size); | ||||
| fFreewheelDriver->SetBufferSize(current_buffer_size); | |||||
| fAudioDriver->Start(); | fAudioDriver->Start(); | ||||
| // SetBufferSize actually failed, so return an error... | // SetBufferSize actually failed, so return an error... | ||||
| return -1; | return -1; | ||||
| @@ -127,9 +127,24 @@ void JackThreadedDriver::RemoveSlave(JackDriverInterface* slave) | |||||
| fDriver->RemoveSlave(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() | std::list<JackDriverInterface*> JackThreadedDriver::GetSlaves() | ||||
| @@ -89,10 +89,17 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi | |||||
| virtual void SetMaster(bool onoff); | virtual void SetMaster(bool onoff); | ||||
| virtual bool GetMaster(); | virtual bool GetMaster(); | ||||
| virtual void AddSlave(JackDriverInterface* slave); | virtual void AddSlave(JackDriverInterface* slave); | ||||
| virtual void RemoveSlave(JackDriverInterface* slave); | virtual void RemoveSlave(JackDriverInterface* slave); | ||||
| virtual std::list<JackDriverInterface*> GetSlaves(); | 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 int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); | ||||
| virtual JackClientControl* GetClientControl() const; | virtual JackClientControl* GetClientControl() const; | ||||
| @@ -476,7 +476,10 @@ int main(int argc, char* argv[]) | |||||
| fprintf(stderr, "Unknown driver \"%s\"\n", *it); | fprintf(stderr, "Unknown driver \"%s\"\n", *it); | ||||
| goto close_server; | 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 | // Loopback driver | ||||
| @@ -491,7 +494,10 @@ int main(int argc, char* argv[]) | |||||
| value.ui = loopback; | value.ui = loopback; | ||||
| jackctl_parameter_set_value(param, &value); | 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); | fprintf(stderr, "Unknown internal \"%s\"\n", *it); | ||||
| goto stop_server; | 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); | notify_server_start(server_name); | ||||
| @@ -14,7 +14,7 @@ extern "C" | |||||
| { | { | ||||
| #endif | #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 MAX_SHM_ID 256 /* generally about 16 per server */ | ||||
| #define JACK_SERVER_NAME_SIZE 256 /* maximum length of server name */ | #define JACK_SERVER_NAME_SIZE 256 /* maximum length of server name */ | ||||
| #define JACK_SHM_MAGIC 0x4a41434b /* shm magic number: "JACK" */ | #define JACK_SHM_MAGIC 0x4a41434b /* shm magic number: "JACK" */ | ||||
| @@ -130,8 +130,21 @@ def build(bld): | |||||
| 'JackNetTool.cpp', | 'JackNetTool.cpp', | ||||
| 'JackNetInterface.cpp', | 'JackNetInterface.cpp', | ||||
| 'JackArgParser.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']: | 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_server_control' : 'server_control.cpp', | ||||
| 'jack_latent_client' : 'latent_client.c', | 'jack_latent_client' : 'latent_client.c', | ||||
| 'jack_midi_dump' : 'midi_dump.c', | 'jack_midi_dump' : 'midi_dump.c', | ||||
| 'jack_midi_latency_test' : 'midi_latency_test.c' | |||||
| } | } | ||||
| example_libs = { | example_libs = { | ||||
| @@ -55,8 +55,11 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) | |||||
| ((alsa_driver_t *)fDriver)->frame_rate); | ((alsa_driver_t *)fDriver)->frame_rate); | ||||
| if (res == 0) { // update fEngineControl and fGraphManager | 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 { | } else { | ||||
| // Restore old values | |||||
| alsa_driver_reset_parameters((alsa_driver_t *)fDriver, fEngineControl->fBufferSize, | alsa_driver_reset_parameters((alsa_driver_t *)fDriver, fEngineControl->fBufferSize, | ||||
| ((alsa_driver_t *)fDriver)->user_nperiods, | ((alsa_driver_t *)fDriver)->user_nperiods, | ||||
| ((alsa_driver_t *)fDriver)->frame_rate); | ((alsa_driver_t *)fDriver)->frame_rate); | ||||
| @@ -65,6 +68,29 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) | |||||
| return res; | 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() | int JackAlsaDriver::Attach() | ||||
| { | { | ||||
| JackPort* port; | JackPort* port; | ||||
| @@ -72,7 +98,6 @@ int JackAlsaDriver::Attach() | |||||
| unsigned long port_flags = (unsigned long)CaptureDriverFlags; | unsigned long port_flags = (unsigned long)CaptureDriverFlags; | ||||
| char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | ||||
| char alias[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(fCaptureChannels < DRIVER_PORT_NUM); | ||||
| assert(fPlaybackChannels < DRIVER_PORT_NUM); | assert(fPlaybackChannels < DRIVER_PORT_NUM); | ||||
| @@ -97,8 +122,6 @@ int JackAlsaDriver::Attach() | |||||
| } | } | ||||
| port = fGraphManager->GetPort(port_index); | port = fGraphManager->GetPort(port_index); | ||||
| port->SetAlias(alias); | 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; | fCapturePortList[i] = port_index; | ||||
| jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_index); | jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_index); | ||||
| } | } | ||||
| @@ -114,11 +137,6 @@ int JackAlsaDriver::Attach() | |||||
| } | } | ||||
| port = fGraphManager->GetPort(port_index); | port = fGraphManager->GetPort(port_index); | ||||
| port->SetAlias(alias); | 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; | fPlaybackPortList[i] = port_index; | ||||
| jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", 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) { | 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); | jack_error ("ALSA: cannot register monitor port for %s", name); | ||||
| } else { | } else { | ||||
| port = fGraphManager->GetPort(port_index); | |||||
| range.min = range.max = alsa_driver->frames_per_cycle; | |||||
| port->SetLatencyRange(JackCaptureLatency, &range); | |||||
| fMonitorPortList[i] = port_index; | fMonitorPortList[i] = port_index; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| UpdateLatencies(); | |||||
| if (alsa_driver->midi) { | if (alsa_driver->midi) { | ||||
| int err = (alsa_driver->midi->attach)(alsa_driver->midi); | int err = (alsa_driver->midi->attach)(alsa_driver->midi); | ||||
| if (err) | if (err) | ||||
| @@ -42,6 +42,8 @@ class JackAlsaDriver : public JackAudioDriver | |||||
| int fReservedCaptureDevice; | int fReservedCaptureDevice; | ||||
| int fReservedPlaybackDevice; | int fReservedPlaybackDevice; | ||||
| void UpdateLatencies(); | |||||
| public: | public: | ||||
| JackAlsaDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) | 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 <string.h> | ||||
| #include "JackFFADODriver.h" | #include "JackFFADODriver.h" | ||||
| #include "JackFFADOMidiInput.h" | |||||
| #include "JackFFADOMidiOutput.h" | |||||
| #include "JackFFADOMidiInputPort.h" | |||||
| #include "JackFFADOMidiOutputPort.h" | |||||
| #include "JackEngineControl.h" | #include "JackEngineControl.h" | ||||
| #include "JackClientControl.h" | #include "JackClientControl.h" | ||||
| #include "JackPort.h" | #include "JackPort.h" | ||||
| @@ -94,14 +94,9 @@ JackFFADODriver::ffado_driver_read (ffado_driver_t * driver, jack_nframes_t nfra | |||||
| /* process the midi data */ | /* process the midi data */ | ||||
| for (chn = 0; chn < driver->capture_nchannels; chn++) { | for (chn = 0; chn < driver->capture_nchannels; chn++) { | ||||
| if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { | 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); | 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)); | memset(midi_buffer, 0, nframes * sizeof(uint32_t)); | ||||
| buf = (jack_default_audio_sample_t *) fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes); | buf = (jack_default_audio_sample_t *) fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes); | ||||
| ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(midi_buffer)); | 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 | } else { // always have a valid buffer | ||||
| ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(driver->nullbuffer)); | 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); | ffado_streaming_transfer_playback_buffers(driver->dev); | ||||
| printExit(); | printExit(); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -476,7 +462,7 @@ int JackFFADODriver::Attach() | |||||
| printError(" cannot enable port %s", buf); | 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 | // setup the midi buffer | ||||
| driver->capture_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); | 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 | // This constructor optionally accepts arguments for the | ||||
| // non-realtime buffer size and the realtime buffer size. Ideally, | // non-realtime buffer size and the realtime buffer size. Ideally, | ||||
| // these would become command-line options for the FFADO driver. | // 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)); | driver->playback_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); | ||||
| port = fGraphManager->GetPort(port_index); | 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); | port->SetLatencyRange(JackPlaybackLatency, &range); | ||||
| fPlaybackPortList[chn] = port_index; | fPlaybackPortList[chn] = port_index; | ||||
| jack_log("JackFFADODriver::Attach fPlaybackPortList[i] %ld ", 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) | if (driver->capture_channels[chn].midi_buffer) | ||||
| free(driver->capture_channels[chn].midi_buffer); | free(driver->capture_channels[chn].midi_buffer); | ||||
| if (driver->capture_channels[chn].midi_input) | 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); | free(driver->capture_channels); | ||||
| @@ -608,7 +594,7 @@ int JackFFADODriver::Detach() | |||||
| if (driver->playback_channels[chn].midi_buffer) | if (driver->playback_channels[chn].midi_buffer) | ||||
| free(driver->playback_channels[chn].midi_buffer); | free(driver->playback_channels[chn].midi_buffer); | ||||
| if (driver->playback_channels[chn].midi_output) | 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); | 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 | 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 | 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 { | namespace Jack { | ||||
| class JackFFADOMidiInput: public JackPhysicalMidiInput { | |||||
| class JackFFADOMidiReceiveQueue: public JackMidiReceiveQueue { | |||||
| private: | private: | ||||
| jack_midi_data_t byte; | |||||
| jack_midi_event_t event; | |||||
| jack_nframes_t index; | |||||
| uint32_t *input_buffer; | 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: | 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 | 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 | 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 { | namespace Jack { | ||||
| class JackFFADOMidiOutput: public JackPhysicalMidiOutput { | |||||
| class JackFFADOMidiSendQueue: public JackMidiSendQueue { | |||||
| private: | private: | ||||
| jack_nframes_t index; | |||||
| jack_nframes_t last_frame; | |||||
| jack_nframes_t length; | |||||
| uint32_t *output_buffer; | 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' | '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', | 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: | if bld.env['BUILD_DRIVER_ALSA'] == True: | ||||
| create_jack_driver_obj(bld, 'alsa', alsa_driver_src, "ALSA") | 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: | if bld.env['BUILD_DRIVER_FREEBOB'] == True: | ||||
| create_jack_driver_obj(bld, 'freebob', 'freebob/JackFreebobDriver.cpp', "LIBFREEBOB") | 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__ | #define __JackMachThread__ | ||||
| #include "JackPosixThread.h" | #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_policy.h> | ||||
| #include <mach/thread_act.h> | #include <mach/thread_act.h> | ||||
| @@ -50,6 +50,7 @@ | |||||
| 4B66550E127C356E00753A79 /* PBXTargetDependency */, | 4B66550E127C356E00753A79 /* PBXTargetDependency */, | ||||
| 4B38120313269CCB00C61B14 /* PBXTargetDependency */, | 4B38120313269CCB00C61B14 /* PBXTargetDependency */, | ||||
| 4B8F16FC1329169F0002AD73 /* PBXTargetDependency */, | 4B8F16FC1329169F0002AD73 /* PBXTargetDependency */, | ||||
| 4B20220C133A9C370019E213 /* PBXTargetDependency */, | |||||
| ); | ); | ||||
| name = "All Universal 32/64 bits"; | name = "All Universal 32/64 bits"; | ||||
| productName = All; | productName = All; | ||||
| @@ -107,6 +108,12 @@ | |||||
| /* Begin PBXBuildFile section */ | /* Begin PBXBuildFile section */ | ||||
| 4B0A28ED0D520852002EFF74 /* tw.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A28EC0D520852002EFF74 /* tw.c */; }; | 4B0A28ED0D520852002EFF74 /* tw.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A28EC0D520852002EFF74 /* tw.c */; }; | ||||
| 4B0A29260D52108E002EFF74 /* 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 */; }; | 4B19B3130E2362E800DD4A82 /* JackAudioAdapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B3060E2362E700DD4A82 /* JackAudioAdapter.cpp */; }; | ||||
| 4B19B3140E2362E800DD4A82 /* JackAudioAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19B3070E2362E700DD4A82 /* JackAudioAdapter.h */; }; | 4B19B3140E2362E800DD4A82 /* JackAudioAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19B3070E2362E700DD4A82 /* JackAudioAdapter.h */; }; | ||||
| 4B19B3150E2362E800DD4A82 /* JackAudioAdapterInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B3080E2362E700DD4A82 /* JackAudioAdapterInterface.cpp */; }; | 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 */; }; | 4B19B31B0E2362E800DD4A82 /* JackLibSampleRateResampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B30E0E2362E700DD4A82 /* JackLibSampleRateResampler.cpp */; }; | ||||
| 4B19B31C0E2362E800DD4A82 /* JackLibSampleRateResampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19B30F0E2362E700DD4A82 /* JackLibSampleRateResampler.h */; }; | 4B19B31C0E2362E800DD4A82 /* JackLibSampleRateResampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19B30F0E2362E700DD4A82 /* JackLibSampleRateResampler.h */; }; | ||||
| 4B19B31F0E2362E800DD4A82 /* JackResampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B19B3120E2362E700DD4A82 /* JackResampler.cpp */; }; | 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 */; }; | 4B2209E112F6BBF300E5DC26 /* JackSocketServerChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B6B30E703B8D0066E42F /* JackSocketServerChannel.cpp */; }; | ||||
| 4B2209E212F6BBF400E5DC26 /* JackSocketServerChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC3B6B40E703B8D0066E42F /* JackSocketServerChannel.h */; }; | 4B2209E212F6BBF400E5DC26 /* JackSocketServerChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC3B6B40E703B8D0066E42F /* JackSocketServerChannel.h */; }; | ||||
| 4B2209E312F6BBF500E5DC26 /* JackSocketServerNotifyChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B6B50E703B8D0066E42F /* JackSocketServerNotifyChannel.cpp */; }; | 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 */; }; | 4B363F230DEB0AB0001F72D9 /* monitor_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B363F220DEB0AB0001F72D9 /* monitor_client.c */; }; | ||||
| 4B363F3E0DEB0C31001F72D9 /* showtime.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B363F3D0DEB0C31001F72D9 /* showtime.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 */; }; | 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 */; }; | 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 */; }; | 4B3811FC13269C8300C61B14 /* latent_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B3811FA13269C8300C61B14 /* latent_client.c */; }; | ||||
| 4B3814201327AA6800C61B14 /* iodelay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B38141F1327AA6800C61B14 /* iodelay.cpp */; }; | 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, ); }; }; | 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, ); }; }; | 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, ); }; }; | 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 */; }; | 4B9A25B50DBF8330006E9FBC /* JackError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9A25B30DBF8330006E9FBC /* JackError.cpp */; }; | ||||
| 4B9A25B60DBF8330006E9FBC /* 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, ); }; }; | 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 */; }; | 4BC3B6A50E703B2E0066E42F /* JackPosixThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC3B6A30E703B2E0066E42F /* JackPosixThread.h */; }; | ||||
| 4BC3B6A60E703B2E0066E42F /* JackPosixThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B6A20E703B2E0066E42F /* JackPosixThread.cpp */; }; | 4BC3B6A60E703B2E0066E42F /* JackPosixThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B6A20E703B2E0066E42F /* JackPosixThread.cpp */; }; | ||||
| 4BC3B6A70E703B2E0066E42F /* JackPosixThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC3B6A30E703B2E0066E42F /* JackPosixThread.h */; }; | 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 */; }; | 4BCC87960D57168300A7FEB1 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BCC87950D57168300A7FEB1 /* Accelerate.framework */; }; | ||||
| 4BCC87970D57168300A7FEB1 /* 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 */; }; | 4BCC87980D57168300A7FEB1 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BCC87950D57168300A7FEB1 /* Accelerate.framework */; }; | ||||
| @@ -877,6 +941,13 @@ | |||||
| remoteGlobalIDString = 4B19B2F60E23620F00DD4A82; | remoteGlobalIDString = 4B19B2F60E23620F00DD4A82; | ||||
| remoteInfo = audioadapter; | remoteInfo = audioadapter; | ||||
| }; | }; | ||||
| 4B20220B133A9C370019E213 /* PBXContainerItemProxy */ = { | |||||
| isa = PBXContainerItemProxy; | |||||
| containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; | |||||
| proxyType = 1; | |||||
| remoteGlobalIDString = 4B2021DC133A9BA40019E213; | |||||
| remoteInfo = "jack_midi_latency 64 bits"; | |||||
| }; | |||||
| 4B224B330E65BA330066BE5B /* PBXContainerItemProxy */ = { | 4B224B330E65BA330066BE5B /* PBXContainerItemProxy */ = { | ||||
| isa = PBXContainerItemProxy; | isa = PBXContainerItemProxy; | ||||
| containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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>"; }; | 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>"; }; | 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>"; }; | 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; }; | 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; }; | 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; }; | 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>"; }; | 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; }; | 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; }; | 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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| 4B2021E0133A9BA40019E213 /* Frameworks */ = { | |||||
| isa = PBXFrameworksBuildPhase; | |||||
| buildActionMask = 2147483647; | |||||
| files = ( | |||||
| ); | |||||
| runOnlyForDeploymentPostprocessing = 0; | |||||
| }; | |||||
| 4B3224E010A3156800838A8E /* Frameworks */ = { | 4B3224E010A3156800838A8E /* Frameworks */ = { | ||||
| isa = PBXFrameworksBuildPhase; | isa = PBXFrameworksBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| @@ -2559,6 +2690,7 @@ | |||||
| 4B3811971326884E00C61B14 /* jack_latent_client */, | 4B3811971326884E00C61B14 /* jack_latent_client */, | ||||
| 4B8F16E513290DC80002AD73 /* jack_midi_dump */, | 4B8F16E513290DC80002AD73 /* jack_midi_dump */, | ||||
| 4B8F16F213290E0E0002AD73 /* jack_midi_dump */, | 4B8F16F213290E0E0002AD73 /* jack_midi_dump */, | ||||
| 4B2021E6133A9BA40019E213 /* jack_midi_latency_test */, | |||||
| ); | ); | ||||
| name = Products; | name = Products; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| @@ -2566,6 +2698,7 @@ | |||||
| 4B03383E0797E19900686131 /* Simple clients */ = { | 4B03383E0797E19900686131 /* Simple clients */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 4B202209133A9C1C0019E213 /* midi_latency_test.c */, | |||||
| 4B8F16F41329161E0002AD73 /* midi_dump.c */, | 4B8F16F41329161E0002AD73 /* midi_dump.c */, | ||||
| 4B3811FA13269C8300C61B14 /* latent_client.c */, | 4B3811FA13269C8300C61B14 /* latent_client.c */, | ||||
| 4B6654FB127C350100753A79 /* server_control.cpp */, | 4B6654FB127C350100753A79 /* server_control.cpp */, | ||||
| @@ -2595,9 +2728,10 @@ | |||||
| 4B05A0420DF72B8500840F4C /* Linux */ = { | 4B05A0420DF72B8500840F4C /* Linux */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 4B349837133A6B6F00D130AB /* firewire */, | |||||
| 4B349825133A6AF500D130AB /* alsarawmidi */, | |||||
| 4B05A04C0DF72BC000840F4C /* alsa */, | 4B05A04C0DF72BC000840F4C /* alsa */, | ||||
| 4B05A07D0DF72BC000840F4C /* driver.h */, | 4B05A07D0DF72BC000840F4C /* driver.h */, | ||||
| 4B05A07E0DF72BC000840F4C /* firewire */, | |||||
| 4B05A0820DF72BC000840F4C /* freebob */, | 4B05A0820DF72BC000840F4C /* freebob */, | ||||
| ); | ); | ||||
| name = Linux; | name = Linux; | ||||
| @@ -2634,17 +2768,6 @@ | |||||
| path = ../linux/alsa; | path = ../linux/alsa; | ||||
| sourceTree = SOURCE_ROOT; | 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 */ = { | 4B05A0820DF72BC000840F4C /* freebob */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| @@ -2709,6 +2832,45 @@ | |||||
| name = Adapter; | name = Adapter; | ||||
| sourceTree = "<group>"; | 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 */ = { | 4B37C20006DF1F900016E567 /* Latency */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| @@ -2889,6 +3051,7 @@ | |||||
| 4BA550FB05E2420000569492 /* Engine */ = { | 4BA550FB05E2420000569492 /* Engine */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 4B193990133F321500547810 /* JackFilters.h */, | |||||
| 4B5F253D0DEE9B8F0041E486 /* JackLockedEngine.h */, | 4B5F253D0DEE9B8F0041E486 /* JackLockedEngine.h */, | ||||
| 4BF8D2130834F02800C94B91 /* JackEngine.h */, | 4BF8D2130834F02800C94B91 /* JackEngine.h */, | ||||
| 4BF8D2140834F02800C94B91 /* JackEngine.cpp */, | 4BF8D2140834F02800C94B91 /* JackEngine.cpp */, | ||||
| @@ -2994,10 +3157,40 @@ | |||||
| 4BF3390D0F8B86AF0080FB5B /* MIDI */ = { | 4BF3390D0F8B86AF0080FB5B /* MIDI */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | 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 */, | 4BF3391F0F8B873E0080FB5B /* JackMidiDriver.cpp */, | ||||
| 4BF339200F8B873E0080FB5B /* JackMidiDriver.h */, | 4BF339200F8B873E0080FB5B /* JackMidiDriver.h */, | ||||
| 4BF339140F8B86DC0080FB5B /* JackCoreMidiDriver.h */, | 4BF339140F8B86DC0080FB5B /* JackCoreMidiDriver.h */, | ||||
| @@ -3086,6 +3279,13 @@ | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| 4B2021DD133A9BA40019E213 /* Headers */ = { | |||||
| isa = PBXHeadersBuildPhase; | |||||
| buildActionMask = 2147483647; | |||||
| files = ( | |||||
| ); | |||||
| runOnlyForDeploymentPostprocessing = 0; | |||||
| }; | |||||
| 4B3224D810A3156800838A8E /* Headers */ = { | 4B3224D810A3156800838A8E /* Headers */ = { | ||||
| isa = PBXHeadersBuildPhase; | isa = PBXHeadersBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| @@ -3181,6 +3381,7 @@ | |||||
| 4B8A38F0117B827900664E07 /* JackSocket.h in Headers */, | 4B8A38F0117B827900664E07 /* JackSocket.h in Headers */, | ||||
| 4B8A38F7117B82B200664E07 /* JackSocketClientChannel.h in Headers */, | 4B8A38F7117B82B200664E07 /* JackSocketClientChannel.h in Headers */, | ||||
| 4B5160A813215E8B00BB7DCB /* systemdeps.h in Headers */, | 4B5160A813215E8B00BB7DCB /* systemdeps.h in Headers */, | ||||
| 4B193993133F321500547810 /* JackFilters.h in Headers */, | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -3243,8 +3444,6 @@ | |||||
| 4BF339220F8B873E0080FB5B /* JackMidiDriver.h in Headers */, | 4BF339220F8B873E0080FB5B /* JackMidiDriver.h in Headers */, | ||||
| 4BDCDBD21001FD0200B15929 /* JackWaitThreadedDriver.h in Headers */, | 4BDCDBD21001FD0200B15929 /* JackWaitThreadedDriver.h in Headers */, | ||||
| 4BDCDC0A1001FDA800B15929 /* JackArgParser.h in Headers */, | 4BDCDC0A1001FDA800B15929 /* JackArgParser.h in Headers */, | ||||
| 4BCBCE6210C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */, | |||||
| 4BCBCE6410C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */, | |||||
| 4B88D04311298BEE007A87C1 /* weakjack.h in Headers */, | 4B88D04311298BEE007A87C1 /* weakjack.h in Headers */, | ||||
| 4B88D04411298BEE007A87C1 /* weakmacros.h in Headers */, | 4B88D04411298BEE007A87C1 /* weakmacros.h in Headers */, | ||||
| 4BC2CA5A113C6CB80076717C /* JackNetInterface.h in Headers */, | 4BC2CA5A113C6CB80076717C /* JackNetInterface.h in Headers */, | ||||
| @@ -3254,6 +3453,16 @@ | |||||
| 4B8A38B0117B812500664E07 /* JackSocketServerChannel.h in Headers */, | 4B8A38B0117B812500664E07 /* JackSocketServerChannel.h in Headers */, | ||||
| 4B8A38C4117B814000664E07 /* JackSocketServerNotifyChannel.h in Headers */, | 4B8A38C4117B814000664E07 /* JackSocketServerNotifyChannel.h in Headers */, | ||||
| 4B5160AA13215ED900BB7DCB /* systemdeps.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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -3549,6 +3758,7 @@ | |||||
| 4B88D04111298BEE007A87C1 /* weakjack.h in Headers */, | 4B88D04111298BEE007A87C1 /* weakjack.h in Headers */, | ||||
| 4B88D04211298BEE007A87C1 /* weakmacros.h in Headers */, | 4B88D04211298BEE007A87C1 /* weakmacros.h in Headers */, | ||||
| 4B5160A913215EBF00BB7DCB /* systemdeps.h in Headers */, | 4B5160A913215EBF00BB7DCB /* systemdeps.h in Headers */, | ||||
| 4B193994133F321500547810 /* JackFilters.h in Headers */, | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -3640,6 +3850,7 @@ | |||||
| 4B88D03C11298BEE007A87C1 /* weakmacros.h in Headers */, | 4B88D03C11298BEE007A87C1 /* weakmacros.h in Headers */, | ||||
| 4B2209ED12F6BC2200E5DC26 /* JackSocket.h in Headers */, | 4B2209ED12F6BC2200E5DC26 /* JackSocket.h in Headers */, | ||||
| 4B2209EF12F6BC2500E5DC26 /* JackSocketClientChannel.h in Headers */, | 4B2209EF12F6BC2500E5DC26 /* JackSocketClientChannel.h in Headers */, | ||||
| 4B193991133F321500547810 /* JackFilters.h in Headers */, | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -3706,8 +3917,6 @@ | |||||
| 4BECB2F60F4451C10091B70A /* JackProcessSync.h in Headers */, | 4BECB2F60F4451C10091B70A /* JackProcessSync.h in Headers */, | ||||
| 4BF339240F8B873E0080FB5B /* JackMidiDriver.h in Headers */, | 4BF339240F8B873E0080FB5B /* JackMidiDriver.h in Headers */, | ||||
| 4B94334B10A5E666002A187F /* systemdeps.h in Headers */, | 4B94334B10A5E666002A187F /* systemdeps.h in Headers */, | ||||
| 4BCBCE5E10C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */, | |||||
| 4BCBCE6010C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */, | |||||
| 4B88D03D11298BEE007A87C1 /* weakjack.h in Headers */, | 4B88D03D11298BEE007A87C1 /* weakjack.h in Headers */, | ||||
| 4B88D03E11298BEE007A87C1 /* weakmacros.h in Headers */, | 4B88D03E11298BEE007A87C1 /* weakmacros.h in Headers */, | ||||
| 4BC2CA56113C6C940076717C /* JackNetInterface.h in Headers */, | 4BC2CA56113C6C940076717C /* JackNetInterface.h in Headers */, | ||||
| @@ -3716,6 +3925,16 @@ | |||||
| 4B2209E412F6BBF600E5DC26 /* JackSocketServerNotifyChannel.h in Headers */, | 4B2209E412F6BBF600E5DC26 /* JackSocketServerNotifyChannel.h in Headers */, | ||||
| 4B2209E712F6BC0300E5DC26 /* JackSocket.h in Headers */, | 4B2209E712F6BC0300E5DC26 /* JackSocket.h in Headers */, | ||||
| 4B2209EA12F6BC1600E5DC26 /* JackSocketNotifyChannel.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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -3901,13 +4120,12 @@ | |||||
| 4BA3396D10B2E36800190E3B /* JackMidiDriver.h in Headers */, | 4BA3396D10B2E36800190E3B /* JackMidiDriver.h in Headers */, | ||||
| 4BA3396E10B2E36800190E3B /* JackWaitThreadedDriver.h in Headers */, | 4BA3396E10B2E36800190E3B /* JackWaitThreadedDriver.h in Headers */, | ||||
| 4BA3396F10B2E36800190E3B /* JackArgParser.h in Headers */, | 4BA3396F10B2E36800190E3B /* JackArgParser.h in Headers */, | ||||
| 4BCBCE6610C4FE3F00450FFE /* JackPhysicalMidiInput.h in Headers */, | |||||
| 4BCBCE6810C4FE3F00450FFE /* JackPhysicalMidiOutput.h in Headers */, | |||||
| 4B88D04511298BEE007A87C1 /* weakjack.h in Headers */, | 4B88D04511298BEE007A87C1 /* weakjack.h in Headers */, | ||||
| 4B88D04611298BEE007A87C1 /* weakmacros.h in Headers */, | 4B88D04611298BEE007A87C1 /* weakmacros.h in Headers */, | ||||
| 4BC2CA5E113C6CCA0076717C /* JackNetInterface.h in Headers */, | 4BC2CA5E113C6CCA0076717C /* JackNetInterface.h in Headers */, | ||||
| 4BC2CA60113C6CD20076717C /* JackNetUnixSocket.h in Headers */, | 4BC2CA60113C6CD20076717C /* JackNetUnixSocket.h in Headers */, | ||||
| 4B5160AE13215EF900BB7DCB /* systemdeps.h in Headers */, | 4B5160AE13215EF900BB7DCB /* systemdeps.h in Headers */, | ||||
| 4B193996133F321500547810 /* JackFilters.h in Headers */, | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -3944,6 +4162,14 @@ | |||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| 4BDCDB951001FB9C00B15929 /* JackCoreMidiDriver.h in Headers */, | 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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -4004,6 +4230,14 @@ | |||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| 4BF3391A0F8B86DC0080FB5B /* JackCoreMidiDriver.h in Headers */, | 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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -4137,6 +4371,25 @@ | |||||
| productReference = 4B19B3000E23620F00DD4A82 /* audioadapter.so */; | productReference = 4B19B3000E23620F00DD4A82 /* audioadapter.so */; | ||||
| productType = "com.apple.product-type.library.dynamic"; | 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 */ = { | 4B3224D710A3156800838A8E /* jack_netone Universal */ = { | ||||
| isa = PBXNativeTarget; | isa = PBXNativeTarget; | ||||
| buildConfigurationList = 4B3224E110A3156800838A8E /* Build configuration list for PBXNativeTarget "jack_netone Universal" */; | buildConfigurationList = 4B3224E110A3156800838A8E /* Build configuration list for PBXNativeTarget "jack_netone Universal" */; | ||||
| @@ -5864,6 +6117,7 @@ | |||||
| 4B35C50A0D4731D1000DE7AE /* jack_midiseq 64 bits */, | 4B35C50A0D4731D1000DE7AE /* jack_midiseq 64 bits */, | ||||
| 4B35C5160D4731D1000DE7AE /* jack_midisine 64 bits */, | 4B35C5160D4731D1000DE7AE /* jack_midisine 64 bits */, | ||||
| 4B8F16E813290E0E0002AD73 /* jack_midi_dump 64 bits */, | 4B8F16E813290E0E0002AD73 /* jack_midi_dump 64 bits */, | ||||
| 4B2021DC133A9BA40019E213 /* jack_midi_latency 64 bits */, | |||||
| 4B35C5220D4731D1000DE7AE /* jack_metro 64 bits */, | 4B35C5220D4731D1000DE7AE /* jack_metro 64 bits */, | ||||
| 4B35C52E0D4731D1000DE7AE /* jack_lsp 64 bits */, | 4B35C52E0D4731D1000DE7AE /* jack_lsp 64 bits */, | ||||
| 4B35C53A0D4731D1000DE7AE /* jack_connect 64 bits */, | 4B35C53A0D4731D1000DE7AE /* jack_connect 64 bits */, | ||||
| @@ -5966,6 +6220,13 @@ | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| 4B2021E1133A9BA40019E213 /* Rez */ = { | |||||
| isa = PBXRezBuildPhase; | |||||
| buildActionMask = 2147483647; | |||||
| files = ( | |||||
| ); | |||||
| runOnlyForDeploymentPostprocessing = 0; | |||||
| }; | |||||
| 4B32255C10A3187800838A8E /* Rez */ = { | 4B32255C10A3187800838A8E /* Rez */ = { | ||||
| isa = PBXRezBuildPhase; | isa = PBXRezBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| @@ -6474,6 +6735,14 @@ | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| 4B2021DE133A9BA40019E213 /* Sources */ = { | |||||
| isa = PBXSourcesBuildPhase; | |||||
| buildActionMask = 2147483647; | |||||
| files = ( | |||||
| 4B20220A133A9C1C0019E213 /* midi_latency_test.c in Sources */, | |||||
| ); | |||||
| runOnlyForDeploymentPostprocessing = 0; | |||||
| }; | |||||
| 4B3224DC10A3156800838A8E /* Sources */ = { | 4B3224DC10A3156800838A8E /* Sources */ = { | ||||
| isa = PBXSourcesBuildPhase; | isa = PBXSourcesBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| @@ -6610,14 +6879,21 @@ | |||||
| 4BF339210F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, | 4BF339210F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, | ||||
| 4BDCDBD11001FD0100B15929 /* JackWaitThreadedDriver.cpp in Sources */, | 4BDCDBD11001FD0100B15929 /* JackWaitThreadedDriver.cpp in Sources */, | ||||
| 4BDCDC091001FDA800B15929 /* JackArgParser.cpp in Sources */, | 4BDCDC091001FDA800B15929 /* JackArgParser.cpp in Sources */, | ||||
| 4BCBCE6110C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */, | |||||
| 4BCBCE6310C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */, | |||||
| 4BC2CA59113C6CB60076717C /* JackNetInterface.cpp in Sources */, | 4BC2CA59113C6CB60076717C /* JackNetInterface.cpp in Sources */, | ||||
| 4BC2CA5B113C6CBE0076717C /* JackNetUnixSocket.cpp in Sources */, | 4BC2CA5B113C6CBE0076717C /* JackNetUnixSocket.cpp in Sources */, | ||||
| 4B8A38A7117B80D300664E07 /* JackSocket.cpp in Sources */, | 4B8A38A7117B80D300664E07 /* JackSocket.cpp in Sources */, | ||||
| 4B8A38AE117B811100664E07 /* JackSocketNotifyChannel.cpp in Sources */, | 4B8A38AE117B811100664E07 /* JackSocketNotifyChannel.cpp in Sources */, | ||||
| 4B8A38B1117B812D00664E07 /* JackSocketServerChannel.cpp in Sources */, | 4B8A38B1117B812D00664E07 /* JackSocketServerChannel.cpp in Sources */, | ||||
| 4B8A38B2117B813400664E07 /* JackSocketServerNotifyChannel.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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -7064,14 +7340,21 @@ | |||||
| 4BBAE4110F42FA6100B8BD3F /* JackEngineProfiling.cpp in Sources */, | 4BBAE4110F42FA6100B8BD3F /* JackEngineProfiling.cpp in Sources */, | ||||
| 4BECB2F50F4451C10091B70A /* JackProcessSync.cpp in Sources */, | 4BECB2F50F4451C10091B70A /* JackProcessSync.cpp in Sources */, | ||||
| 4BF339230F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, | 4BF339230F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, | ||||
| 4BCBCE5D10C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */, | |||||
| 4BCBCE5F10C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */, | |||||
| 4BC2CA55113C6C930076717C /* JackNetInterface.cpp in Sources */, | 4BC2CA55113C6C930076717C /* JackNetInterface.cpp in Sources */, | ||||
| 4BC2CA57113C6C9B0076717C /* JackNetUnixSocket.cpp in Sources */, | 4BC2CA57113C6C9B0076717C /* JackNetUnixSocket.cpp in Sources */, | ||||
| 4B2209E112F6BBF300E5DC26 /* JackSocketServerChannel.cpp in Sources */, | 4B2209E112F6BBF300E5DC26 /* JackSocketServerChannel.cpp in Sources */, | ||||
| 4B2209E312F6BBF500E5DC26 /* JackSocketServerNotifyChannel.cpp in Sources */, | 4B2209E312F6BBF500E5DC26 /* JackSocketServerNotifyChannel.cpp in Sources */, | ||||
| 4B2209E612F6BC0200E5DC26 /* JackSocket.cpp in Sources */, | 4B2209E612F6BC0200E5DC26 /* JackSocket.cpp in Sources */, | ||||
| 4B2209E912F6BC1500E5DC26 /* JackSocketNotifyChannel.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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -7260,8 +7543,6 @@ | |||||
| 4BA339A210B2E36800190E3B /* JackMidiDriver.cpp in Sources */, | 4BA339A210B2E36800190E3B /* JackMidiDriver.cpp in Sources */, | ||||
| 4BA339A310B2E36800190E3B /* JackWaitThreadedDriver.cpp in Sources */, | 4BA339A310B2E36800190E3B /* JackWaitThreadedDriver.cpp in Sources */, | ||||
| 4BA339A410B2E36800190E3B /* JackArgParser.cpp in Sources */, | 4BA339A410B2E36800190E3B /* JackArgParser.cpp in Sources */, | ||||
| 4BCBCE6510C4FE3F00450FFE /* JackPhysicalMidiInput.cpp in Sources */, | |||||
| 4BCBCE6710C4FE3F00450FFE /* JackPhysicalMidiOutput.cpp in Sources */, | |||||
| 4BC2CA5D113C6CC90076717C /* JackNetInterface.cpp in Sources */, | 4BC2CA5D113C6CC90076717C /* JackNetInterface.cpp in Sources */, | ||||
| 4BC2CA5F113C6CD10076717C /* JackNetUnixSocket.cpp in Sources */, | 4BC2CA5F113C6CD10076717C /* JackNetUnixSocket.cpp in Sources */, | ||||
| ); | ); | ||||
| @@ -7304,6 +7585,14 @@ | |||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| 4BDCDB971001FB9C00B15929 /* JackCoreMidiDriver.cpp in Sources */, | 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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -7369,6 +7658,14 @@ | |||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| 4BF3391B0F8B86DC0080FB5B /* JackCoreMidiDriver.cpp in Sources */, | 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; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| @@ -7470,6 +7767,11 @@ | |||||
| target = 4B19B2F60E23620F00DD4A82 /* audioadapter Universal */; | target = 4B19B2F60E23620F00DD4A82 /* audioadapter Universal */; | ||||
| targetProxy = 4B19B32B0E23636E00DD4A82 /* PBXContainerItemProxy */; | targetProxy = 4B19B32B0E23636E00DD4A82 /* PBXContainerItemProxy */; | ||||
| }; | }; | ||||
| 4B20220C133A9C370019E213 /* PBXTargetDependency */ = { | |||||
| isa = PBXTargetDependency; | |||||
| target = 4B2021DC133A9BA40019E213 /* jack_midi_latency 64 bits */; | |||||
| targetProxy = 4B20220B133A9C370019E213 /* PBXContainerItemProxy */; | |||||
| }; | |||||
| 4B224B340E65BA330066BE5B /* PBXTargetDependency */ = { | 4B224B340E65BA330066BE5B /* PBXTargetDependency */ = { | ||||
| isa = PBXTargetDependency; | isa = PBXTargetDependency; | ||||
| target = 4B5E08BF0E5B66EE00BEE4E0 /* netadapter Universal */; | target = 4B5E08BF0E5B66EE00BEE4E0 /* netadapter Universal */; | ||||
| @@ -8054,7 +8356,7 @@ | |||||
| OTHER_CFLAGS = ""; | OTHER_CFLAGS = ""; | ||||
| OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| /usr/local/lib/libsamplerate.a, | |||||
| /opt/local/lib/libsamplerate.a, | |||||
| "-framework", | "-framework", | ||||
| Jackservermp, | Jackservermp, | ||||
| "-framework", | "-framework", | ||||
| @@ -8173,6 +8475,101 @@ | |||||
| }; | }; | ||||
| name = Default; | 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 */ = { | 4B3224E210A3156800838A8E /* Development */ = { | ||||
| isa = XCBuildConfiguration; | isa = XCBuildConfiguration; | ||||
| buildSettings = { | buildSettings = { | ||||
| @@ -8734,7 +9131,7 @@ | |||||
| ); | ); | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| "-framework", | "-framework", | ||||
| Jackdmp, | |||||
| Jackservermp, | |||||
| "-framework", | "-framework", | ||||
| CoreAudio, | CoreAudio, | ||||
| "-framework", | "-framework", | ||||
| @@ -9105,6 +9502,7 @@ | |||||
| "-D__SMP__", | "-D__SMP__", | ||||
| "-DMACH_RPC_MACH_SEMA", | "-DMACH_RPC_MACH_SEMA", | ||||
| "$(OTHER_CPLUSPLUSFLAGS_QUOTED_FOR_TARGET_1)", | "$(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_CPLUSPLUSFLAGS_QUOTED_FOR_TARGET_1 = "-DADDON_DIR=\\\"/usr/local/lib/jackmp\\\""; | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| @@ -9114,7 +9512,7 @@ | |||||
| CoreAudio, | CoreAudio, | ||||
| ); | ); | ||||
| OTHER_REZFLAGS = ""; | OTHER_REZFLAGS = ""; | ||||
| PRODUCT_NAME = Jackdmp; | |||||
| PRODUCT_NAME = Jackservermp; | |||||
| REZ_EXECUTABLE = NO; | REZ_EXECUTABLE = NO; | ||||
| SDKROOT = ""; | SDKROOT = ""; | ||||
| SECTORDER_FLAGS = ""; | SECTORDER_FLAGS = ""; | ||||
| @@ -11111,7 +11509,7 @@ | |||||
| OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| "-framework", | "-framework", | ||||
| Jackdmp, | |||||
| Jackservermp, | |||||
| "-framework", | "-framework", | ||||
| CoreAudio, | CoreAudio, | ||||
| "-framework", | "-framework", | ||||
| @@ -11261,7 +11659,7 @@ | |||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| libportaudio.a, | libportaudio.a, | ||||
| "-framework", | "-framework", | ||||
| Jackdmp, | |||||
| Jackservermp, | |||||
| "-framework", | "-framework", | ||||
| AudioToolbox, | AudioToolbox, | ||||
| "-framework", | "-framework", | ||||
| @@ -11410,7 +11808,7 @@ | |||||
| OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| "-framework", | "-framework", | ||||
| Jackdmp, | |||||
| Jackservermp, | |||||
| "-framework", | "-framework", | ||||
| CoreAudio, | CoreAudio, | ||||
| "-framework", | "-framework", | ||||
| @@ -11548,7 +11946,7 @@ | |||||
| OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| "-framework", | "-framework", | ||||
| Jackdmp, | |||||
| Jackservermp, | |||||
| "-framework", | "-framework", | ||||
| CoreAudio, | CoreAudio, | ||||
| "-framework", | "-framework", | ||||
| @@ -16060,7 +16458,7 @@ | |||||
| CoreAudio, | CoreAudio, | ||||
| ); | ); | ||||
| OTHER_REZFLAGS = ""; | OTHER_REZFLAGS = ""; | ||||
| PRODUCT_NAME = Jackdmp; | |||||
| PRODUCT_NAME = Jackservermp; | |||||
| REZ_EXECUTABLE = NO; | REZ_EXECUTABLE = NO; | ||||
| SDKROOT = ""; | SDKROOT = ""; | ||||
| SECTORDER_FLAGS = ""; | SECTORDER_FLAGS = ""; | ||||
| @@ -17071,7 +17469,7 @@ | |||||
| OTHER_CFLAGS = ""; | OTHER_CFLAGS = ""; | ||||
| OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| /usr/local/lib/libsamplerate.a, | |||||
| /opt/local/lib/libsamplerate.a, | |||||
| "-framework", | "-framework", | ||||
| Jackservermp, | Jackservermp, | ||||
| "-framework", | "-framework", | ||||
| @@ -17220,7 +17618,7 @@ | |||||
| OTHER_CFLAGS = ""; | OTHER_CFLAGS = ""; | ||||
| OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | ||||
| OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
| /usr/local/lib/libsamplerate.a, | |||||
| /opt/local/lib/libsamplerate.a, | |||||
| "-framework", | "-framework", | ||||
| Jackservermp, | Jackservermp, | ||||
| "-framework", | "-framework", | ||||
| @@ -18767,6 +19165,16 @@ | |||||
| defaultConfigurationIsVisible = 0; | defaultConfigurationIsVisible = 0; | ||||
| defaultConfigurationName = Default; | 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" */ = { | 4B3224E110A3156800838A8E /* Build configuration list for PBXNativeTarget "jack_netone Universal" */ = { | ||||
| isa = XCConfigurationList; | isa = XCConfigurationList; | ||||
| buildConfigurations = ( | buildConfigurations = ( | ||||
| @@ -168,7 +168,7 @@ OSStatus JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice, | |||||
| switch (inPropertyID) { | switch (inPropertyID) { | ||||
| case kAudioDevicePropertyNominalSampleRate: { | case kAudioDevicePropertyNominalSampleRate: { | ||||
| jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); | |||||
| jack_log("JackCoreAudioAdapter::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); | |||||
| driver->fState = true; | driver->fState = true; | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -430,12 +430,15 @@ OSStatus JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID* id) | |||||
| jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); | jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); | ||||
| // Get the device only if default input and output are the same | // 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 !!"); | jack_error("Default input and output devices are not the same !!"); | ||||
| return kAudioHardwareBadDeviceError; | 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; | OSStatus err = noErr; | ||||
| UInt32 outSize; | UInt32 outSize; | ||||
| Boolean outWritable; | Boolean outWritable; | ||||
| AudioBufferList* bufferList = 0; | |||||
| channelCount = 0; | channelCount = 0; | ||||
| err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); | err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); | ||||
| if (err == noErr) { | if (err == noErr) { | ||||
| bufferList = (AudioBufferList*)malloc(outSize); | |||||
| AudioBufferList bufferList[outSize]; | |||||
| err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); | err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); | ||||
| if (err == noErr) { | if (err == noErr) { | ||||
| for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) | for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) | ||||
| channelCount += bufferList->mBuffers[i].mNumberChannels; | channelCount += bufferList->mBuffers[i].mNumberChannels; | ||||
| } | } | ||||
| if (bufferList) | |||||
| free(bufferList); | |||||
| } | } | ||||
| return err; | return err; | ||||
| @@ -604,7 +603,7 @@ int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, | |||||
| // Use default driver in duplex mode | // Use default driver in duplex mode | ||||
| } else { | } else { | ||||
| jack_log("JackCoreAudioDriver::Open default driver"); | |||||
| jack_log("JackCoreAudioAdapter::Open default driver"); | |||||
| if (GetDefaultDevice(&fDeviceID) != noErr) { | if (GetDefaultDevice(&fDeviceID) != noErr) { | ||||
| jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); | 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); | osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||||
| jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| return osErr; | return osErr; | ||||
| } | } | ||||
| osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); | osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error"); | |||||
| jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyData error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| return osErr; | return osErr; | ||||
| } | } | ||||
| @@ -1115,18 +1114,18 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| for (UInt32 i = 0; i < captureDeviceID.size(); i++) { | for (UInt32 i = 0; i < captureDeviceID.size(); i++) { | ||||
| if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { | 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 { | } else { | ||||
| // Check clock domain | // Check clock domain | ||||
| osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); | osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); | ||||
| if (osErr != 0) { | if (osErr != 0) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| } else { | } else { | ||||
| keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; | 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) { | 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; | need_clock_drift_compensation = true; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1135,18 +1134,18 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { | for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { | ||||
| if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { | 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 { | } else { | ||||
| // Check clock domain | // Check clock domain | ||||
| osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); | osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); | ||||
| if (osErr != 0) { | if (osErr != 0) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| } else { | } else { | ||||
| keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; | 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) { | 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; | need_clock_drift_compensation = true; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1175,7 +1174,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); | osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| return osErr; | return osErr; | ||||
| } | } | ||||
| @@ -1191,7 +1190,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); | osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| return osErr; | return osErr; | ||||
| } | } | ||||
| @@ -1218,13 +1217,13 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| SInt32 system; | SInt32 system; | ||||
| Gestalt(gestaltSystemVersion, &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) | // Starting with 10.5.4 systems, the AD can be internal... (better) | ||||
| if (system < 0x00001054) { | if (system < 0x00001054) { | ||||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device...."); | |||||
| jack_log("JackCoreAudioAdapter::CreateAggregateDevice : public aggregate device...."); | |||||
| } else { | } else { | ||||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device...."); | |||||
| jack_log("JackCoreAudioAdapter::CreateAggregateDevice : private aggregate device...."); | |||||
| CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); | CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); | ||||
| } | } | ||||
| @@ -1306,14 +1305,14 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| goto error; | goto error; | ||||
| } | } | ||||
| osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); | osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyData error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| goto error; | goto error; | ||||
| } | } | ||||
| @@ -1332,7 +1331,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| outDataSize = sizeof(CFMutableArrayRef); | outDataSize = sizeof(CFMutableArrayRef); | ||||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); | osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); | ||||
| if (osErr != noErr) { | 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); | printError(osErr); | ||||
| goto error; | goto error; | ||||
| } | } | ||||
| @@ -1352,7 +1351,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| outDataSize = sizeof(CFStringRef); | outDataSize = sizeof(CFStringRef); | ||||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First apture is master... | osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First apture is master... | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| goto error; | goto error; | ||||
| } | } | ||||
| @@ -1370,19 +1369,19 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| // Get the property data size | // Get the property data size | ||||
| osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); | osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| } | } | ||||
| // Calculate the number of object IDs | // Calculate the number of object IDs | ||||
| subDevicesNum = outSize / sizeof(AudioObjectID); | 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]; | AudioObjectID subDevices[subDevicesNum]; | ||||
| outSize = sizeof(subDevices); | outSize = sizeof(subDevices); | ||||
| osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); | osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| } | } | ||||
| @@ -1391,7 +1390,7 @@ OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> ca | |||||
| UInt32 theDriftCompensationValue = 1; | UInt32 theDriftCompensationValue = 1; | ||||
| osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); | osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); | ||||
| if (osErr != noErr) { | if (osErr != noErr) { | ||||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); | |||||
| jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); | |||||
| printError(osErr); | printError(osErr); | ||||
| } | } | ||||
| } | } | ||||
| @@ -195,7 +195,7 @@ OSStatus JackCoreAudioDriver::Render(void *inRefCon, | |||||
| driver->fCurrentTime = (AudioTimeStamp *)inTimeStamp; | driver->fCurrentTime = (AudioTimeStamp *)inTimeStamp; | ||||
| driver->fDriverOutputData = ioData; | 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()) { | if (set_threaded_log_function()) { | ||||
| jack_log("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); | jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); | ||||
| // Get the device only if default input and output are the same | // 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 !!"); | jack_error("Default input and output devices are not the same !!"); | ||||
| return kAudioHardwareBadDeviceError; | 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() | int JackCoreAudioDriver::Close() | ||||
| { | { | ||||
| jack_log("JackCoreAudioDriver::Close"); | jack_log("JackCoreAudioDriver::Close"); | ||||
| Stop(); | |||||
| // Generic audio driver close | // Generic audio driver close | ||||
| int res = JackAudioDriver::Close(); | int res = JackAudioDriver::Close(); | ||||
| @@ -1561,6 +1563,53 @@ int JackCoreAudioDriver::Close() | |||||
| return res; | 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() | int JackCoreAudioDriver::Attach() | ||||
| { | { | ||||
| OSStatus err; | OSStatus err; | ||||
| @@ -1571,7 +1620,6 @@ int JackCoreAudioDriver::Attach() | |||||
| char channel_name[64]; | char channel_name[64]; | ||||
| char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | ||||
| char alias[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); | jack_log("JackCoreAudioDriver::Attach fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); | ||||
| @@ -1596,20 +1644,8 @@ int JackCoreAudioDriver::Attach() | |||||
| return -1; | 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 = fGraphManager->GetPort(port_index); | ||||
| port->SetAlias(alias); | port->SetAlias(alias); | ||||
| range.min = range.max = fEngineControl->fBufferSize + value1 + value2 + fCaptureLatency; | |||||
| port->SetLatencyRange(JackCaptureLatency, &range); | |||||
| fCapturePortList[i] = port_index; | fCapturePortList[i] = port_index; | ||||
| } | } | ||||
| @@ -1634,21 +1670,8 @@ int JackCoreAudioDriver::Attach() | |||||
| return -1; | 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 = fGraphManager->GetPort(port_index); | ||||
| port->SetAlias(alias); | 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; | fPlaybackPortList[i] = port_index; | ||||
| // Monitor ports | // Monitor ports | ||||
| @@ -1659,14 +1682,13 @@ int JackCoreAudioDriver::Attach() | |||||
| jack_error("Cannot register monitor port for %s", name); | jack_error("Cannot register monitor port for %s", name); | ||||
| return -1; | return -1; | ||||
| } else { | } 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(); | |||||
| // Input buffers do no change : prepare them only once | // Input buffers do no change : prepare them only once | ||||
| for (int i = 0; i < fCaptureChannels; i++) { | for (int i = 0; i < fCaptureChannels; i++) { | ||||
| fJackInputData->mBuffers[i].mData = GetInputBuffer(i); | fJackInputData->mBuffers[i].mData = GetInputBuffer(i); | ||||
| @@ -1714,17 +1736,19 @@ int JackCoreAudioDriver::Stop() | |||||
| int JackCoreAudioDriver::SetBufferSize(jack_nframes_t buffer_size) | int JackCoreAudioDriver::SetBufferSize(jack_nframes_t buffer_size) | ||||
| { | { | ||||
| OSStatus err; | |||||
| UInt32 outSize = sizeof(UInt32); | 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) { | if (err != noErr) { | ||||
| jack_error("Cannot set buffer size %ld", buffer_size); | jack_error("Cannot set buffer size %ld", buffer_size); | ||||
| printError(err); | printError(err); | ||||
| return -1; | 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 | // Input buffers do no change : prepare them only once | ||||
| for (int i = 0; i < fCaptureChannels; i++) { | for (int i = 0; i < fCaptureChannels; i++) { | ||||
| @@ -40,7 +40,7 @@ typedef UInt8 CAAudioHardwareDeviceSectionID; | |||||
| #define kAudioDeviceSectionOutput ((CAAudioHardwareDeviceSectionID)0x00) | #define kAudioDeviceSectionOutput ((CAAudioHardwareDeviceSectionID)0x00) | ||||
| #define kAudioDeviceSectionGlobal ((CAAudioHardwareDeviceSectionID)0x00) | #define kAudioDeviceSectionGlobal ((CAAudioHardwareDeviceSectionID)0x00) | ||||
| #define kAudioDeviceSectionWildcard ((CAAudioHardwareDeviceSectionID)0xFF) | #define kAudioDeviceSectionWildcard ((CAAudioHardwareDeviceSectionID)0xFF) | ||||
| #define WAIT_COUNTER 60 | #define WAIT_COUNTER 60 | ||||
| /*! | /*! | ||||
| @@ -74,13 +74,13 @@ class JackCoreAudioDriver : public JackAudioDriver | |||||
| float fIOUsage; | float fIOUsage; | ||||
| float fComputationGrain; | float fComputationGrain; | ||||
| bool fClockDriftCompensate; | bool fClockDriftCompensate; | ||||
| /* | |||||
| /* | |||||
| #ifdef MAC_OS_X_VERSION_10_5 | #ifdef MAC_OS_X_VERSION_10_5 | ||||
| AudioDeviceIOProcID fMesureCallbackID; | AudioDeviceIOProcID fMesureCallbackID; | ||||
| #endif | #endif | ||||
| */ | */ | ||||
| static OSStatus Render(void *inRefCon, | static OSStatus Render(void *inRefCon, | ||||
| AudioUnitRenderActionFlags *ioActionFlags, | AudioUnitRenderActionFlags *ioActionFlags, | ||||
| const AudioTimeStamp *inTimeStamp, | const AudioTimeStamp *inTimeStamp, | ||||
| @@ -106,13 +106,13 @@ class JackCoreAudioDriver : public JackAudioDriver | |||||
| OSStatus GetDefaultOutputDevice(AudioDeviceID* id); | OSStatus GetDefaultOutputDevice(AudioDeviceID* id); | ||||
| OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name); | OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name); | ||||
| OSStatus GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput); | OSStatus GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput); | ||||
| // Setup | // Setup | ||||
| OSStatus CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice); | 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 CreateAggregateDeviceAux(vector<AudioDeviceID> captureDeviceID, vector<AudioDeviceID> playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice); | ||||
| OSStatus DestroyAggregateDevice(); | OSStatus DestroyAggregateDevice(); | ||||
| bool IsAggregateDevice(AudioDeviceID device); | bool IsAggregateDevice(AudioDeviceID device); | ||||
| int SetupDevices(const char* capture_driver_uid, | int SetupDevices(const char* capture_driver_uid, | ||||
| const char* playback_driver_uid, | const char* playback_driver_uid, | ||||
| char* capture_driver_name, | char* capture_driver_name, | ||||
| @@ -146,10 +146,12 @@ class JackCoreAudioDriver : public JackAudioDriver | |||||
| int AddListeners(); | int AddListeners(); | ||||
| void RemoveListeners(); | void RemoveListeners(); | ||||
| bool TakeHogAux(AudioDeviceID deviceID, bool isInput); | bool TakeHogAux(AudioDeviceID deviceID, bool isInput); | ||||
| bool TakeHog(); | bool TakeHog(); | ||||
| void UpdateLatencies(); | |||||
| public: | public: | ||||
| JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); | JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); | ||||
| @@ -1,5 +1,6 @@ | |||||
| /* | /* | ||||
| Copyright (C) 2009 Grame | Copyright (C) 2009 Grame | ||||
| Copyright (C) 2011 Devin Anderson | |||||
| This program is free software; you can redistribute it and/or modify | 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 | 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 "JackCoreMidiDriver.h" | ||||
| #include "JackGraphManager.h" | |||||
| #include "JackServer.h" | |||||
| #include "JackCoreMidiUtil.h" | |||||
| #include "JackEngineControl.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; | 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; | 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 | #ifdef __cplusplus | ||||
| extern "C" | |||||
| { | |||||
| extern "C" { | |||||
| #endif | #endif | ||||
| SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() | SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() | ||||
| @@ -423,4 +700,3 @@ extern "C" | |||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -1,5 +1,6 @@ | |||||
| /* | /* | ||||
| Copyright (C) 2009 Grame | Copyright (C) 2009 Grame | ||||
| Copyright (C) 2011 Devin Anderson | |||||
| This program is free software; you can redistribute it and/or modify | 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 | 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__ | #ifndef __JackCoreMidiDriver__ | ||||
| #define __JackCoreMidiDriver__ | #define __JackCoreMidiDriver__ | ||||
| #include <CoreMIDI/CoreMIDI.h> | |||||
| #include "JackCoreMidiPhysicalInputPort.h" | |||||
| #include "JackCoreMidiPhysicalOutputPort.h" | |||||
| #include "JackCoreMidiVirtualInputPort.h" | |||||
| #include "JackCoreMidiVirtualOutputPort.h" | |||||
| #include "JackMidiDriver.h" | #include "JackMidiDriver.h" | ||||
| #include "JackTime.h" | |||||
| namespace Jack | |||||
| { | |||||
| namespace Jack { | |||||
| /*! | |||||
| \brief The CoreMidi driver. | |||||
| */ | |||||
| class JackCoreMidiDriver : public JackMidiDriver | |||||
| { | |||||
| class JackCoreMidiDriver: public JackMidiDriver { | |||||
| private: | 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 | #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; | float factor = 0.5f; | ||||
| old_buffer_size = jack_get_buffer_size(client1); | 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; | linebuf = linecount; | ||||
| if (jack_set_buffer_size(client1, (jack_nframes_t)(old_buffer_size * factor)) < 0) { | if (jack_set_buffer_size(client1, (jack_nframes_t)(old_buffer_size * factor)) < 0) { | ||||
| printf("!!! ERROR !!! jack_set_buffer_size fails !\n"); | printf("!!! ERROR !!! jack_set_buffer_size fails !\n"); | ||||
| } | } | ||||
| jack_sleep(1 * 1000); | jack_sleep(1 * 1000); | ||||
| cur_buffer_size = jack_get_buffer_size(client1); | 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("!!! ERROR !!! Buffer size has not been changed !\n"); | ||||
| printf("!!! Maybe jack was compiled without the '--enable-resize' flag...\n"); | printf("!!! Maybe jack was compiled without the '--enable-resize' flag...\n"); | ||||
| } else { | } else { | ||||
| @@ -106,6 +106,12 @@ | |||||
| <Option compilerVar="WINDRES" /> | <Option compilerVar="WINDRES" /> | ||||
| </Unit> | </Unit> | ||||
| <Unit filename="winmme\JackWinMMEDriver.cpp" /> | <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> | <Extensions> | ||||
| <code_completion /> | <code_completion /> | ||||
| <envvars /> | <envvars /> | ||||
| @@ -56,7 +56,7 @@ | |||||
| <Project filename="multiple_metro.cbp"> | <Project filename="multiple_metro.cbp"> | ||||
| <Depends filename="libjack.cbp" /> | <Depends filename="libjack.cbp" /> | ||||
| </Project> | </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> | </Workspace> | ||||
| </CodeBlocks_workspace_file> | </CodeBlocks_workspace_file> | ||||