git-svn-id: http://subversion.jackaudio.org/jack/jack2/branches/libjacknet@4307 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.9.8
@@ -34,6 +34,31 @@ Valerio Pilo | |||
Jackdmp changes log | |||
--------------------------- | |||
2011-04-04 Stephane Letz <letz@grame.fr> | |||
* Correct driver lifetime management. | |||
2011-04-03 Stephane Letz <letz@grame.fr> | |||
* Fix in JackCoreAudioDriver::Read when there is no inputs. | |||
2011-04-02 Stephane Letz <letz@grame.fr> | |||
* Netdriver can now ask for in/out values from the master (in progress). | |||
* Correct drivers parameter settings. | |||
2011-04-01 Stephane Letz <letz@grame.fr> | |||
* Merge newer-midi branch (Devin Anderson redesign of the MIDI drivers: alsarawmidi, ffado, coremidi and winmme). | |||
* Cleanup JackThreadedDriver::Stop. | |||
* Correct JackNetOneDriver::Close. | |||
* Correction in jackdmp.cpp: notify_server_stop should be done after server destruction. | |||
* Improve error management in JackNetDriver. | |||
2011-03-30 Stephane Letz <letz@grame.fr> | |||
* Version 1.9.8 started. | |||
2011-03-29 Stephane Letz <letz@grame.fr> | |||
* Synchronize JackWeakAPI.cpp with new APIs. | |||
@@ -20,7 +20,7 @@ The audible result of this mode is that if a client is not activated during one | |||
Linux version | |||
-------------- | |||
The published version still uses fifos for server/client synchronization. The use of POSIX named semaphore is implemented but still a bit unstable. Sockets are used for server/client communications. The ALSA backend derived from jackd implementation is used. To build jackdmp, a "waf" (http://code.google.com/p/waf/) based compilation system is available. The code has to be compiled on a machine where ALSA and possibly freeboot (FFADO) headers and libraries are corrected installed. | |||
The published version still uses fifos for server/client synchronization. The use of POSIX named semaphore is implemented but still a bit unstable. Sockets are used for server/client communications. The ALSA backend derived from jackd implementation is used. To build jackdmp, a "waf" (http://code.google.com/p/waf/) based compilation system is available. The code has to be compiled on a machine where ALSA and possibly freebob (FFADO) headers and libraries are corrected installed. | |||
In the top folder do : | |||
@@ -212,7 +212,7 @@ Note : To experiment with the -S option, jackdmp must be launched in a console. | |||
1.9.4 : Solaris boomer backend now working in capture or playback only mode. Add a -G parameter in CoreAudio backend (the computation value in RT thread expressed as percent of period). Use SNDCTL_DSP_SYNCGROUP/SNDCTL_DSP_SYNCSTART API to synchronize input and output in Solaris boomer backend. Big endian bug fix in memops.c. Fix issues in JackNetDriver::DecodeTransportData and JackNetDriver::Initialize. Correct CPU timing in JackNetDriver, now take cycle begin time after Read. Simplify transport in NetJack2: master only can control transport. Change CoreAudio notification thread setup for OSX Snow Leopard. Correct server temporary mode : now set a global and quit after server/client message handling is finished. Add a string parameter to server ==> client notification, add a new JackInfoShutdownCallback type. CoreAudio backend now issue a JackInfoShutdownCallback when an unrecoverable error is detected (sampling rate change, stream configuration changeÉ). Correct jackdmp.cpp (failures case were not correct..). Improve JackCoreAudioDriver code. Raise default port number to 2048. Correct JackProcessSync::LockedTimedWait. Correct JACK_MESSAGE_SIZE value, particularly in OSX RPC code. Now start server channel thread only when backend has been started (so in JackServer::Start). Should solve race conditions at start time. jack_verbose moved to JackGlobals class. Improve aggregate device management in JackCoreAudioDriver : now a "private" device only and cleanup properly. Aggregate device code added to JackCoreAudioAdapter. Implement "hog mode" (exclusive access of the audio device) in JackCoreAudioDriver. Fix jack_set_sample_rate_callback to have he same behavior as in JACK1. Dynamic system version detection in JackCoreAudioDriver to either create public or private aggregate device. In JackCoreAudioDriver, force the SR value to the wanted one *before* creating aggregate device (otherwise creation will fail). In JackCoreAudioDriver, better cleanup of AD when intermediate open failure. In JackCoreAudioDriver::Start, wait for the audio driver to effectively start (use the MeasureCallback). In JackCoreAudioDriver, improve management of input/output channels: -1 is now used internally to indicate a wanted max value. In JackCoreAudioDriver::OpenAUHAL, correct stream format setup and cleanup. Correct crash bug in JackAudioAdapterInterface when not input is used in adapter (temporary fixÉ). Sync JackCoreAudioAdapter code on JackCoreAudioDriver one. JACK_SCHED_POLICY switched to SCHED_FIFO. Now can aggregate device that are themselves AD. No reason to make jack_on_shutdown deprecated, so revert the incorrect change. Thread AcquireRealTime and DropRealTime were (incorrectly) using fThread field. Use pthread_self()) (or GetCurrentThread() on Windows) to get the calling thread. Correctly save and restore RT mode state in freewheel mode. Correct freewheel code on client side. Fix AcquireRealTime and DropRealTime: now distinguish when called from another thread (AcquireRealTime/DropRealTime) and from the thread itself (AcquireSelfRealTime/DropSelfRealTime). Correct JackPosixThread::StartImp : thread priority setting now done in the RT case only. Correct JackGraphManager::GetBuffer for the "client loop with one connection" case : buffer must be copied. Correct JackInfoShutdownCallback prototype, two new JackClientProcessFailure and JackClientZombie JackStatus code. Correct JackCoreAudio driver when empty strings are given as -C, -P or -d parameter. Better memory allocation error checking on client (library) side. Better memory allocation error checking in ringbuffer.c, weak import improvements. Memory allocation error checking for jack_client_new and jack_client_open (server and client side). Memory allocation error checking in server for RPC. Simplify server temporary mode : now use a JackTemporaryException. Lock/Unlock shared memory segments (to test...). Sync with JACK1 : -r parameter now used for no-realtime, realtime (-R) is now default, usable backend given vie platform. In JackCoreAudio driver, (possibly) clock drift compensation when needed in aggregated devices. In JackCoreAudio driver, clock drift compensation in aggregated devices working. In JackCoreAudio driver, clock drift compensation semantic changed a bit : when on, does not activate if not needed (same clock domain). Sync JackCoreAudioAdapter code with JackCoreAudioDriver. | |||
1.9.5 : Dynamic choice of maximum port number. More robust sample rate change handling code in JackCoreAudioDriver. Devin Anderson patch for Jack FFADO driver issues with lost MIDI bytes between periods (and more). Fix port_rename callback : now both old name and new name are given as parameters. Special code in JackCoreAudio driver to handle completely buggy Digidesign CoreAudio user-land driver. Ensure that client-side message buffer thread calls thread_init callback if/when it is set by the client (backport of JACK1 rev 3838). Check dynamic port-max value. Fix JackCoreMidiDriver::ReadProcAux when ring buffer is full (thanks Devin Anderson). Josh Green ALSA driver capture only patch. When threads are cancelled, the exception has to be rethrown. Use a QUIT notification to properly quit the server channel, the server channel thread can then be 'stopped' instead of 'canceled'. Mario Lang alsa_io time calculation overflow patch. Shared memory manager was calling abort in case of fatal error, now return an error in caller. Change JackEngineProfiling and JackAudioAdapterInterface gnuplot scripts to output SVG instead of PDF. | |||
1.9.6 : Improve JackCoreAudioDriver and JackCoreAudioAdapter : when no devices are described, takes default input and output and aggregate them.Correct JackGraphManager::DeactivatePort. Correct JackMachServerChannel::Execute : keep running even in error cases. Raise JACK_PROTOCOL_VERSION number. Arnold Krille firewire patch. Raise JACK_DRIVER_PARAM_STRING_MAX and JACK_PARAM_STRING_MAX to 127 otherwise some audio drivers cannot be loaded on OSX. Fix some file header to have library side code use LGPL. On Windows, now use TRE library for regexp (BSD license instead of GPL license). ffado-portname-sync.patch from ticket #163 applied. Remove call to exit in library code. Make jack_connect/jack_disconnect wait for effective port connection/disconnection. Add tests to validate intclient.h API. On Linux, inter-process synchronization primitive switched to POSIX semaphore. In JackCoreAudioDriver, move code called in MeasureCallback to be called once in IO thread. David Garcia Garzon netone patch. Fix from Fernando Lopez-Lezcano for compilation on fc13. Fix JackPosixSemaphore::TimedWait : same behavior as JackPosixSemaphore::Wait regarding EINTR. David Garcia Garzon unused_pkt_buf_field_jack2 netone patch. Arnold Krille firewire snooping patch. Jan Engelhardt patch for get_cycles on SPARC. Adrian Knoth hurd.patch, kfreebsd-fix.patch and alpha_ia64-sigsegv.patch from ticket 177. Adrian Knoth fix for linux cycle.h (ticket 188). In JackCoreAudioDriver, fix an issue when no value is given for input. | |||
1.9.7 : Sync JackAlsaDriver::alsa_driver_check_card_type with JACK1 backend. Correct JackServer::Open to avoid a race when control API is used on OSX. Improve backend error handling: fatal error returned by Read/Write now cause a Process failure (so a thread exit for blocking backends). Recoverable ones (XRuns..) are now treated internally in ALSA, FreeBob and FFADO backends. In jackdmp.cpp, jackctl_setup_signals moved before jackctl_server_start. Correct symbols export in backends on OSX. ALSA backend : suspend/resume handling. Correct dummy driver. Adrian Knoth jack_lsp patch. Remove JackPortIsActive flag. New latency API implementation. ComputeTotalLatencies now a client/server call. Add latent test client for latency API. Also print playback and capture latency in jack_lsp. jack_client_has_session_callback implementation. Check requested buffer size and limit to 1..8192 - avoids weird behaviour caused by jack_bufsize foobar. jack_port_type_get_buffer_size implementation. Stop using alloca and allocate buffer on the heap for alsa_io. Rename jdelay to jack_iodelay as per Fons' request. Call buffer size callback in activate (actually this is done on client side in the RT thread Init method). Add jack_midi_dump client. Synchronize net JACK1 with JACK1 version. Synchronize jack_connect/jack_disconnect with JACK1 version. Correct JackNetMaster::SetBufferSize. Use jack_default_audio_sample_t instead of float consistently, fix ticket #201. -X now allows to add several slave backends, add -I to load several internal clients. Rework internal slave driver management, JackServerGlobals now handle same parameters as jackdmp. Correct JackEngine::NotifyGraphReorder, update JackDebugClient with latest API. Devin Anderson server-ctl-proposal branch merged on trunk: improved control API, slave backend reworked. Implement renaming in JackDriver::Open to avoid name collision (thanks Devin Anderson). Correct alsa_driver_restart (thanks Devin Anderson). Correction of jack_connect/jack_disconnect: use of jack_activate and volatile keyword for thread shared variable. Correction of JackNetOneDriver for latest CELT API. Synchronize JackWeakAPI.cpp with new APIs. | |||
1.9.7 : Sync JackAlsaDriver::alsa_driver_check_card_type with JACK1 backend. Correct JackServer::Open to avoid a race when control API is used on OSX. Improve backend error handling: fatal error returned by Read/Write now cause a Process failure (so a thread exit for blocking backends). Recoverable ones (XRuns..) are now treated internally in ALSA, FreeBob and FFADO backends. In jackdmp.cpp, jackctl_setup_signals moved before jackctl_server_start. Correct symbols export in backends on OSX. ALSA backend : suspend/resume handling. Correct dummy driver. Adrian Knoth jack_lsp patch. Remove JackPortIsActive flag. New latency API implementation. ComputeTotalLatencies now a client/server call. Add latent test client for latency API. Also print playback and capture latency in jack_lsp. jack_client_has_session_callback implementation. Check requested buffer size and limit to 1..8192 - avoids weird behaviour caused by jack_bufsize foobar. jack_port_type_get_buffer_size implementation. Stop using alloca and allocate buffer on the heap for alsa_io. Rename jdelay to jack_iodelay as per Fons' request. Call buffer size callback in activate (actually this is done on client side in the RT thread Init method). Add jack_midi_dump client. Synchronize net JACK1 with JACK1 version. Synchronize jack_connect/jack_disconnect with JACK1 version. Correct JackNetMaster::SetBufferSize. Use jack_default_audio_sample_t instead of float consistently, fix ticket #201. -X now allows to add several slave backends, add -I to load several internal clients. Rework internal slave driver management, JackServerGlobals now handle same parameters as jackdmp. Correct JackEngine::NotifyGraphReorder, update JackDebugClient with latest API. Devin Anderson server-ctl-proposal branch merged on trunk: improved control API, slave backend reworked. Implement renaming in JackDriver::Open to avoid name collision (thanks Devin Anderson). Correct alsa_driver_restart (thanks Devin Anderson). Correction of jack_connect/jack_disconnect: use of jack_activate and volatile keyword for thread shared variable. Correction of JackNetOneDriver for latest CELT API. Synchronize JackWeakAPI.cpp with new APIs. | |||
This is a work in progress but the implementation is now stable enough to be tested. jackdmp has been used successfully with the following applications : Ardour, Hydrogen, Jamin, QjackCtl, Jack-Rack, SooperLooper, AlsaPlayer... | |||
@@ -44,12 +44,17 @@ JackAudioDriver::~JackAudioDriver() | |||
int JackAudioDriver::SetBufferSize(jack_nframes_t buffer_size) | |||
{ | |||
// Update engine and graph manager state | |||
fEngineControl->fBufferSize = buffer_size; | |||
fGraphManager->SetBufferSize(buffer_size); | |||
fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec | |||
if (!fEngineControl->fTimeOut) | |||
fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); | |||
return 0; | |||
UpdateLatencies(); | |||
// Redirect on slaves drivers... | |||
return JackDriver::SetBufferSize(buffer_size); | |||
} | |||
int JackAudioDriver::SetSampleRate(jack_nframes_t sample_rate) | |||
@@ -58,7 +63,8 @@ int JackAudioDriver::SetSampleRate(jack_nframes_t sample_rate) | |||
fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec | |||
if (!fEngineControl->fTimeOut) | |||
fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); | |||
return 0; | |||
return JackDriver::SetSampleRate(sample_rate); | |||
} | |||
int JackAudioDriver::Open(jack_nframes_t buffer_size, | |||
@@ -76,6 +82,9 @@ int JackAudioDriver::Open(jack_nframes_t buffer_size, | |||
fCaptureChannels = inchannels; | |||
fPlaybackChannels = outchannels; | |||
fWithMonitorPorts = monitor; | |||
memset(fCapturePortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); | |||
memset(fPlaybackPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); | |||
memset(fMonitorPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); | |||
return JackDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); | |||
} | |||
@@ -92,16 +101,39 @@ int JackAudioDriver::Open(bool capturing, | |||
fCaptureChannels = inchannels; | |||
fPlaybackChannels = outchannels; | |||
fWithMonitorPorts = monitor; | |||
memset(fCapturePortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); | |||
memset(fPlaybackPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); | |||
memset(fMonitorPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); | |||
return JackDriver::Open(capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); | |||
} | |||
void JackAudioDriver::UpdateLatencies() | |||
{ | |||
jack_latency_range_t range; | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
range.max = range.min = fEngineControl->fBufferSize; | |||
fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
if (! fEngineControl->fSyncMode) { | |||
range.max = range.min = fEngineControl->fBufferSize * 2; | |||
} | |||
fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); | |||
if (fWithMonitorPorts) { | |||
range.min = range.max = fEngineControl->fBufferSize; | |||
fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &range); | |||
} | |||
} | |||
} | |||
int JackAudioDriver::Attach() | |||
{ | |||
JackPort* port; | |||
jack_port_id_t port_index; | |||
char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | |||
char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | |||
jack_latency_range_t range; | |||
int i; | |||
jack_log("JackAudioDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); | |||
@@ -115,8 +147,6 @@ int JackAudioDriver::Attach() | |||
} | |||
port = fGraphManager->GetPort(port_index); | |||
port->SetAlias(alias); | |||
range.min = range.max = fEngineControl->fBufferSize + fCaptureLatency; | |||
port->SetLatencyRange(JackCaptureLatency, &range); | |||
fCapturePortList[i] = port_index; | |||
jack_log("JackAudioDriver::Attach fCapturePortList[i] port_index = %ld", port_index); | |||
} | |||
@@ -130,9 +160,6 @@ int JackAudioDriver::Attach() | |||
} | |||
port = fGraphManager->GetPort(port_index); | |||
port->SetAlias(alias); | |||
// Add more latency if "async" mode is used... | |||
range.min = range.max = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + fPlaybackLatency; | |||
port->SetLatencyRange(JackPlaybackLatency, &range); | |||
fPlaybackPortList[i] = port_index; | |||
jack_log("JackAudioDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); | |||
@@ -144,14 +171,12 @@ int JackAudioDriver::Attach() | |||
jack_error("Cannot register monitor port for %s", name); | |||
return -1; | |||
} else { | |||
port = fGraphManager->GetPort(port_index); | |||
range.min = range.max = fEngineControl->fBufferSize; | |||
port->SetLatencyRange(JackCaptureLatency, &range); | |||
fMonitorPortList[i] = port_index; | |||
fMonitorPortList[i] = port_index; | |||
} | |||
} | |||
} | |||
UpdateLatencies(); | |||
return 0; | |||
} | |||
@@ -193,9 +218,9 @@ int JackAudioDriver::ProcessNull() | |||
JackDriver::CycleTakeBeginTime(); | |||
if (fEngineControl->fSyncMode) { | |||
ProcessGraphSync(); | |||
ProcessGraphSyncMaster(); | |||
} else { | |||
ProcessGraphAsync(); | |||
ProcessGraphAsyncMaster(); | |||
} | |||
// Keep end cycle time | |||
@@ -230,9 +255,9 @@ int JackAudioDriver::ProcessAsync() | |||
// Process graph | |||
if (fIsMaster) { | |||
ProcessGraphAsync(); | |||
ProcessGraphAsyncMaster(); | |||
} else { | |||
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); | |||
ProcessGraphAsyncSlave(); | |||
} | |||
// Keep end cycle time | |||
@@ -255,12 +280,12 @@ int JackAudioDriver::ProcessSync() | |||
// Process graph | |||
if (fIsMaster) { | |||
if (ProcessGraphSync() < 0) { | |||
if (ProcessGraphSyncMaster() < 0) { | |||
jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); | |||
goto end; | |||
} | |||
} else { | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { | |||
if (ProcessGraphSyncSlave() < 0) { | |||
jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); | |||
goto end; | |||
} | |||
@@ -279,27 +304,50 @@ end: | |||
return 0; | |||
} | |||
void JackAudioDriver::ProcessGraphAsync() | |||
void JackAudioDriver::ProcessGraphAsyncMaster() | |||
{ | |||
// fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle | |||
if (!fEngine->Process(fBeginDateUst, fEndDateUst)) | |||
jack_error("JackAudioDriver::ProcessGraphAsync: Process error"); | |||
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); | |||
if (ProcessSlaves() < 0) | |||
jack_error("JackAudioDriver::ProcessGraphAsync: ProcessSlaves error"); | |||
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: Process error"); | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) | |||
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ResumeRefNum error"); | |||
if (ProcessReadSlaves() < 0) | |||
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessReadSlaves error"); | |||
if (ProcessWriteSlaves() < 0) | |||
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessWriteSlaves error"); | |||
} | |||
void JackAudioDriver::ProcessGraphAsyncSlave() | |||
{ | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) | |||
jack_error("JackAudioDriver::ProcessGraphAsyncSlave: ResumeRefNum error"); | |||
} | |||
int JackAudioDriver::ProcessGraphSync() | |||
int JackAudioDriver::ProcessGraphSyncMaster() | |||
{ | |||
int res = 0; | |||
// fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle | |||
if (fEngine->Process(fBeginDateUst, fEndDateUst)) { | |||
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); | |||
if (ProcessSlaves() < 0) { | |||
jack_error("JackAudioDriver::ProcessGraphSync: ProcessSlaves error, engine may now behave abnormally!!"); | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { | |||
jack_error("JackAudioDriver::ProcessGraphSyncMaster: ResumeRefNum error"); | |||
res = -1; | |||
} | |||
if (ProcessReadSlaves() < 0) { | |||
jack_error("JackAudioDriver::ProcessGraphSync: ProcessReadSlaves error, engine may now behave abnormally!!"); | |||
res = -1; | |||
} | |||
if (ProcessWriteSlaves() < 0) { | |||
jack_error("JackAudioDriver::ProcessGraphSync: ProcessWriteSlaves error, engine may now behave abnormally!!"); | |||
res = -1; | |||
} | |||
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { | |||
jack_error("JackAudioDriver::ProcessGraphSync: SuspendRefNum error, engine may now behave abnormally!!"); | |||
res = -1; | |||
@@ -312,6 +360,11 @@ int JackAudioDriver::ProcessGraphSync() | |||
return res; | |||
} | |||
int JackAudioDriver::ProcessGraphSyncSlave() | |||
{ | |||
return fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); | |||
} | |||
int JackAudioDriver::Start() | |||
{ | |||
int res = JackDriver::Start(); | |||
@@ -342,20 +395,23 @@ void JackAudioDriver::WaitUntilNextCycle() | |||
jack_default_audio_sample_t* JackAudioDriver::GetInputBuffer(int port_index) | |||
{ | |||
assert(fCapturePortList[port_index]); | |||
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize); | |||
return fCapturePortList[port_index] | |||
? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize) | |||
: NULL; | |||
} | |||
jack_default_audio_sample_t* JackAudioDriver::GetOutputBuffer(int port_index) | |||
{ | |||
assert(fPlaybackPortList[port_index]); | |||
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize); | |||
return fPlaybackPortList[port_index] | |||
? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize) | |||
: NULL; | |||
} | |||
jack_default_audio_sample_t* JackAudioDriver::GetMonitorBuffer(int port_index) | |||
{ | |||
assert(fPlaybackPortList[port_index]); | |||
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize); | |||
return fPlaybackPortList[port_index] | |||
? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize) | |||
: NULL; | |||
} | |||
int JackAudioDriver::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) | |||
@@ -35,8 +35,12 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver | |||
protected: | |||
void ProcessGraphAsync(); | |||
int ProcessGraphSync(); | |||
void ProcessGraphAsyncMaster(); | |||
void ProcessGraphAsyncSlave(); | |||
int ProcessGraphSyncMaster(); | |||
int ProcessGraphSyncSlave(); | |||
void WaitUntilNextCycle(); | |||
virtual int ProcessAsync(); | |||
@@ -58,6 +62,7 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver | |||
jack_default_audio_sample_t* GetMonitorBuffer(int port_index); | |||
void HandleLatencyCallback(int status); | |||
void UpdateLatencies(); | |||
public: | |||
@@ -20,7 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#ifndef __JackChannel__ | |||
#define __JackChannel__ | |||
#include "session.h" | |||
#include "types.h" | |||
#include "JackSession.h" | |||
namespace Jack | |||
{ | |||
@@ -27,7 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#include "JackSynchro.h" | |||
#include "JackPlatformPlug.h" | |||
#include "JackChannel.h" | |||
#include "session.h" | |||
#include "varargs.h" | |||
#include <list> | |||
@@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#include "JackPort.h" | |||
#include "JackSynchro.h" | |||
#include "JackNotification.h" | |||
#include "session.h" | |||
#include "JackSession.h" | |||
namespace Jack | |||
{ | |||
@@ -24,7 +24,7 @@ | |||
#include "config.h" | |||
#endif | |||
#define VERSION "1.9.7" | |||
#define VERSION "1.9.8" | |||
#define BUFFER_SIZE_MAX 8192 | |||
@@ -71,7 +71,7 @@ | |||
#define SOCKET_TIME_OUT 5 // in sec | |||
#define DRIVER_OPEN_TIMEOUT 5 // in sec | |||
#define FREEWHEEL_DRIVER_TIMEOUT 10 // in sec | |||
#define DRIVER_TIMEOUT_FACTOR 10 | |||
#define DRIVER_TIMEOUT_FACTOR 10 | |||
#define NO_PORT 0xFFFE | |||
@@ -101,7 +101,7 @@ struct jackctl_driver | |||
jack_driver_desc_t * desc_ptr; | |||
JSList * parameters; | |||
JSList * set_parameters; | |||
JackDriverInfo* info; | |||
JSList * infos; | |||
}; | |||
struct jackctl_internal | |||
@@ -215,7 +215,8 @@ bool | |||
jackctl_add_driver_parameters( | |||
struct jackctl_driver * driver_ptr) | |||
{ | |||
uint32_t i; | |||
unsigned int i; | |||
union jackctl_parameter_value jackctl_value; | |||
jackctl_param_type_t jackctl_type; | |||
struct jackctl_parameter * parameter_ptr; | |||
@@ -308,6 +309,7 @@ jackctl_drivers_load( | |||
driver_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data; | |||
driver_ptr->parameters = NULL; | |||
driver_ptr->set_parameters = NULL; | |||
driver_ptr->infos = NULL; | |||
if (!jackctl_add_driver_parameters(driver_ptr)) | |||
{ | |||
@@ -377,6 +379,7 @@ jackctl_internals_load( | |||
internal_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data; | |||
internal_ptr->parameters = NULL; | |||
internal_ptr->set_parameters = NULL; | |||
internal_ptr->refnum = -1; | |||
if (!jackctl_add_driver_parameters((struct jackctl_driver *)internal_ptr)) | |||
{ | |||
@@ -1214,8 +1217,13 @@ EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver | |||
jack_error("cannot add a slave in a running server"); | |||
return false; | |||
} else { | |||
driver_ptr->info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, driver_ptr->set_parameters); | |||
return (driver_ptr->info != 0); | |||
JackDriverInfo* info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, driver_ptr->set_parameters); | |||
if (info) { | |||
driver_ptr->infos = jack_slist_append(driver_ptr->infos, info); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
} else { | |||
return false; | |||
@@ -1228,10 +1236,17 @@ EXPORT bool jackctl_server_remove_slave(jackctl_server * server_ptr, jackctl_dri | |||
if (server_ptr->engine->IsRunning()) { | |||
jack_error("cannot remove a slave from a running server"); | |||
return false; | |||
} else { | |||
server_ptr->engine->RemoveSlave(driver_ptr->info); | |||
delete driver_ptr->info; | |||
return true; | |||
} else { | |||
if (driver_ptr->infos) { | |||
JackDriverInfo* info = (JackDriverInfo*)driver_ptr->infos->data; | |||
assert(info); | |||
driver_ptr->infos = jack_slist_remove(driver_ptr->infos, info); | |||
server_ptr->engine->RemoveSlave(info); | |||
delete info; | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
} else { | |||
return false; | |||
@@ -27,7 +27,8 @@ | |||
#ifdef WIN32 | |||
#ifdef __MINGW32__ | |||
#include <sys/types.h> | |||
#include <sys/types.h> | |||
typedef _sigset_t sigset_t; | |||
#else | |||
typedef HANDLE sigset_t; | |||
#endif | |||
@@ -169,11 +170,11 @@ jackctl_parameter_set_value( | |||
EXPORT union jackctl_parameter_value | |||
jackctl_parameter_get_default_value( | |||
jackctl_parameter_t * parameter); | |||
EXPORT union jackctl_parameter_value | |||
EXPORT union jackctl_parameter_value | |||
jackctl_parameter_get_default_value( | |||
jackctl_parameter *parameter_ptr); | |||
EXPORT bool | |||
jackctl_parameter_has_range_constraint( | |||
jackctl_parameter_t * parameter_ptr); | |||
@@ -210,33 +211,33 @@ EXPORT bool | |||
jackctl_parameter_constraint_is_fake_value( | |||
jackctl_parameter_t * parameter_ptr); | |||
EXPORT const JSList * | |||
EXPORT const JSList * | |||
jackctl_server_get_internals_list( | |||
jackctl_server *server_ptr); | |||
EXPORT const char * | |||
EXPORT const char * | |||
jackctl_internal_get_name( | |||
jackctl_internal *internal_ptr); | |||
EXPORT const JSList * | |||
EXPORT const JSList * | |||
jackctl_internal_get_parameters( | |||
jackctl_internal *internal_ptr); | |||
EXPORT bool jackctl_server_load_internal( | |||
jackctl_server * server, | |||
jackctl_internal * internal); | |||
EXPORT bool jackctl_server_unload_internal( | |||
jackctl_server * server, | |||
jackctl_internal * internal); | |||
EXPORT bool jackctl_server_add_slave(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
EXPORT bool jackctl_server_remove_slave(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
EXPORT bool | |||
EXPORT bool | |||
jackctl_server_switch_master(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
@@ -56,6 +56,7 @@ JackDriver::JackDriver() | |||
fEngine = NULL; | |||
fGraphManager = NULL; | |||
fBeginDateUst = 0; | |||
fDelayedUsecs = 0.f; | |||
fIsMaster = true; | |||
fIsRunning = false; | |||
} | |||
@@ -300,19 +301,42 @@ void JackDriver::RemoveSlave(JackDriverInterface* slave) | |||
fSlaveList.remove(slave); | |||
} | |||
int JackDriver::ProcessSlaves() | |||
int JackDriver::ProcessReadSlaves() | |||
{ | |||
int res = 0; | |||
list<JackDriverInterface*>::const_iterator it; | |||
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { | |||
JackDriverInterface* slave = *it; | |||
if (slave->Process() < 0) | |||
if (slave->ProcessRead() < 0) | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackDriver::ProcessWriteSlaves() | |||
{ | |||
int res = 0; | |||
list<JackDriverInterface*>::const_iterator it; | |||
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { | |||
JackDriverInterface* slave = *it; | |||
if (slave->ProcessWrite() < 0) | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackDriver::ProcessRead() | |||
{ | |||
return 0; | |||
} | |||
int JackDriver::ProcessWrite() | |||
{ | |||
return 0; | |||
} | |||
int JackDriver::Process() | |||
{ | |||
return 0; | |||
@@ -352,6 +376,12 @@ int JackDriver::Start() | |||
return 0; | |||
} | |||
int JackDriver::Stop() | |||
{ | |||
fIsRunning = false; | |||
return 0; | |||
} | |||
int JackDriver::StartSlaves() | |||
{ | |||
int res = 0; | |||
@@ -370,12 +400,6 @@ int JackDriver::StartSlaves() | |||
return res; | |||
} | |||
int JackDriver::Stop() | |||
{ | |||
fIsRunning = false; | |||
return 0; | |||
} | |||
int JackDriver::StopSlaves() | |||
{ | |||
int res = 0; | |||
@@ -395,12 +419,28 @@ bool JackDriver::IsFixedBufferSize() | |||
int JackDriver::SetBufferSize(jack_nframes_t buffer_size) | |||
{ | |||
return 0; | |||
int res = 0; | |||
list<JackDriverInterface*>::const_iterator it; | |||
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { | |||
JackDriverInterface* slave = *it; | |||
if (slave->SetBufferSize(buffer_size) < 0) | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackDriver::SetSampleRate(jack_nframes_t sample_rate) | |||
{ | |||
return 0; | |||
int res = 0; | |||
list<JackDriverInterface*>::const_iterator it; | |||
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { | |||
JackDriverInterface* slave = *it; | |||
if (slave->SetSampleRate(sample_rate) < 0) | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
bool JackDriver::Initialize() | |||
@@ -34,6 +34,7 @@ namespace Jack | |||
class JackLockedEngine; | |||
class JackGraphManager; | |||
struct JackEngineControl; | |||
class JackSlaveDriverInterface; | |||
/*! | |||
\brief The base interface for drivers. | |||
@@ -91,10 +92,17 @@ class SERVER_EXPORT JackDriverInterface | |||
virtual void SetMaster(bool onoff) = 0; | |||
virtual bool GetMaster() = 0; | |||
virtual void AddSlave(JackDriverInterface* slave) = 0; | |||
virtual void RemoveSlave(JackDriverInterface* slave) = 0; | |||
virtual std::list<JackDriverInterface*> GetSlaves() = 0; | |||
virtual int ProcessSlaves() = 0; | |||
virtual int ProcessReadSlaves() = 0; | |||
virtual int ProcessWriteSlaves() = 0; | |||
virtual int ProcessRead() = 0; | |||
virtual int ProcessWrite() = 0; | |||
virtual bool IsRealTime() const = 0; | |||
virtual bool IsRunning() const = 0; | |||
@@ -159,11 +167,11 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface | |||
void AddSlave(JackDriverInterface* slave); | |||
void RemoveSlave(JackDriverInterface* slave); | |||
std::list<JackDriverInterface*> GetSlaves() | |||
{ | |||
return fSlaveList; | |||
} | |||
int ProcessSlaves(); | |||
virtual int Open(); | |||
@@ -200,10 +208,17 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface | |||
virtual int Write(); | |||
virtual int Start(); | |||
virtual int StartSlaves(); | |||
virtual int Stop(); | |||
virtual int StartSlaves(); | |||
virtual int StopSlaves(); | |||
int ProcessReadSlaves(); | |||
int ProcessWriteSlaves(); | |||
int ProcessRead(); | |||
int ProcessWrite(); | |||
virtual bool IsFixedBufferSize(); | |||
virtual int SetBufferSize(jack_nframes_t buffer_size); | |||
virtual int SetSampleRate(jack_nframes_t sample_rate); | |||
@@ -531,7 +531,7 @@ jack_drivers_load (JSList * drivers) { | |||
HANDLE file; | |||
const char * ptr = NULL; | |||
JSList * driver_list = NULL; | |||
jack_driver_desc_t * desc; | |||
jack_driver_desc_t * desc = NULL; | |||
if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { | |||
// for WIN32 ADDON_DIR is defined in JackConstants.h as relative path | |||
@@ -551,6 +551,11 @@ jack_drivers_load (JSList * drivers) { | |||
} | |||
do { | |||
/* check the filename is of the right format */ | |||
if (strncmp ("jack_", filedata.cFileName, 5) != 0) { | |||
continue; | |||
} | |||
ptr = strrchr (filedata.cFileName, '.'); | |||
if (!ptr) { | |||
continue; | |||
@@ -560,6 +565,11 @@ jack_drivers_load (JSList * drivers) { | |||
continue; | |||
} | |||
/* check if dll is an internal client */ | |||
if (check_symbol(filedata.cFileName, "jack_internal_initialize")) { | |||
continue; | |||
} | |||
desc = jack_get_descriptor (drivers, filedata.cFileName, "driver_get_descriptor"); | |||
if (desc) { | |||
driver_list = jack_slist_append (driver_list, desc); | |||
@@ -586,7 +596,7 @@ jack_drivers_load (JSList * drivers) { | |||
const char * ptr; | |||
int err; | |||
JSList * driver_list = NULL; | |||
jack_driver_desc_t * desc; | |||
jack_driver_desc_t * desc = NULL; | |||
const char* driver_dir; | |||
if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { | |||
@@ -618,8 +628,12 @@ jack_drivers_load (JSList * drivers) { | |||
continue; | |||
} | |||
desc = jack_get_descriptor (drivers, dir_entry->d_name, "driver_get_descriptor"); | |||
/* check if dll is an internal client */ | |||
if (check_symbol(dir_entry->d_name, "jack_internal_initialize")) { | |||
continue; | |||
} | |||
desc = jack_get_descriptor (drivers, dir_entry->d_name, "driver_get_descriptor"); | |||
if (desc) { | |||
driver_list = jack_slist_append (driver_list, desc); | |||
} else { | |||
@@ -669,7 +683,7 @@ jack_internals_load (JSList * internals) { | |||
file = (HANDLE )FindFirstFile(dll_filename, &filedata); | |||
if (file == INVALID_HANDLE_VALUE) { | |||
jack_error("error"); | |||
jack_error("could not open driver directory %s", driver_dir); | |||
return NULL; | |||
} | |||
@@ -30,7 +30,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
typedef jack_driver_desc_t * (*JackDriverDescFunction) (); | |||
typedef Jack::JackDriverClientInterface* (*driverInitialize) (Jack::JackLockedEngine*, Jack::JackSynchro*, const JSList*); | |||
class JackDriverInfo | |||
class SERVER_EXPORT JackDriverInfo | |||
{ | |||
private: | |||
@@ -78,6 +78,7 @@ int JackDummyDriver::Process() | |||
int JackDummyDriver::SetBufferSize(jack_nframes_t buffer_size) | |||
{ | |||
// Generic change, never fails | |||
JackAudioDriver::SetBufferSize(buffer_size); | |||
fWaitTime = (unsigned long)((((float)buffer_size) / ((float)fEngineControl->fSampleRate)) * 1000000.0f); | |||
return 0; | |||
@@ -71,6 +71,7 @@ int JackExternalClient::Open(const char* name, int pid, int refnum, int uuid, in | |||
int JackExternalClient::Close() | |||
{ | |||
jack_log("JackExternalClient::Close"); | |||
fChannel.Close(); | |||
if (fClientControl) { | |||
fClientControl->~JackClientControl(); | |||
@@ -28,26 +28,72 @@ namespace Jack | |||
int JackFreewheelDriver::Process() | |||
{ | |||
if (fIsMaster) { | |||
jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); | |||
JackDriver::CycleTakeBeginTime(); | |||
fEngine->Process(fBeginDateUst, fEndDateUst); | |||
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients | |||
int res = 0; | |||
jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); | |||
JackDriver::CycleTakeBeginTime(); | |||
if (fEngine->Process(fBeginDateUst, fEndDateUst)) { | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable)) { // Signal all clients | |||
jack_error("JackFreewheelDriver::Process: ResumeRefNum error"); | |||
res = -1; | |||
} | |||
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, FREEWHEEL_DRIVER_TIMEOUT * 1000000) < 0) { // Wait for all clients to finish for 10 sec | |||
jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); | |||
jack_error("JackFreewheelDriver::ProcessSync: SuspendRefNum error"); | |||
/* We have a client time-out error, but still continue to process, until a better recovery strategy is chosen */ | |||
return 0; | |||
} | |||
} else { | |||
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients | |||
if (fEngineControl->fSyncMode) { | |||
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { | |||
jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); | |||
return -1; | |||
} | |||
} | |||
} else { // Graph not finished: do not activate it | |||
jack_error("JackFreewheelDriver::Process: Process error"); | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackFreewheelDriver::ProcessRead() | |||
{ | |||
return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); | |||
} | |||
int JackFreewheelDriver::ProcessWrite() | |||
{ | |||
return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); | |||
} | |||
int JackFreewheelDriver::ProcessReadSync() | |||
{ | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { // Signal all clients | |||
jack_error("JackFreewheelDriver::ProcessReadSync: ResumeRefNum error"); | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
int JackFreewheelDriver::ProcessWriteSync() | |||
{ | |||
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { | |||
jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
int JackFreewheelDriver::ProcessReadAsync() | |||
{ | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { // Signal all clients | |||
jack_error("JackFreewheelDriver::ProcessReadAsync: ResumeRefNum error"); | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
int JackFreewheelDriver::ProcessWriteAsync() | |||
{ | |||
return 0; | |||
} | |||
} // end of namespace |
@@ -46,6 +46,16 @@ class JackFreewheelDriver : public JackDriver | |||
} | |||
int Process(); | |||
int ProcessRead(); | |||
int ProcessWrite(); | |||
int ProcessReadSync(); | |||
int ProcessWriteSync(); | |||
int ProcessReadAsync(); | |||
int ProcessWriteAsync(); | |||
}; | |||
} // end of namespace | |||
@@ -32,6 +32,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#include <assert.h> | |||
#include <signal.h> | |||
#ifdef WIN32 | |||
#ifdef __MINGW32__ | |||
#include <sys/types.h> | |||
typedef _sigset_t sigset_t; | |||
#else | |||
typedef HANDLE sigset_t; | |||
#endif | |||
#endif | |||
namespace Jack | |||
{ | |||
@@ -148,7 +148,7 @@ unsigned int JackLibSampleRateResampler::WriteResample(jack_default_audio_sample | |||
res = src_process(fResampler, &src_data); | |||
if (res != 0) { | |||
jack_error("JackLibSampleRateResampler::ReadResample ratio = %f err = %s", fRatio, src_strerror(res)); | |||
jack_error("JackLibSampleRateResampler::WriteResample ratio = %f err = %s", fRatio, src_strerror(res)); | |||
return 0; | |||
} | |||
@@ -167,7 +167,7 @@ unsigned int JackLibSampleRateResampler::WriteResample(jack_default_audio_sample | |||
if (read_frames < frames) { | |||
jack_error("Input available = %ld", available_frames); | |||
jack_error("JackLibSampleRateResampler::ReadResample error read_frames = %ld", read_frames); | |||
jack_error("JackLibSampleRateResampler::WriteResample error read_frames = %ld", read_frames); | |||
} | |||
return read_frames; | |||
@@ -30,20 +30,61 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
namespace Jack | |||
{ | |||
int JackLoopbackDriver::Process() | |||
int JackLoopbackDriver::ProcessRead() | |||
{ | |||
return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); | |||
} | |||
int JackLoopbackDriver::ProcessWrite() | |||
{ | |||
return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); | |||
} | |||
int JackLoopbackDriver::ProcessReadSync() | |||
{ | |||
int res = 0; | |||
// Loopback copy | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); | |||
} | |||
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients | |||
if (fEngineControl->fSyncMode) { | |||
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { | |||
jack_error("JackLoopbackDriver::ProcessSync SuspendRefNum error"); | |||
return -1; | |||
} | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { | |||
jack_error("JackLoopbackDriver::ProcessReadSync - ResumeRefNum error"); | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackLoopbackDriver::ProcessWriteSync() | |||
{ | |||
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { | |||
jack_error("JackLoopbackDriver::ProcessWriteSync SuspendRefNum error"); | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
int JackLoopbackDriver::ProcessReadAsync() | |||
{ | |||
int res = 0; | |||
// Loopback copy | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); | |||
} | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { | |||
jack_error("JackLoopbackDriver::ProcessReadAsync - ResumeRefNum error"); | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackLoopbackDriver::ProcessWriteAsync() | |||
{ | |||
return 0; | |||
} | |||
@@ -33,15 +33,24 @@ namespace Jack | |||
class JackLoopbackDriver : public JackAudioDriver | |||
{ | |||
private: | |||
virtual int ProcessReadSync(); | |||
virtual int ProcessWriteSync(); | |||
virtual int ProcessReadAsync(); | |||
virtual int ProcessWriteAsync(); | |||
public: | |||
JackLoopbackDriver(JackLockedEngine* engine, JackSynchro* table) | |||
: JackAudioDriver("loopback", "", engine, table) | |||
: JackAudioDriver("loopback", "loopback", engine, table) | |||
{} | |||
virtual ~JackLoopbackDriver() | |||
{} | |||
int Process(); | |||
virtual int ProcessRead(); | |||
virtual int ProcessWrite(); | |||
}; | |||
} // end of namespace | |||
@@ -0,0 +1,103 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include <new> | |||
#include "JackMidiAsyncQueue.h" | |||
using Jack::JackMidiAsyncQueue; | |||
JackMidiAsyncQueue::JackMidiAsyncQueue(size_t max_bytes, size_t max_messages) | |||
{ | |||
data_buffer = new jack_midi_data_t[max_bytes]; | |||
byte_ring = jack_ringbuffer_create((max_bytes * sizeof(jack_midi_data_t)) + | |||
1); | |||
if (byte_ring) { | |||
info_ring = jack_ringbuffer_create((max_messages * INFO_SIZE) + 1); | |||
if (info_ring) { | |||
jack_ringbuffer_mlock(byte_ring); | |||
jack_ringbuffer_mlock(info_ring); | |||
this->max_bytes = max_bytes; | |||
return; | |||
} | |||
jack_ringbuffer_free(byte_ring); | |||
} | |||
delete data_buffer; | |||
throw std::bad_alloc(); | |||
} | |||
JackMidiAsyncQueue::~JackMidiAsyncQueue() | |||
{ | |||
jack_ringbuffer_free(byte_ring); | |||
jack_ringbuffer_free(info_ring); | |||
delete[] data_buffer; | |||
} | |||
jack_midi_event_t * | |||
JackMidiAsyncQueue::DequeueEvent() | |||
{ | |||
jack_midi_event_t *event = 0; | |||
if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) { | |||
event = &dequeue_event; | |||
jack_ringbuffer_read(info_ring, (char *) &(event->time), | |||
sizeof(jack_nframes_t)); | |||
size_t size; | |||
jack_ringbuffer_read(info_ring, (char *) &size, sizeof(size_t)); | |||
event->buffer = data_buffer; | |||
event->size = size; | |||
jack_ringbuffer_data_t vector[2]; | |||
jack_ringbuffer_get_read_vector(byte_ring, vector); | |||
size_t size1 = vector[0].len; | |||
memcpy(data_buffer, vector[0].buf, size1 * sizeof(jack_midi_data_t)); | |||
if (size1 < size) { | |||
memcpy(data_buffer + size1, vector[1].buf, | |||
(size - size1) * sizeof(jack_midi_data_t)); | |||
} | |||
jack_ringbuffer_read_advance(byte_ring, | |||
size * sizeof(jack_midi_data_t)); | |||
} | |||
return event; | |||
} | |||
Jack::JackMidiWriteQueue::EnqueueResult | |||
JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
if (size > max_bytes) { | |||
return BUFFER_TOO_SMALL; | |||
} | |||
if (! ((jack_ringbuffer_write_space(info_ring) >= INFO_SIZE) && | |||
(jack_ringbuffer_write_space(byte_ring) >= | |||
(size * sizeof(jack_midi_data_t))))) { | |||
return BUFFER_FULL; | |||
} | |||
jack_ringbuffer_write(byte_ring, (const char *) buffer, | |||
size * sizeof(jack_midi_data_t)); | |||
jack_ringbuffer_write(info_ring, (const char *) (&time), | |||
sizeof(jack_nframes_t)); | |||
jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t)); | |||
return OK; | |||
} | |||
size_t | |||
JackMidiAsyncQueue::GetAvailableSpace() | |||
{ | |||
return jack_ringbuffer_write_space(info_ring) < INFO_SIZE ? 0 : | |||
max_bytes - jack_ringbuffer_read_space(byte_ring); | |||
} |
@@ -0,0 +1,98 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiAsyncQueue__ | |||
#define __JackMidiAsyncQueue__ | |||
#include "JackMidiPort.h" | |||
#include "JackMidiReadQueue.h" | |||
#include "JackMidiWriteQueue.h" | |||
#include "ringbuffer.h" | |||
namespace Jack { | |||
/** | |||
* This is a MIDI message queue designed to allow two threads to pass MIDI | |||
* messages between two threads (though it can also be used to buffer | |||
* events internally). This is especially useful if the MIDI API | |||
* you're attempting to interface with doesn't provide the ability to | |||
* schedule MIDI events ahead of time and/or has blocking send/receive | |||
* calls, as it allows a separate thread to handle input/output while the | |||
* JACK process thread copies events from a `JackMidiBufferReadQueue` to | |||
* this queue, or from this queue to a `JackMidiBufferWriteQueue`. | |||
*/ | |||
class SERVER_EXPORT JackMidiAsyncQueue: | |||
public JackMidiReadQueue, public JackMidiWriteQueue { | |||
private: | |||
static const size_t INFO_SIZE = | |||
sizeof(jack_nframes_t) + sizeof(size_t); | |||
jack_ringbuffer_t *byte_ring; | |||
jack_midi_data_t *data_buffer; | |||
jack_midi_event_t dequeue_event; | |||
jack_ringbuffer_t *info_ring; | |||
size_t max_bytes; | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
/** | |||
* Creates a new asynchronous MIDI message queue. The queue can store | |||
* up to `max_messages` MIDI messages and up to `max_bytes` of MIDI | |||
* data before it starts rejecting messages. | |||
*/ | |||
JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024); | |||
virtual ~JackMidiAsyncQueue(); | |||
/** | |||
* Dequeues and returns a MIDI event. Returns '0' if there are no MIDI | |||
* events available. This method may be overridden. | |||
*/ | |||
virtual jack_midi_event_t * | |||
DequeueEvent(); | |||
/** | |||
* Enqueues the MIDI event specified by the arguments. The return | |||
* value indiciates whether or not the event was successfully enqueued. | |||
* This method may be overridden. | |||
*/ | |||
virtual EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
/** | |||
* Returns the maximum size event that can be enqueued right *now*. | |||
*/ | |||
size_t | |||
GetAvailableSpace(); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,85 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include <new> | |||
#include "JackMidiAsyncWaitQueue.h" | |||
#include "JackMidiUtil.h" | |||
#include "JackTime.h" | |||
using Jack::JackMidiAsyncWaitQueue; | |||
JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes, | |||
size_t max_messages): | |||
JackMidiAsyncQueue(max_bytes, max_messages) | |||
{ | |||
if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) { | |||
throw std::bad_alloc(); | |||
} | |||
} | |||
JackMidiAsyncWaitQueue::~JackMidiAsyncWaitQueue() | |||
{ | |||
semaphore.Destroy(); | |||
} | |||
jack_midi_event_t * | |||
JackMidiAsyncWaitQueue::DequeueEvent() | |||
{ | |||
return DequeueEvent((long) 0); | |||
} | |||
jack_midi_event_t * | |||
JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame) | |||
{ | |||
// XXX: I worry about timer resolution on Solaris and Windows. When the | |||
// resolution for the `JackSynchro` object is milliseconds, the worst-case | |||
// scenario for processor objects is that the wait time becomes less than a | |||
// millisecond, and the processor object continually calls this method, | |||
// expecting to wait a certain amount of microseconds, and ends up not | |||
// waiting at all each time, essentially busy-waiting until the current | |||
// frame is reached. Perhaps there should be a #define that indicates the | |||
// wait time resolution for `JackSynchro` objects so that we can wait a | |||
// little longer if necessary. | |||
jack_time_t frame_time = GetTimeFromFrames(frame); | |||
jack_time_t current_time = GetMicroSeconds(); | |||
return DequeueEvent((frame_time < current_time) ? 0 : | |||
(long) (frame_time - current_time)); | |||
} | |||
jack_midi_event_t * | |||
JackMidiAsyncWaitQueue::DequeueEvent(long usec) | |||
{ | |||
return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ? | |||
JackMidiAsyncQueue::DequeueEvent() : 0; | |||
} | |||
Jack::JackMidiWriteQueue::EnqueueResult | |||
JackMidiAsyncWaitQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
EnqueueResult result = JackMidiAsyncQueue::EnqueueEvent(time, size, | |||
buffer); | |||
if (result == OK) { | |||
semaphore.Signal(); | |||
} | |||
return result; | |||
} |
@@ -0,0 +1,99 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiAsyncWaitQueue__ | |||
#define __JackMidiAsyncWaitQueue__ | |||
#include "JackMidiAsyncQueue.h" | |||
namespace Jack { | |||
/** | |||
* This is an asynchronous wait queue that allows a thread to wait for a | |||
* message, either indefinitely or for a specified time. This is one | |||
* example of a way that the `JackMidiAsyncQueue` class can be extended so | |||
* that process threads can interact with non-process threads to send MIDI | |||
* events. | |||
* | |||
* XXX: As of right now, this code hasn't been tested. Also, note the | |||
* warning in the JackMidiAsyncWaitQueue.cpp about semaphore wait | |||
* resolution. | |||
*/ | |||
class SERVER_EXPORT JackMidiAsyncWaitQueue: public JackMidiAsyncQueue { | |||
private: | |||
JackSynchro semaphore; | |||
public: | |||
using JackMidiAsyncQueue::EnqueueEvent; | |||
/** | |||
* Creates a new asynchronous MIDI wait message queue. The queue can | |||
* store up to `max_messages` MIDI messages and up to `max_bytes` of | |||
* MIDI data before it starts rejecting messages. | |||
*/ | |||
JackMidiAsyncWaitQueue(size_t max_bytes=4096, | |||
size_t max_messages=1024); | |||
~JackMidiAsyncWaitQueue(); | |||
/** | |||
* Dequeues and returns a MIDI event. Returns '0' if there are no MIDI | |||
* events available right now. | |||
*/ | |||
jack_midi_event_t * | |||
DequeueEvent(); | |||
/** | |||
* Waits a specified time for a MIDI event to be available, or | |||
* indefinitely if the time is negative. Returns the MIDI event, or | |||
* '0' if time runs out and no MIDI event is available. | |||
*/ | |||
jack_midi_event_t * | |||
DequeueEvent(long usecs); | |||
/** | |||
* Waits until the specified frame for a MIDI event to be available. | |||
* Returns the MIDI event, or '0' if time runs out and no MIDI event is | |||
* available. | |||
*/ | |||
jack_midi_event_t * | |||
DequeueEvent(jack_nframes_t frame); | |||
/** | |||
* Enqueues the MIDI event specified by the arguments. The return | |||
* value indiciates whether or not the event was successfully enqueued. | |||
*/ | |||
EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,67 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include "JackMidiBufferReadQueue.h" | |||
#include "JackMidiUtil.h" | |||
using Jack::JackMidiBufferReadQueue; | |||
JackMidiBufferReadQueue::JackMidiBufferReadQueue() | |||
{ | |||
event_count = 0; | |||
index = 0; | |||
} | |||
jack_midi_event_t * | |||
JackMidiBufferReadQueue::DequeueEvent() | |||
{ | |||
jack_midi_event_t *e = 0; | |||
if (index < event_count) { | |||
JackMidiEvent *event = &(buffer->events[index]); | |||
midi_event.buffer = event->GetData(buffer); | |||
midi_event.size = event->size; | |||
midi_event.time = last_frame_time + event->time; | |||
e = &midi_event; | |||
index++; | |||
} | |||
return e; | |||
} | |||
void | |||
JackMidiBufferReadQueue::ResetMidiBuffer(JackMidiBuffer *buffer) | |||
{ | |||
event_count = 0; | |||
index = 0; | |||
if (! buffer) { | |||
jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " | |||
"to NULL"); | |||
} else if (! buffer->IsValid()) { | |||
jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " | |||
"to invalid buffer"); | |||
} else { | |||
uint32_t lost_events = buffer->lost_events; | |||
if (lost_events) { | |||
jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - %d events " | |||
"lost during mixdown", lost_events); | |||
} | |||
this->buffer = buffer; | |||
event_count = buffer->event_count; | |||
last_frame_time = GetLastFrame(); | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiBufferReadQueue__ | |||
#define __JackMidiBufferReadQueue__ | |||
#include "JackMidiReadQueue.h" | |||
namespace Jack { | |||
/** | |||
* Wrapper class to present a JackMidiBuffer in a read queue interface. | |||
*/ | |||
class SERVER_EXPORT JackMidiBufferReadQueue: public JackMidiReadQueue { | |||
private: | |||
JackMidiBuffer *buffer; | |||
jack_nframes_t event_count; | |||
jack_nframes_t index; | |||
jack_nframes_t last_frame_time; | |||
jack_midi_event_t midi_event; | |||
public: | |||
JackMidiBufferReadQueue(); | |||
jack_midi_event_t * | |||
DequeueEvent(); | |||
/** | |||
* This method must be called each period to reset the MIDI buffer for | |||
* processing. | |||
*/ | |||
void | |||
ResetMidiBuffer(JackMidiBuffer *buffer); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,65 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include "JackMidiBufferWriteQueue.h" | |||
#include "JackMidiUtil.h" | |||
using Jack::JackMidiBufferWriteQueue; | |||
JackMidiBufferWriteQueue::JackMidiBufferWriteQueue() | |||
{ | |||
// Empty | |||
} | |||
Jack::JackMidiWriteQueue::EnqueueResult | |||
JackMidiBufferWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *data) | |||
{ | |||
if (time >= next_frame_time) { | |||
return EVENT_EARLY; | |||
} | |||
if (time < last_frame_time) { | |||
time = last_frame_time; | |||
} | |||
jack_midi_data_t *dst = buffer->ReserveEvent(time - last_frame_time, size); | |||
if (! dst) { | |||
return size > max_bytes ? BUFFER_TOO_SMALL : BUFFER_FULL; | |||
} | |||
memcpy(dst, data, size); | |||
return OK; | |||
} | |||
void | |||
JackMidiBufferWriteQueue::ResetMidiBuffer(JackMidiBuffer *buffer, | |||
jack_nframes_t frames) | |||
{ | |||
if (! buffer) { | |||
jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " | |||
"to NULL"); | |||
} else if (! buffer->IsValid()) { | |||
jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " | |||
"to invalid buffer"); | |||
} else { | |||
this->buffer = buffer; | |||
buffer->Reset(frames); | |||
last_frame_time = GetLastFrame(); | |||
max_bytes = buffer->MaxEventSize(); | |||
next_frame_time = last_frame_time + frames; | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiBufferWriteQueue__ | |||
#define __JackMidiBufferWriteQueue__ | |||
#include "JackMidiWriteQueue.h" | |||
namespace Jack { | |||
/** | |||
* Wrapper class to present a JackMidiBuffer in a write queue interface. | |||
*/ | |||
class SERVER_EXPORT JackMidiBufferWriteQueue: public JackMidiWriteQueue { | |||
private: | |||
JackMidiBuffer *buffer; | |||
jack_nframes_t last_frame_time; | |||
size_t max_bytes; | |||
jack_nframes_t next_frame_time; | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
JackMidiBufferWriteQueue(); | |||
EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
/** | |||
* This method must be called each period to reset the MIDI buffer for | |||
* processing. | |||
*/ | |||
void | |||
ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames); | |||
}; | |||
} | |||
#endif |
@@ -34,19 +34,10 @@ JackMidiDriver::JackMidiDriver(const char* name, const char* alias, JackLockedEn | |||
: JackDriver(name, alias, engine, table), | |||
fCaptureChannels(0), | |||
fPlaybackChannels(0) | |||
{ | |||
for (int i = 0; i < DRIVER_PORT_NUM; i++) { | |||
fRingBuffer[i] = NULL; | |||
} | |||
} | |||
{} | |||
JackMidiDriver::~JackMidiDriver() | |||
{ | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
if (fRingBuffer[i]) | |||
jack_ringbuffer_free(fRingBuffer[i]); | |||
} | |||
} | |||
{} | |||
int JackMidiDriver::Open(bool capturing, | |||
bool playing, | |||
@@ -60,11 +51,6 @@ int JackMidiDriver::Open(bool capturing, | |||
{ | |||
fCaptureChannels = inchannels; | |||
fPlaybackChannels = outchannels; | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
fRingBuffer[i] = jack_ringbuffer_create(sizeof(jack_default_audio_sample_t) * BUFFER_SIZE_MAX); | |||
} | |||
return JackDriver::Open(capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); | |||
} | |||
@@ -104,6 +90,7 @@ int JackMidiDriver::Attach() | |||
jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); | |||
} | |||
UpdateLatencies(); | |||
return 0; | |||
} | |||
@@ -133,33 +120,108 @@ int JackMidiDriver::Write() | |||
return 0; | |||
} | |||
void JackMidiDriver::UpdateLatencies() | |||
{ | |||
jack_latency_range_t range; | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
range.max = range.min = fEngineControl->fBufferSize; | |||
fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
if (! fEngineControl->fSyncMode) { | |||
range.max = range.min = fEngineControl->fBufferSize * 2; | |||
} | |||
fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); | |||
} | |||
} | |||
int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) | |||
{ | |||
UpdateLatencies(); | |||
return 0; | |||
} | |||
int JackMidiDriver::ProcessNull() | |||
{ | |||
return 0; | |||
} | |||
int JackMidiDriver::Process() | |||
int JackMidiDriver::ProcessRead() | |||
{ | |||
return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); | |||
} | |||
int JackMidiDriver::ProcessWrite() | |||
{ | |||
return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); | |||
} | |||
int JackMidiDriver::ProcessReadSync() | |||
{ | |||
int res = 0; | |||
// Read input buffers for the current cycle | |||
if (Read() < 0) { | |||
jack_error("JackMidiDriver::Process: read error, skip cycle"); | |||
return 0; // Skip cycle, but continue processing... | |||
jack_error("JackMidiDriver::ProcessReadSync: read error, skip cycle"); | |||
res = -1; | |||
} | |||
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); | |||
if (fEngineControl->fSyncMode) { | |||
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) { | |||
jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); | |||
return -1; | |||
} | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { | |||
jack_error("JackMidiDriver::ProcessReadSync - ResumeRefNum error"); | |||
res = -1; | |||
} | |||
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 for the current cycle | |||
// Write output buffers from the current cycle | |||
if (Write() < 0) { | |||
jack_error("JackMidiDriver::Process: write error, skip cycle"); | |||
return 0; // Skip cycle, but continue processing... | |||
jack_error("JackMidiDriver::ProcessWriteSync - Write error"); | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackMidiDriver::ProcessReadAsync() | |||
{ | |||
int res = 0; | |||
// Read input buffers for the current cycle | |||
if (Read() < 0) { | |||
jack_error("JackMidiDriver::ProcessReadAsync: read error, skip cycle"); | |||
res = -1; | |||
} | |||
// Write output buffers from the previous cycle | |||
if (Write() < 0) { | |||
jack_error("JackMidiDriver::ProcessReadAsync - Write error"); | |||
res = -1; | |||
} | |||
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { | |||
jack_error("JackMidiDriver::ProcessReadAsync - ResumeRefNum error"); | |||
res = -1; | |||
} | |||
return res; | |||
} | |||
int JackMidiDriver::ProcessWriteAsync() | |||
{ | |||
return 0; | |||
} | |||
@@ -39,15 +39,21 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver | |||
int fCaptureChannels; | |||
int fPlaybackChannels; | |||
jack_ringbuffer_t* fRingBuffer[DRIVER_PORT_NUM]; | |||
jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; | |||
jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; | |||
JackMidiBuffer* GetInputBuffer(int port_index); | |||
JackMidiBuffer* GetOutputBuffer(int port_index); | |||
virtual int ProcessReadSync(); | |||
virtual int ProcessWriteSync(); | |||
virtual int ProcessReadAsync(); | |||
virtual int ProcessWriteAsync(); | |||
virtual void UpdateLatencies(); | |||
public: | |||
JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); | |||
@@ -62,16 +68,20 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver | |||
const char* playback_driver_name, | |||
jack_nframes_t capture_latency, | |||
jack_nframes_t playback_latency); | |||
virtual int Process(); | |||
virtual int SetBufferSize(jack_nframes_t buffer_size); | |||
virtual int ProcessRead(); | |||
virtual int ProcessWrite(); | |||
virtual int ProcessNull(); | |||
virtual int Attach(); | |||
virtual int Detach(); | |||
virtual int Read(); | |||
virtual int Write(); | |||
}; | |||
} // end of namespace | |||
@@ -55,7 +55,6 @@ SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time | |||
lost_events++; | |||
return 0; | |||
} | |||
JackMidiEvent* event = &events[event_count++]; | |||
event->time = time; | |||
event->size = size; | |||
@@ -90,7 +89,7 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count | |||
{ | |||
JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer); | |||
if (!mix->IsValid()) { | |||
jack_error("MIDI: invalid mix buffer"); | |||
jack_error("Jack::MidiBufferMixdown - invalid mix buffer"); | |||
return; | |||
} | |||
mix->Reset(nframes); | |||
@@ -98,8 +97,10 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count | |||
int event_count = 0; | |||
for (int i = 0; i < src_count; ++i) { | |||
JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]); | |||
if (!buf->IsValid()) | |||
if (!buf->IsValid()) { | |||
jack_error("Jack::MidiBufferMixdown - invalid source buffer"); | |||
return; | |||
} | |||
buf->mix_index = 0; | |||
event_count += buf->event_count; | |||
mix->lost_events += buf->lost_events; | |||
@@ -0,0 +1,300 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include <cassert> | |||
#include <memory> | |||
#include <new> | |||
#include "JackMidiRawInputWriteQueue.h" | |||
using Jack::JackMidiRawInputWriteQueue; | |||
JackMidiRawInputWriteQueue:: | |||
JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, | |||
size_t max_packet_data, size_t max_packets) | |||
{ | |||
packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets); | |||
std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue); | |||
input_ring = jack_ringbuffer_create(max_packet_data + 1); | |||
if (! input_ring) { | |||
throw std::bad_alloc(); | |||
} | |||
jack_ringbuffer_mlock(input_ring); | |||
Clear(); | |||
expected_bytes = 0; | |||
event_pending = false; | |||
packet = 0; | |||
status_byte = 0; | |||
this->write_queue = write_queue; | |||
packet_queue_ptr.release(); | |||
} | |||
JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue() | |||
{ | |||
jack_ringbuffer_free(input_ring); | |||
delete packet_queue; | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::Clear() | |||
{ | |||
jack_ringbuffer_reset(input_ring); | |||
total_bytes = 0; | |||
unbuffered_bytes = 0; | |||
} | |||
Jack::JackMidiWriteQueue::EnqueueResult | |||
JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
return packet_queue->EnqueueEvent(time, size, buffer); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes, | |||
size_t total_bytes) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI " | |||
"byte(s) of a %d byte message could not be buffered. The " | |||
"message has been dropped.", unbuffered_bytes, total_bytes); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleEventLoss(jack_midi_event_t *event) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI " | |||
"event scheduled for frame '%d' could not be processed because " | |||
"the write queue cannot accomodate an event of that size. The " | |||
"event has been discarded.", event->size, event->time); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleIncompleteMessage(size_t total_bytes) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - " | |||
"Discarding %d MIDI byte(s) of an incomplete message. The " | |||
"MIDI cable may have been unplugged.", total_bytes); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleInvalidStatusByte(jack_midi_data_t byte) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - " | |||
"Dropping invalid MIDI status byte '%x'.", (unsigned int) byte); | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd(size_t total_bytes) | |||
{ | |||
jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - " | |||
"Received a sysex end byte without first receiving a sysex " | |||
"start byte. Discarding %d MIDI byte(s). The cable may have " | |||
"been unplugged.", total_bytes); | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time) | |||
{ | |||
bool result = ! unbuffered_bytes; | |||
if (! result) { | |||
HandleBufferFailure(unbuffered_bytes, total_bytes); | |||
} else { | |||
size_t size = jack_ringbuffer_read_space(input_ring); | |||
jack_ringbuffer_data_t vector[2]; | |||
jack_ringbuffer_get_read_vector(input_ring, vector); | |||
// We don't worry about the second part of the vector, as we reset the | |||
// ringbuffer after each parsed message. | |||
PrepareEvent(time, size, (jack_midi_data_t *) vector[0].buf); | |||
} | |||
Clear(); | |||
if (status_byte >= 0xf0) { | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
} | |||
return result; | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
event_byte = byte; | |||
PrepareEvent(time, 1, &event_byte); | |||
return true; | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
event.buffer = buffer; | |||
event.size = size; | |||
event.time = time; | |||
event_pending = true; | |||
} | |||
jack_nframes_t | |||
JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame) | |||
{ | |||
if (event_pending) { | |||
if (! WriteEvent(boundary_frame)) { | |||
return event.time; | |||
} | |||
} | |||
if (! packet) { | |||
packet = packet_queue->DequeueEvent(); | |||
} | |||
for (; packet; packet = packet_queue->DequeueEvent()) { | |||
for (; packet->size; (packet->buffer)++, (packet->size)--) { | |||
if (ProcessByte(packet->time, *(packet->buffer))) { | |||
if (! WriteEvent(boundary_frame)) { | |||
(packet->buffer)++; | |||
(packet->size)--; | |||
return event.time; | |||
} | |||
} | |||
} | |||
} | |||
return 0; | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
if (byte >= 0xf8) { | |||
// Realtime | |||
if (byte == 0xfd) { | |||
HandleInvalidStatusByte(byte); | |||
return false; | |||
} | |||
return PrepareByteEvent(time, byte); | |||
} | |||
if (byte == 0xf7) { | |||
// Sysex end | |||
if (status_byte == 0xf0) { | |||
RecordByte(byte); | |||
return PrepareBufferedEvent(time); | |||
} | |||
HandleUnexpectedSysexEnd(total_bytes); | |||
Clear(); | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
return false; | |||
} | |||
if (byte >= 0x80) { | |||
// Non-realtime status byte | |||
if (total_bytes) { | |||
HandleIncompleteMessage(total_bytes); | |||
Clear(); | |||
} | |||
status_byte = byte; | |||
switch (byte & 0xf0) { | |||
case 0x80: | |||
case 0x90: | |||
case 0xa0: | |||
case 0xb0: | |||
case 0xe0: | |||
// Note On, Note Off, Aftertouch, Control Change, Pitch Wheel | |||
expected_bytes = 3; | |||
break; | |||
case 0xc0: | |||
case 0xd0: | |||
// Program Change, Channel Pressure | |||
expected_bytes = 2; | |||
break; | |||
case 0xf0: | |||
switch (byte) { | |||
case 0xf0: | |||
// Sysex | |||
expected_bytes = 0; | |||
break; | |||
case 0xf1: | |||
case 0xf3: | |||
// MTC Quarter Frame, Song Select | |||
expected_bytes = 2; | |||
break; | |||
case 0xf2: | |||
// Song Position | |||
expected_bytes = 3; | |||
break; | |||
case 0xf4: | |||
case 0xf5: | |||
// Undefined | |||
HandleInvalidStatusByte(byte); | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
return false; | |||
case 0xf6: | |||
// Tune Request | |||
bool result = PrepareByteEvent(time, byte); | |||
if (result) { | |||
expected_bytes = 0; | |||
status_byte = 0; | |||
} | |||
return result; | |||
} | |||
} | |||
RecordByte(byte); | |||
return false; | |||
} | |||
// Data byte | |||
if (! status_byte) { | |||
// Data bytes without a status will be discarded. | |||
total_bytes++; | |||
unbuffered_bytes++; | |||
return false; | |||
} | |||
if (! total_bytes) { | |||
// Apply running status. | |||
RecordByte(status_byte); | |||
} | |||
RecordByte(byte); | |||
return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) : | |||
false; | |||
} | |||
void | |||
JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte) | |||
{ | |||
if (jack_ringbuffer_write(input_ring, (const char *) &byte, 1) != 1) { | |||
unbuffered_bytes++; | |||
} | |||
total_bytes++; | |||
} | |||
bool | |||
JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame) | |||
{ | |||
if ((! boundary_frame) || (event.time < boundary_frame)) { | |||
switch (write_queue->EnqueueEvent(&event)) { | |||
case BUFFER_TOO_SMALL: | |||
HandleEventLoss(&event); | |||
// Fallthrough on purpose | |||
case OK: | |||
event_pending = false; | |||
return true; | |||
default: | |||
// This is here to stop compilers from warning us about not | |||
// handling enumeration values. | |||
; | |||
} | |||
} | |||
return false; | |||
} |
@@ -0,0 +1,170 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiRawInputWriteQueue__ | |||
#define __JackMidiRawInputWriteQueue__ | |||
#include "JackMidiAsyncQueue.h" | |||
#include "JackMidiWriteQueue.h" | |||
#include "ringbuffer.h" | |||
namespace Jack { | |||
/** | |||
* This queue enqueues raw, unparsed MIDI packets, and outputs complete | |||
* MIDI messages to a write queue. | |||
* | |||
* Use this queue if the MIDI API you're interfacing with gives you raw | |||
* MIDI bytes that must be parsed. | |||
*/ | |||
class SERVER_EXPORT JackMidiRawInputWriteQueue: public JackMidiWriteQueue { | |||
private: | |||
jack_midi_event_t event; | |||
jack_midi_data_t event_byte; | |||
bool event_pending; | |||
size_t expected_bytes; | |||
jack_ringbuffer_t *input_ring; | |||
jack_midi_event_t *packet; | |||
JackMidiAsyncQueue *packet_queue; | |||
jack_midi_data_t status_byte; | |||
size_t total_bytes; | |||
size_t unbuffered_bytes; | |||
JackMidiWriteQueue *write_queue; | |||
void | |||
Clear(); | |||
bool | |||
PrepareBufferedEvent(jack_nframes_t time); | |||
bool | |||
PrepareByteEvent(jack_nframes_t time, jack_midi_data_t byte); | |||
void | |||
PrepareEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
bool | |||
ProcessByte(jack_nframes_t time, jack_midi_data_t byte); | |||
void | |||
RecordByte(jack_midi_data_t byte); | |||
bool | |||
WriteEvent(jack_nframes_t boundary_frame); | |||
protected: | |||
/** | |||
* Override this method to specify what happens when there isn't enough | |||
* room in the ringbuffer to contain a parsed event. The default | |||
* method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes); | |||
/** | |||
* Override this method to specify what happens when a parsed event | |||
* can't be written to the write queue because the event's size exceeds | |||
* the total possible space in the write queue. The default method | |||
* outputs an error message. | |||
*/ | |||
virtual void | |||
HandleEventLoss(jack_midi_event_t *event); | |||
/** | |||
* Override this method to specify what happens when an incomplete MIDI | |||
* message is parsed. The default method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleIncompleteMessage(size_t total_bytes); | |||
/** | |||
* Override this method to specify what happens when an invalid MIDI | |||
* status byte is parsed. The default method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleInvalidStatusByte(jack_midi_data_t byte); | |||
/** | |||
* Override this method to specify what happens when a sysex end byte | |||
* is parsed without first parsing a sysex begin byte. The default | |||
* method outputs an error message. | |||
*/ | |||
virtual void | |||
HandleUnexpectedSysexEnd(size_t total_bytes); | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
/** | |||
* Called to create a new raw input write queue. The `write_queue` | |||
* argument is the queue to write parsed messages to. The optional | |||
* `max_packets` argument specifies the number of packets that can be | |||
* enqueued in the internal queue. The optional `max_packet_data` | |||
* argument specifies the total number of MIDI bytes that can be put in | |||
* the internal queue, AND the maximum size for an event that can be | |||
* written to the write queue. | |||
*/ | |||
JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, | |||
size_t max_packet_data=4096, | |||
size_t max_packets=1024); | |||
~JackMidiRawInputWriteQueue(); | |||
EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
/** | |||
* The `Process()` method should be called each time the | |||
* `EnqueueEvent()` method returns `OK`. The `Process()` method will | |||
* return the next frame at which an event should be sent. The return | |||
* value from `Process()` depends upon the result of writing bytes to | |||
* the write queue: | |||
* | |||
* -If the return value is '0', then all *complete* events have been | |||
* sent successfully to the write queue. Don't call `Process()` again | |||
* until another event has been enqueued. | |||
* | |||
* -If the return value is a non-zero value, then it specifies the | |||
* frame that a pending event is scheduled to sent at. If the frame is | |||
* in the future, then `Process()` should be called again at that time; | |||
* otherwise, `Process()` should be called as soon as the write queue | |||
* will accept events again. | |||
*/ | |||
jack_nframes_t | |||
Process(jack_nframes_t boundary_frame=0); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,228 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include <memory> | |||
#include <new> | |||
#include "JackError.h" | |||
#include "JackMidiRawOutputWriteQueue.h" | |||
#include "JackMidiUtil.h" | |||
using Jack::JackMidiRawOutputWriteQueue; | |||
#define STILL_TIME(c, b) ((! (b)) || ((c) < (b))) | |||
JackMidiRawOutputWriteQueue:: | |||
JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size, | |||
size_t max_non_rt_messages, size_t max_rt_messages) | |||
{ | |||
non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages); | |||
std::auto_ptr<JackMidiAsyncQueue> non_rt_ptr(non_rt_queue); | |||
rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages); | |||
std::auto_ptr<JackMidiAsyncQueue> rt_ptr(rt_queue); | |||
non_rt_event = 0; | |||
rt_event = 0; | |||
running_status = 0; | |||
this->send_queue = send_queue; | |||
rt_ptr.release(); | |||
non_rt_ptr.release(); | |||
} | |||
JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue() | |||
{ | |||
delete non_rt_queue; | |||
delete rt_queue; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent() | |||
{ | |||
non_rt_event = non_rt_queue->DequeueEvent(); | |||
bool result = non_rt_event != 0; | |||
if (result) { | |||
non_rt_event_time = non_rt_event->time; | |||
running_status = ApplyRunningStatus(non_rt_event, running_status); | |||
} | |||
return result; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::DequeueRealtimeEvent() | |||
{ | |||
rt_event = rt_queue->DequeueEvent(); | |||
bool result = rt_event != 0; | |||
if (result) { | |||
rt_event_time = rt_event->time; | |||
} | |||
return result; | |||
} | |||
Jack::JackMidiWriteQueue::EnqueueResult | |||
JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) | |||
{ | |||
JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue : | |||
non_rt_queue; | |||
EnqueueResult result = queue->EnqueueEvent(time, size, buffer); | |||
if (result == OK) { | |||
last_enqueued_message_time = time; | |||
} | |||
return result; | |||
} | |||
void | |||
JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
jack_error("JackMidiRawOutputWriteQueue::HandleWriteQueueBug - **BUG** " | |||
"The write queue told us that it couldn't enqueue a 1-byte " | |||
"MIDI event scheduled for frame '%d'. This is probably a bug " | |||
"in the write queue implementation.", time); | |||
} | |||
jack_nframes_t | |||
JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame) | |||
{ | |||
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
while (STILL_TIME(current_frame, boundary_frame)) { | |||
if (! non_rt_event) { | |||
DequeueNonRealtimeEvent(); | |||
} | |||
if (! rt_event) { | |||
DequeueRealtimeEvent(); | |||
} | |||
if (! (non_rt_event || rt_event)) { | |||
return 0; | |||
} | |||
if (! WriteRealtimeEvents(boundary_frame)) { | |||
break; | |||
} | |||
jack_nframes_t non_rt_boundary = | |||
rt_event && STILL_TIME(rt_event_time, boundary_frame) ? | |||
rt_event_time : boundary_frame; | |||
if (! WriteNonRealtimeEvents(non_rt_boundary)) { | |||
break; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
} | |||
// If we get here, that means there is some sort of message available, and | |||
// that either we can't currently write to the write queue or we have | |||
// reached the boundary frame. Return the earliest time that a message is | |||
// scheduled to be sent. | |||
return ! non_rt_event ? rt_event_time : | |||
non_rt_event->size > 1 ? current_frame : | |||
! rt_event ? non_rt_event_time : | |||
non_rt_event_time < rt_event_time ? non_rt_event_time : rt_event_time; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time, | |||
jack_midi_data_t byte) | |||
{ | |||
switch (send_queue->EnqueueEvent(time, 1, &byte)) { | |||
case BUFFER_TOO_SMALL: | |||
HandleWriteQueueBug(time, byte); | |||
case OK: | |||
return true; | |||
default: | |||
// This is here to stop compilers from warning us about not handling | |||
// enumeration values. | |||
; | |||
} | |||
return false; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue:: | |||
WriteNonRealtimeEvents(jack_nframes_t boundary_frame) | |||
{ | |||
if (! non_rt_event) { | |||
if (! DequeueNonRealtimeEvent()) { | |||
return true; | |||
} | |||
} | |||
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
do { | |||
// Send out as much of the non-realtime buffer as we can, save for one | |||
// byte which we will send out when the message is supposed to arrive. | |||
for (; non_rt_event->size > 1; | |||
(non_rt_event->size)--, (non_rt_event->buffer)++) { | |||
if (! STILL_TIME(current_frame, boundary_frame)) { | |||
return true; | |||
} | |||
if (! SendByte(current_frame, *(non_rt_event->buffer))) { | |||
return false; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
} | |||
if (! (STILL_TIME(current_frame, boundary_frame) && | |||
STILL_TIME(non_rt_event_time, boundary_frame))) { | |||
return true; | |||
} | |||
// There's still time. Try to send the byte. | |||
if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) { | |||
return false; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
if (! DequeueNonRealtimeEvent()) { | |||
break; | |||
} | |||
} while (STILL_TIME(current_frame, boundary_frame)); | |||
return true; | |||
} | |||
bool | |||
JackMidiRawOutputWriteQueue::WriteRealtimeEvents(jack_nframes_t boundary_frame) | |||
{ | |||
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); | |||
if (! rt_event) { | |||
if (! DequeueRealtimeEvent()) { | |||
return true; | |||
} | |||
} | |||
for (;;) { | |||
if (! STILL_TIME(current_frame, boundary_frame)) { | |||
return false; | |||
} | |||
// If: | |||
// -there's still time before we need to send the realtime event | |||
// -there's a non-realtime event available for sending | |||
// -non-realtime data can be scheduled before this event | |||
if ((rt_event_time > current_frame) && non_rt_event && | |||
((non_rt_event->size > 1) || | |||
(non_rt_event_time < rt_event_time))) { | |||
return true; | |||
} | |||
if (! SendByte(rt_event_time, *(rt_event->buffer))) { | |||
return false; | |||
} | |||
current_frame = send_queue->GetNextScheduleFrame(); | |||
if (! DequeueRealtimeEvent()) { | |||
return true; | |||
} | |||
} | |||
} |
@@ -0,0 +1,147 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiRawOutputWriteQueue__ | |||
#define __JackMidiRawOutputWriteQueue__ | |||
#include "JackMidiAsyncQueue.h" | |||
#include "JackMidiSendQueue.h" | |||
namespace Jack { | |||
/** | |||
* This queue enqueues valid MIDI events and modifies them for raw output | |||
* to a write queue. It has a number of advantages over straight MIDI | |||
* event copying: | |||
* | |||
* -Running status: Status bytes can be omitted when the status byte of the | |||
* current MIDI message is the same as the status byte of the last sent | |||
* MIDI message. | |||
* | |||
* -Realtime messages: Realtime messages are given priority over | |||
* non-realtime messages. Realtime bytes are interspersed with | |||
* non-realtime bytes so that realtime messages can be sent as close as | |||
* possible to the time they're scheduled for sending. | |||
* | |||
* -Time optimization: Bytes in non-realtime messages are sent out early | |||
* when possible, with the last byte of the message being sent out as close | |||
* to the specified event time as possible. | |||
* | |||
* Use this queue if the MIDI API you're interfacing with allows you to | |||
* send raw MIDI bytes. | |||
*/ | |||
class SERVER_EXPORT JackMidiRawOutputWriteQueue: | |||
public JackMidiWriteQueue { | |||
private: | |||
jack_nframes_t last_enqueued_message_time; | |||
jack_midi_event_t *non_rt_event; | |||
jack_nframes_t non_rt_event_time; | |||
JackMidiAsyncQueue *non_rt_queue; | |||
jack_midi_event_t *rt_event; | |||
jack_nframes_t rt_event_time; | |||
JackMidiAsyncQueue *rt_queue; | |||
jack_midi_data_t running_status; | |||
JackMidiSendQueue *send_queue; | |||
bool | |||
DequeueNonRealtimeEvent(); | |||
bool | |||
DequeueRealtimeEvent(); | |||
bool | |||
SendByte(jack_nframes_t time, jack_midi_data_t byte); | |||
bool | |||
WriteNonRealtimeEvents(jack_nframes_t boundary_frame); | |||
bool | |||
WriteRealtimeEvents(jack_nframes_t boundary_frame); | |||
protected: | |||
/** | |||
* Override this method to specify what happens when the write queue | |||
* says that a 1-byte event is too large for its buffer. Basically, | |||
* this should never happen. | |||
*/ | |||
virtual void | |||
HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte); | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
/** | |||
* Called to create a new raw write queue. The `send_queue` argument | |||
* is the queue to write raw bytes to. The optional `max_rt_messages` | |||
* argument specifies the number of messages that can be enqueued in | |||
* the internal realtime queue. The optional `max_non_rt_messages` | |||
* argument specifies the number of messages that can be enqueued in | |||
* the internal non-realtime queue. The optional `non_rt_size` | |||
* argument specifies the total number of MIDI bytes that can be put in | |||
* the non-realtime queue. | |||
*/ | |||
JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, | |||
size_t non_rt_size=4096, | |||
size_t max_non_rt_messages=1024, | |||
size_t max_rt_messages=128); | |||
~JackMidiRawOutputWriteQueue(); | |||
EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer); | |||
/** | |||
* The `Process()` method should be called each time the | |||
* `EnqueueEvent()` method returns 'OK'. The `Process()` method will | |||
* return the next frame at which an event should be sent. The return | |||
* value from `Process()` depends upon the result of writing bytes to | |||
* the write queue: | |||
* | |||
* -If the return value is '0', then all events that have been enqueued | |||
* in this queue have been sent successfully to the write queue. Don't | |||
* call `Process()` again until another event has been enqueued. | |||
* | |||
* -If the return value is an earlier frame or the current frame, it | |||
* means that the write queue returned 'BUFFER_FULL', 'ERROR', or | |||
* 'EVENT_EARLY' when this queue attempted to send the next byte, and | |||
* that the byte should have already been sent, or is scheduled to be | |||
* sent *now*. `Process()` should be called again when the write queue | |||
* can enqueue events again successfully. How to determine when this | |||
* will happen is left up to the caller. | |||
* | |||
* -If the return value is in the future, then `Process()` should be | |||
* called again at that time, or after another event is enqueued. | |||
*/ | |||
jack_nframes_t | |||
Process(jack_nframes_t boundary_frame=0); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,27 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include "JackMidiReadQueue.h" | |||
using Jack::JackMidiReadQueue; | |||
JackMidiReadQueue::~JackMidiReadQueue() | |||
{ | |||
// Empty | |||
} |
@@ -0,0 +1,55 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiReadQueue__ | |||
#define __JackMidiReadQueue__ | |||
#include "JackMidiPort.h" | |||
namespace Jack { | |||
/** | |||
* Interface for objects that MIDI events can be read from. | |||
*/ | |||
class SERVER_EXPORT JackMidiReadQueue { | |||
public: | |||
virtual | |||
~JackMidiReadQueue(); | |||
/** | |||
* Dequeues an event from the queue. Returns the event, or 0 if no | |||
* events are available for reading. | |||
* | |||
* An event dequeued from the read queue is guaranteed to be valid up | |||
* until another event is dequeued, at which all bets are off. Make | |||
* sure that you handle each event you dequeue before dequeueing the | |||
* next event. | |||
*/ | |||
virtual jack_midi_event_t * | |||
DequeueEvent() = 0; | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,27 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include "JackMidiReceiveQueue.h" | |||
using Jack::JackMidiReceiveQueue; | |||
JackMidiReceiveQueue::~JackMidiReceiveQueue() | |||
{ | |||
// Empty | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiReceiveQueue__ | |||
#define __JackMidiReceiveQueue__ | |||
#include "JackMidiReadQueue.h" | |||
namespace Jack { | |||
/** | |||
* Implemented by MIDI input connections. | |||
*/ | |||
class SERVER_EXPORT JackMidiReceiveQueue: public JackMidiReadQueue { | |||
public: | |||
virtual | |||
~JackMidiReceiveQueue(); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,34 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include "JackMidiSendQueue.h" | |||
#include "JackMidiUtil.h" | |||
using Jack::JackMidiSendQueue; | |||
JackMidiSendQueue::~JackMidiSendQueue() | |||
{ | |||
// Empty | |||
} | |||
jack_nframes_t | |||
JackMidiSendQueue::GetNextScheduleFrame() | |||
{ | |||
return GetCurrentFrame(); | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiSendQueue__ | |||
#define __JackMidiSendQueue__ | |||
#include "JackMidiWriteQueue.h" | |||
namespace Jack { | |||
/** | |||
* Implemented by MIDI output connections. | |||
*/ | |||
class SERVER_EXPORT JackMidiSendQueue: public JackMidiWriteQueue { | |||
public: | |||
using JackMidiWriteQueue::EnqueueEvent; | |||
virtual | |||
~JackMidiSendQueue(); | |||
/** | |||
* Returns the next frame that a MIDI message can be sent at. The | |||
* default method returns the current frame. | |||
*/ | |||
virtual jack_nframes_t | |||
GetNextScheduleFrame(); | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,120 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include "JackEngineControl.h" | |||
#include "JackFrameTimer.h" | |||
#include "JackGlobals.h" | |||
#include "JackMidiUtil.h" | |||
#include "JackTime.h" | |||
jack_midi_data_t | |||
Jack::ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, | |||
jack_midi_data_t running_status) | |||
{ | |||
// Stolen and modified from alsa/midi_pack.h | |||
jack_midi_data_t status = **buffer; | |||
if ((status >= 0x80) && (status < 0xf0)) { | |||
if (status == running_status) { | |||
(*buffer)++; | |||
(*size)--; | |||
} else { | |||
running_status = status; | |||
} | |||
} else if (status < 0xf8) { | |||
running_status = 0; | |||
} | |||
return running_status; | |||
} | |||
jack_midi_data_t | |||
Jack::ApplyRunningStatus(jack_midi_event_t *event, | |||
jack_midi_data_t running_status) | |||
{ | |||
return ApplyRunningStatus(&(event->size), &(event->buffer), | |||
running_status); | |||
} | |||
jack_nframes_t | |||
Jack::GetCurrentFrame() | |||
{ | |||
JackEngineControl *control = GetEngineControl(); | |||
JackTimer timer; | |||
control->ReadFrameTime(&timer); | |||
return timer.Time2Frames(GetMicroSeconds(), control->fBufferSize); | |||
} | |||
jack_nframes_t | |||
Jack::GetFramesFromTime(jack_time_t time) | |||
{ | |||
JackEngineControl* control = GetEngineControl(); | |||
JackTimer timer; | |||
control->ReadFrameTime(&timer); | |||
return timer.Time2Frames(time, control->fBufferSize); | |||
} | |||
jack_nframes_t | |||
Jack::GetLastFrame() | |||
{ | |||
return GetEngineControl()->fFrameTimer.ReadCurrentState()->CurFrame(); | |||
} | |||
int | |||
Jack::GetMessageLength(jack_midi_data_t status_byte) | |||
{ | |||
switch (status_byte & 0xf0) { | |||
case 0x80: | |||
case 0x90: | |||
case 0xa0: | |||
case 0xb0: | |||
case 0xe0: | |||
return 3; | |||
case 0xc0: | |||
case 0xd0: | |||
return 2; | |||
case 0xf0: | |||
switch (status_byte) { | |||
case 0xf0: | |||
return 0; | |||
case 0xf1: | |||
case 0xf3: | |||
return 2; | |||
case 0xf2: | |||
return 3; | |||
case 0xf4: | |||
case 0xf5: | |||
case 0xf7: | |||
case 0xfd: | |||
break; | |||
default: | |||
return 1; | |||
} | |||
} | |||
return -1; | |||
} | |||
jack_time_t | |||
Jack::GetTimeFromFrames(jack_nframes_t frames) | |||
{ | |||
JackEngineControl* control = GetEngineControl(); | |||
JackTimer timer; | |||
control->ReadFrameTime(&timer); | |||
return timer.Frames2Time(frames, control->fBufferSize); | |||
} |
@@ -0,0 +1,102 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiUtil__ | |||
#define __JackMidiUtil__ | |||
#include "JackMidiPort.h" | |||
namespace Jack { | |||
/** | |||
* Use this function to optimize MIDI output by omitting unnecessary status | |||
* bytes. This can't be used with all MIDI APIs, so before using this | |||
* function, make sure that your MIDI API doesn't require complete MIDI | |||
* messages to be sent. | |||
* | |||
* To start using this function, call this method with pointers to the | |||
* `size` and `buffer` arguments of the MIDI message you want to send, and | |||
* set the `running_status` argument to '0'. For each subsequent MIDI | |||
* message, call this method with pointers to its `size` and `buffer` | |||
* arguments, and set the `running_status` argument to the return value of | |||
* the previous call to this function. | |||
* | |||
* Note: This function will alter the `size` and `buffer` of your MIDI | |||
* message for each message that can be optimized. | |||
*/ | |||
SERVER_EXPORT jack_midi_data_t | |||
ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, | |||
jack_midi_data_t running_status=0); | |||
/** | |||
* A wrapper function for the above `ApplyRunningStatus` function. | |||
*/ | |||
SERVER_EXPORT jack_midi_data_t | |||
ApplyRunningStatus(jack_midi_event_t *event, | |||
jack_midi_data_t running_status); | |||
/** | |||
* Gets the estimated current time in frames. This function has the same | |||
* functionality as the JACK client API function `jack_frame_time`. | |||
*/ | |||
SERVER_EXPORT jack_nframes_t | |||
GetCurrentFrame(); | |||
/** | |||
* Gets the estimated frame that will be occurring at the given time. This | |||
* function has the same functionality as the JACK client API function | |||
* `jack_time_to_frames`. | |||
*/ | |||
SERVER_EXPORT jack_nframes_t | |||
GetFramesFromTime(jack_time_t time); | |||
/** | |||
* Gets the precise time at the start of the current process cycle. This | |||
* function has the same functionality as the JACK client API function | |||
* `jack_last_frame_time`. | |||
*/ | |||
SERVER_EXPORT jack_nframes_t | |||
GetLastFrame(); | |||
/** | |||
* Returns the expected message length for the status byte. Returns 0 if | |||
* the status byte is a system exclusive status byte, or -1 if the status | |||
* byte is invalid. | |||
*/ | |||
SERVER_EXPORT int | |||
GetMessageLength(jack_midi_data_t status_byte); | |||
/** | |||
* Gets the estimated time at which the given frame will occur. This | |||
* function has the same functionality as the JACK client API function | |||
* `jack_frames_to_time`. | |||
*/ | |||
SERVER_EXPORT jack_time_t | |||
GetTimeFromFrames(jack_nframes_t frames); | |||
}; | |||
#endif |
@@ -0,0 +1,27 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include "JackMidiWriteQueue.h" | |||
using Jack::JackMidiWriteQueue; | |||
JackMidiWriteQueue::~JackMidiWriteQueue() | |||
{ | |||
// Empty | |||
} |
@@ -0,0 +1,82 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __JackMidiWriteQueue__ | |||
#define __JackMidiWriteQueue__ | |||
#include "JackMidiPort.h" | |||
namespace Jack { | |||
/** | |||
* Interface for classes that act as write queues for MIDI messages. Write | |||
* queues are used by processors to transfer data to the next processor. | |||
*/ | |||
class SERVER_EXPORT JackMidiWriteQueue { | |||
public: | |||
enum EnqueueResult { | |||
BUFFER_FULL, | |||
BUFFER_TOO_SMALL, | |||
EVENT_EARLY, | |||
EN_ERROR, | |||
OK | |||
}; | |||
virtual ~JackMidiWriteQueue(); | |||
/** | |||
* Enqueues a data packet in the write queue of `size` bytes contained | |||
* in `buffer` that will be sent the absolute time specified by `time`. | |||
* This method should not block unless 1.) this write queue represents | |||
* the actual outbound MIDI connection, 2.) the MIDI event is being | |||
* sent *now*, meaning that `time` is less than or equal to *now*, and | |||
* 3.) the method is *not* being called in the process thread. The | |||
* method should return `OK` if the event was enqueued, `BUFFER_FULL` | |||
* if the write queue isn't able to accept the event right now, | |||
* `BUFFER_TOO_SMALL` if this write queue will never be able to accept | |||
* the event because the event is too large, `EVENT_EARLY` if this | |||
* queue cannot schedule events ahead of time, and `EN_ERROR` if an error | |||
* occurs that cannot be specified by another return code. | |||
*/ | |||
virtual EnqueueResult | |||
EnqueueEvent(jack_nframes_t time, size_t size, | |||
jack_midi_data_t *buffer) = 0; | |||
/** | |||
* A wrapper method for the `EnqueueEvent` method above. The optional | |||
* 'frame_offset' argument is an amount of frames to add to the event's | |||
* time. | |||
*/ | |||
inline EnqueueResult | |||
EnqueueEvent(jack_midi_event_t *event, jack_nframes_t frame_offset=0) | |||
{ | |||
return EnqueueEvent(event->time + frame_offset, event->size, | |||
event->buffer); | |||
} | |||
}; | |||
} | |||
#endif |
@@ -164,6 +164,7 @@ namespace Jack | |||
#ifdef JACK_MONITOR | |||
fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize); | |||
#endif | |||
fSocket.Close(); | |||
switch ( fThread.GetStatus() ) | |||
{ | |||
@@ -177,7 +178,7 @@ namespace Jack | |||
} | |||
break; | |||
// Stop when the thread cycle is finished | |||
case JackThread::kRunning: | |||
if ( fThread.Stop() < 0 ) | |||
{ | |||
@@ -185,11 +186,11 @@ namespace Jack | |||
return -1; | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
fSocket.Close(); | |||
return 0; | |||
} | |||
@@ -242,16 +243,16 @@ namespace Jack | |||
//set audio adapter parameters | |||
SetAdaptedBufferSize ( fParams.fPeriodSize ); | |||
SetAdaptedSampleRate ( fParams.fSampleRate ); | |||
// Will do "something" on OSX only... | |||
fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); | |||
if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) { | |||
jack_error("AcquireSelfRealTime error"); | |||
} else { | |||
set_threaded_log_function(); | |||
} | |||
//init done, display parameters | |||
SessionParamsDisplay ( &fParams ); | |||
return true; | |||
@@ -294,13 +295,13 @@ namespace Jack | |||
jack_transport_stop ( fJackClient ); | |||
jack_info ( "NetMaster : transport stops." ); | |||
break; | |||
case JackTransportStarting : | |||
jack_transport_reposition ( fJackClient, &fSendTransportData.fPosition ); | |||
jack_transport_start ( fJackClient ); | |||
jack_info ( "NetMaster : transport starts." ); | |||
break; | |||
case JackTransportRolling : | |||
//TODO , we need to : | |||
// - find a way to call TransportEngine->SetNetworkSync() | |||
@@ -362,7 +363,7 @@ namespace Jack | |||
int JackNetAdapter::Write() | |||
{ | |||
EncodeSyncPacket(); | |||
if ( SyncSend() == SOCKET_ERROR ) | |||
return SOCKET_ERROR; | |||
@@ -376,7 +377,7 @@ namespace Jack | |||
//in case of fatal network error, stop the process | |||
if (Read() == SOCKET_ERROR) | |||
return SOCKET_ERROR; | |||
PushAndPull(fSoftCaptureBuffer, fSoftPlaybackBuffer, fAdaptedBufferSize); | |||
//then write data to network | |||
@@ -386,7 +387,7 @@ namespace Jack | |||
return 0; | |||
} | |||
} // namespace Jack | |||
//loader------------------------------------------------------------------------------ | |||
@@ -403,10 +404,10 @@ extern "C" | |||
SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() | |||
{ | |||
jack_driver_desc_t* desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) ); | |||
strcpy(desc->name, "netadapter"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 | |||
strcpy(desc->desc, "netjack net <==> audio backend adapter"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 | |||
desc->nparams = 11; | |||
desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); | |||
@@ -473,7 +474,7 @@ extern "C" | |||
strcpy ( desc->params[i].value.str, "slow" ); | |||
strcpy ( desc->params[i].short_desc, "Slow, Normal or Fast mode." ); | |||
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); | |||
i++; | |||
strcpy(desc->params[i].name, "quality"); | |||
desc->params[i].character = 'q'; | |||
@@ -481,7 +482,7 @@ extern "C" | |||
desc->params[i].value.ui = 0; | |||
strcpy(desc->params[i].short_desc, "Resample algorithm quality (0 - 4)"); | |||
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); | |||
i++; | |||
strcpy(desc->params[i].name, "ring-buffer"); | |||
desc->params[i].character = 'g'; | |||
@@ -489,7 +490,7 @@ extern "C" | |||
desc->params[i].value.ui = 32768; | |||
strcpy(desc->params[i].short_desc, "Fixed ringbuffer size"); | |||
strcpy(desc->params[i].long_desc, "Fixed ringbuffer size (if not set => automatic adaptative)"); | |||
i++; | |||
strcpy ( desc->params[i].name, "auto-connect" ); | |||
desc->params[i].character = 'c'; | |||
@@ -497,7 +498,7 @@ extern "C" | |||
desc->params[i].value.i = false; | |||
strcpy ( desc->params[i].short_desc, "Auto connect netmaster to system ports" ); | |||
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); | |||
return desc; | |||
} | |||
@@ -508,20 +509,19 @@ extern "C" | |||
Jack::JackAudioAdapter* adapter; | |||
jack_nframes_t buffer_size = jack_get_buffer_size ( jack_client ); | |||
jack_nframes_t sample_rate = jack_get_sample_rate ( jack_client ); | |||
try { | |||
adapter = new Jack::JackAudioAdapter(jack_client, new Jack::JackNetAdapter(jack_client, buffer_size, sample_rate, params), params, false); | |||
assert ( adapter ); | |||
if ( adapter->Open() == 0 ) | |||
if (adapter->Open() == 0) { | |||
return 0; | |||
else | |||
{ | |||
} else { | |||
delete adapter; | |||
return 1; | |||
} | |||
} catch (...) { | |||
return 1; | |||
} | |||
@@ -30,44 +30,48 @@ namespace Jack | |||
\brief Net adapter. | |||
*/ | |||
class JackNetAdapter : public JackAudioAdapterInterface, public JackNetSlaveInterface, public JackRunnableInterface | |||
class JackNetAdapter : public JackAudioAdapterInterface, | |||
public JackNetSlaveInterface, | |||
public JackRunnableInterface | |||
{ | |||
private: | |||
//jack data | |||
jack_client_t* fJackClient; | |||
//transport data | |||
int fLastTransportState; | |||
int fLastTimebaseMaster; | |||
//sample buffers | |||
sample_t** fSoftCaptureBuffer; | |||
sample_t** fSoftPlaybackBuffer; | |||
private: | |||
//adapter thread | |||
JackThread fThread; | |||
//jack data | |||
jack_client_t* fJackClient; | |||
//transport | |||
void EncodeTransportData(); | |||
void DecodeTransportData(); | |||
//transport data | |||
int fLastTransportState; | |||
int fLastTimebaseMaster; | |||
public: | |||
//sample buffers | |||
sample_t** fSoftCaptureBuffer; | |||
sample_t** fSoftPlaybackBuffer; | |||
JackNetAdapter ( jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params ); | |||
~JackNetAdapter(); | |||
//adapter thread | |||
JackThread fThread; | |||
int Open(); | |||
int Close(); | |||
//transport | |||
void EncodeTransportData(); | |||
void DecodeTransportData(); | |||
int SetBufferSize ( jack_nframes_t buffer_size ); | |||
public: | |||
bool Init(); | |||
bool Execute(); | |||
JackNetAdapter(jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params); | |||
~JackNetAdapter(); | |||
int Read(); | |||
int Write(); | |||
int Open(); | |||
int Close(); | |||
int Process(); | |||
int SetBufferSize(jack_nframes_t buffer_size); | |||
bool Init(); | |||
bool Execute(); | |||
int Read(); | |||
int Write(); | |||
int Process(); | |||
}; | |||
} | |||
@@ -125,6 +125,7 @@ namespace Jack | |||
bool JackNetDriver::Initialize() | |||
{ | |||
jack_log("JackNetDriver::Initialize()"); | |||
FreePorts(); | |||
//new loading, but existing socket, restart the driver | |||
if (fSocket.IsSocket()) { | |||
@@ -143,7 +144,7 @@ namespace Jack | |||
//init network | |||
if (!JackNetSlaveInterface::Init()) { | |||
jack_error("JackNetSlaveInterface::Init() error..." ); | |||
jack_error("Starting network fails..."); | |||
return false; | |||
} | |||
@@ -153,21 +154,32 @@ namespace Jack | |||
return false; | |||
} | |||
// If -1 at conection time, in/out channels count is sent by the master | |||
fCaptureChannels = fParams.fSendAudioChannels; | |||
fPlaybackChannels = fParams.fReturnAudioChannels; | |||
//allocate midi ports lists | |||
fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels]; | |||
fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels]; | |||
assert ( fMidiCapturePortList ); | |||
assert ( fMidiPlaybackPortList ); | |||
assert(fMidiCapturePortList); | |||
assert(fMidiPlaybackPortList); | |||
for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { | |||
fMidiCapturePortList[midi_port_index] = 0; | |||
} | |||
for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { | |||
fMidiPlaybackPortList[midi_port_index] = 0; | |||
} | |||
//register jack ports | |||
if ( AllocPorts() != 0 ) | |||
{ | |||
jack_error ( "Can't allocate ports." ); | |||
if (AllocPorts() != 0) { | |||
jack_error("Can't allocate ports."); | |||
return false; | |||
} | |||
//init done, display parameters | |||
SessionParamsDisplay ( &fParams ); | |||
SessionParamsDisplay(&fParams); | |||
//monitor | |||
#ifdef JACK_MONITOR | |||
@@ -257,7 +269,7 @@ namespace Jack | |||
char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | |||
unsigned long port_flags; | |||
int audio_port_index; | |||
uint midi_port_index; | |||
int midi_port_index; | |||
jack_latency_range_t range; | |||
//audio | |||
@@ -365,22 +377,38 @@ namespace Jack | |||
int JackNetDriver::FreePorts() | |||
{ | |||
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]); | |||
fCapturePortList[audio_port_index] = 0; | |||
} | |||
} | |||
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]); | |||
fPlaybackPortList[audio_port_index] = 0; | |||
} | |||
} | |||
for (int 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]); | |||
fMidiCapturePortList[midi_port_index] = 0; | |||
} | |||
} | |||
for (int 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]); | |||
fMidiPlaybackPortList[midi_port_index] = 0; | |||
} | |||
} | |||
// Clear MIDI channels | |||
fParams.fSendMidiChannels = 0; | |||
fParams.fReturnMidiChannels = 0; | |||
return 0; | |||
} | |||
@@ -479,8 +507,8 @@ namespace Jack | |||
//driver processes-------------------------------------------------------------------- | |||
int JackNetDriver::Read() | |||
{ | |||
uint midi_port_index; | |||
uint audio_port_index; | |||
int midi_port_index; | |||
int audio_port_index; | |||
//buffers | |||
for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) | |||
@@ -524,7 +552,7 @@ namespace Jack | |||
int JackNetDriver::Write() | |||
{ | |||
uint midi_port_index; | |||
int midi_port_index; | |||
int audio_port_index; | |||
//buffers | |||
@@ -603,17 +631,17 @@ namespace Jack | |||
strcpy ( desc->params[i].name, "input_ports" ); | |||
desc->params[i].character = 'C'; | |||
desc->params[i].type = JackDriverParamInt; | |||
desc->params[i].value.i = 2; | |||
desc->params[i].value.i = -1; | |||
strcpy ( desc->params[i].short_desc, "Number of audio input ports" ); | |||
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); | |||
strcpy ( desc->params[i].long_desc, "Number of audio input ports. If -1, audio physical input from the master"); | |||
i++; | |||
strcpy ( desc->params[i].name, "output_ports" ); | |||
desc->params[i].character = 'P'; | |||
desc->params[i].type = JackDriverParamInt; | |||
desc->params[i].value.i = 2; | |||
desc->params[i].value.i = -1; | |||
strcpy ( desc->params[i].short_desc, "Number of audio output ports" ); | |||
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); | |||
strcpy ( desc->params[i].long_desc, "Number of audio output ports. If -1, audio physical output from the master"); | |||
i++; | |||
strcpy ( desc->params[i].name, "midi_in_ports" ); | |||
@@ -626,7 +654,7 @@ namespace Jack | |||
i++; | |||
strcpy ( desc->params[i].name, "midi_out_ports" ); | |||
desc->params[i].character = 'o'; | |||
desc->params[i].type = JackDriverParamUInt; | |||
desc->params[i].type = JackDriverParamInt; | |||
desc->params[i].value.i = 0; | |||
strcpy ( desc->params[i].short_desc, "Number of midi output ports" ); | |||
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); | |||
@@ -668,8 +696,8 @@ namespace Jack | |||
uint transport_sync = 1; | |||
jack_nframes_t period_size = 128; | |||
jack_nframes_t sample_rate = 48000; | |||
int audio_capture_ports = 2; | |||
int audio_playback_ports = 2; | |||
int audio_capture_ports = -1; | |||
int audio_playback_ports = -1; | |||
int midi_input_ports = 0; | |||
int midi_output_ports = 0; | |||
bool monitor = false; | |||
@@ -36,15 +36,17 @@ namespace Jack | |||
class JackNetDriver : public JackAudioDriver, public JackNetSlaveInterface | |||
{ | |||
private: | |||
//jack data | |||
jack_port_id_t* fMidiCapturePortList; | |||
jack_port_id_t* fMidiPlaybackPortList; | |||
//transport | |||
int fLastTransportState; | |||
int fLastTimebaseMaster; | |||
//monitoring | |||
#ifdef JACK_MONITOR | |||
JackGnuPlotMonitor<float>* fNetTimeMon; | |||
@@ -53,7 +55,7 @@ namespace Jack | |||
bool Initialize(); | |||
void FreeAll(); | |||
int AllocPorts(); | |||
int FreePorts(); | |||
@@ -65,6 +67,7 @@ namespace Jack | |||
JackMidiBuffer* GetMidiOutputBuffer ( int port_index ); | |||
public: | |||
JackNetDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | |||
const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports, | |||
char* net_name, uint transport_sync, char network_master_mode ); | |||
@@ -27,9 +27,9 @@ using namespace std; | |||
#define PACKET_AVAILABLE_SIZE (fParams.fMtu - sizeof(packet_header_t)) | |||
#define HEADER_SIZE (sizeof(packet_header_t)) | |||
/* | |||
TODO : since midi buffers now uses up to BUFFER_SIZE_MAX frames, | |||
probably also use BUFFER_SIZE_MAX in everything related to MIDI events | |||
/* | |||
TODO : since midi buffers now uses up to BUFFER_SIZE_MAX frames, | |||
probably also use BUFFER_SIZE_MAX in everything related to MIDI events | |||
handling (see MidiBufferInit in JackMidiPort.cpp) | |||
*/ | |||
@@ -169,7 +169,7 @@ namespace Jack | |||
//timeout on receive (for init) | |||
if ( fSocket.SetTimeOut ( MASTER_INIT_TIMEOUT ) < 0 ) | |||
jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) ); | |||
//connect | |||
if ( fSocket.Connect() == SOCKET_ERROR ) { | |||
jack_error ( "Can't connect : %s", StrError ( NET_ERROR_CODE ) ); | |||
@@ -184,17 +184,17 @@ namespace Jack | |||
memset(&net_params, 0, sizeof ( session_params_t )); | |||
SetPacketType ( &fParams, SLAVE_SETUP ); | |||
SessionParamsHToN(&fParams, &net_params); | |||
if ( fSocket.Send ( &net_params, sizeof ( session_params_t ), 0 ) == SOCKET_ERROR ) | |||
jack_error ( "Error in send : ", StrError ( NET_ERROR_CODE ) ); | |||
memset(&net_params, 0, sizeof (session_params_t)); | |||
if (((rx_bytes = fSocket.Recv(&net_params, sizeof(session_params_t), 0)) == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) | |||
{ | |||
jack_error ( "Problem with network." ); | |||
return false; | |||
} | |||
SessionParamsNToH(&net_params, &host_params); | |||
} | |||
while ( ( GetPacketType ( &host_params ) != START_MASTER ) && ( ++attempt < SLAVE_SETUP_RETRY ) ); | |||
@@ -320,7 +320,7 @@ namespace Jack | |||
jack_info ( "Exiting '%s'", fParams.fName ); | |||
SetPacketType ( &fParams, KILL_MASTER ); | |||
JackNetSocket mcast_socket ( fMulticastIP, fSocket.GetPort() ); | |||
session_params_t net_params; | |||
memset(&net_params, 0, sizeof ( session_params_t )); | |||
SessionParamsHToN(&fParams, &net_params); | |||
@@ -329,7 +329,7 @@ namespace Jack | |||
jack_error ( "Can't create socket : %s", StrError ( NET_ERROR_CODE ) ); | |||
if ( mcast_socket.SendTo ( &net_params, sizeof ( session_params_t ), 0, fMulticastIP ) == SOCKET_ERROR ) | |||
jack_error ( "Can't send suicide request : %s", StrError ( NET_ERROR_CODE ) ); | |||
mcast_socket.Close(); | |||
} | |||
@@ -347,32 +347,32 @@ namespace Jack | |||
jack_error ( "'%s' : %s, exiting.", fParams.fName, StrError(NET_ERROR_CODE)); | |||
//ask to the manager to properly remove the master | |||
Exit(); | |||
// UGLY temporary way to be sure the thread does not call code possibly causing a deadlock in JackEngine. | |||
ThreadExit(); | |||
} else { | |||
jack_error ( "Error in master receive : %s", StrError(NET_ERROR_CODE)); | |||
} | |||
} | |||
packet_header_t* header = reinterpret_cast<packet_header_t*>(fRxBuffer); | |||
PacketHeaderNToH(header, header); | |||
return rx_bytes; | |||
} | |||
int JackNetMasterInterface::Send(size_t size, int flags) | |||
{ | |||
int tx_bytes; | |||
packet_header_t* header = reinterpret_cast<packet_header_t*>(fTxBuffer); | |||
PacketHeaderHToN(header, header); | |||
if (((tx_bytes = fSocket.Send(fTxBuffer, size, flags)) == SOCKET_ERROR) && fRunning) { | |||
net_error_t error = fSocket.GetError(); | |||
if (error == NET_CONN_ERROR) { | |||
//fatal connection issue, exit | |||
jack_error ("'%s' : %s, exiting.", fParams.fName, StrError (NET_ERROR_CODE)); | |||
Exit(); | |||
// UGLY temporary way to be sure the thread does not call code possibly causing a deadlock in JackEngine. | |||
ThreadExit(); | |||
} else { | |||
@@ -381,7 +381,7 @@ namespace Jack | |||
} | |||
return tx_bytes; | |||
} | |||
bool JackNetMasterInterface::IsSynched() | |||
{ | |||
if (fParams.fNetworkMode == 's') { | |||
@@ -390,7 +390,7 @@ namespace Jack | |||
return true; | |||
} | |||
} | |||
int JackNetMasterInterface::SyncSend() | |||
{ | |||
fTxHeader.fCycle++; | |||
@@ -452,12 +452,12 @@ namespace Jack | |||
{ | |||
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer ); | |||
int rx_bytes = Recv(HEADER_SIZE, MSG_PEEK); | |||
if ( ( rx_bytes == 0 ) || ( rx_bytes == SOCKET_ERROR ) ) | |||
return rx_bytes; | |||
fCycleOffset = fTxHeader.fCycle - rx_head->fCycle; | |||
switch ( fParams.fNetworkMode ) | |||
{ | |||
case 's' : | |||
@@ -468,12 +468,12 @@ namespace Jack | |||
//the slow mode is the safest mode because it wait twice the bandwidth relative time (send/return + process) | |||
/* | |||
if (fCycleOffset < CYCLE_OFFSET_SLOW) { | |||
return 0; | |||
} else { | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
} | |||
*/ | |||
if (fCycleOffset < CYCLE_OFFSET_SLOW) { | |||
return 0; | |||
} else { | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
} | |||
*/ | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
@@ -481,7 +481,7 @@ namespace Jack | |||
jack_info("Warning : '%s' runs in slow network mode, but data received too late (%d cycle(s) offset)", fParams.fName, fCycleOffset); | |||
fLastfCycleOffset = fCycleOffset; | |||
break; | |||
case 'n' : | |||
//normal use of the network : | |||
// - extra latency is set to one cycle, what is the time needed to receive streams using full network bandwidth | |||
@@ -492,12 +492,12 @@ namespace Jack | |||
} else { | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
} | |||
if (fCycleOffset > CYCLE_OFFSET_NORMAL) { | |||
jack_info("'%s' can't run in normal network mode, data received too late (%d cycle(s) offset)", fParams.fName, fCycleOffset); | |||
} | |||
break; | |||
case 'f' : | |||
//fast mode suppose the network bandwith is larger than required for the transmission (only a few channels for example) | |||
// - packets can be quickly received, quickly is here relative to the cycle duration | |||
@@ -514,7 +514,7 @@ namespace Jack | |||
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
return rx_bytes; | |||
} | |||
int JackNetMasterInterface::DataRecv() | |||
{ | |||
int rx_bytes = 0; | |||
@@ -522,12 +522,12 @@ namespace Jack | |||
uint recvd_audio_pckt = 0; | |||
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer ); | |||
while ( !fRxHeader.fIsLastPckt ) | |||
{ | |||
//how much data is queued on the rx buffer ? | |||
rx_bytes = Recv(HEADER_SIZE, MSG_PEEK); | |||
//error here, problem with recv, just skip the cycle (return -1) | |||
if ( rx_bytes == SOCKET_ERROR ) | |||
return rx_bytes; | |||
@@ -569,7 +569,7 @@ namespace Jack | |||
return rx_bytes; | |||
} | |||
void JackNetMasterInterface::EncodeSyncPacket() | |||
{ | |||
//this method contains every step of sync packet informations coding | |||
@@ -637,9 +637,9 @@ namespace Jack | |||
return true; | |||
} | |||
// Separate the connection protocol into two separated step | |||
bool JackNetSlaveInterface::InitConnection(int time_out) | |||
{ | |||
jack_log("JackNetSlaveInterface::InitConnection()"); | |||
@@ -659,10 +659,10 @@ namespace Jack | |||
return false; | |||
} | |||
while (status != NET_CONNECTED && --try_count > 0); | |||
return (try_count != 0); | |||
} | |||
bool JackNetSlaveInterface::InitRendering() | |||
{ | |||
jack_log("JackNetSlaveInterface::InitRendering()"); | |||
@@ -676,8 +676,8 @@ namespace Jack | |||
if (status == NET_ERROR) | |||
return false; | |||
} | |||
while (status != NET_ROLLING); | |||
while (status != NET_ROLLING); | |||
return true; | |||
} | |||
@@ -701,7 +701,7 @@ namespace Jack | |||
} | |||
//timeout on receive | |||
if ( fSocket.SetTimeOut ( SLAVE_INIT_TIMEOUT ) == SOCKET_ERROR ) | |||
if ( fSocket.SetTimeOut ( SLAVE_INIT_TIMEOUT ) == SOCKET_ERROR ) | |||
jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) ); | |||
//disable local loop | |||
@@ -718,7 +718,7 @@ namespace Jack | |||
SessionParamsHToN(&fParams, &net_params); | |||
if ( fSocket.SendTo ( &net_params, sizeof ( session_params_t ), 0, fMulticastIP ) == SOCKET_ERROR ) | |||
jack_error ( "Error in data send : %s", StrError ( NET_ERROR_CODE ) ); | |||
//filter incoming packets : don't exit while no error is detected | |||
memset(&net_params, 0, sizeof ( session_params_t )); | |||
rx_bytes = fSocket.CatchHost ( &net_params, sizeof ( session_params_t ), 0 ); | |||
@@ -768,7 +768,7 @@ namespace Jack | |||
bool JackNetSlaveInterface::SetParams() | |||
{ | |||
jack_log ( "JackNetSlaveInterface::SetParams" ); | |||
jack_log("JackNetSlaveInterface::SetParams"); | |||
JackNetInterface::SetParams(); | |||
@@ -861,7 +861,7 @@ namespace Jack | |||
jack_error ( "Fatal error in slave receive : %s", StrError ( NET_ERROR_CODE ) ); | |||
} | |||
} | |||
packet_header_t* header = reinterpret_cast<packet_header_t*>(fRxBuffer); | |||
PacketHeaderNToH(header, header); | |||
return rx_bytes; | |||
@@ -872,7 +872,7 @@ namespace Jack | |||
packet_header_t* header = reinterpret_cast<packet_header_t*>(fTxBuffer); | |||
PacketHeaderHToN(header, header); | |||
int tx_bytes = fSocket.Send ( fTxBuffer, size, flags ); | |||
//handle errors | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
{ | |||
@@ -902,7 +902,7 @@ namespace Jack | |||
return rx_bytes; | |||
} | |||
while ((strcmp(rx_head->fPacketType, "header") != 0) && (rx_head->fDataType != 's')); | |||
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
return rx_bytes; | |||
} | |||
@@ -1020,7 +1020,7 @@ namespace Jack | |||
} | |||
return 0; | |||
} | |||
//network sync------------------------------------------------------------------------ | |||
void JackNetSlaveInterface::EncodeSyncPacket() | |||
{ | |||
@@ -1037,7 +1037,7 @@ namespace Jack | |||
//then others | |||
//... | |||
} | |||
void JackNetSlaveInterface::DecodeSyncPacket() | |||
{ | |||
//this method contains every step of sync packet informations decoding process | |||
@@ -31,12 +31,10 @@ namespace Jack | |||
class SERVER_EXPORT JackNetInterface | |||
{ | |||
private: | |||
protected: | |||
void Initialize(); | |||
protected: | |||
session_params_t fParams; | |||
JackNetSocket fSocket; | |||
char fMulticastIP[32]; | |||
@@ -44,7 +42,7 @@ namespace Jack | |||
//headers | |||
packet_header_t fTxHeader; | |||
packet_header_t fRxHeader; | |||
// transport | |||
net_transport_data_t fSendTransportData; | |||
net_transport_data_t fReturnTransportData; | |||
@@ -90,7 +88,9 @@ namespace Jack | |||
JackNetInterface ( session_params_t& params, JackNetSocket& socket, const char* multicast_ip ); | |||
public: | |||
virtual ~JackNetInterface(); | |||
}; | |||
/** | |||
@@ -99,7 +99,9 @@ namespace Jack | |||
class SERVER_EXPORT JackNetMasterInterface : public JackNetInterface | |||
{ | |||
protected: | |||
bool fRunning; | |||
int fCycleOffset; | |||
int fLastfCycleOffset; | |||
@@ -107,22 +109,22 @@ namespace Jack | |||
bool Init(); | |||
int SetRxTimeout(); | |||
bool SetParams(); | |||
void Exit(); | |||
int SyncRecv(); | |||
int SyncSend(); | |||
int DataRecv(); | |||
int DataSend(); | |||
//sync packet | |||
void EncodeSyncPacket(); | |||
void DecodeSyncPacket(); | |||
int Send ( size_t size, int flags ); | |||
int Recv ( size_t size, int flags ); | |||
bool IsSynched(); | |||
public: | |||
@@ -141,25 +143,26 @@ namespace Jack | |||
class SERVER_EXPORT JackNetSlaveInterface : public JackNetInterface | |||
{ | |||
protected: | |||
static uint fSlaveCounter; | |||
bool Init(); | |||
bool InitConnection(int time_out); | |||
bool InitRendering(); | |||
net_status_t SendAvailableToMaster(long count = LONG_MAX); // long here (and not int...) | |||
net_status_t SendStartToMaster(); | |||
bool SetParams(); | |||
int SyncRecv(); | |||
int SyncSend(); | |||
int DataRecv(); | |||
int DataSend(); | |||
//sync packet | |||
void EncodeSyncPacket(); | |||
void DecodeSyncPacket(); | |||
@@ -168,6 +171,7 @@ namespace Jack | |||
int Send ( size_t size, int flags ); | |||
public: | |||
JackNetSlaveInterface() : JackNetInterface() | |||
{ | |||
//open Socket API with the first slave | |||
@@ -180,6 +184,7 @@ namespace Jack | |||
} | |||
} | |||
} | |||
JackNetSlaveInterface ( const char* ip, int port ) : JackNetInterface ( ip, port ) | |||
{ | |||
//open Socket API with the first slave | |||
@@ -192,6 +197,7 @@ namespace Jack | |||
} | |||
} | |||
} | |||
~JackNetSlaveInterface() | |||
{ | |||
//close Socket API with the last slave | |||
@@ -37,7 +37,7 @@ namespace Jack | |||
fSendTransportData.fState = -1; | |||
fReturnTransportData.fState = -1; | |||
fLastTransportState = -1; | |||
uint port_index; | |||
int port_index; | |||
//jack audio ports | |||
fAudioCapturePorts = new jack_port_t* [fParams.fSendAudioChannels]; | |||
@@ -169,7 +169,7 @@ namespace Jack | |||
//jack ports-------------------------------------------------------------------------- | |||
int JackNetMaster::AllocPorts() | |||
{ | |||
uint i; | |||
int i; | |||
char name[24]; | |||
jack_nframes_t port_latency = jack_get_buffer_size ( fJackClient ); | |||
jack_latency_range_t range; | |||
@@ -251,7 +251,7 @@ namespace Jack | |||
ports = jack_get_ports(fJackClient, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); | |||
if (ports != NULL) { | |||
for (unsigned int i = 0; i < fParams.fSendAudioChannels && ports[i]; i++) { | |||
for (int i = 0; i < fParams.fSendAudioChannels && ports[i]; i++) { | |||
jack_connect(fJackClient, ports[i], jack_port_name(fAudioCapturePorts[i])); | |||
} | |||
free(ports); | |||
@@ -259,7 +259,7 @@ namespace Jack | |||
ports = jack_get_ports(fJackClient, NULL, NULL, JackPortIsPhysical | JackPortIsInput); | |||
if (ports != NULL) { | |||
for (unsigned int i = 0; i < fParams.fReturnAudioChannels && ports[i]; i++) { | |||
for (int i = 0; i < fParams.fReturnAudioChannels && ports[i]; i++) { | |||
jack_connect(fJackClient, jack_port_name(fAudioPlaybackPorts[i]), ports[i]); | |||
} | |||
free(ports); | |||
@@ -270,7 +270,7 @@ namespace Jack | |||
{ | |||
jack_log ( "JackNetMaster::FreePorts, ID %u", fParams.fID ); | |||
uint port_index; | |||
int port_index; | |||
for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) | |||
if ( fAudioCapturePorts[port_index] ) | |||
jack_port_unregister ( fJackClient, fAudioCapturePorts[port_index] ); | |||
@@ -396,7 +396,7 @@ namespace Jack | |||
{ | |||
JackNetMaster* obj = static_cast<JackNetMaster*>(arg); | |||
if (nframes != obj->fParams.fPeriodSize) { | |||
jack_error("Cannot handle bufer size change, so JackNetMaster proxy will be removed..."); | |||
jack_error("Cannot handle buffer size change, so JackNetMaster proxy will be removed..."); | |||
obj->Exit(); | |||
} | |||
return 0; | |||
@@ -413,7 +413,7 @@ namespace Jack | |||
if ( !fRunning ) | |||
return 0; | |||
uint port_index; | |||
int port_index; | |||
int res = 0; | |||
#ifdef JACK_MONITOR | |||
@@ -546,6 +546,19 @@ namespace Jack | |||
SocketAPIEnd(); | |||
} | |||
int JackNetMasterManager::CountIO(int flags) | |||
{ | |||
const char **ports; | |||
int count = 0; | |||
ports = jack_get_ports(fManagerClient, NULL, NULL, flags); | |||
if (ports != NULL) { | |||
while(ports[count]) count++; | |||
free(ports); | |||
} | |||
return count; | |||
} | |||
int JackNetMasterManager::SetSyncCallback ( jack_transport_state_t state, jack_position_t* pos, void* arg ) | |||
{ | |||
return static_cast<JackNetMasterManager*> ( arg )->SyncCallback ( state, pos ); | |||
@@ -670,13 +683,23 @@ namespace Jack | |||
params.fID = ++fGlobalID; | |||
params.fSampleRate = jack_get_sample_rate ( fManagerClient ); | |||
params.fPeriodSize = jack_get_buffer_size ( fManagerClient ); | |||
SetSlaveName ( params ); | |||
if (params.fSendAudioChannels == -1) { | |||
params.fSendAudioChannels = CountIO(JackPortIsPhysical | JackPortIsOutput); | |||
jack_info("Takes physical %d inputs for client", params.fSendAudioChannels); | |||
} | |||
if (params.fReturnAudioChannels == -1) { | |||
params.fReturnAudioChannels = CountIO(JackPortIsPhysical | JackPortIsInput); | |||
jack_info("Takes physical %d outputs for client", params.fReturnAudioChannels); | |||
} | |||
SetSlaveName (params); | |||
//create a new master and add it to the list | |||
JackNetMaster* master = new JackNetMaster(fSocket, params, fMulticastIP); | |||
if ( master->Init(fAutoConnect) ) | |||
{ | |||
fMasterList.push_back ( master ); | |||
if (master->Init(fAutoConnect)) { | |||
fMasterList.push_back(master); | |||
return master; | |||
} | |||
delete master; | |||
@@ -37,7 +37,9 @@ namespace Jack | |||
class JackNetMaster : public JackNetMasterInterface | |||
{ | |||
friend class JackNetMasterManager; | |||
private: | |||
static int SetProcess ( jack_nframes_t nframes, void* arg ); | |||
static int SetBufferSize (jack_nframes_t nframes, void* arg); | |||
static void SetTimebaseCallback ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void* arg ); | |||
@@ -74,6 +76,7 @@ namespace Jack | |||
void ConnectPorts(); | |||
public: | |||
JackNetMaster ( JackNetSocket& socket, session_params_t& params, const char* multicast_ip); | |||
~JackNetMaster (); | |||
@@ -90,7 +93,9 @@ namespace Jack | |||
class JackNetMasterManager | |||
{ | |||
friend class JackNetMaster; | |||
private: | |||
static int SetSyncCallback ( jack_transport_state_t state, jack_position_t* pos, void* arg ); | |||
static void* NetManagerThread ( void* arg ); | |||
@@ -111,7 +116,11 @@ namespace Jack | |||
void SetSlaveName ( session_params_t& params ); | |||
int SyncCallback ( jack_transport_state_t state, jack_position_t* pos ); | |||
int CountIO(int flags); | |||
public: | |||
JackNetMasterManager ( jack_client_t* jack_client, const JSList* params); | |||
~JackNetMasterManager(); | |||
}; | |||
@@ -27,72 +27,69 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
namespace Jack | |||
{ | |||
/** | |||
\Brief This class describes the Net Backend | |||
*/ | |||
class JackNetOneDriver : public JackAudioDriver | |||
{ | |||
private: | |||
netjack_driver_state_t netj; | |||
void | |||
render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); | |||
void | |||
render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats ); | |||
#ifdef HAVE_CELT | |||
void | |||
render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); | |||
void | |||
render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); | |||
#endif | |||
void | |||
render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); | |||
void | |||
render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats); | |||
public: | |||
JackNetOneDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | |||
int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports, | |||
int sample_rate, int period_size, int resample_factor, | |||
const char* net_name, uint transport_sync, int bitdepth, int use_autoconfig, | |||
int latency, int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val ); | |||
~JackNetOneDriver(); | |||
int Open ( jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing, | |||
int inchannels, int outchannels, bool monitor, const char* capture_driver_name, | |||
const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency ); | |||
int Close(); | |||
int Attach(); | |||
int Detach(); | |||
int Read(); | |||
int Write(); | |||
bool Initialize(); | |||
int AllocPorts(); | |||
void FreePorts(); | |||
// BufferSize can't be changed | |||
bool IsFixedBufferSize() | |||
{ | |||
return true; | |||
} | |||
int SetBufferSize ( jack_nframes_t buffer_size ) | |||
{ | |||
return -1; | |||
} | |||
int SetSampleRate ( jack_nframes_t sample_rate ) | |||
{ | |||
return -1; | |||
} | |||
}; | |||
/** | |||
\Brief This class describes the Net Backend | |||
*/ | |||
class JackNetOneDriver : public JackAudioDriver | |||
{ | |||
private: | |||
netjack_driver_state_t netj; | |||
void | |||
render_payload_to_jack_ports_float(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); | |||
void | |||
render_jack_ports_to_payload_float(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats ); | |||
#ifdef HAVE_CELT | |||
void | |||
render_payload_to_jack_ports_celt(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); | |||
void | |||
render_jack_ports_to_payload_celt(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); | |||
#endif | |||
void | |||
render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); | |||
void | |||
render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats); | |||
public: | |||
JackNetOneDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | |||
int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports, | |||
int sample_rate, int period_size, int resample_factor, | |||
const char* net_name, uint transport_sync, int bitdepth, int use_autoconfig, | |||
int latency, int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val); | |||
~JackNetOneDriver(); | |||
int Open(jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing, | |||
int inchannels, int outchannels, bool monitor, const char* capture_driver_name, | |||
const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); | |||
int Close(); | |||
int Attach(); | |||
int Detach(); | |||
int Read(); | |||
int Write(); | |||
bool Initialize(); | |||
int AllocPorts(); | |||
void FreePorts(); | |||
// BufferSize can't be changed | |||
bool IsFixedBufferSize() { | |||
return true; | |||
} | |||
int SetBufferSize(jack_nframes_t buffer_size) { | |||
return -1; | |||
} | |||
int SetSampleRate(jack_nframes_t sample_rate) { | |||
return -1; | |||
} | |||
}; | |||
} | |||
#endif |
@@ -374,11 +374,13 @@ namespace Jack | |||
void NetCeltAudioBuffer::SetBuffer(int index, sample_t* buffer) | |||
{ | |||
assert(fPortBuffer); | |||
fPortBuffer[index] = buffer; | |||
} | |||
sample_t* NetCeltAudioBuffer::GetBuffer(int index) | |||
{ | |||
assert(fPortBuffer); | |||
return fPortBuffer[index]; | |||
} | |||
@@ -88,10 +88,10 @@ namespace Jack | |||
uint32_t fMtu; //connection mtu | |||
uint32_t fID; //slave's ID | |||
uint32_t fTransportSync; //is the transport synced ? | |||
uint32_t fSendAudioChannels; //number of master->slave channels | |||
uint32_t fReturnAudioChannels; //number of slave->master channels | |||
uint32_t fSendMidiChannels; //number of master->slave midi channels | |||
uint32_t fReturnMidiChannels; //number of slave->master midi channels | |||
int32_t fSendAudioChannels; //number of master->slave channels | |||
int32_t fReturnAudioChannels; //number of slave->master channels | |||
int32_t fSendMidiChannels; //number of master->slave midi channels | |||
int32_t fReturnMidiChannels; //number of slave->master midi channels | |||
uint32_t fSampleRate; //session sample rate | |||
uint32_t fPeriodSize; //period size | |||
uint32_t fSampleEncoder; //samples encoder | |||
@@ -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 |
@@ -107,7 +107,7 @@ class SERVER_EXPORT JackPort | |||
// Since we are in shared memory, the resulting pointer cannot be cached, so align it here... | |||
jack_default_audio_sample_t* GetBuffer() | |||
{ | |||
return (jack_default_audio_sample_t*)((long)fBuffer & ~15L) + 4; | |||
return (jack_default_audio_sample_t*)((uintptr_t)fBuffer & ~15L) + 4; | |||
} | |||
int GetRefNum() const; | |||
@@ -58,7 +58,7 @@ JackServer::JackServer(bool sync, bool temporary, int timeout, bool rt, int prio | |||
new JackFreewheelDriver(fEngine, GetSynchroTable()); | |||
fThreadedFreewheelDriver = new JackThreadedDriver(freewheelDriver); | |||
fFreewheelDriver = freewheelDriver; | |||
fFreewheelDriver = freewheelDriver; | |||
fDriverInfo = new JackDriverInfo(); | |||
fAudioDriver = NULL; | |||
fFreewheel = false; | |||
@@ -223,13 +223,11 @@ int JackServer::SetBufferSize(jack_nframes_t buffer_size) | |||
} | |||
if (fAudioDriver->SetBufferSize(buffer_size) == 0) { | |||
fFreewheelDriver->SetBufferSize(buffer_size); | |||
fEngine->NotifyBufferSize(buffer_size); | |||
return fAudioDriver->Start(); | |||
} else { // Failure: try to restore current value | |||
jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size); | |||
fAudioDriver->SetBufferSize(current_buffer_size); | |||
fFreewheelDriver->SetBufferSize(current_buffer_size); | |||
fAudioDriver->Start(); | |||
// SetBufferSize actually failed, so return an error... | |||
return -1; | |||
@@ -0,0 +1,69 @@ | |||
/* | |||
Copyright (C) 2001 Paul Davis | |||
Copyright (C) 2004 Jack O'Quin | |||
Copyright (C) 2010 Torben Hohn | |||
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 __jack_session_int_h__ | |||
#define __jack_session_int_h__ | |||
#include <stdint.h> | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
enum JackSessionEventType { | |||
JackSessionSave = 1, | |||
JackSessionSaveAndQuit = 2, | |||
JackSessionSaveTemplate = 3 | |||
}; | |||
typedef enum JackSessionEventType jack_session_event_type_t; | |||
enum JackSessionFlags { | |||
JackSessionSaveError = 0x01, | |||
JackSessionNeedTerminal = 0x02 | |||
}; | |||
typedef enum JackSessionFlags jack_session_flags_t; | |||
struct _jack_session_event { | |||
jack_session_event_type_t type; | |||
const char *session_dir; | |||
const char *client_uuid; | |||
char *command_line; | |||
jack_session_flags_t flags; | |||
uint32_t future; | |||
}; | |||
typedef struct _jack_session_event jack_session_event_t; | |||
typedef void (*JackSessionCallback)(jack_session_event_t *event, | |||
void *arg); | |||
typedef struct { | |||
const char *uuid; | |||
const char *client_name; | |||
const char *command; | |||
jack_session_flags_t flags; | |||
} jack_session_command_t; | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -127,9 +127,24 @@ void JackThreadedDriver::RemoveSlave(JackDriverInterface* slave) | |||
fDriver->RemoveSlave(slave); | |||
} | |||
int JackThreadedDriver::ProcessSlaves() | |||
int JackThreadedDriver::ProcessReadSlaves() | |||
{ | |||
return fDriver->ProcessSlaves(); | |||
return fDriver->ProcessReadSlaves(); | |||
} | |||
int JackThreadedDriver::ProcessWriteSlaves() | |||
{ | |||
return fDriver->ProcessWriteSlaves(); | |||
} | |||
int JackThreadedDriver::ProcessRead() | |||
{ | |||
return fDriver->ProcessRead(); | |||
} | |||
int JackThreadedDriver::ProcessWrite() | |||
{ | |||
return fDriver->ProcessWrite(); | |||
} | |||
std::list<JackDriverInterface*> JackThreadedDriver::GetSlaves() | |||
@@ -184,7 +199,6 @@ int JackThreadedDriver::Stop() | |||
case JackThread::kIniting: | |||
if (fThread.Kill() < 0) { | |||
jack_error("Cannot kill thread"); | |||
return -1; | |||
} | |||
break; | |||
@@ -192,7 +206,6 @@ int JackThreadedDriver::Stop() | |||
case JackThread::kRunning: | |||
if (fThread.Stop() < 0) { | |||
jack_error("Cannot stop thread"); | |||
return -1; | |||
} | |||
break; | |||
@@ -89,10 +89,17 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi | |||
virtual void SetMaster(bool onoff); | |||
virtual bool GetMaster(); | |||
virtual void AddSlave(JackDriverInterface* slave); | |||
virtual void RemoveSlave(JackDriverInterface* slave); | |||
virtual std::list<JackDriverInterface*> GetSlaves(); | |||
virtual int ProcessSlaves(); | |||
virtual int ProcessReadSlaves(); | |||
virtual int ProcessWriteSlaves(); | |||
virtual int ProcessRead(); | |||
virtual int ProcessWrite(); | |||
virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); | |||
virtual JackClientControl* GetClientControl() const; | |||
@@ -22,15 +22,15 @@ | |||
#define __JackWaitThreadedDriver__ | |||
#include "JackThreadedDriver.h" | |||
#include "JackAudioDriver.h" | |||
#include "JackDriver.h" | |||
namespace Jack | |||
{ | |||
/*! | |||
\brief To be used as a wrapper of JackNetDriver. | |||
\brief To be used as a wrapper of JackNetDriver. | |||
The idea is to behave as the "dummy" driver, until the network connection is really started and processing starts. | |||
The idea is to behave as the "dummy" driver, until the network connection is really started and processing starts. | |||
The Execute method will call the ProcessNull method until the decorated driver Init method returns. | |||
A helper JackDriverStarter thread is used for that purpose. | |||
*/ | |||
@@ -38,54 +38,58 @@ A helper JackDriverStarter thread is used for that purpose. | |||
class SERVER_EXPORT JackWaitThreadedDriver : public JackThreadedDriver | |||
{ | |||
private: | |||
struct SERVER_EXPORT JackDriverStarter : public JackRunnableInterface | |||
struct SERVER_EXPORT JackDriverStarter : public JackRunnableInterface | |||
{ | |||
JackDriver* fDriver; | |||
JackThread fThread; | |||
bool fRunning; | |||
volatile bool fRunning; | |||
JackDriverStarter(JackDriver* driver) | |||
:fDriver(driver),fThread(this),fRunning(false) | |||
{} | |||
~JackDriverStarter() | |||
{ | |||
fThread.Kill(); | |||
} | |||
int Start() | |||
{ | |||
fRunning = false; | |||
return fThread.Start(); | |||
} | |||
// JackRunnableInterface interface | |||
bool Execute() | |||
{ | |||
// Blocks until decorated driver is started (that is when it's Initialize method returns true). | |||
while (!fDriver->Initialize()) {} | |||
fRunning = true; | |||
// Blocks until decorated driver is started (that is when it's Initialize method returns). | |||
if (fDriver->Initialize()) { | |||
fRunning = true; | |||
} else { | |||
jack_error("Initing net driver fails..."); | |||
} | |||
return false; | |||
} | |||
}; | |||
JackDriverStarter fStarter; | |||
public: | |||
JackWaitThreadedDriver(JackDriver* netdriver) | |||
:JackThreadedDriver(netdriver),fStarter(netdriver) | |||
JackWaitThreadedDriver(JackDriver* net_driver) | |||
:JackThreadedDriver(net_driver), fStarter(net_driver) | |||
{} | |||
virtual ~JackWaitThreadedDriver() | |||
{} | |||
// JackRunnableInterface interface | |||
bool Init(); | |||
bool Execute(); | |||
}; | |||
}; | |||
} // end of namespace | |||
@@ -235,7 +235,7 @@ DECL_FUNCTION(jack_time_t, jack_frames_to_time, (const jack_client_t *client, ja | |||
DECL_FUNCTION(jack_nframes_t, jack_frame_time, (const jack_client_t *client), (client)); | |||
DECL_FUNCTION(jack_nframes_t, jack_last_frame_time, (const jack_client_t *client), (client)); | |||
DECL_FUNCTION(float, jack_cpu_load, (jack_client_t *client), (client)); | |||
DECL_FUNCTION_NULL(pthread_t, jack_client_thread_id, (jack_client_t *client), (client)); | |||
DECL_FUNCTION_NULL(jack_native_thread_t, jack_client_thread_id, (jack_client_t *client), (client)); | |||
DECL_VOID_FUNCTION(jack_set_error_function, (print_function fun), (fun)); | |||
DECL_VOID_FUNCTION(jack_set_info_function, (print_function fun), (fun)); | |||
@@ -261,17 +261,17 @@ DECL_VOID_FUNCTION(jack_set_transport_info, (jack_client_t *client, jack_transpo | |||
DECL_FUNCTION(int, jack_client_real_time_priority, (jack_client_t* client), (client)); | |||
DECL_FUNCTION(int, jack_client_max_real_time_priority, (jack_client_t* client), (client)); | |||
DECL_FUNCTION(int, jack_acquire_real_time_scheduling, (pthread_t thread, int priority), (thread, priority)); | |||
DECL_FUNCTION(int, jack_acquire_real_time_scheduling, (jack_native_thread_t thread, int priority), (thread, priority)); | |||
DECL_FUNCTION(int, jack_client_create_thread, (jack_client_t* client, | |||
pthread_t *thread, | |||
jack_native_thread_t *thread, | |||
int priority, | |||
int realtime, // boolean | |||
thread_routine routine, | |||
void *arg), (client, thread, priority, realtime, routine, arg)); | |||
DECL_FUNCTION(int, jack_drop_real_time_scheduling, (pthread_t thread), (thread)); | |||
DECL_FUNCTION(int, jack_drop_real_time_scheduling, (jack_native_thread_t thread), (thread)); | |||
DECL_FUNCTION(int, jack_client_stop_thread, (jack_client_t* client, pthread_t thread), (client, thread)); | |||
DECL_FUNCTION(int, jack_client_kill_thread, (jack_client_t* client, pthread_t thread), (client, thread)); | |||
DECL_FUNCTION(int, jack_client_stop_thread, (jack_client_t* client, jack_native_thread_t thread), (client, thread)); | |||
DECL_FUNCTION(int, jack_client_kill_thread, (jack_client_t* client, jack_native_thread_t thread), (client, thread)); | |||
#ifndef WIN32 | |||
DECL_VOID_FUNCTION(jack_set_thread_creator, (jack_thread_creator_t jtc), (jtc)); | |||
#endif | |||
@@ -191,7 +191,7 @@ int main(int argc, char* argv[]) | |||
const JSList * server_parameters; | |||
const char* server_name = "default"; | |||
jackctl_driver_t * master_driver_ctl; | |||
jackctl_driver_t * loopback_driver_ctl; | |||
jackctl_driver_t * loopback_driver_ctl = NULL; | |||
int replace_registry = 0; | |||
const char *options = "-d:X:I:P:uvshVrRL:STFl:t:mn:p:" | |||
@@ -476,14 +476,16 @@ int main(int argc, char* argv[]) | |||
fprintf(stderr, "Unknown driver \"%s\"\n", *it); | |||
goto close_server; | |||
} | |||
jackctl_server_add_slave(server_ctl, slave_driver_ctl); | |||
if (!jackctl_server_add_slave(server_ctl, slave_driver_ctl)) { | |||
fprintf(stderr, "Driver \"%s\" cannot be loaded\n", *it); | |||
goto close_server; | |||
} | |||
} | |||
// Loopback driver | |||
if (loopback > 0) { | |||
loopback_driver_ctl = jackctl_server_get_driver(server_ctl, "loopback"); | |||
// XX: What if this fails? | |||
if (loopback_driver_ctl != NULL) { | |||
const JSList * loopback_parameters = jackctl_driver_get_parameters(loopback_driver_ctl); | |||
param = jackctl_get_parameter(loopback_parameters, "channels"); | |||
@@ -491,9 +493,14 @@ int main(int argc, char* argv[]) | |||
value.ui = loopback; | |||
jackctl_parameter_set_value(param, &value); | |||
} | |||
jackctl_server_add_slave(server_ctl, loopback_driver_ctl); | |||
if (!jackctl_server_add_slave(server_ctl, loopback_driver_ctl)) { | |||
fprintf(stderr, "Driver \"loopback\" cannot be loaded\n"); | |||
goto close_server; | |||
} | |||
} else { | |||
fprintf(stderr, "Driver \"loopback\" not found\n"); | |||
goto close_server; | |||
} | |||
} | |||
// Start the server | |||
@@ -509,7 +516,10 @@ int main(int argc, char* argv[]) | |||
fprintf(stderr, "Unknown internal \"%s\"\n", *it); | |||
goto stop_server; | |||
} | |||
jackctl_server_load_internal(server_ctl, internal_driver_ctl); | |||
if (!jackctl_server_load_internal(server_ctl, internal_driver_ctl)) { | |||
fprintf(stderr, "Internal client \"%s\" cannot be loaded\n", *it); | |||
goto stop_server; | |||
} | |||
} | |||
notify_server_start(server_name); | |||
@@ -523,12 +533,25 @@ int main(int argc, char* argv[]) | |||
if (! jackctl_server_stop(server_ctl)) { | |||
fprintf(stderr, "Cannot stop server...\n"); | |||
} | |||
if (notify_sent) { | |||
notify_server_stop(server_name); | |||
} | |||
close_server: | |||
if (loopback > 0 && loopback_driver_ctl) { | |||
jackctl_server_remove_slave(server_ctl, loopback_driver_ctl); | |||
} | |||
// Slave drivers | |||
for (it = slaves_list.begin(); it != slaves_list.end(); it++) { | |||
jackctl_driver_t * slave_driver_ctl = jackctl_server_get_driver(server_ctl, *it); | |||
jackctl_server_remove_slave(server_ctl, slave_driver_ctl); | |||
} | |||
// Internal clients | |||
for (it = internals_list.begin(); it != internals_list.end(); it++) { | |||
jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it); | |||
jackctl_server_unload_internal(server_ctl, internal_driver_ctl); | |||
} | |||
jackctl_server_close(server_ctl); | |||
destroy_server: | |||
jackctl_server_destroy(server_ctl); | |||
if (notify_sent) { | |||
notify_server_stop(server_name); | |||
} | |||
return return_value; | |||
} |
@@ -233,7 +233,7 @@ jack_nframes_t jack_thread_wait (jack_client_t*, int status) JACK_OPTIONAL_WEAK_ | |||
* | |||
* @return the number of frames of data to process | |||
*/ | |||
jack_nframes_t jack_cycle_wait (jack_client_t* client) JACK_OPTIONAL_WEAK_EXPORT; | |||
jack_nframes_t jack_cycle_wait (jack_client_t* client) JACK_OPTIONAL_WEAK_EXPORT; | |||
/** | |||
* Signal next clients in the graph. | |||
@@ -310,7 +310,7 @@ int jack_set_thread_init_callback (jack_client_t *client, | |||
* jack_on_info_shutdown() will. | |||
*/ | |||
void jack_on_shutdown (jack_client_t *client, | |||
JackShutdownCallback shutdown_callback, void *arg) JACK_WEAK_EXPORT; | |||
JackShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; | |||
/** | |||
* @param client pointer to JACK client structure. | |||
@@ -336,7 +336,7 @@ void jack_on_shutdown (jack_client_t *client, | |||
* jack_on_info_shutdown() will. | |||
*/ | |||
void jack_on_info_shutdown (jack_client_t *client, | |||
JackInfoShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; | |||
JackInfoShutdownCallback shutdown_callback, void *arg) JACK_WEAK_EXPORT; | |||
/** | |||
* Tell the Jack server to call @a process_callback whenever there is | |||
@@ -40,13 +40,23 @@ | |||
the symbol it used with. For this to work full may | |||
require linker arguments in the client as well. | |||
*/ | |||
#define JACK_WEAK_EXPORT __attribute__((weak)) | |||
#ifdef WIN32 | |||
/* | |||
Not working with __declspec(dllexport) so normal linking | |||
Linking with JackWeakAPI.cpp will be the preferred way. | |||
*/ | |||
#define JACK_WEAK_EXPORT | |||
#else | |||
/* Add other things here for non-gcc platforms */ | |||
#ifdef WIN32 | |||
#define JACK_WEAK_EXPORT | |||
#endif | |||
#define JACK_WEAK_EXPORT __attribute__((weak)) | |||
#endif | |||
#else | |||
/* Add other things here for non-gcc platforms */ | |||
#ifdef WIN32 | |||
#define JACK_WEAK_EXPORT | |||
#endif | |||
#endif | |||
#endif | |||
@@ -59,13 +69,14 @@ | |||
#ifdef __GNUC__ | |||
#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((__deprecated__)) | |||
#else | |||
/* Add other things here for non-gcc platforms */ | |||
#ifdef WIN32 | |||
#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT | |||
#endif | |||
/* Add other things here for non-gcc platforms */ | |||
#ifdef WIN32 | |||
#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT | |||
#endif | |||
#endif /* __GNUC__ */ | |||
#endif | |||
#endif /* __weakmacros_h__ */ | |||
@@ -55,12 +55,11 @@ $Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $ | |||
#include <samplerate.h> | |||
#endif | |||
#include "JackError.h" | |||
#include "netjack.h" | |||
#include "netjack_packet.h" | |||
// JACK2 | |||
#include "control.h" | |||
#define MIN(x,y) ((x)<(y) ? (x) : (y)) | |||
static int sync_state = 1; | |||
@@ -68,15 +68,14 @@ | |||
#include <samplerate.h> | |||
#endif | |||
#include "JackError.h" | |||
#if HAVE_CELT | |||
#include <celt/celt.h> | |||
#endif | |||
#include "netjack_packet.h" | |||
// JACK2 specific. | |||
#include "control.h" | |||
#ifdef NO_JACK_ERROR | |||
#define jack_error printf | |||
#endif | |||
@@ -130,8 +130,21 @@ def build(bld): | |||
'JackNetTool.cpp', | |||
'JackNetInterface.cpp', | |||
'JackArgParser.cpp', | |||
'JackPhysicalMidiInput.cpp', | |||
'JackPhysicalMidiOutput.cpp', | |||
#'JackPhysicalMidiInput.cpp', | |||
#'JackPhysicalMidiOutput.cpp', | |||
'JackMidiAsyncQueue.cpp', | |||
'JackMidiAsyncWaitQueue.cpp', | |||
'JackMidiBufferReadQueue.cpp', | |||
'JackMidiBufferWriteQueue.cpp', | |||
'JackMidiRawInputWriteQueue.cpp', | |||
'JackMidiRawOutputWriteQueue.cpp', | |||
'JackMidiReadQueue.cpp', | |||
'JackMidiReceiveQueue.cpp', | |||
'JackMidiSendQueue.cpp', | |||
'JackMidiUtil.cpp', | |||
'JackMidiWriteQueue.cpp' | |||
] | |||
if bld.env['IS_LINUX']: | |||
@@ -64,6 +64,56 @@ jack_controller_find_driver( | |||
return NULL; | |||
} | |||
bool | |||
jack_controller_add_slave_drivers( | |||
struct jack_controller * controller_ptr) | |||
{ | |||
struct list_head * node_ptr; | |||
struct jack_controller_slave_driver * driver_ptr; | |||
list_for_each(node_ptr, &controller_ptr->slave_drivers) | |||
{ | |||
driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | |||
driver_ptr->handle = jack_controller_find_driver(controller_ptr->server, driver_ptr->name); | |||
if (driver_ptr->handle == NULL) | |||
{ | |||
jack_error("Unknown driver \"%s\"", driver_ptr->name); | |||
goto fail; | |||
} | |||
if (!jackctl_server_add_slave(controller_ptr->server, driver_ptr->handle)) | |||
{ | |||
jack_error("Driver \"%s\" cannot be loaded", driver_ptr->name); | |||
goto fail; | |||
} | |||
} | |||
return true; | |||
fail: | |||
driver_ptr->handle = NULL; | |||
return false; | |||
} | |||
void | |||
jack_controller_remove_slave_drivers( | |||
struct jack_controller * controller_ptr) | |||
{ | |||
struct list_head * node_ptr; | |||
struct jack_controller_slave_driver * driver_ptr; | |||
list_for_each(node_ptr, &controller_ptr->slave_drivers) | |||
{ | |||
driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | |||
if (driver_ptr->handle != NULL) | |||
{ | |||
jackctl_server_remove_slave(controller_ptr->server, driver_ptr->handle); | |||
driver_ptr->handle = NULL; | |||
} | |||
} | |||
} | |||
jackctl_internal_t * | |||
jack_controller_find_internal( | |||
jackctl_server_t *server, | |||
@@ -161,6 +211,8 @@ jack_controller_start_server( | |||
goto fail; | |||
} | |||
jack_controller_add_slave_drivers(controller_ptr); | |||
if (!jackctl_server_start( | |||
controller_ptr->server)) | |||
{ | |||
@@ -221,6 +273,8 @@ fail_stop_server: | |||
} | |||
fail_close_server: | |||
jack_controller_remove_slave_drivers(controller_ptr); | |||
if (!jackctl_server_close(controller_ptr->server)) | |||
{ | |||
jack_error("failed to close jack server"); | |||
@@ -263,6 +317,8 @@ jack_controller_stop_server( | |||
return FALSE; | |||
} | |||
jack_controller_remove_slave_drivers(controller_ptr); | |||
if (!jackctl_server_close(controller_ptr->server)) | |||
{ | |||
jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to close server"); | |||
@@ -387,6 +443,7 @@ jack_controller_create( | |||
controller_ptr->started = false; | |||
controller_ptr->driver = NULL; | |||
controller_ptr->driver_set = false; | |||
INIT_LIST_HEAD(&controller_ptr->slave_drivers); | |||
drivers = (JSList *)jackctl_server_get_drivers_list(controller_ptr->server); | |||
controller_ptr->drivers_count = jack_slist_length(drivers); | |||
@@ -465,41 +522,58 @@ fail: | |||
} | |||
bool | |||
jack_controller_add_slave( | |||
struct jack_controller *controller_ptr, | |||
jack_controller_add_slave_driver( | |||
struct jack_controller * controller_ptr, | |||
const char * driver_name) | |||
{ | |||
jackctl_driver_t *driver; | |||
struct jack_controller_slave_driver * driver_ptr; | |||
driver = jack_controller_find_driver(controller_ptr->server, driver_name); | |||
if (driver == NULL) | |||
driver_ptr = malloc(sizeof(struct jack_controller_slave_driver)); | |||
if (driver_ptr == NULL) | |||
{ | |||
jack_error("malloc() failed to allocate jack_controller_slave_driver struct"); | |||
return false; | |||
} | |||
jack_info("driver \"%s\" selected", driver_name); | |||
driver_ptr->name = strdup(driver_name); | |||
if (driver_ptr->name == NULL) | |||
{ | |||
jack_error("strdup() failed for slave driver name \"%s\"", driver_name); | |||
free(driver_ptr); | |||
return false; | |||
} | |||
driver_ptr->handle = NULL; | |||
return jackctl_server_add_slave(controller_ptr->server, driver); | |||
jack_info("slave driver \"%s\" added", driver_name); | |||
list_add_tail(&driver_ptr->siblings, &controller_ptr->slave_drivers); | |||
return true; | |||
} | |||
bool | |||
jack_controller_remove_slave( | |||
struct jack_controller *controller_ptr, | |||
jack_controller_remove_slave_driver( | |||
struct jack_controller * controller_ptr, | |||
const char * driver_name) | |||
{ | |||
jackctl_driver_t *driver; | |||
struct list_head * node_ptr; | |||
struct jack_controller_slave_driver * driver_ptr; | |||
driver = jack_controller_find_driver(controller_ptr->server, driver_name); | |||
if (driver == NULL) | |||
list_for_each(node_ptr, &controller_ptr->slave_drivers) | |||
{ | |||
return false; | |||
driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); | |||
if (strcmp(driver_ptr->name, driver_name) == 0) | |||
{ | |||
jack_info("slave driver \"%s\" removed", driver_name); | |||
list_del(&driver_ptr->siblings); | |||
free(driver_ptr->name); | |||
free(driver_ptr); | |||
return true; | |||
} | |||
} | |||
jack_info("driver \"%s\" selected", driver_name); | |||
return jackctl_server_remove_slave(controller_ptr->server, driver); | |||
return false; | |||
} | |||
bool | |||
@@ -603,6 +603,73 @@ oom: | |||
jack_error ("Ran out of memory trying to construct method return"); | |||
} | |||
static | |||
void | |||
jack_controller_fill_parameter_info( | |||
jackctl_parameter_t * parameter, | |||
struct parameter_info * info_ptr) | |||
{ | |||
info_ptr->type = jackctl_parameter_get_type(parameter); | |||
info_ptr->name = jackctl_parameter_get_name(parameter); | |||
info_ptr->short_decr = jackctl_parameter_get_short_description(parameter); | |||
info_ptr->long_descr = jackctl_parameter_get_long_description(parameter); | |||
} | |||
static | |||
bool | |||
jack_controller_append_parameter_info_struct( | |||
DBusMessageIter * iter_ptr, | |||
struct parameter_info * info_ptr) | |||
{ | |||
DBusMessageIter struct_iter; | |||
unsigned char type; | |||
/* Open the struct. */ | |||
if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_STRUCT, NULL, &struct_iter)) | |||
{ | |||
goto fail; | |||
} | |||
/* Append parameter type. */ | |||
type = PARAM_TYPE_JACK_TO_DBUS(info_ptr->type); | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &type)) | |||
{ | |||
goto fail_close; | |||
} | |||
/* Append parameter name. */ | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->name)) | |||
{ | |||
goto fail_close; | |||
} | |||
/* Append parameter short description. */ | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->short_decr)) | |||
{ | |||
goto fail_close; | |||
} | |||
/* Append parameter long description. */ | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->long_descr)) | |||
{ | |||
goto fail_close; | |||
} | |||
/* Close the struct. */ | |||
if (!dbus_message_iter_close_container(iter_ptr, &struct_iter)) | |||
{ | |||
goto fail; | |||
} | |||
return true; | |||
fail_close: | |||
dbus_message_iter_close_container(iter_ptr, &struct_iter); | |||
fail: | |||
return false; | |||
} | |||
static | |||
void | |||
jack_controller_get_parameters_info( | |||
@@ -610,9 +677,8 @@ jack_controller_get_parameters_info( | |||
struct parameter_info * special_parameter_info_ptr, | |||
const JSList * parameters_list) | |||
{ | |||
DBusMessageIter iter, array_iter, struct_iter; | |||
unsigned char type; | |||
const char *str; | |||
DBusMessageIter iter, array_iter; | |||
struct parameter_info info; | |||
call->reply = dbus_message_new_method_return (call->message); | |||
if (!call->reply) | |||
@@ -630,39 +696,7 @@ jack_controller_get_parameters_info( | |||
if (special_parameter_info_ptr != NULL) | |||
{ | |||
/* Open the struct. */ | |||
if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
/* Append parameter type. */ | |||
type = PARAM_TYPE_JACK_TO_DBUS(special_parameter_info_ptr->type); | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BYTE, &type)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Append parameter name. */ | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &special_parameter_info_ptr->name)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Append parameter short description. */ | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &special_parameter_info_ptr->short_decr)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Append parameter long description. */ | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &special_parameter_info_ptr->long_descr)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Close the struct. */ | |||
if (!dbus_message_iter_close_container (&array_iter, &struct_iter)) | |||
if (!jack_controller_append_parameter_info_struct(&array_iter, special_parameter_info_ptr)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
@@ -671,42 +705,8 @@ jack_controller_get_parameters_info( | |||
/* Append parameter descriptions to the array. */ | |||
while (parameters_list != NULL) | |||
{ | |||
/* Open the struct. */ | |||
if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
/* Append parameter type. */ | |||
type = PARAM_TYPE_JACK_TO_DBUS(jackctl_parameter_get_type(parameters_list->data)); | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BYTE, &type)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Append parameter name. */ | |||
str = jackctl_parameter_get_name(parameters_list->data); | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &str)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Append parameter short description. */ | |||
str = jackctl_parameter_get_short_description(parameters_list->data); | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &str)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Append parameter long description. */ | |||
str = jackctl_parameter_get_long_description(parameters_list->data); | |||
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &str)) | |||
{ | |||
goto fail_close2_unref; | |||
} | |||
/* Close the struct. */ | |||
if (!dbus_message_iter_close_container (&array_iter, &struct_iter)) | |||
jack_controller_fill_parameter_info(parameters_list->data, &info); | |||
if (!jack_controller_append_parameter_info_struct(&array_iter, &info)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
@@ -722,9 +722,6 @@ jack_controller_get_parameters_info( | |||
return; | |||
fail_close2_unref: | |||
dbus_message_iter_close_container (&iter, &struct_iter); | |||
fail_close_unref: | |||
dbus_message_iter_close_container (&iter, &array_iter); | |||
@@ -851,8 +848,7 @@ jack_controller_get_parameter_info_ex( | |||
struct jack_dbus_method_call * call, | |||
struct parameter_info * info_ptr) | |||
{ | |||
DBusMessageIter iter, struct_iter; | |||
unsigned char type; | |||
DBusMessageIter iter; | |||
call->reply = dbus_message_new_method_return(call->message); | |||
if (!call->reply) | |||
@@ -862,48 +858,13 @@ jack_controller_get_parameter_info_ex( | |||
dbus_message_iter_init_append(call->reply, &iter); | |||
/* Open the struct. */ | |||
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) | |||
{ | |||
goto fail_unref; | |||
} | |||
/* Append parameter type. */ | |||
type = PARAM_TYPE_JACK_TO_DBUS(info_ptr->type); | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &type)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
/* Append parameter name. */ | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->name)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
/* Append parameter short description. */ | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->short_decr)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
/* Append parameter long description. */ | |||
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->long_descr)) | |||
{ | |||
goto fail_close_unref; | |||
} | |||
/* Close the struct. */ | |||
if (!dbus_message_iter_close_container(&iter, &struct_iter)) | |||
if (!jack_controller_append_parameter_info_struct(&iter, info_ptr)) | |||
{ | |||
goto fail_unref; | |||
} | |||
return; | |||
fail_close_unref: | |||
dbus_message_iter_close_container(&iter, &struct_iter); | |||
fail_unref: | |||
dbus_message_unref(call->reply); | |||
call->reply = NULL; | |||
@@ -920,11 +881,7 @@ jack_controller_get_parameter_info( | |||
{ | |||
struct parameter_info info; | |||
info.type = jackctl_parameter_get_type(parameter); | |||
info.name = jackctl_parameter_get_name(parameter); | |||
info.short_decr = jackctl_parameter_get_short_description(parameter); | |||
info.long_descr = jackctl_parameter_get_long_description(parameter); | |||
jack_controller_fill_parameter_info(parameter, &info); | |||
jack_controller_get_parameter_info_ex(call, &info); | |||
} | |||
@@ -229,10 +229,15 @@ jack_control_run_method( | |||
"jack_controller_load_internal failed for internal (%s)", internal_name); | |||
} | |||
} | |||
else if (strcmp (call->method_name, "AddSlave") == 0) | |||
else if (strcmp (call->method_name, "AddSlaveDriver") == 0) | |||
{ | |||
const char *driver_name; | |||
if (controller_ptr->started) | |||
{ | |||
goto fail_started; | |||
} | |||
if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &driver_name, DBUS_TYPE_INVALID)) | |||
{ | |||
/* The method call had invalid arguments meaning that | |||
@@ -241,17 +246,22 @@ jack_control_run_method( | |||
goto exit; | |||
} | |||
if (!jack_controller_add_slave(controller_ptr, driver_name)) { | |||
if (!jack_controller_add_slave_driver(controller_ptr, driver_name)) { | |||
jack_dbus_error( | |||
call, | |||
JACK_DBUS_ERROR_GENERIC, | |||
"jack_controller_add_slave failed for driver (%s)", driver_name); | |||
"jack_controller_add_slave_driver failed for driver (%s)", driver_name); | |||
} | |||
} | |||
else if (strcmp (call->method_name, "RemoveSlave") == 0) | |||
else if (strcmp (call->method_name, "RemoveSlaveDriver") == 0) | |||
{ | |||
const char *driver_name; | |||
if (controller_ptr->started) | |||
{ | |||
goto fail_started; | |||
} | |||
if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &driver_name, DBUS_TYPE_INVALID)) | |||
{ | |||
/* The method call had invalid arguments meaning that | |||
@@ -260,11 +270,11 @@ jack_control_run_method( | |||
goto exit; | |||
} | |||
if (!jack_controller_remove_slave(controller_ptr, driver_name)) { | |||
if (!jack_controller_remove_slave_driver(controller_ptr, driver_name)) { | |||
jack_dbus_error( | |||
call, | |||
JACK_DBUS_ERROR_GENERIC, | |||
"jack_controller_remove_slave failed for driver (%s)", driver_name); | |||
"jack_controller_remove_slave_driver failed for driver (%s)", driver_name); | |||
} | |||
} | |||
else if (strcmp (call->method_name, "UnloadInternal") == 0) | |||
@@ -293,8 +303,7 @@ jack_control_run_method( | |||
} | |||
jack_dbus_construct_method_return_single(call, type, arg); | |||
return true; | |||
goto exit; | |||
not_started: | |||
jack_dbus_only_error( | |||
@@ -302,6 +311,15 @@ not_started: | |||
JACK_DBUS_ERROR_SERVER_NOT_RUNNING, | |||
"Can't execute method '%s' with stopped JACK server", | |||
call->method_name); | |||
goto exit; | |||
fail_started: | |||
jack_dbus_only_error( | |||
call, | |||
JACK_DBUS_ERROR_SERVER_RUNNING, | |||
"Can't execute method '%s' with started JACK server", | |||
call->method_name); | |||
goto exit; | |||
exit: | |||
return true; | |||
@@ -361,12 +379,12 @@ JACK_DBUS_METHOD_ARGUMENTS_BEGIN(UnloadInternal) | |||
JACK_DBUS_METHOD_ARGUMENT("internal", "s", false) | |||
JACK_DBUS_METHOD_ARGUMENTS_END | |||
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(AddSlave) | |||
JACK_DBUS_METHOD_ARGUMENT("internal", "s", false) | |||
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(AddSlaveDriver) | |||
JACK_DBUS_METHOD_ARGUMENT("driver_name", "s", false) | |||
JACK_DBUS_METHOD_ARGUMENTS_END | |||
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(RemoveSlave) | |||
JACK_DBUS_METHOD_ARGUMENT("internal", "s", false) | |||
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(RemoveSlaveDriver) | |||
JACK_DBUS_METHOD_ARGUMENT("driver_name", "s", false) | |||
JACK_DBUS_METHOD_ARGUMENTS_END | |||
JACK_DBUS_METHODS_BEGIN | |||
@@ -384,8 +402,8 @@ JACK_DBUS_METHODS_BEGIN | |||
JACK_DBUS_METHOD_DESCRIBE(ResetXruns, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(LoadInternal, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(UnloadInternal, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(AddSlave, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(RemoveSlave, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(AddSlaveDriver, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(RemoveSlaveDriver, NULL) | |||
JACK_DBUS_METHODS_END | |||
JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(ServerStarted) | |||
@@ -26,6 +26,14 @@ | |||
#include "jack/control.h" | |||
#include "jack/jack.h" | |||
#include "jackdbus.h" | |||
#include "list.h" | |||
struct jack_controller_slave_driver | |||
{ | |||
struct list_head siblings; | |||
char * name; | |||
jackctl_driver_t * handle; | |||
}; | |||
struct jack_controller | |||
{ | |||
@@ -45,6 +53,7 @@ struct jack_controller | |||
jackctl_driver_t *driver; | |||
bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */ | |||
struct list_head slave_drivers; | |||
struct jack_dbus_object_descriptor dbus_descriptor; | |||
}; | |||
@@ -87,12 +96,12 @@ jack_controller_switch_master( | |||
void *dbus_call_context_ptr); | |||
bool | |||
jack_controller_add_slave( | |||
jack_controller_add_slave_driver( | |||
struct jack_controller *controller_ptr, | |||
const char * driver_name); | |||
bool | |||
jack_controller_remove_slave( | |||
jack_controller_remove_slave_driver( | |||
struct jack_controller *controller_ptr, | |||
const char * driver_name); | |||
@@ -46,6 +46,7 @@ jack_controller_settings_uninit(); | |||
#define JACK_DBUS_ERROR_UNKNOWN_METHOD "org.jackaudio.Error.UnknownMethod" | |||
#define JACK_DBUS_ERROR_SERVER_NOT_RUNNING "org.jackaudio.Error.ServerNotRunning" | |||
#define JACK_DBUS_ERROR_SERVER_RUNNING "org.jackaudio.Error.ServerRunning" | |||
#define JACK_DBUS_ERROR_UNKNOWN_DRIVER "org.jackaudio.Error.UnknownDriver" | |||
#define JACK_DBUS_ERROR_UNKNOWN_INTERNAL "org.jackaudio.Error.UnknownInternal" | |||
#define JACK_DBUS_ERROR_UNKNOWN_PARAMETER "org.jackaudio.Error.UnknownParameter" | |||
@@ -31,7 +31,7 @@ PROJECT_NAME = "Jack2" | |||
# This could be handy for archiving the generated documentation or | |||
# if some version control system is used. | |||
PROJECT_NUMBER = 1.9.7 | |||
PROJECT_NUMBER = 1.9.8 | |||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) | |||
# base path where the generated documentation will be put. | |||
@@ -116,6 +116,8 @@ def main(): | |||
print " dp - get parameters of currently selected driver" | |||
print " dpd <param> - get long description for driver parameter" | |||
print " dps <param> <value> - set driver parameter" | |||
print " asd <driver> - add slave driver" | |||
print " rsd <driver> - remove slave driver" | |||
print " il - get list of available internals" | |||
print " ip <name> - get parameters of given internal" | |||
print " ipd <name> <param> - get long description for internal parameter" | |||
@@ -305,6 +307,26 @@ def main(): | |||
name = sys.argv[index] | |||
index += 1 | |||
result = control_iface.UnloadInternal(name) | |||
elif arg == 'asd': | |||
print "--- add slave driver" | |||
if index >= len(sys.argv): | |||
print "add slave driver command requires driver name argument" | |||
sys.exit() | |||
name = sys.argv[index] | |||
index += 1 | |||
result = control_iface.AddSlaveDriver(name) | |||
elif arg == 'rsd': | |||
print "--- remove slave driver" | |||
if index >= len(sys.argv): | |||
print "remove slave driver command requires driver name argument" | |||
sys.exit() | |||
name = sys.argv[index] | |||
index += 1 | |||
result = control_iface.RemoveSlaveDriver(name) | |||
else: | |||
print "Unknown command '%s'" % arg | |||
except dbus.DBusException, e: | |||
@@ -1,4 +1,4 @@ | |||
/** @file simple_client.c | |||
/** @file latent_client.c | |||
* | |||
* @brief This simple client demonstrates the most basic features of JACK | |||
* as they would be used by many applications. | |||
@@ -8,7 +8,7 @@ | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <string.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
@@ -19,12 +19,12 @@ jack_client_t *client; | |||
jack_default_audio_sample_t *delay_line; | |||
jack_nframes_t delay_index; | |||
jack_nframes_t latency = 1024; | |||
#ifdef WIN32 | |||
#define jack_sleep(val) Sleep((val)) | |||
#else | |||
#define jack_sleep(val) usleep((val) * 1000) | |||
jack_nframes_t latency = 1024; | |||
#ifdef WIN32 | |||
#define jack_sleep(val) Sleep((val)) | |||
#else | |||
#define jack_sleep(val) usleep((val) * 1000) | |||
#endif | |||
/** | |||
@@ -213,3 +213,4 @@ main (int argc, char *argv[]) | |||
jack_client_close (client); | |||
exit (0); | |||
} | |||
@@ -0,0 +1,777 @@ | |||
/* | |||
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 semaphores and command-line argument handling. | |||
*/ | |||
#include <errno.h> | |||
#include <math.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#ifdef WIN32 | |||
#include <windows.h> | |||
#include <unistd.h> | |||
#else | |||
#include <semaphore.h> | |||
#endif | |||
#include <signal.h> | |||
#define ABS(x) (((x) >= 0) ? (x) : (-(x))) | |||
#ifdef WIN32 | |||
typedef HANDLE semaphore_t; | |||
#else | |||
typedef sem_t *semaphore_t; | |||
#endif | |||
const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer"; | |||
const char *ERROR_SHUTDOWN = "the JACK server has been shutdown"; | |||
const char *ERROR_TIMEOUT1 = "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_SHUTDOWN = "handle_shutdown"; | |||
const char *SOURCE_SIGNAL_SEMAPHORE = "signal_semaphore"; | |||
const char *SOURCE_WAIT_SEMAPHORE = "wait_semaphore"; | |||
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; | |||
semaphore_t init_semaphore; | |||
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; | |||
semaphore_t process_semaphore; | |||
int process_state; | |||
char *program_name; | |||
jack_port_t *remote_in_port; | |||
jack_port_t *remote_out_port; | |||
size_t samples; | |||
int timeout; | |||
jack_nframes_t total_latency; | |||
jack_time_t total_latency_time; | |||
size_t unexpected_messages; | |||
size_t xrun_count; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
#ifdef WIN32 | |||
char semaphore_error_msg[1024]; | |||
#endif | |||
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 int | |||
signal_semaphore(semaphore_t semaphore); | |||
static int | |||
wait_semaphore(semaphore_t semaphore, int block); | |||
static semaphore_t | |||
create_semaphore(int id) | |||
{ | |||
semaphore_t semaphore; | |||
#ifdef WIN32 | |||
semaphore = CreateSemaphore(NULL, 0, 1, NULL); | |||
#elif defined (__APPLE__) | |||
char name[128]; | |||
sprintf(name, "midi_sem_%d", id); | |||
semaphore = sem_open(name, O_CREAT, 0777, 0); | |||
if (semaphore == (sem_t *) SEM_FAILED) { | |||
semaphore = NULL; | |||
} | |||
#else | |||
semaphore = malloc(sizeof(semaphore_t)); | |||
if (semaphore != NULL) { | |||
if (sem_init(semaphore, 0, 0)) { | |||
free(semaphore); | |||
semaphore = NULL; | |||
} | |||
} | |||
#endif | |||
return semaphore; | |||
} | |||
static void | |||
destroy_semaphore(semaphore_t semaphore, int id) | |||
{ | |||
#ifdef WIN32 | |||
CloseHandle(semaphore); | |||
#else | |||
sem_destroy(semaphore); | |||
#ifdef __APPLE__ | |||
{ | |||
char name[128]; | |||
sprintf(name, "midi_sem_%d", id); | |||
sem_unlink(name); | |||
} | |||
#else | |||
free(semaphore); | |||
#endif | |||
#endif | |||
} | |||
static void | |||
die(const char *source, const char *error_message) | |||
{ | |||
output_error(source, error_message); | |||
output_usage(); | |||
exit(EXIT_FAILURE); | |||
} | |||
static const char * | |||
get_semaphore_error() | |||
{ | |||
#ifdef WIN32 | |||
DWORD error = GetLastError(); | |||
if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, | |||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |||
semaphore_error_msg, 1024, NULL)) { | |||
snprintf(semaphore_error_msg, 1023, "Unknown OS error code '%d'", | |||
error); | |||
} | |||
return semaphore_error_msg; | |||
#else | |||
return strerror(errno); | |||
#endif | |||
} | |||
static void | |||
handle_info(const char *message) | |||
{ | |||
/* Suppress info */ | |||
} | |||
static int | |||
handle_process(jack_nframes_t frames, void *arg) | |||
{ | |||
jack_midi_data_t *buffer; | |||
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; | |||
jack_time_t microseconds; | |||
void *port_buffer; | |||
jack_time_t time; | |||
jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames)); | |||
switch (process_state) { | |||
case 0: | |||
/* State: initializing */ | |||
switch (wait_semaphore(init_semaphore, 0)) { | |||
case -1: | |||
set_process_error(SOURCE_WAIT_SEMAPHORE, get_semaphore_error()); | |||
// Fallthrough on purpose | |||
case 0: | |||
return 0; | |||
} | |||
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 */ | |||
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++; | |||
} | |||
microseconds = jack_frames_to_time(client, last_frame_time) - | |||
last_activity_time; | |||
if ((microseconds / 1000000) >= timeout) { | |||
set_process_error(SOURCE_PROCESS, ERROR_TIMEOUT1); | |||
} | |||
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; | |||
if (! signal_semaphore(process_semaphore)) { | |||
// Sigh ... | |||
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | |||
} | |||
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); | |||
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(SOURCE_SHUTDOWN, ERROR_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; | |||
if (! signal_semaphore(process_semaphore)) { | |||
// Sigh | |||
output_error(source, message); | |||
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | |||
} | |||
} | |||
static int | |||
signal_semaphore(semaphore_t semaphore) | |||
{ | |||
#ifdef WIN32 | |||
return ReleaseSemaphore(semaphore, 1, NULL); | |||
#else | |||
return ! sem_post(semaphore); | |||
#endif | |||
} | |||
static int | |||
wait_semaphore(semaphore_t semaphore, int block) | |||
{ | |||
#ifdef WIN32 | |||
DWORD result = WaitForSingleObject(semaphore, block ? INFINITE : 0); | |||
switch (result) { | |||
case WAIT_OBJECT_0: | |||
return 1; | |||
case WAIT_TIMEOUT: | |||
return 0; | |||
} | |||
return -1; | |||
#else | |||
if (block) { | |||
while (sem_wait(semaphore)) { | |||
if (errno != EINTR) { | |||
return -1; | |||
} | |||
} | |||
} else { | |||
while (sem_trywait(semaphore)) { | |||
switch (errno) { | |||
case EAGAIN: | |||
return 0; | |||
case EINTR: | |||
continue; | |||
default: | |||
return -1; | |||
} | |||
} | |||
} | |||
return 1; | |||
#endif | |||
} | |||
int | |||
main(int argc, char **argv) | |||
{ | |||
size_t jitter_plot[101]; | |||
size_t latency_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; | |||
} | |||
/* install a signal handler to properly quits jack client */ | |||
#ifdef WIN32 | |||
signal(SIGINT, signal_handler); | |||
signal(SIGABRT, signal_handler); | |||
signal(SIGTERM, signal_handler); | |||
#else | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGTERM, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
signal(SIGINT, signal_handler); | |||
#endif | |||
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; | |||
init_semaphore = create_semaphore(0); | |||
if (init_semaphore == NULL) { | |||
error_message = get_semaphore_error(); | |||
error_source = "create_semaphore"; | |||
goto unregister_out_port; | |||
} | |||
process_semaphore = create_semaphore(1); | |||
if (process_semaphore == NULL) { | |||
error_message = get_semaphore_error(); | |||
error_source = "create_semaphore"; | |||
goto destroy_init_semaphore; | |||
} | |||
if (jack_activate(client)) { | |||
error_message = "could not activate client"; | |||
error_source = "jack_activate"; | |||
goto destroy_process_semaphore; | |||
} | |||
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; | |||
} | |||
if (! signal_semaphore(init_semaphore)) { | |||
error_message = get_semaphore_error(); | |||
error_source = "post_semaphore"; | |||
goto deactivate_client; | |||
} | |||
if (wait_semaphore(process_semaphore, 1) == -1) { | |||
error_message = get_semaphore_error(); | |||
error_source = "wait_semaphore"; | |||
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 latency_plot_offset = | |||
floor(((double) lowest_latency_time) / 100.0) / 10.0; | |||
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; | |||
latency_plot[i] = 0; | |||
} | |||
for (i = 0; i < samples; i++) { | |||
double latency_time_value = (double) latency_time_values[i]; | |||
double latency_plot_time = | |||
(latency_time_value / 1000.0) - latency_plot_offset; | |||
double jitter_time = ABS(average_latency_time - | |||
latency_time_value); | |||
if (latency_plot_time >= 10.0) { | |||
(latency_plot[100])++; | |||
} else { | |||
(latency_plot[(int) (latency_plot_time * 10.0)])++; | |||
} | |||
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" | |||
"Lowest latency: %.2f ms (%u frames)\n" | |||
"Highest 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("\nLatency Plot:\n"); | |||
for (i = 0; i < 100; i++) { | |||
if (latency_plot[i]) { | |||
printf("%.1f - %.1f ms: %u\n", | |||
latency_plot_offset + (((float) i) / 10.0), | |||
latency_plot_offset + (((float) (i + 1)) / 10.0), | |||
latency_plot[i]); | |||
} | |||
} | |||
if (latency_plot[100]) { | |||
printf(" > %.1f ms: %u\n", latency_plot_offset + 10.0, | |||
latency_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_process_semaphore: | |||
destroy_semaphore(process_semaphore, 1); | |||
destroy_init_semaphore: | |||
destroy_semaphore(init_semaphore, 0); | |||
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; | |||
} |
@@ -35,7 +35,7 @@ showtime () | |||
transport_state = jack_transport_query (client, ¤t); | |||
frame_time = jack_frame_time (client); | |||
printf ("frame = %u frame_time = %u usecs = %lld \t", current.frame, frame_time, current.usecs); | |||
printf ("frame = %u frame_time = %u usecs = %ld \t", current.frame, frame_time, current.usecs); | |||
switch (transport_state) { | |||
case JackTransportStopped: | |||
@@ -1,4 +1,4 @@ | |||
/** @file simple_client.c | |||
/** @file thru_client.c | |||
* | |||
* @brief This simple through client demonstrates the basic features of JACK | |||
* as they would be used by many applications. | |||
@@ -1,4 +1,4 @@ | |||
/** @file simple_client.c | |||
/** @file tw.c | |||
* | |||
* @brief This simple client demonstrates the basic features of JACK | |||
* as they would be used by many applications. | |||
@@ -42,56 +42,56 @@ _process (jack_nframes_t nframes) | |||
{ | |||
jack_default_audio_sample_t *in, *out; | |||
jack_transport_state_t ts = jack_transport_query(client, NULL); | |||
if (ts == JackTransportRolling) { | |||
if (client_state == Init) | |||
client_state = Run; | |||
in = jack_port_get_buffer (input_port, nframes); | |||
out = jack_port_get_buffer (output_port, nframes); | |||
memcpy (out, in, | |||
sizeof (jack_default_audio_sample_t) * nframes); | |||
} else if (ts == JackTransportStopped) { | |||
if (client_state == Run) { | |||
client_state = Exit; | |||
return -1; // to stop the thread | |||
} | |||
} | |||
return 0; | |||
return 0; | |||
} | |||
static void* jack_thread(void *arg) | |||
static void* jack_thread(void *arg) | |||
{ | |||
jack_client_t* client = (jack_client_t*) arg; | |||
while (1) { | |||
jack_nframes_t frames = jack_cycle_wait (client); | |||
int status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
/* | |||
Possibly do something else after signaling next clients in the graph | |||
*/ | |||
/* End condition */ | |||
if (status != 0) | |||
return 0; | |||
return 0; | |||
} | |||
/* not reached*/ | |||
return 0; | |||
} | |||
/* | |||
static void* jack_thread(void *arg) | |||
static void* jack_thread(void *arg) | |||
{ | |||
jack_client_t* client = (jack_client_t*) arg; | |||
while (1) { | |||
jack_nframes_t frames; | |||
int status; | |||
@@ -103,7 +103,7 @@ static void* jack_thread(void *arg) | |||
frames = jack_cycle_wait (client); | |||
status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
// cycle 3 | |||
// cycle 3 | |||
frames = jack_cycle_wait (client); | |||
status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
@@ -112,7 +112,7 @@ static void* jack_thread(void *arg) | |||
status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
} | |||
return 0; | |||
} | |||
*/ | |||
@@ -172,8 +172,8 @@ main (int argc, char *argv[]) | |||
/* tell the JACK server to call `process()' whenever | |||
there is work to be done. | |||
*/ | |||
if (jack_set_process_thread(client, jack_thread, client) < 0) | |||
*/ | |||
if (jack_set_process_thread(client, jack_thread, client) < 0) | |||
exit(1); | |||
/* tell the JACK server to call `jack_shutdown()' if | |||
@@ -183,7 +183,7 @@ main (int argc, char *argv[]) | |||
jack_on_shutdown (client, jack_shutdown, 0); | |||
/* display the current sample rate. | |||
/* display the current sample rate. | |||
*/ | |||
printf ("engine sample rate: %" PRIu32 "\n", | |||
@@ -231,7 +231,7 @@ main (int argc, char *argv[]) | |||
} | |||
free (ports); | |||
ports = jack_get_ports (client, NULL, NULL, | |||
JackPortIsPhysical|JackPortIsInput); | |||
if (ports == NULL) { | |||
@@ -244,7 +244,7 @@ main (int argc, char *argv[]) | |||
} | |||
free (ports); | |||
/* install a signal handler to properly quits jack client */ | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGTERM, signal_handler); | |||
@@ -40,7 +40,7 @@ main (int argc, char *argv[]) | |||
int wait_timeout = 0; | |||
time_t start_timestamp; | |||
struct option long_options[] = { | |||
{ "server", 1, 0, 's' }, | |||
{ "wait", 0, 0, 'w' }, | |||
@@ -29,6 +29,7 @@ example_programs = { | |||
'jack_net_master' : 'netmaster.c', | |||
'jack_latent_client' : 'latent_client.c', | |||
'jack_midi_dump' : 'midi_dump.c', | |||
'jack_midi_latency_test' : 'midi_latency_test.c' | |||
} | |||
example_libs = { | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
Copyright (C) 2002 Jeremy Hall | |||
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 | |||
@@ -30,18 +30,18 @@ int count = 0; | |||
jack_port_t* output_port; | |||
static int | |||
process(jack_nframes_t nframes, void* arg) | |||
process(jack_nframes_t nframes, void* arg) | |||
{ | |||
if (count++ == 1000) { | |||
printf("process block\n"); | |||
//while (1) {} | |||
sleep(1); | |||
} | |||
return 0; | |||
} | |||
static void | |||
static void | |||
shutdown (void *arg) | |||
{ | |||
printf("shutdown \n"); | |||
@@ -57,7 +57,7 @@ main (int argc, char *argv[]) | |||
fprintf (stderr, "jack server not running?\n"); | |||
goto error; | |||
} | |||
jack_set_process_callback (client, process, NULL); | |||
jack_on_shutdown(client, shutdown, NULL); | |||
output_port = jack_port_register (client, "port1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||
@@ -67,9 +67,9 @@ main (int argc, char *argv[]) | |||
fprintf (stderr, "cannot activate client"); | |||
goto error; | |||
} | |||
jack_connect(client, jack_port_name(output_port), "coreaudio:Built-in Audio:in2"); | |||
while (running) { | |||
sleep(1); | |||
printf ("run\n"); | |||
@@ -78,9 +78,9 @@ main (int argc, char *argv[]) | |||
jack_deactivate (client); | |||
jack_client_close (client); | |||
return 0; | |||
error: | |||
if (client) | |||
if (client) | |||
jack_client_close (client); | |||
return 1; | |||
} | |||
@@ -259,7 +259,7 @@ extern "C" | |||
i++; | |||
strcpy ( desc->params[i].name, "inchannels" ); | |||
desc->params[i].character = 'i'; | |||
desc->params[i].type = JackDriverParamUInt; | |||
desc->params[i].type = JackDriverParamInt; | |||
desc->params[i].value.i = 0; | |||
strcpy ( desc->params[i].short_desc, | |||
"Number of capture channels (defaults to hardware max)" ); | |||
@@ -268,7 +268,7 @@ extern "C" | |||
i++; | |||
strcpy ( desc->params[i].name, "outchannels" ); | |||
desc->params[i].character = 'o'; | |||
desc->params[i].type = JackDriverParamUInt; | |||
desc->params[i].type = JackDriverParamInt; | |||
desc->params[i].value.i = 0; | |||
strcpy ( desc->params[i].short_desc, | |||
"Number of playback channels (defaults to hardware max)" ); | |||
@@ -277,7 +277,7 @@ extern "C" | |||
i++; | |||
strcpy(desc->params[i].name, "quality"); | |||
desc->params[i].character = 'q'; | |||
desc->params[i].type = JackDriverParamInt; | |||
desc->params[i].type = JackDriverParamUInt; | |||
desc->params[i].value.ui = 0; | |||
strcpy(desc->params[i].short_desc, "Resample algorithm quality (0 - 4)"); | |||
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); | |||
@@ -285,7 +285,7 @@ extern "C" | |||
i++; | |||
strcpy(desc->params[i].name, "ring-buffer"); | |||
desc->params[i].character = 'g'; | |||
desc->params[i].type = JackDriverParamInt; | |||
desc->params[i].type = JackDriverParamUInt; | |||
desc->params[i].value.ui = 32768; | |||
strcpy(desc->params[i].short_desc, "Fixed ringbuffer size"); | |||
strcpy(desc->params[i].long_desc, "Fixed ringbuffer size (if not set => automatic adaptative)"); | |||
@@ -55,8 +55,11 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) | |||
((alsa_driver_t *)fDriver)->frame_rate); | |||
if (res == 0) { // update fEngineControl and fGraphManager | |||
JackAudioDriver::SetBufferSize(buffer_size); // never fails | |||
JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails | |||
// ALSA specific | |||
UpdateLatencies(); | |||
} else { | |||
// Restore old values | |||
alsa_driver_reset_parameters((alsa_driver_t *)fDriver, fEngineControl->fBufferSize, | |||
((alsa_driver_t *)fDriver)->user_nperiods, | |||
((alsa_driver_t *)fDriver)->frame_rate); | |||
@@ -65,6 +68,29 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size) | |||
return res; | |||
} | |||
void JackAlsaDriver::UpdateLatencies() | |||
{ | |||
jack_latency_range_t range; | |||
alsa_driver_t* alsa_driver = (alsa_driver_t*)fDriver; | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
range.min = range.max = alsa_driver->frames_per_cycle + alsa_driver->capture_frame_latency; | |||
fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
// Add one buffer more latency if "async" mode is used... | |||
range.min = range.max = (alsa_driver->frames_per_cycle * (alsa_driver->user_nperiods - 1)) + | |||
((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + alsa_driver->playback_frame_latency; | |||
fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); | |||
// Monitor port | |||
if (fWithMonitorPorts) { | |||
range.min = range.max = alsa_driver->frames_per_cycle; | |||
fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &range); | |||
} | |||
} | |||
} | |||
int JackAlsaDriver::Attach() | |||
{ | |||
JackPort* port; | |||
@@ -72,7 +98,6 @@ int JackAlsaDriver::Attach() | |||
unsigned long port_flags = (unsigned long)CaptureDriverFlags; | |||
char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | |||
char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; | |||
jack_latency_range_t range; | |||
assert(fCaptureChannels < DRIVER_PORT_NUM); | |||
assert(fPlaybackChannels < DRIVER_PORT_NUM); | |||
@@ -97,8 +122,6 @@ int JackAlsaDriver::Attach() | |||
} | |||
port = fGraphManager->GetPort(port_index); | |||
port->SetAlias(alias); | |||
range.min = range.max = alsa_driver->frames_per_cycle + alsa_driver->capture_frame_latency; | |||
port->SetLatencyRange(JackCaptureLatency, &range); | |||
fCapturePortList[i] = port_index; | |||
jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_index); | |||
} | |||
@@ -114,11 +137,6 @@ int JackAlsaDriver::Attach() | |||
} | |||
port = fGraphManager->GetPort(port_index); | |||
port->SetAlias(alias); | |||
// Add one buffer more latency if "async" mode is used... | |||
range.min = range.max = (alsa_driver->frames_per_cycle * (alsa_driver->user_nperiods - 1)) + | |||
((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + alsa_driver->playback_frame_latency; | |||
port->SetLatencyRange(JackPlaybackLatency, &range); | |||
fPlaybackPortList[i] = port_index; | |||
jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", port_index); | |||
@@ -129,14 +147,13 @@ int JackAlsaDriver::Attach() | |||
if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { | |||
jack_error ("ALSA: cannot register monitor port for %s", name); | |||
} else { | |||
port = fGraphManager->GetPort(port_index); | |||
range.min = range.max = alsa_driver->frames_per_cycle; | |||
port->SetLatencyRange(JackCaptureLatency, &range); | |||
fMonitorPortList[i] = port_index; | |||
} | |||
} | |||
} | |||
UpdateLatencies(); | |||
if (alsa_driver->midi) { | |||
int err = (alsa_driver->midi->attach)(alsa_driver->midi); | |||
if (err) | |||
@@ -275,7 +292,7 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, | |||
} | |||
} | |||
fDriver = alsa_driver_new ("alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name, | |||
fDriver = alsa_driver_new ((char*)"alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name, | |||
NULL, | |||
nframes, | |||
user_nperiods, | |||
@@ -828,7 +845,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () | |||
i++; | |||
strcpy (params[i].name, "inchannels"); | |||
params[i].character = 'i'; | |||
params[i].type = JackDriverParamUInt; | |||
params[i].type = JackDriverParamInt; | |||
params[i].value.i = 0; | |||
strcpy (params[i].short_desc, | |||
"Number of capture channels (defaults to hardware max)"); | |||
@@ -837,7 +854,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () | |||
i++; | |||
strcpy (params[i].name, "outchannels"); | |||
params[i].character = 'o'; | |||
params[i].type = JackDriverParamUInt; | |||
params[i].type = JackDriverParamInt; | |||
params[i].value.i = 0; | |||
strcpy (params[i].short_desc, | |||
"Number of playback channels (defaults to hardware max)"); | |||
@@ -855,7 +872,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () | |||
strcpy (params[i].name, "input-latency"); | |||
params[i].character = 'I'; | |||
params[i].type = JackDriverParamUInt; | |||
params[i].value.i = 0; | |||
params[i].value.ui = 0; | |||
strcpy (params[i].short_desc, "Extra input latency (frames)"); | |||
strcpy (params[i].long_desc, params[i].short_desc); | |||
@@ -863,7 +880,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () | |||
strcpy (params[i].name, "output-latency"); | |||
params[i].character = 'O'; | |||
params[i].type = JackDriverParamUInt; | |||
params[i].value.i = 0; | |||
params[i].value.ui = 0; | |||
strcpy (params[i].short_desc, "Extra output latency (frames)"); | |||
strcpy (params[i].long_desc, params[i].short_desc); | |||
@@ -42,6 +42,8 @@ class JackAlsaDriver : public JackAudioDriver | |||
int fReservedCaptureDevice; | |||
int fReservedPlaybackDevice; | |||
void UpdateLatencies(); | |||
public: | |||
JackAlsaDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) | |||
@@ -204,8 +204,8 @@ alsa_driver_ice1712_hardware (alsa_driver_t *driver) | |||
static int | |||
alsa_driver_usx2y_hardware (alsa_driver_t *driver) | |||
{ | |||
driver->hw = jack_alsa_usx2y_hw_new (driver); | |||
return 0; | |||
driver->hw = jack_alsa_usx2y_hw_new (driver); | |||
return 0; | |||
} | |||
*/ | |||
@@ -0,0 +1,614 @@ | |||
/* | |||
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 <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() | |||
{ | |||
delete thread; | |||
} | |||
int | |||
JackALSARawMidiDriver::Attach() | |||
{ | |||
const char *alias; | |||
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; | |||
} | |||
alias = input_port->GetAlias(); | |||
port = fGraphManager->GetPort(index); | |||
port->SetAlias(alias); | |||
port->SetLatencyRange(JackCaptureLatency, &latency_range); | |||
fCapturePortList[i] = index; | |||
jack_info("JackALSARawMidiDriver::Attach - input port registered " | |||
"(name='%s', alias='%s').", name, alias); | |||
} | |||
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; | |||
} | |||
alias = output_port->GetAlias(); | |||
port = fGraphManager->GetPort(index); | |||
port->SetAlias(alias); | |||
port->SetLatencyRange(JackPlaybackLatency, &latency_range); | |||
fPlaybackPortList[i] = index; | |||
jack_info("JackALSARawMidiDriver::Attach - output port registered " | |||
"(name='%s', alias='%s').", name, alias); | |||
} | |||
return 0; | |||
} | |||
int | |||
JackALSARawMidiDriver::Close() | |||
{ | |||
// Generic MIDI driver close | |||
int result = JackMidiDriver::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 result; | |||
} | |||
bool | |||
JackALSARawMidiDriver::Execute() | |||
{ | |||
jack_nframes_t timeout_frame = 0; | |||
for (;;) { | |||
jack_nframes_t process_frame; | |||
unsigned short revents; | |||
jack_nframes_t *timeout_frame_ptr; | |||
if (! timeout_frame) { | |||
timeout_frame_ptr = 0; | |||
} else { | |||
timeout_frame_ptr = &timeout_frame; | |||
} | |||
if (Poll(timeout_frame_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; | |||
} | |||
int | |||
JackALSARawMidiDriver::Poll(const jack_nframes_t *wakeup_frame) | |||
{ | |||
struct timespec timeout; | |||
struct timespec *timeout_ptr; | |||
if (! wakeup_frame) { | |||
timeout_ptr = 0; | |||
} else { | |||
timeout_ptr = &timeout; | |||
jack_time_t next_time = GetTimeFromFrames(*wakeup_frame); | |||
jack_time_t now = GetMicroSeconds(); | |||
if (next_time <= now) { | |||
timeout.tv_sec = 0; | |||
timeout.tv_nsec = 0; | |||
} else { | |||
jack_time_t wait_time = next_time - now; | |||
timeout.tv_sec = wait_time / 1000000; | |||
timeout.tv_nsec = (wait_time % 1000000) * 1000; | |||
} | |||
} | |||
return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0); | |||
} | |||
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,98 @@ | |||
/* | |||
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 __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_nframes_t *wakeup_frame); | |||
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,143 @@ | |||
/* | |||
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 <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,62 @@ | |||
/* | |||
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 __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,172 @@ | |||
/* | |||
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 <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; | |||
} |