diff --git a/ChangeLog b/ChangeLog index e40d91ab..15c83a4e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,39 +1,254 @@ ---------------------------- - Contributors ---------------------------- - -Dmitry Baikov -Gabriel M. Beddingfield -Steven Chamberlain -Thom Johansen -Thibault LeMeur -Tom Szilagyi -Andrzej Szombierski -Kjetil S.Matheussen -Pieter Palmers -Tim Blechmann -Marc-Olivier Barre -Nedko Arnaudov -Fernando Lopez-Lezcano -Romain Moret -Florian Faber -Michael Voigt -Torben Hohn -Paul Davis -Peter L Jones -Devin Anderson -Josh Green -Mario Lang +--------------------------- + Contributors +--------------------------- + +Dmitry Baikov +Gabriel M. Beddingfield +Steven Chamberlain +Thom Johansen +Thibault LeMeur +Tom Szilagyi +Andrzej Szombierski +Kjetil S.Matheussen +Pieter Palmers +Tim Blechmann +Marc-Olivier Barre +Nedko Arnaudov +Fernando Lopez-Lezcano +Romain Moret +Florian Faber +Michael Voigt +Torben Hohn +Paul Davis +Peter L Jones +Devin Anderson +Josh Green +Mario Lang Arnold Krille Jan Engelhardt Adrian Knoth David Garcia Garzon Valerio Pilo +Chris Caudle +John Emmas --------------------------- Jackdmp changes log --------------------------- +2011-11-25 Stephane Letz + + * More robust dynamic port management in JACK/CoreMidi bridge. + * Correct jack_port_name_size API. + +2011-11-24 Stephane Letz + + * Dynamic port management in JACK/CoreMidi bridge. + * Correct jack_client_create_thread (when realtime in on). + +2011-11-21 Stephane Letz + + * John Emmas third auto-launch server on Windows patch. + +2011-11-07 Stephane Letz + + * John Emmas first auto-launch server on Windows patch. + * John Emmas second auto-launch server on Windows patch. + +2011-11-06 Stephane Letz + + * Enable local access in NetJack2 code. + +2011-11-04 Stephane Letz + + * Fix jack_set_port_name API. + +2011-11-03 Stephane Letz + + * Add missing jack_client_get_uuid API. + * John Emmas Windows server launching patch (1). + +2011-10-28 Stephane Letz + + * John Emmas POST_PACKED_STRUCTURE patch. + +2011-10-27 Stephane Letz + + * Gabriel Beddingfield patch (r4541) reverted. + +2011-10-10 Stephane Letz + + * John Emmas patch for DSP CPU computation. + +2011-09-27 Stephane Letz + + * Gabriel Beddingfield patch for ALSA driver: error when source is non-native byte-order float. + +2011-08-31 Stephane Letz + + * Correct Start/Stop for Control API. + +2011-08-30 Stephane Letz + + * Check driver type in jackdmp.cpp. + +2011-08-28 Stephane Letz + + * Correct JackBasePosixMutex::Trylock. + * Correct JackMessageBuffer::Execute. + +2011-08-26 Stephane Letz + + * More robust code in synchronization primitives and in JackMessageBuffer. + * Non blocking notifications in JackEngine::NotifyAddClient and JackEngine::NotifyRemoveClient. + * More robust Control API implementation. + * Add jackctl_driver_get_type in Control API. + * Singleton behaviour for JackCoreMidiDriver and JackWinMMEDriver. + +2011-07-29 Stephane Letz + + * New JackTimedDriver class to be used by JackDummyDriver, JackNetDriver and JackNetOneDriver classes. + +2011-07-28 Stephane Letz + + * Enable explicit channel mapping in CoreAudio driver. + +2011-07-25 Stephane Letz + + * NetJack2: no more timeout, correct JackWaitThreadedDriver::Execute. + +2011-07-25 Stephane Letz + + * NetJack2: improve latency management, cleanup. + +2011-07-23 Stephane Letz + + * Possible fix for http://trac.jackaudio.org/ticket/193. + +2011-07-22 Stephane Letz + + * NetJack2: improve error reporting. + +2011-07-16 Stephane Letz + + * Error in JackActivationCount::Signal now uses jack_log instead of jack_error. + * EXPORT macro renamed to LIB_EXPORT. + +2011-07-12 Stephane Letz + + * NetJack2 now only send data on network only is ports are connected both sides. + +2011-07-11 Stephane Letz + + * Add JACK_NETJACK_PORT and JACK_NETJACK_MULTICAST environment variables for NetJack2. + +2011-07-08 Stephane Letz + + * NetJack2 now only send data on network for connected ports. + +2011-07-03 Stephane Letz + + * More debug code in JackMMCSS class. + +2011-07-03 Stephane Letz + + * -l in JackCoreAudioDriver now display devices names and then quit. + +2011-07-01 Stephane Letz + + * Fix bugs in JackNetAdapter. + +2011-06-29 Stephane Letz + + * Special CATCH_CLOSE_EXCEPTION_RETURN to handle Close API calls. + +2011-06-28 Stephane Letz + + * Another round of code improvements to handle completely buggy Digidesign CoreAudio user-land driver. + +2011-06-20 Stephane Letz + + * Correct Dummy driver. + +2011-06-17 Stephane Letz + + * NetJack2: connection error handling. + +2011-06-16 Stephane Letz + + * Changes in NetJack2 connection management: no more timeout, any transmission error considered as fatal. + * NetJack2: timeout again... + +2011-06-11 Stephane Letz + + * Special version of jack_attach_shm/jack_release_shm on client side for POSIX shared memory, to solve a memory leak issue. + +2011-06-10 Stephane Letz + + * SaveConnections/RestoreConnections in NetDriver. + * SaveConnections/RestoreConnections moved in JackAudioDriver. + +2011-06-09 Stephane Letz + + * Correct NetJack2 connection handling. + +2011-05-27 Stephane Letz + + * Correct rd_acquire in dbus code. + +2011-05-16 Stephane Letz + + * Correct OSX real-time thread setup. + +2011-05-11 Stephane Letz + + * Correct MIDI in NetJack2. + +2011-05-05 Stephane Letz + + * Libjacknet in progress. + +2011-05-02 Stephane Letz + + * Merge branch switch-master-port-registration-notifications: correct driver port registration. + +2011-04-21 Stephane Letz + + * CELT code for NetJack2. + +2011-04-20 Stephane Letz + + * Add XRun detection in PortAudio driver. + +2011-04-18 Stephane Letz + + * JackWeakAPI.cpp renamed in JackWeakAPI.c. + +2011-04-04 Stephane Letz + + * Correct driver lifetime management. + +2011-04-03 Stephane Letz + + * Fix in JackCoreAudioDriver::Read when there is no inputs. + +2011-04-02 Stephane Letz + + * NetDriver can now ask for in/out values from the master (in progress). + * Correct drivers parameter settings. + +2011-04-01 Stephane Letz + + * 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 + + * Version 1.9.8 started. + 2011-03-29 Stephane Letz * Synchronize JackWeakAPI.cpp with new APIs. @@ -117,7 +332,7 @@ Valerio Pilo 2010-11-17 Stephane Letz - * ALSA backend : suspend/resume handling (jack1 r4075). + * ALSA backend: suspend/resume handling (jack1 r4075). * Correct dummy driver. 2010-11-05 Stephane Letz @@ -169,7 +384,7 @@ Valerio Pilo 2010-06-13 Stephane Letz - * Fix JackPosixSemaphore::TimedWait : same behavior as JackPosixSemaphore::Wait regarding EINTR. + * Fix JackPosixSemaphore::TimedWait: same behavior as JackPosixSemaphore::Wait regarding EINTR. 2010-05-31 Stephane Letz @@ -216,7 +431,7 @@ Valerio Pilo 2010-03-04 Stephane Letz - * Correct JackMachServerChannel::Execute : keep running even in error cases. + * Correct JackMachServerChannel::Execute: keep running even in error cases. * Raise JACK_PROTOCOL_VERSION number. 2010-03-03 Stephane Letz @@ -225,7 +440,7 @@ Valerio Pilo 2010-03-02 Stephane Letz - * Improve JackCoreAudioDriver and JackCoreAudioAdapter : when no devices are described, takes default input and output and aggregate them. + * Improve JackCoreAudioDriver and JackCoreAudioAdapter: when no devices are described, takes default input and output and aggregate them. 2010-02-15 Stephane Letz @@ -267,7 +482,7 @@ Valerio Pilo 2009-12-01 Stephane Letz - * Fix port_rename callback : now both old name and new name are given as parameters. + * Fix port_rename callback: now both old name and new name are given as parameters. 2009-11-30 Stephane Letz @@ -297,7 +512,7 @@ Valerio Pilo 2009-11-17 Stephane Letz * 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). + * In JackCoreAudio driver, clock drift compensation semantic changed a bit: when on, does not activate if not needed (same clock domain). 2009-11-16 Stephane Letz @@ -305,14 +520,14 @@ Valerio Pilo 2009-11-14 Stephane Letz - * Sync with JACK1 : -r parameter now used for no-realtime, realtime (-R) is now default, usable backend given vie platform. + * Sync with JACK1: -r parameter now used for no-realtime, realtime (-R) is now default, usable backend given vie platform. 2009-11-13 Stephane Letz * 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. + * Simplify server temporary mode: now use a JackTemporaryException. * Lock/Unlock shared memory segments (to test...). 2009-11-12 Stephane Letz @@ -329,12 +544,12 @@ Valerio Pilo 2009-11-09 Stephane Letz - * Correct JackGraphManager::GetBuffer for the "client loop with one connection" case : buffer must be copied. + * Correct JackGraphManager::GetBuffer for the "client loop with one connection" case: buffer must be copied. 2009-11-07 Stephane Letz * 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 JackPosixThread::StartImp: thread priority setting now done in the RT case only. 2009-11-06 Stephane Letz @@ -375,7 +590,7 @@ Valerio Pilo 2009-10-25 Stephane Letz - * Improve aggregate device management in JackCoreAudioDriver : now a "private" device only and cleanup properly. + * Improve aggregate device management in JackCoreAudioDriver: now a "private" device only and cleanup properly. * Aggregate device code added to JackCoreAudioAdapter. 2009-10-23 Stephane Letz @@ -396,7 +611,7 @@ Valerio Pilo 2009-10-17 Stephane Letz - * Correct server temporary mode : now set a global and quit after server/client message handling is finished. + * Correct server temporary mode: now set a global and quit after server/client message handling is finished. 2009-10-15 Stephane Letz @@ -477,12 +692,12 @@ Valerio Pilo 2009-06-30 Stephane Letz - * Tim Bechmann patch : hammerfall, only release monitor thread, if it has been created. + * Tim Bechmann patch: hammerfall, only release monitor thread, if it has been created. 2009-06-19 Stephane Letz * Correct JackTransportEngine::MakeAllLocating, sync callback has to be called in this case also. - * NetJack2 code : better error checkout, method renaming. + * NetJack2 code: better error checkout, method renaming. 2009-06-17 Stephane Letz @@ -582,7 +797,7 @@ Valerio Pilo 2009-03-11 Stephane Letz - * Client incorrect re-naming fixed : now done at socket level also. + * Client incorrect re-naming fixed: now done at socket level also. 2009-03-10 Stephane Letz @@ -797,7 +1012,7 @@ Valerio Pilo 2008-10-08 Stephane Letz - * Fix a SMP related bug introduced in rev 2957 : remove the __SMP__ flag and define LOCK for SMP in all cases. + * Fix a SMP related bug introduced in rev 2957: remove the __SMP__ flag and define LOCK for SMP in all cases. 2008-10-02 Stephane Letz @@ -812,7 +1027,7 @@ Valerio Pilo 2008-10-10 Stephane Letz - * Improve OSS backend : SNDCTL_DSP_SETFRAGMENT must be done before, use of AFMT_S16_LE kind of values. + * Improve OSS backend: SNDCTL_DSP_SETFRAGMENT must be done before, use of AFMT_S16_LE kind of values. 2008-10-09 Stephane Letz @@ -821,7 +1036,7 @@ Valerio Pilo 2008-10-08 Stephane Letz - * Fix a SMP related bug introduced in rev 2957 : remove the __SMP__ flag and define LOCK for SMP in all cases. + * Fix a SMP related bug introduced in rev 2957: remove the __SMP__ flag and define LOCK for SMP in all cases. 2008-10-03 Stephane Letz @@ -938,7 +1153,7 @@ Valerio Pilo 2008-07-08 Stephane Letz * Add jack_get_descriptor in internal clients API. - * Fix JackFreewheelDriver::Process() in case if client time-out : continue processing until a better recovery strategy is chosen. + * Fix JackFreewheelDriver::Process() in case if client time-out: continue processing until a better recovery strategy is chosen. 2008-07-08 Stephane Letz @@ -1011,7 +1226,7 @@ Valerio Pilo 2008-06-02 Stephane Letz - * Tim Blechmann patch to remove unnecessary virtual methods : choice of the appropriate platform version is now done at compilation time. + * Tim Blechmann patch to remove unnecessary virtual methods: choice of the appropriate platform version is now done at compilation time. 2008-06-02 Stephane Letz @@ -1038,12 +1253,12 @@ Valerio Pilo 2008-05-27 Stephane Letz - * Correct timing in drivers : frame time has to be incremented before Read. + * Correct timing in drivers: frame time has to be incremented before Read. 2008-05-26 Stephane Letz * Merge control branch. - * Cleanup example clients : use jack_client_open and install a proper 'quit' signal handler. + * Cleanup example clients: use jack_client_open and install a proper 'quit' signal handler. 2008-05-24 Stephane Letz @@ -1064,7 +1279,7 @@ Valerio Pilo * Correct JackEngine::PortUnRegister, JackEngine::ClientCloseAux and JackEngine::ClientDeactivate to correctly send notifications. * New jack_get_client_pid API, implemented on server side. - * Better handling of graph state read functions : never wait when used in the real-time thread, current state is used. + * Better handling of graph state read functions: never wait when used in the real-time thread, current state is used. 2008-05-20 Stephane Letz @@ -1074,7 +1289,7 @@ Valerio Pilo 2008-05-19 Stephane Letz * Use of placement new for dynamic port allocation is possibly not safe... so avoid that until a definitive answer is found. - * JackAudioDriver::ProcessAsync and JackAudioDriver::ProcessSync were broken at some point : 0 has to be returned in all cases. + * JackAudioDriver::ProcessAsync and JackAudioDriver::ProcessSync were broken at some point: 0 has to be returned in all cases. 2008-05-16 Stephane Letz @@ -1103,7 +1318,7 @@ Valerio Pilo 2008-05-05 Stephane Letz - * Fix JackClient::Close : notification channel is stopped first to avoid receiving notifications while closing and Close is again a synchronous call. + * Fix JackClient::Close: notification channel is stopped first to avoid receiving notifications while closing and Close is again a synchronous call. * No more remaining client close in JackEngine::Close(). 2008-05-01 Stephane Letz @@ -1144,7 +1359,7 @@ Valerio Pilo 2008-03-29 Stephane Letz * Correct a missing parameter in the usage message of jack_midiseq. - * Add a client counter in wrapper layer : library is unloaded only when all clients have been closed. + * Add a client counter in wrapper layer: library is unloaded only when all clients have been closed. 2008-03-28 Stephane Letz @@ -1334,7 +1549,7 @@ Valerio Pilo 2008-01-03 Stephane Letz - * Dmitry Baikov MIDI patch : alsa_seqmidi and alsa_rammidi drivers. + * Dmitry Baikov MIDI patch: alsa_seqmidi and alsa_rammidi drivers. 2008-01-03 Stephane Letz @@ -1741,4 +1956,4 @@ Valerio Pilo 2006-09-03 Stephane Letz - * First import of version 0.58 base code + * First import of version 0.58 base code diff --git a/README b/README index b9a0a533..eac3210b 100644 --- a/README +++ b/README @@ -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,8 @@ 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. +1.9.8 : Merge newer-midi branch (Devin Anderson redesign of the MIDI drivers: alsarawmidi, ffado, coremidi and winmme). Correction in jackdmp.cpp: notify_server_stop should be done after server destruction. Correct driver lifetime management. Add XRun detection in PortAudio driver. CELT code for NetJack2. Merge branch switch-master-port-registration-notifications: correct driver port registration. Libjacknet in progress. Correct MIDI in NetJack2. Correct OSX real-time thread setup. Correct rd_acquire in dbus code. Correct NetJack2 connection handling. SaveConnections/RestoreConnections in NetDriver and JackAudioDriver. Special version of jack_attach_shm/jack_release_shm on client side for POSIX shared memory, to solve a memory leak issue. Another round of code improvements to handle completely buggy Digidesign CoreAudio user-land driver. Special CATCH_CLOSE_EXCEPTION_RETURN to handle Close API calls. Add JACK_NETJACK_PORT and JACK_NETJACK_MULTICAST environment variables for NetJack2. NetJack2 now only send data on network only is ports are connected both sides. Fix for "starting two instances of same app in parallel does not work" bug. Enable explicit channel mapping in CoreAudio driver. New JackTimedDriver class to be used by JackDummyDriver, JackNetDriver and JackNetOneDriver classes. More robust code in synchronization primitives and in JackMessageBuffer. More robust Control API implementation. Add jackctl_driver_get_type in Control API. Singleton behaviour for JackCoreMidiDriver and JackWinMMEDriver. John Emmas patch for DSP CPU computation. John Emmas Windows server launching patch. Fix jack_set_port_name API. Enable local access in NetJack2 code. Dynamic port management in JACK/CoreMidi bridge. 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... diff --git a/README_NETJACK2 b/README_NETJACK2 index 206c4ecf..47749fef 100644 --- a/README_NETJACK2 +++ b/README_NETJACK2 @@ -1,9 +1,9 @@ ------------------------------- -NetJack for Jackmp +NetJack2 for Jack2 ------------------------------- -This release includes a version of netjack designed for jackmp. Indeed, the original concept has been completely redesigned to better fit to the jackmp architecture, but also in order to provide additional capabilities, and ultimately a greater robustness. +This release includes a version of netjack designed for jack2. Indeed, the original concept has been completely redesigned to better fit to the Jack2 architecture, but also in order to provide additional capabilities, and ultimately a greater robustness. This document describes the major changes between those two systems, then a simple how-to for setting up a basic usage of 'netjack2'. @@ -13,24 +13,24 @@ Major changes and architecture ------------------------------- -The biggest difference between netjack and netjack2 is the way of slicing audio and midi streams into network packets. For one audio cycle, netjack used to take all audio and midi buffers (one per channel), put butt all of them, then send it over the network. The problem is that a network packet has a fixed maximum size, depending on the network infrastructure (for 100mb, it reaches 1500bytes - MTU of the network). The solution is then to slice those buffers into smaller ones, and then send as many packets as we need. This cutting up can be done by network equipments, but it's more efficient and secure to include it in the software data management. Still this slicing brings another issue : all the packets are not pleased with any emission order and are unfortunately received in a random order, thanks to UDP. So we can't deal with data as it comes, we need to re-bufferize incoming streams in order to rebuild complete audio buffers. +The biggest difference between netjack1 and netjack2 is the way of slicing audio and midi streams into network packets. For one audio cycle, netjack1 used to take all audio and midi buffers (one per channel), put butt all of them, then send it over the network. The problem is that a network packet has a fixed maximum size, depending on the network infrastructure (for 100mb, it reaches 1500bytes - MTU of the network). The solution is then to slice those buffers into smaller ones, and then send as many packets as we need. This cutting up can be done by network equipments, but it's more efficient and secure to include it in the software data management. Still this slicing brings another issue : all the packets are not pleased with any emission order and are unfortunately received in a random order, thanks to UDP. So we can't deal with data as it comes, we need to re-bufferize incoming streams in order to rebuild complete audio buffers. -In netjack2, the main idea is to make this slicing depending on the network capabilities. If we can put only 128 complete audio frames (128 samples for all audio channels) in a network packet, the elementary packet will so carry 128 frames, and in one cycle, we will transmit as many packet as we need. We take the example of 128 frames because it's the current value for 2 channels. This value is determinated by taking the maximum 'power of 2' frames we can put in a packet. If we take 2 channels, 4 bytes per sample (float values), we get 8 bytes per frame, with 128 frames, we now have 1024 bytes, so we can put these 1024 bytes in one packet, and add a small header which identify the packet. This technique allows to separate the packets (in time) so they can be received in the order they have been emitted. If the master is running at 512 frames per second, four audio packets are sent per cycle and the slave deals with them as they arrive. With gigabytes networks, the MTU is larger, so we can put more data in one packet (in this example, we can even put the complete cycle in one packet). +In netjack2, the main idea is to make this slicing depending on the network capabilities. If we can put only 128 complete audio frames (128 samples for all audio channels) in a network packet, the elementary packet will so carry 128 frames, and in one cycle, we will transmit as many packet as we need. We take the example of 128 frames because it's the current value for 2 channels. This value is determined by taking the maximum 'power of 2' frames we can put in a packet. If we take 2 channels, 4 bytes per sample (float values), we get 8 bytes per frame, with 128 frames, we now have 1024 bytes, so we can put these 1024 bytes in one packet, and add a small header which identify the packet. This technique allows to separate the packets (in time) so they can be received in the order they have been emitted. If the master is running at 512 frames per second, four audio packets are sent per cycle and the slave deals with them as they arrive. With gigabytes networks, the MTU is larger, so we can put more data in one packet (in this example, we can even put the complete cycle in one packet). -For midi data, netjack used to send the whole buffer, in this example, 512 frames * 4 bytes per sample and per midi port. Those 2048 bytes are in 99% of the time filled to a few bytes, but rarely more. This means that if we have 2 audio and 2 midi channels to transmit, everything happens as if we had 4 audio channels, which is quite a waste of bandwidth. In netjack2, the idea is to take into account that fact, by sending only the useful bytes, and not more. It's completely unappropriate to overload the network with useless data. So we now have : 99% of the time one midi packet (of a few dozen of bytes), followed by four audio packets (in this example). +For midi data, netjack1 used to send the whole buffer, in this example, 512 frames * 4 bytes per sample and per midi port. Those 2048 bytes are in 99% of the time filled to a few bytes, but rarely more. This means that if we have 2 audio and 2 midi channels to transmit, everything happens as if we had 4 audio channels, which is quite a waste of bandwidth. In netjack2, the idea is to take into account that fact, by sending only the useful bytes, and not more. It's completely inappropriate to overload the network with useless data. So we now have : 99% of the time one midi packet (of a few dozen of bytes), followed by four audio packets (in this example). This way of separating audio and midi is quite important. We deal here with network transmissions, and also need to be 'realtime'. We need a system which allow to carry as many audio and midi data streams as we need and can, as if the distant computer was in fact a simple jack client. With all those constraints, we can't avoid packets loss. The better thing to do is to deal with it. But to loose an audio packet is different from skipping a midi one. Indeed, an audio loss leads to audio click, or undesirable, but very short side effect. Whereas a midi data loss can be completely disastrous. Imagine that we play some notes, with sustain, and we loose the sustain 0 value, which stops the effect. The sustain keeps going on on all following notes until the next 'sustain off' event. A simple missing byte can put all the midi system offside (that's the purpose of all the big PANIC buttons on midi softwares...). That's why we need to separate audio (more than one per cycle) from midi (one packet at 99% of the time). If we loose an audio packet, we probably still have an available midi packet, so we can use what we received, even if some audio is missing. -Those audio and midi packets are preceded by a synchronization packet, which will make the slave directly synchronized on the master's cycle rythm. This packet also carries transport data. Thus it's actually possible to synchronize also transport. This feature goes a little further than in netjack. The idea here is to make every computer of the network fully synchronized on the master's transport. That means the master needs to know the state of every slow sync clients of each of its slaves. The master can now manage the transport state (especially the 'rolling' state) of each slave thus the main transport waits for the last slow sync client before turning 'rolling'. By doing this, the transport can start (roll) in the same cycle for every computers managed by the master. +Those audio and midi packets are preceded by a synchronization packet, which will make the slave directly synchronized on the master's cycle rhythm. This packet also carries transport data. Thus it's actually possible to synchronize also transport. This feature goes a little further than in netjack1. The idea here is to make every computer of the network fully synchronized on the master's transport. That means the master needs to know the state of every slow sync clients of each of its slaves. The master can now manage the transport state (especially the 'rolling' state) of each slave thus the main transport waits for the last slow sync client before turning 'rolling'. By doing this, the transport can start (roll) in the same cycle for every computers managed by the master. -The second main difference between netjack and netjack2 is the way the two computers (master and slave) synchronise their parametering and launch. In netjack, once the slave configured (by the command line) and launched, it was waiting for the first incoming packet to synchronize (launch its first audio cycle) then run. The two computers needed to be configured separately but with the same parameters to run correctly. +The second main difference between netjack1 and netjack2 is the way the two computers (master and slave) synchronize their parameters and launch. In netjack1, once the slave configured (by the command line) and launched, it was waiting for the first incoming packet to synchronize (launch its first audio cycle) then run. The two computers needed to be configured separately but with the same parameters to run correctly. -In netjack2, the only thing you have to set for the slave is its number of in/out midi and audio channels. No more need to choose and set parameters depending on the master, they are automatically determinated and communicated to the slave. This first synchronization step uses a multicast communication, no more need to know by advance all the IP addresses. The slave says on a multicast address "hey, I'm available". A master get the message, and communicate parametering to the slave. Once synchronization done, data transfers can start. Moreover, the master being still listening on the multicast address, it can catch other slaves and manage them (create a jack client to communicate with the slave, and neatily close everything when the slave is gone). +In netjack2, the only thing you have to set for the slave is its number of in/out midi and audio channels. No more need to choose and set parameters depending on the master, they are automatically determined and communicated to the slave. This first synchronization step uses a multicast communication, no more need to know by advance all the IP addresses. The slave says on a multicast address "hey, I'm available". A master get the message, and communicate parameterers to the slave. Once synchronization done, data transfers can start. Moreover, the master being still listening on the multicast address, it can catch other slaves and manage them (create a jack client to communicate with the slave, and neatly close everything when the slave is gone). -The loaded internal client is no longer only an interface for the slave, like in netjack. It's now called 'network manager', it doesn't deal with audio or midi, just with some kind of 'network logistical messages'. The manager automatically create a new internal client as soon as a new slave is seen on the network (by sending messages on the multicast address the manager is listening on). This manager is also able to remove one of its internal client as soon as a slave has left the network. This conception allow a complete separation of audio exchanges from parametering and management. +The loaded internal client is no longer only an interface for the slave, like in netjack1. It's now called 'network manager', it doesn't deal with audio or midi, just with some kind of 'network logistical messages'. The manager automatically create a new internal client as soon as a new slave is seen on the network (by sending messages on the multicast address the manager is listening on). This manager is also able to remove one of its internal client as soon as a slave has left the network. This conception allow a complete separation of audio exchanges from parameterers and management. -The 'unloading' of the internal client (the manager) will cause a full cleaning of the infrastructure. The jack clients are all removed from the server, the slave are all turned available again, ready to be cought by another master etc. When a slave quits, it's also automatically removed from the manager's slaves list. +The 'unloading' of the internal client (the manager) will cause a full cleaning of the infrastructure. The jack clients are all removed from the server, the slave are all turned available again, ready to be caught by another master etc. When a slave quits, it's also automatically removed from the manager's slaves list. ------------------------------- @@ -40,13 +40,13 @@ How-to use this ? Netjackmp is very simple to use. On the master's side, an internal client deals with the slaves, and the slaves themselves are classical jack servers running under a 'network audio driver'. The difference between the two versions is that the master now has a manager, which takes care of the slaves, while listening on the multicast address and create a new master as soon as a slave is available. But everything is transparent to the user, that's why it uses multicast (someone says "hello", and anyone who wants to hear it just has to listen). -So, just compile and install jackmp as you are used to, on linux, using './waf configure', './waf' and './waf install' as root. On macosx, you can use the xcode project. On Windows, you can use the Code::Blocks workspace (you also have a small script to make an all in one installer). +So, just compile and install Jack2 as you are used to, on linux, using './waf configure', './waf' and './waf install' as root. On macosx, you can use the xcode project. On Windows, you can use the Code::Blocks workspace (you also have a small script to make an all in one installer). On the master, just launch a classical jack server, the period size doesn't matter. Then, load the network manager using jack_load : 'jack_load netmanager' -This will load the internal client, which will wait for an available slave (see the message window on qjackctl - or the console output). If you want to listen to a specific multicast socket, you can add some options. To spicify a complete command line, you can use : +This will load the internal client, which will wait for an available slave (see the message window on QjackCtl - or the console output). If you want to listen to a specific multicast socket, you can add some options. To specify a complete command line, you can use : 'jack_load netmanager -i"-a xxx.xxx.xxx.xxx -p udp_port"' @@ -56,7 +56,7 @@ On the slave, just launch a new jack server using : 'jackd -R -d net' -As in a standard backend in Jackmp, you can use '-S' (synchronous mode). The asynchronous mode (without '-S') allows to send the computed data during the next cycle. In synchronous mode, data are sent back at the end of the cycle, that means after the process. You can specify some options, like '-n name' (will give a name to the slave, default is the network hostname), '-C input_ports' (the number of master-->slave channels), '-P output_ports' (the number of slave-->master channels), default is 2 ; or '-i midi_in_ports' and '-o midi_out_ports', default is 0. If you set multicast address or port on the master, you can add '-a xxx.xxx.xxx.xxx' and '-p udp_port'. +As in a standard backend in Jack2, you can use '-S' (synchronous mode). The asynchronous mode (without '-S') allows to send the computed data during the next cycle. In synchronous mode, data are sent back at the end of the cycle, that means after the process. You can specify some options, like '-n name' (will give a name to the slave, default is the network hostname), '-C input_ports' (the number of master-->slave channels), '-P output_ports' (the number of slave-->master channels), default is 2 ; or '-i midi_in_ports' and '-o midi_out_ports', default is 0. If you set multicast address or port on the master, you can add '-a xxx.xxx.xxx.xxx' and '-p udp_port'. You can also use others network mode, with '-m' option (fast, normal or slow). Fast mode allow a zero latency transmission. This mode means the master waits for its returned data from the slave in the current cycle. This mode is appropriated for 'small' transmissions (only a few channels with a light process on the slave). Normal mode brings one cycle latency. It allow a normal use of the network. @@ -69,6 +69,6 @@ For additional informations, you can go to the NetJack2 Wiki at : http://trac.ja What's next ? ------------------------------- -The development of netjack continues and some things are always moving... If you use it, please report encountered bugs, ideas or anything you think about. +The development of netjack2 continues and some things are always moving... If you use it, please report encountered bugs, ideas or anything you think about. If you have any question, you can subscribe the jackaudio developers mailing list at http://www.jackaudio.org/ or join the IRC channel '#jack' on FreeNode. diff --git a/common/JackAPI.cpp b/common/JackAPI.cpp index 5c0d4cb1..5c1be12d 100644 --- a/common/JackAPI.cpp +++ b/common/JackAPI.cpp @@ -25,9 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "JackClientControl.h" #include "JackGlobals.h" #include "JackTime.h" -#include "JackCompilerDeps.h" #include "JackPortType.h" -#include "JackPlatformPlug.h" #include #ifdef __CLIENTDEBUG__ @@ -44,7 +42,7 @@ extern "C" typedef void (*print_function)(const char*); typedef void *(*thread_routine)(void*); - EXPORT + LIB_EXPORT void jack_get_version( int *major_ptr, @@ -52,219 +50,221 @@ extern "C" int *micro_ptr, int *proto_ptr); - EXPORT + LIB_EXPORT const char* jack_get_version_string(); jack_client_t * jack_client_new_aux(const char* client_name, jack_options_t options, jack_status_t *status); - EXPORT jack_client_t * jack_client_open(const char* client_name, + + LIB_EXPORT jack_client_t * jack_client_open(const char* client_name, jack_options_t options, jack_status_t *status, ...); - EXPORT jack_client_t * jack_client_new(const char* client_name); - EXPORT int jack_client_name_size(void); - EXPORT char* jack_get_client_name(jack_client_t *client); - EXPORT int jack_internal_client_new(const char* client_name, + LIB_EXPORT jack_client_t * jack_client_new(const char* client_name); + LIB_EXPORT int jack_client_name_size(void); + LIB_EXPORT char* jack_get_client_name(jack_client_t *client); + LIB_EXPORT int jack_internal_client_new(const char* client_name, const char* load_name, const char* load_init); - EXPORT void jack_internal_client_close(const char* client_name); - EXPORT int jack_is_realtime(jack_client_t *client); - EXPORT void jack_on_shutdown(jack_client_t *client, + LIB_EXPORT void jack_internal_client_close(const char* client_name); + LIB_EXPORT int jack_is_realtime(jack_client_t *client); + LIB_EXPORT void jack_on_shutdown(jack_client_t *client, JackShutdownCallback shutdown_callback, void *arg); - EXPORT void jack_on_info_shutdown(jack_client_t *client, + LIB_EXPORT void jack_on_info_shutdown(jack_client_t *client, JackInfoShutdownCallback shutdown_callback, void *arg); - EXPORT int jack_set_process_callback(jack_client_t *client, + LIB_EXPORT int jack_set_process_callback(jack_client_t *client, JackProcessCallback process_callback, void *arg); - EXPORT jack_nframes_t jack_thread_wait(jack_client_t *client, int status); + LIB_EXPORT jack_nframes_t jack_thread_wait(jack_client_t *client, int status); // new - EXPORT jack_nframes_t jack_cycle_wait(jack_client_t*); - EXPORT void jack_cycle_signal(jack_client_t*, int status); - EXPORT int jack_set_process_thread(jack_client_t* client, JackThreadCallback fun, void *arg); + LIB_EXPORT jack_nframes_t jack_cycle_wait(jack_client_t*); + LIB_EXPORT void jack_cycle_signal(jack_client_t*, int status); + LIB_EXPORT int jack_set_process_thread(jack_client_t* client, JackThreadCallback fun, void *arg); - EXPORT int jack_set_thread_init_callback(jack_client_t *client, + LIB_EXPORT int jack_set_thread_init_callback(jack_client_t *client, JackThreadInitCallback thread_init_callback, void *arg); - EXPORT int jack_set_freewheel_callback(jack_client_t *client, + LIB_EXPORT int jack_set_freewheel_callback(jack_client_t *client, JackFreewheelCallback freewheel_callback, void *arg); - EXPORT int jack_set_freewheel(jack_client_t* client, int onoff); - EXPORT int jack_set_buffer_size(jack_client_t *client, jack_nframes_t nframes); - EXPORT int jack_set_buffer_size_callback(jack_client_t *client, + LIB_EXPORT int jack_set_freewheel(jack_client_t* client, int onoff); + LIB_EXPORT int jack_set_buffer_size(jack_client_t *client, jack_nframes_t nframes); + LIB_EXPORT int jack_set_buffer_size_callback(jack_client_t *client, JackBufferSizeCallback bufsize_callback, void *arg); - EXPORT int jack_set_sample_rate_callback(jack_client_t *client, + LIB_EXPORT int jack_set_sample_rate_callback(jack_client_t *client, JackSampleRateCallback srate_callback, void *arg); - EXPORT int jack_set_client_registration_callback(jack_client_t *, + LIB_EXPORT int jack_set_client_registration_callback(jack_client_t *, JackClientRegistrationCallback registration_callback, void *arg); - EXPORT int jack_set_port_registration_callback(jack_client_t *, + LIB_EXPORT int jack_set_port_registration_callback(jack_client_t *, JackPortRegistrationCallback registration_callback, void *arg); - EXPORT int jack_set_port_connect_callback(jack_client_t *, + LIB_EXPORT int jack_set_port_connect_callback(jack_client_t *, JackPortConnectCallback connect_callback, void *arg); - EXPORT int jack_set_port_rename_callback(jack_client_t *, + LIB_EXPORT int jack_set_port_rename_callback(jack_client_t *, JackPortRenameCallback rename_callback, void *arg); - EXPORT int jack_set_graph_order_callback(jack_client_t *, + LIB_EXPORT int jack_set_graph_order_callback(jack_client_t *, JackGraphOrderCallback graph_callback, void *); - EXPORT int jack_set_xrun_callback(jack_client_t *, + LIB_EXPORT int jack_set_xrun_callback(jack_client_t *, JackXRunCallback xrun_callback, void *arg); - EXPORT int jack_set_latency_callback(jack_client_t *client, + LIB_EXPORT int jack_set_latency_callback(jack_client_t *client, JackLatencyCallback latency_callback, void *arg); - EXPORT int jack_activate(jack_client_t *client); - EXPORT int jack_deactivate(jack_client_t *client); - EXPORT jack_port_t * jack_port_register(jack_client_t *client, + LIB_EXPORT int jack_activate(jack_client_t *client); + LIB_EXPORT int jack_deactivate(jack_client_t *client); + LIB_EXPORT jack_port_t * jack_port_register(jack_client_t *client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); - EXPORT int jack_port_unregister(jack_client_t *, jack_port_t *); - EXPORT void * jack_port_get_buffer(jack_port_t *, jack_nframes_t); - EXPORT const char* jack_port_name(const jack_port_t *port); - EXPORT const char* jack_port_short_name(const jack_port_t *port); - EXPORT int jack_port_flags(const jack_port_t *port); - EXPORT const char* jack_port_type(const jack_port_t *port); - EXPORT jack_port_type_id_t jack_port_type_id(const jack_port_t *port); - EXPORT int jack_port_is_mine(const jack_client_t *, const jack_port_t *port); - EXPORT int jack_port_connected(const jack_port_t *port); - EXPORT int jack_port_connected_to(const jack_port_t *port, + LIB_EXPORT int jack_port_unregister(jack_client_t *, jack_port_t *); + LIB_EXPORT void * jack_port_get_buffer(jack_port_t *, jack_nframes_t); + LIB_EXPORT const char* jack_port_name(const jack_port_t *port); + LIB_EXPORT const char* jack_port_short_name(const jack_port_t *port); + LIB_EXPORT int jack_port_flags(const jack_port_t *port); + LIB_EXPORT const char* jack_port_type(const jack_port_t *port); + LIB_EXPORT jack_port_type_id_t jack_port_type_id(const jack_port_t *port); + LIB_EXPORT int jack_port_is_mine(const jack_client_t *, const jack_port_t *port); + LIB_EXPORT int jack_port_connected(const jack_port_t *port); + LIB_EXPORT int jack_port_connected_to(const jack_port_t *port, const char* port_name); - EXPORT const char* * jack_port_get_connections(const jack_port_t *port); - EXPORT const char* * jack_port_get_all_connections(const jack_client_t *client, + LIB_EXPORT const char* * jack_port_get_connections(const jack_port_t *port); + LIB_EXPORT const char* * jack_port_get_all_connections(const jack_client_t *client, const jack_port_t *port); - EXPORT int jack_port_tie(jack_port_t *src, jack_port_t *dst); - EXPORT int jack_port_untie(jack_port_t *port); + LIB_EXPORT int jack_port_tie(jack_port_t *src, jack_port_t *dst); + LIB_EXPORT int jack_port_untie(jack_port_t *port); // Old latency API - EXPORT jack_nframes_t jack_port_get_latency(jack_port_t *port); - EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t *, + LIB_EXPORT jack_nframes_t jack_port_get_latency(jack_port_t *port); + LIB_EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t *, jack_port_t *port); - EXPORT void jack_port_set_latency(jack_port_t *, jack_nframes_t); - EXPORT int jack_recompute_total_latency(jack_client_t*, jack_port_t* port); + LIB_EXPORT void jack_port_set_latency(jack_port_t *, jack_nframes_t); + LIB_EXPORT int jack_recompute_total_latency(jack_client_t*, jack_port_t* port); // New latency API - EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); - EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); - EXPORT int jack_recompute_total_latencies(jack_client_t*); - - EXPORT int jack_port_set_name(jack_port_t *port, const char* port_name); - EXPORT int jack_port_set_alias(jack_port_t *port, const char* alias); - EXPORT int jack_port_unset_alias(jack_port_t *port, const char* alias); - EXPORT int jack_port_get_aliases(const jack_port_t *port, char* const aliases[2]); - EXPORT int jack_port_request_monitor(jack_port_t *port, int onoff); - EXPORT int jack_port_request_monitor_by_name(jack_client_t *client, + LIB_EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); + LIB_EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); + LIB_EXPORT int jack_recompute_total_latencies(jack_client_t*); + + LIB_EXPORT int jack_port_set_name(jack_port_t *port, const char* port_name); + LIB_EXPORT int jack_port_set_alias(jack_port_t *port, const char* alias); + LIB_EXPORT int jack_port_unset_alias(jack_port_t *port, const char* alias); + LIB_EXPORT int jack_port_get_aliases(const jack_port_t *port, char* const aliases[2]); + LIB_EXPORT int jack_port_request_monitor(jack_port_t *port, int onoff); + LIB_EXPORT int jack_port_request_monitor_by_name(jack_client_t *client, const char* port_name, int onoff); - EXPORT int jack_port_ensure_monitor(jack_port_t *port, int onoff); - EXPORT int jack_port_monitoring_input(jack_port_t *port); - EXPORT int jack_connect(jack_client_t *, + LIB_EXPORT int jack_port_ensure_monitor(jack_port_t *port, int onoff); + LIB_EXPORT int jack_port_monitoring_input(jack_port_t *port); + LIB_EXPORT int jack_connect(jack_client_t *, const char* source_port, const char* destination_port); - EXPORT int jack_disconnect(jack_client_t *, + LIB_EXPORT int jack_disconnect(jack_client_t *, const char* source_port, const char* destination_port); - EXPORT int jack_port_disconnect(jack_client_t *, jack_port_t *); - EXPORT int jack_port_name_size(void); - EXPORT int jack_port_type_size(void); - EXPORT size_t jack_port_type_get_buffer_size(jack_client_t *client, const char* port_type); - EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t *); - EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t *); - EXPORT const char* * jack_get_ports(jack_client_t *, + LIB_EXPORT int jack_port_disconnect(jack_client_t *, jack_port_t *); + LIB_EXPORT int jack_port_name_size(void); + LIB_EXPORT int jack_port_type_size(void); + LIB_EXPORT size_t jack_port_type_get_buffer_size(jack_client_t *client, const char* port_type); + LIB_EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t *); + LIB_EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t *); + LIB_EXPORT const char* * jack_get_ports(jack_client_t *, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); - EXPORT jack_port_t * jack_port_by_name(jack_client_t *, const char* port_name); - EXPORT jack_port_t * jack_port_by_id(jack_client_t *client, + LIB_EXPORT jack_port_t * jack_port_by_name(jack_client_t *, const char* port_name); + LIB_EXPORT jack_port_t * jack_port_by_id(jack_client_t *client, jack_port_id_t port_id); - EXPORT int jack_engine_takeover_timebase(jack_client_t *); - EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t *); - EXPORT jack_time_t jack_get_time(); - EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t time); - EXPORT jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames); - EXPORT jack_nframes_t jack_frame_time(const jack_client_t *); - EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t *client); - EXPORT float jack_cpu_load(jack_client_t *client); - EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t *); - EXPORT void jack_set_error_function(print_function); - EXPORT void jack_set_info_function(print_function); - - EXPORT float jack_get_max_delayed_usecs(jack_client_t *client); - EXPORT float jack_get_xrun_delayed_usecs(jack_client_t *client); - EXPORT void jack_reset_max_delayed_usecs(jack_client_t *client); - - EXPORT int jack_release_timebase(jack_client_t *client); - EXPORT int jack_set_sync_callback(jack_client_t *client, + LIB_EXPORT int jack_engine_takeover_timebase(jack_client_t *); + LIB_EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t *); + LIB_EXPORT jack_time_t jack_get_time(); + LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t time); + LIB_EXPORT jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames); + LIB_EXPORT jack_nframes_t jack_frame_time(const jack_client_t *); + LIB_EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t *client); + LIB_EXPORT float jack_cpu_load(jack_client_t *client); + LIB_EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t *); + LIB_EXPORT void jack_set_error_function(print_function); + LIB_EXPORT void jack_set_info_function(print_function); + + LIB_EXPORT float jack_get_max_delayed_usecs(jack_client_t *client); + LIB_EXPORT float jack_get_xrun_delayed_usecs(jack_client_t *client); + LIB_EXPORT void jack_reset_max_delayed_usecs(jack_client_t *client); + + LIB_EXPORT int jack_release_timebase(jack_client_t *client); + LIB_EXPORT int jack_set_sync_callback(jack_client_t *client, JackSyncCallback sync_callback, void *arg); - EXPORT int jack_set_sync_timeout(jack_client_t *client, + LIB_EXPORT int jack_set_sync_timeout(jack_client_t *client, jack_time_t timeout); - EXPORT int jack_set_timebase_callback(jack_client_t *client, + LIB_EXPORT int jack_set_timebase_callback(jack_client_t *client, int conditional, JackTimebaseCallback timebase_callback, void *arg); - EXPORT int jack_transport_locate(jack_client_t *client, + LIB_EXPORT int jack_transport_locate(jack_client_t *client, jack_nframes_t frame); - EXPORT jack_transport_state_t jack_transport_query(const jack_client_t *client, + LIB_EXPORT jack_transport_state_t jack_transport_query(const jack_client_t *client, jack_position_t *pos); - EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t *client); - EXPORT int jack_transport_reposition(jack_client_t *client, - jack_position_t *pos); - EXPORT void jack_transport_start(jack_client_t *client); - EXPORT void jack_transport_stop(jack_client_t *client); - EXPORT void jack_get_transport_info(jack_client_t *client, + LIB_EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t *client); + LIB_EXPORT int jack_transport_reposition(jack_client_t *client, + const jack_position_t *pos); + LIB_EXPORT void jack_transport_start(jack_client_t *client); + LIB_EXPORT void jack_transport_stop(jack_client_t *client); + LIB_EXPORT void jack_get_transport_info(jack_client_t *client, jack_transport_info_t *tinfo); - EXPORT void jack_set_transport_info(jack_client_t *client, + LIB_EXPORT void jack_set_transport_info(jack_client_t *client, jack_transport_info_t *tinfo); - EXPORT int jack_client_real_time_priority(jack_client_t*); - EXPORT int jack_client_max_real_time_priority(jack_client_t*); - EXPORT int jack_acquire_real_time_scheduling(jack_native_thread_t thread, int priority); - EXPORT int jack_client_create_thread(jack_client_t* client, + LIB_EXPORT int jack_client_real_time_priority(jack_client_t*); + LIB_EXPORT int jack_client_max_real_time_priority(jack_client_t*); + LIB_EXPORT int jack_acquire_real_time_scheduling(jack_native_thread_t thread, int priority); + LIB_EXPORT int jack_client_create_thread(jack_client_t* client, jack_native_thread_t *thread, int priority, int realtime, // boolean thread_routine routine, void *arg); - EXPORT int jack_drop_real_time_scheduling(jack_native_thread_t thread); + LIB_EXPORT int jack_drop_real_time_scheduling(jack_native_thread_t thread); - EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread); - EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread); + LIB_EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread); + LIB_EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread); #ifndef WIN32 - EXPORT void jack_set_thread_creator(jack_thread_creator_t jtc); + LIB_EXPORT void jack_set_thread_creator(jack_thread_creator_t jtc); #endif - EXPORT char * jack_get_internal_client_name(jack_client_t *client, + LIB_EXPORT char * jack_get_internal_client_name(jack_client_t *client, jack_intclient_t intclient); - EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t *client, + LIB_EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t *client, const char* client_name, jack_status_t *status); - EXPORT jack_intclient_t jack_internal_client_load(jack_client_t *client, + LIB_EXPORT jack_intclient_t jack_internal_client_load(jack_client_t *client, const char* client_name, jack_options_t options, jack_status_t *status, ...); - EXPORT jack_intclient_t jack_internal_client_load_aux(jack_client_t *client, + LIB_EXPORT jack_intclient_t jack_internal_client_load_aux(jack_client_t *client, const char* client_name, jack_options_t options, jack_status_t *status, va_list ap); - EXPORT jack_status_t jack_internal_client_unload(jack_client_t *client, + LIB_EXPORT jack_status_t jack_internal_client_unload(jack_client_t *client, jack_intclient_t intclient); - EXPORT void jack_free(void* ptr); - - EXPORT int jack_set_session_callback(jack_client_t* ext_client, JackSessionCallback session_callback, void* arg); - EXPORT jack_session_command_t *jack_session_notify(jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path); - EXPORT int jack_session_reply(jack_client_t* ext_client, jack_session_event_t *event); - EXPORT void jack_session_event_free(jack_session_event_t* ev); - EXPORT char* jack_get_uuid_for_client_name(jack_client_t* ext_client, const char* client_name); - EXPORT char* jack_get_client_name_by_uuid(jack_client_t* ext_client, const char* client_uuid); - EXPORT int jack_reserve_client_name(jack_client_t* ext_client, const char* name, const char* uuid); - EXPORT void jack_session_commands_free(jack_session_command_t *cmds); - EXPORT int jack_client_has_session_callback(jack_client_t *client, const char* client_name); + LIB_EXPORT void jack_free(void* ptr); + + LIB_EXPORT int jack_set_session_callback(jack_client_t* ext_client, JackSessionCallback session_callback, void* arg); + LIB_EXPORT jack_session_command_t *jack_session_notify(jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path); + LIB_EXPORT int jack_session_reply(jack_client_t* ext_client, jack_session_event_t *event); + LIB_EXPORT void jack_session_event_free(jack_session_event_t* ev); + LIB_EXPORT char* jack_client_get_uuid (jack_client_t *client); + LIB_EXPORT char* jack_get_uuid_for_client_name(jack_client_t* ext_client, const char* client_name); + LIB_EXPORT char* jack_get_client_name_by_uuid(jack_client_t* ext_client, const char* client_uuid); + LIB_EXPORT int jack_reserve_client_name(jack_client_t* ext_client, const char* name, const char* uuid); + LIB_EXPORT void jack_session_commands_free(jack_session_command_t *cmds); + LIB_EXPORT int jack_client_has_session_callback(jack_client_t *client, const char* client_name); #ifdef __cplusplus } @@ -299,17 +299,17 @@ static inline void WaitGraphChange() } } -EXPORT void jack_set_error_function(print_function func) +LIB_EXPORT void jack_set_error_function(print_function func) { jack_error_callback = (func == NULL) ? &default_jack_error_callback : func; } -EXPORT void jack_set_info_function(print_function func) +LIB_EXPORT void jack_set_info_function(print_function func) { jack_info_callback = (func == NULL) ? &default_jack_info_callback : func; } -EXPORT jack_client_t* jack_client_new(const char* client_name) +LIB_EXPORT jack_client_t* jack_client_new(const char* client_name) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_new"); @@ -333,7 +333,7 @@ EXPORT jack_client_t* jack_client_new(const char* client_name) } } -EXPORT void* jack_port_get_buffer(jack_port_t* port, jack_nframes_t frames) +LIB_EXPORT void* jack_port_get_buffer(jack_port_t* port, jack_nframes_t frames) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_get_buffer"); @@ -349,7 +349,7 @@ EXPORT void* jack_port_get_buffer(jack_port_t* port, jack_nframes_t frames) } } -EXPORT const char* jack_port_name(const jack_port_t* port) +LIB_EXPORT const char* jack_port_name(const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_name"); @@ -365,7 +365,7 @@ EXPORT const char* jack_port_name(const jack_port_t* port) } } -EXPORT const char* jack_port_short_name(const jack_port_t* port) +LIB_EXPORT const char* jack_port_short_name(const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_short_name"); @@ -381,7 +381,7 @@ EXPORT const char* jack_port_short_name(const jack_port_t* port) } } -EXPORT int jack_port_flags(const jack_port_t* port) +LIB_EXPORT int jack_port_flags(const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_flags"); @@ -397,7 +397,7 @@ EXPORT int jack_port_flags(const jack_port_t* port) } } -EXPORT const char* jack_port_type(const jack_port_t* port) +LIB_EXPORT const char* jack_port_type(const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_type"); @@ -413,7 +413,7 @@ EXPORT const char* jack_port_type(const jack_port_t* port) } } -EXPORT jack_port_type_id_t jack_port_type_id(const jack_port_t *port) +LIB_EXPORT jack_port_type_id_t jack_port_type_id(const jack_port_t *port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_type_id"); @@ -429,7 +429,7 @@ EXPORT jack_port_type_id_t jack_port_type_id(const jack_port_t *port) } } -EXPORT int jack_port_connected(const jack_port_t* port) +LIB_EXPORT int jack_port_connected(const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_connected"); @@ -446,7 +446,7 @@ EXPORT int jack_port_connected(const jack_port_t* port) } } -EXPORT int jack_port_connected_to(const jack_port_t* port, const char* port_name) +LIB_EXPORT int jack_port_connected_to(const jack_port_t* port, const char* port_name) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_connected_to"); @@ -472,7 +472,7 @@ EXPORT int jack_port_connected_to(const jack_port_t* port, const char* port_name } } -EXPORT int jack_port_tie(jack_port_t* src, jack_port_t* dst) +LIB_EXPORT int jack_port_tie(jack_port_t* src, jack_port_t* dst) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_tie"); @@ -498,7 +498,7 @@ EXPORT int jack_port_tie(jack_port_t* src, jack_port_t* dst) } } -EXPORT int jack_port_untie(jack_port_t* port) +LIB_EXPORT int jack_port_untie(jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_untie"); @@ -514,7 +514,7 @@ EXPORT int jack_port_untie(jack_port_t* port) } } -EXPORT jack_nframes_t jack_port_get_latency(jack_port_t* port) +LIB_EXPORT jack_nframes_t jack_port_get_latency(jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_get_latency"); @@ -531,7 +531,7 @@ EXPORT jack_nframes_t jack_port_get_latency(jack_port_t* port) } } -EXPORT void jack_port_set_latency(jack_port_t* port, jack_nframes_t frames) +LIB_EXPORT void jack_port_set_latency(jack_port_t* port, jack_nframes_t frames) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_set_latency"); @@ -547,7 +547,7 @@ EXPORT void jack_port_set_latency(jack_port_t* port, jack_nframes_t frames) } } -EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) +LIB_EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_get_latency_range"); @@ -564,7 +564,7 @@ EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback } } -EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) +LIB_EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_set_latency_range"); @@ -581,7 +581,7 @@ EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback } } -EXPORT int jack_recompute_total_latency(jack_client_t* ext_client, jack_port_t* port) +LIB_EXPORT int jack_recompute_total_latency(jack_client_t* ext_client, jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_recompute_total_latency"); @@ -603,7 +603,7 @@ EXPORT int jack_recompute_total_latency(jack_client_t* ext_client, jack_port_t* } } -EXPORT int jack_recompute_total_latencies(jack_client_t* ext_client) +LIB_EXPORT int jack_recompute_total_latencies(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_recompute_total_latencies"); @@ -618,11 +618,7 @@ EXPORT int jack_recompute_total_latencies(jack_client_t* ext_client) } } -/* -This is unsafe if case of concurrent access, and should be "serialized" doing a server call. -*/ - -EXPORT int jack_port_set_name(jack_port_t* port, const char* name) +LIB_EXPORT int jack_port_set_name(jack_port_t* port, const char* name) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_set_name"); @@ -636,19 +632,18 @@ EXPORT int jack_port_set_name(jack_port_t* port, const char* name) jack_error("jack_port_set_name called with a NULL port name"); return -1; } else { - JackGraphManager* manager = GetGraphManager(); - int refnum; - if (manager && ((refnum = manager->GetPort(myport)->GetRefNum()) > 0)) { - JackClient* client = JackGlobals::fClientTable[refnum]; - assert(client); - return client->PortRename(myport, name); - } else { - return -1; + JackClient* client = NULL; + for (int i = 0; i < CLIENT_NUM; i++) { + // Find a valid client + if ((client = JackGlobals::fClientTable[i])) { + break; + } } + return (client) ? client->PortRename(myport, name) : -1; } } -EXPORT int jack_port_set_alias(jack_port_t* port, const char* name) +LIB_EXPORT int jack_port_set_alias(jack_port_t* port, const char* name) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_set_alias"); @@ -667,7 +662,7 @@ EXPORT int jack_port_set_alias(jack_port_t* port, const char* name) } } -EXPORT int jack_port_unset_alias(jack_port_t* port, const char* name) +LIB_EXPORT int jack_port_unset_alias(jack_port_t* port, const char* name) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_unset_alias"); @@ -686,7 +681,7 @@ EXPORT int jack_port_unset_alias(jack_port_t* port, const char* name) } } -EXPORT int jack_port_get_aliases(const jack_port_t* port, char* const aliases[2]) +LIB_EXPORT int jack_port_get_aliases(const jack_port_t* port, char* const aliases[2]) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_get_aliases"); @@ -702,7 +697,7 @@ EXPORT int jack_port_get_aliases(const jack_port_t* port, char* const aliases[2] } } -EXPORT int jack_port_request_monitor(jack_port_t* port, int onoff) +LIB_EXPORT int jack_port_request_monitor(jack_port_t* port, int onoff) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_request_monitor"); @@ -718,7 +713,7 @@ EXPORT int jack_port_request_monitor(jack_port_t* port, int onoff) } } -EXPORT int jack_port_request_monitor_by_name(jack_client_t* ext_client, const char* port_name, int onoff) +LIB_EXPORT int jack_port_request_monitor_by_name(jack_client_t* ext_client, const char* port_name, int onoff) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_request_monitor_by_name"); @@ -741,7 +736,7 @@ EXPORT int jack_port_request_monitor_by_name(jack_client_t* ext_client, const ch } } -EXPORT int jack_port_ensure_monitor(jack_port_t* port, int onoff) +LIB_EXPORT int jack_port_ensure_monitor(jack_port_t* port, int onoff) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_ensure_monitor"); @@ -757,7 +752,7 @@ EXPORT int jack_port_ensure_monitor(jack_port_t* port, int onoff) } } -EXPORT int jack_port_monitoring_input(jack_port_t* port) +LIB_EXPORT int jack_port_monitoring_input(jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_monitoring_input"); @@ -773,7 +768,7 @@ EXPORT int jack_port_monitoring_input(jack_port_t* port) } } -EXPORT int jack_is_realtime(jack_client_t* ext_client) +LIB_EXPORT int jack_is_realtime(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_is_realtime"); @@ -788,7 +783,7 @@ EXPORT int jack_is_realtime(jack_client_t* ext_client) } } -EXPORT void jack_on_shutdown(jack_client_t* ext_client, JackShutdownCallback callback, void* arg) +LIB_EXPORT void jack_on_shutdown(jack_client_t* ext_client, JackShutdownCallback callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_on_shutdown"); @@ -801,7 +796,7 @@ EXPORT void jack_on_shutdown(jack_client_t* ext_client, JackShutdownCallback cal } } -EXPORT void jack_on_info_shutdown(jack_client_t* ext_client, JackInfoShutdownCallback callback, void* arg) +LIB_EXPORT void jack_on_info_shutdown(jack_client_t* ext_client, JackInfoShutdownCallback callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_on_info_shutdown"); @@ -814,7 +809,7 @@ EXPORT void jack_on_info_shutdown(jack_client_t* ext_client, JackInfoShutdownCal } } -EXPORT int jack_set_process_callback(jack_client_t* ext_client, JackProcessCallback callback, void* arg) +LIB_EXPORT int jack_set_process_callback(jack_client_t* ext_client, JackProcessCallback callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_process_callback"); @@ -828,7 +823,7 @@ EXPORT int jack_set_process_callback(jack_client_t* ext_client, JackProcessCallb } } -EXPORT jack_nframes_t jack_thread_wait(jack_client_t* ext_client, int status) +LIB_EXPORT jack_nframes_t jack_thread_wait(jack_client_t* ext_client, int status) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_thread_wait"); @@ -843,7 +838,7 @@ EXPORT jack_nframes_t jack_thread_wait(jack_client_t* ext_client, int status) } } -EXPORT jack_nframes_t jack_cycle_wait(jack_client_t* ext_client) +LIB_EXPORT jack_nframes_t jack_cycle_wait(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_cycle_wait"); @@ -857,7 +852,7 @@ EXPORT jack_nframes_t jack_cycle_wait(jack_client_t* ext_client) } } -EXPORT void jack_cycle_signal(jack_client_t* ext_client, int status) +LIB_EXPORT void jack_cycle_signal(jack_client_t* ext_client, int status) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_cycle_signal"); @@ -870,7 +865,7 @@ EXPORT void jack_cycle_signal(jack_client_t* ext_client, int status) } } -EXPORT int jack_set_process_thread(jack_client_t* ext_client, JackThreadCallback fun, void *arg) +LIB_EXPORT int jack_set_process_thread(jack_client_t* ext_client, JackThreadCallback fun, void *arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_process_thread"); @@ -884,7 +879,7 @@ EXPORT int jack_set_process_thread(jack_client_t* ext_client, JackThreadCallback } } -EXPORT int jack_set_freewheel_callback(jack_client_t* ext_client, JackFreewheelCallback freewheel_callback, void* arg) +LIB_EXPORT int jack_set_freewheel_callback(jack_client_t* ext_client, JackFreewheelCallback freewheel_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_freewheel_callback"); @@ -898,7 +893,7 @@ EXPORT int jack_set_freewheel_callback(jack_client_t* ext_client, JackFreewheelC } } -EXPORT int jack_set_freewheel(jack_client_t* ext_client, int onoff) +LIB_EXPORT int jack_set_freewheel(jack_client_t* ext_client, int onoff) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_freewheel"); @@ -912,7 +907,7 @@ EXPORT int jack_set_freewheel(jack_client_t* ext_client, int onoff) } } -EXPORT int jack_set_buffer_size(jack_client_t* ext_client, jack_nframes_t buffer_size) +LIB_EXPORT int jack_set_buffer_size(jack_client_t* ext_client, jack_nframes_t buffer_size) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_buffer_size"); @@ -928,7 +923,7 @@ EXPORT int jack_set_buffer_size(jack_client_t* ext_client, jack_nframes_t buffer } } -EXPORT int jack_set_buffer_size_callback(jack_client_t* ext_client, JackBufferSizeCallback bufsize_callback, void* arg) +LIB_EXPORT int jack_set_buffer_size_callback(jack_client_t* ext_client, JackBufferSizeCallback bufsize_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_buffer_size_callback"); @@ -942,7 +937,7 @@ EXPORT int jack_set_buffer_size_callback(jack_client_t* ext_client, JackBufferSi } } -EXPORT int jack_set_sample_rate_callback(jack_client_t* ext_client, JackSampleRateCallback srate_callback, void* arg) +LIB_EXPORT int jack_set_sample_rate_callback(jack_client_t* ext_client, JackSampleRateCallback srate_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_sample_rate_callback"); @@ -956,7 +951,7 @@ EXPORT int jack_set_sample_rate_callback(jack_client_t* ext_client, JackSampleRa } } -EXPORT int jack_set_client_registration_callback(jack_client_t* ext_client, JackClientRegistrationCallback registration_callback, void* arg) +LIB_EXPORT int jack_set_client_registration_callback(jack_client_t* ext_client, JackClientRegistrationCallback registration_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_client_registration_callback"); @@ -970,7 +965,7 @@ EXPORT int jack_set_client_registration_callback(jack_client_t* ext_client, Jack } } -EXPORT int jack_set_port_registration_callback(jack_client_t* ext_client, JackPortRegistrationCallback registration_callback, void* arg) +LIB_EXPORT int jack_set_port_registration_callback(jack_client_t* ext_client, JackPortRegistrationCallback registration_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_port_registration_callback"); @@ -984,7 +979,7 @@ EXPORT int jack_set_port_registration_callback(jack_client_t* ext_client, JackPo } } -EXPORT int jack_set_port_connect_callback(jack_client_t* ext_client, JackPortConnectCallback portconnect_callback, void* arg) +LIB_EXPORT int jack_set_port_connect_callback(jack_client_t* ext_client, JackPortConnectCallback portconnect_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_port_connect_callback"); @@ -998,7 +993,7 @@ EXPORT int jack_set_port_connect_callback(jack_client_t* ext_client, JackPortCon } } -EXPORT int jack_set_port_rename_callback(jack_client_t* ext_client, JackPortRenameCallback rename_callback, void* arg) +LIB_EXPORT int jack_set_port_rename_callback(jack_client_t* ext_client, JackPortRenameCallback rename_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_port_rename_callback"); @@ -1012,7 +1007,7 @@ EXPORT int jack_set_port_rename_callback(jack_client_t* ext_client, JackPortRena } } -EXPORT int jack_set_graph_order_callback(jack_client_t* ext_client, JackGraphOrderCallback graph_callback, void* arg) +LIB_EXPORT int jack_set_graph_order_callback(jack_client_t* ext_client, JackGraphOrderCallback graph_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_graph_order_callback"); @@ -1027,7 +1022,7 @@ EXPORT int jack_set_graph_order_callback(jack_client_t* ext_client, JackGraphOrd } } -EXPORT int jack_set_xrun_callback(jack_client_t* ext_client, JackXRunCallback xrun_callback, void* arg) +LIB_EXPORT int jack_set_xrun_callback(jack_client_t* ext_client, JackXRunCallback xrun_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_xrun_callback"); @@ -1041,7 +1036,7 @@ EXPORT int jack_set_xrun_callback(jack_client_t* ext_client, JackXRunCallback xr } } -EXPORT int jack_set_latency_callback(jack_client_t* ext_client, JackLatencyCallback latency_callback, void *arg) +LIB_EXPORT int jack_set_latency_callback(jack_client_t* ext_client, JackLatencyCallback latency_callback, void *arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_latency_callback"); @@ -1055,7 +1050,7 @@ EXPORT int jack_set_latency_callback(jack_client_t* ext_client, JackLatencyCallb } } -EXPORT int jack_set_thread_init_callback(jack_client_t* ext_client, JackThreadInitCallback init_callback, void *arg) +LIB_EXPORT int jack_set_thread_init_callback(jack_client_t* ext_client, JackThreadInitCallback init_callback, void *arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_thread_init_callback"); @@ -1070,7 +1065,7 @@ EXPORT int jack_set_thread_init_callback(jack_client_t* ext_client, JackThreadIn } } -EXPORT int jack_activate(jack_client_t* ext_client) +LIB_EXPORT int jack_activate(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_activate"); @@ -1084,7 +1079,7 @@ EXPORT int jack_activate(jack_client_t* ext_client) } } -EXPORT int jack_deactivate(jack_client_t* ext_client) +LIB_EXPORT int jack_deactivate(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_deactivate"); @@ -1098,7 +1093,7 @@ EXPORT int jack_deactivate(jack_client_t* ext_client) } } -EXPORT jack_port_t* jack_port_register(jack_client_t* ext_client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) +LIB_EXPORT jack_port_t* jack_port_register(jack_client_t* ext_client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_register"); @@ -1115,7 +1110,7 @@ EXPORT jack_port_t* jack_port_register(jack_client_t* ext_client, const char* po } } -EXPORT int jack_port_unregister(jack_client_t* ext_client, jack_port_t* port) +LIB_EXPORT int jack_port_unregister(jack_client_t* ext_client, jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_unregister"); @@ -1134,7 +1129,7 @@ EXPORT int jack_port_unregister(jack_client_t* ext_client, jack_port_t* port) return client->PortUnRegister(myport); } -EXPORT int jack_port_is_mine(const jack_client_t* ext_client, const jack_port_t* port) +LIB_EXPORT int jack_port_is_mine(const jack_client_t* ext_client, const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_is_mine"); @@ -1153,7 +1148,7 @@ EXPORT int jack_port_is_mine(const jack_client_t* ext_client, const jack_port_t* return client->PortIsMine(myport); } -EXPORT const char** jack_port_get_connections(const jack_port_t* port) +LIB_EXPORT const char** jack_port_get_connections(const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_get_connections"); @@ -1171,7 +1166,7 @@ EXPORT const char** jack_port_get_connections(const jack_port_t* port) } // Calling client does not need to "own" the port -EXPORT const char** jack_port_get_all_connections(const jack_client_t* ext_client, const jack_port_t* port) +LIB_EXPORT const char** jack_port_get_all_connections(const jack_client_t* ext_client, const jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_get_all_connections"); @@ -1194,7 +1189,7 @@ EXPORT const char** jack_port_get_all_connections(const jack_client_t* ext_clien } } -EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t* ext_client, jack_port_t* port) +LIB_EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t* ext_client, jack_port_t* port) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_get_total_latency"); @@ -1222,7 +1217,7 @@ EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t* ext_client, jac } } -EXPORT int jack_connect(jack_client_t* ext_client, const char* src, const char* dst) +LIB_EXPORT int jack_connect(jack_client_t* ext_client, const char* src, const char* dst) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_connect"); @@ -1239,7 +1234,7 @@ EXPORT int jack_connect(jack_client_t* ext_client, const char* src, const char* } } -EXPORT int jack_disconnect(jack_client_t* ext_client, const char* src, const char* dst) +LIB_EXPORT int jack_disconnect(jack_client_t* ext_client, const char* src, const char* dst) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_disconnect"); @@ -1256,7 +1251,7 @@ EXPORT int jack_disconnect(jack_client_t* ext_client, const char* src, const cha } } -EXPORT int jack_port_disconnect(jack_client_t* ext_client, jack_port_t* src) +LIB_EXPORT int jack_port_disconnect(jack_client_t* ext_client, jack_port_t* src) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_disconnect"); @@ -1275,7 +1270,7 @@ EXPORT int jack_port_disconnect(jack_client_t* ext_client, jack_port_t* src) return client->PortDisconnect(myport); } -EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t* ext_client) +LIB_EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_sample_rate"); @@ -1290,7 +1285,7 @@ EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t* ext_client) } } -EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t* ext_client) +LIB_EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_buffer_size"); @@ -1305,7 +1300,7 @@ EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t* ext_client) } } -EXPORT const char** jack_get_ports(jack_client_t* ext_client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) +LIB_EXPORT const char** jack_get_ports(jack_client_t* ext_client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_ports"); @@ -1319,7 +1314,7 @@ EXPORT const char** jack_get_ports(jack_client_t* ext_client, const char* port_n return (manager ? manager->GetPorts(port_name_pattern, type_name_pattern, flags) : NULL); } -EXPORT jack_port_t* jack_port_by_name(jack_client_t* ext_client, const char* portname) +LIB_EXPORT jack_port_t* jack_port_by_name(jack_client_t* ext_client, const char* portname) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_by_name"); @@ -1342,7 +1337,7 @@ EXPORT jack_port_t* jack_port_by_name(jack_client_t* ext_client, const char* por } } -EXPORT jack_port_t* jack_port_by_id(jack_client_t* ext_client, jack_port_id_t id) +LIB_EXPORT jack_port_t* jack_port_by_id(jack_client_t* ext_client, jack_port_id_t id) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_by_id"); @@ -1351,7 +1346,7 @@ EXPORT jack_port_t* jack_port_by_id(jack_client_t* ext_client, jack_port_id_t id return (jack_port_t*)((uintptr_t)id); } -EXPORT int jack_engine_takeover_timebase(jack_client_t* ext_client) +LIB_EXPORT int jack_engine_takeover_timebase(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_engine_takeover_timebase"); @@ -1366,7 +1361,7 @@ EXPORT int jack_engine_takeover_timebase(jack_client_t* ext_client) } } -EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t* ext_client) +LIB_EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_frames_since_cycle_start"); @@ -1381,7 +1376,7 @@ EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t* ext_cli } } -EXPORT jack_time_t jack_get_time() +LIB_EXPORT jack_time_t jack_get_time() { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_time"); @@ -1389,7 +1384,7 @@ EXPORT jack_time_t jack_get_time() return GetMicroSeconds(); } -EXPORT jack_time_t jack_frames_to_time(const jack_client_t* ext_client, jack_nframes_t frames) +LIB_EXPORT jack_time_t jack_frames_to_time(const jack_client_t* ext_client, jack_nframes_t frames) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_frames_to_time"); @@ -1410,7 +1405,7 @@ EXPORT jack_time_t jack_frames_to_time(const jack_client_t* ext_client, jack_nfr } } -EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t* ext_client, jack_time_t time) +LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t* ext_client, jack_time_t time) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_time_to_frames"); @@ -1431,7 +1426,7 @@ EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t* ext_client, jack_ } } -EXPORT jack_nframes_t jack_frame_time(const jack_client_t* ext_client) +LIB_EXPORT jack_nframes_t jack_frame_time(const jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_frame_time"); @@ -1439,7 +1434,7 @@ EXPORT jack_nframes_t jack_frame_time(const jack_client_t* ext_client) return jack_time_to_frames(ext_client, GetMicroSeconds()); } -EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t* ext_client) +LIB_EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_last_frame_time"); @@ -1448,7 +1443,7 @@ EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t* ext_client) return (control) ? control->fFrameTimer.ReadCurrentState()->CurFrame() : 0; } -EXPORT float jack_cpu_load(jack_client_t* ext_client) +LIB_EXPORT float jack_cpu_load(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_cpu_load"); @@ -1463,7 +1458,7 @@ EXPORT float jack_cpu_load(jack_client_t* ext_client) } } -EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t* ext_client) +LIB_EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_thread_id"); @@ -1477,7 +1472,7 @@ EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t* ext_client) } } -EXPORT char* jack_get_client_name(jack_client_t* ext_client) +LIB_EXPORT char* jack_get_client_name(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_client_name"); @@ -1491,22 +1486,22 @@ EXPORT char* jack_get_client_name(jack_client_t* ext_client) } } -EXPORT int jack_client_name_size(void) +LIB_EXPORT int jack_client_name_size(void) { return JACK_CLIENT_NAME_SIZE; } -EXPORT int jack_port_name_size(void) +LIB_EXPORT int jack_port_name_size(void) { - return JACK_PORT_NAME_SIZE; + return REAL_JACK_PORT_NAME_SIZE; } -EXPORT int jack_port_type_size(void) +LIB_EXPORT int jack_port_type_size(void) { return JACK_PORT_TYPE_SIZE; } -EXPORT size_t jack_port_type_get_buffer_size(jack_client_t* ext_client, const char* port_type) +LIB_EXPORT size_t jack_port_type_get_buffer_size(jack_client_t* ext_client, const char* port_type) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_port_type_get_buffer_size"); @@ -1527,7 +1522,7 @@ EXPORT size_t jack_port_type_get_buffer_size(jack_client_t* ext_client, const ch } // transport.h -EXPORT int jack_release_timebase(jack_client_t* ext_client) +LIB_EXPORT int jack_release_timebase(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_release_timebase"); @@ -1541,7 +1536,7 @@ EXPORT int jack_release_timebase(jack_client_t* ext_client) } } -EXPORT int jack_set_sync_callback(jack_client_t* ext_client, JackSyncCallback sync_callback, void *arg) +LIB_EXPORT int jack_set_sync_callback(jack_client_t* ext_client, JackSyncCallback sync_callback, void *arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_sync_callback"); @@ -1555,7 +1550,7 @@ EXPORT int jack_set_sync_callback(jack_client_t* ext_client, JackSyncCallback sy } } -EXPORT int jack_set_sync_timeout(jack_client_t* ext_client, jack_time_t timeout) +LIB_EXPORT int jack_set_sync_timeout(jack_client_t* ext_client, jack_time_t timeout) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_sync_timeout"); @@ -1569,7 +1564,7 @@ EXPORT int jack_set_sync_timeout(jack_client_t* ext_client, jack_time_t timeout) } } -EXPORT int jack_set_timebase_callback(jack_client_t* ext_client, int conditional, JackTimebaseCallback timebase_callback, void* arg) +LIB_EXPORT int jack_set_timebase_callback(jack_client_t* ext_client, int conditional, JackTimebaseCallback timebase_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_timebase_callback"); @@ -1583,7 +1578,7 @@ EXPORT int jack_set_timebase_callback(jack_client_t* ext_client, int conditional } } -EXPORT int jack_transport_locate(jack_client_t* ext_client, jack_nframes_t frame) +LIB_EXPORT int jack_transport_locate(jack_client_t* ext_client, jack_nframes_t frame) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_transport_locate"); @@ -1598,7 +1593,7 @@ EXPORT int jack_transport_locate(jack_client_t* ext_client, jack_nframes_t frame } } -EXPORT jack_transport_state_t jack_transport_query(const jack_client_t* ext_client, jack_position_t* pos) +LIB_EXPORT jack_transport_state_t jack_transport_query(const jack_client_t* ext_client, jack_position_t* pos) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_transport_query"); @@ -1612,7 +1607,7 @@ EXPORT jack_transport_state_t jack_transport_query(const jack_client_t* ext_clie } } -EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t* ext_client) +LIB_EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_current_transport_frame"); @@ -1626,7 +1621,7 @@ EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t* ext_ } } -EXPORT int jack_transport_reposition(jack_client_t* ext_client, jack_position_t* pos) +LIB_EXPORT int jack_transport_reposition(jack_client_t* ext_client, const jack_position_t* pos) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_transport_reposition"); @@ -1641,7 +1636,7 @@ EXPORT int jack_transport_reposition(jack_client_t* ext_client, jack_position_t* } } -EXPORT void jack_transport_start(jack_client_t* ext_client) +LIB_EXPORT void jack_transport_start(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_transport_start"); @@ -1654,7 +1649,7 @@ EXPORT void jack_transport_start(jack_client_t* ext_client) } } -EXPORT void jack_transport_stop(jack_client_t* ext_client) +LIB_EXPORT void jack_transport_stop(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_transport_stop"); @@ -1668,7 +1663,7 @@ EXPORT void jack_transport_stop(jack_client_t* ext_client) } // deprecated -EXPORT void jack_get_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) +LIB_EXPORT void jack_get_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_transport_info"); @@ -1678,7 +1673,7 @@ EXPORT void jack_get_transport_info(jack_client_t* ext_client, jack_transport_in memset(tinfo, 0, sizeof(jack_transport_info_t)); } -EXPORT void jack_set_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) +LIB_EXPORT void jack_set_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_transport_info"); @@ -1689,7 +1684,7 @@ EXPORT void jack_set_transport_info(jack_client_t* ext_client, jack_transport_in } // statistics.h -EXPORT float jack_get_max_delayed_usecs(jack_client_t* ext_client) +LIB_EXPORT float jack_get_max_delayed_usecs(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_max_delayed_usecs"); @@ -1704,7 +1699,7 @@ EXPORT float jack_get_max_delayed_usecs(jack_client_t* ext_client) } } -EXPORT float jack_get_xrun_delayed_usecs(jack_client_t* ext_client) +LIB_EXPORT float jack_get_xrun_delayed_usecs(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_xrun_delayed_usecs"); @@ -1719,7 +1714,7 @@ EXPORT float jack_get_xrun_delayed_usecs(jack_client_t* ext_client) } } -EXPORT void jack_reset_max_delayed_usecs(jack_client_t* ext_client) +LIB_EXPORT void jack_reset_max_delayed_usecs(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_reset_max_delayed_usecs"); @@ -1734,7 +1729,7 @@ EXPORT void jack_reset_max_delayed_usecs(jack_client_t* ext_client) } // thread.h -EXPORT int jack_client_real_time_priority(jack_client_t* ext_client) +LIB_EXPORT int jack_client_real_time_priority(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_real_time_priority"); @@ -1749,7 +1744,7 @@ EXPORT int jack_client_real_time_priority(jack_client_t* ext_client) } } -EXPORT int jack_client_max_real_time_priority(jack_client_t* ext_client) +LIB_EXPORT int jack_client_max_real_time_priority(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_max_real_time_priority"); @@ -1760,17 +1755,19 @@ EXPORT int jack_client_max_real_time_priority(jack_client_t* ext_client) return -1; } else { JackEngineControl* control = GetEngineControl(); - return (control->fRealTime) ? control->fMaxClientPriority : -1; + return (control->fRealTime) ? control->fMaxClientPriority : -1; } } -EXPORT int jack_acquire_real_time_scheduling(jack_native_thread_t thread, int priority) +LIB_EXPORT int jack_acquire_real_time_scheduling(jack_native_thread_t thread, int priority) { JackEngineControl* control = GetEngineControl(); - return (control ? JackThread::AcquireRealTimeImp(thread, priority, GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint) : -1); + return (control + ? JackThread::AcquireRealTimeImp(thread, priority, control->fPeriod, control->fComputation, control->fConstraint) + : -1); } -EXPORT int jack_client_create_thread(jack_client_t* client, +LIB_EXPORT int jack_client_create_thread(jack_client_t* client, jack_native_thread_t *thread, int priority, int realtime, /* boolean */ @@ -1780,15 +1777,19 @@ EXPORT int jack_client_create_thread(jack_client_t* client, #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_create_thread"); #endif - return JackThread::StartImp(thread, priority, realtime, routine, arg); + JackEngineControl* control = GetEngineControl(); + int res = JackThread::StartImp(thread, priority, realtime, routine, arg); + return (res == 0) + ? ((realtime ? JackThread::AcquireRealTimeImp(*thread, priority, control->fPeriod, control->fComputation, control->fConstraint) : res)) + : res; } -EXPORT int jack_drop_real_time_scheduling(jack_native_thread_t thread) +LIB_EXPORT int jack_drop_real_time_scheduling(jack_native_thread_t thread) { return JackThread::DropRealTimeImp(thread); } -EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) +LIB_EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_stop_thread"); @@ -1796,7 +1797,7 @@ EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t t return JackThread::StopImp(thread); } -EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) +LIB_EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_kill_thread"); @@ -1805,27 +1806,33 @@ EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t t } #ifndef WIN32 -EXPORT void jack_set_thread_creator (jack_thread_creator_t jtc) +LIB_EXPORT void jack_set_thread_creator (jack_thread_creator_t jtc) { JackGlobals::fJackThreadCreator = (jtc == NULL) ? pthread_create : jtc; } #endif // intclient.h -EXPORT int jack_internal_client_new (const char* client_name, +LIB_EXPORT int jack_internal_client_new (const char* client_name, const char* load_name, const char* load_init) { +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_internal_client_new"); +#endif jack_error("jack_internal_client_new: deprecated"); return -1; } -EXPORT void jack_internal_client_close (const char* client_name) +LIB_EXPORT void jack_internal_client_close (const char* client_name) { +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_internal_client_close"); +#endif jack_error("jack_internal_client_close: deprecated"); } -EXPORT char* jack_get_internal_client_name(jack_client_t* ext_client, jack_intclient_t intclient) +LIB_EXPORT char* jack_get_internal_client_name(jack_client_t* ext_client, jack_intclient_t intclient) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_internal_client_name"); @@ -1842,7 +1849,7 @@ EXPORT char* jack_get_internal_client_name(jack_client_t* ext_client, jack_intcl } } -EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t* ext_client, const char* client_name, jack_status_t* status) +LIB_EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t* ext_client, const char* client_name, jack_status_t* status) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_internal_client_handle"); @@ -1860,7 +1867,7 @@ EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t* ext_client, c } } -EXPORT jack_intclient_t jack_internal_client_load_aux(jack_client_t* ext_client, const char* client_name, jack_options_t options, jack_status_t* status, va_list ap) +LIB_EXPORT jack_intclient_t jack_internal_client_load_aux(jack_client_t* ext_client, const char* client_name, jack_options_t options, jack_status_t* status, va_list ap) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_internal_client_load_aux"); @@ -1873,7 +1880,7 @@ EXPORT jack_intclient_t jack_internal_client_load_aux(jack_client_t* ext_client, jack_varargs_t va; jack_status_t my_status; - if (status == NULL) /* no status from caller? */ + if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; @@ -1890,8 +1897,11 @@ EXPORT jack_intclient_t jack_internal_client_load_aux(jack_client_t* ext_client, } } -EXPORT jack_intclient_t jack_internal_client_load(jack_client_t *client, const char* client_name, jack_options_t options, jack_status_t *status, ...) +LIB_EXPORT jack_intclient_t jack_internal_client_load(jack_client_t *client, const char* client_name, jack_options_t options, jack_status_t *status, ...) { +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_internal_client_load"); +#endif va_list ap; va_start(ap, status); jack_intclient_t res = jack_internal_client_load_aux(client, client_name, options, status, ap); @@ -1899,7 +1909,7 @@ EXPORT jack_intclient_t jack_internal_client_load(jack_client_t *client, const c return res; } -EXPORT jack_status_t jack_internal_client_unload(jack_client_t* ext_client, jack_intclient_t intclient) +LIB_EXPORT jack_status_t jack_internal_client_unload(jack_client_t* ext_client, jack_intclient_t intclient) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_internal_client_load"); @@ -1918,14 +1928,14 @@ EXPORT jack_status_t jack_internal_client_unload(jack_client_t* ext_client, jack } } -EXPORT -void -jack_get_version( - int *major_ptr, - int *minor_ptr, - int *micro_ptr, - int *proto_ptr) +LIB_EXPORT void jack_get_version(int *major_ptr, + int *minor_ptr, + int *micro_ptr, + int *proto_ptr) { +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_get_version"); +#endif // FIXME: We need these comming from build system *major_ptr = 0; *minor_ptr = 0; @@ -1933,22 +1943,26 @@ jack_get_version( *proto_ptr = 0; } -EXPORT -const char* -jack_get_version_string() +LIB_EXPORT const char* jack_get_version_string() { +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_get_version_string"); +#endif return VERSION; } -EXPORT void jack_free(void* ptr) +LIB_EXPORT void jack_free(void* ptr) { +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_free"); +#endif if (ptr) { free(ptr); } } // session.h -EXPORT int jack_set_session_callback(jack_client_t* ext_client, JackSessionCallback session_callback, void* arg) +LIB_EXPORT int jack_set_session_callback(jack_client_t* ext_client, JackSessionCallback session_callback, void* arg) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_set_session_callback"); @@ -1963,7 +1977,7 @@ EXPORT int jack_set_session_callback(jack_client_t* ext_client, JackSessionCallb } } -EXPORT jack_session_command_t *jack_session_notify(jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path) +LIB_EXPORT jack_session_command_t* jack_session_notify(jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_session_notify"); @@ -1978,7 +1992,7 @@ EXPORT jack_session_command_t *jack_session_notify(jack_client_t* ext_client, co } } -EXPORT int jack_session_reply(jack_client_t* ext_client, jack_session_event_t *event) +LIB_EXPORT int jack_session_reply(jack_client_t* ext_client, jack_session_event_t *event) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_session_reply"); @@ -1993,8 +2007,11 @@ EXPORT int jack_session_reply(jack_client_t* ext_client, jack_session_event_t *e } } -EXPORT void jack_session_event_free(jack_session_event_t* ev) +LIB_EXPORT void jack_session_event_free(jack_session_event_t* ev) { +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_session_event_free"); +#endif if (ev) { if (ev->session_dir) free((void *)ev->session_dir); @@ -2006,7 +2023,23 @@ EXPORT void jack_session_event_free(jack_session_event_t* ev) } } -EXPORT char *jack_get_uuid_for_client_name(jack_client_t* ext_client, const char* client_name) +LIB_EXPORT char *jack_client_get_uuid(jack_client_t* ext_client) +{ +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_client_get_uuid"); +#endif + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_client_get_uuid called with a NULL client"); + return NULL; + } else { + char retval[16]; + snprintf(retval, sizeof(retval), "%d", client->GetClientControl()->fSessionID); + return strdup(retval); + } +} + +LIB_EXPORT char* jack_get_uuid_for_client_name(jack_client_t* ext_client, const char* client_name) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_uuid_for_client_name"); @@ -2021,7 +2054,7 @@ EXPORT char *jack_get_uuid_for_client_name(jack_client_t* ext_client, const char } } -EXPORT char *jack_get_client_name_by_uuid(jack_client_t* ext_client, const char* client_uuid) +LIB_EXPORT char* jack_get_client_name_by_uuid(jack_client_t* ext_client, const char* client_uuid) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_get_client_name_by_uuid"); @@ -2036,7 +2069,7 @@ EXPORT char *jack_get_client_name_by_uuid(jack_client_t* ext_client, const char* } } -EXPORT int jack_reserve_client_name(jack_client_t* ext_client, const char* client_name, const char* uuid) +LIB_EXPORT int jack_reserve_client_name(jack_client_t* ext_client, const char* client_name, const char* uuid) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_reserve_client_name"); @@ -2051,21 +2084,29 @@ EXPORT int jack_reserve_client_name(jack_client_t* ext_client, const char* clien } } -EXPORT void jack_session_commands_free(jack_session_command_t *cmds) +LIB_EXPORT void jack_session_commands_free(jack_session_command_t *cmds) { - if (!cmds) +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_session_commands_free"); +#endif + + if (!cmds) { return; + } int i = 0; while (1) { - if (cmds[i].client_name) + if (cmds[i].client_name) { free ((char *)cmds[i].client_name); - if (cmds[i].command) + } + if (cmds[i].command) { free ((char *)cmds[i].command); - if (cmds[i].uuid) + } + if (cmds[i].uuid) { free ((char *)cmds[i].uuid); - else + } else { break; + } i += 1; } @@ -2073,7 +2114,7 @@ EXPORT void jack_session_commands_free(jack_session_command_t *cmds) free(cmds); } -EXPORT int jack_client_has_session_callback(jack_client_t* ext_client, const char* client_name) +LIB_EXPORT int jack_client_has_session_callback(jack_client_t* ext_client, const char* client_name) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_has_session_callback"); diff --git a/common/JackActivationCount.cpp b/common/JackActivationCount.cpp index 1b266405..b7bde1f0 100644 --- a/common/JackActivationCount.cpp +++ b/common/JackActivationCount.cpp @@ -12,7 +12,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -30,7 +30,7 @@ bool JackActivationCount::Signal(JackSynchro* synchro, JackClientControl* contro { if (fValue == 0) { // Transfer activation to next clients - jack_error("JackActivationCount::Signal value = 0 ref = %ld", control->fRefNum); + jack_log("JackActivationCount::Signal value = 0 ref = %ld", control->fRefNum); return synchro->Signal(); } else if (DEC_ATOMIC(&fValue) == 1) { return synchro->Signal(); diff --git a/common/JackActivationCount.h b/common/JackActivationCount.h index 8a22bc67..7efc031c 100644 --- a/common/JackActivationCount.h +++ b/common/JackActivationCount.h @@ -33,6 +33,7 @@ struct JackClientControl; \brief Client activation counter. */ +PRE_PACKED_STRUCTURE class JackActivationCount { diff --git a/common/JackAtomicArrayState.h b/common/JackAtomicArrayState.h index 177158da..a2c0139d 100644 --- a/common/JackAtomicArrayState.h +++ b/common/JackAtomicArrayState.h @@ -30,7 +30,7 @@ namespace Jack /*! \brief Counter for CAS */ - +PRE_PACKED_STRUCTURE struct AtomicArrayCounter { union { @@ -109,6 +109,7 @@ Requirement: // CHECK livelock +PRE_PACKED_STRUCTURE template class JackAtomicArrayState { diff --git a/common/JackAtomicState.h b/common/JackAtomicState.h index eaf164ee..1eef4e11 100644 --- a/common/JackAtomicState.h +++ b/common/JackAtomicState.h @@ -31,6 +31,7 @@ namespace Jack \brief Counter for CAS */ +PRE_PACKED_STRUCTURE struct AtomicCounter { union { @@ -84,6 +85,7 @@ struct AtomicCounter // CHECK livelock +PRE_PACKED_STRUCTURE template class JackAtomicState { diff --git a/common/JackAudioAdapter.cpp b/common/JackAudioAdapter.cpp index 9319b0f7..96b248af 100644 --- a/common/JackAudioAdapter.cpp +++ b/common/JackAudioAdapter.cpp @@ -32,158 +32,188 @@ using namespace std; namespace Jack { -//static methods *********************************************************** - int JackAudioAdapter::Process (jack_nframes_t frames, void* arg) - { - JackAudioAdapter* adapter = static_cast(arg); - jack_default_audio_sample_t* inputBuffer[adapter->fAudioAdapter->GetInputs()]; - jack_default_audio_sample_t* outputBuffer[adapter->fAudioAdapter->GetOutputs()]; - - // Always clear output - for (int i = 0; i < adapter->fAudioAdapter->GetInputs(); i++) { - inputBuffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(adapter->fCapturePortList[i], frames); - memset(inputBuffer[i], 0, frames * sizeof(jack_default_audio_sample_t)); - } - - for (int i = 0; i < adapter->fAudioAdapter->GetOutputs(); i++) { - outputBuffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(adapter->fPlaybackPortList[i], frames); - } - - adapter->fAudioAdapter->PullAndPush(inputBuffer, outputBuffer, frames); - return 0; +int JackAudioAdapter::Process(jack_nframes_t frames, void* arg) +{ + JackAudioAdapter* adapter = static_cast(arg); + jack_default_audio_sample_t* inputBuffer[adapter->fAudioAdapter->GetInputs()]; + jack_default_audio_sample_t* outputBuffer[adapter->fAudioAdapter->GetOutputs()]; + + // Always clear output + for (int i = 0; i < adapter->fAudioAdapter->GetInputs(); i++) { + inputBuffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(adapter->fCapturePortList[i], frames); + memset(inputBuffer[i], 0, frames * sizeof(jack_default_audio_sample_t)); } - int JackAudioAdapter::BufferSize ( jack_nframes_t buffer_size, void* arg ) - { - JackAudioAdapter* adapter = static_cast ( arg ); - adapter->Reset(); - adapter->fAudioAdapter->SetHostBufferSize ( buffer_size ); - return 0; + for (int i = 0; i < adapter->fAudioAdapter->GetOutputs(); i++) { + outputBuffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(adapter->fPlaybackPortList[i], frames); } - int JackAudioAdapter::SampleRate ( jack_nframes_t sample_rate, void* arg ) - { - JackAudioAdapter* adapter = static_cast ( arg ); - adapter->Reset(); - adapter->fAudioAdapter->SetHostSampleRate ( sample_rate ); - return 0; - } + adapter->fAudioAdapter->PullAndPush(inputBuffer, outputBuffer, frames); + return 0; +} + +int JackAudioAdapter::BufferSize(jack_nframes_t buffer_size, void* arg) +{ + JackAudioAdapter* adapter = static_cast(arg); + adapter->Reset(); + adapter->fAudioAdapter->SetHostBufferSize(buffer_size); + return 0; +} -//JackAudioAdapter ********************************************************* +int JackAudioAdapter::SampleRate(jack_nframes_t sample_rate, void* arg) +{ + JackAudioAdapter* adapter = static_cast(arg); + adapter->Reset(); + adapter->fAudioAdapter->SetHostSampleRate(sample_rate); + return 0; +} - JackAudioAdapter::JackAudioAdapter (jack_client_t* jack_client, JackAudioAdapterInterface* audio_io, const JSList* params, bool system) - :fJackClient(jack_client), fAudioAdapter(audio_io) - { - const JSList* node; - const jack_driver_param_t* param; - fAutoConnect = false; +void JackAudioAdapter::Latency(jack_latency_callback_mode_t mode, void* arg) +{ + JackAudioAdapter* adapter = static_cast(arg); - for (node = params; node; node = jack_slist_next(node)) { - param = (const jack_driver_param_t*) node->data; - switch (param->character) { - case 'c': - fAutoConnect = true; - break; - } + if (mode == JackCaptureLatency) { + for (int i = 0; i < adapter->fAudioAdapter->GetInputs(); i++) { + jack_latency_range_t range; + range.min = range.max = adapter->fAudioAdapter->GetInputLatency(i); + jack_port_set_latency_range(adapter->fCapturePortList[i], JackCaptureLatency, &range); + } + + } else { + for (int i = 0; i < adapter->fAudioAdapter->GetOutputs(); i++) { + jack_latency_range_t range; + range.min = range.max = adapter->fAudioAdapter->GetOutputLatency(i); + jack_port_set_latency_range(adapter->fPlaybackPortList[i], JackPlaybackLatency, &range); } } +} - JackAudioAdapter::~JackAudioAdapter() - { - // When called, Close has already been used for the client, thus ports are already unregistered. - delete fAudioAdapter; +JackAudioAdapter::JackAudioAdapter(jack_client_t* client, JackAudioAdapterInterface* audio_io, const JSList* params) + :fClient(client), fAudioAdapter(audio_io) +{ + const JSList* node; + const jack_driver_param_t* param; + fAutoConnect = false; + + for (node = params; node; node = jack_slist_next(node)) { + param = (const jack_driver_param_t*)node->data; + switch (param->character) { + case 'c': + fAutoConnect = true; + break; + } } +} - void JackAudioAdapter::FreePorts() - { - for (int i = 0; i < fAudioAdapter->GetInputs(); i++ ) - if ( fCapturePortList[i] ) - jack_port_unregister ( fJackClient, fCapturePortList[i] ); - for (int i = 0; i < fAudioAdapter->GetOutputs(); i++ ) - if ( fPlaybackPortList[i] ) - jack_port_unregister ( fJackClient, fPlaybackPortList[i] ); +JackAudioAdapter::~JackAudioAdapter() +{ + // When called, Close has already been used for the client, thus ports are already unregistered. + delete fAudioAdapter; +} - delete[] fCapturePortList; - delete[] fPlaybackPortList; +void JackAudioAdapter::FreePorts() +{ + for (int i = 0; i < fAudioAdapter->GetInputs(); i++) { + if (fCapturePortList[i]) { + jack_port_unregister(fClient, fCapturePortList[i]); + } + } + for (int i = 0; i < fAudioAdapter->GetOutputs(); i++) { + if (fPlaybackPortList[i]) { + jack_port_unregister(fClient, fPlaybackPortList[i]); + } } - void JackAudioAdapter::ConnectPorts() - { - const char **ports; + delete[] fCapturePortList; + delete[] fPlaybackPortList; +} - ports = jack_get_ports(fJackClient, NULL, NULL, JackPortIsPhysical | JackPortIsInput); - if (ports != NULL) { - for (int i = 0; i < fAudioAdapter->GetInputs() && ports[i]; i++) { - jack_connect(fJackClient,jack_port_name(fCapturePortList[i]), ports[i]); - } - free(ports); - } +void JackAudioAdapter::ConnectPorts() +{ + const char** ports; - ports = jack_get_ports(fJackClient, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); - if (ports != NULL) { - for (int i = 0; i < fAudioAdapter->GetOutputs() && ports[i]; i++) { - jack_connect(fJackClient, ports[i], jack_port_name(fPlaybackPortList[i])); - } - free(ports); + ports = jack_get_ports(fClient, NULL, NULL, JackPortIsPhysical | JackPortIsInput); + if (ports != NULL) { + for (int i = 0; i < fAudioAdapter->GetInputs() && ports[i]; i++) { + jack_connect(fClient, jack_port_name(fCapturePortList[i]), ports[i]); } + jack_free(ports); } - void JackAudioAdapter::Reset() - { - fAudioAdapter->Reset(); + ports = jack_get_ports(fClient, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); + if (ports != NULL) { + for (int i = 0; i < fAudioAdapter->GetOutputs() && ports[i]; i++) { + jack_connect(fClient, ports[i], jack_port_name(fPlaybackPortList[i])); + } + jack_free(ports); } +} - int JackAudioAdapter::Open() - { - char name[32]; - jack_log("JackAudioAdapter::Open fCaptureChannels %d fPlaybackChannels %d", fAudioAdapter->GetInputs(), fAudioAdapter->GetOutputs()); - fAudioAdapter->Create(); +void JackAudioAdapter::Reset() +{ + fAudioAdapter->Reset(); +} - //jack ports - fCapturePortList = new jack_port_t*[fAudioAdapter->GetInputs()]; - fPlaybackPortList = new jack_port_t*[fAudioAdapter->GetOutputs()]; +int JackAudioAdapter::Open() +{ + char name[32]; + jack_log("JackAudioAdapter::Open fCaptureChannels %d fPlaybackChannels %d", fAudioAdapter->GetInputs(), fAudioAdapter->GetOutputs()); + fAudioAdapter->Create(); - for (int i = 0; i < fAudioAdapter->GetInputs(); i++) - { - sprintf(name, "capture_%d", i + 1); - if ((fCapturePortList[i] = jack_port_register(fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) - goto fail; - } + //jack ports + fCapturePortList = new jack_port_t*[fAudioAdapter->GetInputs()]; + fPlaybackPortList = new jack_port_t*[fAudioAdapter->GetOutputs()]; - for (int i = 0; i < fAudioAdapter->GetOutputs(); i++) - { - sprintf(name, "playback_%d", i + 1); - if ((fPlaybackPortList[i] = jack_port_register(fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 )) == NULL) - goto fail; + for (int i = 0; i < fAudioAdapter->GetInputs(); i++) { + snprintf(name, sizeof(name), "capture_%d", i + 1); + if ((fCapturePortList[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, 0)) == NULL) { + goto fail; } + } - //callbacks and activation - if ( jack_set_process_callback ( fJackClient, Process, this ) < 0 ) - goto fail; - if ( jack_set_buffer_size_callback ( fJackClient, BufferSize, this ) < 0 ) + for (int i = 0; i < fAudioAdapter->GetOutputs(); i++) { + snprintf(name, sizeof(name), "playback_%d", i + 1); + if ((fPlaybackPortList[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, 0)) == NULL) { goto fail; - if ( jack_set_sample_rate_callback ( fJackClient, SampleRate, this ) < 0 ) - goto fail; - if ( jack_activate ( fJackClient ) < 0 ) - goto fail; - - if (fAutoConnect) - ConnectPorts(); - - // Ring buffer are now allocated.. - return fAudioAdapter->Open(); + } + } - fail: - FreePorts(); - fAudioAdapter->Destroy(); - return -1; + //callbacks and activation + if (jack_set_process_callback(fClient, Process, this) < 0) { + goto fail; + } + if (jack_set_buffer_size_callback(fClient, BufferSize, this) < 0) { + goto fail; + } + if (jack_set_sample_rate_callback(fClient, SampleRate, this) < 0) { + goto fail; + } + if (jack_set_latency_callback(fClient, Latency, this) < 0) { + goto fail; + } + if (jack_activate(fClient) < 0) { + goto fail; } - int JackAudioAdapter::Close() - { - fAudioAdapter->Close(); - fAudioAdapter->Destroy(); - return 0; + if (fAutoConnect) { + ConnectPorts(); } + // Ring buffers are now allocated... + return fAudioAdapter->Open(); + return 0; + +fail: + FreePorts(); + fAudioAdapter->Destroy(); + return -1; +} + +int JackAudioAdapter::Close() +{ + fAudioAdapter->Close(); + fAudioAdapter->Destroy(); + return 0; +} + } //namespace diff --git a/common/JackAudioAdapter.h b/common/JackAudioAdapter.h index 0eb93eed..fd7ed13e 100644 --- a/common/JackAudioAdapter.h +++ b/common/JackAudioAdapter.h @@ -34,14 +34,15 @@ namespace Jack { private: - static int Process ( jack_nframes_t, void* arg ); - static int BufferSize ( jack_nframes_t buffer_size, void *arg ); - static int SampleRate ( jack_nframes_t sample_rate, void *arg ); + static int Process(jack_nframes_t, void* arg); + static int BufferSize(jack_nframes_t buffer_size, void* arg); + static int SampleRate(jack_nframes_t sample_rate, void* arg); + static void Latency(jack_latency_callback_mode_t mode, void* arg); jack_port_t** fCapturePortList; jack_port_t** fPlaybackPortList; - jack_client_t* fJackClient; + jack_client_t* fClient; JackAudioAdapterInterface* fAudioAdapter; bool fAutoConnect; @@ -51,7 +52,7 @@ namespace Jack public: - JackAudioAdapter(jack_client_t* jack_client, JackAudioAdapterInterface* audio_io, const JSList* params = NULL, bool system = false); + JackAudioAdapter(jack_client_t* client, JackAudioAdapterInterface* audio_io, const JSList* params = NULL); ~JackAudioAdapter(); int Open(); @@ -60,4 +61,7 @@ namespace Jack } +#define CaptureDriverFlags static_cast(JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal) +#define PlaybackDriverFlags static_cast(JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal) + #endif diff --git a/common/JackAudioAdapterFactory.cpp b/common/JackAudioAdapterFactory.cpp index 14266854..3c7b0879 100644 --- a/common/JackAudioAdapterFactory.cpp +++ b/common/JackAudioAdapterFactory.cpp @@ -53,7 +53,7 @@ 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 { #ifdef __linux__ @@ -71,17 +71,16 @@ extern "C" #if defined(__sun__) || defined(sun) adapter = new Jack::JackAudioAdapter(jack_client, new Jack::JackOSSAdapter(buffer_size, sample_rate, params)); #endif - - assert(adapter); - - if (adapter->Open() == 0) + + assert(adapter); + + if (adapter->Open() == 0) { return 0; - else - { + } else { delete adapter; return 1; } - + } catch (...) { return 1; } @@ -94,13 +93,14 @@ extern "C" int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); - Jack::JackArgParser parser ( load_init ); - if ( parser.GetArgc() > 0 ) - parse_params = parser.ParseParams ( desc, ¶ms ); + Jack::JackArgParser parser(load_init); + if (parser.GetArgc() > 0) { + parse_params = parser.ParseParams(desc, ¶ms); + } if (parse_params) { - res = jack_internal_initialize ( jack_client, params ); - parser.FreeParams ( params ); + res = jack_internal_initialize(jack_client, params); + parser.FreeParams(params); } return res; } diff --git a/common/JackAudioAdapterInterface.cpp b/common/JackAudioAdapterInterface.cpp index 738363b6..6beeed5e 100644 --- a/common/JackAudioAdapterInterface.cpp +++ b/common/JackAudioAdapterInterface.cpp @@ -17,9 +17,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef __APPLE__ +#include +#endif + #include "JackAudioAdapter.h" +#ifndef MY_TARGET_OS_IPHONE #include "JackLibSampleRateResampler.h" -#include "JackTime.h" +#endif +#include "JackTime.h" #include namespace Jack @@ -40,12 +46,10 @@ namespace Jack void MeasureTable::Save(unsigned int fHostBufferSize, unsigned int fHostSampleRate, unsigned int fAdaptedSampleRate, unsigned int fAdaptedBufferSize) { - char buffer[1024]; FILE* file = fopen("JackAudioAdapter.log", "w"); int max = (fCount) % TABLE_MAX - 1; - for (int i = 1; i < max; i++) - { + for (int i = 1; i < max; i++) { fprintf(file, "%d \t %d \t %d \t %f \t %f \t %d \t %d \n", fTable[i].delta, fTable[i].time1, fTable[i].time2, fTable[i].r1, fTable[i].r2, fTable[i].pos1, fTable[i].pos2); @@ -62,15 +66,13 @@ namespace Jack fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); - sprintf(buffer, "\"JackAudioAdapter.log\" using 2 title \"Ringbuffer error\" with lines,"); - fprintf(file, buffer); - sprintf(buffer, "\"JackAudioAdapter.log\" using 3 title \"Ringbuffer error with timing correction\" with lines"); - fprintf(file, buffer); - - fprintf(file, "\n unset multiplot\n"); + fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Ringbuffer error\" with lines,"); + fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Ringbuffer error with timing correction\" with lines"); + + fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming1.svg\n"); fprintf(file, "set terminal svg\n"); - + fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" @@ -78,15 +80,13 @@ namespace Jack fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); - sprintf(buffer, "\"JackAudioAdapter.log\" using 2 title \"Consumer interrupt period\" with lines,"); - fprintf(file, buffer); - sprintf(buffer, "\"JackAudioAdapter.log\" using 3 title \"Producer interrupt period\" with lines\n"); - fprintf(file, buffer); + fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Consumer interrupt period\" with lines,"); + fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Producer interrupt period\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); - + fclose(file); - + // Adapter timing 2 file = fopen("AdapterTiming2.plot", "w"); fprintf(file, "set multiplot\n"); @@ -96,15 +96,13 @@ namespace Jack fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"resampling ratio\"\n"); fprintf(file, "plot "); - sprintf(buffer, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); - fprintf(file, buffer); - sprintf(buffer, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines"); - fprintf(file, buffer); - - fprintf(file, "\n unset multiplot\n"); + fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); + fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines"); + + fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming2.svg\n"); fprintf(file, "set terminal svg\n"); - + fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" @@ -112,13 +110,11 @@ namespace Jack fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"resampling ratio\"\n"); fprintf(file, "plot "); - sprintf(buffer, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); - fprintf(file, buffer); - sprintf(buffer, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines\n"); - fprintf(file, buffer); + fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); + fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); - + fclose(file); // Adapter timing 3 @@ -130,15 +126,13 @@ namespace Jack fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); - sprintf(buffer, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); - fprintf(file, buffer); - sprintf(buffer, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines"); - fprintf(file, buffer); - - fprintf(file, "\n unset multiplot\n"); + fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); + fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines"); + + fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming3.svg\n"); fprintf(file, "set terminal svg\n"); - + fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" @@ -146,13 +140,11 @@ namespace Jack fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); - sprintf(buffer, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); - fprintf(file, buffer); - sprintf(buffer, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines\n"); - fprintf(file, buffer); + fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); + fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); - + fclose(file); } @@ -162,47 +154,56 @@ namespace Jack { fRingbufferCurSize *= 2; } - + void JackAudioAdapterInterface::AdaptRingBufferSize() { - if (fHostBufferSize > fAdaptedBufferSize) + if (fHostBufferSize > fAdaptedBufferSize) { fRingbufferCurSize = 4 * fHostBufferSize; - else + } else { fRingbufferCurSize = 4 * fAdaptedBufferSize; + } } - + void JackAudioAdapterInterface::ResetRingBuffers() { - if (fRingbufferCurSize > DEFAULT_RB_SIZE) + if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; - - for (int i = 0; i < fCaptureChannels; i++) + } + + for (int i = 0; i < fCaptureChannels; i++) { fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); - for (int i = 0; i < fPlaybackChannels; i++) + } + for (int i = 0; i < fPlaybackChannels; i++) { fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); + } } - + void JackAudioAdapterInterface::Reset() { ResetRingBuffers(); fRunning = false; } +#ifdef MY_TARGET_OS_IPHONE + void JackAudioAdapterInterface::Create() + {} +#else void JackAudioAdapterInterface::Create() { //ringbuffers fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; - + if (fAdaptative) { AdaptRingBufferSize(); jack_info("Ringbuffer automatic adaptative mode size = %d frames", fRingbufferCurSize); } else { - if (fRingbufferCurSize > DEFAULT_RB_SIZE) + if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; + } jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize); } - + for (int i = 0; i < fCaptureChannels; i++ ) { fCaptureRingBuffer[i] = new JackLibSampleRateResampler(fQuality); fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); @@ -211,56 +212,68 @@ namespace Jack fPlaybackRingBuffer[i] = new JackLibSampleRateResampler(fQuality); fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } - - if (fCaptureChannels > 0) + + if (fCaptureChannels > 0) { jack_log("ReadSpace = %ld", fCaptureRingBuffer[0]->ReadSpace()); - if (fPlaybackChannels > 0) + } + if (fPlaybackChannels > 0) { jack_log("WriteSpace = %ld", fPlaybackRingBuffer[0]->WriteSpace()); + } } +#endif void JackAudioAdapterInterface::Destroy() { - for (int i = 0; i < fCaptureChannels; i++ ) - delete ( fCaptureRingBuffer[i] ); - for (int i = 0; i < fPlaybackChannels; i++ ) - delete ( fPlaybackRingBuffer[i] ); + for (int i = 0; i < fCaptureChannels; i++) { + delete(fCaptureRingBuffer[i]); + } + for (int i = 0; i < fPlaybackChannels; i++) { + delete (fPlaybackRingBuffer[i]); + } delete[] fCaptureRingBuffer; delete[] fPlaybackRingBuffer; } - + int JackAudioAdapterInterface::PushAndPull(float** inputBuffer, float** outputBuffer, unsigned int frames) { bool failure = false; fRunning = true; - + // Finer estimation of the position in the ringbuffer int delta_frames = (fPullAndPushTime > 0) ? (int)((float(long(GetMicroSeconds() - fPullAndPushTime)) * float(fAdaptedSampleRate)) / 1000000.f) : 0; - + double ratio = 1; - + // TODO : done like this just to avoid crash when input only or output only... - if (fCaptureChannels > 0) + if (fCaptureChannels > 0) { ratio = fPIControler.GetRatio(fCaptureRingBuffer[0]->GetError() - delta_frames); - else if (fPlaybackChannels > 0) + } else if (fPlaybackChannels > 0) { ratio = fPIControler.GetRatio(fPlaybackRingBuffer[0]->GetError() - delta_frames); - + } + #ifdef JACK_MONITOR - if (fCaptureRingBuffer[0] != NULL) + if (fCaptureRingBuffer && fCaptureRingBuffer[0] != NULL) fTable.Write(fCaptureRingBuffer[0]->GetError(), fCaptureRingBuffer[0]->GetError() - delta_frames, ratio, 1/ratio, fCaptureRingBuffer[0]->ReadSpace(), fCaptureRingBuffer[0]->ReadSpace()); #endif - + // Push/pull from ringbuffer for (int i = 0; i < fCaptureChannels; i++) { fCaptureRingBuffer[i]->SetRatio(ratio); - if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], frames) < frames) - failure = true; + if (inputBuffer[i]) { + if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], frames) < frames) { + failure = true; + } + } } for (int i = 0; i < fPlaybackChannels; i++) { fPlaybackRingBuffer[i]->SetRatio(1/ratio); - if (fPlaybackRingBuffer[i]->ReadResample(outputBuffer[i], frames) < frames) - failure = true; + if (outputBuffer[i]) { + if (fPlaybackRingBuffer[i]->ReadResample(outputBuffer[i], frames) < frames) { + failure = true; + } + } } // Reset all ringbuffers in case of failure if (failure) { @@ -276,26 +289,32 @@ namespace Jack } } - int JackAudioAdapterInterface::PullAndPush(float** inputBuffer, float** outputBuffer, unsigned int frames) + int JackAudioAdapterInterface::PullAndPush(float** inputBuffer, float** outputBuffer, unsigned int frames) { fPullAndPushTime = GetMicroSeconds(); if (!fRunning) return 0; int res = 0; - + // Push/pull from ringbuffer for (int i = 0; i < fCaptureChannels; i++) { - if (fCaptureRingBuffer[i]->Read(inputBuffer[i], frames) < frames) - res = -1; + if (inputBuffer[i]) { + if (fCaptureRingBuffer[i]->Read(inputBuffer[i], frames) < frames) { + res = -1; + } + } } for (int i = 0; i < fPlaybackChannels; i++) { - if (fPlaybackRingBuffer[i]->Write(outputBuffer[i], frames) < frames) - res = -1; + if (outputBuffer[i]) { + if (fPlaybackRingBuffer[i]->Write(outputBuffer[i], frames) < frames) { + res = -1; + } + } } - + return res; } - + } // namespace diff --git a/common/JackAudioAdapterInterface.h b/common/JackAudioAdapterInterface.h index 7a60ba23..5e6e2437 100644 --- a/common/JackAudioAdapterInterface.h +++ b/common/JackAudioAdapterInterface.h @@ -22,7 +22,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackResampler.h" #include "JackFilters.h" -#include "JackConstants.h" #include namespace Jack @@ -49,7 +48,7 @@ namespace Jack Measure fTable[TABLE_MAX]; int fCount; - MeasureTable() :fCount ( 0 ) + MeasureTable() :fCount(0) {} void Write(int time1, int time2, float r1, float r2, int pos1, int pos2); @@ -102,17 +101,35 @@ namespace Jack public: - JackAudioAdapterInterface ( jack_nframes_t buffer_size, jack_nframes_t sample_rate ): - fCaptureChannels ( 0 ), - fPlaybackChannels ( 0 ), - fHostBufferSize ( buffer_size ), - fHostSampleRate ( sample_rate ), - fAdaptedBufferSize ( buffer_size), - fAdaptedSampleRate ( sample_rate ), + JackAudioAdapterInterface(jack_nframes_t buffer_size, jack_nframes_t sample_rate, jack_nframes_t ring_buffer_size = DEFAULT_ADAPTATIVE_SIZE): + fCaptureChannels(0), + fPlaybackChannels(0), + fHostBufferSize(buffer_size), + fHostSampleRate(sample_rate), + fAdaptedBufferSize(buffer_size), + fAdaptedSampleRate(sample_rate), fPIControler(sample_rate / sample_rate, 256), fCaptureRingBuffer(NULL), fPlaybackRingBuffer(NULL), fQuality(0), - fRingbufferCurSize(DEFAULT_ADAPTATIVE_SIZE), + fRingbufferCurSize(ring_buffer_size), + fPullAndPushTime(0), + fRunning(false), + fAdaptative(true) + {} + JackAudioAdapterInterface(jack_nframes_t host_buffer_size, + jack_nframes_t host_sample_rate, + jack_nframes_t adapted_buffer_size, + jack_nframes_t adapted_sample_rate, + jack_nframes_t ring_buffer_size = DEFAULT_ADAPTATIVE_SIZE) : + fCaptureChannels(0), + fPlaybackChannels(0), + fHostBufferSize(host_buffer_size), + fHostSampleRate(host_sample_rate), + fAdaptedBufferSize(adapted_buffer_size), + fAdaptedSampleRate(adapted_sample_rate), + fPIControler(host_sample_rate / host_sample_rate, 256), + fQuality(0), + fRingbufferCurSize(ring_buffer_size), fPullAndPushTime(0), fRunning(false), fAdaptative(true) @@ -123,8 +140,8 @@ namespace Jack virtual void Reset(); - void Create(); - void Destroy(); + virtual void Create(); + virtual void Destroy(); virtual int Open() { @@ -136,76 +153,81 @@ namespace Jack return 0; } - virtual int SetHostBufferSize ( jack_nframes_t buffer_size ) + virtual int SetHostBufferSize(jack_nframes_t buffer_size) { fHostBufferSize = buffer_size; - if (fAdaptative) + if (fAdaptative) { AdaptRingBufferSize(); + } return 0; } - virtual int SetAdaptedBufferSize ( jack_nframes_t buffer_size ) + virtual int SetAdaptedBufferSize(jack_nframes_t buffer_size) { fAdaptedBufferSize = buffer_size; - if (fAdaptative) + if (fAdaptative) { AdaptRingBufferSize(); + } return 0; } - virtual int SetBufferSize ( jack_nframes_t buffer_size ) + virtual int SetBufferSize(jack_nframes_t buffer_size) { - SetHostBufferSize ( buffer_size ); - SetAdaptedBufferSize ( buffer_size ); + SetHostBufferSize(buffer_size); + SetAdaptedBufferSize(buffer_size); return 0; } - virtual int SetHostSampleRate ( jack_nframes_t sample_rate ) + virtual int SetHostSampleRate(jack_nframes_t sample_rate) { fHostSampleRate = sample_rate; fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); return 0; } - virtual int SetAdaptedSampleRate ( jack_nframes_t sample_rate ) + virtual int SetAdaptedSampleRate(jack_nframes_t sample_rate) { fAdaptedSampleRate = sample_rate; fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); return 0; } - virtual int SetSampleRate ( jack_nframes_t sample_rate ) + virtual int SetSampleRate(jack_nframes_t sample_rate) { - SetHostSampleRate ( sample_rate ); - SetAdaptedSampleRate ( sample_rate ); + SetHostSampleRate(sample_rate); + SetAdaptedSampleRate(sample_rate); return 0; } - void SetInputs ( int inputs ) + void SetInputs(int inputs) { - jack_log ( "JackAudioAdapterInterface::SetInputs %d", inputs ); + jack_log("JackAudioAdapterInterface::SetInputs %d", inputs); fCaptureChannels = inputs; } - void SetOutputs ( int outputs ) + void SetOutputs(int outputs) { - jack_log ( "JackAudioAdapterInterface::SetOutputs %d", outputs ); + jack_log("JackAudioAdapterInterface::SetOutputs %d", outputs); fPlaybackChannels = outputs; } int GetInputs() { - jack_log ( "JackAudioAdapterInterface::GetInputs %d", fCaptureChannels ); + //jack_log("JackAudioAdapterInterface::GetInputs %d", fCaptureChannels); return fCaptureChannels; } int GetOutputs() { - jack_log ( "JackAudioAdapterInterface::GetOutputs %d", fPlaybackChannels ); + //jack_log ("JackAudioAdapterInterface::GetOutputs %d", fPlaybackChannels); return fPlaybackChannels; } - int PushAndPull(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int inNumberFrames); - int PullAndPush(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int inNumberFrames); + virtual int GetInputLatency(int port_index) { return 0; } + virtual int GetOutputLatency(int port_index) { return 0; } + + int PushAndPull(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int frames); + int PullAndPush(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int frames); }; diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index 272c1e66..b44d5bc2 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -29,14 +29,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackException.h" #include +using namespace std; + namespace Jack { JackAudioDriver::JackAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) - : JackDriver(name, alias, engine, table), - fCaptureChannels(0), - fPlaybackChannels(0), - fWithMonitorPorts(false) + : JackDriver(name, alias, engine, table) {} JackAudioDriver::~JackAudioDriver() @@ -44,21 +43,29 @@ 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) + 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) { fEngineControl->fSampleRate = sample_rate; fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec - if (!fEngineControl->fTimeOut) + if (!fEngineControl->fTimeOut) { fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); - return 0; + } + + return JackDriver::SetSampleRate(sample_rate); } int JackAudioDriver::Open(jack_nframes_t buffer_size, @@ -76,7 +83,11 @@ int JackAudioDriver::Open(jack_nframes_t buffer_size, fCaptureChannels = inchannels; fPlaybackChannels = outchannels; fWithMonitorPorts = monitor; - return JackDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); + 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); } int JackAudioDriver::Open(bool capturing, @@ -92,66 +103,88 @@ int JackAudioDriver::Open(bool capturing, fCaptureChannels = inchannels; fPlaybackChannels = outchannels; fWithMonitorPorts = monitor; - return JackDriver::Open(capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); + 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 input_range; + jack_latency_range_t output_range; + jack_latency_range_t monitor_range; + + for (int i = 0; i < fCaptureChannels; i++) { + input_range.max = input_range.min = fEngineControl->fBufferSize + fCaptureLatency; + fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &input_range); + } + + for (int i = 0; i < fPlaybackChannels; i++) { + output_range.max = output_range.min = fPlaybackLatency; + if (fEngineControl->fSyncMode) { + output_range.max = output_range.min += fEngineControl->fBufferSize; + } else { + output_range.max = output_range.min += fEngineControl->fBufferSize * 2; + } + fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &output_range); + if (fWithMonitorPorts) { + monitor_range.min = monitor_range.max = fEngineControl->fBufferSize; + fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &monitor_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; + char name[REAL_JACK_PORT_NAME_SIZE]; + char alias[REAL_JACK_PORT_NAME_SIZE]; int i; jack_log("JackAudioDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); for (i = 0; i < fCaptureChannels; i++) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); - snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { + snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); + snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } 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); } for (i = 0; i < fPlaybackChannels; i++) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); - snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { + snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); + snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } 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); // Monitor ports if (fWithMonitorPorts) { jack_log("Create monitor port"); - snprintf(name, sizeof(name) - 1, "%s:monitor_%u", fClientControl.fName, i + 1); - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, fEngineControl->fBufferSize)) == NO_PORT) { + snprintf(name, sizeof(name), "%s:monitor_%u", fClientControl.fName, i + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, fEngineControl->fBufferSize, &port_index) < 0) { 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; } @@ -161,13 +194,14 @@ int JackAudioDriver::Detach() jack_log("JackAudioDriver::Detach"); for (i = 0; i < fCaptureChannels; i++) { - fGraphManager->ReleasePort(fClientControl.fRefNum, fCapturePortList[i]); + fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[i]); } for (i = 0; i < fPlaybackChannels; i++) { - fGraphManager->ReleasePort(fClientControl.fRefNum, fPlaybackPortList[i]); - if (fWithMonitorPorts) - fGraphManager->ReleasePort(fClientControl.fRefNum, fMonitorPortList[i]); + fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[i]); + if (fWithMonitorPorts) { + fEngine->PortUnRegister(fClientControl.fRefNum, fMonitorPortList[i]); + } } return 0; @@ -187,26 +221,19 @@ int JackAudioDriver::Write() return 0; } -int JackAudioDriver::ProcessNull() +int JackAudioDriver::Process() { - // Keep begin cycle time - JackDriver::CycleTakeBeginTime(); - - if (fEngineControl->fSyncMode) { - ProcessGraphSync(); - } else { - ProcessGraphAsync(); - } - - // Keep end cycle time - JackDriver::CycleTakeEndTime(); - WaitUntilNextCycle(); - return 0; + return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); } -int JackAudioDriver::Process() +void JackAudioDriver::ProcessGraphAsync() { - return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); + // Process graph + if (fIsMaster) { + ProcessGraphAsyncMaster(); + } else { + ProcessGraphAsyncSlave(); + } } /* @@ -229,17 +256,30 @@ int JackAudioDriver::ProcessAsync() } // Process graph - if (fIsMaster) { - ProcessGraphAsync(); - } else { - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); - } + ProcessGraphAsync(); // Keep end cycle time JackDriver::CycleTakeEndTime(); return 0; } +void JackAudioDriver::ProcessGraphSync() +{ + // Process graph + if (fIsMaster) { + if (ProcessGraphSyncMaster() < 0) { + //jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); + //goto end; + } + } else { + if (ProcessGraphSyncSlave() < 0) { + //jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); + //goto end; + } + } +} + + /* The driver SYNC mode: the server does synchronize to the end of client graph execution, if graph process succeed, output buffers computed at the *current cycle* are used. @@ -254,17 +294,7 @@ int JackAudioDriver::ProcessSync() } // Process graph - if (fIsMaster) { - if (ProcessGraphSync() < 0) { - jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); - goto end; - } - } else { - if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { - jack_error("JackAudioDriver::ProcessSync: process error, skip cycle..."); - goto end; - } - } + ProcessGraphSync(); // Write output buffers from the current cycle if (Write() < 0) { @@ -272,34 +302,55 @@ int JackAudioDriver::ProcessSync() return -1; } -end: - // Keep end cycle time JackDriver::CycleTakeEndTime(); 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 +363,11 @@ int JackAudioDriver::ProcessGraphSync() return res; } +int JackAudioDriver::ProcessGraphSyncSlave() +{ + return fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); +} + int JackAudioDriver::Start() { int res = JackDriver::Start(); @@ -332,30 +388,25 @@ int JackAudioDriver::Stop() return res; } -void JackAudioDriver::WaitUntilNextCycle() -{ - int wait_time_usec = (int((float(fEngineControl->fBufferSize) / (float(fEngineControl->fSampleRate))) * 1000000.0f)); - wait_time_usec = int(wait_time_usec - (GetMicroSeconds() - fBeginDateUst)); - if (wait_time_usec > 0) - JackSleep(wait_time_usec); -} - 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) diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index 8eaca692..4f3b8149 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -35,29 +35,22 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver protected: - void ProcessGraphAsync(); - int ProcessGraphSync(); - void WaitUntilNextCycle(); - - virtual int ProcessAsync(); - virtual int ProcessSync(); - - int fCaptureChannels; - int fPlaybackChannels; - - // Static tables since the actual number of ports may be changed by the real driver - // thus dynamic allocation is more difficult to handle - jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; - jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; - jack_port_id_t fMonitorPortList[DRIVER_PORT_NUM]; - - bool fWithMonitorPorts; - jack_default_audio_sample_t* GetInputBuffer(int port_index); jack_default_audio_sample_t* GetOutputBuffer(int port_index); jack_default_audio_sample_t* GetMonitorBuffer(int port_index); void HandleLatencyCallback(int status); + virtual void UpdateLatencies(); + + int ProcessAsync(); + void ProcessGraphAsync(); + void ProcessGraphAsyncMaster(); + void ProcessGraphAsyncSlave(); + + int ProcessSync(); + void ProcessGraphSync(); + int ProcessGraphSyncMaster(); + int ProcessGraphSyncSlave(); public: @@ -87,7 +80,6 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver jack_nframes_t playback_latency); virtual int Process(); - virtual int ProcessNull(); virtual int Attach(); virtual int Detach(); diff --git a/common/JackChannel.h b/common/JackChannel.h index c9ee89e5..5979ba8e 100644 --- a/common/JackChannel.h +++ b/common/JackChannel.h @@ -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 { @@ -73,7 +74,7 @@ class JackClientChannelInterface return -1; } - virtual void ClientCheck(const char* name, int uuid, char* name_res, int protocol, int options, int* status, int* result) + virtual void ClientCheck(const char* name, int uuid, char* name_res, int protocol, int options, int* status, int* result, int open) {} virtual void ClientOpen(const char* name, int pid, int uuid, int* shared_engine, int* shared_client, int* shared_graph, int* result) {} diff --git a/common/JackClient.cpp b/common/JackClient.cpp index 8c3c6de5..9e1c952b 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -81,6 +81,8 @@ JackClient::JackClient(JackSynchro* table):fThread(this) fThreadFunArg = NULL; fSessionArg = NULL; fLatencyArg = NULL; + + fSessionReply = kPendingSessionReply; } JackClient::~JackClient() @@ -149,6 +151,8 @@ int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, { int res = 0; + jack_log("JackClient::ClientNotify ref = %ld name = %s notify = %ld", refnum, name, notify); + // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient switch (notify) { @@ -284,17 +288,18 @@ int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, case kSessionCallback: jack_log("JackClient::kSessionCallback"); if (fSession) { - jack_session_event_t *event = (jack_session_event_t *) malloc( sizeof(jack_session_event_t) ); + jack_session_event_t* event = (jack_session_event_t*)malloc( sizeof(jack_session_event_t)); char uuid_buf[JACK_UUID_SIZE]; - event->type = (jack_session_event_type_t) value1; - event->session_dir = strdup( message ); + event->type = (jack_session_event_type_t)value1; + event->session_dir = strdup(message); event->command_line = NULL; - event->flags = (jack_session_flags_t) 0; - snprintf( uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID ); - event->client_uuid = strdup( uuid_buf ); - fImmediateSessionReply = false; + event->flags = (jack_session_flags_t)0; + snprintf(uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID); + event->client_uuid = strdup(uuid_buf); + fSessionReply = kPendingSessionReply; + // Session callback may change fSessionReply by directly using jack_session_reply fSession(event, fSessionArg); - res = (fImmediateSessionReply) ? 1 : 2; + res = fSessionReply; } break; @@ -642,7 +647,7 @@ int JackClient::PortRegister(const char* port_name, const char* port_type, unsig // Check port name length string name = string(GetClientControl()->fName) + string(":") + port_name_str; - if (name.size() >= JACK_PORT_NAME_SIZE) { + if (name.size() >= REAL_JACK_PORT_NAME_SIZE) { jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n" "Please use %lu characters or less", GetClientControl()->fName, @@ -843,14 +848,14 @@ void JackClient::TransportLocate(jack_nframes_t frame) GetEngineControl()->fTransport.RequestNewPos(&pos); } -int JackClient::TransportReposition(jack_position_t* pos) +int JackClient::TransportReposition(const jack_position_t* pos) { jack_position_t tmp = *pos; jack_log("JackClient::TransportReposition pos = %ld", pos->frame); if (tmp.valid & ~JACK_POSITION_MASK) { return EINVAL; } else { - GetEngineControl()->fTransport.RequestNewPos(pos); + GetEngineControl()->fTransport.RequestNewPos(&tmp); return 0; } } @@ -1232,8 +1237,9 @@ int JackClient::SessionReply(jack_session_event_t* ev) jack_log("JackClient::SessionReply... we are here"); if (fChannel->IsChannelThread()) { - jack_log( "JackClient::SessionReply... in callback reply"); - fImmediateSessionReply = true; + jack_log("JackClient::SessionReply... in callback reply"); + // OK, immediate reply... + fSessionReply = kImmediateSessionReply; return 0; } diff --git a/common/JackClient.h b/common/JackClient.h index a03e17d2..86630583 100644 --- a/common/JackClient.h +++ b/common/JackClient.h @@ -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 @@ -94,7 +93,7 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable JackSynchro* fSynchroTable; std::list fPortList; - bool fImmediateSessionReply; + JackSessionReply fSessionReply; int StartThread(); void SetupDriverSync(bool freewheel); @@ -165,7 +164,7 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable virtual void TransportLocate(jack_nframes_t frame); virtual jack_transport_state_t TransportQuery(jack_position_t* pos); virtual jack_nframes_t GetCurrentTransportFrame(); - virtual int TransportReposition(jack_position_t* pos); + virtual int TransportReposition(const jack_position_t* pos); virtual void TransportStart(); virtual void TransportStop(); diff --git a/common/JackClientControl.h b/common/JackClientControl.h index 29e4a5b7..925f09dd 100644 --- a/common/JackClientControl.h +++ b/common/JackClientControl.h @@ -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 { @@ -34,6 +34,7 @@ namespace Jack \brief Client control possibly in shared memory. */ +PRE_PACKED_STRUCTURE struct JackClientControl : public JackShmMemAble { char fName[JACK_CLIENT_NAME_SIZE + 1]; diff --git a/common/JackConnectionManager.h b/common/JackConnectionManager.h index 804ffcc3..5041557a 100644 --- a/common/JackConnectionManager.h +++ b/common/JackConnectionManager.h @@ -36,6 +36,7 @@ struct JackClientControl; \brief Utility class. */ +PRE_PACKED_STRUCTURE template class JackFixedArray { @@ -122,6 +123,7 @@ class JackFixedArray \brief Utility class. */ +PRE_PACKED_STRUCTURE template class JackFixedArray1 : public JackFixedArray { @@ -158,6 +160,7 @@ class JackFixedArray1 : public JackFixedArray \brief Utility class. */ +PRE_PACKED_STRUCTURE template class JackFixedMatrix { @@ -253,6 +256,7 @@ class JackFixedMatrix \brief Utility class. */ +PRE_PACKED_STRUCTURE template class JackLoopFeedback { @@ -366,6 +370,7 @@ class JackLoopFeedback \brief For client timing measurements. */ +PRE_PACKED_STRUCTURE struct JackClientTiming { jack_time_t fSignaledAt; @@ -402,6 +407,7 @@ struct JackClientTiming */ +PRE_PACKED_STRUCTURE class SERVER_EXPORT JackConnectionManager { diff --git a/common/JackConstants.h b/common/JackConstants.h index 4b9578bd..2f090e3d 100644 --- a/common/JackConstants.h +++ b/common/JackConstants.h @@ -24,7 +24,7 @@ #include "config.h" #endif -#define VERSION "1.9.7" +#define VERSION "1.9.8" #define BUFFER_SIZE_MAX 8192 @@ -36,6 +36,8 @@ #define JACK_UUID_SIZE 32 #define JACK_SESSION_COMMAND_SIZE 256 +#define REAL_JACK_PORT_NAME_SIZE JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE + #ifndef PORT_NUM #define PORT_NUM 2048 #endif @@ -71,7 +73,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 diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index 6a8937ad..65adc742 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -114,7 +114,7 @@ struct jackctl_driver jack_driver_desc_t * desc_ptr; JSList * parameters; JSList * set_parameters; - JackDriverInfo* info; + JSList * infos; }; struct jackctl_internal @@ -228,7 +228,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; @@ -321,6 +322,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)) { @@ -390,6 +392,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)) { @@ -496,7 +499,7 @@ do_nothing_handler(int sig) snprintf (buf, sizeof(buf), "received signal %d during shutdown (ignored)\n", sig); } -EXPORT sigset_t +SERVER_EXPORT sigset_t jackctl_setup_signals( unsigned int flags) { @@ -574,7 +577,7 @@ jackctl_setup_signals( return signals; } -EXPORT void +SERVER_EXPORT void jackctl_wait_signals(sigset_t signals) { int sig; @@ -641,7 +644,7 @@ get_realtime_priority_constraint() return constraint_ptr; } -EXPORT jackctl_server_t * jackctl_server_create( +SERVER_EXPORT jackctl_server_t * jackctl_server_create( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name)) { @@ -674,7 +677,7 @@ EXPORT jackctl_server_t * jackctl_server_create( goto fail_free_parameters; } - value.b = false; + value.b = true; if (jackctl_add_parameter( &server_ptr->parameters, "realtime", @@ -857,138 +860,158 @@ fail: return NULL; } -EXPORT void jackctl_server_destroy(jackctl_server *server_ptr) +SERVER_EXPORT void jackctl_server_destroy(jackctl_server *server_ptr) { - jackctl_server_free_drivers(server_ptr); - jackctl_server_free_internals(server_ptr); - jackctl_server_free_parameters(server_ptr); - free(server_ptr); + if (server_ptr) { + jackctl_server_free_drivers(server_ptr); + jackctl_server_free_internals(server_ptr); + jackctl_server_free_parameters(server_ptr); + free(server_ptr); + } } -EXPORT const JSList * jackctl_server_get_drivers_list(jackctl_server *server_ptr) +SERVER_EXPORT const JSList * jackctl_server_get_drivers_list(jackctl_server *server_ptr) { - return server_ptr->drivers; + return (server_ptr) ? server_ptr->drivers : NULL; } -EXPORT bool jackctl_server_stop(jackctl_server *server_ptr) +SERVER_EXPORT bool jackctl_server_stop(jackctl_server *server_ptr) { - server_ptr->engine->Stop(); - return true; + if (server_ptr) { + server_ptr->engine->Stop(); + return true; + } else { + return false; + } } -EXPORT bool jackctl_server_close(jackctl_server *server_ptr) +SERVER_EXPORT bool jackctl_server_close(jackctl_server *server_ptr) { - server_ptr->engine->Close(); - delete server_ptr->engine; + if (server_ptr) { + server_ptr->engine->Close(); + delete server_ptr->engine; - /* clean up shared memory and files from this server instance */ - jack_log("cleaning up shared memory"); + /* clean up shared memory and files from this server instance */ + jack_log("cleaning up shared memory"); - jack_cleanup_shm(); + jack_cleanup_shm(); - jack_log("cleaning up files"); + jack_log("cleaning up files"); - JackTools::CleanupFiles(server_ptr->name.str); + JackTools::CleanupFiles(server_ptr->name.str); - jack_log("unregistering server `%s'", server_ptr->name.str); + jack_log("unregistering server `%s'", server_ptr->name.str); - jack_unregister_server(server_ptr->name.str); + jack_unregister_server(server_ptr->name.str); - server_ptr->engine = NULL; + server_ptr->engine = NULL; - return true; + return true; + } else { + return false; + } } -EXPORT const JSList * jackctl_server_get_parameters(jackctl_server *server_ptr) +SERVER_EXPORT const JSList * jackctl_server_get_parameters(jackctl_server *server_ptr) { - return server_ptr->parameters; + return (server_ptr) ? server_ptr->parameters : NULL; } -EXPORT bool +SERVER_EXPORT bool jackctl_server_open( jackctl_server *server_ptr, jackctl_driver *driver_ptr) { - int rc; JackSelfConnectMode self_connect_mode; - rc = jack_register_server(server_ptr->name.str, server_ptr->replace_registry.b); - switch (rc) - { - case EEXIST: - jack_error("`%s' server already active", server_ptr->name.str); - goto fail; - case ENOSPC: - jack_error("too many servers already active"); - goto fail; - case ENOMEM: - jack_error("no access to shm registry"); - goto fail; - } + try { - jack_log("server `%s' registered", server_ptr->name.str); + if (!server_ptr || !driver_ptr) { + return false; + } - /* clean up shared memory and files from any previous - * instance of this server name */ - jack_cleanup_shm(); - JackTools::CleanupFiles(server_ptr->name.str); + int rc = jack_register_server(server_ptr->name.str, server_ptr->replace_registry.b); + switch (rc) + { + case EEXIST: + jack_error("`%s' server already active", server_ptr->name.str); + goto fail; + case ENOSPC: + jack_error("too many servers already active"); + goto fail; + case ENOMEM: + jack_error("no access to shm registry"); + goto fail; + } - if (!server_ptr->realtime.b && server_ptr->client_timeout.i == 0) - server_ptr->client_timeout.i = 500; /* 0.5 sec; usable when non realtime. */ + jack_log("server `%s' registered", server_ptr->name.str); - /* check port max value before allocating server */ - if (server_ptr->port_max.ui > PORT_NUM_MAX) { - jack_error("JACK server started with too much ports %d (when port max can be %d)", server_ptr->port_max.ui, PORT_NUM_MAX); - goto fail; - } + /* clean up shared memory and files from any previous + * instance of this server name */ + jack_cleanup_shm(); + JackTools::CleanupFiles(server_ptr->name.str); - switch (server_ptr->self_connect_mode.c) - { - case SELF_CONNECT_MODE_ALLOW_CHAR: - self_connect_mode = JackSelfConnectAllow; - break; - case SELF_CONNECT_MODE_FAIL_EXTERNAL_ONLY_CHAR: - self_connect_mode = JackSelfConnectFailExternalOnly; - break; - case SELF_CONNECT_MODE_IGNORE_EXTERNAL_ONLY_CHAR: - self_connect_mode = JackSelfConnectIgnoreExternalOnly; - break; - case SELF_CONNECT_MODE_FAIL_ALL_CHAR: - self_connect_mode = JackSelfConnectFailAll; - break; - case SELF_CONNECT_MODE_IGNORE_ALL_CHAR: - self_connect_mode = JackSelfConnectIgnoreAll; - break; - default: - self_connect_mode = JACK_DEFAULT_SELF_CONNECT_MODE; - } + if (!server_ptr->realtime.b && server_ptr->client_timeout.i == 0) { + server_ptr->client_timeout.i = 500; /* 0.5 sec; usable when non realtime. */ + } - /* get the engine/driver started */ - server_ptr->engine = new JackServer( - server_ptr->sync.b, - server_ptr->temporary.b, - server_ptr->client_timeout.i, - server_ptr->realtime.b, - server_ptr->realtime_priority.i, - server_ptr->port_max.ui, - server_ptr->verbose.b, - (jack_timer_type_t)server_ptr->clock_source.ui, - self_connect_mode, - server_ptr->name.str); - if (server_ptr->engine == NULL) - { - jack_error("Failed to create new JackServer object"); - goto fail_unregister; - } + switch (server_ptr->self_connect_mode.c) + { + case SELF_CONNECT_MODE_ALLOW_CHAR: + self_connect_mode = JackSelfConnectAllow; + break; + case SELF_CONNECT_MODE_FAIL_EXTERNAL_ONLY_CHAR: + self_connect_mode = JackSelfConnectFailExternalOnly; + break; + case SELF_CONNECT_MODE_IGNORE_EXTERNAL_ONLY_CHAR: + self_connect_mode = JackSelfConnectIgnoreExternalOnly; + break; + case SELF_CONNECT_MODE_FAIL_ALL_CHAR: + self_connect_mode = JackSelfConnectFailAll; + break; + case SELF_CONNECT_MODE_IGNORE_ALL_CHAR: + self_connect_mode = JackSelfConnectIgnoreAll; + break; + default: + self_connect_mode = JACK_DEFAULT_SELF_CONNECT_MODE; + } - rc = server_ptr->engine->Open(driver_ptr->desc_ptr, driver_ptr->set_parameters); - if (rc < 0) - { - jack_error("JackServer::Open() failed with %d", rc); - goto fail_delete; - } + /* check port max value before allocating server */ + if (server_ptr->port_max.ui > PORT_NUM_MAX) { + jack_error("JACK server started with too much ports %d (when port max can be %d)", server_ptr->port_max.ui, PORT_NUM_MAX); + goto fail; + } - return true; + /* get the engine/driver started */ + server_ptr->engine = new JackServer( + server_ptr->sync.b, + server_ptr->temporary.b, + server_ptr->client_timeout.i, + server_ptr->realtime.b, + server_ptr->realtime_priority.i, + server_ptr->port_max.ui, + server_ptr->verbose.b, + (jack_timer_type_t)server_ptr->clock_source.ui, + self_connect_mode, + server_ptr->name.str); + if (server_ptr->engine == NULL) + { + jack_error("Failed to create new JackServer object"); + goto fail_unregister; + } + + rc = server_ptr->engine->Open(driver_ptr->desc_ptr, driver_ptr->set_parameters); + if (rc < 0) + { + jack_error("JackServer::Open() failed with %d", rc); + goto fail_delete; + } + + return true; + + } catch (std::exception e) { + jack_error("jackctl_server_open error..."); + } fail_delete: delete server_ptr->engine; @@ -1011,74 +1034,92 @@ fail: return false; } -EXPORT bool +SERVER_EXPORT bool jackctl_server_start( jackctl_server *server_ptr) { - int rc = server_ptr->engine->Start(); - bool result = rc >= 0; - if (! result) - { - jack_error("JackServer::Start() failed with %d", rc); + if (!server_ptr) { + return false; + } else { + int rc = server_ptr->engine->Start(); + bool result = rc >= 0; + if (! result) + { + jack_error("JackServer::Start() failed with %d", rc); + } + return result; } - return result; } -EXPORT const char * jackctl_driver_get_name(jackctl_driver *driver_ptr) +SERVER_EXPORT const char * jackctl_driver_get_name(jackctl_driver *driver_ptr) { - return driver_ptr->desc_ptr->name; + return (driver_ptr) ? driver_ptr->desc_ptr->name : NULL; } -EXPORT const JSList * jackctl_driver_get_parameters(jackctl_driver *driver_ptr) +SERVER_EXPORT jackctl_driver_type_t jackctl_driver_get_type(jackctl_driver *driver_ptr) { - return driver_ptr->parameters; + return (driver_ptr) ? (jackctl_driver_type_t)driver_ptr->desc_ptr->type : (jackctl_driver_type_t)0; } -EXPORT jack_driver_desc_t * jackctl_driver_get_desc(jackctl_driver *driver_ptr) +SERVER_EXPORT const JSList * jackctl_driver_get_parameters(jackctl_driver *driver_ptr) { - return driver_ptr->desc_ptr; + return (driver_ptr) ? driver_ptr->parameters : NULL; } -EXPORT const char * jackctl_parameter_get_name(jackctl_parameter *parameter_ptr) +SERVER_EXPORT jack_driver_desc_t * jackctl_driver_get_desc(jackctl_driver *driver_ptr) { - return parameter_ptr->name; + return (driver_ptr) ? driver_ptr->desc_ptr : NULL; } -EXPORT const char * jackctl_parameter_get_short_description(jackctl_parameter *parameter_ptr) +SERVER_EXPORT const char * jackctl_parameter_get_name(jackctl_parameter *parameter_ptr) { - return parameter_ptr->short_description; + return (parameter_ptr) ? parameter_ptr->name : NULL; } -EXPORT const char * jackctl_parameter_get_long_description(jackctl_parameter *parameter_ptr) +SERVER_EXPORT const char * jackctl_parameter_get_short_description(jackctl_parameter *parameter_ptr) { - return parameter_ptr->long_description; + return (parameter_ptr) ? parameter_ptr->short_description : NULL; } -EXPORT bool jackctl_parameter_has_range_constraint(jackctl_parameter *parameter_ptr) +SERVER_EXPORT const char * jackctl_parameter_get_long_description(jackctl_parameter *parameter_ptr) { - return parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) != 0; + return (parameter_ptr) ? parameter_ptr->long_description : NULL; } -EXPORT bool jackctl_parameter_has_enum_constraint(jackctl_parameter *parameter_ptr) +SERVER_EXPORT bool jackctl_parameter_has_range_constraint(jackctl_parameter *parameter_ptr) { - return parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) == 0; + return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) != 0) : false; } -EXPORT uint32_t jackctl_parameter_get_enum_constraints_count(jackctl_parameter *parameter_ptr) +SERVER_EXPORT bool jackctl_parameter_has_enum_constraint(jackctl_parameter *parameter_ptr) { + return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) == 0): false; +} + +SERVER_EXPORT uint32_t jackctl_parameter_get_enum_constraints_count(jackctl_parameter *parameter_ptr) +{ + if (!parameter_ptr) { + return NULL; + } + if (!jackctl_parameter_has_enum_constraint(parameter_ptr)) { return 0; } return parameter_ptr->constraint_ptr->constraint.enumeration.count; -} + } -EXPORT union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value(jackctl_parameter *parameter_ptr, uint32_t index) +SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value(jackctl_parameter *parameter_ptr, uint32_t index) { jack_driver_param_value_t * value_ptr; union jackctl_parameter_value jackctl_value; + if (!parameter_ptr) { + memset(&jackctl_value, 0, sizeof(jackctl_value)); + return jackctl_value; + } + value_ptr = ¶meter_ptr->constraint_ptr->constraint.enumeration.possible_values_array[index].value; switch (parameter_ptr->type) @@ -1103,13 +1144,17 @@ EXPORT union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value return jackctl_value; } -EXPORT const char * jackctl_parameter_get_enum_constraint_description(jackctl_parameter *parameter_ptr, uint32_t index) +SERVER_EXPORT const char * jackctl_parameter_get_enum_constraint_description(jackctl_parameter *parameter_ptr, uint32_t index) { - return parameter_ptr->constraint_ptr->constraint.enumeration.possible_values_array[index].short_desc; + return (parameter_ptr) ? parameter_ptr->constraint_ptr->constraint.enumeration.possible_values_array[index].short_desc : NULL; } -EXPORT void jackctl_parameter_get_range_constraint(jackctl_parameter *parameter_ptr, union jackctl_parameter_value * min_ptr, union jackctl_parameter_value * max_ptr) +SERVER_EXPORT void jackctl_parameter_get_range_constraint(jackctl_parameter *parameter_ptr, union jackctl_parameter_value * min_ptr, union jackctl_parameter_value * max_ptr) { + if (!parameter_ptr || !min_ptr || !max_ptr) { + return; + } + switch (parameter_ptr->type) { case JackParamInt: @@ -1126,38 +1171,48 @@ EXPORT void jackctl_parameter_get_range_constraint(jackctl_parameter *parameter_ } } -EXPORT bool jackctl_parameter_constraint_is_strict(jackctl_parameter_t * parameter_ptr) +SERVER_EXPORT bool jackctl_parameter_constraint_is_strict(jackctl_parameter_t * parameter_ptr) { - return parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_STRICT) != 0; + return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_STRICT) != 0) : false; } -EXPORT bool jackctl_parameter_constraint_is_fake_value(jackctl_parameter_t * parameter_ptr) +SERVER_EXPORT bool jackctl_parameter_constraint_is_fake_value(jackctl_parameter_t * parameter_ptr) { - return parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_FAKE_VALUE) != 0; + return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_FAKE_VALUE) != 0) : false; } -EXPORT jackctl_param_type_t jackctl_parameter_get_type(jackctl_parameter *parameter_ptr) +SERVER_EXPORT jackctl_param_type_t jackctl_parameter_get_type(jackctl_parameter *parameter_ptr) { - return parameter_ptr->type; + return (parameter_ptr) ? parameter_ptr->type : (jackctl_param_type_t)0; } -EXPORT char jackctl_parameter_get_id(jackctl_parameter_t * parameter_ptr) +SERVER_EXPORT char jackctl_parameter_get_id(jackctl_parameter_t * parameter_ptr) { - return parameter_ptr->id; + return (parameter_ptr) ? parameter_ptr->id : 0; } -EXPORT bool jackctl_parameter_is_set(jackctl_parameter *parameter_ptr) +SERVER_EXPORT bool jackctl_parameter_is_set(jackctl_parameter *parameter_ptr) { - return parameter_ptr->is_set; + return (parameter_ptr) ? parameter_ptr->is_set : false; } -EXPORT union jackctl_parameter_value jackctl_parameter_get_value(jackctl_parameter *parameter_ptr) +SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_value(jackctl_parameter *parameter_ptr) { - return *parameter_ptr->value_ptr; + if (parameter_ptr) { + return *parameter_ptr->value_ptr; + } else { + union jackctl_parameter_value jackctl_value; + memset(&jackctl_value, 0, sizeof(jackctl_value)); + return jackctl_value; + } } -EXPORT bool jackctl_parameter_reset(jackctl_parameter *parameter_ptr) +SERVER_EXPORT bool jackctl_parameter_reset(jackctl_parameter *parameter_ptr) { + if (!parameter_ptr) { + return NULL; + } + if (!parameter_ptr->is_set) { return true; @@ -1170,8 +1225,12 @@ EXPORT bool jackctl_parameter_reset(jackctl_parameter *parameter_ptr) return true; } -EXPORT bool jackctl_parameter_set_value(jackctl_parameter *parameter_ptr, const union jackctl_parameter_value * value_ptr) +SERVER_EXPORT bool jackctl_parameter_set_value(jackctl_parameter *parameter_ptr, const union jackctl_parameter_value * value_ptr) { + if (!parameter_ptr || !value_ptr) { + return NULL; + } + bool new_driver_parameter; /* for driver parameters, set the parameter by adding jack_driver_param_t in the set_parameters list */ @@ -1229,32 +1288,42 @@ EXPORT bool jackctl_parameter_set_value(jackctl_parameter *parameter_ptr, const return true; } -EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value(jackctl_parameter *parameter_ptr) +SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value(jackctl_parameter *parameter_ptr) { - return *parameter_ptr->default_value_ptr; + if (parameter_ptr) { + return *parameter_ptr->default_value_ptr; + } else { + union jackctl_parameter_value jackctl_value; + memset(&jackctl_value, 0, sizeof(jackctl_value)); + return jackctl_value; + } } // Internals clients -EXPORT const JSList * jackctl_server_get_internals_list(jackctl_server *server_ptr) +SERVER_EXPORT const JSList * jackctl_server_get_internals_list(jackctl_server *server_ptr) { - return server_ptr->internals; + return (server_ptr) ? server_ptr->internals : NULL; } -EXPORT const char * jackctl_internal_get_name(jackctl_internal *internal_ptr) +SERVER_EXPORT const char * jackctl_internal_get_name(jackctl_internal *internal_ptr) { - return internal_ptr->desc_ptr->name; + return (internal_ptr) ? internal_ptr->desc_ptr->name : NULL; } -EXPORT const JSList * jackctl_internal_get_parameters(jackctl_internal *internal_ptr) +SERVER_EXPORT const JSList * jackctl_internal_get_parameters(jackctl_internal *internal_ptr) { - return internal_ptr->parameters; + return (internal_ptr) ? internal_ptr->parameters : NULL; } -EXPORT bool jackctl_server_load_internal( +SERVER_EXPORT bool jackctl_server_load_internal( jackctl_server * server_ptr, jackctl_internal * internal) { + if (!server_ptr || !internal) { + return false; + } + int status; if (server_ptr->engine != NULL) { server_ptr->engine->InternalClientLoad2(internal->desc_ptr->name, internal->desc_ptr->name, internal->set_parameters, JackNullOption, &internal->refnum, -1, &status); @@ -1264,56 +1333,73 @@ EXPORT bool jackctl_server_load_internal( } } -EXPORT bool jackctl_server_unload_internal( +SERVER_EXPORT bool jackctl_server_unload_internal( jackctl_server * server_ptr, jackctl_internal * internal) { + if (!server_ptr || !internal) { + return false; + } + int status; if (server_ptr->engine != NULL && internal->refnum > 0) { - // Client object is internally kept in JackEngine, and will be desallocated in InternalClientUnload + // Client object is internally kept in JackEngine, and will be deallocated in InternalClientUnload return ((server_ptr->engine->GetEngine()->InternalClientUnload(internal->refnum, &status)) == 0); } else { return false; } } -EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) +SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) { - if (server_ptr->engine != NULL) { + if (server_ptr && server_ptr->engine) { if (server_ptr->engine->IsRunning()) { 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; } } -EXPORT bool jackctl_server_remove_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) +SERVER_EXPORT bool jackctl_server_remove_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) { - if (server_ptr->engine != NULL) { + if (server_ptr && server_ptr->engine) { 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; + 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; } } -EXPORT bool jackctl_server_switch_master(jackctl_server * server_ptr, jackctl_driver * driver_ptr) +SERVER_EXPORT bool jackctl_server_switch_master(jackctl_server * server_ptr, jackctl_driver * driver_ptr) { - if (server_ptr->engine != NULL) { + if (server_ptr && server_ptr->engine) { return (server_ptr->engine->SwitchMaster(driver_ptr->desc_ptr, driver_ptr->set_parameters) == 0); } else { return false; } } + diff --git a/common/JackControlAPI.h b/common/JackControlAPI.h index 8e87d513..0363d24c 100644 --- a/common/JackControlAPI.h +++ b/common/JackControlAPI.h @@ -28,6 +28,7 @@ #ifdef WIN32 #ifdef __MINGW32__ #include +typedef _sigset_t sigset_t; #else typedef HANDLE sigset_t; #endif @@ -43,6 +44,13 @@ typedef enum JackParamBool, /**< @brief value type is a boolean */ } jackctl_param_type_t; +/** Driver types, intentionally similar to jack_driver_type_t */ +typedef enum +{ + JackMaster = 1, /**< @brief master driver */ + JackSlave, /**< @brief slave driver */ +} jackctl_driver_type_t; + /** @brief Max value that jackctl_param_type_t type can have */ #define JACK_PARAM_MAX (JackParamBool + 1) @@ -79,164 +87,168 @@ extern "C" { } /* Adjust editor indent */ #endif -EXPORT sigset_t +SERVER_EXPORT sigset_t jackctl_setup_signals( unsigned int flags); -EXPORT void +SERVER_EXPORT void jackctl_wait_signals( sigset_t signals); -EXPORT jackctl_server_t * +SERVER_EXPORT jackctl_server_t * jackctl_server_create( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name)); -EXPORT void +SERVER_EXPORT void jackctl_server_destroy( jackctl_server_t * server); -EXPORT const JSList * +SERVER_EXPORT const JSList * jackctl_server_get_drivers_list( jackctl_server_t * server); -EXPORT bool +SERVER_EXPORT bool jackctl_server_open( jackctl_server_t * server, jackctl_driver_t * driver); -EXPORT bool +SERVER_EXPORT bool jackctl_server_start( jackctl_server_t * server); -EXPORT bool +SERVER_EXPORT bool jackctl_server_stop( jackctl_server_t * server); -EXPORT bool +SERVER_EXPORT bool jackctl_server_close( jackctl_server_t * server); -EXPORT const JSList * +SERVER_EXPORT const JSList * jackctl_server_get_parameters( jackctl_server_t * server); -EXPORT const char * +SERVER_EXPORT const char * jackctl_driver_get_name( jackctl_driver_t * driver); -EXPORT const JSList * +SERVER_EXPORT jackctl_driver_type_t +jackctl_driver_get_type( + jackctl_driver_t * driver); + +SERVER_EXPORT const JSList * jackctl_driver_get_parameters( jackctl_driver_t * driver); -EXPORT const char * +SERVER_EXPORT const char * jackctl_parameter_get_name( jackctl_parameter_t * parameter); -EXPORT const char * +SERVER_EXPORT const char * jackctl_parameter_get_short_description( jackctl_parameter_t * parameter); -EXPORT const char * +SERVER_EXPORT const char * jackctl_parameter_get_long_description( jackctl_parameter_t * parameter); -EXPORT jackctl_param_type_t +SERVER_EXPORT jackctl_param_type_t jackctl_parameter_get_type( jackctl_parameter_t * parameter); -EXPORT char +SERVER_EXPORT char jackctl_parameter_get_id( jackctl_parameter_t * parameter); -EXPORT bool +SERVER_EXPORT bool jackctl_parameter_is_set( jackctl_parameter_t * parameter); -EXPORT bool +SERVER_EXPORT bool jackctl_parameter_reset( jackctl_parameter_t * parameter); -EXPORT union jackctl_parameter_value +SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_value( jackctl_parameter_t * parameter); -EXPORT bool +SERVER_EXPORT bool jackctl_parameter_set_value( jackctl_parameter_t * parameter, const union jackctl_parameter_value * value_ptr); -EXPORT union jackctl_parameter_value +SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value( jackctl_parameter_t * parameter); - -EXPORT union jackctl_parameter_value + +SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value( jackctl_parameter *parameter_ptr); - -EXPORT bool + +SERVER_EXPORT bool jackctl_parameter_has_range_constraint( jackctl_parameter_t * parameter_ptr); -EXPORT bool +SERVER_EXPORT bool jackctl_parameter_has_enum_constraint( jackctl_parameter_t * parameter_ptr); -EXPORT uint32_t +SERVER_EXPORT uint32_t jackctl_parameter_get_enum_constraints_count( jackctl_parameter_t * parameter_ptr); -EXPORT union jackctl_parameter_value +SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value( jackctl_parameter_t * parameter_ptr, uint32_t index); -EXPORT const char * +SERVER_EXPORT const char * jackctl_parameter_get_enum_constraint_description( jackctl_parameter_t * parameter_ptr, uint32_t index); -EXPORT void +SERVER_EXPORT void jackctl_parameter_get_range_constraint( jackctl_parameter_t * parameter_ptr, union jackctl_parameter_value * min_ptr, union jackctl_parameter_value * max_ptr); -EXPORT bool +SERVER_EXPORT bool jackctl_parameter_constraint_is_strict( jackctl_parameter_t * parameter_ptr); -EXPORT bool +SERVER_EXPORT bool jackctl_parameter_constraint_is_fake_value( jackctl_parameter_t * parameter_ptr); -EXPORT const JSList * +SERVER_EXPORT const JSList * jackctl_server_get_internals_list( jackctl_server *server_ptr); - -EXPORT const char * + +SERVER_EXPORT const char * jackctl_internal_get_name( jackctl_internal *internal_ptr); - -EXPORT const JSList * + +SERVER_EXPORT const JSList * jackctl_internal_get_parameters( jackctl_internal *internal_ptr); - -EXPORT bool jackctl_server_load_internal( + +SERVER_EXPORT bool jackctl_server_load_internal( jackctl_server * server, jackctl_internal * internal); - -EXPORT bool jackctl_server_unload_internal( + +SERVER_EXPORT bool jackctl_server_unload_internal( jackctl_server * server, jackctl_internal * internal); - -EXPORT bool jackctl_server_add_slave(jackctl_server_t * server, + +SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server_t * server, jackctl_driver_t * driver); -EXPORT bool jackctl_server_remove_slave(jackctl_server_t * server, +SERVER_EXPORT bool jackctl_server_remove_slave(jackctl_server_t * server, jackctl_driver_t * driver); -EXPORT bool +SERVER_EXPORT bool jackctl_server_switch_master(jackctl_server_t * server, jackctl_driver_t * driver); @@ -248,3 +260,4 @@ jackctl_server_switch_master(jackctl_server_t * server, #endif #endif + diff --git a/common/JackDebugClient.cpp b/common/JackDebugClient.cpp index a1a2fe69..3b0af9b4 100644 --- a/common/JackDebugClient.cpp +++ b/common/JackDebugClient.cpp @@ -90,7 +90,7 @@ int JackDebugClient::Open(const char* server_name, const char* name, int uuid, j /* Convert it to local time representation. */ loctime = localtime (&curtime); strftime (buffer, 256, "%I-%M", loctime); - sprintf(provstr, "JackClientDebug-%s-%s.log", name, buffer); + snprintf(provstr, sizeof(provstr), "JackClientDebug-%s-%s.log", name, buffer); fStream = new ofstream(provstr, ios_base::ate); if (fStream->is_open()) { if (res == -1) { @@ -333,7 +333,7 @@ int JackDebugClient::SetFreeWheel(int onoff) *fStream << "!!! ERROR !!! : Freewheel setup seems incorrect : set = ON while FW is already ON " << endl; if (!onoff && !fFreewheel) *fStream << "!!! ERROR !!! : Freewheel setup seems incorrect : set = OFF while FW is already OFF " << endl; - fFreewheel = onoff; + fFreewheel = onoff ? true : false; return fClient->SetFreeWheel(onoff); } diff --git a/common/JackDebugClient.h b/common/JackDebugClient.h index 465d9465..f781b075 100644 --- a/common/JackDebugClient.h +++ b/common/JackDebugClient.h @@ -46,7 +46,7 @@ PortFollower; \brief A "decorator" debug client to validate API use. */ -class SERVER_EXPORT JackDebugClient : public JackClient +class LIB_EXPORT JackDebugClient : public JackClient { protected: diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index ce860468..7099af4d 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -37,7 +37,10 @@ namespace Jack { JackDriver::JackDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) - :fClientControl(name) + :fClientControl(name), + fCaptureChannels(0), + fPlaybackChannels(0), + fWithMonitorPorts(false) { assert(strlen(name) < JACK_CLIENT_NAME_SIZE); fSynchroTable = table; @@ -56,8 +59,12 @@ JackDriver::JackDriver() fEngine = NULL; fGraphManager = NULL; fBeginDateUst = 0; + fDelayedUsecs = 0.f; fIsMaster = true; fIsRunning = false; + fCaptureChannels = 0; + fPlaybackChannels = 0; + fWithMonitorPorts = false; } JackDriver::~JackDriver() @@ -123,8 +130,9 @@ int JackDriver::Open(bool capturing, strcpy(fPlaybackDriverName, playback_driver_name); fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec - if (!fEngineControl->fTimeOut) + if (!fEngineControl->fTimeOut) { fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); + } fGraphManager->DirectConnect(fClientControl.fRefNum, fClientControl.fRefNum); // Connect driver to itself for "sync" mode SetupDriverSync(fClientControl.fRefNum, false); @@ -176,8 +184,9 @@ int JackDriver::Open(jack_nframes_t buffer_size, strcpy(fPlaybackDriverName, playback_driver_name); fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec - if (!fEngineControl->fTimeOut) + if (!fEngineControl->fTimeOut) { fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); + } fGraphManager->SetBufferSize(buffer_size); fGraphManager->DirectConnect(fClientControl.fRefNum, fClientControl.fRefNum); // Connect driver to itself for "sync" mode @@ -216,6 +225,8 @@ void JackDriver::SetupDriverSync(int ref, bool freewheel) int JackDriver::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { + jack_log("JackDriver::ClientNotify ref = %ld driver = %s name = %s notify = %ld", refnum, fClientControl.fName, name, notify); + switch (notify) { case kStartFreewheelCallback: @@ -227,7 +238,7 @@ int JackDriver::ClientNotify(int refnum, const char* name, int notify, int sync, jack_log("JackDriver::kStopFreewheel"); SetupDriverSync(fClientControl.fRefNum, false); break; - } + } return 0; } @@ -300,25 +311,47 @@ void JackDriver::RemoveSlave(JackDriverInterface* slave) fSlaveList.remove(slave); } -int JackDriver::ProcessSlaves() +int JackDriver::ProcessReadSlaves() { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; - if (slave->Process() < 0) - res = -1; + if (slave->IsRunning()) { + if (slave->ProcessRead() < 0) { + res = -1; + } + } + } + return res; +} +int JackDriver::ProcessWriteSlaves() +{ + int res = 0; + list::const_iterator it; + for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { + JackDriverInterface* slave = *it; + if (slave->IsRunning()) { + if (slave->ProcessWrite() < 0) { + res = -1; + } + } } return res; } -int JackDriver::Process() +int JackDriver::ProcessRead() { return 0; } -int JackDriver::ProcessNull() +int JackDriver::ProcessWrite() +{ + return 0; +} + +int JackDriver::Process() { return 0; } @@ -352,6 +385,12 @@ int JackDriver::Start() return 0; } +int JackDriver::Stop() +{ + fIsRunning = false; + return 0; +} + int JackDriver::StartSlaves() { int res = 0; @@ -360,30 +399,23 @@ int JackDriver::StartSlaves() JackDriverInterface* slave = *it; if (slave->Start() < 0) { res = -1; - // XXX: We should attempt to stop all of the slaves that we've // started here. - break; } } return res; } -int JackDriver::Stop() -{ - fIsRunning = false; - return 0; -} - int JackDriver::StopSlaves() { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; - if (slave->Stop() < 0) + if (slave->Stop() < 0) { res = -1; + } } return res; } @@ -395,12 +427,28 @@ bool JackDriver::IsFixedBufferSize() int JackDriver::SetBufferSize(jack_nframes_t buffer_size) { - return 0; + int res = 0; + list::const_iterator it; + for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { + JackDriverInterface* slave = *it; + if (slave->SetBufferSize(buffer_size) < 0) { + res = -1; + } + } + return res; } int JackDriver::SetSampleRate(jack_nframes_t sample_rate) { - return 0; + int res = 0; + list::const_iterator it; + for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { + JackDriverInterface* slave = *it; + if (slave->SetSampleRate(sample_rate) < 0) { + res = -1; + } + } + return res; } bool JackDriver::Initialize() @@ -409,4 +457,57 @@ bool JackDriver::Initialize() } +void JackDriver::SaveConnections() +{ + const char** connections; + fConnections.clear(); + char alias1[REAL_JACK_PORT_NAME_SIZE]; + char alias2[REAL_JACK_PORT_NAME_SIZE]; + char* aliases[2]; + + aliases[0] = alias1; + aliases[1] = alias2; + + for (int i = 0; i < fCaptureChannels; ++i) { + if (fCapturePortList[i] && (connections = fGraphManager->GetConnections(fCapturePortList[i])) != 0) { + for (int j = 0; connections[j]; j++) { + /* + fGraphManager->GetPort(fCapturePortList[i])->GetAliases(aliases); + fConnections.push_back(make_pair(aliases[0], connections[j])); + jack_info("Save connection: %s %s", aliases[0], connections[j]); + */ + fConnections.push_back(make_pair(fGraphManager->GetPort(fCapturePortList[i])->GetName(), connections[j])); + jack_info("Save connection: %s %s", fGraphManager->GetPort(fCapturePortList[i])->GetName(), connections[j]); + } + free(connections); + } + } + + for (int i = 0; i < fPlaybackChannels; ++i) { + if (fPlaybackPortList[i] && (connections = fGraphManager->GetConnections(fPlaybackPortList[i])) != 0) { + for (int j = 0; connections[j]; j++) { + /* + fGraphManager->GetPort(fPlaybackPortList[i])->GetAliases(aliases); + fConnections.push_back(make_pair(connections[j], aliases[0])); + jack_info("Save connection: %s %s", connections[j], aliases[0]); + */ + fConnections.push_back(make_pair(connections[j], fGraphManager->GetPort(fPlaybackPortList[i])->GetName())); + jack_info("Save connection: %s %s", connections[j], fGraphManager->GetPort(fPlaybackPortList[i])->GetName()); + } + free(connections); + } + } +} + +void JackDriver::RestoreConnections() +{ + list >::const_iterator it; + + for (it = fConnections.begin(); it != fConnections.end(); it++) { + pair connection = *it; + jack_info("Restore connection: %s %s", connection.first.c_str(), connection.second.c_str()); + fEngine->PortConnect(fClientControl.fRefNum, connection.first.c_str(), connection.second.c_str()); + } +} + } // end of namespace diff --git a/common/JackDriver.h b/common/JackDriver.h index 68f937c2..05502b04 100644 --- a/common/JackDriver.h +++ b/common/JackDriver.h @@ -34,6 +34,7 @@ namespace Jack class JackLockedEngine; class JackGraphManager; struct JackEngineControl; +class JackSlaveDriverInterface; /*! \brief The base interface for drivers. @@ -52,14 +53,14 @@ class SERVER_EXPORT JackDriverInterface virtual int Open() = 0; virtual int Open (bool capturing, - bool playing, - int inchannels, - int outchannels, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency) = 0; + 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) = 0; virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, @@ -87,14 +88,20 @@ class SERVER_EXPORT JackDriverInterface virtual int SetSampleRate(jack_nframes_t sample_rate) = 0; virtual int Process() = 0; - virtual int ProcessNull() = 0; virtual void SetMaster(bool onoff) = 0; virtual bool GetMaster() = 0; + virtual void AddSlave(JackDriverInterface* slave) = 0; virtual void RemoveSlave(JackDriverInterface* slave) = 0; + virtual std::list GetSlaves() = 0; - virtual int ProcessSlaves() = 0; + + virtual int ProcessReadSlaves() = 0; + virtual int ProcessWriteSlaves() = 0; + + virtual int ProcessRead() = 0; + virtual int ProcessWrite() = 0; virtual bool IsRealTime() const = 0; virtual bool IsRunning() const = 0; @@ -122,12 +129,16 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface char fCaptureDriverName[JACK_CLIENT_NAME_SIZE + 1]; char fPlaybackDriverName[JACK_CLIENT_NAME_SIZE + 1]; + char fAliasName[JACK_CLIENT_NAME_SIZE + 1]; + jack_nframes_t fCaptureLatency; jack_nframes_t fPlaybackLatency; + jack_time_t fBeginDateUst; jack_time_t fEndDateUst; float fDelayedUsecs; + JackLockedEngine* fEngine; JackGraphManager* fGraphManager; JackSynchro* fSynchroTable; @@ -137,6 +148,19 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface bool fIsMaster; bool fIsRunning; + int fCaptureChannels; + int fPlaybackChannels; + + // Static tables since the actual number of ports may be changed by the real driver + // thus dynamic allocation is more difficult to handle + jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; + jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; + jack_port_id_t fMonitorPortList[DRIVER_PORT_NUM]; + + bool fWithMonitorPorts; + + std::list > fConnections; // Connections list + void CycleIncTime(); void CycleTakeBeginTime(); void CycleTakeEndTime(); @@ -159,23 +183,23 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface void AddSlave(JackDriverInterface* slave); void RemoveSlave(JackDriverInterface* slave); + std::list GetSlaves() { return fSlaveList; } - int ProcessSlaves(); virtual int Open(); virtual int Open (bool capturing, - bool playing, - int inchannels, - int outchannels, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency); + 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); virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, @@ -191,7 +215,6 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface virtual int Close(); virtual int Process(); - virtual int ProcessNull(); virtual int Attach(); virtual int Detach(); @@ -200,10 +223,20 @@ 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 void SaveConnections(); + virtual void RestoreConnections(); + virtual bool IsFixedBufferSize(); virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetSampleRate(jack_nframes_t sample_rate); diff --git a/common/JackDriverLoader.cpp b/common/JackDriverLoader.cpp index 2d270656..f7de89ee 100644 --- a/common/JackDriverLoader.cpp +++ b/common/JackDriverLoader.cpp @@ -25,14 +25,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include #include +#include #ifndef WIN32 #include #endif -jack_driver_desc_t * jackctl_driver_get_desc(jackctl_driver_t * driver); +jack_driver_desc_t* jackctl_driver_get_desc(jackctl_driver_t * driver); -EXPORT void jack_print_driver_options (jack_driver_desc_t* desc, FILE* file) +SERVER_EXPORT void jack_print_driver_options(jack_driver_desc_t* desc, FILE* file) { unsigned long i; char arg_default[JACK_DRIVER_PARAM_STRING_MAX + 1]; @@ -49,17 +50,18 @@ EXPORT void jack_print_driver_options (jack_driver_desc_t* desc, FILE* file) sprintf (arg_default, "%c", desc->params[i].value.c); break; case JackDriverParamString: - if (desc->params[i].value.str && strcmp (desc->params[i].value.str, "") != 0) + if (desc->params[i].value.str && strcmp (desc->params[i].value.str, "") != 0) { sprintf (arg_default, "%s", desc->params[i].value.str); - else + } else { sprintf (arg_default, "none"); + } break; case JackDriverParamBool: sprintf (arg_default, "%s", desc->params[i].value.i ? "true" : "false"); break; } - fprintf (file, "\t-%c, --%s \t%s (default: %s)\n", + fprintf(file, "\t-%c, --%s \t%s (default: %s)\n", desc->params[i].character, desc->params[i].name, desc->params[i].long_desc, @@ -68,17 +70,17 @@ EXPORT void jack_print_driver_options (jack_driver_desc_t* desc, FILE* file) } static void -jack_print_driver_param_usage (jack_driver_desc_t * desc, unsigned long param, FILE *file) +jack_print_driver_param_usage (jack_driver_desc_t* desc, unsigned long param, FILE *file) { fprintf (file, "Usage information for the '%s' parameter for driver '%s':\n", desc->params[param].name, desc->name); fprintf (file, "%s\n", desc->params[param].long_desc); } -EXPORT void jack_free_driver_params(JSList * driver_params) +SERVER_EXPORT void jack_free_driver_params(JSList * driver_params) { - JSList *node_ptr = driver_params; - JSList *next_node_ptr; + JSList*node_ptr = driver_params; + JSList*next_node_ptr; while (node_ptr) { next_node_ptr = node_ptr->next; @@ -88,15 +90,15 @@ EXPORT void jack_free_driver_params(JSList * driver_params) } } -int -jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr) +SERVER_EXPORT int +jack_parse_driver_params(jack_driver_desc_t* desc, int argc, char* argv[], JSList** param_ptr) { struct option * long_options; - char * options, * options_ptr; + char* options, * options_ptr; unsigned long i; int opt; unsigned int param_index; - JSList * params = NULL; + JSList* params = NULL; jack_driver_param_t * driver_param; if (argc <= 1) { @@ -173,10 +175,10 @@ jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSL if (optarg) { switch (desc->params[param_index].type) { case JackDriverParamInt: - driver_param->value.i = atoi (optarg); + driver_param->value.i = atoi(optarg); break; case JackDriverParamUInt: - driver_param->value.ui = strtoul (optarg, NULL, 10); + driver_param->value.ui = strtoul(optarg, NULL, 10); break; case JackDriverParamChar: driver_param->value.c = optarg[0]; @@ -185,26 +187,14 @@ jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSL strncpy (driver_param->value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX); break; case JackDriverParamBool: - - /* - if (strcasecmp ("false", optarg) == 0 || - strcasecmp ("off", optarg) == 0 || - strcasecmp ("no", optarg) == 0 || - strcasecmp ("0", optarg) == 0 || - strcasecmp ("(null)", optarg) == 0 ) { - */ - // steph - if (strcmp ("false", optarg) == 0 || - strcmp ("off", optarg) == 0 || - strcmp ("no", optarg) == 0 || - strcmp ("0", optarg) == 0 || - strcmp ("(null)", optarg) == 0 ) { + if (strcasecmp("false", optarg) == 0 || + strcasecmp("off", optarg) == 0 || + strcasecmp("no", optarg) == 0 || + strcasecmp("0", optarg) == 0 || + strcasecmp("(null)", optarg) == 0 ) { driver_param->value.i = false; - } else { - driver_param->value.i = true; - } break; } @@ -222,31 +212,33 @@ jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSL free (options); free (long_options); - if (param_ptr) + if (param_ptr) { *param_ptr = params; - + } return 0; } -EXPORT int -jackctl_parse_driver_params (jackctl_driver *driver_ptr, int argc, char* argv[]) +SERVER_EXPORT int +jackctl_parse_driver_params(jackctl_driver *driver_ptr, int argc, char* argv[]) { - struct option * long_options; - char * options, * options_ptr; + struct option* long_options; + char* options, * options_ptr; unsigned long i; int opt; - JSList * node_ptr; + JSList* node_ptr; jackctl_parameter_t * param = NULL; union jackctl_parameter_value value; - if (argc <= 1) + if (argc <= 1) { return 0; + } - const JSList * driver_params = jackctl_driver_get_parameters(driver_ptr); - if (driver_params == NULL) + const JSList* driver_params = jackctl_driver_get_parameters(driver_ptr); + if (driver_params == NULL) { return 1; + } - jack_driver_desc_t * desc = jackctl_driver_get_desc(driver_ptr); + jack_driver_desc_t* desc = jackctl_driver_get_desc(driver_ptr); /* check for help */ if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) { @@ -274,7 +266,7 @@ jackctl_parse_driver_params (jackctl_driver *driver_ptr, int argc, char* argv[]) options_ptr = options; for (i = 0; i < desc->nparams; i++) { - sprintf (options_ptr, "%c::", desc->params[i].character); + sprintf(options_ptr, "%c::", desc->params[i].character); options_ptr += 3; long_options[i].name = desc->params[i].name; long_options[i].flag = NULL; @@ -317,11 +309,11 @@ jackctl_parse_driver_params (jackctl_driver *driver_ptr, int argc, char* argv[]) if (optarg) { switch (jackctl_parameter_get_type(param)) { case JackDriverParamInt: - value.i = atoi (optarg); + value.i = atoi(optarg); jackctl_parameter_set_value(param, &value); break; case JackDriverParamUInt: - value.ui = strtoul (optarg, NULL, 10); + value.ui = strtoul(optarg, NULL, 10); jackctl_parameter_set_value(param, &value); break; case JackDriverParamChar: @@ -329,23 +321,15 @@ jackctl_parse_driver_params (jackctl_driver *driver_ptr, int argc, char* argv[]) jackctl_parameter_set_value(param, &value); break; case JackDriverParamString: - strncpy (value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX); + strncpy(value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX); jackctl_parameter_set_value(param, &value); break; case JackDriverParamBool: - /* - if (strcasecmp ("false", optarg) == 0 || - strcasecmp ("off", optarg) == 0 || - strcasecmp ("no", optarg) == 0 || - strcasecmp ("0", optarg) == 0 || - strcasecmp ("(null)", optarg) == 0 ) { - */ - // steph - if (strcmp ("false", optarg) == 0 || - strcmp ("off", optarg) == 0 || - strcmp ("no", optarg) == 0 || - strcmp ("0", optarg) == 0 || - strcmp ("(null)", optarg) == 0 ) { + if (strcasecmp("false", optarg) == 0 || + strcasecmp("off", optarg) == 0 || + strcasecmp("no", optarg) == 0 || + strcasecmp("0", optarg) == 0 || + strcasecmp("(null)", optarg) == 0 ) { value.i = false; } else { value.i = true; @@ -368,14 +352,14 @@ jackctl_parse_driver_params (jackctl_driver *driver_ptr, int argc, char* argv[]) return 0; } -jack_driver_desc_t * -jack_find_driver_descriptor (JSList * drivers, const char * name) +jack_driver_desc_t* +jack_find_driver_descriptor (JSList * drivers, const char* name) { - jack_driver_desc_t * desc = 0; - JSList * node; + jack_driver_desc_t* desc = 0; + JSList* node; for (node = drivers; node; node = jack_slist_next (node)) { - desc = (jack_driver_desc_t *) node->data; + desc = (jack_driver_desc_t*) node->data; if (strcmp (desc->name, name) != 0) { desc = NULL; @@ -387,18 +371,18 @@ jack_find_driver_descriptor (JSList * drivers, const char * name) return desc; } -static jack_driver_desc_t * -jack_get_descriptor (JSList * drivers, const char * sofile, const char * symbol) +static jack_driver_desc_t* +jack_get_descriptor (JSList * drivers, const char* sofile, const char* symbol) { - jack_driver_desc_t * descriptor, * other_descriptor; + jack_driver_desc_t* descriptor, * other_descriptor; JackDriverDescFunction so_get_descriptor = NULL; - JSList * node; + JSList* node; void * dlhandle; - char * filename; + char* filename; #ifdef WIN32 int dlerr; #else - const char * dlerr; + const char* dlerr; #endif int err; @@ -410,7 +394,15 @@ jack_get_descriptor (JSList * drivers, const char * sofile, const char * symbol) #ifdef WIN32 char temp_driver_dir1[512]; char temp_driver_dir2[512]; - GetCurrentDirectory(512, temp_driver_dir1); + if (3 < GetModuleFileName(NULL, temp_driver_dir1, 512)) { + char *p = strrchr(temp_driver_dir1, '\\'); + if (p && (p != temp_driver_dir1)) + *p = 0; + else + GetCurrentDirectory(512, temp_driver_dir1); + } else { + GetCurrentDirectory(512, temp_driver_dir1); + } sprintf(temp_driver_dir2, "%s/%s", temp_driver_dir1, ADDON_DIR); driver_dir = temp_driver_dir2; #else @@ -418,8 +410,9 @@ jack_get_descriptor (JSList * drivers, const char * sofile, const char * symbol) #endif } - filename = (char *)malloc(strlen (driver_dir) + 1 + strlen(sofile) + 1); - sprintf (filename, "%s/%s", driver_dir, sofile); + int len = strlen(driver_dir) + 1 + strlen(sofile) + 1; + filename = (char*)malloc(len); + snprintf(filename, len, "%s/%s", driver_dir, sofile); if ((dlhandle = LoadDriverModule(filename)) == NULL) { #ifdef WIN32 @@ -466,7 +459,7 @@ jack_get_descriptor (JSList * drivers, const char * sofile, const char * symbol) /* check it doesn't exist already */ for (node = drivers; node; node = jack_slist_next (node)) { - other_descriptor = (jack_driver_desc_t *) node->data; + other_descriptor = (jack_driver_desc_t*) node->data; if (strcmp(descriptor->name, other_descriptor->name) == 0) { jack_error("the drivers in '%s' and '%s' both have the name '%s'; using the first", @@ -494,16 +487,25 @@ static bool check_symbol(const char* sofile, const char* symbol) #ifdef WIN32 char temp_driver_dir1[512]; char temp_driver_dir2[512]; - GetCurrentDirectory(512, temp_driver_dir1); - sprintf(temp_driver_dir2, "%s/%s", temp_driver_dir1, ADDON_DIR); + if (3 < GetModuleFileName(NULL, temp_driver_dir1, 512)) { + char *p = strrchr(temp_driver_dir1, '\\'); + if (p && (p != temp_driver_dir1)) + *p = 0; + else + GetCurrentDirectory(512, temp_driver_dir1); + } else { + GetCurrentDirectory(512, temp_driver_dir1); + } + snprintf(temp_driver_dir2, sizeof(temp_driver_dir2), "%s/%s", temp_driver_dir1, ADDON_DIR); driver_dir = temp_driver_dir2; #else driver_dir = ADDON_DIR; #endif } - char* filename = (char *)malloc(strlen (driver_dir) + 1 + strlen(sofile) + 1); - sprintf (filename, "%s/%s", driver_dir, sofile); + int len = strlen(driver_dir) + 1 + strlen(sofile) + 1; + char* filename = (char*)malloc(len); + snprintf(filename, len, "%s/%s", driver_dir, sofile); if ((dlhandle = LoadDriverModule(filename)) == NULL) { #ifdef WIN32 @@ -524,24 +526,32 @@ static bool check_symbol(const char* sofile, const char* symbol) JSList * jack_drivers_load (JSList * drivers) { - char * driver_dir; + char* driver_dir; char driver_dir_storage[512]; char dll_filename[512]; WIN32_FIND_DATA filedata; HANDLE file; - const char * ptr = NULL; - JSList * driver_list = NULL; - jack_driver_desc_t * desc; + const char* ptr = NULL; + JSList* driver_list = NULL; + 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 - GetCurrentDirectory(512, driver_dir_storage); + if (3 < GetModuleFileName(NULL, driver_dir_storage, 512)) { + char *p = strrchr(driver_dir_storage, '\\'); + if (p && (p != driver_dir_storage)) + *p = 0; + else + GetCurrentDirectory(512, driver_dir_storage); + } else { + GetCurrentDirectory(512, driver_dir_storage); + } strcat(driver_dir_storage, "/"); strcat(driver_dir_storage, ADDON_DIR); driver_dir = driver_dir_storage; } - sprintf(dll_filename, "%s/*.dll", driver_dir); + snprintf(dll_filename, sizeof(dll_filename), "%s/*.dll", driver_dir); file = (HANDLE )FindFirstFile(dll_filename, &filedata); @@ -551,6 +561,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 +575,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); @@ -583,10 +603,10 @@ JSList * jack_drivers_load (JSList * drivers) { struct dirent * dir_entry; DIR * dir_stream; - const char * ptr; + const char* ptr; int err; - JSList * driver_list = NULL; - jack_driver_desc_t * desc; + JSList* driver_list = NULL; + jack_driver_desc_t* desc = NULL; const char* driver_dir; if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { @@ -618,8 +638,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 { @@ -647,29 +671,37 @@ jack_drivers_load (JSList * drivers) { JSList * jack_internals_load (JSList * internals) { - char * driver_dir; + char* driver_dir; char driver_dir_storage[512]; char dll_filename[512]; WIN32_FIND_DATA filedata; HANDLE file; - const char * ptr = NULL; - JSList * driver_list = NULL; - jack_driver_desc_t * desc; + const char* ptr = NULL; + JSList* driver_list = NULL; + jack_driver_desc_t* desc; if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { // for WIN32 ADDON_DIR is defined in JackConstants.h as relative path - GetCurrentDirectory(512, driver_dir_storage); + if (3 < GetModuleFileName(NULL, driver_dir_storage, 512)) { + char *p = strrchr(driver_dir_storage, '\\'); + if (p && (p != driver_dir_storage)) + *p = 0; + else + GetCurrentDirectory(512, driver_dir_storage); + } else { + GetCurrentDirectory(512, driver_dir_storage); + } strcat(driver_dir_storage, "/"); strcat(driver_dir_storage, ADDON_DIR); driver_dir = driver_dir_storage; } - sprintf(dll_filename, "%s/*.dll", driver_dir); + snprintf(dll_filename, sizeof(dll_filename), "%s/*.dll", driver_dir); 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; } @@ -712,10 +744,10 @@ JSList * jack_internals_load (JSList * internals) { struct dirent * dir_entry; DIR * dir_stream; - const char * ptr; + const char* ptr; int err; - JSList * driver_list = NULL; - jack_driver_desc_t * desc; + JSList* driver_list = NULL; + jack_driver_desc_t* desc; const char* driver_dir; if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { @@ -779,7 +811,7 @@ Jack::JackDriverClientInterface* JackDriverInfo::Open(jack_driver_desc_t* driver #ifdef WIN32 int errstr; #else - const char * errstr; + const char* errstr; #endif fHandle = LoadDriverModule (driver_desc->file); @@ -820,3 +852,106 @@ JackDriverInfo::~JackDriverInfo() if (fHandle) UnloadDriverModule(fHandle); } + +SERVER_EXPORT +jack_driver_desc_t* +jack_driver_descriptor_construct( + const char * name, + jack_driver_type_t type, + const char * description, + jack_driver_desc_filler_t * filler_ptr) +{ + size_t name_len; + size_t description_len; + jack_driver_desc_t* desc_ptr; + + name_len = strlen(name); + description_len = strlen(description); + + if (name_len > sizeof(desc_ptr->name) - 1 || + description_len > sizeof(desc_ptr->desc) - 1) { + assert(false); + return 0; + } + + desc_ptr = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); + if (desc_ptr == NULL) { + jack_error("calloc() failed to allocate memory for driver descriptor struct"); + return 0; + } + + memcpy(desc_ptr->name, name, name_len + 1); + memcpy(desc_ptr->desc, description, description_len + 1); + + desc_ptr->nparams = 0; + desc_ptr->type = type; + + if (filler_ptr != NULL) { + filler_ptr->size = 0; + } + + return desc_ptr; +} + +SERVER_EXPORT +int +jack_driver_descriptor_add_parameter( + jack_driver_desc_t* desc_ptr, + jack_driver_desc_filler_t * filler_ptr, + const char* name, + char character, + jack_driver_param_type_t type, + const jack_driver_param_value_t * value_ptr, + jack_driver_param_constraint_desc_t * constraint, + const char* short_desc, + const char* long_desc) +{ + size_t name_len; + size_t short_desc_len; + size_t long_desc_len; + jack_driver_param_desc_t * param_ptr; + size_t newsize; + + name_len = strlen(name); + short_desc_len = strlen(short_desc); + + if (long_desc != NULL) { + long_desc_len = strlen(long_desc); + } else { + long_desc = short_desc; + long_desc_len = short_desc_len; + } + + if (name_len > sizeof(param_ptr->name) - 1 || + short_desc_len > sizeof(param_ptr->short_desc) - 1 || + long_desc_len > sizeof(param_ptr->long_desc) - 1) { + assert(false); + return 0; + } + + if (desc_ptr->nparams == filler_ptr->size) { + newsize = filler_ptr->size + 20; // most drivers have less than 20 parameters + param_ptr = (jack_driver_param_desc_t*)realloc (desc_ptr->params, newsize * sizeof (jack_driver_param_desc_t)); + if (param_ptr == NULL) { + jack_error("realloc() failed for parameter array of %zu elements", newsize); + return false; + } + filler_ptr->size = newsize; + desc_ptr->params = param_ptr; + } + + assert(desc_ptr->nparams < filler_ptr->size); + param_ptr = desc_ptr->params + desc_ptr->nparams; + + memcpy(param_ptr->name, name, name_len + 1); + param_ptr->character = character; + param_ptr->type = type; + param_ptr->value = *value_ptr; + param_ptr->constraint = constraint; + memcpy(param_ptr->short_desc, short_desc, short_desc_len + 1); + memcpy(param_ptr->long_desc, long_desc, long_desc_len + 1); + + desc_ptr->nparams++; + + return true; +} diff --git a/common/JackDriverLoader.h b/common/JackDriverLoader.h index c194304d..dc8cc701 100644 --- a/common/JackDriverLoader.h +++ b/common/JackDriverLoader.h @@ -27,10 +27,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackDriver.h" #include "JackSystemDeps.h" -typedef jack_driver_desc_t * (*JackDriverDescFunction) (); +typedef jack_driver_desc_t* (*JackDriverDescFunction) (); typedef Jack::JackDriverClientInterface* (*driverInitialize) (Jack::JackLockedEngine*, Jack::JackSynchro*, const JSList*); -class JackDriverInfo +class SERVER_EXPORT JackDriverInfo { private: @@ -54,14 +54,23 @@ class JackDriverInfo }; -jack_driver_desc_t * jack_find_driver_descriptor(JSList * drivers, const char * name); +jack_driver_desc_t* jack_find_driver_descriptor(JSList* drivers, const char* name); -JSList * jack_drivers_load(JSList * drivers); -JSList * jack_internals_load(JSList * internals); +JSList* jack_drivers_load(JSList* drivers); +JSList* jack_internals_load(JSList* internals); -EXPORT int jackctl_parse_driver_params (jackctl_driver * driver_ptr, int argc, char* argv[]); -EXPORT void jack_free_driver_params(JSList * param_ptr); -EXPORT void jack_print_driver_options(jack_driver_desc_t* desc, FILE* file); +#ifdef __cplusplus +extern "C" +{ +#endif + +SERVER_EXPORT int jackctl_parse_driver_params(jackctl_driver * driver_ptr, int argc, char* argv[]); +SERVER_EXPORT void jack_free_driver_params(JSList * param_ptr); +SERVER_EXPORT void jack_print_driver_options(jack_driver_desc_t* desc, FILE* file); + +#ifdef __cplusplus +} +#endif #endif diff --git a/common/JackDummyDriver.cpp b/common/JackDummyDriver.cpp index fb996a11..f24f659e 100644 --- a/common/JackDummyDriver.cpp +++ b/common/JackDummyDriver.cpp @@ -19,71 +19,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackDummyDriver.h" -#include "JackEngineControl.h" -#include "JackGraphManager.h" #include "JackDriverLoader.h" #include "JackThreadedDriver.h" #include "JackCompilerDeps.h" #include #include +#include -namespace Jack -{ - -int JackDummyDriver::Open(jack_nframes_t buffer_size, - jack_nframes_t samplerate, - 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) -{ - if (JackAudioDriver::Open(buffer_size, - samplerate, - capturing, - playing, - inchannels, - outchannels, - monitor, - capture_driver_name, - playback_driver_name, - capture_latency, - playback_latency) == 0) { - fEngineControl->fPeriod = 0; - fEngineControl->fComputation = 500 * 1000; - fEngineControl->fConstraint = 500 * 1000; - int buffer_size = int((fWaitTime * fEngineControl->fSampleRate) / 1000000.0f); - if (buffer_size > BUFFER_SIZE_MAX) { - buffer_size = BUFFER_SIZE_MAX; - jack_error("Buffer size set to %d ", BUFFER_SIZE_MAX); - } - SetBufferSize(buffer_size); - return 0; - } else { - return -1; - } -} - -int JackDummyDriver::Process() -{ - JackDriver::CycleTakeBeginTime(); - JackAudioDriver::Process(); - JackSleep(std::max(0L, long(fWaitTime - (GetMicroSeconds() - fBeginDateUst)))); - return 0; -} - -int JackDummyDriver::SetBufferSize(jack_nframes_t buffer_size) -{ - JackAudioDriver::SetBufferSize(buffer_size); - fWaitTime = (unsigned long)((((float)buffer_size) / ((float)fEngineControl->fSampleRate)) * 1000000.0f); - return 0; -} - -} // end of namespace #ifdef __cplusplus extern "C" @@ -92,73 +34,36 @@ extern "C" SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t * desc; - unsigned int i; - - desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); - strcpy(desc->name, "dummy"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 - strcpy(desc->desc, "Timer based backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 - - desc->nparams = 6; - desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); - - i = 0; - strcpy(desc->params[i].name, "capture"); - desc->params[i].character = 'C'; - desc->params[i].type = JackDriverParamUInt; - desc->params[i].value.ui = 2U; - strcpy(desc->params[i].short_desc, "Number of capture ports"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); - - i++; - strcpy(desc->params[i].name, "playback"); - desc->params[i].character = 'P'; - desc->params[i].type = JackDriverParamUInt; - desc->params[1].value.ui = 2U; - strcpy(desc->params[i].short_desc, "Number of playback ports"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); - - i++; - strcpy(desc->params[i].name, "rate"); - desc->params[i].character = 'r'; - desc->params[i].type = JackDriverParamUInt; - desc->params[i].value.ui = 48000U; - strcpy(desc->params[i].short_desc, "Sample rate"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); - - i++; - strcpy(desc->params[i].name, "monitor"); - desc->params[i].character = 'm'; - desc->params[i].type = JackDriverParamBool; - desc->params[i].value.i = 0; - strcpy(desc->params[i].short_desc, "Provide monitor ports for the output"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); - - i++; - strcpy(desc->params[i].name, "period"); - desc->params[i].character = 'p'; - desc->params[i].type = JackDriverParamUInt; - desc->params[i].value.ui = 1024U; - strcpy(desc->params[i].short_desc, "Frames per period"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); - - i++; - strcpy(desc->params[i].name, "wait"); - desc->params[i].character = 'w'; - desc->params[i].type = JackDriverParamUInt; - desc->params[i].value.ui = 21333U; - strcpy(desc->params[i].short_desc, - "Number of usecs to wait between engine processes"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("dummy", JackDriverMaster, "Timer based backend", &filler); + + value.ui = 2U; + jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamUInt, &value, NULL, "Number of capture ports", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamUInt, &value, NULL, "Number of playback ports", NULL); + + value.ui = 48000U; + jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); + + value.ui = 1024U; + jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); + + value.ui = 21333U; + jack_driver_descriptor_add_parameter(desc, &filler, "wait", 'w', JackDriverParamUInt, &value, NULL, "Number of usecs to wait between engine processes", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { jack_nframes_t sample_rate = 48000; - jack_nframes_t period_size = 1024; + jack_nframes_t buffer_size = 1024; unsigned int capture_ports = 2; unsigned int playback_ports = 2; - unsigned long wait_time = 0; + int wait_time = 0; const JSList * node; const jack_driver_param_t * param; bool monitor = false; @@ -181,7 +86,7 @@ extern "C" break; case 'p': - period_size = param->value.ui; + buffer_size = param->value.ui; break; case 'w': @@ -194,11 +99,16 @@ extern "C" } } - if (wait_time == 0) // Not set - wait_time = (unsigned long)((((float)period_size) / ((float)sample_rate)) * 1000000.0f); + if (wait_time > 0) { + buffer_size = lroundf((wait_time * sample_rate) / 1000000.0f); + if (buffer_size > BUFFER_SIZE_MAX) { + buffer_size = BUFFER_SIZE_MAX; + jack_error("Buffer size set to %d", BUFFER_SIZE_MAX); + } + } - Jack::JackDriverClientInterface* driver = new Jack::JackThreadedDriver(new Jack::JackDummyDriver("system", "dummy_pcm", engine, table, wait_time)); - if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, monitor, "dummy", "dummy", 0, 0) == 0) { + Jack::JackDriverClientInterface* driver = new Jack::JackThreadedDriver(new Jack::JackDummyDriver("system", "dummy_pcm", engine, table)); + if (driver->Open(buffer_size, sample_rate, 1, 1, capture_ports, playback_ports, monitor, "dummy", "dummy", 0, 0) == 0) { return driver; } else { delete driver; diff --git a/common/JackDummyDriver.h b/common/JackDummyDriver.h index cfd6b474..5bab9836 100644 --- a/common/JackDummyDriver.h +++ b/common/JackDummyDriver.h @@ -21,7 +21,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackDummyDriver__ #define __JackDummyDriver__ -#include "JackAudioDriver.h" +#include "JackTimedDriver.h" namespace Jack { @@ -30,42 +30,29 @@ namespace Jack \brief The dummy driver. */ -class JackDummyDriver : public JackAudioDriver +class JackDummyDriver : public JackTimedDriver { - private: - - long fWaitTime; public: - JackDummyDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, unsigned long wait_time) - : JackAudioDriver(name, alias, engine, table), fWaitTime(wait_time) + JackDummyDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackTimedDriver(name, alias, engine, table) {} virtual ~JackDummyDriver() {} - int Open(jack_nframes_t buffersize, - jack_nframes_t samplerate, - bool capturing, - bool playing, - int chan_in, - int chan_out, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency); - - int Process(); - - // BufferSize can be changed - bool IsFixedBufferSize() + virtual int Process() { - return false; + JackDriver::CycleTakeBeginTime(); + + if (JackAudioDriver::Process() < 0) { + return -1; + } else { + ProcessWait(); + return 0; + } } - int SetBufferSize(jack_nframes_t buffer_size); - }; } // end of namespace diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index fe77c08e..fa69574c 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -142,7 +142,6 @@ void JackEngine::ProcessNext(jack_time_t cur_cycle_begin) fLastSwitchUsecs = cur_cycle_begin; if (fGraphManager->RunNextGraph()) { // True if the graph actually switched to a new state fChannel.Notify(ALL_CLIENTS, kGraphOrderCallback, 0); - //NotifyGraphReorder(); } fSignal.Signal(); // Signal for threads waiting for next cycle } @@ -200,13 +199,11 @@ void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions if (status != NotTriggered && status != Finished) { jack_error("JackEngine::XRun: client = %s was not run: state = %ld", client->GetClientControl()->fName, status); fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients - //NotifyXRun(ALL_CLIENTS); } if (status == Finished && (long)(finished_date - callback_usecs) > 0) { jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName); fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients - //NotifyXRun(ALL_CLIENTS); } } } @@ -252,11 +249,11 @@ void JackEngine::NotifyClient(int refnum, int event, int sync, const char* messa /* Important for internal clients : unlock before calling the notification callbacks. */ - bool res = fMutex.Unlock(); + bool res = Unlock(); if (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, message, value1, value2) < 0) jack_error("NotifyClient fails name = %s event = %ld val1 = %ld val2 = %ld", client->GetClientControl()->fName, event, value1, value2); if (res) - fMutex.Lock(); + Lock(); } else { jack_log("JackEngine::NotifyClient: no callback for event = %ld", event); @@ -277,10 +274,10 @@ int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* nam // Notify existing clients of the new client and new client of existing clients. for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* old_client = fClientTable[i]; - if (old_client) { - if (old_client->ClientNotify(refnum, name, kAddClient, true, "", 0, 0) < 0) { + if (old_client && old_client != new_client) { + if (old_client->ClientNotify(refnum, name, kAddClient, false, "", 0, 0) < 0) { jack_error("NotifyAddClient old_client fails name = %s", old_client->GetClientControl()->fName); - return -1; + // Not considered as a failure... } if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, kAddClient, true, "", 0, 0) < 0) { jack_error("NotifyAddClient new_client fails name = %s", name); @@ -298,7 +295,7 @@ void JackEngine::NotifyRemoveClient(const char* name, int refnum) for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client) { - client->ClientNotify(refnum, name, kRemoveClient, true, "",0, 0); + client->ClientNotify(refnum, name, kRemoveClient, false, "", 0, 0); } } } @@ -309,7 +306,6 @@ void JackEngine::NotifyXRun(jack_time_t callback_usecs, float delayed_usecs) // Use the audio thread => request thread communication channel fEngineControl->NotifyXRun(callback_usecs, delayed_usecs); fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); - //NotifyXRun(ALL_CLIENTS); } void JackEngine::NotifyXRun(int refnum) @@ -429,7 +425,7 @@ int JackEngine::ClientCheck(const char* name, int uuid, char* name_res, int prot *status = 0; strcpy(name_res, name); - jack_log("Check protocol client %ld server = %ld", protocol, JACK_PROTOCOL_VERSION); + jack_log("Check protocol client = %ld server = %ld", protocol, JACK_PROTOCOL_VERSION); if (protocol != JACK_PROTOCOL_VERSION) { *status |= (JackFailure | JackVersionError); @@ -565,7 +561,6 @@ int JackEngine::ClientExternalOpen(const char* name, int pid, int uuid, int* ref } else { strncpy(real_name, name, JACK_CLIENT_NAME_SIZE); } - EnsureUUID(uuid); } @@ -688,12 +683,12 @@ int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wai int i; fGraphManager->GetInputPorts(refnum, ports); - for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) { + for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY); i++) { PortUnRegister(refnum, ports[i]); } fGraphManager->GetOutputPorts(refnum, ports); - for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) { + for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY); i++) { PortUnRegister(refnum, ports[i]); } @@ -806,6 +801,7 @@ int JackEngine::PortRegister(int refnum, const char* name, const char *type, uns return -1; } + // buffer_size is actually ignored... *port_index = fGraphManager->AllocatePort(refnum, name, type, (JackPortFlags)flags, fEngineControl->fBufferSize); if (*port_index != NO_PORT) { if (client->GetClientControl()->fActive) @@ -1004,7 +1000,7 @@ int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t ds int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name) { - char old_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char old_name[REAL_JACK_PORT_NAME_SIZE]; strcpy(old_name, fGraphManager->GetPort(port)->GetName()); fGraphManager->GetPort(port)->SetName(name); NotifyPortRename(port, old_name); @@ -1015,12 +1011,15 @@ int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name) // Session management //-------------------- -void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket) +void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket, JackSessionNotifyResult** result) { if (fSessionPendingReplies != 0) { JackSessionNotifyResult res(-1); res.Write(socket); jack_log("JackEngine::SessionNotify ... busy"); + if (result != NULL) { + *result = NULL; + } return; } @@ -1044,30 +1043,36 @@ void JackEngine::SessionNotify(int refnum, const char *target, jack_session_even } char path_buf[JACK_PORT_NAME_SIZE]; - snprintf( path_buf, sizeof(path_buf), "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR ); + snprintf(path_buf, sizeof(path_buf), "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR); int res = JackTools::MkDir(path_buf); if (res) - jack_error( "JackEngine::SessionNotify: can not create session directory '%s'", path_buf ); + jack_error("JackEngine::SessionNotify: can not create session directory '%s'", path_buf); - int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int) type, 0); + int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int)type, 0); - if (result == 2) { + if (result == kPendingSessionReply) { fSessionPendingReplies += 1; - } else if (result == 1) { + } else if (result == kImmediateSessionReply) { char uuid_buf[JACK_UUID_SIZE]; - snprintf( uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID ); - fSessionResult->fCommandList.push_back( JackSessionCommand( uuid_buf, - client->GetClientControl()->fName, - client->GetClientControl()->fSessionCommand, - client->GetClientControl()->fSessionFlags )); + snprintf(uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID); + fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf, + client->GetClientControl()->fName, + client->GetClientControl()->fSessionCommand, + client->GetClientControl()->fSessionFlags)); } } } + if (result != NULL) { + *result = fSessionResult; + } + if (fSessionPendingReplies == 0) { fSessionResult->Write(socket); - delete fSessionResult; + if (result == NULL) { + delete fSessionResult; + } fSessionResult = NULL; } else { fSessionTransaction = socket; @@ -1078,7 +1083,7 @@ void JackEngine::SessionReply(int refnum) { JackClientInterface* client = fClientTable[refnum]; char uuid_buf[JACK_UUID_SIZE]; - snprintf( uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID); + snprintf(uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID); fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf, client->GetClientControl()->fName, client->GetClientControl()->fSessionCommand, @@ -1087,7 +1092,10 @@ void JackEngine::SessionReply(int refnum) if (fSessionPendingReplies == 0) { fSessionResult->Write(fSessionTransaction); - delete fSessionResult; + if (fSessionTransaction != NULL) + { + delete fSessionResult; + } fSessionResult = NULL; } } @@ -1143,11 +1151,11 @@ void JackEngine::ReserveClientName(const char *name, const char *uuid, int *resu *result = 0; } -void JackEngine::ClientHasSessionCallbackRequest(const char *name, int *result) +void JackEngine::ClientHasSessionCallback(const char *name, int *result) { JackClientInterface* client = NULL; for (int i = 0; i < CLIENT_NUM; i++) { - JackClientInterface* client = fClientTable[i]; + client = fClientTable[i]; if (client && (strcmp(client->GetClientControl()->fName, name) == 0)) break; } diff --git a/common/JackEngine.h b/common/JackEngine.h index 355f005e..c8c3c6a9 100644 --- a/common/JackEngine.h +++ b/common/JackEngine.h @@ -55,8 +55,8 @@ class SERVER_EXPORT JackEngine : public JackLockAble jack_time_t fLastSwitchUsecs; int fSessionPendingReplies; - JackChannelTransaction *fSessionTransaction; - JackSessionNotifyResult *fSessionResult; + JackChannelTransaction* fSessionTransaction; + JackSessionNotifyResult* fSessionResult; std::map fReservationMap; int fMaxUUID; @@ -148,13 +148,13 @@ class SERVER_EXPORT JackEngine : public JackLockAble void NotifyQuit(); // Session management - void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket); + void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket, JackSessionNotifyResult** result); void SessionReply(int refnum); void GetUUIDForClientName(const char *client_name, char *uuid_res, int *result); void GetClientNameForUUID(const char *uuid, char *name_res, int *result); void ReserveClientName(const char *name, const char *uuid, int *result); - void ClientHasSessionCallbackRequest(const char *name, int *result); + void ClientHasSessionCallback(const char *name, int *result); }; diff --git a/common/JackEngineControl.cpp b/common/JackEngineControl.cpp index d9ab2b16..4af6f68d 100644 --- a/common/JackEngineControl.cpp +++ b/common/JackEngineControl.cpp @@ -53,23 +53,37 @@ void JackEngineControl::CalcCPULoad(JackClientInterface** table, } // Store the execution time for later averaging - fRollingClientUsecs[fRollingClientUsecsIndex++] = last_cycle_end - fPrevCycleTime; + if (last_cycle_end > 0) + fRollingClientUsecs[fRollingClientUsecsIndex++] = last_cycle_end - fPrevCycleTime; if (fRollingClientUsecsIndex >= JACK_ENGINE_ROLLING_COUNT) fRollingClientUsecsIndex = 0; - // Every so often, recompute the current maximum use over the - // last JACK_ENGINE_ROLLING_COUNT client iterations. - - if (++fRollingClientUsecsCnt % fRollingInterval == 0) { - + // Each time we have a full set of iterations, recompute the current + // usage from the latest JACK_ENGINE_ROLLING_COUNT client entries. + if (fRollingClientUsecsCnt && (fRollingClientUsecsIndex == 0)) { + jack_time_t avg_usecs = 0; jack_time_t max_usecs = 0; - for (int i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) + + for (int i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) { + avg_usecs += fRollingClientUsecs[i]; // This is really a running + // total to be averaged later max_usecs = JACK_MAX(fRollingClientUsecs[i], max_usecs); + } fMaxUsecs = JACK_MAX(fMaxUsecs, max_usecs); - fSpareUsecs = jack_time_t((max_usecs < fPeriodUsecs) ? fPeriodUsecs - max_usecs : 0); + + if (max_usecs < ((fPeriodUsecs * 95) / 100)) { + // Average the values from our JACK_ENGINE_ROLLING_COUNT array + fSpareUsecs = (jack_time_t)(fPeriodUsecs - (avg_usecs / JACK_ENGINE_ROLLING_COUNT)); + } else { + // Use the 'worst case' value (or zero if we exceeded 'fPeriodUsecs') + fSpareUsecs = jack_time_t((max_usecs < fPeriodUsecs) ? fPeriodUsecs - max_usecs : 0); + } + fCPULoad = ((1.f - (float(fSpareUsecs) / float(fPeriodUsecs))) * 50.f + (fCPULoad * 0.5f)); } + + fRollingClientUsecsCnt++; } void JackEngineControl::ResetRollingUsecs() diff --git a/common/JackEngineControl.h b/common/JackEngineControl.h index 3ba54b87..32445958 100644 --- a/common/JackEngineControl.h +++ b/common/JackEngineControl.h @@ -45,6 +45,7 @@ class JackGraphManager; \brief Engine control in shared memory. */ +PRE_PACKED_STRUCTURE struct SERVER_EXPORT JackEngineControl : public JackShmMem { // Shared state @@ -69,12 +70,12 @@ struct SERVER_EXPORT JackEngineControl : public JackShmMem bool fVerbose; // CPU Load - jack_time_t fPrevCycleTime; - jack_time_t fCurCycleTime; - jack_time_t fSpareUsecs; - jack_time_t fMaxUsecs; - jack_time_t fRollingClientUsecs[JACK_ENGINE_ROLLING_COUNT]; - int fRollingClientUsecsCnt; + jack_time_t fPrevCycleTime; + jack_time_t fCurCycleTime; + jack_time_t fSpareUsecs; + jack_time_t fMaxUsecs; + jack_time_t fRollingClientUsecs[JACK_ENGINE_ROLLING_COUNT]; + unsigned int fRollingClientUsecsCnt; int fRollingClientUsecsIndex; int fRollingInterval; float fCPULoad; diff --git a/common/JackEngineProfiling.cpp b/common/JackEngineProfiling.cpp index 4557fec9..f70cc7e4 100644 --- a/common/JackEngineProfiling.cpp +++ b/common/JackEngineProfiling.cpp @@ -12,7 +12,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -34,7 +34,7 @@ namespace Jack JackEngineProfiling::JackEngineProfiling():fAudioCycle(0),fMeasuredClient(0) { jack_info("Engine profiling activated, beware %ld MBytes are needed to record profiling points...", sizeof(fProfileTable) / (1024 * 1024)); - + // Force memory page in memset(fProfileTable, 0, sizeof(fProfileTable)); } @@ -47,32 +47,32 @@ JackEngineProfiling::~JackEngineProfiling() if (!fStream.is_open()) { jack_error("JackEngineProfiling::Save cannot open JackEngineProfiling.log file"); } else { - + // For each measured point for (int i = 2; i < TIME_POINTS; i++) { - + // Driver timing values long d1 = long(fProfileTable[i].fCurCycleBegin - fProfileTable[i - 1].fCurCycleBegin); long d2 = long(fProfileTable[i].fPrevCycleEnd - fProfileTable[i - 1].fCurCycleBegin); - + if (d1 <= 0 || fProfileTable[i].fAudioCycle <= 0) continue; // Skip non valid cycles - + // Print driver delta and end cycle fStream << d1 << "\t" << d2 << "\t"; - + // For each measured client - for (unsigned int j = 0; j < fMeasuredClient; j++) { - + for (unsigned int j = 0; j < fMeasuredClient; j++) { + int ref = fIntervalTable[j].fRefNum; - - // Is valid client cycle - if (fProfileTable[i].fClientTable[ref].fStatus != NotTriggered) { - + + // Is valid client cycle + if (fProfileTable[i].fClientTable[ref].fStatus != NotTriggered) { + long d5 = long(fProfileTable[i].fClientTable[ref].fSignaledAt - fProfileTable[i - 1].fCurCycleBegin); long d6 = long(fProfileTable[i].fClientTable[ref].fAwakeAt - fProfileTable[i - 1].fCurCycleBegin); long d7 = long(fProfileTable[i].fClientTable[ref].fFinishedAt - fProfileTable[i - 1].fCurCycleBegin); - + fStream << ref << "\t" ; fStream << ((d5 > 0) ? d5 : 0) << "\t"; fStream << ((d6 > 0) ? d6 : 0) << "\t" ; @@ -80,73 +80,73 @@ JackEngineProfiling::~JackEngineProfiling() fStream << ((d6 > 0 && d5 > 0) ? (d6 - d5) : 0) << "\t" ; fStream << ((d7 > 0 && d6 > 0) ? (d7 - d6) : 0) << "\t" ; fStream << fProfileTable[i].fClientTable[ref].fStatus << "\t" ;; - - } else { // Print tabs + + } else { // Print tabs fStream << "\t \t \t \t \t \t \t"; } } - + // Terminate line fStream << std::endl; } } - + // Driver period std::ofstream fStream1("Timing1.plot", std::ios_base::ate); - + if (!fStream1.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing1.plot file"); } else { - + fStream1 << "set grid\n"; fStream1 << "set title \"Audio driver timing\"\n"; fStream1 << "set xlabel \"audio cycles\"\n"; fStream1 << "set ylabel \"usec\"\n"; fStream1 << "plot \"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines \n"; - + fStream1 << "set output 'Timing1.svg\n"; fStream1 << "set terminal svg\n"; - + fStream1 << "set grid\n"; fStream1 << "set title \"Audio driver timing\"\n"; fStream1 << "set xlabel \"audio cycles\"\n"; fStream1 << "set ylabel \"usec\"\n"; fStream1 << "plot \"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines \n"; - fStream1 << "unset output\n"; + fStream1 << "unset output\n"; } - + // Driver end date std::ofstream fStream2("Timing2.plot", std::ios_base::ate); - + if (!fStream2.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing2.plot file"); } else { - + fStream2 << "set grid\n"; fStream2 << "set title \"Driver end date\"\n"; fStream2 << "set xlabel \"audio cycles\"\n"; fStream2 << "set ylabel \"usec\"\n"; fStream2 << "plot \"JackEngineProfiling.log\" using 2 title \"Driver end date\" with lines \n"; - + fStream2 << "set output 'Timing2.svg\n"; fStream2 << "set terminal svg\n"; - + fStream2 << "set grid\n"; fStream2 << "set title \"Driver end date\"\n"; fStream2 << "set xlabel \"audio cycles\"\n"; fStream2 << "set ylabel \"usec\"\n"; fStream2 << "plot \"JackEngineProfiling.log\" using 2 title \"Driver end date\" with lines \n"; - fStream2 << "unset output\n"; + fStream2 << "unset output\n"; } - + // Clients end date if (fMeasuredClient > 0) { std::ofstream fStream3("Timing3.plot", std::ios_base::ate); - + if (!fStream3.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing3.plot file"); } else { - + fStream3 << "set multiplot\n"; fStream3 << "set grid\n"; fStream3 << "set title \"Clients end date\"\n"; @@ -170,11 +170,11 @@ JackEngineProfiling::~JackEngineProfiling() fStream3 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) - 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } - - fStream3 << "\n unset multiplot\n"; + + fStream3 << "\n unset multiplot\n"; fStream3 << "set output 'Timing3.svg\n"; fStream3 << "set terminal svg\n"; - + fStream3 << "set multiplot\n"; fStream3 << "set grid\n"; fStream3 << "set title \"Clients end date\"\n"; @@ -198,19 +198,19 @@ JackEngineProfiling::~JackEngineProfiling() fStream3 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) - 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } - fStream3 << "\nunset multiplot\n"; - fStream3 << "unset output\n"; + fStream3 << "\nunset multiplot\n"; + fStream3 << "unset output\n"; } } // Clients scheduling if (fMeasuredClient > 0) { std::ofstream fStream4("Timing4.plot", std::ios_base::ate); - + if (!fStream4.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing4.plot file"); } else { - + fStream4 << "set multiplot\n"; fStream4 << "set grid\n"; fStream4 << "set title \"Clients scheduling latency\"\n"; @@ -224,11 +224,11 @@ JackEngineProfiling::~JackEngineProfiling() fStream4 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } - - fStream4 << "\n unset multiplot\n"; + + fStream4 << "\n unset multiplot\n"; fStream4 << "set output 'Timing4.svg\n"; fStream4 << "set terminal svg\n"; - + fStream4 << "set multiplot\n"; fStream4 << "set grid\n"; fStream4 << "set title \"Clients scheduling latency\"\n"; @@ -242,11 +242,11 @@ JackEngineProfiling::~JackEngineProfiling() fStream4 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } - fStream4 << "\nunset multiplot\n"; - fStream4 << "unset output\n"; + fStream4 << "\nunset multiplot\n"; + fStream4 << "unset output\n"; } } - + // Clients duration if (fMeasuredClient > 0) { std::ofstream fStream5("Timing5.plot", std::ios_base::ate); @@ -254,7 +254,7 @@ JackEngineProfiling::~JackEngineProfiling() if (!fStream5.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing5.plot file"); } else { - + fStream5 << "set multiplot\n"; fStream5 << "set grid\n"; fStream5 << "set title \"Clients duration\"\n"; @@ -268,11 +268,11 @@ JackEngineProfiling::~JackEngineProfiling() fStream5 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) + 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } - - fStream5 << "\n unset multiplot\n"; + + fStream5 << "\n unset multiplot\n"; fStream5 << "set output 'Timing5.svg\n"; fStream5 << "set terminal svg\n"; - + fStream5 << "set multiplot\n"; fStream5 << "set grid\n"; fStream5 << "set title \"Clients duration\"\n"; @@ -286,11 +286,11 @@ JackEngineProfiling::~JackEngineProfiling() fStream5 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) + 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } - fStream5 << "\nunset multiplot\n"; - fStream5 << "unset output\n"; + fStream5 << "\nunset multiplot\n"; + fStream5 << "unset output\n"; } } - + std::ofstream fStream6("Timings.html", std::ios_base::ate); if (!fStream6.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timings.html file"); @@ -315,8 +315,8 @@ JackEngineProfiling::~JackEngineProfiling() fStream6 << "
Timing5
"; fStream6 << " \n"; fStream6 << "\n"; - } - + } + std::ofstream fStream7("generate_timings", std::ios_base::ate); if (!fStream7.is_open()) { jack_error("JackEngineProfiling::Save cannot open generate_timings file"); @@ -326,7 +326,7 @@ JackEngineProfiling::~JackEngineProfiling() fStream7 << "gnuplot -persist Timing3.plot\n"; fStream7 << "gnuplot -persist Timing4.plot\n"; fStream7 << "gnuplot -persist Timing5.plot\n"; - } + } } bool JackEngineProfiling::CheckClient(const char* name, int cur_point) @@ -340,14 +340,14 @@ bool JackEngineProfiling::CheckClient(const char* name, int cur_point) return false; } -void JackEngineProfiling::Profile(JackClientInterface** table, - JackGraphManager* manager, +void JackEngineProfiling::Profile(JackClientInterface** table, + JackGraphManager* manager, jack_time_t period_usecs, - jack_time_t cur_cycle_begin, + jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end) { fAudioCycle = (fAudioCycle + 1) % TIME_POINTS; - + // Keeps cycle data fProfileTable[fAudioCycle].fPeriodUsecs = period_usecs; fProfileTable[fAudioCycle].fCurCycleBegin = cur_cycle_begin; @@ -358,7 +358,7 @@ void JackEngineProfiling::Profile(JackClientInterface** table, JackClientInterface* client = table[i]; JackClientTiming* timing = manager->GetClientTiming(i); if (client && client->GetClientControl()->fActive && client->GetClientControl()->fCallback[kRealTimeCallback]) { - + if (!CheckClient(client->GetClientControl()->fName, fAudioCycle)) { // Keep new measured client fIntervalTable[fMeasuredClient].fRefNum = i; @@ -380,5 +380,5 @@ JackTimingMeasure* JackEngineProfiling::GetCurMeasure() { return &fProfileTable[fAudioCycle]; } - + } // end of namespace diff --git a/common/JackEngineProfiling.h b/common/JackEngineProfiling.h index c06487a0..04fdb953 100644 --- a/common/JackEngineProfiling.h +++ b/common/JackEngineProfiling.h @@ -37,6 +37,7 @@ namespace Jack \brief Timing stucture for a client. */ +PRE_PACKED_STRUCTURE struct JackTimingMeasureClient { int fRefNum; @@ -59,6 +60,7 @@ struct JackTimingMeasureClient \brief Timing interval in the global table for a given client */ +PRE_PACKED_STRUCTURE struct JackTimingClientInterval { int fRefNum; @@ -78,6 +80,7 @@ struct JackTimingClientInterval \brief Timing stucture for a table of clients. */ +PRE_PACKED_STRUCTURE struct JackTimingMeasure { unsigned int fAudioCycle; @@ -102,6 +105,7 @@ struct JackTimingMeasure class JackClientInterface; class JackGraphManager; +PRE_PACKED_STRUCTURE class SERVER_EXPORT JackEngineProfiling { diff --git a/common/JackError.cpp b/common/JackError.cpp index 7763d997..d78f95e5 100644 --- a/common/JackError.cpp +++ b/common/JackError.cpp @@ -33,7 +33,7 @@ static bool change_thread_log_function(jack_log_function_t log_function) && jack_tls_set(JackGlobals::fKeyLogFunction, (void*)log_function)); } -EXPORT int set_threaded_log_function() +SERVER_EXPORT int set_threaded_log_function() { return change_thread_log_function(JackMessageBufferAdd); } @@ -88,7 +88,7 @@ static void jack_format_and_log(int level, const char *prefix, const char *fmt, log_function(level, buffer); } -EXPORT void jack_error(const char *fmt, ...) +SERVER_EXPORT void jack_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -96,7 +96,7 @@ EXPORT void jack_error(const char *fmt, ...) va_end(ap); } -EXPORT void jack_info(const char *fmt, ...) +SERVER_EXPORT void jack_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -104,7 +104,7 @@ EXPORT void jack_info(const char *fmt, ...) va_end(ap); } -EXPORT void jack_log(const char *fmt,...) +SERVER_EXPORT void jack_log(const char *fmt,...) { if (JackGlobals::fVerbose) { va_list ap; @@ -114,23 +114,23 @@ EXPORT void jack_log(const char *fmt,...) } } -EXPORT void default_jack_error_callback(const char *desc) +SERVER_EXPORT void default_jack_error_callback(const char *desc) { fprintf(stderr, "%s\n", desc); fflush(stderr); } -EXPORT void default_jack_info_callback(const char *desc) +SERVER_EXPORT void default_jack_info_callback(const char *desc) { fprintf(stdout, "%s\n", desc); fflush(stdout); } -EXPORT void silent_jack_error_callback(const char *desc) +SERVER_EXPORT void silent_jack_error_callback(const char *desc) {} -EXPORT void silent_jack_info_callback(const char *desc) +SERVER_EXPORT void silent_jack_info_callback(const char *desc) {} -EXPORT void (*jack_error_callback)(const char *desc) = &default_jack_error_callback; -EXPORT void (*jack_info_callback)(const char *desc) = &default_jack_info_callback; +SERVER_EXPORT void (*jack_error_callback)(const char *desc) = &default_jack_error_callback; +SERVER_EXPORT void (*jack_info_callback)(const char *desc) = &default_jack_info_callback; diff --git a/common/JackError.h b/common/JackError.h index 15e39f86..3e9d890a 100644 --- a/common/JackError.h +++ b/common/JackError.h @@ -34,27 +34,27 @@ extern "C" #define LOG_LEVEL_INFO 1 #define LOG_LEVEL_ERROR 2 - EXPORT void jack_error(const char *fmt, ...); + SERVER_EXPORT void jack_error(const char *fmt, ...); - EXPORT void jack_info(const char *fmt, ...); + SERVER_EXPORT void jack_info(const char *fmt, ...); // like jack_info() but only if verbose mode is enabled - EXPORT void jack_log(const char *fmt, ...); + SERVER_EXPORT void jack_log(const char *fmt, ...); - EXPORT extern void (*jack_error_callback)(const char *desc); - EXPORT extern void (*jack_info_callback)(const char *desc); + SERVER_EXPORT extern void (*jack_error_callback)(const char *desc); + SERVER_EXPORT extern void (*jack_info_callback)(const char *desc); - EXPORT extern void default_jack_error_callback(const char *desc); - EXPORT extern void default_jack_info_callback(const char *desc); + SERVER_EXPORT extern void default_jack_error_callback(const char *desc); + SERVER_EXPORT extern void default_jack_info_callback(const char *desc); - EXPORT extern void silent_jack_error_callback(const char *desc); - EXPORT extern void silent_jack_info_callback(const char *desc); + SERVER_EXPORT extern void silent_jack_error_callback(const char *desc); + SERVER_EXPORT extern void silent_jack_info_callback(const char *desc); typedef void (* jack_log_function_t)(int level, const char *message); void jack_log_function(int level, const char *message); - EXPORT int set_threaded_log_function(); + SERVER_EXPORT int set_threaded_log_function(); #ifdef __cplusplus } diff --git a/common/JackException.h b/common/JackException.h index 58a9a23b..8e873e9e 100644 --- a/common/JackException.h +++ b/common/JackException.h @@ -23,12 +23,18 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include #include -#include "JackCompilerDeps.h" #include "JackError.h" namespace Jack { +#define ThrowIf(inCondition, inException) \ + if(inCondition) \ + { \ + throw(inException); \ + } + + /*! \brief Exception base class. */ @@ -52,8 +58,9 @@ class SERVER_EXPORT JackException : public std::runtime_error { void PrintMessage() { std::string str = what(); - if (str != "") + if (str != "") { jack_info(str.c_str()); + } } }; @@ -62,9 +69,9 @@ class SERVER_EXPORT JackException : public std::runtime_error { */ class SERVER_EXPORT JackTemporaryException : public JackException { - + public: - + JackTemporaryException(const std::string& msg) : JackException(msg) {} JackTemporaryException(char* msg) : JackException(msg) @@ -74,15 +81,15 @@ class SERVER_EXPORT JackTemporaryException : public JackException { JackTemporaryException() : JackException("") {} }; - + /*! - \brief + \brief */ class SERVER_EXPORT JackQuitException : public JackException { - + public: - + JackQuitException(const std::string& msg) : JackException(msg) {} JackQuitException(char* msg) : JackException(msg) @@ -92,7 +99,7 @@ class SERVER_EXPORT JackQuitException : public JackException { JackQuitException() : JackException("") {} }; - + /*! \brief Exception possibly thrown by Net slaves. */ diff --git a/common/JackExternalClient.cpp b/common/JackExternalClient.cpp index 1dca599a..5fd1b60f 100644 --- a/common/JackExternalClient.cpp +++ b/common/JackExternalClient.cpp @@ -36,7 +36,7 @@ JackExternalClient::~JackExternalClient() int JackExternalClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { int result = -1; - jack_log("JackExternalClient::ClientNotify ref = %ld name = %s notify = %ld", refnum, name, notify); + jack_log("JackExternalClient::ClientNotify ref = %ld client = %s name = %s notify = %ld", refnum, fClientControl->fName, name, notify); fChannel.ClientNotify(refnum, name, notify, sync, message, value1, value2, &result); return result; } @@ -49,17 +49,17 @@ int JackExternalClient::Open(const char* name, int pid, int refnum, int uuid, in jack_error("Cannot connect to client name = %s\n", name); return -1; } - + // Use "placement new" to allocate object in shared memory JackShmMemAble* shared_mem = static_cast(JackShmMem::operator new(sizeof(JackClientControl))); shared_mem->Init(); fClientControl = new(shared_mem) JackClientControl(name, pid, refnum, uuid); - + if (!fClientControl) { jack_error("Cannot allocate client shared memory segment"); return -1; } - + *shared_client = shared_mem->GetShmIndex(); jack_log("JackExternalClient::Open name = %s index = %ld base = %x", name, shared_mem->GetShmIndex(), shared_mem->GetShmAddress()); 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(); diff --git a/common/JackExternalClient.h b/common/JackExternalClient.h index c7c7fc42..19318a33 100644 --- a/common/JackExternalClient.h +++ b/common/JackExternalClient.h @@ -38,8 +38,8 @@ class JackExternalClient : public JackClientInterface private: - JackNotifyChannel fChannel; /*! Server/client communication channel */ - JackClientControl* fClientControl; /*! Client control in shared memory */ + JackNotifyChannel fChannel; /*! Server/client communication channel */ + JackClientControl* fClientControl; /*! Client control in shared memory */ public: diff --git a/common/JackFilters.h b/common/JackFilters.h index 76bf7ecd..6b5f0d32 100644 --- a/common/JackFilters.h +++ b/common/JackFilters.h @@ -20,33 +20,42 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackFilters__ #define __JackFilters__ +#ifdef __APPLE__ +#include +#endif + #include "jack.h" +#ifndef MY_TARGET_OS_IPHONE #include "JackAtomicState.h" +#endif #include #include namespace Jack { +#ifndef TARGET_OS_IPHONE + #define MAX_SIZE 64 - - struct JackFilter + + PRE_PACKED_STRUCTURE + struct JackFilter { - + jack_time_t fTable[MAX_SIZE]; - + JackFilter() { for (int i = 0; i < MAX_SIZE; i++) fTable[i] = 0; } - + void AddValue(jack_time_t val) { memcpy(&fTable[1], &fTable[0], sizeof(jack_time_t) * (MAX_SIZE - 1)); fTable[0] = val; } - + jack_time_t GetVal() { jack_time_t mean = 0; @@ -54,14 +63,15 @@ namespace Jack mean += fTable[i]; return mean / MAX_SIZE; } - + } POST_PACKED_STRUCTURE; - + + PRE_PACKED_STRUCTURE class JackDelayLockedLoop { - + private: - + jack_nframes_t fFrames; jack_time_t fCurrentWakeup; jack_time_t fCurrentCallback; @@ -72,17 +82,17 @@ namespace Jack jack_time_t fPeriodUsecs; float fFilterCoefficient; /* set once, never altered */ bool fUpdating; - + public: - + JackDelayLockedLoop() {} - + JackDelayLockedLoop(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { Init(buffer_size, sample_rate); } - + void Init(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { fFrames = 0; @@ -95,7 +105,7 @@ namespace Jack fSampleRate = sample_rate; fPeriodUsecs = jack_time_t(1000000.f / fSampleRate * fBufferSize); // in microsec } - + void Init(jack_time_t callback_usecs) { fFrames = 0; @@ -104,7 +114,7 @@ namespace Jack fCurrentCallback = callback_usecs; fNextWakeUp = callback_usecs + fPeriodUsecs; } - + void IncFrame(jack_time_t callback_usecs) { float delta = (int64_t)callback_usecs - (int64_t)fNextWakeUp; @@ -114,41 +124,42 @@ namespace Jack fSecondOrderIntegrator += 0.5f * fFilterCoefficient * delta; fNextWakeUp = fCurrentWakeup + fPeriodUsecs + (int64_t) floorf((fFilterCoefficient * (delta + fSecondOrderIntegrator))); } - + jack_nframes_t Time2Frames(jack_time_t time) { long delta = (long) rint(((double) ((long long)(time - fCurrentWakeup)) / ((long long)(fNextWakeUp - fCurrentWakeup))) * fBufferSize); return (delta < 0) ? ((fFrames > 0) ? fFrames : 1) : (fFrames + delta); } - + jack_time_t Frames2Time(jack_nframes_t frames) { long delta = (long) rint(((double) ((long long)(frames - fFrames)) * ((long long)(fNextWakeUp - fCurrentWakeup))) / fBufferSize); return (delta < 0) ? ((fCurrentWakeup > 0) ? fCurrentWakeup : 1) : (fCurrentWakeup + delta); } - + jack_nframes_t CurFrame() { return fFrames; } - + jack_time_t CurTime() { return fCurrentWakeup; } - + } POST_PACKED_STRUCTURE; - + + PRE_PACKED_STRUCTURE class JackAtomicDelayLockedLoop : public JackAtomicState { public: - + JackAtomicDelayLockedLoop(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { fState[0].Init(buffer_size, sample_rate); fState[1].Init(buffer_size, sample_rate); } - + void Init(jack_time_t callback_usecs) { JackDelayLockedLoop* dll = WriteNextStateStart(); @@ -156,7 +167,7 @@ namespace Jack WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } - + void Init(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { JackDelayLockedLoop* dll = WriteNextStateStart(); @@ -164,7 +175,7 @@ namespace Jack WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } - + void IncFrame(jack_time_t callback_usecs) { JackDelayLockedLoop* dll = WriteNextStateStart(); @@ -172,44 +183,46 @@ namespace Jack WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } - + jack_nframes_t Time2Frames(jack_time_t time) { UInt16 next_index = GetCurrentIndex(); UInt16 cur_index; jack_nframes_t res; - + do { cur_index = next_index; res = ReadCurrentState()->Time2Frames(time); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read - + return res; } - + jack_time_t Frames2Time(jack_nframes_t frames) { UInt16 next_index = GetCurrentIndex(); UInt16 cur_index; jack_time_t res; - + do { cur_index = next_index; res = ReadCurrentState()->Frames2Time(frames); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read - + return res; } } POST_PACKED_STRUCTURE; - + +#endif + /* Torben Hohn PI controler from JACK1 */ - + struct JackPIControler { - + double resample_mean; double static_resample_factor; @@ -224,12 +237,12 @@ namespace Jack double pclamp; double controlquant; int smooth_size; - + double hann(double x) { return 0.5 * (1.0 - cos(2 * M_PI * x)); } - + JackPIControler(double resample_factor, int fir_size) { resample_mean = resample_factor; @@ -239,7 +252,7 @@ namespace Jack offset_differential_index = 0; offset_integral = 0.0; smooth_size = fir_size; - + for (int i = 0; i < fir_size; i++) { offset_array[i] = 0.0; window_array[i] = hann(double(i) / (double(fir_size) - 1.0)); @@ -251,19 +264,19 @@ namespace Jack pclamp = 15.0; controlquant = 10000.0; } - + ~JackPIControler() { delete[] offset_array; delete[] window_array; } - + void Init(double resample_factor) { resample_mean = resample_factor; static_resample_factor = resample_factor; } - + /* double GetRatio(int fill_level) { @@ -271,14 +284,14 @@ namespace Jack // Save offset. offset_array[(offset_differential_index++) % smooth_size] = offset; - + // Build the mean of the windowed offset array basically fir lowpassing. double smooth_offset = 0.0; for (int i = 0; i < smooth_size; i++) { smooth_offset += offset_array[(i + offset_differential_index - 1) % smooth_size] * window_array[i]; } smooth_offset /= double(smooth_size); - + // This is the integral of the smoothed_offset offset_integral += smooth_offset; @@ -286,13 +299,13 @@ namespace Jack // It only used in the P component and the I component is used for the fine tuning anyways. if (fabs(smooth_offset) < pclamp) smooth_offset = 0.0; - - // Ok, now this is the PI controller. + + // Ok, now this is the PI controller. // u(t) = K * (e(t) + 1/T \int e(t') dt') - // Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T - double current_resample_factor + // Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T + double current_resample_factor = static_resample_factor - smooth_offset / catch_factor - offset_integral / catch_factor / catch_factor2; - + // Now quantize this value around resample_mean, so that the noise which is in the integral component doesnt hurt. current_resample_factor = floor((current_resample_factor - resample_mean) * controlquant + 0.5) / controlquant + resample_mean; @@ -309,25 +322,25 @@ namespace Jack // This is the integral of the smoothed_offset offset_integral += smooth_offset; - // Ok, now this is the PI controller. + // Ok, now this is the PI controller. // u(t) = K * (e(t) + 1/T \int e(t') dt') - // Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T + // Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T return static_resample_factor - smooth_offset/catch_factor - offset_integral/catch_factor/catch_factor2; } - + void OurOfBounds() { int i; // Set the resample_rate... we need to adjust the offset integral, to do this. // first look at the PI controller, this code is just a special case, which should never execute once - // everything is swung in. + // everything is swung in. offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; // Also clear the array. we are beginning a new control cycle. for (i = 0; i < smooth_size; i++) { offset_array[i] = 0.0; } } - + }; } diff --git a/common/JackFrameTimer.cpp b/common/JackFrameTimer.cpp index bd6d9b53..180de970 100644 --- a/common/JackFrameTimer.cpp +++ b/common/JackFrameTimer.cpp @@ -13,7 +13,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ diff --git a/common/JackFrameTimer.h b/common/JackFrameTimer.h index afac2f78..3ab3e153 100644 --- a/common/JackFrameTimer.h +++ b/common/JackFrameTimer.h @@ -32,6 +32,7 @@ namespace Jack \brief A structure used for time management. */ +PRE_PACKED_STRUCTURE class SERVER_EXPORT JackTimer { @@ -73,6 +74,7 @@ class SERVER_EXPORT JackTimer \brief A class using the JackAtomicState to manage jack time. */ +PRE_PACKED_STRUCTURE class SERVER_EXPORT JackFrameTimer : public JackAtomicState { diff --git a/common/JackFreewheelDriver.cpp b/common/JackFreewheelDriver.cpp index c28e30c3..94987c44 100644 --- a/common/JackFreewheelDriver.cpp +++ b/common/JackFreewheelDriver.cpp @@ -28,26 +28,72 @@ namespace Jack int JackFreewheelDriver::Process() { - if (fIsMaster) { - jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); - JackDriver::CycleTakeBeginTime(); - fEngine->Process(fBeginDateUst, fEndDateUst); - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients + int res = 0; + + jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); + JackDriver::CycleTakeBeginTime(); + + if (fEngine->Process(fBeginDateUst, fEndDateUst)) { + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable)) { // Signal all clients + jack_error("JackFreewheelDriver::Process: ResumeRefNum error"); + res = -1; + } + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, FREEWHEEL_DRIVER_TIMEOUT * 1000000) < 0) { // Wait for all clients to finish for 10 sec - jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); + jack_error("JackFreewheelDriver::ProcessSync: SuspendRefNum error"); /* We have a client time-out error, but still continue to process, until a better recovery strategy is chosen */ return 0; } - } else { - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients - if (fEngineControl->fSyncMode) { - if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); - return -1; - } - } + + } else { // Graph not finished: do not activate it + jack_error("JackFreewheelDriver::Process: Process error"); + res = -1; + } + + return res; +} + +int JackFreewheelDriver::ProcessRead() +{ + return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); +} + +int JackFreewheelDriver::ProcessWrite() +{ + return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); +} + +int JackFreewheelDriver::ProcessReadSync() +{ + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { // Signal all clients + jack_error("JackFreewheelDriver::ProcessReadSync: ResumeRefNum error"); + return -1; + } + return 0; +} + +int JackFreewheelDriver::ProcessWriteSync() +{ + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); + return -1; } return 0; } +int JackFreewheelDriver::ProcessReadAsync() +{ + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { // Signal all clients + jack_error("JackFreewheelDriver::ProcessReadAsync: ResumeRefNum error"); + return -1; + } + return 0; +} + +int JackFreewheelDriver::ProcessWriteAsync() +{ + return 0; +} + } // end of namespace diff --git a/common/JackFreewheelDriver.h b/common/JackFreewheelDriver.h index b988ffb0..02a9cf76 100644 --- a/common/JackFreewheelDriver.h +++ b/common/JackFreewheelDriver.h @@ -46,6 +46,16 @@ class JackFreewheelDriver : public JackDriver } int Process(); + + int ProcessRead(); + int ProcessWrite(); + + int ProcessReadSync(); + int ProcessWriteSync(); + + int ProcessReadAsync(); + int ProcessWriteAsync(); + }; } // end of namespace diff --git a/common/JackGlobals.cpp b/common/JackGlobals.cpp index 2314c84d..5bd3b52a 100644 --- a/common/JackGlobals.cpp +++ b/common/JackGlobals.cpp @@ -23,7 +23,7 @@ namespace Jack { bool JackGlobals::fVerbose = 0; - + jack_tls_key JackGlobals::fRealTime; static bool gKeyRealtimeInitialized = jack_tls_allocate_key(&JackGlobals::fRealTime); @@ -31,13 +31,13 @@ jack_tls_key JackGlobals::fKeyLogFunction; static bool fKeyLogFunctionInitialized = jack_tls_allocate_key(&JackGlobals::fKeyLogFunction); JackMutex* JackGlobals::fOpenMutex = new JackMutex(); -bool JackGlobals::fServerRunning = false; +volatile bool JackGlobals::fServerRunning = false; JackClient* JackGlobals::fClientTable[CLIENT_NUM] = {}; #ifndef WIN32 jack_thread_creator_t JackGlobals::fJackThreadCreator = pthread_create; #endif - + #ifdef __CLIENTDEBUG__ std::ofstream* JackGlobals::fStream = NULL; @@ -52,13 +52,13 @@ void JackGlobals::CheckContext(const char* name) curtime = time (NULL); /* Convert it to local time representation. */ loctime = localtime (&curtime); - strftime (buffer, 256, "%I-%M", loctime); - sprintf(provstr, "JackAPICall-%s.log", buffer); + strftime(buffer, 256, "%I-%M", loctime); + snprintf(provstr, sizeof(provstr), "JackAPICall-%s.log", buffer); JackGlobals::fStream = new std::ofstream(provstr, std::ios_base::ate); JackGlobals::fStream->is_open(); } (*fStream) << "JACK API call : " << name << ", calling thread : " << pthread_self() << std::endl; } -#endif +#endif } // end of namespace diff --git a/common/JackGlobals.h b/common/JackGlobals.h index 63dc9ab2..6f215e7d 100644 --- a/common/JackGlobals.h +++ b/common/JackGlobals.h @@ -21,14 +21,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define __JackGlobals__ #include "JackPlatformPlug.h" +#include "JackSystemDeps.h" #include "JackConstants.h" -#ifdef __CLIENTDEBUG__ +#ifdef __CLIENTDEBUG__ #include #include #include #include -#endif +#endif namespace Jack { @@ -39,13 +40,13 @@ struct JackGlobals { static jack_tls_key fRealTime; static jack_tls_key fKeyLogFunction; static JackMutex* fOpenMutex; - static bool fServerRunning; + static volatile bool fServerRunning; static JackClient* fClientTable[]; static bool fVerbose; #ifndef WIN32 static jack_thread_creator_t fJackThreadCreator; #endif - + #ifdef __CLIENTDEBUG__ static std::ofstream* fStream; static void CheckContext(const char* name); @@ -53,9 +54,9 @@ struct JackGlobals { }; // Each "side" server and client will implement this to get the shared graph manager, engine control and inter-process synchro table. -extern EXPORT JackGraphManager* GetGraphManager(); -extern EXPORT JackEngineControl* GetEngineControl(); -extern EXPORT JackSynchro* GetSynchroTable(); +extern SERVER_EXPORT JackGraphManager* GetGraphManager(); +extern SERVER_EXPORT JackEngineControl* GetEngineControl(); +extern SERVER_EXPORT JackSynchro* GetSynchroTable(); } // end of namespace diff --git a/common/JackGraphManager.cpp b/common/JackGraphManager.cpp index 1366316a..05c8d309 100644 --- a/common/JackGraphManager.cpp +++ b/common/JackGraphManager.cpp @@ -180,14 +180,13 @@ void* JackGraphManager::GetBuffer(jack_port_id_t port_index, jack_nframes_t buff return GetBuffer(0); // port_index 0 is not used } + jack_int_t len = manager->Connections(port_index); + // Output port if (port->fFlags & JackPortIsOutput) { return (port->fTied != NO_PORT) ? GetBuffer(port->fTied, buffer_size) : GetBuffer(port_index); } - // Input port - jack_int_t len = manager->Connections(port_index); - // No connections : return a zero-filled buffer if (len == 0) { port->ClearBuffer(buffer_size); @@ -742,8 +741,9 @@ jack_port_id_t JackGraphManager::GetPort(const char* name) { for (unsigned int i = 0; i < fPortMax; i++) { JackPort* port = GetPort(i); - if (port->IsUsed() && port->NameEquals(name)) + if (port->IsUsed() && port->NameEquals(name)) { return i; + } } return NO_PORT; } @@ -791,9 +791,9 @@ const char** JackGraphManager::GetConnections(jack_port_id_t port_index) next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read - if (res[0]) { // at least one connection + if (res[0]) { // At least one connection return res; - } else { // empty array, should return NULL + } else { // Empty array, should return NULL free(res); return NULL; } @@ -874,10 +874,10 @@ const char** JackGraphManager::GetPorts(const char* port_name_pattern, const cha next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read - if (res[0]) { // at least one port + if (res[0]) { // At least one port return res; } else { - free(res); // empty array, should return NULL + free(res); // Empty array, should return NULL return NULL; } } diff --git a/common/JackGraphManager.h b/common/JackGraphManager.h index df6237e5..15f0275f 100644 --- a/common/JackGraphManager.h +++ b/common/JackGraphManager.h @@ -36,6 +36,7 @@ namespace Jack \brief Graph manager: contains the connection manager and the port array. */ +PRE_PACKED_STRUCTURE class SERVER_EXPORT JackGraphManager : public JackShmMem, public JackAtomicState { diff --git a/common/JackInternalClient.cpp b/common/JackInternalClient.cpp index 550db2ef..12d0c671 100644 --- a/common/JackInternalClient.cpp +++ b/common/JackInternalClient.cpp @@ -38,17 +38,17 @@ JackGraphManager* JackInternalClient::fGraphManager = NULL; JackEngineControl* JackInternalClient::fEngineControl = NULL; // Used for external C API (JackAPI.cpp) -EXPORT JackGraphManager* GetGraphManager() +SERVER_EXPORT JackGraphManager* GetGraphManager() { return JackServerGlobals::fInstance->GetGraphManager(); } -EXPORT JackEngineControl* GetEngineControl() +SERVER_EXPORT JackEngineControl* GetEngineControl() { return JackServerGlobals::fInstance->GetEngineControl(); } -EXPORT JackSynchro* GetSynchroTable() +SERVER_EXPORT JackSynchro* GetSynchroTable() { return JackServerGlobals::fInstance->GetSynchroTable(); } @@ -71,13 +71,14 @@ int JackInternalClient::Open(const char* server_name, const char* name, int uuid strncpy(fServerName, server_name, sizeof(fServerName)); - fChannel->ClientCheck(name, uuid, name_res, JACK_PROTOCOL_VERSION, (int)options, (int*)status, &result); + fChannel->ClientCheck(name, uuid, name_res, JACK_PROTOCOL_VERSION, (int)options, (int*)status, &result, false); if (result < 0) { int status1 = *status; - if (status1 & JackVersionError) + if (status1 & JackVersionError) { jack_error("JACK protocol mismatch %d", JACK_PROTOCOL_VERSION); - else + } else { jack_error("Client name = %s conflits with another running client", name); + } goto error; } diff --git a/common/JackInternalClientChannel.h b/common/JackInternalClientChannel.h index dc81ac8c..e2ead4b7 100644 --- a/common/JackInternalClientChannel.h +++ b/common/JackInternalClientChannel.h @@ -50,7 +50,7 @@ class JackInternalClientChannel : public detail::JackClientChannelInterface return 0; } - void ClientCheck(const char* name, int uuid, char* name_res, int protocol, int options, int* status, int* result) + void ClientCheck(const char* name, int uuid, char* name_res, int protocol, int options, int* status, int* result, int open) { *result = fEngine->ClientCheck(name, uuid, name_res, protocol, options, status); } @@ -146,7 +146,42 @@ class JackInternalClientChannel : public detail::JackClientChannelInterface void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, jack_session_command_t** result) { - *result = NULL; + JackSessionNotifyResult* res; + fEngine->SessionNotify(refnum, target, type, path, NULL, &res); + if (res == NULL) + { + *result = NULL; + return; + } + + *result = res->GetCommands(); + delete(res); + } + + void SessionReply(int refnum, int* result) + { + fEngine->SessionReply(refnum); + *result = 0; + } + + void GetUUIDForClientName(int refnum, const char* client_name, char* uuid_res, int* result) + { + fEngine->GetUUIDForClientName(client_name, uuid_res, result); + } + + void GetClientNameForUUID(int refnum, const char* uuid, char* name_res, int* result) + { + fEngine->GetClientNameForUUID(uuid, name_res, result); + } + + void ReserveClientName(int refnum, const char* client_name, const char *uuid, int* result) + { + fEngine->ReserveClientName(client_name, uuid, result); + } + + void ClientHasSessionCallback(const char* client_name, int* result) + { + fEngine->ClientHasSessionCallback(client_name, result); } diff --git a/common/JackLibAPI.cpp b/common/JackLibAPI.cpp index f22b5e07..1b2fa589 100644 --- a/common/JackLibAPI.cpp +++ b/common/JackLibAPI.cpp @@ -13,7 +13,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -39,19 +39,22 @@ extern "C" jack_client_t * jack_client_new_aux (const char *client_name, jack_options_t options, jack_status_t *status); - jack_client_t * jack_client_open_aux (const char *client_name, - jack_options_t options, - jack_status_t *status, va_list ap); - EXPORT jack_client_t * jack_client_open (const char *client_name, + + LIB_EXPORT jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...); - EXPORT int jack_client_close (jack_client_t *client); - EXPORT int jack_get_client_pid (const char *name); + LIB_EXPORT int jack_client_close (jack_client_t *client); + LIB_EXPORT int jack_get_client_pid (const char *name); + #ifdef __cplusplus } #endif +static jack_client_t * jack_client_open_aux (const char *client_name, + jack_options_t options, + jack_status_t *status, va_list ap); + JackLibGlobals* JackLibGlobals::fGlobals = NULL; int JackLibGlobals::fClientCount = 0; @@ -60,14 +63,14 @@ jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t optio jack_varargs_t va; /* variable arguments */ jack_status_t my_status; JackClient* client; - + if (client_name == NULL) { jack_error("jack_client_new called with a NULL client_name"); return NULL; } jack_log("jack_client_new %s", client_name); - + if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; @@ -81,7 +84,7 @@ jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t optio /* parse variable arguments */ jack_varargs_init(&va); - + JackLibGlobals::Init(); // jack library initialisation if (try_start_server(&va, options, status)) { @@ -108,19 +111,19 @@ jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t optio } } -jack_client_t* jack_client_open_aux(const char* client_name, jack_options_t options, jack_status_t* status, va_list ap) +static jack_client_t* jack_client_open_aux(const char* client_name, jack_options_t options, jack_status_t* status, va_list ap) { jack_varargs_t va; /* variable arguments */ jack_status_t my_status; JackClient* client; - + if (client_name == NULL) { jack_error("jack_client_open called with a NULL client_name"); return NULL; } jack_log("jack_client_open %s", client_name); - + if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; @@ -134,7 +137,7 @@ jack_client_t* jack_client_open_aux(const char* client_name, jack_options_t opti /* parse variable arguments */ jack_varargs_parse(options, ap, &va); - + JackLibGlobals::Init(); // jack library initialisation if (try_start_server(&va, options, status)) { @@ -161,7 +164,7 @@ jack_client_t* jack_client_open_aux(const char* client_name, jack_options_t opti } } -EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options_t options, jack_status_t* status, ...) +LIB_EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options_t options, jack_status_t* status, ...) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_open"); @@ -184,7 +187,7 @@ EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options } } -EXPORT int jack_client_close(jack_client_t* ext_client) +LIB_EXPORT int jack_client_close(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_close"); @@ -206,7 +209,7 @@ EXPORT int jack_client_close(jack_client_t* ext_client) return res; } -EXPORT int jack_get_client_pid(const char *name) +LIB_EXPORT int jack_get_client_pid(const char *name) { jack_error("jack_get_client_pid : not implemented on library side"); return 0; diff --git a/common/JackLibClient.cpp b/common/JackLibClient.cpp index 2b54aa16..4ba58c9c 100644 --- a/common/JackLibClient.cpp +++ b/common/JackLibClient.cpp @@ -12,7 +12,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -100,22 +100,19 @@ int JackLibClient::Open(const char* server_name, const char* name, int uuid, jac JackLibGlobals::fGlobals->fGraphManager.SetShmIndex(shared_graph, fServerName); fClientControl.SetShmIndex(shared_client, fServerName); JackGlobals::fVerbose = GetEngineControl()->fVerbose; - } catch (int n) { - jack_error("Map shared memory segments exception %d", n); - goto error; } catch (...) { - jack_error("Unknown error..."); + jack_error("Map shared memory segments exception"); goto error; } SetupDriverSync(false); - + // Connect shared synchro : the synchro must be usable in I/O mode when several clients live in the same process if (!fSynchroTable[GetClientControl()->fRefNum].Connect(name_res, fServerName)) { jack_error("Cannot ConnectSemaphore %s client", name_res); goto error; } - + JackGlobals::fClientTable[GetClientControl()->fRefNum] = this; JackGlobals::fServerRunning = true; SetClockSource(GetEngineControl()->fClockSource); @@ -146,7 +143,7 @@ int JackLibClient::ClientNotifyImp(int refnum, const char* name, int notify, int case kRemoveClient: jack_log("JackClient::RemoveClient name = %s, ref = %ld ", name, refnum); - if (strcmp(GetClientControl()->fName, name) != 0) + if (GetClientControl() && strcmp(GetClientControl()->fName, name) != 0) res = fSynchroTable[refnum].Disconnect() ? 0 : -1; break; } diff --git a/common/JackLibClient.h b/common/JackLibClient.h index ef07581c..000c9aa6 100644 --- a/common/JackLibClient.h +++ b/common/JackLibClient.h @@ -32,7 +32,7 @@ namespace Jack \brief Client on the library side. */ -class SERVER_EXPORT JackLibClient : public JackClient +class LIB_EXPORT JackLibClient : public JackClient { private: diff --git a/common/JackLibGlobals.h b/common/JackLibGlobals.h index 8d312295..be36cb47 100644 --- a/common/JackLibGlobals.h +++ b/common/JackLibGlobals.h @@ -32,6 +32,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include +#ifdef WIN32 +#ifdef __MINGW32__ +#include +typedef _sigset_t sigset_t; +#else +typedef HANDLE sigset_t; +#endif +#endif namespace Jack { @@ -42,7 +50,7 @@ class JackClient; \brief Global library static structure: singleton kind of pattern. */ -struct SERVER_EXPORT JackLibGlobals +struct LIB_EXPORT JackLibGlobals { JackShmReadWritePtr fGraphManager; /*! Shared memory Port manager */ JackShmReadWritePtr fEngineControl; /*! Shared engine control */ // transport engine has to be writable diff --git a/common/JackLibSampleRateResampler.cpp b/common/JackLibSampleRateResampler.cpp index ea70438d..27e36b5e 100644 --- a/common/JackLibSampleRateResampler.cpp +++ b/common/JackLibSampleRateResampler.cpp @@ -58,8 +58,9 @@ JackLibSampleRateResampler::JackLibSampleRateResampler(unsigned int quality) int error; fResampler = src_new(quality, 1, &error); - if (error != 0) + if (error != 0) { jack_error("JackLibSampleRateResampler::JackLibSampleRateResampler err = %s", src_strerror(error)); + } } JackLibSampleRateResampler::~JackLibSampleRateResampler() @@ -148,7 +149,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 +168,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; diff --git a/common/JackLibSampleRateResampler.h b/common/JackLibSampleRateResampler.h index 4d6821d8..7083b284 100644 --- a/common/JackLibSampleRateResampler.h +++ b/common/JackLibSampleRateResampler.h @@ -21,8 +21,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #define __JackLibSampleRateResampler__ #include "JackResampler.h" -#include "types.h" - #include namespace Jack diff --git a/common/JackLockedEngine.h b/common/JackLockedEngine.h index e8858046..bec5ee2c 100644 --- a/common/JackLockedEngine.h +++ b/common/JackLockedEngine.h @@ -41,17 +41,26 @@ catch (...) { */ #define CATCH_EXCEPTION_RETURN \ + } catch(std::bad_alloc& e) { \ + jack_error("Memory allocation error..."); \ + return -1; \ + } catch (...) { \ + jack_error("Unknown error..."); \ + throw; \ + } \ + +#define CATCH_CLOSE_EXCEPTION_RETURN \ } catch(std::bad_alloc& e) { \ jack_error("Memory allocation error..."); \ return -1; \ } catch(JackTemporaryException& e) { \ jack_error("JackTemporaryException : now quits..."); \ JackTools::KillServer(); \ - return -1; \ + return 0; \ } catch (...) { \ jack_error("Unknown error..."); \ throw; \ - } \ + } #define CATCH_EXCEPTION \ } catch(std::bad_alloc& e) { \ @@ -122,15 +131,15 @@ class SERVER_EXPORT JackLockedEngine { TRY_CALL JackLock lock(&fEngine); - return (fEngine.CheckClient(refnum)) ? fEngine.ClientExternalClose(refnum) : - 1; - CATCH_EXCEPTION_RETURN + return (fEngine.CheckClient(refnum)) ? fEngine.ClientExternalClose(refnum) : -1; + CATCH_CLOSE_EXCEPTION_RETURN } int ClientInternalClose(int refnum, bool wait) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.ClientInternalClose(refnum, wait) : -1; - CATCH_EXCEPTION_RETURN + CATCH_CLOSE_EXCEPTION_RETURN } int ClientActivate(int refnum, bool is_real_time) @@ -261,6 +270,7 @@ class SERVER_EXPORT JackLockedEngine fEngine.NotifyGraphReorder(); CATCH_EXCEPTION } + void NotifyBufferSize(jack_nframes_t buffer_size) { TRY_CALL @@ -315,11 +325,11 @@ class SERVER_EXPORT JackLockedEngine CATCH_EXCEPTION } - void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket) + void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket, JackSessionNotifyResult** result) { TRY_CALL JackLock lock(&fEngine); - fEngine.SessionNotify(refnum, target, type, path, socket); + fEngine.SessionNotify(refnum, target, type, path, socket, result); CATCH_EXCEPTION } @@ -353,11 +363,11 @@ class SERVER_EXPORT JackLockedEngine CATCH_EXCEPTION } - void ClientHasSessionCallbackRequest(const char *name, int *result) + void ClientHasSessionCallback(const char *name, int *result) { TRY_CALL JackLock lock(&fEngine); - fEngine.ClientHasSessionCallbackRequest(name, result); + fEngine.ClientHasSessionCallback(name, result); CATCH_EXCEPTION } }; diff --git a/common/JackLoopbackDriver.cpp b/common/JackLoopbackDriver.cpp index e91cce1e..5c0376d9 100644 --- a/common/JackLoopbackDriver.cpp +++ b/common/JackLoopbackDriver.cpp @@ -30,20 +30,61 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { -int JackLoopbackDriver::Process() +int JackLoopbackDriver::ProcessRead() { + return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); +} + +int JackLoopbackDriver::ProcessWrite() +{ + return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); +} + +int JackLoopbackDriver::ProcessReadSync() +{ + int res = 0; + // Loopback copy for (int i = 0; i < fCaptureChannels; i++) { memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); } - fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); // Signal all clients - if (fEngineControl->fSyncMode) { - if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { - jack_error("JackLoopbackDriver::ProcessSync SuspendRefNum error"); - return -1; - } + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackLoopbackDriver::ProcessReadSync - ResumeRefNum error"); + res = -1; + } + + return res; +} + +int JackLoopbackDriver::ProcessWriteSync() +{ + if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) { + jack_error("JackLoopbackDriver::ProcessWriteSync SuspendRefNum error"); + return -1; + } + return 0; +} + +int JackLoopbackDriver::ProcessReadAsync() +{ + int res = 0; + + // Loopback copy + for (int i = 0; i < fCaptureChannels; i++) { + memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); } + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackLoopbackDriver::ProcessReadAsync - ResumeRefNum error"); + res = -1; + } + + return res; +} + +int JackLoopbackDriver::ProcessWriteAsync() +{ return 0; } @@ -57,22 +98,13 @@ extern "C" SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() { jack_driver_desc_t * desc; - unsigned int i; - - desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); - strcpy(desc->name, "loopback"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 - strcpy(desc->desc, "Loopback backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 - - desc->nparams = 1; - desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); - - i = 0; - strcpy(desc->params[i].name, "channels"); - desc->params[i].character = 'c'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.ui = 0; - strcpy(desc->params[i].short_desc, "Maximum number of loopback ports"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("loopback", JackDriverSlave, "Loopback backend", &filler); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "channels", 'c', JackDriverParamInt, &value, NULL, "Maximum number of loopback ports", NULL); return desc; } diff --git a/common/JackLoopbackDriver.h b/common/JackLoopbackDriver.h index 5c542e3a..247f852e 100644 --- a/common/JackLoopbackDriver.h +++ b/common/JackLoopbackDriver.h @@ -33,15 +33,24 @@ namespace Jack class JackLoopbackDriver : public JackAudioDriver { + private: + + virtual int ProcessReadSync(); + virtual int ProcessWriteSync(); + + virtual int ProcessReadAsync(); + virtual int ProcessWriteAsync(); + public: JackLoopbackDriver(JackLockedEngine* engine, JackSynchro* table) - : JackAudioDriver("loopback", "", engine, table) + : JackAudioDriver("loopback", "loopback", engine, table) {} virtual ~JackLoopbackDriver() {} - int Process(); + virtual int ProcessRead(); + virtual int ProcessWrite(); }; } // end of namespace diff --git a/common/JackMessageBuffer.cpp b/common/JackMessageBuffer.cpp index d867933d..20fbb630 100644 --- a/common/JackMessageBuffer.cpp +++ b/common/JackMessageBuffer.cpp @@ -48,11 +48,16 @@ void JackMessageBuffer::Stop() } else { jack_log("no message buffer overruns"); } - fGuard.Lock(); - fRunning = false; - fGuard.Signal(); - fGuard.Unlock(); - fThread.Stop(); + + if (fGuard.Lock()) { + fRunning = false; + fGuard.Signal(); + fGuard.Unlock(); + fThread.Stop(); + } else { + fThread.Kill(); + } + Flush(); } @@ -79,21 +84,29 @@ void JackMessageBuffer::AddMessage(int level, const char *message) bool JackMessageBuffer::Execute() { - while (fRunning) { - fGuard.Lock(); - fGuard.Wait(); - /* the client asked for all threads to run a thread - initialization callback, which includes us. - */ - if (fInit) { - fInit(fInitArg); - fInit = NULL; - /* and we're done */ - fGuard.Signal(); + if (fGuard.Lock()) { + while (fRunning) { + fGuard.Wait(); + /* the client asked for all threads to run a thread + initialization callback, which includes us. + */ + if (fInit) { + fInit(fInitArg); + fInit = NULL; + /* and we're done */ + fGuard.Signal(); + } + + /* releasing the mutex reduces contention */ + fGuard.Unlock(); + Flush(); + fGuard.Lock(); } - Flush(); fGuard.Unlock(); + } else { + jack_error("JackMessageBuffer::Execute lock cannot be taken"); } + return false; } @@ -126,16 +139,19 @@ void JackMessageBufferAdd(int level, const char *message) void JackMessageBuffer::SetInitCallback(JackThreadInitCallback callback, void *arg) { - fGuard.Lock(); - /* set up the callback */ - fInitArg = arg; - fInit = callback; - /* wake msg buffer thread */ - fGuard.Signal(); - /* wait for it to be done */ - fGuard.Wait(); - /* and we're done */ - fGuard.Unlock(); + if (fGuard.Lock()) { + /* set up the callback */ + fInitArg = arg; + fInit = callback; + /* wake msg buffer thread */ + fGuard.Signal(); + /* wait for it to be done */ + fGuard.Wait(); + /* and we're done */ + fGuard.Unlock(); + } else { + jack_error("JackMessageBuffer::SetInitCallback lock cannot be taken"); + } } }; diff --git a/common/JackMessageBuffer.h b/common/JackMessageBuffer.h index ec13a5dc..c64797e2 100644 --- a/common/JackMessageBuffer.h +++ b/common/JackMessageBuffer.h @@ -10,19 +10,19 @@ * Copyright (C) 2004 Rui Nuno Capela, Steve Harris * Copyright (C) 2008 Nedko Arnaudov * Copyright (C) 2008 Grame - * + * * 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 + * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ @@ -56,7 +56,7 @@ class JackMessageBuffer : public JackRunnableInterface { private: - + JackThreadInitCallback fInit; void* fInitArg; JackMessage fBuffers[MB_BUFFERS]; @@ -68,15 +68,15 @@ class JackMessageBuffer : public JackRunnableInterface bool fRunning; void Flush(); - + void Start(); void Stop(); - + public: - - JackMessageBuffer(); + + JackMessageBuffer(); ~JackMessageBuffer(); - + // JackRunnableInterface interface bool Execute(); @@ -90,16 +90,16 @@ class JackMessageBuffer : public JackRunnableInterface }; #ifdef __cplusplus -extern "C" +extern "C" { #endif void JackMessageBufferAdd(int level, const char *message); #ifdef __cplusplus -} +} #endif }; -#endif +#endif diff --git a/common/JackMidiAPI.cpp b/common/JackMidiAPI.cpp index b06a93a2..60c7c4b2 100644 --- a/common/JackMidiAPI.cpp +++ b/common/JackMidiAPI.cpp @@ -13,39 +13,37 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackError.h" #include "JackMidiPort.h" -#include "JackCompilerDeps.h" #include #include -#include "JackSystemDeps.h" #ifdef __cplusplus extern "C" { #endif - EXPORT jack_nframes_t jack_midi_get_event_count(void* port_buffer); + LIB_EXPORT uint32_t jack_midi_get_event_count(void* port_buffer); - EXPORT int jack_midi_event_get(jack_midi_event_t* event, - void* port_buffer, jack_nframes_t event_index); + LIB_EXPORT int jack_midi_event_get(jack_midi_event_t* event, + void* port_buffer, uint32_t event_index); - EXPORT void jack_midi_clear_buffer(void* port_buffer); + LIB_EXPORT void jack_midi_clear_buffer(void* port_buffer); - EXPORT size_t jack_midi_max_event_size(void* port_buffer); + LIB_EXPORT size_t jack_midi_max_event_size(void* port_buffer); - EXPORT jack_midi_data_t* jack_midi_event_reserve(void* port_buffer, + LIB_EXPORT jack_midi_data_t* jack_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size); - EXPORT int jack_midi_event_write(void* port_buffer, + LIB_EXPORT int jack_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size); - EXPORT jack_nframes_t jack_midi_get_lost_event_count(void* port_buffer); + LIB_EXPORT jack_nframes_t jack_midi_get_lost_event_count(void* port_buffer); #ifdef __cplusplus } @@ -53,23 +51,26 @@ extern "C" using namespace Jack; -EXPORT -jack_nframes_t jack_midi_get_event_count(void* port_buffer) +LIB_EXPORT +uint32_t jack_midi_get_event_count(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; - if (!buf || !buf->IsValid()) + if (!buf || !buf->IsValid()) { return 0; + } return buf->event_count; } -EXPORT -int jack_midi_event_get(jack_midi_event_t *event, void* port_buffer, jack_nframes_t event_index) +LIB_EXPORT +int jack_midi_event_get(jack_midi_event_t *event, void* port_buffer, uint32_t event_index) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; - if (!buf || !buf->IsValid()) + if (!buf || !buf->IsValid()) { return -EINVAL; - if (event_index >= buf->event_count) + } + if (event_index >= buf->event_count) { return -ENOBUFS; + } JackMidiEvent* ev = &buf->events[event_index]; event->time = ev->time; event->size = ev->size; @@ -77,15 +78,16 @@ int jack_midi_event_get(jack_midi_event_t *event, void* port_buffer, jack_nframe return 0; } -EXPORT +LIB_EXPORT void jack_midi_clear_buffer(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; - if (buf && buf->IsValid()) + if (buf && buf->IsValid()) { buf->Reset(buf->nframes); + } } -EXPORT +LIB_EXPORT size_t jack_midi_max_event_size(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; @@ -94,35 +96,52 @@ size_t jack_midi_max_event_size(void* port_buffer) return 0; } -EXPORT +LIB_EXPORT jack_midi_data_t* jack_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; - if (!buf && !buf->IsValid()) + if (! buf) { + jack_error("jack_midi_event_reserve: port buffer is set to NULL"); + return 0; + } + if (! buf->IsValid()) { + jack_error("jack_midi_event_reserve: port buffer is invalid"); + return 0; + } + if (time >= buf->nframes) { + jack_error("jack_midi_event_reserve: time parameter is out of range " + "(%lu >= %lu)", time, buf->nframes); return 0; - if (time >= buf->nframes || (buf->event_count && buf->events[buf->event_count - 1].time > time)) + } + if (buf->event_count && (buf->events[buf->event_count - 1].time > time)) { + jack_error("jack_midi_event_reserve: time parameter is earlier than " + "last reserved event"); return 0; + } return buf->ReserveEvent(time, data_size); } -EXPORT +LIB_EXPORT int jack_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; - if (!buf && !buf->IsValid()) + if (!buf && !buf->IsValid()) { return -EINVAL; - if (time >= buf->nframes || (buf->event_count && buf->events[buf->event_count - 1].time > time)) + } + if (time >= buf->nframes || (buf->event_count && buf->events[buf->event_count - 1].time > time)) { return -EINVAL; + } jack_midi_data_t* dest = buf->ReserveEvent(time, data_size); - if (!dest) + if (!dest) { return -ENOBUFS; + } memcpy(dest, data, data_size); return 0; } -EXPORT -jack_nframes_t jack_midi_get_lost_event_count(void* port_buffer) +LIB_EXPORT +uint32_t jack_midi_get_lost_event_count(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (buf && buf->IsValid()) diff --git a/common/JackMidiAsyncQueue.cpp b/common/JackMidiAsyncQueue.cpp new file mode 100644 index 00000000..ab64db3f --- /dev/null +++ b/common/JackMidiAsyncQueue.cpp @@ -0,0 +1,96 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include + +#include "JackMidiAsyncQueue.h" + +using Jack::JackMidiAsyncQueue; + +JackMidiAsyncQueue::JackMidiAsyncQueue(size_t max_bytes, size_t max_messages) +{ + data_buffer = new jack_midi_data_t[max_bytes]; + byte_ring = jack_ringbuffer_create((max_bytes * sizeof(jack_midi_data_t)) + + 1); + if (byte_ring) { + info_ring = jack_ringbuffer_create((max_messages * INFO_SIZE) + 1); + if (info_ring) { + jack_ringbuffer_mlock(byte_ring); + jack_ringbuffer_mlock(info_ring); + this->max_bytes = max_bytes; + return; + } + jack_ringbuffer_free(byte_ring); + } + delete data_buffer; + throw std::bad_alloc(); +} + +JackMidiAsyncQueue::~JackMidiAsyncQueue() +{ + jack_ringbuffer_free(byte_ring); + jack_ringbuffer_free(info_ring); + delete[] data_buffer; +} + +jack_midi_event_t * +JackMidiAsyncQueue::DequeueEvent() +{ + jack_midi_event_t *event = 0; + if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) { + size_t size; + event = &dequeue_event; + jack_ringbuffer_read(info_ring, (char *) &(event->time), + sizeof(jack_nframes_t)); + jack_ringbuffer_read(info_ring, (char *) &size, + sizeof(size_t)); + jack_ringbuffer_read(byte_ring, (char *) data_buffer, + size * sizeof(jack_midi_data_t)); + event->buffer = data_buffer; + event->size = size; + } + return event; +} + +Jack::JackMidiWriteQueue::EnqueueResult +JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer) +{ + if (size > max_bytes) { + return BUFFER_TOO_SMALL; + } + if (! ((jack_ringbuffer_write_space(info_ring) >= INFO_SIZE) && + (jack_ringbuffer_write_space(byte_ring) >= + (size * sizeof(jack_midi_data_t))))) { + return BUFFER_FULL; + } + jack_ringbuffer_write(byte_ring, (const char *) buffer, + size * sizeof(jack_midi_data_t)); + jack_ringbuffer_write(info_ring, (const char *) (&time), + sizeof(jack_nframes_t)); + jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t)); + return OK; +} + +size_t +JackMidiAsyncQueue::GetAvailableSpace() +{ + return jack_ringbuffer_write_space(info_ring) < INFO_SIZE ? 0 : + max_bytes - jack_ringbuffer_read_space(byte_ring); +} diff --git a/common/JackMidiAsyncQueue.h b/common/JackMidiAsyncQueue.h new file mode 100644 index 00000000..5f23eff4 --- /dev/null +++ b/common/JackMidiAsyncQueue.h @@ -0,0 +1,98 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiAsyncQueue__ +#define __JackMidiAsyncQueue__ + +#include "JackMidiPort.h" +#include "JackMidiReadQueue.h" +#include "JackMidiWriteQueue.h" +#include "ringbuffer.h" + +namespace Jack { + + /** + * This is a MIDI message queue designed to allow one thread to pass MIDI + * messages to another thread (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 MIDI buffer to this queue, or vice versa. + */ + + class SERVER_EXPORT JackMidiAsyncQueue: + public JackMidiReadQueue, public JackMidiWriteQueue { + + private: + + static const size_t INFO_SIZE = + sizeof(jack_nframes_t) + sizeof(size_t); + + jack_ringbuffer_t *byte_ring; + jack_midi_data_t *data_buffer; + jack_midi_event_t dequeue_event; + jack_ringbuffer_t *info_ring; + size_t max_bytes; + + public: + + using JackMidiWriteQueue::EnqueueEvent; + + /** + * Creates a new asynchronous MIDI message queue. The queue can store + * up to `max_messages` MIDI messages and up to `max_bytes` of MIDI + * data before it starts rejecting messages. + */ + + JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024); + + virtual + ~JackMidiAsyncQueue(); + + /** + * Dequeues and returns a MIDI event. Returns '0' if there are no MIDI + * events available. This method may be overridden. + */ + + virtual jack_midi_event_t * + DequeueEvent(); + + /** + * Enqueues the MIDI event specified by the arguments. The return + * value indiciates whether or not the event was successfully enqueued. + * This method may be overridden. + */ + + virtual EnqueueResult + EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer); + + /** + * Returns the maximum size event that can be enqueued right *now*. + */ + + size_t + GetAvailableSpace(); + + }; + +} + +#endif diff --git a/common/JackMidiAsyncWaitQueue.cpp b/common/JackMidiAsyncWaitQueue.cpp new file mode 100644 index 00000000..016737cb --- /dev/null +++ b/common/JackMidiAsyncWaitQueue.cpp @@ -0,0 +1,85 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include + +#include "JackMidiAsyncWaitQueue.h" +#include "JackMidiUtil.h" +#include "JackTime.h" + +using Jack::JackMidiAsyncWaitQueue; + +JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes, + size_t max_messages): + JackMidiAsyncQueue(max_bytes, max_messages) +{ + if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) { + throw std::bad_alloc(); + } +} + +JackMidiAsyncWaitQueue::~JackMidiAsyncWaitQueue() +{ + semaphore.Destroy(); +} + +jack_midi_event_t * +JackMidiAsyncWaitQueue::DequeueEvent() +{ + return DequeueEvent((long) 0); +} + +jack_midi_event_t * +JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame) +{ + + // XXX: I worry about timer resolution on Solaris and Windows. When the + // resolution for the `JackSynchro` object is milliseconds, the worst-case + // scenario for processor objects is that the wait time becomes less than a + // millisecond, and the processor object continually calls this method, + // expecting to wait a certain amount of microseconds, and ends up not + // waiting at all each time, essentially busy-waiting until the current + // frame is reached. Perhaps there should be a #define that indicates the + // wait time resolution for `JackSynchro` objects so that we can wait a + // little longer if necessary. + + jack_time_t frame_time = GetTimeFromFrames(frame); + jack_time_t current_time = GetMicroSeconds(); + return DequeueEvent((frame_time < current_time) ? 0 : + (long) (frame_time - current_time)); +} + +jack_midi_event_t * +JackMidiAsyncWaitQueue::DequeueEvent(long usec) +{ + return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ? + JackMidiAsyncQueue::DequeueEvent() : 0; +} + +Jack::JackMidiWriteQueue::EnqueueResult +JackMidiAsyncWaitQueue::EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer) +{ + EnqueueResult result = JackMidiAsyncQueue::EnqueueEvent(time, size, + buffer); + if (result == OK) { + semaphore.Signal(); + } + return result; +} diff --git a/common/JackMidiAsyncWaitQueue.h b/common/JackMidiAsyncWaitQueue.h new file mode 100644 index 00000000..8499f688 --- /dev/null +++ b/common/JackMidiAsyncWaitQueue.h @@ -0,0 +1,99 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiAsyncWaitQueue__ +#define __JackMidiAsyncWaitQueue__ + +#include "JackMidiAsyncQueue.h" + +namespace Jack { + + /** + * This is an asynchronous wait queue that allows a thread to wait for a + * message, either indefinitely or for a specified time. This is one + * example of a way that the `JackMidiAsyncQueue` class can be extended so + * that process threads can interact with non-process threads to send MIDI + * events. + * + * XXX: As of right now, this code hasn't been tested. Also, note the + * warning in the JackMidiAsyncWaitQueue.cpp about semaphore wait + * resolution. + */ + + class SERVER_EXPORT JackMidiAsyncWaitQueue: public JackMidiAsyncQueue { + + private: + + JackSynchro semaphore; + + public: + + using JackMidiAsyncQueue::EnqueueEvent; + + /** + * Creates a new asynchronous MIDI wait message queue. The queue can + * store up to `max_messages` MIDI messages and up to `max_bytes` of + * MIDI data before it starts rejecting messages. + */ + + JackMidiAsyncWaitQueue(size_t max_bytes=4096, + size_t max_messages=1024); + + ~JackMidiAsyncWaitQueue(); + + /** + * Dequeues and returns a MIDI event. Returns '0' if there are no MIDI + * events available right now. + */ + + jack_midi_event_t * + DequeueEvent(); + + /** + * Waits a specified time for a MIDI event to be available, or + * indefinitely if the time is negative. Returns the MIDI event, or + * '0' if time runs out and no MIDI event is available. + */ + + jack_midi_event_t * + DequeueEvent(long usecs); + + /** + * Waits until the specified frame for a MIDI event to be available. + * Returns the MIDI event, or '0' if time runs out and no MIDI event is + * available. + */ + + jack_midi_event_t * + DequeueEvent(jack_nframes_t frame); + + /** + * Enqueues the MIDI event specified by the arguments. The return + * value indiciates whether or not the event was successfully enqueued. + */ + + EnqueueResult + EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer); + + }; + +} + +#endif diff --git a/common/JackMidiBufferReadQueue.cpp b/common/JackMidiBufferReadQueue.cpp new file mode 100644 index 00000000..c53e596c --- /dev/null +++ b/common/JackMidiBufferReadQueue.cpp @@ -0,0 +1,67 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackMidiBufferReadQueue.h" +#include "JackMidiUtil.h" + +using Jack::JackMidiBufferReadQueue; + +JackMidiBufferReadQueue::JackMidiBufferReadQueue() +{ + event_count = 0; + index = 0; +} + +jack_midi_event_t * +JackMidiBufferReadQueue::DequeueEvent() +{ + jack_midi_event_t *e = 0; + if (index < event_count) { + JackMidiEvent *event = &(buffer->events[index]); + midi_event.buffer = event->GetData(buffer); + midi_event.size = event->size; + midi_event.time = last_frame_time + event->time; + e = &midi_event; + index++; + } + return e; +} + +void +JackMidiBufferReadQueue::ResetMidiBuffer(JackMidiBuffer *buffer) +{ + event_count = 0; + index = 0; + if (! buffer) { + jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " + "to NULL"); + } else if (! buffer->IsValid()) { + jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " + "to invalid buffer"); + } else { + uint32_t lost_events = buffer->lost_events; + if (lost_events) { + jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - %d events " + "lost during mixdown", lost_events); + } + this->buffer = buffer; + event_count = buffer->event_count; + last_frame_time = GetLastFrame(); + } +} diff --git a/common/JackMidiBufferReadQueue.h b/common/JackMidiBufferReadQueue.h new file mode 100644 index 00000000..84337e42 --- /dev/null +++ b/common/JackMidiBufferReadQueue.h @@ -0,0 +1,60 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiBufferReadQueue__ +#define __JackMidiBufferReadQueue__ + +#include "JackMidiReadQueue.h" + +namespace Jack { + + /** + * Wrapper class to present a JackMidiBuffer in a read queue interface. + */ + + class SERVER_EXPORT JackMidiBufferReadQueue: public JackMidiReadQueue { + + private: + + JackMidiBuffer *buffer; + jack_nframes_t event_count; + jack_nframes_t index; + jack_nframes_t last_frame_time; + jack_midi_event_t midi_event; + + public: + + JackMidiBufferReadQueue(); + + jack_midi_event_t * + DequeueEvent(); + + /** + * This method must be called each period to reset the MIDI buffer for + * processing. + */ + + void + ResetMidiBuffer(JackMidiBuffer *buffer); + + }; + +} + +#endif diff --git a/common/JackMidiBufferWriteQueue.cpp b/common/JackMidiBufferWriteQueue.cpp new file mode 100644 index 00000000..7e6ae67f --- /dev/null +++ b/common/JackMidiBufferWriteQueue.cpp @@ -0,0 +1,65 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackMidiBufferWriteQueue.h" +#include "JackMidiUtil.h" + +using Jack::JackMidiBufferWriteQueue; + +JackMidiBufferWriteQueue::JackMidiBufferWriteQueue() +{ + // Empty +} + +Jack::JackMidiWriteQueue::EnqueueResult +JackMidiBufferWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *data) +{ + if (time >= next_frame_time) { + return EVENT_EARLY; + } + if (time < last_frame_time) { + time = last_frame_time; + } + jack_midi_data_t *dst = buffer->ReserveEvent(time - last_frame_time, size); + if (! dst) { + return size > max_bytes ? BUFFER_TOO_SMALL : BUFFER_FULL; + } + memcpy(dst, data, size); + return OK; +} + +void +JackMidiBufferWriteQueue::ResetMidiBuffer(JackMidiBuffer *buffer, + jack_nframes_t frames) +{ + if (! buffer) { + jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " + "to NULL"); + } else if (! buffer->IsValid()) { + jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " + "to invalid buffer"); + } else { + this->buffer = buffer; + buffer->Reset(frames); + last_frame_time = GetLastFrame(); + max_bytes = buffer->MaxEventSize(); + next_frame_time = last_frame_time + frames; + } +} diff --git a/common/JackMidiBufferWriteQueue.h b/common/JackMidiBufferWriteQueue.h new file mode 100644 index 00000000..90d5cbf1 --- /dev/null +++ b/common/JackMidiBufferWriteQueue.h @@ -0,0 +1,62 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiBufferWriteQueue__ +#define __JackMidiBufferWriteQueue__ + +#include "JackMidiWriteQueue.h" + +namespace Jack { + + /** + * Wrapper class to present a JackMidiBuffer in a write queue interface. + */ + + class SERVER_EXPORT JackMidiBufferWriteQueue: public JackMidiWriteQueue { + + private: + + JackMidiBuffer *buffer; + jack_nframes_t last_frame_time; + size_t max_bytes; + jack_nframes_t next_frame_time; + + public: + + using JackMidiWriteQueue::EnqueueEvent; + + JackMidiBufferWriteQueue(); + + EnqueueResult + EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer); + + /** + * This method must be called each period to reset the MIDI buffer for + * processing. + */ + + void + ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames); + + }; + +} + +#endif diff --git a/common/JackMidiDriver.cpp b/common/JackMidiDriver.cpp index 15d507b1..f1ec84ba 100644 --- a/common/JackMidiDriver.cpp +++ b/common/JackMidiDriver.cpp @@ -27,26 +27,17 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackException.h" #include +using namespace std; + namespace Jack { JackMidiDriver::JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) - : JackDriver(name, alias, engine, table), - fCaptureChannels(0), - fPlaybackChannels(0) -{ - for (int i = 0; i < DRIVER_PORT_NUM; i++) { - fRingBuffer[i] = NULL; - } -} + : JackDriver(name, alias, engine, table) +{} 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); } @@ -72,16 +58,16 @@ int JackMidiDriver::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]; + char name[REAL_JACK_PORT_NAME_SIZE]; + char alias[REAL_JACK_PORT_NAME_SIZE]; int i; jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); for (i = 0; i < fCaptureChannels; i++) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); - snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { + snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); + snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } @@ -92,9 +78,9 @@ int JackMidiDriver::Attach() } for (i = 0; i < fPlaybackChannels; i++) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); - snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { + snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); + snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } @@ -104,6 +90,7 @@ int JackMidiDriver::Attach() jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } + UpdateLatencies(); return 0; } @@ -113,53 +100,113 @@ int JackMidiDriver::Detach() jack_log("JackMidiDriver::Detach"); for (i = 0; i < fCaptureChannels; i++) { - fGraphManager->ReleasePort(fClientControl.fRefNum, fCapturePortList[i]); + fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[i]); } for (i = 0; i < fPlaybackChannels; i++) { - fGraphManager->ReleasePort(fClientControl.fRefNum, fPlaybackPortList[i]); + fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[i]); } return 0; } -int JackMidiDriver::Read() +void JackMidiDriver::UpdateLatencies() { - return 0; + 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::Write() +int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) { + UpdateLatencies(); return 0; } -int JackMidiDriver::ProcessNull() +int JackMidiDriver::ProcessRead() { - return 0; + return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); +} + +int JackMidiDriver::ProcessWrite() +{ + return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); } -int JackMidiDriver::Process() +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"); + 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 from the current cycle + if (Write() < 0) { + jack_error("JackMidiDriver::ProcessWriteSync: write error"); + res = -1; + } + + return res; +} + +int JackMidiDriver::ProcessReadAsync() +{ + int res = 0; + + // Read input buffers for the current cycle + if (Read() < 0) { + jack_error("JackMidiDriver::ProcessReadAsync: read error"); + res = -1; } - // Write output buffers for the current cycle + // Write output buffers from the previous cycle if (Write() < 0) { - jack_error("JackMidiDriver::Process: write error, skip cycle"); - return 0; // Skip cycle, but continue processing... + jack_error("JackMidiDriver::ProcessReadAsync: write error"); + res = -1; + } + + if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) { + jack_error("JackMidiDriver::ProcessReadAsync: ResumeRefNum error"); + res = -1; } + return res; +} + +int JackMidiDriver::ProcessWriteAsync() +{ return 0; } diff --git a/common/JackMidiDriver.h b/common/JackMidiDriver.h index 7c5bc5e0..5e6edb6f 100644 --- a/common/JackMidiDriver.h +++ b/common/JackMidiDriver.h @@ -37,17 +37,17 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver protected: - 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 +62,15 @@ 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 ProcessNull(); + + virtual int SetBufferSize(jack_nframes_t buffer_size); + + virtual int ProcessRead(); + virtual int ProcessWrite(); virtual int Attach(); virtual int Detach(); - - virtual int Read(); - virtual int Write(); - + }; } // end of namespace diff --git a/common/JackMidiPort.cpp b/common/JackMidiPort.cpp index 42ee245c..2426cb72 100644 --- a/common/JackMidiPort.cpp +++ b/common/JackMidiPort.cpp @@ -52,10 +52,11 @@ SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time { jack_shmsize_t space = MaxEventSize(); if (space == 0 || size > space) { + jack_error("JackMidiBuffer::ReserveEvent - the buffer does not have " + "enough room to enqueue a %lu byte event", size); lost_events++; return 0; } - JackMidiEvent* event = &events[event_count++]; event->time = time; event->size = size; @@ -90,7 +91,7 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count { JackMidiBuffer* mix = static_cast(mixbuffer); if (!mix->IsValid()) { - jack_error("MIDI: invalid mix buffer"); + jack_error("Jack::MidiBufferMixdown - invalid mix buffer"); return; } mix->Reset(nframes); @@ -98,8 +99,10 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count int event_count = 0; for (int i = 0; i < src_count; ++i) { JackMidiBuffer* buf = static_cast(src_buffers[i]); - if (!buf->IsValid()) + if (!buf->IsValid()) { + jack_error("Jack::MidiBufferMixdown - invalid source buffer"); return; + } buf->mix_index = 0; event_count += buf->event_count; mix->lost_events += buf->lost_events; diff --git a/common/JackMidiRawInputWriteQueue.cpp b/common/JackMidiRawInputWriteQueue.cpp new file mode 100644 index 00000000..63bc79b4 --- /dev/null +++ b/common/JackMidiRawInputWriteQueue.cpp @@ -0,0 +1,299 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#include "JackMidiRawInputWriteQueue.h" + +using Jack::JackMidiRawInputWriteQueue; + +JackMidiRawInputWriteQueue:: +JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, + size_t max_packet_data, size_t max_packets) +{ + packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets); + std::auto_ptr packet_queue_ptr(packet_queue); + input_buffer = new jack_midi_data_t[max_packet_data]; + Clear(); + expected_bytes = 0; + event_pending = false; + input_buffer_size = max_packet_data; + packet = 0; + status_byte = 0; + this->write_queue = write_queue; + packet_queue_ptr.release(); +} + +JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue() +{ + delete[] input_buffer; + delete packet_queue; +} + +void +JackMidiRawInputWriteQueue::Clear() +{ + 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); +} + +size_t +JackMidiRawInputWriteQueue::GetAvailableSpace() +{ + return packet_queue->GetAvailableSpace(); +} + +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 { + PrepareEvent(time, total_bytes, input_buffer); + } + 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 (total_bytes < input_buffer_size) { + input_buffer[total_bytes] = byte; + } else { + unbuffered_bytes++; + } + total_bytes++; +} + +bool +JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame) +{ + if ((! boundary_frame) || (event.time < boundary_frame)) { + switch (write_queue->EnqueueEvent(&event)) { + case BUFFER_TOO_SMALL: + HandleEventLoss(&event); + // Fallthrough on purpose + case OK: + event_pending = false; + return true; + default: + // This is here to stop compilers from warning us about not + // handling enumeration values. + ; + } + } + return false; +} diff --git a/common/JackMidiRawInputWriteQueue.h b/common/JackMidiRawInputWriteQueue.h new file mode 100644 index 00000000..32573d0f --- /dev/null +++ b/common/JackMidiRawInputWriteQueue.h @@ -0,0 +1,177 @@ +/* +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" + +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_midi_data_t *input_buffer; + size_t input_buffer_size; + 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); + + /** + * Returns the maximum size event that can be enqueued right *now*. + */ + + size_t + GetAvailableSpace(); + + /** + * The `Process()` method should be called each time the + * `EnqueueEvent()` method returns `OK`. The `Process()` method will + * return the next frame at which an event should be sent. The return + * value from `Process()` depends upon the result of writing bytes to + * the write queue: + * + * -If the return value is '0', then all *complete* events have been + * sent successfully to the write queue. Don't call `Process()` again + * until another event has been enqueued. + * + * -If the return value is a non-zero value, then it specifies the + * frame that a pending event is scheduled to sent at. If the frame is + * in the future, then `Process()` should be called again at that time; + * otherwise, `Process()` should be called as soon as the write queue + * will accept events again. + */ + + jack_nframes_t + Process(jack_nframes_t boundary_frame=0); + + }; + +} + +#endif diff --git a/common/JackMidiRawOutputWriteQueue.cpp b/common/JackMidiRawOutputWriteQueue.cpp new file mode 100644 index 00000000..075a1ff7 --- /dev/null +++ b/common/JackMidiRawOutputWriteQueue.cpp @@ -0,0 +1,158 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "JackError.h" +#include "JackMidiRawOutputWriteQueue.h" +#include "JackMidiUtil.h" + +using Jack::JackMidiRawOutputWriteQueue; + +#define STILL_TIME(c, b) ((! (b)) || ((c) < (b))) + +JackMidiRawOutputWriteQueue:: +JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size, + size_t max_non_rt_messages, size_t max_rt_messages) +{ + non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages); + std::auto_ptr non_rt_ptr(non_rt_queue); + rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages); + std::auto_ptr rt_ptr(rt_queue); + non_rt_event = 0; + rt_event = 0; + running_status = 0; + this->send_queue = send_queue; + rt_ptr.release(); + non_rt_ptr.release(); +} + +JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue() +{ + delete non_rt_queue; + delete rt_queue; +} + +void +JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent() +{ + non_rt_event = non_rt_queue->DequeueEvent(); + if (non_rt_event) { + non_rt_event_time = non_rt_event->time; + running_status = ApplyRunningStatus(non_rt_event, running_status); + } +} + +void +JackMidiRawOutputWriteQueue::DequeueRealtimeEvent() +{ + rt_event = rt_queue->DequeueEvent(); + if (rt_event) { + rt_event_time = rt_event->time; + } +} + +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; + return queue->EnqueueEvent(time, size, buffer); +} + +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) +{ + if (! non_rt_event) { + DequeueNonRealtimeEvent(); + } + if (! rt_event) { + DequeueRealtimeEvent(); + } + while (rt_event) { + jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); + if ((rt_event_time > current_frame) && non_rt_event && + (non_rt_event_time < rt_event_time)) { + if (! SendNonRTBytes(rt_event_time < boundary_frame ? + rt_event_time : boundary_frame)) { + return non_rt_event_time; + } + current_frame = send_queue->GetNextScheduleFrame(); + } + if (! STILL_TIME(current_frame, boundary_frame)) { + return (! non_rt_event) ? rt_event_time : + non_rt_event_time < rt_event_time ? non_rt_event_time : + rt_event_time; + } + if (! SendByte(rt_event_time, *(rt_event->buffer))) { + return rt_event_time; + } + DequeueRealtimeEvent(); + } + SendNonRTBytes(boundary_frame); + return non_rt_event ? non_rt_event_time : 0; +} + +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::SendNonRTBytes(jack_nframes_t boundary_frame) +{ + while (non_rt_event) { + for (; non_rt_event->size; + (non_rt_event->size)--, (non_rt_event->buffer)++) { + jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); + if (! STILL_TIME(current_frame, boundary_frame)) { + return true; + } + if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) { + return false; + } + } + DequeueNonRealtimeEvent(); + } + return true; +} diff --git a/common/JackMidiRawOutputWriteQueue.h b/common/JackMidiRawOutputWriteQueue.h new file mode 100644 index 00000000..967591bf --- /dev/null +++ b/common/JackMidiRawOutputWriteQueue.h @@ -0,0 +1,139 @@ +/* +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 couple 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. + * + * 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_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; + + void + DequeueNonRealtimeEvent(); + + void + DequeueRealtimeEvent(); + + bool + SendByte(jack_nframes_t time, jack_midi_data_t byte); + + bool + SendNonRTBytes(jack_nframes_t boundary_frame); + + protected: + + /** + * Override this method to specify what happens when the write queue + * says that a 1-byte event is too large for its buffer. Basically, + * this should never happen. + */ + + virtual void + HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte); + + public: + + using JackMidiWriteQueue::EnqueueEvent; + + /** + * Called to create a new raw write queue. The `send_queue` argument + * is the queue to write raw bytes to. The optional `max_rt_messages` + * argument specifies the number of messages that can be enqueued in + * the internal realtime queue. The optional `max_non_rt_messages` + * argument specifies the number of messages that can be enqueued in + * the internal non-realtime queue. The optional `non_rt_size` + * argument specifies the total number of MIDI bytes that can be put in + * the non-realtime queue. + */ + + JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, + size_t non_rt_size=4096, + size_t max_non_rt_messages=1024, + size_t max_rt_messages=128); + + ~JackMidiRawOutputWriteQueue(); + + EnqueueResult + EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer); + + /** + * The `Process()` method should be called each time the + * `EnqueueEvent()` method returns 'OK'. The `Process()` method will + * return the next frame at which an event should be sent. The return + * value from `Process()` depends upon the result of writing bytes to + * the write queue: + * + * -If the return value is '0', then all events that have been enqueued + * in this queue have been sent successfully to the write queue. Don't + * call `Process()` again until another event has been enqueued. + * + * -If the return value is an earlier frame or the current frame, it + * means that the write queue returned 'BUFFER_FULL', 'ERROR', or + * 'EVENT_EARLY' when this queue attempted to send the next byte, and + * that the byte should have already been sent, or is scheduled to be + * sent *now*. `Process()` should be called again when the write queue + * can enqueue events again successfully. How to determine when this + * will happen is left up to the caller. + * + * -If the return value is in the future, then `Process()` should be + * called again at that time, or after another event is enqueued. + */ + + jack_nframes_t + Process(jack_nframes_t boundary_frame=0); + + }; + +} + +#endif diff --git a/common/JackMidiReadQueue.cpp b/common/JackMidiReadQueue.cpp new file mode 100644 index 00000000..a6869691 --- /dev/null +++ b/common/JackMidiReadQueue.cpp @@ -0,0 +1,27 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackMidiReadQueue.h" + +using Jack::JackMidiReadQueue; + +JackMidiReadQueue::~JackMidiReadQueue() +{ + // Empty +} diff --git a/common/JackMidiReadQueue.h b/common/JackMidiReadQueue.h new file mode 100644 index 00000000..0f4ca692 --- /dev/null +++ b/common/JackMidiReadQueue.h @@ -0,0 +1,55 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiReadQueue__ +#define __JackMidiReadQueue__ + +#include "JackMidiPort.h" + +namespace Jack { + + /** + * Interface for objects that MIDI events can be read from. + */ + + class SERVER_EXPORT JackMidiReadQueue { + + public: + + virtual + ~JackMidiReadQueue(); + + /** + * Dequeues an event from the queue. Returns the event, or 0 if no + * events are available for reading. + * + * An event dequeued from the read queue is guaranteed to be valid up + * until another event is dequeued, at which all bets are off. Make + * sure that you handle each event you dequeue before dequeueing the + * next event. + */ + + virtual jack_midi_event_t * + DequeueEvent() = 0; + + }; + +} + +#endif diff --git a/common/JackMidiReceiveQueue.cpp b/common/JackMidiReceiveQueue.cpp new file mode 100644 index 00000000..3eb3573e --- /dev/null +++ b/common/JackMidiReceiveQueue.cpp @@ -0,0 +1,27 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackMidiReceiveQueue.h" + +using Jack::JackMidiReceiveQueue; + +JackMidiReceiveQueue::~JackMidiReceiveQueue() +{ + // Empty +} diff --git a/common/JackMidiReceiveQueue.h b/common/JackMidiReceiveQueue.h new file mode 100644 index 00000000..1d19c3c1 --- /dev/null +++ b/common/JackMidiReceiveQueue.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiReceiveQueue__ +#define __JackMidiReceiveQueue__ + +#include "JackMidiReadQueue.h" + +namespace Jack { + + /** + * Implemented by MIDI input connections. + */ + + class SERVER_EXPORT JackMidiReceiveQueue: public JackMidiReadQueue { + + public: + + virtual + ~JackMidiReceiveQueue(); + + }; + +} + +#endif diff --git a/common/JackMidiSendQueue.cpp b/common/JackMidiSendQueue.cpp new file mode 100644 index 00000000..ac66d812 --- /dev/null +++ b/common/JackMidiSendQueue.cpp @@ -0,0 +1,34 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackMidiSendQueue.h" +#include "JackMidiUtil.h" + +using Jack::JackMidiSendQueue; + +JackMidiSendQueue::~JackMidiSendQueue() +{ + // Empty +} + +jack_nframes_t +JackMidiSendQueue::GetNextScheduleFrame() +{ + return GetCurrentFrame(); +} diff --git a/common/JackMidiSendQueue.h b/common/JackMidiSendQueue.h new file mode 100644 index 00000000..0cb8df44 --- /dev/null +++ b/common/JackMidiSendQueue.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiSendQueue__ +#define __JackMidiSendQueue__ + +#include "JackMidiWriteQueue.h" + +namespace Jack { + + /** + * Implemented by MIDI output connections. + */ + + class SERVER_EXPORT JackMidiSendQueue: public JackMidiWriteQueue { + + public: + + using JackMidiWriteQueue::EnqueueEvent; + + virtual + ~JackMidiSendQueue(); + + /** + * Returns the next frame that a MIDI message can be sent at. The + * default method returns the current frame. + */ + + virtual jack_nframes_t + GetNextScheduleFrame(); + + }; + +} + +#endif diff --git a/common/JackMidiUtil.cpp b/common/JackMidiUtil.cpp new file mode 100644 index 00000000..a0dc0bae --- /dev/null +++ b/common/JackMidiUtil.cpp @@ -0,0 +1,121 @@ +/* +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() +{ + jack_time_t time = GetMicroSeconds(); + JackEngineControl *control = GetEngineControl(); + JackTimer timer; + control->ReadFrameTime(&timer); + return timer.Time2Frames(time, control->fBufferSize); +} + +jack_nframes_t +Jack::GetFramesFromTime(jack_time_t time) +{ + JackEngineControl* control = GetEngineControl(); + JackTimer timer; + control->ReadFrameTime(&timer); + return timer.Time2Frames(time, control->fBufferSize); +} + +jack_nframes_t +Jack::GetLastFrame() +{ + return GetEngineControl()->fFrameTimer.ReadCurrentState()->CurFrame(); +} + +int +Jack::GetMessageLength(jack_midi_data_t status_byte) +{ + switch (status_byte & 0xf0) { + case 0x80: + case 0x90: + case 0xa0: + case 0xb0: + case 0xe0: + return 3; + case 0xc0: + case 0xd0: + return 2; + case 0xf0: + switch (status_byte) { + case 0xf0: + return 0; + case 0xf1: + case 0xf3: + return 2; + case 0xf2: + return 3; + case 0xf4: + case 0xf5: + case 0xf7: + case 0xfd: + break; + default: + return 1; + } + } + return -1; +} + +jack_time_t +Jack::GetTimeFromFrames(jack_nframes_t frames) +{ + JackEngineControl* control = GetEngineControl(); + JackTimer timer; + control->ReadFrameTime(&timer); + return timer.Frames2Time(frames, control->fBufferSize); +} diff --git a/common/JackMidiUtil.h b/common/JackMidiUtil.h new file mode 100644 index 00000000..52db64c8 --- /dev/null +++ b/common/JackMidiUtil.h @@ -0,0 +1,102 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiUtil__ +#define __JackMidiUtil__ + +#include "JackMidiPort.h" + +namespace Jack { + + /** + * Use this function to optimize MIDI output by omitting unnecessary status + * bytes. This can't be used with all MIDI APIs, so before using this + * function, make sure that your MIDI API doesn't require complete MIDI + * messages to be sent. + * + * To start using this function, call this method with pointers to the + * `size` and `buffer` arguments of the MIDI message you want to send, and + * set the `running_status` argument to '0'. For each subsequent MIDI + * message, call this method with pointers to its `size` and `buffer` + * arguments, and set the `running_status` argument to the return value of + * the previous call to this function. + * + * Note: This function will alter the `size` and `buffer` of your MIDI + * message for each message that can be optimized. + */ + + SERVER_EXPORT jack_midi_data_t + ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, + jack_midi_data_t running_status=0); + + /** + * A wrapper function for the above `ApplyRunningStatus` function. + */ + + SERVER_EXPORT jack_midi_data_t + ApplyRunningStatus(jack_midi_event_t *event, + jack_midi_data_t running_status); + + /** + * Gets the estimated current time in frames. This function has the same + * functionality as the JACK client API function `jack_frame_time`. + */ + + SERVER_EXPORT jack_nframes_t + GetCurrentFrame(); + + /** + * Gets the estimated frame that will be occurring at the given time. This + * function has the same functionality as the JACK client API function + * `jack_time_to_frames`. + */ + + SERVER_EXPORT jack_nframes_t + GetFramesFromTime(jack_time_t time); + + /** + * Gets the precise time at the start of the current process cycle. This + * function has the same functionality as the JACK client API function + * `jack_last_frame_time`. + */ + + SERVER_EXPORT jack_nframes_t + GetLastFrame(); + + /** + * Returns the expected message length for the status byte. Returns 0 if + * the status byte is a system exclusive status byte, or -1 if the status + * byte is invalid. + */ + + SERVER_EXPORT int + GetMessageLength(jack_midi_data_t status_byte); + + /** + * Gets the estimated time at which the given frame will occur. This + * function has the same functionality as the JACK client API function + * `jack_frames_to_time`. + */ + + SERVER_EXPORT jack_time_t + GetTimeFromFrames(jack_nframes_t frames); + +}; + +#endif diff --git a/common/JackMidiWriteQueue.cpp b/common/JackMidiWriteQueue.cpp new file mode 100644 index 00000000..37fd9067 --- /dev/null +++ b/common/JackMidiWriteQueue.cpp @@ -0,0 +1,27 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackMidiWriteQueue.h" + +using Jack::JackMidiWriteQueue; + +JackMidiWriteQueue::~JackMidiWriteQueue() +{ + // Empty +} diff --git a/common/JackMidiWriteQueue.h b/common/JackMidiWriteQueue.h new file mode 100644 index 00000000..f21a58ff --- /dev/null +++ b/common/JackMidiWriteQueue.h @@ -0,0 +1,82 @@ +/* +Copyright (C) 2010 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackMidiWriteQueue__ +#define __JackMidiWriteQueue__ + +#include "JackMidiPort.h" + +namespace Jack { + + /** + * Interface for classes that act as write queues for MIDI messages. Write + * queues are used by processors to transfer data to the next processor. + */ + + class SERVER_EXPORT JackMidiWriteQueue { + + public: + + enum EnqueueResult { + BUFFER_FULL, + BUFFER_TOO_SMALL, + EVENT_EARLY, + EN_ERROR, + OK + }; + + virtual ~JackMidiWriteQueue(); + + /** + * Enqueues a data packet in the write queue of `size` bytes contained + * in `buffer` that will be sent the absolute time specified by `time`. + * This method should not block unless 1.) this write queue represents + * the actual outbound MIDI connection, 2.) the MIDI event is being + * sent *now*, meaning that `time` is less than or equal to *now*, and + * 3.) the method is *not* being called in the process thread. The + * method should return `OK` if the event was enqueued, `BUFFER_FULL` + * if the write queue isn't able to accept the event right now, + * `BUFFER_TOO_SMALL` if this write queue will never be able to accept + * the event because the event is too large, `EVENT_EARLY` if this + * queue cannot schedule events ahead of time, and `EN_ERROR` if an error + * occurs that cannot be specified by another return code. + */ + + virtual EnqueueResult + EnqueueEvent(jack_nframes_t time, size_t size, + jack_midi_data_t *buffer) = 0; + + /** + * A wrapper method for the `EnqueueEvent` method above. The optional + * 'frame_offset' argument is an amount of frames to add to the event's + * time. + */ + + inline EnqueueResult + EnqueueEvent(jack_midi_event_t *event, jack_nframes_t frame_offset=0) + { + return EnqueueEvent(event->time + frame_offset, event->size, + event->buffer); + } + + }; + +} + +#endif diff --git a/common/JackMutex.h b/common/JackMutex.h index 0a2600d7..2bdea991 100644 --- a/common/JackMutex.h +++ b/common/JackMutex.h @@ -37,7 +37,7 @@ class JackLockAble { protected: - + JackMutex fMutex; JackLockAble() @@ -46,10 +46,10 @@ class JackLockAble {} public: - - void Lock() + + bool Lock() { - fMutex.Lock(); + return fMutex.Lock(); } bool Trylock() @@ -57,9 +57,9 @@ class JackLockAble return fMutex.Trylock(); } - void Unlock() + bool Unlock() { - fMutex.Unlock(); + return fMutex.Unlock(); } }; diff --git a/common/JackNetAPI.cpp b/common/JackNetAPI.cpp new file mode 100644 index 00000000..42f06137 --- /dev/null +++ b/common/JackNetAPI.cpp @@ -0,0 +1,1043 @@ +/* +Copyright (C) 2009-2011 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include "JackNetInterface.h" +#include "JackError.h" +#include "JackException.h" +#include "JackAudioAdapterInterface.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + // NetJack common API + + #define MASTER_NAME_SIZE 256 + + enum JackNetEncoder { + + JackFloatEncoder = 0, + JackIntEncoder = 1, + JackCeltEncoder = 2, + JackMaxEncoder = 3 + }; + + typedef struct { + + int audio_input; + int audio_output; + int midi_input; + int midi_output; + int mtu; + int time_out; // in millisecond, -1 means in infinite + int encoder; // one of JackNetEncoder + int kbps; // KB per second for CELT encoder + int latency; // network cycles + + } jack_slave_t; + + typedef struct { + + int audio_input; + int audio_output; + int midi_input; + int midi_output; + jack_nframes_t buffer_size; + jack_nframes_t sample_rate; + char master_name[MASTER_NAME_SIZE]; + + } jack_master_t; + + // NetJack slave API + + typedef struct _jack_net_slave jack_net_slave_t; + + typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size, + int audio_input, + float** audio_input_buffer, + int midi_input, + void** midi_input_buffer, + int audio_output, + float** audio_output_buffer, + int midi_output, + void** midi_output_buffer, + void* data); + + typedef int (*JackNetSlaveBufferSizeCallback) (jack_nframes_t nframes, void *arg); + typedef int (*JackNetSlaveSampleRateCallback) (jack_nframes_t nframes, void *arg); + typedef void (*JackNetSlaveShutdownCallback) (void* data); + + SERVER_EXPORT jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result); + SERVER_EXPORT int jack_net_slave_close(jack_net_slave_t* net); + + SERVER_EXPORT int jack_net_slave_activate(jack_net_slave_t* net); + SERVER_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net); + + SERVER_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg); + SERVER_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t* net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg); + SERVER_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t* net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg); + SERVER_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t* net, JackNetSlaveShutdownCallback shutdown_callback, void *arg); + + // NetJack master API + + typedef struct _jack_net_master jack_net_master_t; + + SERVER_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, const char* name, jack_master_t* request, jack_slave_t* result); + SERVER_EXPORT int jack_net_master_close(jack_net_master_t* net); + + SERVER_EXPORT int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer); + SERVER_EXPORT int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer); + + // NetJack adapter API + + typedef struct _jack_adapter jack_adapter_t; + + SERVER_EXPORT jack_adapter_t* jack_create_adapter(int input, int output, + jack_nframes_t host_buffer_size, + jack_nframes_t host_sample_rate, + jack_nframes_t adapted_buffer_size, + jack_nframes_t adapted_sample_rate); + SERVER_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter); + SERVER_EXPORT void jack_flush_adapter(jack_adapter_t* adapter); + + SERVER_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); + SERVER_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); + +#ifdef __cplusplus +} +#endif + +namespace Jack +{ + +struct JackNetExtMaster : public JackNetMasterInterface { + + // Data buffers + float** fAudioCaptureBuffer; + float** fAudioPlaybackBuffer; + + JackMidiBuffer** fMidiCaptureBuffer; + JackMidiBuffer** fMidiPlaybackBuffer; + + jack_master_t fRequest; + + JackNetExtMaster(const char* ip, + int port, + const char* name, + jack_master_t* request) + { + fRunning = true; + assert(strlen(ip) < 32); + strcpy(fMulticastIP, ip); + fSocket.SetPort(port); + fRequest.buffer_size = request->buffer_size; + fRequest.sample_rate = request->sample_rate; + fAudioCaptureBuffer = NULL; + fAudioPlaybackBuffer = NULL; + fMidiCaptureBuffer = NULL; + fMidiPlaybackBuffer = NULL; + } + + virtual ~JackNetExtMaster() + {} + + int Open(jack_slave_t* result) + { + // Init socket API (win32) + if (SocketAPIInit() < 0) { + jack_error("Can't init Socket API, exiting..."); + return -1; + } + + // Request socket + if (fSocket.NewSocket() == SOCKET_ERROR) { + jack_error("Can't create the network management input socket : %s", StrError(NET_ERROR_CODE)); + return -1; + } + + // Bind the socket to the local port + if (fSocket.Bind() == SOCKET_ERROR) { + jack_error("Can't bind the network manager socket : %s", StrError(NET_ERROR_CODE)); + fSocket.Close(); + return -1; + } + + // Join multicast group + if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) { + jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE)); + } + + // Local loop + if (fSocket.SetLocalLoop() == SOCKET_ERROR) { + jack_error("Can't set local loop : %s", StrError(NET_ERROR_CODE)); + } + + // Set a timeout on the multicast receive (the thread can now be cancelled) + if (fSocket.SetTimeOut(MANAGER_INIT_TIMEOUT) == SOCKET_ERROR) { + jack_error("Can't set timeout : %s", StrError(NET_ERROR_CODE)); + } + + // Main loop, wait for data, deal with it and wait again + int attempt = 0; + int rx_bytes = 0; + + do + { + session_params_t net_params; + rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0); + SessionParamsNToH(&net_params, &fParams); + + if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { + jack_error("Error in receive : %s", StrError(NET_ERROR_CODE)); + if (++attempt == 10) { + jack_error("Can't receive on the socket, exiting net manager" ); + goto error; + } + } + + if (rx_bytes == sizeof(session_params_t )) { + + switch (GetPacketType(&fParams)) { + + case SLAVE_AVAILABLE: + if (MasterInit() == 0) { + SessionParamsDisplay(&fParams); + fRunning = false; + } else { + jack_error("Can't init new net master..."); + goto error; + } + jack_info("Waiting for a slave..."); + break; + + case KILL_MASTER: + break; + + default: + break; + } + } + } + while (fRunning); + + // Set result paramaters + result->audio_input = fParams.fSendAudioChannels; + result->audio_output = fParams.fReturnAudioChannels; + result->midi_input = fParams.fSendMidiChannels; + result->midi_output = fParams.fReturnMidiChannels; + result->mtu = fParams.fMtu; + result->latency = fParams.fNetworkLatency; + return 0; + + error: + fSocket.Close(); + return -1; + } + + int MasterInit() + { + // Check MASTER <==> SLAVE network protocol coherency + if (fParams.fProtocolVersion != MASTER_PROTOCOL) { + jack_error("Error : slave is running with a different protocol %s", fParams.fName); + return -1; + } + + // Settings + fSocket.GetName(fParams.fMasterNetName); + fParams.fID = 1; + fParams.fSampleEncoder = JackFloatEncoder; + fParams.fPeriodSize = fRequest.buffer_size; + fParams.fSampleRate = fRequest.sample_rate; + + // Close request socket + fSocket.Close(); + + // Network slave init + if (!JackNetMasterInterface::Init()) { + return -1; + } + + // Set global parameters + if (!SetParams()) { + return -1; + } + + AllocPorts(); + return 0; + } + + int Close() + { + fSocket.Close(); + FreePorts(); + return 0; + } + + void AllocPorts() + { + // Set buffers + if (fParams.fSendAudioChannels > 0) { + fAudioCaptureBuffer = new float*[fParams.fSendAudioChannels]; + for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { + fAudioCaptureBuffer[audio_port_index] = new float[fParams.fPeriodSize]; + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, fAudioCaptureBuffer[audio_port_index]); + } + } + + if (fParams.fSendMidiChannels > 0) { + fMidiCaptureBuffer = new JackMidiBuffer*[fParams.fSendMidiChannels]; + for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { + fMidiCaptureBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiCaptureBuffer->SetBuffer(midi_port_index, fMidiCaptureBuffer[midi_port_index]); + } + } + + if (fParams.fReturnAudioChannels > 0) { + fAudioPlaybackBuffer = new float*[fParams.fReturnAudioChannels]; + for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { + fAudioPlaybackBuffer[audio_port_index] = new float[fParams.fPeriodSize]; + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, fAudioPlaybackBuffer[audio_port_index]); + } + } + + if (fParams.fReturnMidiChannels > 0) { + fMidiPlaybackBuffer = new JackMidiBuffer*[fParams.fReturnMidiChannels]; + for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { + fMidiPlaybackBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, fMidiPlaybackBuffer[midi_port_index]); + } + } + } + + void FreePorts() + { + if (fAudioPlaybackBuffer) { + for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) + delete[] fAudioPlaybackBuffer[audio_port_index]; + delete[] fAudioPlaybackBuffer; + fAudioPlaybackBuffer = NULL; + } + + if (fMidiPlaybackBuffer) { + for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) + delete[] (fMidiPlaybackBuffer[midi_port_index]); + delete[] fMidiPlaybackBuffer; + fMidiPlaybackBuffer = NULL; + } + + if (fAudioCaptureBuffer) { + for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) + delete[] fAudioCaptureBuffer[audio_port_index]; + delete[] fAudioCaptureBuffer; + fAudioCaptureBuffer = NULL; + } + + if (fMidiCaptureBuffer) { + for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) + delete[] fMidiCaptureBuffer[midi_port_index]; + delete[] fMidiCaptureBuffer; + fMidiCaptureBuffer = NULL; + } + } + + int Read(int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer) + { + try { + assert(audio_input == fParams.fReturnAudioChannels); + + for (int audio_port_index = 0; audio_port_index < audio_input; audio_port_index++) { + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, audio_input_buffer[audio_port_index]); + } + + for (int midi_port_index = 0; midi_port_index < midi_input; midi_port_index++) { + fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_input_buffer)[midi_port_index]); + } + + if (SyncRecv() == SOCKET_ERROR) { + return 0; + } + + DecodeSyncPacket(); + return DataRecv(); + + } catch (JackNetException& e) { + jack_error("Connection lost."); + return -1; + } + } + + int Write(int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer) + { + try { + assert(audio_output == fParams.fSendAudioChannels); + + for (int audio_port_index = 0; audio_port_index < audio_output; audio_port_index++) { + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, audio_output_buffer[audio_port_index]); + } + + for (int midi_port_index = 0; midi_port_index < midi_output; midi_port_index++) { + fNetMidiCaptureBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_output_buffer)[midi_port_index]); + } + + EncodeSyncPacket(); + + if (SyncSend() == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + return DataSend(); + + } catch (JackNetException& e) { + jack_error("Connection lost."); + return -1; + } + } + + // Transport + void EncodeTransportData() + {} + + void DecodeTransportData() + {} + +}; + +struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterface { + + JackThread fThread; + + JackNetSlaveProcessCallback fProcessCallback; + void* fProcessArg; + + JackNetSlaveShutdownCallback fShutdownCallback; + void* fShutdownArg; + + JackNetSlaveBufferSizeCallback fBufferSizeCallback; + void* fBufferSizeArg; + + JackNetSlaveSampleRateCallback fSampleRateCallback; + void* fSampleRateArg; + + //sample buffers + float** fAudioCaptureBuffer; + float** fAudioPlaybackBuffer; + + JackMidiBuffer** fMidiCaptureBuffer; + JackMidiBuffer** fMidiPlaybackBuffer; + + int fConnectTimeOut; + + JackNetExtSlave(const char* ip, + int port, + const char* name, + jack_slave_t* request) + :fThread(this), + fProcessCallback(NULL),fProcessArg(NULL), + fShutdownCallback(NULL), fShutdownArg(NULL), + fBufferSizeCallback(NULL), fBufferSizeArg(NULL), + fSampleRateCallback(NULL), fSampleRateArg(NULL), + fAudioCaptureBuffer(NULL), fAudioPlaybackBuffer(NULL), + fMidiCaptureBuffer(NULL), fMidiPlaybackBuffer(NULL) + { + char host_name[JACK_CLIENT_NAME_SIZE]; + + // Request parameters + assert(strlen(ip) < 32); + strcpy(fMulticastIP, ip); + fParams.fMtu = request->mtu; + fParams.fTransportSync = 0; + fParams.fSendAudioChannels = request->audio_input; + fParams.fReturnAudioChannels = request->audio_output; + fParams.fSendMidiChannels = request->midi_input; + fParams.fReturnMidiChannels = request->midi_output; + fParams.fNetworkLatency = request->latency; + fParams.fSampleEncoder = request->encoder; + fParams.fKBps = request->kbps; + fParams.fSlaveSyncMode = 1; + fConnectTimeOut = request->time_out; + + // Create name with hostname and client name + GetHostName(host_name, JACK_CLIENT_NAME_SIZE); + snprintf(fParams.fName, JACK_CLIENT_NAME_SIZE, "%s_%s", host_name, name); + fSocket.GetName(fParams.fSlaveNetName); + + // Set the socket parameters + fSocket.SetPort(port); + fSocket.SetAddress(fMulticastIP, port); + } + + virtual ~JackNetExtSlave() + {} + + int Open(jack_master_t* result) + { + if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) { + jack_error("Error : network latency is limited to %d", NETWORK_MAX_LATENCY); + return -1; + } + + // Init network connection + if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) { + jack_error("Initing network fails..."); + return -1; + } + + // Finish connection... + if (!JackNetSlaveInterface::InitRendering()) { + jack_error("Starting network fails..."); + return -1; + } + + // Then set global parameters + if (!SetParams()) { + jack_error("SetParams error..."); + return -1; + } + + // Set result + if (result != NULL) { + result->buffer_size = fParams.fPeriodSize; + result->sample_rate = fParams.fSampleRate; + result->audio_input = fParams.fSendAudioChannels; + result->audio_output = fParams.fReturnAudioChannels; + result->midi_input = fParams.fSendMidiChannels; + result->midi_output = fParams.fReturnMidiChannels; + strcpy(result->master_name, fParams.fMasterNetName); + } + + AllocPorts(); + return 0; + } + + int Restart() + { + // If shutdown cb is set, then call it + if (fShutdownCallback) { + fShutdownCallback(fShutdownArg); + } + + // Init network connection + if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) { + jack_error("Initing network fails..."); + return -1; + } + + // Finish connection... + if (!JackNetSlaveInterface::InitRendering()) { + jack_error("Starting network fails..."); + return -1; + } + + // Then set global parameters + if (!SetParams()) { + jack_error("SetParams error..."); + return -1; + } + + // We need to notify possibly new buffer size and sample rate (see Execute) + if (fBufferSizeCallback) { + fBufferSizeCallback(fParams.fPeriodSize, fBufferSizeArg); + } + + if (fSampleRateCallback) { + fSampleRateCallback(fParams.fSampleRate, fSampleRateArg); + } + + AllocPorts(); + return 0; + } + + int Close() + { + fSocket.Close(); + FreePorts(); + return 0; + } + + void AllocPorts() + { + // Set buffers + fAudioCaptureBuffer = new float*[fParams.fSendAudioChannels]; + for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { + fAudioCaptureBuffer[audio_port_index] = new float[fParams.fPeriodSize]; + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, fAudioCaptureBuffer[audio_port_index]); + } + + fMidiCaptureBuffer = new JackMidiBuffer*[fParams.fSendMidiChannels]; + for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { + fMidiCaptureBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiCaptureBuffer->SetBuffer(midi_port_index, fMidiCaptureBuffer[midi_port_index]); + } + + fAudioPlaybackBuffer = new float*[fParams.fReturnAudioChannels]; + for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { + fAudioPlaybackBuffer[audio_port_index] = new float[fParams.fPeriodSize]; + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, fAudioPlaybackBuffer[audio_port_index]); + } + + fMidiPlaybackBuffer = new JackMidiBuffer*[fParams.fReturnMidiChannels]; + for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { + fMidiPlaybackBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, fMidiPlaybackBuffer[midi_port_index]); + } + } + + void FreePorts() + { + if (fAudioCaptureBuffer) { + for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) + delete[] fAudioCaptureBuffer[audio_port_index]; + delete[] fAudioCaptureBuffer; + fAudioCaptureBuffer = NULL; + } + + if (fMidiCaptureBuffer) { + for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) + delete[] (fMidiCaptureBuffer[midi_port_index]); + delete[] fMidiCaptureBuffer; + fMidiCaptureBuffer = NULL; + } + + if (fAudioPlaybackBuffer) { + for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) + delete[] fAudioPlaybackBuffer[audio_port_index]; + delete[] fAudioPlaybackBuffer; + fAudioPlaybackBuffer = NULL; + } + + if (fMidiPlaybackBuffer) { + for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) + delete[] fMidiPlaybackBuffer[midi_port_index]; + delete[] fMidiPlaybackBuffer; + fMidiPlaybackBuffer = NULL; + } + } + + // Transport + void EncodeTransportData() + {} + + void DecodeTransportData() + {} + + bool Init() + { + // Will do "something" on OSX only... + UInt64 period, constraint; + period = constraint = UInt64(1000000000.f * (float(fParams.fPeriodSize) / float(fParams.fSampleRate))); + UInt64 computation = JackTools::ComputationMicroSec(fParams.fPeriodSize) * 1000; + fThread.SetParams(period, computation, constraint); + + return (fThread.AcquireSelfRealTime(80) == 0); // TODO: get a value from the server + } + + bool Execute() + { + try { + // Keep running even in case of error + while (fThread.GetStatus() == JackThread::kRunning) { + if (Process() == SOCKET_ERROR) { + return false; + } + } + return false; + } catch (JackNetException& e) { + + // Otherwise just restart... + e.PrintMessage(); + fThread.DropRealTime(); + fThread.SetStatus(JackThread::kIniting); + FreePorts(); + if (Restart() == 0 && Init()) { + fThread.SetStatus(JackThread::kRunning); + return true; + } else { + return false; + } + } + } + + int Read() + { + //receive sync (launch the cycle) + if (SyncRecv() == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + DecodeSyncPacket(); + return DataRecv(); + } + + int Write() + { + EncodeSyncPacket(); + + if (SyncSend() == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + return DataSend(); + } + + int Process() + { + // Read data from the network, throw JackNetException in case of network error... + if (Read() == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + fProcessCallback(fParams.fPeriodSize, + fParams.fSendAudioChannels, + fAudioCaptureBuffer, + fParams.fSendMidiChannels, + (void**)fMidiCaptureBuffer, + fParams.fReturnAudioChannels, + fAudioPlaybackBuffer, + fParams.fReturnMidiChannels, + (void**)fMidiPlaybackBuffer, + fProcessArg); + + // Then write data to network, throw JackNetException in case of network error... + if (Write() == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + return 0; + } + + int Start() + { + return (fProcessCallback == 0) ? -1 : fThread.StartSync(); + } + + int Stop() + { + return (fProcessCallback == 0) ? -1 : fThread.Kill(); + } + + // Callback + int SetProcessCallback(JackNetSlaveProcessCallback net_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fProcessCallback = net_callback; + fProcessArg = arg; + return 0; + } + } + + int SetShutdownCallback(JackNetSlaveShutdownCallback shutdown_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fShutdownCallback = shutdown_callback; + fShutdownArg = arg; + return 0; + } + } + + int SetBufferSizeCallback(JackNetSlaveBufferSizeCallback bufsize_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fBufferSizeCallback = bufsize_callback; + fBufferSizeArg = arg; + return 0; + } + } + + int SetSampleRateCallback(JackNetSlaveSampleRateCallback samplerate_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fSampleRateCallback = samplerate_callback; + fSampleRateArg = arg; + return 0; + } + } + +}; + +struct JackNetAdapter : public JackAudioAdapterInterface { + + JackNetAdapter(int input, int output, + jack_nframes_t host_buffer_size, + jack_nframes_t host_sample_rate, + jack_nframes_t adapted_buffer_size, + jack_nframes_t adapted_sample_rate) + :JackAudioAdapterInterface(host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate) + { + fCaptureChannels = input; + fPlaybackChannels = output; + Create(); + } + + void Create() + { + //ringbuffers + + if (fCaptureChannels > 0) { + fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; + } + if (fPlaybackChannels > 0) { + fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; + } + + if (fAdaptative) { + AdaptRingBufferSize(); + jack_info("Ringbuffer automatic adaptative mode size = %d frames", fRingbufferCurSize); + } else { + if (fRingbufferCurSize > DEFAULT_RB_SIZE) { + fRingbufferCurSize = DEFAULT_RB_SIZE; + } + jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize); + } + + for (int i = 0; i < fCaptureChannels; i++ ) { + fCaptureRingBuffer[i] = new JackResampler(); + fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); + } + for (int i = 0; i < fPlaybackChannels; i++ ) { + fPlaybackRingBuffer[i] = new JackResampler(); + fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); + } + + if (fCaptureChannels > 0) { + jack_log("ReadSpace = %ld", fCaptureRingBuffer[0]->ReadSpace()); + } + if (fPlaybackChannels > 0) { + jack_log("WriteSpace = %ld", fPlaybackRingBuffer[0]->WriteSpace()); + } + } + + virtual ~JackNetAdapter() + { + Destroy(); + } + + void Flush() + { + for (int i = 0; i < fCaptureChannels; i++ ) { + fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); + } + for (int i = 0; i < fPlaybackChannels; i++ ) { + fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); + } + } + +}; + + +} // end of namespace + +using namespace Jack; + +SERVER_EXPORT jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result) +{ + JackNetExtSlave* slave = new JackNetExtSlave(ip, port, name, request); + if (slave->Open(result) == 0) { + return (jack_net_slave_t*)slave; + } else { + delete slave; + return NULL; + } +} + +SERVER_EXPORT int jack_net_slave_close(jack_net_slave_t* net) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + slave->Close(); + delete slave; + return 0; +} + +SERVER_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetProcessCallback(net_callback, arg); +} + +SERVER_EXPORT int jack_net_slave_activate(jack_net_slave_t* net) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->Start(); +} + +SERVER_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->Stop(); +} + +SERVER_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetBufferSizeCallback(bufsize_callback, arg); +} + +SERVER_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetSampleRateCallback(samplerate_callback, arg); +} + +SERVER_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetShutdownCallback(shutdown_callback, arg); +} + +// Master API + +SERVER_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, const char* name, jack_master_t* request, jack_slave_t* result) +{ + JackNetExtMaster* master = new JackNetExtMaster(ip, port, name, request); + if (master->Open(result) == 0) { + return (jack_net_master_t*)master; + } else { + delete master; + return NULL; + } +} + +SERVER_EXPORT int jack_net_master_close(jack_net_master_t* net) +{ + JackNetExtMaster* master = (JackNetExtMaster*)net; + master->Close(); + delete master; + return 0; +} +SERVER_EXPORT int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer) +{ + JackNetExtMaster* master = (JackNetExtMaster*)net; + return master->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer); +} + +SERVER_EXPORT int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer) +{ + JackNetExtMaster* master = (JackNetExtMaster*)net; + return master->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer); +} + +// Adapter API + +SERVER_EXPORT jack_adapter_t* jack_create_adapter(int input, int output, + jack_nframes_t host_buffer_size, + jack_nframes_t host_sample_rate, + jack_nframes_t adapted_buffer_size, + jack_nframes_t adapted_sample_rate) +{ + try { + return (jack_adapter_t*)new JackNetAdapter(input, output, host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate); + } catch (...) { + return NULL; + } +} + +SERVER_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter) +{ + delete((JackNetAdapter*)adapter); + return 0; +} + +SERVER_EXPORT void jack_flush_adapter(jack_adapter_t* adapter) +{ + JackNetAdapter* slave = (JackNetAdapter*)adapter; + slave->Flush(); +} + +SERVER_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames) +{ + JackNetAdapter* slave = (JackNetAdapter*)adapter; + return slave->PushAndPull(input, output, frames); +} + +SERVER_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames) +{ + JackNetAdapter* slave = (JackNetAdapter*)adapter; + return slave->PullAndPush(input, output, frames); +} + + +//#ifdef MY_TARGET_OS_IPHONE +#if 1 + +static void jack_format_and_log(int level, const char *prefix, const char *fmt, va_list ap) +{ + char buffer[300]; + size_t len; + + if (prefix != NULL) { + len = strlen(prefix); + memcpy(buffer, prefix, len); + } else { + len = 0; + } + + vsnprintf(buffer + len, sizeof(buffer) - len, fmt, ap); + printf("%s", buffer); + printf("\n"); +} + +SERVER_EXPORT void jack_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); + va_end(ap); +} + +SERVER_EXPORT void jack_info(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); + va_end(ap); +} + +SERVER_EXPORT void jack_log(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); + va_end(ap); +} + +#else + +// Empty code for now.. + +SERVER_EXPORT void jack_error(const char *fmt, ...) +{} + +SERVER_EXPORT void jack_info(const char *fmt, ...) +{} + +SERVER_EXPORT void jack_log(const char *fmt, ...) +{} + +#endif + diff --git a/common/JackNetAdapter.cpp b/common/JackNetAdapter.cpp index 5532f01e..a41ada28 100644 --- a/common/JackNetAdapter.cpp +++ b/common/JackNetAdapter.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -25,72 +25,89 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { - JackNetAdapter::JackNetAdapter ( jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params ) - : JackAudioAdapterInterface ( buffer_size, sample_rate ), JackNetSlaveInterface(), fThread ( this ) + JackNetAdapter::JackNetAdapter(jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params) + : JackAudioAdapterInterface(buffer_size, sample_rate), JackNetSlaveInterface(), fThread(this) { - jack_log ( "JackNetAdapter::JackNetAdapter" ); - - //global parametering - //we can't call JackNetSlaveInterface constructor with some parameters before - //because we don't have full parametering right now - //parameters will be parsed from the param list, and then JackNetSlaveInterface will be filled with proper values - strcpy ( fMulticastIP, DEFAULT_MULTICAST_IP ); - uint port = DEFAULT_PORT; - GetHostName ( fParams.fName, JACK_CLIENT_NAME_SIZE ); - fSocket.GetName ( fParams.fSlaveNetName ); + jack_log("JackNetAdapter::JackNetAdapter"); + + /* + Global parameter setting : we can't call JackNetSlaveInterface constructor with some parameters before, + because we don't have full parametering right now, parameters will be parsed from the param list, + and then JackNetSlaveInterface will be filled with proper values. + */ + char multicast_ip[32]; + uint udp_port; + GetHostName(fParams.fName, JACK_CLIENT_NAME_SIZE); + fSocket.GetName(fParams.fSlaveNetName); fParams.fMtu = DEFAULT_MTU; + // Desactivated for now... fParams.fTransportSync = 0; - fParams.fSendAudioChannels = 2; - fParams.fReturnAudioChannels = 2; + int send_audio = -1; + int return_audio = -1; fParams.fSendMidiChannels = 0; fParams.fReturnMidiChannels = 0; fParams.fSampleRate = sample_rate; fParams.fPeriodSize = buffer_size; fParams.fSlaveSyncMode = 1; - fParams.fNetworkMode = 's'; - fJackClient = jack_client; + fParams.fNetworkLatency = 2; + fParams.fSampleEncoder = JackFloatEncoder; + fClient = jack_client; + + // Possibly use env variable + const char* default_udp_port = getenv("JACK_NETJACK_PORT"); + udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT; + + const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); + if (default_multicast_ip) { + strcpy(multicast_ip, default_multicast_ip); + } else { + strcpy(multicast_ip, DEFAULT_MULTICAST_IP); + } //options parsing const JSList* node; const jack_driver_param_t* param; - for ( node = params; node; node = jack_slist_next ( node ) ) + for (node = params; node; node = jack_slist_next(node)) { - param = ( const jack_driver_param_t* ) node->data; - switch ( param->character ) - { + param = (const jack_driver_param_t*) node->data; + + switch (param->character) { case 'a' : - if (strlen (param->value.str) < 32) - strcpy(fMulticastIP, param->value.str); - else - jack_error("Can't use multicast address %s, using default %s", param->value.ui, DEFAULT_MULTICAST_IP); + assert(strlen(param->value.str) < 32); + strcpy(multicast_ip, param->value.str); break; case 'p' : - fSocket.SetPort ( param->value.ui ); + udp_port = param->value.ui; break; case 'M' : fParams.fMtu = param->value.i; break; case 'C' : - fParams.fSendAudioChannels = param->value.i; + send_audio = param->value.i; break; case 'P' : - fParams.fReturnAudioChannels = param->value.i; + return_audio = param->value.i; break; case 'n' : - strncpy ( fParams.fName, param->value.str, JACK_CLIENT_NAME_SIZE ); + strncpy(fParams.fName, param->value.str, JACK_CLIENT_NAME_SIZE); break; case 't' : - //fParams.fTransportSync = param->value.ui; + fParams.fTransportSync = param->value.ui; + break; + #if HAVE_CELT + case 'c': + if (param->value.i > 0) { + fParams.fSampleEncoder = JackCeltEncoder; + fParams.fKBps = param->value.i; + } break; - case 'm' : - if ( strcmp ( param->value.str, "normal" ) == 0 ) - fParams.fNetworkMode = 'n'; - else if ( strcmp ( param->value.str, "slow" ) == 0 ) - fParams.fNetworkMode = 's'; - else if ( strcmp ( param->value.str, "fast" ) == 0 ) - fParams.fNetworkMode = 'f'; - else - jack_error ( "Unknown network mode, using 'normal' mode." ); + #endif + case 'l' : + fParams.fNetworkLatency = param->value.i; + if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) { + jack_error("Error : network latency is limited to %d\n", NETWORK_MAX_LATENCY); + throw std::bad_alloc(); + } break; case 'q': fQuality = param->value.ui; @@ -102,34 +119,41 @@ namespace Jack } } - //set the socket parameters - fSocket.SetPort ( port ); - fSocket.SetAddress ( fMulticastIP, port ); + strcpy(fMulticastIP, multicast_ip); + + // Set the socket parameters + fSocket.SetPort(udp_port); + fSocket.SetAddress(fMulticastIP, udp_port); + + // If not set, takes default + fParams.fSendAudioChannels = (send_audio == -1) ? 2 : send_audio; + + // If not set, takes default + fParams.fReturnAudioChannels = (return_audio == -1) ? 2 : return_audio; - //set the audio adapter interface channel values - SetInputs ( fParams.fSendAudioChannels ); - SetOutputs ( fParams.fReturnAudioChannels ); + // Set the audio adapter interface channel values + SetInputs(fParams.fSendAudioChannels); + SetOutputs(fParams.fReturnAudioChannels); - //soft buffers will be allocated later (once network initialization done) + // Soft buffers will be allocated later (once network initialization done) fSoftCaptureBuffer = NULL; fSoftPlaybackBuffer = NULL; } JackNetAdapter::~JackNetAdapter() { - jack_log ( "JackNetAdapter::~JackNetAdapter" ); + jack_log("JackNetAdapter::~JackNetAdapter"); - int port_index; - if ( fSoftCaptureBuffer ) - { - for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) + if (fSoftCaptureBuffer) { + for (int port_index = 0; port_index < fCaptureChannels; port_index++) { delete[] fSoftCaptureBuffer[port_index]; + } delete[] fSoftCaptureBuffer; } - if ( fSoftPlaybackBuffer ) - { - for ( port_index = 0; port_index < fPlaybackChannels; port_index++ ) + if (fSoftPlaybackBuffer) { + for (int port_index = 0; port_index < fPlaybackChannels; port_index++) { delete[] fSoftPlaybackBuffer[port_index]; + } delete[] fSoftPlaybackBuffer; } } @@ -137,14 +161,11 @@ namespace Jack //open/close-------------------------------------------------------------------------- int JackNetAdapter::Open() { - jack_log ( "JackNetAdapter::Open" ); - - jack_info ( "NetAdapter started in %s mode %s Master's transport sync.", - ( fParams.fSlaveSyncMode ) ? "sync" : "async", ( fParams.fTransportSync ) ? "with" : "without" ); + jack_info("NetAdapter started in %s mode %s Master's transport sync.", + (fParams.fSlaveSyncMode) ? "sync" : "async", (fParams.fTransportSync) ? "with" : "without"); - if ( fThread.StartSync() < 0 ) - { - jack_error ( "Cannot start netadapter thread" ); + if (fThread.StartSync() < 0) { + jack_error("Cannot start netadapter thread"); return -1; } @@ -153,89 +174,79 @@ namespace Jack int JackNetAdapter::Close() { - jack_log ( "JackNetAdapter::Close" ); + int res = 0; + jack_log("JackNetAdapter::Close"); #ifdef JACK_MONITOR fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize); #endif - switch ( fThread.GetStatus() ) - { - // Kill the thread in Init phase - case JackThread::kStarting: - case JackThread::kIniting: - if ( fThread.Kill() < 0 ) - { - jack_error ( "Cannot kill thread" ); - return -1; - } - break; - // Stop when the thread cycle is finished - - case JackThread::kRunning: - if ( fThread.Stop() < 0 ) - { - jack_error ( "Cannot stop thread" ); - return -1; - } - break; - - default: - break; + if (fThread.Kill() < 0) { + jack_error("Cannot kill thread"); + res = -1; } + fSocket.Close(); - return 0; - } + return res; + } - int JackNetAdapter::SetBufferSize ( jack_nframes_t buffer_size ) + int JackNetAdapter::SetBufferSize(jack_nframes_t buffer_size) { - JackAudioAdapterInterface::SetHostBufferSize ( buffer_size ); + JackAudioAdapterInterface::SetHostBufferSize(buffer_size); return 0; } //thread------------------------------------------------------------------------------ + // TODO : if failure, thread exist... need to restart ? + bool JackNetAdapter::Init() { - jack_log ( "JackNetAdapter::Init" ); - - int port_index; + jack_log("JackNetAdapter::Init"); //init network connection - if ( !JackNetSlaveInterface::Init() ) + if (!JackNetSlaveInterface::Init()) { + jack_error("JackNetSlaveInterface::Init() error..."); return false; + } //then set global parameters - SetParams(); + if (!SetParams()) { + jack_error("SetParams error..."); + return false; + } //set buffers - fSoftCaptureBuffer = new sample_t*[fCaptureChannels]; - for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) - { - fSoftCaptureBuffer[port_index] = new sample_t[fParams.fPeriodSize]; - fNetAudioCaptureBuffer->SetBuffer ( port_index, fSoftCaptureBuffer[port_index] ); + if (fCaptureChannels > 0) { + fSoftCaptureBuffer = new sample_t*[fCaptureChannels]; + for (int port_index = 0; port_index < fCaptureChannels; port_index++) { + fSoftCaptureBuffer[port_index] = new sample_t[fParams.fPeriodSize]; + fNetAudioCaptureBuffer->SetBuffer(port_index, fSoftCaptureBuffer[port_index]); + } } - fSoftPlaybackBuffer = new sample_t*[fPlaybackChannels]; - for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) - { - fSoftPlaybackBuffer[port_index] = new sample_t[fParams.fPeriodSize]; - fNetAudioPlaybackBuffer->SetBuffer ( port_index, fSoftPlaybackBuffer[port_index] ); + + if (fPlaybackChannels > 0) { + fSoftPlaybackBuffer = new sample_t*[fPlaybackChannels]; + for (int port_index = 0; port_index < fPlaybackChannels; port_index++) { + fSoftPlaybackBuffer[port_index] = new sample_t[fParams.fPeriodSize]; + fNetAudioPlaybackBuffer->SetBuffer(port_index, fSoftPlaybackBuffer[port_index]); + } } //set audio adapter parameters - SetAdaptedBufferSize ( fParams.fPeriodSize ); - SetAdaptedSampleRate ( fParams.fSampleRate ); - + 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 ); + SessionParamsDisplay(&fParams); return true; } @@ -244,12 +255,13 @@ namespace Jack try { // Keep running even in case of error while (fThread.GetStatus() == JackThread::kRunning) - if (Process() == SOCKET_ERROR) + if (Process() == SOCKET_ERROR) { return false; + } return false; } catch (JackNetException& e) { e.PrintMessage(); - jack_info("NetAdapter is restarted."); + jack_info("NetAdapter is restarted"); Reset(); fThread.DropSelfRealTime(); fThread.SetStatus(JackThread::kIniting); @@ -268,26 +280,25 @@ namespace Jack //TODO : we need here to get the actual timebase master to eventually release it from its duty (see JackNetDriver) //is there a new transport state ? - if ( fSendTransportData.fNewState && ( fSendTransportData.fState != jack_transport_query ( fJackClient, NULL ) ) ) - { - switch ( fSendTransportData.fState ) + if (fSendTransportData.fNewState &&(fSendTransportData.fState != jack_transport_query(fClient, NULL))) { + switch (fSendTransportData.fState) { case JackTransportStopped : - jack_transport_stop ( fJackClient ); - jack_info ( "NetMaster : transport stops." ); + jack_transport_stop(fClient); + jack_info("NetMaster : transport stops"); break; - + case JackTransportStarting : - jack_transport_reposition ( fJackClient, &fSendTransportData.fPosition ); - jack_transport_start ( fJackClient ); - jack_info ( "NetMaster : transport starts." ); + jack_transport_reposition(fClient, &fSendTransportData.fPosition); + jack_transport_start(fClient); + jack_info("NetMaster : transport starts"); break; - + case JackTransportRolling : - //TODO , we need to : + // TODO, we need to : // - find a way to call TransportEngine->SetNetworkSync() // - turn the transport state to JackTransportRolling - jack_info ( "NetMaster : transport rolls." ); + jack_info("NetMaster : transport rolls"); break; } } @@ -299,33 +310,30 @@ namespace Jack int refnum = -1; bool conditional = 0; //TODO : get the actual timebase master - if ( refnum != fLastTimebaseMaster ) - { + if (refnum != fLastTimebaseMaster) { //timebase master has released its function - if ( refnum == -1 ) - { + if (refnum == -1) { fReturnTransportData.fTimebaseMaster = RELEASE_TIMEBASEMASTER; - jack_info ( "Sending a timebase master release request." ); - } - //there is a new timebase master - else - { - fReturnTransportData.fTimebaseMaster = ( conditional ) ? CONDITIONAL_TIMEBASEMASTER : TIMEBASEMASTER; - jack_info ( "Sending a %s timebase master request.", ( conditional ) ? "conditional" : "non-conditional" ); + jack_info("Sending a timebase master release request."); + } else { + //there is a new timebase master + fReturnTransportData.fTimebaseMaster = (conditional) ? CONDITIONAL_TIMEBASEMASTER : TIMEBASEMASTER; + jack_info("Sending a %s timebase master request.", (conditional) ? "conditional" : "non-conditional"); } fLastTimebaseMaster = refnum; - } - else + } else { fReturnTransportData.fTimebaseMaster = NO_CHANGE; + } //update transport state and position - fReturnTransportData.fState = jack_transport_query ( fJackClient, &fReturnTransportData.fPosition ); + fReturnTransportData.fState = jack_transport_query(fClient, &fReturnTransportData.fPosition); //is it a new state (that the master need to know...) ? - fReturnTransportData.fNewState = ( ( fReturnTransportData.fState != fLastTransportState ) && - ( fReturnTransportData.fState != fSendTransportData.fState ) ); - if ( fReturnTransportData.fNewState ) - jack_info ( "Sending transport state '%s'.", GetTransportState ( fReturnTransportData.fState ) ); + fReturnTransportData.fNewState = ((fReturnTransportData.fState != fLastTransportState) && + (fReturnTransportData.fState != fSendTransportData.fState)); + if (fReturnTransportData.fNewState) { + jack_info("Sending transport state '%s'.", GetTransportState(fReturnTransportData.fState)); + } fLastTransportState = fReturnTransportData.fState; } @@ -334,8 +342,9 @@ namespace Jack { //don't return -1 in case of sync recv failure //we need the process to continue for network error detection - if ( SyncRecv() == SOCKET_ERROR ) + if (SyncRecv() == SOCKET_ERROR) { return 0; + } DecodeSyncPacket(); return DataRecv(); @@ -344,9 +353,10 @@ namespace Jack int JackNetAdapter::Write() { EncodeSyncPacket(); - - if ( SyncSend() == SOCKET_ERROR ) + + if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; + } return DataSend(); } @@ -356,19 +366,21 @@ namespace Jack { //read data from the network //in case of fatal network error, stop the process - if (Read() == SOCKET_ERROR) + if (Read() == SOCKET_ERROR) { return SOCKET_ERROR; - + } + PushAndPull(fSoftCaptureBuffer, fSoftPlaybackBuffer, fAdaptedBufferSize); //then write data to network //in case of failure, stop process - if (Write() == SOCKET_ERROR) + if (Write() == SOCKET_ERROR) { return SOCKET_ERROR; + } return 0; } - + } // namespace Jack //loader------------------------------------------------------------------------------ @@ -384,155 +396,102 @@ 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 ) ); - - int i = 0; - strcpy ( desc->params[i].name, "multicast_ip" ); - desc->params[i].character = 'a'; - desc->params[i].type = JackDriverParamString; - strcpy ( desc->params[i].value.str, DEFAULT_MULTICAST_IP ); - strcpy ( desc->params[i].short_desc, "Multicast Address" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "udp_net_port" ); - desc->params[i].character = 'p'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = DEFAULT_PORT; - strcpy ( desc->params[i].short_desc, "UDP port" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "mtu" ); - desc->params[i].character = 'M'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = DEFAULT_MTU; - strcpy ( desc->params[i].short_desc, "MTU to the master" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "input-ports" ); - desc->params[i].character = 'C'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = 2; - strcpy ( desc->params[i].short_desc, "Number of audio input ports" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "output-ports" ); - desc->params[i].character = 'P'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = 2; - strcpy ( desc->params[i].short_desc, "Number of audio output ports" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "client-name" ); - desc->params[i].character = 'n'; - desc->params[i].type = JackDriverParamString; - strcpy ( desc->params[i].value.str, "'hostname'" ); - strcpy ( desc->params[i].short_desc, "Name of the jack client" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "transport-sync" ); - desc->params[i].character = 't'; - desc->params[i].type = JackDriverParamUInt; - desc->params[i].value.ui = 1U; - strcpy ( desc->params[i].short_desc, "Sync transport with master's" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "mode" ); - desc->params[i].character = 'm'; - desc->params[i].type = JackDriverParamString; - 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'; - desc->params[i].type = JackDriverParamInt; - 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'; - desc->params[i].type = JackDriverParamInt; - 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'; - desc->params[i].type = JackDriverParamBool; - 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 ); - + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("netadapter", JackDriverNone, "netjack net <==> audio backend adapter", &filler); + + strcpy(value.str, DEFAULT_MULTICAST_IP); + jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast Address", NULL); + + value.i = DEFAULT_PORT; + jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); + + value.i = DEFAULT_MTU; + jack_driver_descriptor_add_parameter(desc, &filler, "mtu", 'M', JackDriverParamInt, &value, NULL, "MTU to the master", NULL); + + value.i = 2; + jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio output ports", NULL); + + #if HAVE_CELT + value.i = -1; + jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamInt, &value, NULL, "Set CELT encoding and number of kBits per channel", NULL); + #endif + + strcpy(value.str, "'hostname'"); + jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL); + + value.ui = 0U; + jack_driver_descriptor_add_parameter(desc, &filler, "transport-sync", 't', JackDriverParamUInt, &value, NULL, "Sync transport with master's", NULL); + + value.ui = 5U; + jack_driver_descriptor_add_parameter(desc, &filler, "latency", 'l', JackDriverParamUInt, &value, NULL, "Network latency", NULL); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "quality", 'q', JackDriverParamInt, &value, NULL, "Resample algorithm quality (0 - 4)", NULL); + + value.i = 32768; + jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)"); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect netmaster to system ports", ""); + return desc; } - SERVER_EXPORT int jack_internal_initialize ( jack_client_t* jack_client, const JSList* params ) + SERVER_EXPORT int jack_internal_initialize(jack_client_t* client, const JSList* params) { - jack_log ( "Loading netadapter" ); + jack_log("Loading netadapter"); 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 ); - + jack_nframes_t buffer_size = jack_get_buffer_size(client); + jack_nframes_t sample_rate = jack_get_sample_rate(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 ) + adapter = new Jack::JackAudioAdapter(client, new Jack::JackNetAdapter(client, buffer_size, sample_rate, params), params); + assert(adapter); + + if (adapter->Open() == 0) { return 0; - else - { + } else { delete adapter; return 1; } - + } catch (...) { + jack_info("NetAdapter allocation error"); return 1; } } - SERVER_EXPORT int jack_initialize ( jack_client_t* jack_client, const char* load_init ) + SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); - Jack::JackArgParser parser ( load_init ); - if ( parser.GetArgc() > 0 ) - parse_params = parser.ParseParams ( desc, ¶ms ); + Jack::JackArgParser parser(load_init); + if (parser.GetArgc() > 0) { + parse_params = parser.ParseParams(desc, ¶ms); + } if (parse_params) { - res = jack_internal_initialize ( jack_client, params ); - parser.FreeParams ( params ); + res = jack_internal_initialize(jack_client, params); + parser.FreeParams(params); } return res; } - SERVER_EXPORT void jack_finish ( void* arg ) + SERVER_EXPORT void jack_finish(void* arg) { - Jack::JackAudioAdapter* adapter = static_cast ( arg ); + Jack::JackAudioAdapter* adapter = static_cast(arg); if (adapter) { - jack_log ( "Unloading netadapter" ); + jack_log("Unloading netadapter"); adapter->Close(); delete adapter; } diff --git a/common/JackNetAdapter.h b/common/JackNetAdapter.h index f6b7675b..05de4632 100644 --- a/common/JackNetAdapter.h +++ b/common/JackNetAdapter.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -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* fClient; - //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(); }; } diff --git a/common/JackNetDriver.cpp b/common/JackNetDriver.cpp index d2327eb1..545eecdf 100644 --- a/common/JackNetDriver.cpp +++ b/common/JackNetDriver.cpp @@ -1,6 +1,5 @@ /* -Copyright (C) 2001 Paul Davis -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -19,32 +18,39 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackNetDriver.h" #include "JackEngineControl.h" -#include "JackGraphManager.h" +#include "JackLockedEngine.h" #include "JackWaitThreadedDriver.h" - using namespace std; namespace Jack { - JackNetDriver::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_mode ) - : JackAudioDriver ( name, alias, engine, table ), JackNetSlaveInterface ( ip, port ) + JackNetDriver::JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, + const char* ip, int udp_port, int mtu, int midi_input_ports, int midi_output_ports, + char* net_name, uint transport_sync, int network_latency, int celt_encoding) + : JackWaiterDriver(name, alias, engine, table), JackNetSlaveInterface(ip, udp_port) { - jack_log ( "JackNetDriver::JackNetDriver ip %s, port %d", ip, port ); + jack_log("JackNetDriver::JackNetDriver ip %s, port %d", ip, udp_port); // Use the hostname if no name parameter was given - if ( strcmp ( net_name, "" ) == 0 ) - GetHostName ( net_name, JACK_CLIENT_NAME_SIZE ); + if (strcmp(net_name, "") == 0) { + GetHostName(net_name, JACK_CLIENT_NAME_SIZE); + } fParams.fMtu = mtu; fParams.fSendMidiChannels = midi_input_ports; fParams.fReturnMidiChannels = midi_output_ports; - strcpy ( fParams.fName, net_name ); - fSocket.GetName ( fParams.fSlaveNetName ); + if (celt_encoding > 0) { + fParams.fSampleEncoder = JackCeltEncoder; + fParams.fKBps = celt_encoding; + } else { + fParams.fSampleEncoder = JackFloatEncoder; + //fParams.fSampleEncoder = JackIntEncoder; + } + strcpy(fParams.fName, net_name); + fSocket.GetName(fParams.fSlaveNetName); fParams.fTransportSync = transport_sync; - fParams.fNetworkMode = network_mode; + fParams.fNetworkLatency = network_latency; fSendTransportData.fState = -1; fReturnTransportData.fState = -1; fLastTransportState = -1; @@ -67,42 +73,16 @@ namespace Jack } //open, close, attach and detach------------------------------------------------------ - int JackNetDriver::Open ( jack_nframes_t buffer_size, jack_nframes_t samplerate, 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 ) - { - if ( JackAudioDriver::Open ( buffer_size, - samplerate, - capturing, - playing, - inchannels, - outchannels, - monitor, - capture_driver_name, - playback_driver_name, - capture_latency, - playback_latency ) == 0 ) - { - fEngineControl->fPeriod = 0; - fEngineControl->fComputation = 500 * 1000; - fEngineControl->fConstraint = 500 * 1000; - return 0; - } - else - { - return -1; - } - } int JackNetDriver::Close() { #ifdef JACK_MONITOR - if ( fNetTimeMon ) + if (fNetTimeMon) { fNetTimeMon->Save(); + } #endif FreeAll(); - return JackDriver::Close(); + return JackWaiterDriver::Close(); } // Attach and Detach are defined as empty methods: port allocation is done when driver actually start (that is in Init) @@ -124,92 +104,99 @@ namespace Jack bool JackNetDriver::Initialize() { - jack_log("JackNetDriver::Initialize()"); + jack_log("JackNetDriver::Initialize"); + SaveConnections(); + FreePorts(); - //new loading, but existing socket, restart the driver + // New loading, but existing socket, restart the driver if (fSocket.IsSocket()) { jack_info("Restarting driver..."); FreeAll(); } - //set the parameters to send + // Set the parameters to send fParams.fSendAudioChannels = fCaptureChannels; fParams.fReturnAudioChannels = fPlaybackChannels; fParams.fSlaveSyncMode = fEngineControl->fSyncMode; - //display some additional infos - jack_info ( "NetDriver started in %s mode %s Master's transport sync.", - ( fParams.fSlaveSyncMode ) ? "sync" : "async", ( fParams.fTransportSync ) ? "with" : "without" ); + // Display some additional infos + jack_info("NetDriver started in %s mode %s Master's transport sync.", + (fParams.fSlaveSyncMode) ? "sync" : "async", (fParams.fTransportSync) ? "with" : "without"); + + // Init network + if (!JackNetSlaveInterface::Init()) { + jack_error("Starting network fails..."); + return false; + } - //init network - if ( !JackNetSlaveInterface::Init() ) + // Set global parameters + if (!SetParams()) { + jack_error("SetParams error..."); return false; + } - //set global parameters - SetParams(); + // If -1 at connection time, in/out channels count is sent by the master + fCaptureChannels = fParams.fSendAudioChannels; + fPlaybackChannels = fParams.fReturnAudioChannels; - //allocate midi ports lists + // Allocate midi ports lists fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels]; fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels]; - assert ( fMidiCapturePortList ); - assert ( fMidiPlaybackPortList ); - //register jack ports - if ( AllocPorts() != 0 ) - { - jack_error ( "Can't allocate ports." ); + 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."); return false; } - //init done, display parameters - SessionParamsDisplay ( &fParams ); + // Init done, display parameters + SessionParamsDisplay(&fParams); - //monitor + // Monitor #ifdef JACK_MONITOR string plot_name; - //NetTimeMon - plot_name = string ( fParams.fName ); - plot_name += string ( "_slave" ); - plot_name += ( fEngineControl->fSyncMode ) ? string ( "_sync" ) : string ( "_async" ); - switch ( fParams.fNetworkMode ) - { - case 's' : - plot_name += string ( "_slow" ); - break; - - case 'n' : - plot_name += string ( "_normal" ); - break; - - case 'f' : - plot_name += string ( "_fast" ); - break; - } - fNetTimeMon = new JackGnuPlotMonitor ( 128, 5, plot_name ); + // NetTimeMon + plot_name = string(fParams.fName); + plot_name += string("_slave"); + plot_name += (fEngineControl->fSyncMode) ? string("_sync") : string("_async"); + plot_name += string("_latency"); + fNetTimeMon = new JackGnuPlotMonitor(128, 5, plot_name); string net_time_mon_fields[] = { - string ( "sync decoded" ), - string ( "end of read" ), - string ( "start of write" ), - string ( "sync send" ), - string ( "end of write" ) + string("sync decoded"), + string("end of read"), + string("start of write"), + string("sync send"), + string("end of write") }; string net_time_mon_options[] = { - string ( "set xlabel \"audio cycles\"" ), - string ( "set ylabel \"% of audio cycle\"" ) + string("set xlabel \"audio cycles\""), + string("set ylabel \"% of audio cycle\"") }; - fNetTimeMon->SetPlotFile ( net_time_mon_options, 2, net_time_mon_fields, 5 ); + fNetTimeMon->SetPlotFile(net_time_mon_options, 2, net_time_mon_fields, 5); #endif - //driver parametering - JackAudioDriver::SetBufferSize ( fParams.fPeriodSize ); - JackAudioDriver::SetSampleRate ( fParams.fSampleRate ); + // Driver parametering + JackTimedDriver::SetBufferSize(fParams.fPeriodSize); + JackTimedDriver::SetSampleRate(fParams.fSampleRate); - JackDriver::NotifyBufferSize ( fParams.fPeriodSize ); - JackDriver::NotifySampleRate ( fParams.fSampleRate ); + JackDriver::NotifyBufferSize(fParams.fPeriodSize); + JackDriver::NotifySampleRate(fParams.fSampleRate); - //transport engine parametering - fEngineControl->fTransport.SetNetworkSync ( fParams.fTransportSync ); + // Transport engine parametering + fEngineControl->fTransport.SetNetworkSync(fParams.fTransportSync); + + RestoreConnections(); return true; } @@ -244,115 +231,95 @@ namespace Jack //jack ports and buffers-------------------------------------------------------------- int JackNetDriver::AllocPorts() { - jack_log ( "JackNetDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate ); + jack_log("JackNetDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + + /* + fNetAudioCaptureBuffer fNetAudioPlaybackBuffer + fSendAudioChannels fReturnAudioChannels + + fCapturePortList fPlaybackPortList + fCaptureChannels ==> SLAVE ==> fPlaybackChannels + "capture_" "playback_" + */ JackPort* port; - jack_port_id_t port_id; - char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - unsigned long port_flags; + jack_port_id_t port_index; + char name[REAL_JACK_PORT_NAME_SIZE]; + char alias[REAL_JACK_PORT_NAME_SIZE]; int audio_port_index; - uint midi_port_index; + int midi_port_index; jack_latency_range_t range; //audio - port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; - for ( audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++ ) - { - snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, audio_port_index + 1 ); - snprintf ( name, sizeof ( name ) - 1, "%s:capture_%d", fClientControl.fName, audio_port_index + 1 ); - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, - static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", name ); + for (audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++) { + snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, audio_port_index + 1); + snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, audio_port_index + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, + CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", name); return -1; } - port = fGraphManager->GetPort ( port_id ); - port->SetAlias ( alias ); + //port latency + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); range.min = range.max = fEngineControl->fBufferSize; port->SetLatencyRange(JackCaptureLatency, &range); - fCapturePortList[audio_port_index] = port_id; - jack_log ( "JackNetDriver::AllocPorts() fCapturePortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_id, port->GetLatency() ); + fCapturePortList[audio_port_index] = port_index; + jack_log("JackNetDriver::AllocPorts() fCapturePortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_index, port->GetLatency()); } - port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; - for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) - { - snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, audio_port_index + 1 ); - snprintf ( name, sizeof ( name ) - 1, "%s:playback_%d",fClientControl.fName, audio_port_index + 1 ); - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, - static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", name ); + + for (audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) { + snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, audio_port_index + 1); + snprintf(name, sizeof(name), "%s:playback_%d",fClientControl.fName, audio_port_index + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, + PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", name); return -1; } - port = fGraphManager->GetPort ( port_id ); - port->SetAlias ( alias ); + //port latency - switch ( fParams.fNetworkMode ) - { - case 'f' : - range.min = range.max = (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize; - break; - case 'n' : - range.min = range.max = (fEngineControl->fBufferSize + (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize); - break; - case 's' : - range.min = range.max = (2 * fEngineControl->fBufferSize + (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize); - break; - } + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + range.min = range.max = (fParams.fNetworkLatency * fEngineControl->fBufferSize + (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize); port->SetLatencyRange(JackPlaybackLatency, &range); - fPlaybackPortList[audio_port_index] = port_id; - jack_log ( "JackNetDriver::AllocPorts() fPlaybackPortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_id, port->GetLatency() ); + fPlaybackPortList[audio_port_index] = port_index; + jack_log("JackNetDriver::AllocPorts() fPlaybackPortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_index, port->GetLatency()); } + //midi - port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; - for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) - { - snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, midi_port_index + 1 ); - snprintf ( name, sizeof ( name ) - 1, "%s:midi_capture_%d", fClientControl.fName, midi_port_index + 1 ); - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, - static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", name ); + for (midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { + snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, midi_port_index + 1); + snprintf(name, sizeof (name), "%s:midi_capture_%d", fClientControl.fName, midi_port_index + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", name); return -1; } - port = fGraphManager->GetPort ( port_id ); + //port latency + port = fGraphManager->GetPort(port_index); range.min = range.max = fEngineControl->fBufferSize; port->SetLatencyRange(JackCaptureLatency, &range); - fMidiCapturePortList[midi_port_index] = port_id; - jack_log ( "JackNetDriver::AllocPorts() fMidiCapturePortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_id, port->GetLatency() ); + fMidiCapturePortList[midi_port_index] = port_index; + jack_log("JackNetDriver::AllocPorts() fMidiCapturePortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_index, port->GetLatency()); } - port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; - for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) - { - snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, midi_port_index + 1 ); - snprintf ( name, sizeof ( name ) - 1, "%s:midi_playback_%d", fClientControl.fName, midi_port_index + 1 ); - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, - static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", name ); + for (midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { + snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, midi_port_index + 1); + snprintf(name, sizeof(name), "%s:midi_playback_%d", fClientControl.fName, midi_port_index + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", name); return -1; } - port = fGraphManager->GetPort ( port_id ); + //port latency - switch ( fParams.fNetworkMode ) - { - case 'f' : - range.min = range.max = (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize; - break; - case 'n' : - range.min = range.max = (fEngineControl->fBufferSize + (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize); - break; - case 's' : - range.min = range.max = (2 * fEngineControl->fBufferSize + (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize); - break; - } + port = fGraphManager->GetPort(port_index); + range.min = range.max = (fParams.fNetworkLatency * fEngineControl->fBufferSize + (fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize); port->SetLatencyRange(JackPlaybackLatency, &range); - fMidiPlaybackPortList[midi_port_index] = port_id; - jack_log ( "JackNetDriver::AllocPorts() fMidiPlaybackPortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_id, port->GetLatency() ); + fMidiPlaybackPortList[midi_port_index] = port_index; + jack_log("JackNetDriver::AllocPorts() fMidiPlaybackPortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_index, port->GetLatency()); } return 0; @@ -360,33 +327,70 @@ 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) { + fEngine->PortUnRegister(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) { + fEngine->PortUnRegister(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) { + fEngine->PortUnRegister(fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index]); + fMidiPlaybackPortList[midi_port_index] = 0; + } + } return 0; } - JackMidiBuffer* JackNetDriver::GetMidiInputBuffer ( int port_index ) + void JackNetDriver::SaveConnections() + { + JackDriver::SaveConnections(); + const char** connections; + + for (int i = 0; i < fParams.fSendMidiChannels; ++i) { + if (fCapturePortList[i] && (connections = fGraphManager->GetConnections(fMidiCapturePortList[i])) != 0) { + for (int j = 0; connections[j]; j++) { + fConnections.push_back(make_pair(fGraphManager->GetPort(fMidiCapturePortList[i])->GetName(), connections[j])); + } + free(connections); + } + } + + for (int i = 0; i < fParams.fReturnMidiChannels; ++i) { + if (fPlaybackPortList[i] && (connections = fGraphManager->GetConnections(fMidiPlaybackPortList[i])) != 0) { + for (int j = 0; connections[j]; j++) { + fConnections.push_back(make_pair(connections[j], fGraphManager->GetPort(fMidiPlaybackPortList[i])->GetName())); + } + free(connections); + } + } + } + + JackMidiBuffer* JackNetDriver::GetMidiInputBuffer(int port_index) { - return static_cast ( fGraphManager->GetBuffer ( fMidiCapturePortList[port_index], fEngineControl->fBufferSize ) ); + return static_cast(fGraphManager->GetBuffer(fMidiCapturePortList[port_index], fEngineControl->fBufferSize)); } - JackMidiBuffer* JackNetDriver::GetMidiOutputBuffer ( int port_index ) + JackMidiBuffer* JackNetDriver::GetMidiOutputBuffer(int port_index) { - return static_cast ( fGraphManager->GetBuffer ( fMidiPlaybackPortList[port_index], fEngineControl->fBufferSize ) ); + return static_cast(fGraphManager->GetBuffer(fMidiPlaybackPortList[port_index], fEngineControl->fBufferSize)); } //transport--------------------------------------------------------------------------- @@ -398,35 +402,34 @@ namespace Jack // - conditional request : don't change anything too, the master will know if this slave is actually the timebase master int refnum; bool conditional; - if ( fSendTransportData.fTimebaseMaster == TIMEBASEMASTER ) - { - fEngineControl->fTransport.GetTimebaseMaster ( refnum, conditional ); - if ( refnum != -1 ) - fEngineControl->fTransport.ResetTimebase ( refnum ); - jack_info ( "The NetMaster is now the new timebase master." ); + if (fSendTransportData.fTimebaseMaster == TIMEBASEMASTER) { + fEngineControl->fTransport.GetTimebaseMaster(refnum, conditional); + if (refnum != -1) { + fEngineControl->fTransport.ResetTimebase(refnum); + } + jack_info("The NetMaster is now the new timebase master."); } //is there a transport state change to handle ? - if ( fSendTransportData.fNewState && ( fSendTransportData.fState != fEngineControl->fTransport.GetState() ) ) - { + if (fSendTransportData.fNewState &&(fSendTransportData.fState != fEngineControl->fTransport.GetState())) { - switch ( fSendTransportData.fState ) + switch (fSendTransportData.fState) { case JackTransportStopped : - fEngineControl->fTransport.SetCommand ( TransportCommandStop ); - jack_info ( "Master stops transport." ); + fEngineControl->fTransport.SetCommand(TransportCommandStop); + jack_info("Master stops transport."); break; case JackTransportStarting : - fEngineControl->fTransport.RequestNewPos ( &fSendTransportData.fPosition ); - fEngineControl->fTransport.SetCommand ( TransportCommandStart ); - jack_info ( "Master starts transport frame = %d", fSendTransportData.fPosition.frame); + fEngineControl->fTransport.RequestNewPos(&fSendTransportData.fPosition); + fEngineControl->fTransport.SetCommand(TransportCommandStart); + jack_info("Master starts transport frame = %d", fSendTransportData.fPosition.frame); break; case JackTransportRolling : - //fEngineControl->fTransport.SetCommand ( TransportCommandStart ); - fEngineControl->fTransport.SetState ( JackTransportRolling ); - jack_info ( "Master is rolling." ); + //fEngineControl->fTransport.SetCommand(TransportCommandStart); + fEngineControl->fTransport.SetState(JackTransportRolling); + jack_info("Master is rolling."); break; } } @@ -434,62 +437,67 @@ namespace Jack void JackNetDriver::EncodeTransportData() { - /* Desactivated //is there a timebase master change ? int refnum; bool conditional; - fEngineControl->fTransport.GetTimebaseMaster ( refnum, conditional ); - if ( refnum != fLastTimebaseMaster ) - { + fEngineControl->fTransport.GetTimebaseMaster(refnum, conditional); + if (refnum != fLastTimebaseMaster) { //timebase master has released its function - if ( refnum == -1 ) - { + if (refnum == -1) { fReturnTransportData.fTimebaseMaster = RELEASE_TIMEBASEMASTER; - jack_info ( "Sending a timebase master release request." ); - } - //there is a new timebase master - else - { - fReturnTransportData.fTimebaseMaster = ( conditional ) ? CONDITIONAL_TIMEBASEMASTER : TIMEBASEMASTER; - jack_info ( "Sending a %s timebase master request.", ( conditional ) ? "conditional" : "non-conditional" ); + jack_info("Sending a timebase master release request."); + } else { + //there is a new timebase master + fReturnTransportData.fTimebaseMaster = (conditional) ? CONDITIONAL_TIMEBASEMASTER : TIMEBASEMASTER; + jack_info("Sending a %s timebase master request.", (conditional) ? "conditional" : "non-conditional"); } fLastTimebaseMaster = refnum; - } - else + } else { fReturnTransportData.fTimebaseMaster = NO_CHANGE; - */ + } //update transport state and position - fReturnTransportData.fState = fEngineControl->fTransport.Query ( &fReturnTransportData.fPosition ); + fReturnTransportData.fState = fEngineControl->fTransport.Query(&fReturnTransportData.fPosition); //is it a new state (that the master need to know...) ? - fReturnTransportData.fNewState = (( fReturnTransportData.fState == JackTransportNetStarting) && - ( fReturnTransportData.fState != fLastTransportState ) && - ( fReturnTransportData.fState != fSendTransportData.fState ) ); - if ( fReturnTransportData.fNewState ) - jack_info ( "Sending '%s'.", GetTransportState ( fReturnTransportData.fState ) ); + fReturnTransportData.fNewState = ((fReturnTransportData.fState == JackTransportNetStarting) && + (fReturnTransportData.fState != fLastTransportState) && + (fReturnTransportData.fState != fSendTransportData.fState)); + if (fReturnTransportData.fNewState) { + jack_info("Sending '%s'.", GetTransportState(fReturnTransportData.fState)); + } fLastTransportState = fReturnTransportData.fState; } //driver processes-------------------------------------------------------------------- + int JackNetDriver::Read() { - uint midi_port_index; - uint audio_port_index; - //buffers - for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) - fNetMidiCaptureBuffer->SetBuffer ( midi_port_index, GetMidiInputBuffer ( midi_port_index ) ); - for ( audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++ ) - fNetAudioCaptureBuffer->SetBuffer ( audio_port_index, GetInputBuffer ( audio_port_index ) ); + for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { + fNetMidiCaptureBuffer->SetBuffer(midi_port_index, GetMidiInputBuffer(midi_port_index)); + } + + for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { + #ifdef OPTIMIZED_PROTOCOL + if (fGraphManager->GetConnectionsNum(fCapturePortList[audio_port_index]) > 0) { + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, GetInputBuffer(audio_port_index)); + } else { + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, NULL); + } + #else + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, GetInputBuffer(audio_port_index)); + #endif + } #ifdef JACK_MONITOR fNetTimeMon->New(); #endif //receive sync (launch the cycle) - if ( SyncRecv() == SOCKET_ERROR ) - return 0; + if (SyncRecv() == SOCKET_ERROR) { + return SOCKET_ERROR; + } #ifdef JACK_MONITOR // For timing @@ -501,17 +509,22 @@ namespace Jack DecodeSyncPacket(); #ifdef JACK_MONITOR - fNetTimeMon->Add ( ( ( float ) ( GetMicroSeconds() - fRcvSyncUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); + fNetTimeMon->Add((float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f); #endif //audio, midi or sync if driver is late - if ( DataRecv() == SOCKET_ERROR ) + int res = DataRecv(); + if (res == SOCKET_ERROR) { return SOCKET_ERROR; + } else if (res == NET_PACKET_ERROR) { + jack_time_t cur_time = GetMicroSeconds(); + NotifyXRun(cur_time, float(cur_time - fBeginDateUst)); // Better this value than nothing... + } //take the time at the beginning of the cycle JackDriver::CycleTakeBeginTime(); #ifdef JACK_MONITOR - fNetTimeMon->Add ( ( ( float ) ( GetMicroSeconds() - fRcvSyncUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); + fNetTimeMon->Add((float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f); #endif return 0; @@ -519,36 +532,51 @@ namespace Jack int JackNetDriver::Write() { - uint midi_port_index; - int audio_port_index; - //buffers - for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) - fNetMidiPlaybackBuffer->SetBuffer ( midi_port_index, GetMidiOutputBuffer ( midi_port_index ) ); - for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) - fNetAudioPlaybackBuffer->SetBuffer ( audio_port_index, GetOutputBuffer ( audio_port_index ) ); + for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { + fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, GetMidiOutputBuffer(midi_port_index)); + } + + for (int audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) { + #ifdef OPTIMIZED_PROTOCOL + // Port is connected on other side... + if (fNetAudioPlaybackBuffer->GetConnected(audio_port_index)) { + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[audio_port_index]) > 0) { + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, GetOutputBuffer(audio_port_index)); + } else { + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, NULL); + } + } else { + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, NULL); + } + #else + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, GetOutputBuffer(audio_port_index)); + #endif + } #ifdef JACK_MONITOR - fNetTimeMon->Add ( ( ( float ) ( GetMicroSeconds() - fRcvSyncUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); + fNetTimeMon->AddLast((float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f); #endif //sync EncodeSyncPacket(); //send sync - if ( SyncSend() == SOCKET_ERROR ) + if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; + } #ifdef JACK_MONITOR - fNetTimeMon->Add ( ( ( float ) ( GetMicroSeconds() - fRcvSyncUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); + fNetTimeMon->Add(((float)(GetMicroSeconds() - fRcvSyncUst) / (float)fEngineControl->fPeriodUsecs) * 100.f); #endif //send data - if ( DataSend() == SOCKET_ERROR ) + if (DataSend() == SOCKET_ERROR) { return SOCKET_ERROR; + } #ifdef JACK_MONITOR - fNetTimeMon->AddLast ( ( ( float ) ( GetMicroSeconds() - fRcvSyncUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); + fNetTimeMon->AddLast(((float)(GetMicroSeconds() - fRcvSyncUst) / (float)fEngineControl->fPeriodUsecs) * 100.f); #endif return 0; @@ -560,127 +588,87 @@ namespace Jack extern "C" { #endif - SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor () + SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor() { - jack_driver_desc_t* desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) ); - - strcpy ( desc->name, "net" ); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 - strcpy ( desc->desc, "netjack slave backend component" ); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 - - desc->nparams = 10; - desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); - - int i = 0; - strcpy ( desc->params[i].name, "multicast_ip" ); - desc->params[i].character = 'a'; - desc->params[i].type = JackDriverParamString; - strcpy ( desc->params[i].value.str, DEFAULT_MULTICAST_IP ); - strcpy ( desc->params[i].short_desc, "Multicast Address" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "udp_net_port" ); - desc->params[i].character = 'p'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = DEFAULT_PORT; - strcpy ( desc->params[i].short_desc, "UDP port" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "mtu" ); - desc->params[i].character = 'M'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = DEFAULT_MTU; - strcpy ( desc->params[i].short_desc, "MTU to the master" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "input_ports" ); - desc->params[i].character = 'C'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = 2; - strcpy ( desc->params[i].short_desc, "Number of audio input ports" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "output_ports" ); - desc->params[i].character = 'P'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = 2; - strcpy ( desc->params[i].short_desc, "Number of audio output ports" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "midi_in_ports" ); - desc->params[i].character = 'i'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = 0; - strcpy ( desc->params[i].short_desc, "Number of midi input ports" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "midi_out_ports" ); - desc->params[i].character = 'o'; - desc->params[i].type = JackDriverParamUInt; - 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 ); - - i++; - strcpy ( desc->params[i].name, "client_name" ); - desc->params[i].character = 'n'; - desc->params[i].type = JackDriverParamString; - strcpy ( desc->params[i].value.str, "'hostname'" ); - strcpy ( desc->params[i].short_desc, "Name of the jack client" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "transport_sync" ); - desc->params[i].character = 't'; - desc->params[i].type = JackDriverParamUInt; - desc->params[i].value.ui = 1U; - strcpy ( desc->params[i].short_desc, "Sync transport with master's" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "mode" ); - desc->params[i].character = 'm'; - desc->params[i].type = JackDriverParamString; - 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 ); + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("net", JackDriverMaster, "netjack slave backend component", &filler); + + strcpy(value.str, DEFAULT_MULTICAST_IP); + jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast Address", NULL); + + value.i = DEFAULT_PORT; + jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); + + value.i = DEFAULT_MTU; + jack_driver_descriptor_add_parameter(desc, &filler, "mtu", 'M', JackDriverParamInt, &value, NULL, "MTU to the master", NULL); + + value.i = -1; + jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", "Number of audio input ports. If -1, audio physical input from the master"); + jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", "Number of audio output ports. If -1, audio physical output from the master"); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "midi-in-ports", 'i', JackDriverParamInt, &value, NULL, "Number of midi input ports", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "midi-out-ports", 'o', JackDriverParamInt, &value, NULL, "Number of midi output ports", NULL); + +#if HAVE_CELT + value.i = -1; + jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamInt, &value, NULL, "Set CELT encoding and number of kBits per channel", NULL); +#endif + strcpy(value.str, "'hostname'"); + jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL); + + value.ui = 0U; + jack_driver_descriptor_add_parameter(desc, &filler, "transport-sync", 't', JackDriverParamUInt, &value, NULL, "Sync transport with master's", NULL); + + value.ui = 5U; + jack_driver_descriptor_add_parameter(desc, &filler, "latency", 'l', JackDriverParamUInt, &value, NULL, "Network latency", NULL); return desc; } - SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize ( Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params ) + SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { - char multicast_ip[16]; - strcpy ( multicast_ip, DEFAULT_MULTICAST_IP ); + char multicast_ip[32]; char net_name[JACK_CLIENT_NAME_SIZE + 1]; - int udp_port = DEFAULT_PORT; + int udp_port; int mtu = DEFAULT_MTU; - uint transport_sync = 1; - jack_nframes_t period_size = 128; + // Desactivated for now... + uint transport_sync = 0; + jack_nframes_t period_size = 1024; 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; + int celt_encoding = -1; bool monitor = false; - char network_mode = 's'; + int network_latency = 5; const JSList* node; const jack_driver_param_t* param; net_name[0] = 0; - for ( node = params; node; node = jack_slist_next ( node ) ) - { - param = ( const jack_driver_param_t* ) node->data; - switch ( param->character ) + // Possibly use env variable + const char* default_udp_port = getenv("JACK_NETJACK_PORT"); + udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT; + + const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); + if (default_multicast_ip) { + strcpy(multicast_ip, default_multicast_ip); + } else { + strcpy(multicast_ip, DEFAULT_MULTICAST_IP); + } + + for (node = params; node; node = jack_slist_next(node)) { + param = (const jack_driver_param_t*) node->data; + switch (param->character) { case 'a' : - strncpy ( multicast_ip, param->value.str, 15 ); + assert(strlen(param->value.str) < 32); + strcpy(multicast_ip, param->value.str); break; case 'p': udp_port = param->value.ui; @@ -700,46 +688,42 @@ namespace Jack case 'o': midi_output_ports = param->value.i; break; + #if HAVE_CELT + case 'c': + celt_encoding = param->value.i; + break; + #endif case 'n' : - strncpy ( net_name, param->value.str, JACK_CLIENT_NAME_SIZE ); + strncpy(net_name, param->value.str, JACK_CLIENT_NAME_SIZE); break; case 't' : transport_sync = param->value.ui; break; - case 'm' : - if ( strcmp ( param->value.str, "normal" ) == 0 ) - network_mode = 'n'; - else if ( strcmp ( param->value.str, "slow" ) == 0 ) - network_mode = 's'; - else if ( strcmp ( param->value.str, "fast" ) == 0 ) - network_mode = 'f'; - else - jack_error ( "Unknown network mode, using 'normal' mode." ); + case 'l' : + network_latency = param->value.ui; + if (network_latency > NETWORK_MAX_LATENCY) { + printf("Error : network latency is limited to %d\n", NETWORK_MAX_LATENCY); + return NULL; + } break; } } - try - { + try { - Jack::JackDriverClientInterface* driver = - new Jack::JackWaitThreadedDriver ( - new Jack::JackNetDriver ( "system", "net_pcm", engine, table, multicast_ip, udp_port, mtu, - midi_input_ports, midi_output_ports, net_name, transport_sync, network_mode ) ); - if ( driver->Open ( period_size, sample_rate, 1, 1, audio_capture_ports, audio_playback_ports, - monitor, "from_master_", "to_master_", 0, 0 ) == 0 ) - { + Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver( + new Jack::JackNetDriver("system", "net_pcm", engine, table, multicast_ip, udp_port, mtu, + midi_input_ports, midi_output_ports, + net_name, transport_sync, + network_latency, celt_encoding)); + if (driver->Open(period_size, sample_rate, 1, 1, audio_capture_ports, audio_playback_ports, monitor, "from_master_", "to_master_", 0, 0) == 0) { return driver; - } - else - { + } else { delete driver; return NULL; } - } - catch ( ... ) - { + } catch (...) { return NULL; } } diff --git a/common/JackNetDriver.h b/common/JackNetDriver.h index af137b43..48a733bf 100644 --- a/common/JackNetDriver.h +++ b/common/JackNetDriver.h @@ -1,6 +1,5 @@ /* -Copyright (C) 2001 Paul Davis -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,12 +20,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackNetDriver__ #define __JackNetDriver__ -#include "JackAudioDriver.h" +#include "JackTimedDriver.h" #include "JackNetInterface.h" -#ifdef JACK_MONITOR -#include "JackFrameTimer.h" -#endif +//#define JACK_MONITOR namespace Jack { @@ -34,17 +31,19 @@ namespace Jack \Brief This class describes the Net Backend */ - class JackNetDriver : public JackAudioDriver, public JackNetSlaveInterface + class JackNetDriver : public JackWaiterDriver, 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* fNetTimeMon; @@ -53,7 +52,7 @@ namespace Jack bool Initialize(); void FreeAll(); - + int AllocPorts(); int FreePorts(); @@ -61,18 +60,18 @@ namespace Jack void EncodeTransportData(); void DecodeTransportData(); - JackMidiBuffer* GetMidiInputBuffer ( int port_index ); - JackMidiBuffer* GetMidiOutputBuffer ( int port_index ); + JackMidiBuffer* GetMidiInputBuffer(int port_index); + JackMidiBuffer* GetMidiOutputBuffer(int port_index); + + void SaveConnections(); 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 ); - ~JackNetDriver(); - - 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 ); + + 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, int network_latency, int celt_encoding); + virtual ~JackNetDriver(); + int Close(); int Attach(); @@ -87,12 +86,12 @@ namespace Jack return true; } - int SetBufferSize ( jack_nframes_t buffer_size ) + int SetBufferSize(jack_nframes_t buffer_size) { return -1; } - int SetSampleRate ( jack_nframes_t sample_rate ) + int SetSampleRate(jack_nframes_t sample_rate) { return -1; } diff --git a/common/JackNetInterface.cpp b/common/JackNetInterface.cpp index b794e8bf..7f81d069 100644 --- a/common/JackNetInterface.cpp +++ b/common/JackNetInterface.cpp @@ -1,6 +1,5 @@ /* -Copyright (C) 2001 Paul Davis -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -19,14 +18,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackNetInterface.h" #include "JackException.h" -#include "JackPlatformPlug.h" #include using namespace std; -/* - 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) */ @@ -36,33 +34,24 @@ namespace Jack JackNetInterface::JackNetInterface() : fSocket() { - fTxBuffer = NULL; - fRxBuffer = NULL; - fNetAudioCaptureBuffer = NULL; - fNetAudioPlaybackBuffer = NULL; - fNetMidiCaptureBuffer = NULL; - fNetMidiPlaybackBuffer = NULL; - memset(&fSendTransportData, 0, sizeof(net_transport_data_t)); - memset(&fReturnTransportData, 0, sizeof(net_transport_data_t)); + Initialize(); } - JackNetInterface::JackNetInterface ( const char* multicast_ip, int port ) : fSocket ( multicast_ip, port ) + JackNetInterface::JackNetInterface(const char* multicast_ip, int port) : fSocket(multicast_ip, port) { strcpy(fMulticastIP, multicast_ip); - fTxBuffer = NULL; - fRxBuffer = NULL; - fNetAudioCaptureBuffer = NULL; - fNetAudioPlaybackBuffer = NULL; - fNetMidiCaptureBuffer = NULL; - fNetMidiPlaybackBuffer = NULL; - memset(&fSendTransportData, 0, sizeof(net_transport_data_t)); - memset(&fReturnTransportData, 0, sizeof(net_transport_data_t)); + Initialize(); } - JackNetInterface::JackNetInterface ( session_params_t& params, JackNetSocket& socket, const char* multicast_ip ) : fSocket ( socket ) + JackNetInterface::JackNetInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip) : fSocket(socket) { fParams = params; strcpy(fMulticastIP, multicast_ip); + Initialize(); + } + + void JackNetInterface::Initialize() + { fTxBuffer = NULL; fRxBuffer = NULL; fNetAudioCaptureBuffer = NULL; @@ -73,9 +62,21 @@ namespace Jack memset(&fReturnTransportData, 0, sizeof(net_transport_data_t)); } + void JackNetInterface::FreeNetworkBuffers() + { + delete fNetMidiCaptureBuffer; + delete fNetMidiPlaybackBuffer; + delete fNetAudioCaptureBuffer; + delete fNetAudioPlaybackBuffer; + fNetMidiCaptureBuffer = NULL; + fNetMidiPlaybackBuffer = NULL; + fNetAudioCaptureBuffer = NULL; + fNetAudioPlaybackBuffer = NULL; + } + JackNetInterface::~JackNetInterface() { - jack_log ( "JackNetInterface::~JackNetInterface" ); + jack_log("JackNetInterface::~JackNetInterface"); fSocket.Close(); delete[] fTxBuffer; @@ -86,178 +87,212 @@ namespace Jack delete fNetMidiPlaybackBuffer; } - void JackNetInterface::SetFramesPerPacket() - { - jack_log ( "JackNetInterface::SetFramesPerPacket" ); - - if (fParams.fSendAudioChannels == 0 && fParams.fReturnAudioChannels == 0) { - fParams.fFramesPerPacket = fParams.fPeriodSize; - } else { - jack_nframes_t period = ( int ) powf ( 2.f, ( int ) ( log (float ( fParams.fMtu - sizeof ( packet_header_t ) ) - / ( max ( fParams.fReturnAudioChannels, fParams.fSendAudioChannels ) * sizeof ( sample_t ) ) ) / log ( 2. ) ) ); - fParams.fFramesPerPacket = ( period > fParams.fPeriodSize ) ? fParams.fPeriodSize : period; - } - } - int JackNetInterface::SetNetBufferSize() { - jack_log ( "JackNetInterface::SetNetBufferSize" ); + // audio + float audio_size = (fNetAudioCaptureBuffer) + ? fNetAudioCaptureBuffer->GetCycleSize() + : (fNetAudioPlaybackBuffer) ? fNetAudioPlaybackBuffer->GetCycleSize() : 0; + jack_log("audio_size %f", audio_size); - float audio_size, midi_size; - int bufsize; - //audio - audio_size = fParams.fMtu * ( fParams.fPeriodSize / fParams.fFramesPerPacket ); - //midi - midi_size = fParams.fMtu * ( max ( fParams.fSendMidiChannels, fParams.fReturnMidiChannels ) * - fParams.fPeriodSize * sizeof ( sample_t ) / ( fParams.fMtu - sizeof ( packet_header_t ) ) ); - //bufsize = sync + audio + midi - bufsize = MAX_LATENCY * (fParams.fMtu + ( int ) audio_size + ( int ) midi_size); + // midi + float midi_size = (fNetMidiCaptureBuffer) + ? fNetMidiCaptureBuffer->GetCycleSize() + : (fNetMidiPlaybackBuffer) ? fNetMidiPlaybackBuffer->GetCycleSize() : 0; + jack_log("midi_size %f", midi_size); - //tx buffer - if ( fSocket.SetOption ( SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof ( bufsize ) ) == SOCKET_ERROR ) + // bufsize = sync + audio + midi + int bufsize = NETWORK_MAX_LATENCY * (fParams.fMtu + (int)audio_size + (int)midi_size); + jack_log("SetNetBufferSize bufsize = %d", bufsize); + + // tx buffer + if (fSocket.SetOption(SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) == SOCKET_ERROR) { return SOCKET_ERROR; + } - //rx buffer - if ( fSocket.SetOption ( SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof ( bufsize ) ) == SOCKET_ERROR ) + // rx buffer + if (fSocket.SetOption(SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) == SOCKET_ERROR) { return SOCKET_ERROR; + } return 0; } - int JackNetInterface::GetNMidiPckt() + bool JackNetInterface::SetParams() { - //even if there is no midi data, jack need an empty buffer to know there is no event to read - //99% of the cases : all data in one packet - if ( fTxHeader.fMidiDataSize <= ( fParams.fMtu - sizeof ( packet_header_t ) ) ) - return 1; - //else, get the number of needed packets (simply slice the biiig buffer) - int npckt = fTxHeader.fMidiDataSize / ( fParams.fMtu - sizeof ( packet_header_t ) ); - if ( fTxHeader.fMidiDataSize % ( fParams.fMtu - sizeof ( packet_header_t ) ) ) - return ++npckt; - return npckt; + // TX header init + strcpy(fTxHeader.fPacketType, "header"); + fTxHeader.fID = fParams.fID; + fTxHeader.fCycle = 0; + fTxHeader.fSubCycle = 0; + fTxHeader.fIsLastPckt = 0; + + // RX header init + strcpy(fRxHeader.fPacketType, "header"); + fRxHeader.fID = fParams.fID; + fRxHeader.fCycle = 0; + fRxHeader.fSubCycle = 0; + fRxHeader.fIsLastPckt = 0; + + // network buffers + fTxBuffer = new char[fParams.fMtu]; + fRxBuffer = new char[fParams.fMtu]; + assert(fTxBuffer); + assert(fRxBuffer); + + // net audio/midi buffers'addresses + fTxData = fTxBuffer + HEADER_SIZE; + fRxData = fRxBuffer + HEADER_SIZE; + + return true; } - bool JackNetInterface::IsNextPacket() + int JackNetInterface::MidiSend(NetMidiBuffer* buffer, int midi_channnels, int audio_channels) { - packet_header_t* rx_head = reinterpret_cast ( fRxBuffer ); - //ignore first cycle - if ( fRxHeader.fCycle <= 1 ) { - return true; + if (midi_channnels > 0) { + // set global header fields and get the number of midi packets + fTxHeader.fDataType = 'm'; + uint data_size = buffer->RenderFromJackPorts(); + fTxHeader.fNumPacket = buffer->GetNumPackets(data_size, PACKET_AVAILABLE_SIZE(&fParams)); + + for (uint subproc = 0; subproc < fTxHeader.fNumPacket; subproc++) { + fTxHeader.fSubCycle = subproc; + fTxHeader.fIsLastPckt = ((subproc == (fTxHeader.fNumPacket - 1)) && audio_channels == 0) ? 1 : 0; + fTxHeader.fPacketSize = HEADER_SIZE + buffer->RenderToNetwork(subproc, data_size); + memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); + if (Send(fTxHeader.fPacketSize, 0) == SOCKET_ERROR) { + return SOCKET_ERROR; + } + } } - //same PcktID (cycle), next SubPcktID (subcycle) - if ( ( fRxHeader.fSubCycle < ( fNSubProcess - 1 ) ) && ( rx_head->fCycle == fRxHeader.fCycle ) && ( rx_head->fSubCycle == ( fRxHeader.fSubCycle + 1 ) ) ) { - return true; + return 0; + } + + int JackNetInterface::AudioSend(NetAudioBuffer* buffer, int audio_channels) + { + // audio + if (audio_channels > 0) { + fTxHeader.fDataType = 'a'; + fTxHeader.fActivePorts = buffer->RenderFromJackPorts(); + fTxHeader.fNumPacket = buffer->GetNumPackets(fTxHeader.fActivePorts); + + for (uint subproc = 0; subproc < fTxHeader.fNumPacket; subproc++) { + fTxHeader.fSubCycle = subproc; + fTxHeader.fIsLastPckt = (subproc == (fTxHeader.fNumPacket - 1)) ? 1 : 0; + fTxHeader.fPacketSize = HEADER_SIZE + buffer->RenderToNetwork(subproc, fTxHeader.fActivePorts); + memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); + // PacketHeaderDisplay(&fTxHeader); + if (Send(fTxHeader.fPacketSize, 0) == SOCKET_ERROR) { + return SOCKET_ERROR; + } + } } - //next PcktID (cycle), SubPcktID reset to 0 (first subcyle) - if ( ( rx_head->fCycle == ( fRxHeader.fCycle + 1 ) ) && ( fRxHeader.fSubCycle == ( fNSubProcess - 1 ) ) && ( rx_head->fSubCycle == 0 ) ) { - return true; + return 0; + } + + int JackNetInterface::MidiRecv(packet_header_t* rx_head, NetMidiBuffer* buffer, uint& recvd_midi_pckt) + { + int rx_bytes = Recv(rx_head->fPacketSize, 0); + fRxHeader.fCycle = rx_head->fCycle; + fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; + buffer->RenderFromNetwork(rx_head->fSubCycle, rx_bytes - HEADER_SIZE); + // Last midi packet is received, so finish rendering... + if (++recvd_midi_pckt == rx_head->fNumPacket) { + buffer->RenderToJackPorts(); } - //else, packet(s) missing, return false - return false; + return rx_bytes; } - void JackNetInterface::SetParams() + int JackNetInterface::AudioRecv(packet_header_t* rx_head, NetAudioBuffer* buffer) { - //number of audio subcycles (packets) - fNSubProcess = fParams.fPeriodSize / fParams.fFramesPerPacket; + int rx_bytes = Recv(rx_head->fPacketSize, 0); + fRxHeader.fCycle = rx_head->fCycle; + fRxHeader.fSubCycle = rx_head->fSubCycle; + fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; + fRxHeader.fActivePorts = rx_head->fActivePorts; + rx_bytes = buffer->RenderFromNetwork(rx_head->fCycle, rx_head->fSubCycle, fRxHeader.fActivePorts); + // Last audio packet is received, so finish rendering... + if (fRxHeader.fIsLastPckt) { + buffer->RenderToJackPorts(); + } + return rx_bytes; + } - //payload size - fPayloadSize = fParams.fMtu - sizeof ( packet_header_t ); + int JackNetInterface::FinishRecv(NetAudioBuffer* buffer) + { + // TODO : finish midi and audio rendering ? + buffer->RenderToJackPorts(); + return NET_PACKET_ERROR; + } - //TX header init - strcpy ( fTxHeader.fPacketType, "header" ); - fTxHeader.fID = fParams.fID; - fTxHeader.fCycle = 0; - fTxHeader.fSubCycle = 0; - fTxHeader.fMidiDataSize = 0; - fTxHeader.fBitdepth = fParams.fBitdepth; - fTxHeader.fIsLastPckt = 0; + NetAudioBuffer* JackNetInterface::AudioBufferFactory(int nports, char* buffer) + { + switch (fParams.fSampleEncoder) { - //RX header init - strcpy ( fRxHeader.fPacketType, "header" ); - fRxHeader.fID = fParams.fID; - fRxHeader.fCycle = 0; - fRxHeader.fSubCycle = 0; - fRxHeader.fMidiDataSize = 0; - fRxHeader.fBitdepth = fParams.fBitdepth; - fRxHeader.fIsLastPckt = 0; + case JackFloatEncoder: + return new NetFloatAudioBuffer(&fParams, nports, buffer); - //network buffers - fTxBuffer = new char[fParams.fMtu]; - fRxBuffer = new char[fParams.fMtu]; - assert ( fTxBuffer ); - assert ( fRxBuffer ); + case JackIntEncoder: + return new NetIntAudioBuffer(&fParams, nports, buffer); - //net audio/midi buffers'addresses - fTxData = fTxBuffer + sizeof ( packet_header_t ); - fRxData = fRxBuffer + sizeof ( packet_header_t ); + #if HAVE_CELT + case JackCeltEncoder: + return new NetCeltAudioBuffer(&fParams, nports, buffer, fParams.fKBps); + #endif + } + return NULL; } // JackNetMasterInterface ************************************************************************************ bool JackNetMasterInterface::Init() { - jack_log ( "JackNetMasterInterface::Init, ID %u.", fParams.fID ); + jack_log("JackNetMasterInterface::Init, ID %u", fParams.fID); session_params_t host_params; uint attempt = 0; int rx_bytes = 0; - //socket - if ( fSocket.NewSocket() == SOCKET_ERROR ) { - jack_error ( "Can't create socket : %s", StrError ( NET_ERROR_CODE ) ); + // socket + if (fSocket.NewSocket() == SOCKET_ERROR) { + jack_error("Can't create socket : %s", StrError(NET_ERROR_CODE)); return false; } - //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 ) ); + // 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)); return false; } - //set the number of complete audio frames we can put in a packet - SetFramesPerPacket(); - - //send 'SLAVE_SETUP' until 'START_MASTER' received - jack_info ( "Sending parameters to %s ...", fParams.fSlaveNetName ); + // send 'SLAVE_SETUP' until 'START_MASTER' received + jack_info("Sending parameters to %s...", fParams.fSlaveNetName); do { session_params_t net_params; - memset(&net_params, 0, sizeof ( session_params_t )); - SetPacketType ( &fParams, SLAVE_SETUP ); + 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." ); + + if (fSocket.Send(&net_params, sizeof(session_params_t), 0) == SOCKET_ERROR) { + jack_error("Error in send : %s", 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 ) ); - if ( attempt == SLAVE_SETUP_RETRY ) { - jack_error ( "Slave doesn't respond, exiting." ); - return false; - } - //set the new timeout for the socket - if ( SetRxTimeout() == SOCKET_ERROR ) { - jack_error ( "Can't set rx timeout : %s", StrError ( NET_ERROR_CODE ) ); - return false; + SessionParamsNToH(&net_params, &host_params); } - - //set the new rx buffer size - if ( SetNetBufferSize() == SOCKET_ERROR ) { - jack_error ( "Can't set net buffer sizes : %s", StrError ( NET_ERROR_CODE ) ); + while ((GetPacketType(&host_params) != START_MASTER) && (++attempt < SLAVE_SETUP_RETRY)); + if (attempt == SLAVE_SETUP_RETRY) { + jack_error("Slave doesn't respond, exiting"); return false; } @@ -266,334 +301,307 @@ namespace Jack int JackNetMasterInterface::SetRxTimeout() { - jack_log ( "JackNetMasterInterface::SetRxTimeout" ); - - float time = 0; - //slow or normal mode, short timeout on recv (2 audio subcycles) - if ( ( fParams.fNetworkMode == 's' ) || ( fParams.fNetworkMode == 'n' ) ) - time = 2000000.f * ( static_cast ( fParams.fFramesPerPacket ) / static_cast ( fParams.fSampleRate ) ); - //fast mode, wait for 75% of the entire cycle duration - else if ( fParams.fNetworkMode == 'f' ) - time = 750000.f * ( static_cast ( fParams.fPeriodSize ) / static_cast ( fParams.fSampleRate ) ); - return fSocket.SetTimeOut ( static_cast ( time ) ); + jack_log("JackNetMasterInterface::SetRxTimeout"); + float time = 3 * 1000000.f * (static_cast(fParams.fPeriodSize) / static_cast(fParams.fSampleRate)); + return fSocket.SetTimeOut(static_cast(time)); } - void JackNetMasterInterface::SetParams() + bool JackNetMasterInterface::SetParams() { - jack_log ( "JackNetMasterInterface::SetParams" ); + jack_log("JackNetMasterInterface::SetParams audio in = %d audio out = %d MIDI in = %d MIDI out = %d", + fParams.fSendAudioChannels, fParams.fReturnAudioChannels, + fParams.fSendMidiChannels, fParams.fReturnMidiChannels); JackNetInterface::SetParams(); fTxHeader.fDataStream = 's'; fRxHeader.fDataStream = 'r'; - //midi net buffers - fNetMidiCaptureBuffer = new NetMidiBuffer ( &fParams, fParams.fSendMidiChannels, fTxData ); - fNetMidiPlaybackBuffer = new NetMidiBuffer ( &fParams, fParams.fReturnMidiChannels, fRxData ); - assert ( fNetMidiCaptureBuffer ); - assert ( fNetMidiPlaybackBuffer ); + fMaxCycleOffset = fParams.fNetworkLatency; + + // midi net buffers + if (fParams.fSendMidiChannels > 0) { + fNetMidiCaptureBuffer = new NetMidiBuffer(&fParams, fParams.fSendMidiChannels, fTxData); + } + + if (fParams.fReturnMidiChannels > 0) { + fNetMidiPlaybackBuffer = new NetMidiBuffer(&fParams, fParams.fReturnMidiChannels, fRxData); + } + + try { + + // audio net buffers + if (fParams.fSendAudioChannels > 0) { + fNetAudioCaptureBuffer = AudioBufferFactory(fParams.fSendAudioChannels, fTxData); + assert(fNetAudioCaptureBuffer); + } + + if (fParams.fReturnAudioChannels > 0) { + fNetAudioPlaybackBuffer = AudioBufferFactory(fParams.fReturnAudioChannels, fRxData); + assert(fNetAudioPlaybackBuffer); + } + + } catch (exception&) { + jack_error("NetAudioBuffer allocation error..."); + return false; + } + + // set the new timeout for the socket + /* + if (SetRxTimeout() == SOCKET_ERROR) { + jack_error("Can't set rx timeout : %s", StrError(NET_ERROR_CODE)); + goto error; + } + */ + + // set the new rx buffer size + if (SetNetBufferSize() == SOCKET_ERROR) { + jack_error("Can't set net buffer sizes : %s", StrError(NET_ERROR_CODE)); + goto error; + } - //audio net buffers - fNetAudioCaptureBuffer = new NetAudioBuffer ( &fParams, fParams.fSendAudioChannels, fTxData ); - fNetAudioPlaybackBuffer = new NetAudioBuffer ( &fParams, fParams.fReturnAudioChannels, fRxData ); - assert ( fNetAudioCaptureBuffer ); - assert ( fNetAudioPlaybackBuffer ); + return true; - //audio netbuffer length - fAudioTxLen = sizeof ( packet_header_t ) + fNetAudioCaptureBuffer->GetSize(); - fAudioRxLen = sizeof ( packet_header_t ) + fNetAudioPlaybackBuffer->GetSize(); + error: + FreeNetworkBuffers(); + return false; } void JackNetMasterInterface::Exit() { - jack_log ( "JackNetMasterInterface::Exit, ID %u", fParams.fID ); + jack_log("JackNetMasterInterface::Exit, ID %u", fParams.fID); - //stop process + // stop process fRunning = false; - //send a 'multicast euthanasia request' - new socket is required on macosx - jack_info ( "Exiting '%s'", fParams.fName ); - SetPacketType ( &fParams, KILL_MASTER ); - JackNetSocket mcast_socket ( fMulticastIP, fSocket.GetPort() ); - + // send a 'multicast euthanasia request' - new socket is required on macosx + 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 )); + memset(&net_params, 0, sizeof(session_params_t)); SessionParamsHToN(&fParams, &net_params); - if ( mcast_socket.NewSocket() == SOCKET_ERROR ) - 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 ) ); - + if (mcast_socket.NewSocket() == SOCKET_ERROR) { + 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(); } - int JackNetMasterInterface::Recv ( size_t size, int flags ) + void JackNetMasterInterface::FatalRecvError() + { + // fatal connection issue, exit + jack_error("Recv connection lost error = %s, '%s' exiting", StrError(NET_ERROR_CODE), fParams.fName); + // 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(); + } + + void JackNetMasterInterface::FatalSendError() + { + // fatal connection issue, exit + jack_error("Send connection lost error = %s, '%s' exiting", StrError(NET_ERROR_CODE), fParams.fName); + // 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(); + } + + int JackNetMasterInterface::Recv(size_t size, int flags) { int rx_bytes; - if ( ( ( rx_bytes = fSocket.Recv ( fRxBuffer, size, flags ) ) == SOCKET_ERROR ) && fRunning ) - { + + if (((rx_bytes = fSocket.Recv(fRxBuffer, size, flags)) == SOCKET_ERROR) && fRunning) { + + /* net_error_t error = fSocket.GetError(); - //no data isn't really a network error, so just return 0 avalaible read bytes - if ( error == NET_NO_DATA ) + // no data isn't really a network error, so just return 0 available read bytes + if (error == NET_NO_DATA) { return 0; - else if ( error == NET_CONN_ERROR ) - { - //fatal connection issue, exit - 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 if (error == NET_CONN_ERROR) { + FatalRecvError(); + } else { + jack_error("Error in master receive : %s", StrError(NET_ERROR_CODE)); } - else - jack_error ( "Error in master receive : %s", StrError ( NET_ERROR_CODE ) ); + */ + + FatalRecvError(); } - + packet_header_t* header = reinterpret_cast(fRxBuffer); PacketHeaderNToH(header, header); return rx_bytes; } - - int JackNetMasterInterface::Send ( size_t size, int flags ) + + int JackNetMasterInterface::Send(size_t size, int flags) { int tx_bytes; packet_header_t* header = reinterpret_cast(fTxBuffer); PacketHeaderHToN(header, header); - - if ( ( ( tx_bytes = fSocket.Send ( fTxBuffer, size, flags ) ) == SOCKET_ERROR ) && fRunning ) - { + + 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(); + if (error == NET_CONN_ERROR) { + FatalSendError(); + } else { + jack_error("Error in master send : %s", StrError(NET_ERROR_CODE)); } - else - jack_error ( "Error in master send : %s", StrError ( NET_ERROR_CODE ) ); + */ + FatalSendError(); } return tx_bytes; } - + bool JackNetMasterInterface::IsSynched() { - if (fParams.fNetworkMode == 's') { - return (fCycleOffset < 3); - } else { - return true; - } + return (fCurrentCycleOffset <= fMaxCycleOffset); } - + int JackNetMasterInterface::SyncSend() { fTxHeader.fCycle++; fTxHeader.fSubCycle = 0; fTxHeader.fDataType = 's'; - fTxHeader.fIsLastPckt = ( fParams.fSendMidiChannels == 0 && fParams.fSendAudioChannels == 0) ? 1 : 0; + fTxHeader.fIsLastPckt = (fParams.fSendMidiChannels == 0 && fParams.fSendAudioChannels == 0) ? 1 : 0; fTxHeader.fPacketSize = fParams.fMtu; - memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); - return Send ( fTxHeader.fPacketSize, 0 ); + + memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); + // PacketHeaderDisplay(&fTxHeader); + return Send(fTxHeader.fPacketSize, 0); } int JackNetMasterInterface::DataSend() { - uint subproc; - //midi - if ( fParams.fSendMidiChannels > 0) - { - //set global header fields and get the number of midi packets - fTxHeader.fDataType = 'm'; - fTxHeader.fMidiDataSize = fNetMidiCaptureBuffer->RenderFromJackPorts(); - fTxHeader.fNMidiPckt = GetNMidiPckt(); - for ( subproc = 0; subproc < fTxHeader.fNMidiPckt; subproc++ ) - { - fTxHeader.fSubCycle = subproc; - fTxHeader.fIsLastPckt = ( ( subproc == ( fTxHeader.fNMidiPckt - 1 ) ) && (fParams.fSendAudioChannels == 0)) ? 1 : 0; - fTxHeader.fPacketSize = sizeof ( packet_header_t ) + fNetMidiCaptureBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize ); - memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); - if ( Send ( fTxHeader.fPacketSize, 0 ) == SOCKET_ERROR ) - return SOCKET_ERROR; - } - } - - //audio - if ( fParams.fSendAudioChannels > 0) - { - fTxHeader.fDataType = 'a'; - fTxHeader.fMidiDataSize = 0; - fTxHeader.fNMidiPckt = 0; - for ( subproc = 0; subproc < fNSubProcess; subproc++ ) - { - fTxHeader.fSubCycle = subproc; - fTxHeader.fIsLastPckt = ( subproc == ( fNSubProcess - 1 ) ) ? 1 : 0; - fTxHeader.fPacketSize = fAudioTxLen; - memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); - fNetAudioCaptureBuffer->RenderFromJackPorts ( subproc ); - if ( Send ( fTxHeader.fPacketSize, 0 ) == SOCKET_ERROR ) - return SOCKET_ERROR; - } + if (MidiSend(fNetMidiCaptureBuffer, fParams.fSendMidiChannels, fParams.fSendAudioChannels) == SOCKET_ERROR) { + return SOCKET_ERROR; } - - return 0; + return AudioSend(fNetAudioCaptureBuffer, fParams.fSendAudioChannels); } int JackNetMasterInterface::SyncRecv() { - packet_header_t* rx_head = reinterpret_cast ( fRxBuffer ); - int rx_bytes = Recv ( fParams.fMtu, MSG_PEEK ); - - if ( ( rx_bytes == 0 ) || ( rx_bytes == SOCKET_ERROR ) ) - return rx_bytes; + int rx_bytes = 0; + packet_header_t* rx_head = reinterpret_cast(fRxBuffer); - fCycleOffset = fTxHeader.fCycle - rx_head->fCycle; - - switch ( fParams.fNetworkMode ) - { - case 's' : - //slow mode : allow to use full bandwidth and heavy process on the slave - // - extra latency is set to two cycles, one cycle for send/receive operations + one cycle for heavy process on the slave - // - if the network is two fast, just wait the next cycle, this mode allows a shorter cycle duration for the master - // - this mode will skip the two first cycles, thus it lets time for data to be processed and queued on the socket rx buffer - //the slow mode is the safest mode because it wait twice the bandwidth relative time (send/return + process) - if (fCycleOffset < 2) - return 0; - else - rx_bytes = Recv ( rx_head->fPacketSize, 0 ); - - if (fCycleOffset > 2) { - jack_info("Warning : '%s' runs in slow network mode, but data received too late (%d cycle(s) offset)", fParams.fName, 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 - // - if the network is too fast, just wait the next cycle, the benefit here is the master's cycle is shorter - // - indeed, data is supposed to be on the network rx buffer, so we don't have to wait for it - if (fCycleOffset < 1) - return 0; - else - rx_bytes = Recv ( rx_head->fPacketSize, 0 ); - - if (fCycleOffset != 1) - 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 - // - here, receive data, we can't keep it queued on the rx buffer, - // - but if there is a cycle offset, tell the user, that means we're not in fast mode anymore, network is too slow - rx_bytes = Recv ( rx_head->fPacketSize, 0 ); - - if (fCycleOffset != 0) - jack_info("'%s' can't run in fast network mode, data received too late (%d cycle(s) offset)", fParams.fName, fCycleOffset); - break; + /* + int rx_bytes = Recv(fParams.fMtu, MSG_PEEK); + + if ((rx_bytes == 0) || (rx_bytes == SOCKET_ERROR)) { + // 0 bytes considered an error (lost connection) + return SOCKET_ERROR; } - fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; - return rx_bytes; + fCurrentCycleOffset = fTxHeader.fCycle - rx_head->fCycle; + */ + + // receive sync (launch the cycle) + do { + rx_bytes = Recv(fParams.fMtu, MSG_PEEK); + // connection issue, send will detect it, so don't skip the cycle (return 0) + if (rx_bytes == SOCKET_ERROR) { + return SOCKET_ERROR; + } + } + while ((strcmp(rx_head->fPacketType, "header") != 0) && (rx_head->fDataType != 's')); + + fCurrentCycleOffset = fTxHeader.fCycle - rx_head->fCycle; + + if (fCurrentCycleOffset < fMaxCycleOffset) { + jack_info("Synching with latency = %d", fCurrentCycleOffset); + return 0; + } else { + rx_bytes = Recv(rx_head->fPacketSize, 0); + fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; + return rx_bytes; + } } - + int JackNetMasterInterface::DataRecv() { int rx_bytes = 0; - uint jumpcnt = 0; uint recvd_midi_pckt = 0; - uint recvd_audio_pckt = 0; - packet_header_t* rx_head = reinterpret_cast ( fRxBuffer ); - - while ( !fRxHeader.fIsLastPckt ) - { - //how much data is queued on the rx buffer ? - rx_bytes = Recv ( fParams.fMtu, MSG_PEEK ); - - if ( rx_bytes == SOCKET_ERROR ) + packet_header_t* rx_head = reinterpret_cast(fRxBuffer); + + while (!fRxHeader.fIsLastPckt) { + // how much data is queued on the rx buffer ? + rx_bytes = Recv(fParams.fMtu, MSG_PEEK); + + // error here, problem with recv, just skip the cycle (return -1) + if (rx_bytes == SOCKET_ERROR) { return rx_bytes; - //if no data - if ( ( rx_bytes == 0 ) && ( ++jumpcnt == fNSubProcess ) ) - { - jack_error ( "No data from %s...", fParams.fName ); - jumpcnt = 0; } - //else if data is valid, - if ( rx_bytes && ( rx_head->fDataStream == 'r' ) && ( rx_head->fID == fParams.fID ) ) - { - //read data - switch ( rx_head->fDataType ) - { - case 'm': //midi - rx_bytes = Recv ( rx_head->fPacketSize, 0 ); - fRxHeader.fCycle = rx_head->fCycle; - fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; - fNetMidiPlaybackBuffer->RenderFromNetwork ( rx_head->fSubCycle, rx_bytes - sizeof ( packet_header_t ) ); - if ( ++recvd_midi_pckt == rx_head->fNMidiPckt ) - fNetMidiPlaybackBuffer->RenderToJackPorts(); - jumpcnt = 0; + + if (rx_bytes && (rx_head->fDataStream == 'r') && (rx_head->fID == fParams.fID)) { + // read data + switch (rx_head->fDataType) { + + case 'm': // midi + rx_bytes = MidiRecv(rx_head, fNetMidiPlaybackBuffer, recvd_midi_pckt); break; - case 'a': //audio - rx_bytes = Recv ( rx_head->fPacketSize, 0 ); - // SL: 25/01/09 - // if ( !IsNextPacket() ) - // jack_error ( "Packet(s) missing from '%s'...", fParams.fName ); - if (recvd_audio_pckt++ != rx_head->fSubCycle) { - jack_error("Packet(s) missing from '%s'...", fParams.fSlaveNetName); - } - fRxHeader.fCycle = rx_head->fCycle; - fRxHeader.fSubCycle = rx_head->fSubCycle; - fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; - fNetAudioPlaybackBuffer->RenderToJackPorts ( rx_head->fSubCycle ); - jumpcnt = 0; + case 'a': // audio + rx_bytes = AudioRecv(rx_head, fNetAudioPlaybackBuffer); break; - case 's': //sync - /* SL: 25/01/09 - if ( rx_head->fCycle == fTxHeader.fCycle ) - return 0; - */ + case 's': // sync jack_info("NetMaster : overloaded, skipping receive from '%s'", fParams.fName); - return 0; + return FinishRecv(fNetAudioPlaybackBuffer); } } } + return rx_bytes; } - + void JackNetMasterInterface::EncodeSyncPacket() { - //this method contains every step of sync packet informations coding - //first of all, reset sync packet - memset ( fTxData, 0, fPayloadSize ); + // This method contains every step of sync packet informations coding + // first of all, clear sync packet + memset(fTxData, 0, PACKET_AVAILABLE_SIZE(&fParams)); - //then, first step : transport + // then, first step : transport if (fParams.fTransportSync) { EncodeTransportData(); - TransportDataHToN( &fSendTransportData, &fSendTransportData); - //copy to TxBuffer - memcpy ( fTxData, &fSendTransportData, sizeof ( net_transport_data_t ) ); + TransportDataHToN(&fSendTransportData, &fSendTransportData); + // copy to TxBuffer + memcpy(fTxData, &fSendTransportData, sizeof(net_transport_data_t)); } - //then others (freewheel etc.) - //... + // then others (freewheel etc.) + // ... + + // Transport not used for now... + + // Write active ports list + fTxHeader.fActivePorts = (fNetAudioPlaybackBuffer) ? fNetAudioPlaybackBuffer->ActivePortsToNetwork(fTxData) : 0; } void JackNetMasterInterface::DecodeSyncPacket() { - //this method contains every step of sync packet informations decoding process - //first : transport + // This method contains every step of sync packet informations decoding process + // first : transport if (fParams.fTransportSync) { - //copy received transport data to transport data structure - memcpy ( &fReturnTransportData, fRxData, sizeof ( net_transport_data_t ) ); - TransportDataNToH( &fReturnTransportData, &fReturnTransportData); + // copy received transport data to transport data structure + memcpy(&fReturnTransportData, fRxData, sizeof(net_transport_data_t)); + TransportDataNToH(&fReturnTransportData, &fReturnTransportData); DecodeTransportData(); } - //then others - //... + // then others + // ... + + // Transport not used for now... + packet_header_t* rx_head = reinterpret_cast(fRxBuffer); + + // Read active ports list + if (fNetAudioCaptureBuffer) { + fNetAudioCaptureBuffer->ActivePortsFromNetwork(fRxData, rx_head->fActivePorts); + } } // JackNetSlaveInterface ************************************************************************************************ @@ -602,229 +610,269 @@ namespace Jack bool JackNetSlaveInterface::Init() { - jack_log ( "JackNetSlaveInterface::Init()" ); + jack_log("JackNetSlaveInterface::Init()"); - //set the parameters to send - strcpy ( fParams.fPacketType, "params" ); + // set the parameters to send + strcpy(fParams.fPacketType, "params"); fParams.fProtocolVersion = SLAVE_PROTOCOL; - SetPacketType ( &fParams, SLAVE_AVAILABLE ); + SetPacketType(&fParams, SLAVE_AVAILABLE); - //init loop : get a master and start, do it until connection is ok + // init loop : get a master and start, do it until connection is ok net_status_t status; - do - { - //first, get a master, do it until a valid connection is running - do - { + do { + // first, get a master, do it until a valid connection is running + do { status = SendAvailableToMaster(); - if ( status == NET_SOCKET_ERROR ) + if (status == NET_SOCKET_ERROR) { return false; + } } - while ( status != NET_CONNECTED ); + while (status != NET_CONNECTED); - //then tell the master we are ready - jack_info ( "Initializing connection with %s...", fParams.fMasterNetName ); + // then tell the master we are ready + jack_info("Initializing connection with %s...", fParams.fMasterNetName); status = SendStartToMaster(); - if ( status == NET_ERROR ) + if (status == NET_ERROR) { return false; + } } - while ( status != NET_ROLLING ); + while (status != NET_ROLLING); return true; } - + // Separate the connection protocol into two separated step - - bool JackNetSlaveInterface::InitConnection() + bool JackNetSlaveInterface::InitConnection(int time_out_sec) { - jack_log ( "JackNetSlaveInterface::InitConnection()" ); + jack_log("JackNetSlaveInterface::InitConnection()"); + uint try_count = (time_out_sec > 0) ? ((1000000 * time_out_sec) / SLAVE_INIT_TIMEOUT) : LONG_MAX; - //set the parameters to send - strcpy (fParams.fPacketType, "params"); + // set the parameters to send + strcpy(fParams.fPacketType, "params"); fParams.fProtocolVersion = SLAVE_PROTOCOL; - SetPacketType (&fParams, SLAVE_AVAILABLE); + SetPacketType(&fParams, SLAVE_AVAILABLE); net_status_t status; - do - { - //get a master - status = SendAvailableToMaster(); - if (status == NET_SOCKET_ERROR) + do { + // get a master + status = SendAvailableToMaster(try_count); + if (status == NET_SOCKET_ERROR) { return false; + } } - while (status != NET_CONNECTED); - - return true; + while (status != NET_CONNECTED && --try_count > 0); + + return (try_count != 0); } - + bool JackNetSlaveInterface::InitRendering() { jack_log("JackNetSlaveInterface::InitRendering()"); net_status_t status; - do - { - //then tell the master we are ready + do { + // then tell the master we are ready jack_info("Initializing connection with %s...", fParams.fMasterNetName); status = SendStartToMaster(); if (status == NET_ERROR) return false; } - while (status != NET_ROLLING); - + while (status != NET_ROLLING); + return true; } - net_status_t JackNetSlaveInterface::SendAvailableToMaster() + net_status_t JackNetSlaveInterface::SendAvailableToMaster(long try_count) { - jack_log ( "JackNetSlaveInterface::SendAvailableToMaster()" ); - //utility + jack_log("JackNetSlaveInterface::SendAvailableToMaster()"); + // utility session_params_t host_params; int rx_bytes = 0; - //socket - if ( fSocket.NewSocket() == SOCKET_ERROR ) { - jack_error ( "Fatal error : network unreachable - %s", StrError ( NET_ERROR_CODE ) ); + // socket + if (fSocket.NewSocket() == SOCKET_ERROR) { + jack_error("Fatal error : network unreachable - %s", StrError(NET_ERROR_CODE)); return NET_SOCKET_ERROR; } - //bind the socket - if ( fSocket.Bind() == SOCKET_ERROR ) { - jack_error ( "Can't bind the socket : %s", StrError ( NET_ERROR_CODE ) ); - return NET_SOCKET_ERROR; + if (fSocket.IsLocal(fMulticastIP)) { + jack_info("Local IP is used..."); + } else { + // bind the socket + if (fSocket.Bind() == SOCKET_ERROR) { + jack_error("Can't bind the socket : %s", StrError(NET_ERROR_CODE)); + return NET_SOCKET_ERROR; + } } - //timeout on receive - if ( fSocket.SetTimeOut ( SLAVE_INIT_TIMEOUT ) == SOCKET_ERROR ) - jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) ); + // timeout on receive + if (fSocket.SetTimeOut(SLAVE_INIT_TIMEOUT) == SOCKET_ERROR) + jack_error("Can't set timeout : %s", StrError(NET_ERROR_CODE)); - //disable local loop - if ( fSocket.SetLocalLoop() == SOCKET_ERROR ) - jack_error ( "Can't disable multicast loop : %s", StrError ( NET_ERROR_CODE ) ); + // disable local loop + if (fSocket.SetLocalLoop() == SOCKET_ERROR) + jack_error("Can't disable multicast loop : %s", StrError(NET_ERROR_CODE)); - //send 'AVAILABLE' until 'SLAVE_SETUP' received - jack_info ( "Waiting for a master..." ); - do - { - //send 'available' + // send 'AVAILABLE' until 'SLAVE_SETUP' received + jack_info("Waiting for a master..."); + do { + // send 'available' session_params_t net_params; - memset(&net_params, 0, sizeof ( session_params_t )); + memset(&net_params, 0, sizeof(session_params_t)); 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 ); + 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); SessionParamsNToH(&net_params, &host_params); - if ( ( rx_bytes == SOCKET_ERROR ) && ( fSocket.GetError() != NET_NO_DATA ) ) - { - jack_error ( "Can't receive : %s", StrError ( NET_ERROR_CODE ) ); + if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { + jack_error("Can't receive : %s", StrError(NET_ERROR_CODE)); return NET_RECV_ERROR; } } - while ( strcmp ( host_params.fPacketType, fParams.fPacketType ) && ( GetPacketType ( &host_params ) != SLAVE_SETUP ) ); + while (strcmp(host_params.fPacketType, fParams.fPacketType) && (GetPacketType(&host_params) != SLAVE_SETUP) && (--try_count > 0)); - //everything is OK, copy parameters - fParams = host_params; - - //set the new buffer sizes - if ( SetNetBufferSize() == SOCKET_ERROR ) { - jack_error ( "Can't set net buffer sizes : %s", StrError ( NET_ERROR_CODE ) ); - return NET_SOCKET_ERROR; + // Time out failure.. + if (try_count == 0) { + jack_error("Time out error in connect"); + return NET_CONNECT_ERROR; } - //connect the socket - if ( fSocket.Connect() == SOCKET_ERROR ) { - jack_error ( "Error in connect : %s", StrError ( NET_ERROR_CODE ) ); + // everything is OK, copy parameters + fParams = host_params; + + // connect the socket + if (fSocket.Connect() == SOCKET_ERROR) { + jack_error("Error in connect : %s", StrError(NET_ERROR_CODE)); return NET_CONNECT_ERROR; } - return NET_CONNECTED; } net_status_t JackNetSlaveInterface::SendStartToMaster() { - jack_log ( "JackNetSlaveInterface::SendStartToMaster" ); + jack_log("JackNetSlaveInterface::SendStartToMaster"); - //tell the master to start + // tell the master to start session_params_t net_params; - memset(&net_params, 0, sizeof ( session_params_t )); - SetPacketType ( &fParams, START_MASTER ); + memset(&net_params, 0, sizeof(session_params_t)); + SetPacketType(&fParams, START_MASTER); SessionParamsHToN(&fParams, &net_params); - if ( fSocket.Send ( &net_params, sizeof ( session_params_t ), 0 ) == SOCKET_ERROR ) - { - jack_error ( "Error in send : %s", StrError ( NET_ERROR_CODE ) ); - return ( fSocket.GetError() == NET_CONN_ERROR ) ? NET_ERROR : NET_SEND_ERROR; + if (fSocket.Send(&net_params, sizeof(session_params_t), 0) == SOCKET_ERROR) { + jack_error("Error in send : %s", StrError(NET_ERROR_CODE)); + return (fSocket.GetError() == NET_CONN_ERROR) ? NET_ERROR : NET_SEND_ERROR; } return NET_ROLLING; } - void JackNetSlaveInterface::SetParams() + bool JackNetSlaveInterface::SetParams() { - jack_log ( "JackNetSlaveInterface::SetParams" ); + jack_log("JackNetSlaveInterface::SetParams audio in = %d audio out = %d MIDI in = %d MIDI out = %d", + fParams.fSendAudioChannels, fParams.fReturnAudioChannels, + fParams.fSendMidiChannels, fParams.fReturnMidiChannels); JackNetInterface::SetParams(); fTxHeader.fDataStream = 'r'; fRxHeader.fDataStream = 's'; - //midi net buffers - fNetMidiCaptureBuffer = new NetMidiBuffer ( &fParams, fParams.fSendMidiChannels, fRxData ); - fNetMidiPlaybackBuffer = new NetMidiBuffer ( &fParams, fParams.fReturnMidiChannels, fTxData ); + // midi net buffers + if (fParams.fSendMidiChannels > 0) { + fNetMidiCaptureBuffer = new NetMidiBuffer(&fParams, fParams.fSendMidiChannels, fRxData); + } + + if (fParams.fReturnMidiChannels > 0) { + fNetMidiPlaybackBuffer = new NetMidiBuffer(&fParams, fParams.fReturnMidiChannels, fTxData); + } + + try { + + // audio net buffers + if (fParams.fSendAudioChannels > 0) { + fNetAudioCaptureBuffer = AudioBufferFactory(fParams.fSendAudioChannels, fRxData); + assert(fNetAudioCaptureBuffer); + } + + if (fParams.fReturnAudioChannels > 0) { + fNetAudioPlaybackBuffer = AudioBufferFactory(fParams.fReturnAudioChannels, fTxData); + assert(fNetAudioPlaybackBuffer); + } + + } catch (exception&) { + jack_error("NetAudioBuffer allocation error..."); + return false; + } + + // set the new buffer sizes + if (SetNetBufferSize() == SOCKET_ERROR) { + jack_error("Can't set net buffer sizes : %s", StrError(NET_ERROR_CODE)); + goto error; + } - //audio net buffers - fNetAudioCaptureBuffer = new NetAudioBuffer ( &fParams, fParams.fSendAudioChannels, fRxData ); - fNetAudioPlaybackBuffer = new NetAudioBuffer ( &fParams, fParams.fReturnAudioChannels, fTxData ); + return true; - //audio netbuffer length - fAudioTxLen = sizeof ( packet_header_t ) + fNetAudioPlaybackBuffer->GetSize(); - fAudioRxLen = sizeof ( packet_header_t ) + fNetAudioCaptureBuffer->GetSize(); + error: + FreeNetworkBuffers(); + return false; } - int JackNetSlaveInterface::Recv ( size_t size, int flags ) + void JackNetSlaveInterface::FatalRecvError() { - int rx_bytes = fSocket.Recv ( fRxBuffer, size, flags ); - //handle errors - if ( rx_bytes == SOCKET_ERROR ) - { + jack_error("Recv connection lost error = %s", StrError(NET_ERROR_CODE)); + throw JackNetException(); + } + + void JackNetSlaveInterface::FatalSendError() + { + jack_error("Send connection lost error = %s", StrError(NET_ERROR_CODE)); + throw JackNetException(); + } + + int JackNetSlaveInterface::Recv(size_t size, int flags) + { + int rx_bytes = fSocket.Recv(fRxBuffer, size, flags); + // handle errors + if (rx_bytes == SOCKET_ERROR) { + /* net_error_t error = fSocket.GetError(); - //no data isn't really an error in realtime processing, so just return 0 - if ( error == NET_NO_DATA ) - jack_error ( "No data, is the master still running ?" ); - //if a network error occurs, this exception will restart the driver - else if ( error == NET_CONN_ERROR ) - { - jack_error ( "Connection lost." ); - throw JackNetException(); + // no data isn't really an error in realtime processing, so just return 0 + if (error == NET_NO_DATA) { + jack_error("No data, is the master still running ?"); + // if a network error occurs, this exception will restart the driver + } else if (error == NET_CONN_ERROR) { + FatalRecvError(); + } else { + jack_error("Fatal error in slave receive : %s", StrError(NET_ERROR_CODE)); } - else - jack_error ( "Fatal error in slave receive : %s", StrError ( NET_ERROR_CODE ) ); + */ + FatalRecvError(); } - + packet_header_t* header = reinterpret_cast(fRxBuffer); PacketHeaderNToH(header, header); return rx_bytes; } - int JackNetSlaveInterface::Send ( size_t size, int flags ) + int JackNetSlaveInterface::Send(size_t size, int flags) { packet_header_t* header = reinterpret_cast(fTxBuffer); PacketHeaderHToN(header, header); - int tx_bytes = fSocket.Send ( fTxBuffer, size, flags ); - - //handle errors - if ( tx_bytes == SOCKET_ERROR ) - { + int tx_bytes = fSocket.Send(fTxBuffer, size, flags); + + // handle errors + if (tx_bytes == SOCKET_ERROR) { + /* net_error_t error = fSocket.GetError(); - //if a network error occurs, this exception will restart the driver - if ( error == NET_CONN_ERROR ) - { - jack_error ( "Connection lost." ); - throw JackNetException(); + // if a network error occurs, this exception will restart the driver + if (error == NET_CONN_ERROR) { + FatalSendError(); + } else { + jack_error("Fatal error in slave send : %s", StrError(NET_ERROR_CODE)); } - else - jack_error ( "Fatal error in slave send : %s", StrError ( NET_ERROR_CODE ) ); + */ + FatalSendError(); } return tx_bytes; } @@ -832,157 +880,129 @@ namespace Jack int JackNetSlaveInterface::SyncRecv() { int rx_bytes = 0; - packet_header_t* rx_head = reinterpret_cast ( fRxBuffer ); - //receive sync (launch the cycle) - do - { - rx_bytes = Recv ( fParams.fMtu, 0 ); - //connection issue, send will detect it, so don't skip the cycle (return 0) - if ( rx_bytes == SOCKET_ERROR ) + packet_header_t* rx_head = reinterpret_cast(fRxBuffer); + + // receive sync (launch the cycle) + do { + rx_bytes = Recv(fParams.fMtu, 0); + // connection issue, send will detect it, so don't skip the cycle (return 0) + if (rx_bytes == SOCKET_ERROR) { return rx_bytes; + } } while ((strcmp(rx_head->fPacketType, "header") != 0) && (rx_head->fDataType != 's')); - + fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; return rx_bytes; } int JackNetSlaveInterface::DataRecv() { - uint recvd_midi_pckt = 0; - uint recvd_audio_pckt = 0; int rx_bytes = 0; - packet_header_t* rx_head = reinterpret_cast ( fRxBuffer ); + uint recvd_midi_pckt = 0; + packet_header_t* rx_head = reinterpret_cast(fRxBuffer); - while ( !fRxHeader.fIsLastPckt ) - { - rx_bytes = Recv ( fParams.fMtu, MSG_PEEK ); - //error here, problem with recv, just skip the cycle (return -1) + while (!fRxHeader.fIsLastPckt) { + // how much data is queued on the rx buffer ? + rx_bytes = Recv(fParams.fMtu, MSG_PEEK); - if ( rx_bytes == SOCKET_ERROR ) + // error here, problem with recv, just skip the cycle (return -1) + if (rx_bytes == SOCKET_ERROR) { return rx_bytes; - if ( rx_bytes && ( rx_head->fDataStream == 's' ) && ( rx_head->fID == fParams.fID ) ) - { - switch ( rx_head->fDataType ) - { - case 'm': //midi - rx_bytes = Recv ( rx_head->fPacketSize, 0 ); - fRxHeader.fCycle = rx_head->fCycle; - fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; - fNetMidiCaptureBuffer->RenderFromNetwork ( rx_head->fSubCycle, rx_bytes - sizeof ( packet_header_t ) ); - if ( ++recvd_midi_pckt == rx_head->fNMidiPckt ) - fNetMidiCaptureBuffer->RenderToJackPorts(); + } + + if (rx_bytes && (rx_head->fDataStream == 's') && (rx_head->fID == fParams.fID)) { + // read data + switch (rx_head->fDataType) { + + case 'm': // midi + rx_bytes = MidiRecv(rx_head, fNetMidiCaptureBuffer, recvd_midi_pckt); break; - case 'a': //audio - rx_bytes = Recv ( rx_head->fPacketSize, 0 ); - //SL: 25/01/09 - // if ( !IsNextPacket() ) - // jack_error ( "Packet(s) missing..." ); - if (recvd_audio_pckt++ != rx_head->fSubCycle) { - jack_error("Packet(s) missing from '%s'...", fParams.fMasterNetName); - } - fRxHeader.fCycle = rx_head->fCycle; - fRxHeader.fSubCycle = rx_head->fSubCycle; - fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; - fNetAudioCaptureBuffer->RenderToJackPorts ( rx_head->fSubCycle ); + case 'a': // audio + rx_bytes = AudioRecv(rx_head, fNetAudioCaptureBuffer); break; - case 's': //sync - jack_info ( "NetSlave : overloaded, skipping receive." ); - return 0; + case 's': // sync + jack_info("NetSlave : overloaded, skipping receive"); + return FinishRecv(fNetAudioCaptureBuffer); } } } + fRxHeader.fCycle = rx_head->fCycle; - return 0; + return rx_bytes; } int JackNetSlaveInterface::SyncSend() { - //tx header - if ( fParams.fSlaveSyncMode ) + // tx header + if (fParams.fSlaveSyncMode) { fTxHeader.fCycle = fRxHeader.fCycle; - else + } else { fTxHeader.fCycle++; + } fTxHeader.fSubCycle = 0; fTxHeader.fDataType = 's'; - fTxHeader.fIsLastPckt = ( fParams.fReturnMidiChannels == 0 && fParams.fReturnAudioChannels == 0) ? 1 : 0; + fTxHeader.fIsLastPckt = (fParams.fReturnMidiChannels == 0 && fParams.fReturnAudioChannels == 0) ? 1 : 0; fTxHeader.fPacketSize = fParams.fMtu; - memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); - return Send ( fTxHeader.fPacketSize, 0 ); + + memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); + // PacketHeaderDisplay(&fTxHeader); + return Send(fTxHeader.fPacketSize, 0); } int JackNetSlaveInterface::DataSend() { - uint subproc; - - //midi - if ( fParams.fReturnMidiChannels > 0) - { - fTxHeader.fDataType = 'm'; - fTxHeader.fMidiDataSize = fNetMidiPlaybackBuffer->RenderFromJackPorts(); - fTxHeader.fNMidiPckt = GetNMidiPckt(); - for ( subproc = 0; subproc < fTxHeader.fNMidiPckt; subproc++ ) - { - fTxHeader.fSubCycle = subproc; - fTxHeader.fIsLastPckt = ( ( subproc == ( fTxHeader.fNMidiPckt - 1 ) ) && !fParams.fReturnAudioChannels ) ? 1 : 0; - fTxHeader.fPacketSize = sizeof ( packet_header_t ) + fNetMidiPlaybackBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize ); - memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); - if ( Send ( fTxHeader.fPacketSize, 0 ) == SOCKET_ERROR ) - return SOCKET_ERROR; - } - } - - //audio - if ( fParams.fReturnAudioChannels > 0) - { - fTxHeader.fDataType = 'a'; - fTxHeader.fMidiDataSize = 0; - fTxHeader.fNMidiPckt = 0; - for ( subproc = 0; subproc < fNSubProcess; subproc++ ) - { - fTxHeader.fSubCycle = subproc; - fTxHeader.fIsLastPckt = ( subproc == ( fNSubProcess - 1 ) ) ? 1 : 0; - fTxHeader.fPacketSize = fAudioTxLen; - memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); - fNetAudioPlaybackBuffer->RenderFromJackPorts ( subproc ); - if ( Send ( fTxHeader.fPacketSize, 0 ) == SOCKET_ERROR ) - return SOCKET_ERROR; - } + if (MidiSend(fNetMidiPlaybackBuffer, fParams.fReturnMidiChannels, fParams.fReturnAudioChannels) == SOCKET_ERROR) { + return SOCKET_ERROR; } - return 0; + return AudioSend(fNetAudioPlaybackBuffer, fParams.fReturnAudioChannels); } - - //network sync------------------------------------------------------------------------ + + // network sync------------------------------------------------------------------------ void JackNetSlaveInterface::EncodeSyncPacket() { - //this method contains every step of sync packet informations coding - //first of all, reset sync packet - memset ( fTxData, 0, fPayloadSize ); - //then first step : transport + // This method contains every step of sync packet informations coding + // first of all, clear sync packet + memset(fTxData, 0, PACKET_AVAILABLE_SIZE(&fParams)); + + // then first step : transport if (fParams.fTransportSync) { EncodeTransportData(); - TransportDataHToN( &fReturnTransportData, &fReturnTransportData); - //copy to TxBuffer - memcpy ( fTxData, &fReturnTransportData, sizeof ( net_transport_data_t ) ); + TransportDataHToN(&fReturnTransportData, &fReturnTransportData); + // copy to TxBuffer + memcpy(fTxData, &fReturnTransportData, sizeof(net_transport_data_t)); } - //then others - //... + // then others + // ... + + // Transport is not used for now... + + // Write active ports list + fTxHeader.fActivePorts = (fNetAudioCaptureBuffer) ? fNetAudioCaptureBuffer->ActivePortsToNetwork(fTxData) : 0; } - + void JackNetSlaveInterface::DecodeSyncPacket() { - //this method contains every step of sync packet informations decoding process - //first : transport + // This method contains every step of sync packet informations decoding process + // first : transport if (fParams.fTransportSync) { - //copy received transport data to transport data structure - memcpy ( &fSendTransportData, fRxData, sizeof ( net_transport_data_t ) ); - TransportDataNToH( &fSendTransportData, &fSendTransportData); + // copy received transport data to transport data structure + memcpy(&fSendTransportData, fRxData, sizeof(net_transport_data_t)); + TransportDataNToH(&fSendTransportData, &fSendTransportData); DecodeTransportData(); } - //then others - //... + // then others + // ... + + // Transport not used for now... + packet_header_t* rx_head = reinterpret_cast(fRxBuffer); + + // Read active ports list + if (fNetAudioPlaybackBuffer) { + fNetAudioPlaybackBuffer->ActivePortsFromNetwork(fRxData, rx_head->fActivePorts); + } } } diff --git a/common/JackNetInterface.h b/common/JackNetInterface.h index ca90875f..25ec36f0 100644 --- a/common/JackNetInterface.h +++ b/common/JackNetInterface.h @@ -1,6 +1,5 @@ /* -Copyright (C) 2001 Paul Davis -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -25,58 +24,67 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { + +#define DEFAULT_MULTICAST_IP "225.3.19.154" +#define DEFAULT_PORT 19000 +#define DEFAULT_MTU 1500 + +#define SLAVE_SETUP_RETRY 5 + +#define MANAGER_INIT_TIMEOUT 2000000 // in usec +#define MASTER_INIT_TIMEOUT 1000000 // in usec +#define SLAVE_INIT_TIMEOUT 1000000 // in usec + +#define NETWORK_MAX_LATENCY 20 + /** - \Brief This class describes the basic Net Interface, used by both master and slave + \Brief This class describes the basic Net Interface, used by both master and slave. */ class SERVER_EXPORT JackNetInterface { + protected: + + void Initialize(); + session_params_t fParams; JackNetSocket fSocket; char fMulticastIP[32]; - uint fNSubProcess; - //headers + // headers packet_header_t fTxHeader; packet_header_t fRxHeader; - + // transport net_transport_data_t fSendTransportData; net_transport_data_t fReturnTransportData; - //network buffers + // network buffers char* fTxBuffer; char* fRxBuffer; char* fTxData; char* fRxData; - //jack buffers + // JACK buffers NetMidiBuffer* fNetMidiCaptureBuffer; NetMidiBuffer* fNetMidiPlaybackBuffer; NetAudioBuffer* fNetAudioCaptureBuffer; NetAudioBuffer* fNetAudioPlaybackBuffer; - //sizes - int fAudioRxLen; - int fAudioTxLen; - int fPayloadSize; - - //utility methods - void SetFramesPerPacket(); + // utility methods int SetNetBufferSize(); - int GetNMidiPckt(); - bool IsNextPacket(); + void FreeNetworkBuffers(); - //virtual methods : depends on the sub class master/slave - virtual void SetParams(); + // virtual methods : depends on the sub class master/slave + virtual bool SetParams(); virtual bool Init() = 0; - //transport + // transport virtual void EncodeTransportData() = 0; virtual void DecodeTransportData() = 0; - //sync packet + // sync packet virtual void EncodeSyncPacket() = 0; virtual void DecodeSyncPacket() = 0; @@ -85,15 +93,30 @@ namespace Jack virtual int DataRecv() = 0; virtual int DataSend() = 0; - virtual int Send ( size_t size, int flags ) = 0; - virtual int Recv ( size_t size, int flags ) = 0; + virtual int Send(size_t size, int flags) = 0; + virtual int Recv(size_t size, int flags) = 0; - JackNetInterface(); - JackNetInterface ( const char* multicast_ip, int port ); - JackNetInterface ( session_params_t& params, JackNetSocket& socket, const char* multicast_ip ); + virtual void FatalRecvError() = 0; + virtual void FatalSendError() = 0; + + int MidiSend(NetMidiBuffer* buffer, int midi_channnels, int audio_channels); + int AudioSend(NetAudioBuffer* buffer, int audio_channels); + + int MidiRecv(packet_header_t* rx_head, NetMidiBuffer* buffer, uint& recvd_midi_pckt); + int AudioRecv(packet_header_t* rx_head, NetAudioBuffer* buffer); + + int FinishRecv(NetAudioBuffer* buffer); + + NetAudioBuffer* AudioBufferFactory(int nports, char* buffer); public: + + JackNetInterface(); + JackNetInterface(const char* multicast_ip, int port); + JackNetInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip); + virtual ~JackNetInterface(); + }; /** @@ -102,38 +125,48 @@ namespace Jack class SERVER_EXPORT JackNetMasterInterface : public JackNetInterface { + protected: + bool fRunning; - int fCycleOffset; + + int fCurrentCycleOffset; + int fMaxCycleOffset; + int fLastfCycleOffset; bool Init(); int SetRxTimeout(); - void SetParams(); - + bool SetParams(); + void Exit(); - + int SyncRecv(); int SyncSend(); - + int DataRecv(); int DataSend(); - - //sync packet + + // sync packet void EncodeSyncPacket(); void DecodeSyncPacket(); - int Send ( size_t size, int flags ); - int Recv ( size_t size, int flags ); - + int Send(size_t size, int flags); + int Recv(size_t size, int flags); + bool IsSynched(); + void FatalRecvError(); + void FatalSendError(); + public: - JackNetMasterInterface() : JackNetInterface(), fRunning(false), fCycleOffset(0) + + JackNetMasterInterface() : JackNetInterface(), fRunning(false), fCurrentCycleOffset(0), fMaxCycleOffset(0), fLastfCycleOffset(0) {} - JackNetMasterInterface ( session_params_t& params, JackNetSocket& socket, const char* multicast_ip ) - : JackNetInterface ( params, socket, multicast_ip ) + JackNetMasterInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip) + : JackNetInterface(params, socket, multicast_ip) {} - ~JackNetMasterInterface() + + virtual~JackNetMasterInterface() {} }; @@ -143,75 +176,67 @@ namespace Jack class SERVER_EXPORT JackNetSlaveInterface : public JackNetInterface { + protected: - + static uint fSlaveCounter; - + bool Init(); - bool InitConnection(); + bool InitConnection(int time_out_sec); bool InitRendering(); - - net_status_t SendAvailableToMaster(); + + net_status_t SendAvailableToMaster(long count = LONG_MAX); // long here (and not int...) net_status_t SendStartToMaster(); - - void SetParams(); - + + bool SetParams(); + int SyncRecv(); int SyncSend(); - + int DataRecv(); int DataSend(); - - //sync packet + + // sync packet void EncodeSyncPacket(); void DecodeSyncPacket(); - int Recv ( size_t size, int flags ); - int Send ( size_t size, int flags ); + int Recv(size_t size, int flags); + int Send(size_t size, int flags); - public: - JackNetSlaveInterface() : JackNetInterface() + void FatalRecvError(); + void FatalSendError(); + + void InitAPI() { - //open Socket API with the first slave - if ( fSlaveCounter++ == 0 ) - { - if ( SocketAPIInit() < 0 ) - { - jack_error ( "Can't init Socket API, exiting..." ); - throw -1; + // open Socket API with the first slave + if (fSlaveCounter++ == 0) { + if (SocketAPIInit() < 0) { + jack_error("Can't init Socket API, exiting..."); + throw std::bad_alloc(); } } } - JackNetSlaveInterface ( const char* ip, int port ) : JackNetInterface ( ip, port ) + + public: + + JackNetSlaveInterface() : JackNetInterface() { - //open Socket API with the first slave - if ( fSlaveCounter++ == 0 ) - { - if ( SocketAPIInit() < 0 ) - { - jack_error ( "Can't init Socket API, exiting..." ); - throw -1; - } - } + InitAPI(); + } + + JackNetSlaveInterface(const char* ip, int port) : JackNetInterface(ip, port) + { + InitAPI(); } - ~JackNetSlaveInterface() + + virtual ~JackNetSlaveInterface() { - //close Socket API with the last slave - if ( --fSlaveCounter == 0 ) + // close Socket API with the last slave + if (--fSlaveCounter == 0) { SocketAPIEnd(); + } } }; } -#define DEFAULT_MULTICAST_IP "225.3.19.154" -#define DEFAULT_PORT 19000 -#define DEFAULT_MTU 1500 - -#define SLAVE_SETUP_RETRY 5 - -#define MASTER_INIT_TIMEOUT 1000000 // in usec -#define SLAVE_INIT_TIMEOUT 2000000 // in usec - -#define MAX_LATENCY 6 - #endif diff --git a/common/JackNetManager.cpp b/common/JackNetManager.cpp index e882cb6e..3dd940b2 100644 --- a/common/JackNetManager.cpp +++ b/common/JackNetManager.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2008 Romain Moret at Grame +Copyright(C) 2008-2011 Romain Moret at Grame 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 @@ -18,7 +18,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackNetManager.h" #include "JackArgParser.h" -#include "JackTime.h" +#include "JackServerGlobals.h" +#include "JackLockedEngine.h" using namespace std; @@ -26,79 +27,74 @@ namespace Jack { //JackNetMaster****************************************************************************************************** - JackNetMaster::JackNetMaster ( JackNetSocket& socket, session_params_t& params, const char* multicast_ip) - : JackNetMasterInterface ( params, socket, multicast_ip ) + JackNetMaster::JackNetMaster(JackNetSocket& socket, session_params_t& params, const char* multicast_ip) + : JackNetMasterInterface(params, socket, multicast_ip) { - jack_log ( "JackNetMaster::JackNetMaster" ); + jack_log("JackNetMaster::JackNetMaster"); //settings - fClientName = const_cast ( fParams.fName ); + fClientName = const_cast(fParams.fName); fJackClient = NULL; fSendTransportData.fState = -1; fReturnTransportData.fState = -1; fLastTransportState = -1; - uint port_index; + int port_index; //jack audio ports fAudioCapturePorts = new jack_port_t* [fParams.fSendAudioChannels]; - for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) + for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { fAudioCapturePorts[port_index] = NULL; + } + fAudioPlaybackPorts = new jack_port_t* [fParams.fReturnAudioChannels]; - for ( port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ ) + for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { fAudioPlaybackPorts[port_index] = NULL; + } + //jack midi ports fMidiCapturePorts = new jack_port_t* [fParams.fSendMidiChannels]; - for ( port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) + for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { fMidiCapturePorts[port_index] = NULL; + } + fMidiPlaybackPorts = new jack_port_t* [fParams.fReturnMidiChannels]; - for ( port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) + for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { fMidiPlaybackPorts[port_index] = NULL; + } //monitor #ifdef JACK_MONITOR - fPeriodUsecs = ( int ) ( 1000000.f * ( ( float ) fParams.fPeriodSize / ( float ) fParams.fSampleRate ) ); + fPeriodUsecs = (int)(1000000.f * ((float) fParams.fPeriodSize / (float) fParams.fSampleRate)); string plot_name; - plot_name = string ( fParams.fName ); - plot_name += string ( "_master" ); - plot_name += string ( ( fParams.fSlaveSyncMode ) ? "_sync" : "_async" ); - switch ( fParams.fNetworkMode ) - { - case 's' : - plot_name += string ( "_slow" ); - break; - case 'n' : - plot_name += string ( "_normal" ); - break; - case 'f' : - plot_name += string ( "_fast" ); - break; - } - fNetTimeMon = new JackGnuPlotMonitor ( 128, 4, plot_name ); + plot_name = string(fParams.fName); + plot_name += string("_master"); + plot_name += string((fParams.fSlaveSyncMode) ? "_sync" : "_async"); + plot_name += string("_latency"); + fNetTimeMon = new JackGnuPlotMonitor(128, 4, plot_name); string net_time_mon_fields[] = { - string ( "sync send" ), - string ( "end of send" ), - string ( "sync recv" ), - string ( "end of cycle" ) + string("sync send"), + string("end of send"), + string("sync recv"), + string("end of cycle") }; string net_time_mon_options[] = { - string ( "set xlabel \"audio cycles\"" ), - string ( "set ylabel \"% of audio cycle\"" ) + string("set xlabel \"audio cycles\""), + string("set ylabel \"% of audio cycle\"") }; - fNetTimeMon->SetPlotFile ( net_time_mon_options, 2, net_time_mon_fields, 4 ); + fNetTimeMon->SetPlotFile(net_time_mon_options, 2, net_time_mon_fields, 4); #endif } JackNetMaster::~JackNetMaster() { - jack_log ( "JackNetMaster::~JackNetMaster, ID %u.", fParams.fID ); + jack_log("JackNetMaster::~JackNetMaster ID = %u", fParams.fID); - if ( fJackClient ) - { - jack_deactivate ( fJackClient ); + if (fJackClient) { + jack_deactivate(fJackClient); FreePorts(); - jack_client_close ( fJackClient ); + jack_client_close(fJackClient); } delete[] fAudioCapturePorts; delete[] fAudioPlaybackPorts; @@ -113,29 +109,34 @@ namespace Jack bool JackNetMaster::Init(bool auto_connect) { //network init - if ( !JackNetMasterInterface::Init() ) + if (!JackNetMasterInterface::Init()) { + jack_error("JackNetMasterInterface::Init() error..."); return false; + } //set global parameters - SetParams(); + if (!SetParams()) { + jack_error("SetParams error..."); + return false; + } //jack client and process jack_status_t status; - if ( ( fJackClient = jack_client_open ( fClientName, JackNullOption, &status, NULL ) ) == NULL ) - { - jack_error ( "Can't open a new jack client." ); + if ((fJackClient = jack_client_open(fClientName, JackNullOption, &status, NULL)) == NULL) { + jack_error("Can't open a new JACK client"); return false; } - if (jack_set_process_callback(fJackClient, SetProcess, this ) < 0) - goto fail; + if (jack_set_process_callback(fJackClient, SetProcess, this) < 0) { + goto fail; + } - if (jack_set_buffer_size_callback(fJackClient, SetBufferSize, this) < 0) - goto fail; + if (jack_set_buffer_size_callback(fJackClient, SetBufferSize, this) < 0) { + goto fail; + } - if ( AllocPorts() != 0 ) - { - jack_error ( "Can't allocate jack ports." ); + if (AllocPorts() != 0) { + jack_error("Can't allocate JACK ports"); goto fail; } @@ -143,20 +144,20 @@ namespace Jack fRunning = true; //finally activate jack client - if ( jack_activate ( fJackClient ) != 0 ) - { - jack_error ( "Can't activate jack client." ); + if (jack_activate(fJackClient) != 0) { + jack_error("Can't activate JACK client"); goto fail; } - if (auto_connect) + if (auto_connect) { ConnectPorts(); - jack_info ( "New NetMaster started." ); + } + jack_info("New NetMaster started"); return true; fail: FreePorts(); - jack_client_close ( fJackClient ); + jack_client_close(fJackClient); fJackClient = NULL; return false; } @@ -164,79 +165,49 @@ 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_nframes_t port_latency = jack_get_buffer_size(fJackClient); jack_latency_range_t range; - jack_log ( "JackNetMaster::AllocPorts" ); + jack_log("JackNetMaster::AllocPorts"); //audio - for ( i = 0; i < fParams.fSendAudioChannels; i++ ) - { - sprintf ( name, "to_slave_%d", i+1 ); - if ( ( fAudioCapturePorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0 ) ) == NULL ) + for (i = 0; i < fParams.fSendAudioChannels; i++) { + snprintf(name, sizeof(name), "to_slave_%d", i+1); + if ((fAudioCapturePorts[i] = jack_port_register(fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0)) == NULL) return -1; //port latency range.min = range.max = 0; jack_port_set_latency_range(fAudioCapturePorts[i], JackCaptureLatency, &range); } - for ( i = 0; i < fParams.fReturnAudioChannels; i++ ) - { - sprintf ( name, "from_slave_%d", i+1 ); - if ( ( fAudioPlaybackPorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0 ) ) == NULL ) + for (i = 0; i < fParams.fReturnAudioChannels; i++) { + snprintf(name, sizeof(name), "from_slave_%d", i+1); + if ((fAudioPlaybackPorts[i] = jack_port_register(fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0)) == NULL) return -1; //port latency - switch ( fParams.fNetworkMode ) - { - case 'f' : - range.min = range.max = (fParams.fSlaveSyncMode) ? 0 : port_latency; - jack_port_set_latency_range(fAudioPlaybackPorts[i], JackPlaybackLatency, &range); - break; - case 'n' : - range.min = range.max = port_latency + (fParams.fSlaveSyncMode) ? 0 : port_latency; - jack_port_set_latency_range(fAudioPlaybackPorts[i], JackPlaybackLatency, &range); - break; - case 's' : - range.min = range.max = 2 * port_latency + (fParams.fSlaveSyncMode) ? 0 : port_latency; - jack_port_set_latency_range(fAudioPlaybackPorts[i], JackPlaybackLatency, &range); - break; - } + range.min = range.max = fParams.fNetworkLatency * port_latency + (fParams.fSlaveSyncMode) ? 0 : port_latency; + jack_port_set_latency_range(fAudioPlaybackPorts[i], JackPlaybackLatency, &range); } - //midi - for ( i = 0; i < fParams.fSendMidiChannels; i++ ) - { - sprintf ( name, "midi_to_slave_%d", i+1 ); - if ( ( fMidiCapturePorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 0 ) ) == NULL ) + for (i = 0; i < fParams.fSendMidiChannels; i++) { + snprintf(name, sizeof(name), "midi_to_slave_%d", i+1); + if ((fMidiCapturePorts[i] = jack_port_register(fJackClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 0)) == NULL) return -1; //port latency range.min = range.max = 0; jack_port_set_latency_range(fMidiCapturePorts[i], JackCaptureLatency, &range); } - for ( i = 0; i < fParams.fReturnMidiChannels; i++ ) - { - sprintf ( name, "midi_from_slave_%d", i+1 ); - if ( ( fMidiPlaybackPorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal, 0 ) ) == NULL ) + + for (i = 0; i < fParams.fReturnMidiChannels; i++) { + snprintf(name, sizeof(name), "midi_from_slave_%d", i+1); + if ((fMidiPlaybackPorts[i] = jack_port_register(fJackClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal, 0)) == NULL) return -1; //port latency - switch ( fParams.fNetworkMode ) - { - case 'f' : - range.min = range.max = (fParams.fSlaveSyncMode) ? 0 : port_latency; - jack_port_set_latency_range(fMidiPlaybackPorts[i], JackPlaybackLatency, &range); - break; - case 'n' : - range.min = range.max = port_latency + (fParams.fSlaveSyncMode) ? 0 : port_latency; - jack_port_set_latency_range(fMidiPlaybackPorts[i], JackPlaybackLatency, &range); - break; - case 's' : - range.min = range.max = 2 * port_latency + (fParams.fSlaveSyncMode) ? 0 : port_latency; - jack_port_set_latency_range(fMidiPlaybackPorts[i], JackPlaybackLatency, &range); - break; - } + range.min = range.max = fParams.fNetworkLatency * port_latency + (fParams.fSlaveSyncMode) ? 0 : port_latency; + jack_port_set_latency_range(fMidiPlaybackPorts[i], JackPlaybackLatency, &range); } return 0; } @@ -247,7 +218,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); @@ -255,7 +226,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); @@ -264,21 +235,29 @@ namespace Jack void JackNetMaster::FreePorts() { - jack_log ( "JackNetMaster::FreePorts, ID %u", fParams.fID ); - - uint port_index; - for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) - if ( fAudioCapturePorts[port_index] ) - jack_port_unregister ( fJackClient, fAudioCapturePorts[port_index] ); - for ( port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ ) - if ( fAudioPlaybackPorts[port_index] ) - jack_port_unregister ( fJackClient, fAudioPlaybackPorts[port_index] ); - for ( port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) - if ( fMidiCapturePorts[port_index] ) - jack_port_unregister ( fJackClient, fMidiCapturePorts[port_index] ); - for ( port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) - if ( fMidiPlaybackPorts[port_index] ) - jack_port_unregister ( fJackClient, fMidiPlaybackPorts[port_index] ); + jack_log("JackNetMaster::FreePorts ID = %u", fParams.fID); + + int port_index; + for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { + if (fAudioCapturePorts[port_index]) { + jack_port_unregister(fJackClient, fAudioCapturePorts[port_index]); + } + } + for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { + if (fAudioPlaybackPorts[port_index]) { + jack_port_unregister(fJackClient, fAudioPlaybackPorts[port_index]); + } + } + for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { + if (fMidiCapturePorts[port_index]) { + jack_port_unregister(fJackClient, fMidiCapturePorts[port_index]); + } + } + for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { + if (fMidiPlaybackPorts[port_index]) { + jack_port_unregister(fJackClient, fMidiPlaybackPorts[port_index]); + } + } } //transport--------------------------------------------------------------------------- @@ -289,87 +268,88 @@ namespace Jack fSendTransportData.fTimebaseMaster = NO_CHANGE; //update state and position - fSendTransportData.fState = static_cast ( jack_transport_query ( fJackClient, &fSendTransportData.fPosition ) ); + fSendTransportData.fState = static_cast(jack_transport_query(fJackClient, &fSendTransportData.fPosition)); //is it a new state ? - fSendTransportData.fNewState = ( ( fSendTransportData.fState != fLastTransportState ) && - ( fSendTransportData.fState != fReturnTransportData.fState ) ); - if ( fSendTransportData.fNewState ) - jack_info ( "Sending '%s' to '%s' frame = %ld", GetTransportState ( fSendTransportData.fState ), fParams.fName, fSendTransportData.fPosition.frame ); + fSendTransportData.fNewState = ((fSendTransportData.fState != fLastTransportState) && (fSendTransportData.fState != fReturnTransportData.fState)); + if (fSendTransportData.fNewState) { + jack_info("Sending '%s' to '%s' frame = %ld", GetTransportState(fSendTransportData.fState), fParams.fName, fSendTransportData.fPosition.frame); + } fLastTransportState = fSendTransportData.fState; } void JackNetMaster::DecodeTransportData() { //is there timebase master change ? - if ( fReturnTransportData.fTimebaseMaster != NO_CHANGE ) - { + if (fReturnTransportData.fTimebaseMaster != NO_CHANGE) { + int timebase = 0; - switch ( fReturnTransportData.fTimebaseMaster ) + switch (fReturnTransportData.fTimebaseMaster) { case RELEASE_TIMEBASEMASTER : - timebase = jack_release_timebase ( fJackClient ); - if ( timebase < 0 ) - jack_error ( "Can't release timebase master." ); - else - jack_info ( "'%s' isn't the timebase master anymore.", fParams.fName ); + timebase = jack_release_timebase(fJackClient); + if (timebase < 0) { + jack_error("Can't release timebase master"); + } else { + jack_info("'%s' isn't the timebase master anymore", fParams.fName); + } break; case TIMEBASEMASTER : - timebase = jack_set_timebase_callback ( fJackClient, 0, SetTimebaseCallback, this ); - if ( timebase < 0 ) - jack_error ( "Can't set a new timebase master." ); - else - jack_info ( "'%s' is the new timebase master.", fParams.fName ); + timebase = jack_set_timebase_callback(fJackClient, 0, SetTimebaseCallback, this); + if (timebase < 0) { + jack_error("Can't set a new timebase master"); + } else { + jack_info("'%s' is the new timebase master", fParams.fName); + } break; case CONDITIONAL_TIMEBASEMASTER : - timebase = jack_set_timebase_callback ( fJackClient, 1, SetTimebaseCallback, this ); - if ( timebase != EBUSY ) - { - if ( timebase < 0 ) - jack_error ( "Can't set a new timebase master." ); + timebase = jack_set_timebase_callback(fJackClient, 1, SetTimebaseCallback, this); + if (timebase != EBUSY) { + if (timebase < 0) + jack_error("Can't set a new timebase master"); else - jack_info ( "'%s' is the new timebase master.", fParams.fName ); + jack_info("'%s' is the new timebase master", fParams.fName); } break; } } //is the slave in a new transport state and is this state different from master's ? - if ( fReturnTransportData.fNewState && ( fReturnTransportData.fState != jack_transport_query ( fJackClient, NULL ) ) ) - { - switch ( fReturnTransportData.fState ) + if (fReturnTransportData.fNewState && (fReturnTransportData.fState != jack_transport_query(fJackClient, NULL))) { + + switch (fReturnTransportData.fState) { case JackTransportStopped : - jack_transport_stop ( fJackClient ); - jack_info ( "'%s' stops transport.", fParams.fName ); + jack_transport_stop(fJackClient); + jack_info("'%s' stops transport", fParams.fName); break; case JackTransportStarting : - if ( jack_transport_reposition ( fJackClient, &fReturnTransportData.fPosition ) == EINVAL ) - jack_error ( "Can't set new position." ); - jack_transport_start ( fJackClient ); - jack_info ( "'%s' starts transport frame = %d", fParams.fName, fReturnTransportData.fPosition.frame); + if (jack_transport_reposition(fJackClient, &fReturnTransportData.fPosition) == EINVAL) + jack_error("Can't set new position"); + jack_transport_start(fJackClient); + jack_info("'%s' starts transport frame = %d", fParams.fName, fReturnTransportData.fPosition.frame); break; case JackTransportNetStarting : - jack_info ( "'%s' is ready to roll..", fParams.fName ); + jack_info("'%s' is ready to roll...", fParams.fName); break; case JackTransportRolling : - jack_info ( "'%s' is rolling.", fParams.fName ); + jack_info("'%s' is rolling", fParams.fName); break; } } } - void JackNetMaster::SetTimebaseCallback ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void* arg ) + void JackNetMaster::SetTimebaseCallback(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void* arg) { - static_cast ( arg )->TimebaseCallback ( pos ); + static_cast(arg)->TimebaseCallback(pos); } - void JackNetMaster::TimebaseCallback ( jack_position_t* pos ) + void JackNetMaster::TimebaseCallback(jack_position_t* pos) { pos->bar = fReturnTransportData.fPosition.bar; pos->beat = fReturnTransportData.fPosition.beat; @@ -385,32 +365,36 @@ namespace Jack bool JackNetMaster::IsSlaveReadyToRoll() { - return ( fReturnTransportData.fState == JackTransportNetStarting ); + return (fReturnTransportData.fState == JackTransportNetStarting); } int JackNetMaster::SetBufferSize(jack_nframes_t nframes, void* arg) { JackNetMaster* obj = static_cast(arg); if (nframes != obj->fParams.fPeriodSize) { - jack_error("Cannot handle bufer size change, so JackNetMaster proxy will be removed..."); + jack_error("Cannot handle buffer size change, so JackNetMaster proxy will be removed..."); obj->Exit(); } return 0; } //process----------------------------------------------------------------------------- - int JackNetMaster::SetProcess ( jack_nframes_t nframes, void* arg ) + int JackNetMaster::SetProcess(jack_nframes_t nframes, void* arg) { - return static_cast ( arg )->Process(); + try { + return static_cast(arg)->Process(); + } catch (JackNetException& e) { + return 0; + } } int JackNetMaster::Process() { - if ( !fRunning ) - return 0; + int res; - uint port_index; - int res = 0; + if (!fRunning) { + return 0; + } #ifdef JACK_MONITOR jack_time_t begin_time = GetMicroSeconds(); @@ -418,38 +402,75 @@ namespace Jack #endif //buffers - for ( port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) - fNetMidiCaptureBuffer->SetBuffer ( port_index, static_cast ( jack_port_get_buffer ( fMidiCapturePorts[port_index], - fParams.fPeriodSize ) ) ); - for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) - fNetAudioCaptureBuffer->SetBuffer ( port_index, static_cast ( jack_port_get_buffer ( fAudioCapturePorts[port_index], - fParams.fPeriodSize ) ) ); - for ( port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) - fNetMidiPlaybackBuffer->SetBuffer ( port_index, static_cast ( jack_port_get_buffer ( fMidiPlaybackPorts[port_index], - fParams.fPeriodSize ) ) ); - for ( port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ ) - fNetAudioPlaybackBuffer->SetBuffer ( port_index, static_cast ( jack_port_get_buffer ( fAudioPlaybackPorts[port_index], - fParams.fPeriodSize ) ) ); + for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { + fNetMidiCaptureBuffer->SetBuffer(midi_port_index, + static_cast(jack_port_get_buffer(fMidiCapturePorts[midi_port_index], + fParams.fPeriodSize))); + } + for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { + + #ifdef OPTIMIZED_PROTOCOL + if (fNetAudioCaptureBuffer->GetConnected(audio_port_index)) { + // Port is connected on other side... + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, + ((jack_port_connected(fAudioCapturePorts[audio_port_index]) > 0) + ? static_cast(jack_port_get_buffer(fAudioCapturePorts[audio_port_index], fParams.fPeriodSize)) + : NULL)); + } else { + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, NULL); + } + #else + fNetAudioCaptureBuffer->SetBuffer(audio_port_index, + static_cast(jack_port_get_buffer(fAudioCapturePorts[audio_port_index], + fParams.fPeriodSize))); + #endif + // TODO + } + + for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { + fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, + static_cast(jack_port_get_buffer(fMidiPlaybackPorts[midi_port_index], + fParams.fPeriodSize))); + } + for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { + + #ifdef OPTIMIZED_PROTOCOL + sample_t* out = (jack_port_connected(fAudioPlaybackPorts[audio_port_index]) > 0) + ? static_cast(jack_port_get_buffer(fAudioPlaybackPorts[audio_port_index], fParams.fPeriodSize)) + : NULL; + if (out) { + memset(out, 0, sizeof(float) * fParams.fPeriodSize); + } + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out); + #else + sample_t* out = static_cast(jack_port_get_buffer(fAudioPlaybackPorts[audio_port_index], fParams.fPeriodSize)); + if (out) { + memset(out, 0, sizeof(float) * fParams.fPeriodSize); + } + fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out))); + #endif + } if (IsSynched()) { // only send if connection is "synched" //encode the first packet EncodeSyncPacket(); - //send sync - if ( SyncSend() == SOCKET_ERROR ) + if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; + } #ifdef JACK_MONITOR - fNetTimeMon->Add ( ( ( ( float ) (GetMicroSeconds() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f ); + fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif //send data - if ( DataSend() == SOCKET_ERROR ) + if (DataSend() == SOCKET_ERROR) { return SOCKET_ERROR; + } #ifdef JACK_MONITOR - fNetTimeMon->Add ( ( ( ( float ) (GetMicroSeconds() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f ); + fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif } else { @@ -458,11 +479,32 @@ namespace Jack //receive sync res = SyncRecv(); - if ( ( res == 0 ) || ( res == SOCKET_ERROR ) ) + if ((res == 0) || (res == SOCKET_ERROR)) { return res; + } + + /* + switch (SyncRecv()) { + + case 0: + jack_error("Connection is not yet synched, skip cycle..."); + return 0; + + case SOCKET_ERROR: + jack_error("Connection is lost, quit master..."); + //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(); + break; + + default: + break; + } + */ #ifdef JACK_MONITOR - fNetTimeMon->Add ( ( ( ( float ) (GetMicroSeconds() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f ); + fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif //decode sync @@ -470,45 +512,80 @@ namespace Jack //receive data res = DataRecv(); - if ( ( res == 0 ) || ( res == SOCKET_ERROR ) ) + if ((res == 0) || (res == SOCKET_ERROR)) { return res; + } else if (res == NET_PACKET_ERROR) { + // Well not a real XRun... + JackServerGlobals::fInstance->GetEngine()->NotifyXRun(GetMicroSeconds(), 0); + } + + /* + switch (DataRecv()) { + + case 0: + jack_error("Connection is not yet synched, skip cycle..."); + return 0; + + case SOCKET_ERROR: + jack_error("Connection is lost, quit master..."); + //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(); + break; + + default: + break; + } + */ #ifdef JACK_MONITOR - fNetTimeMon->AddLast ( ( ( ( float ) (GetMicroSeconds() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f ); + fNetTimeMon->AddLast((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif return 0; } //JackNetMasterManager*********************************************************************************************** - JackNetMasterManager::JackNetMasterManager ( jack_client_t* client, const JSList* params ) : fSocket() + JackNetMasterManager::JackNetMasterManager(jack_client_t* client, const JSList* params) : fSocket() { - jack_log ( "JackNetMasterManager::JackNetMasterManager" ); + jack_log("JackNetMasterManager::JackNetMasterManager"); fManagerClient = client; - fManagerName = jack_get_client_name ( fManagerClient ); - strcpy(fMulticastIP, DEFAULT_MULTICAST_IP); - fSocket.SetPort ( DEFAULT_PORT ); + fManagerName = jack_get_client_name(fManagerClient); fGlobalID = 0; fRunning = true; fAutoConnect = false; const JSList* node; const jack_driver_param_t* param; - for ( node = params; node; node = jack_slist_next ( node ) ) - { - param = ( const jack_driver_param_t* ) node->data; - switch ( param->character ) + + // Possibly use env variable + const char* default_udp_port = getenv("JACK_NETJACK_PORT"); + fSocket.SetPort((default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT); + + const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); + if (default_multicast_ip) { + strcpy(fMulticastIP, default_multicast_ip); + } else { + strcpy(fMulticastIP, DEFAULT_MULTICAST_IP); + } + + for (node = params; node; node = jack_slist_next(node)) { + + param = (const jack_driver_param_t*) node->data; + switch (param->character) { case 'a' : - if (strlen (param->value.str) < 32) + if (strlen(param->value.str) < 32) { strcpy(fMulticastIP, param->value.str); - else + } else { jack_error("Can't use multicast address %s, using default %s", param->value.ui, DEFAULT_MULTICAST_IP); + } break; case 'p': - fSocket.SetPort ( param->value.ui ); + fSocket.SetPort(param->value.ui); break; case 'c': @@ -518,59 +595,82 @@ namespace Jack } //set sync callback - jack_set_sync_callback ( fManagerClient, SetSyncCallback, this ); + jack_set_sync_callback(fManagerClient, SetSyncCallback, this); //activate the client (for sync callback) - if ( jack_activate ( fManagerClient ) != 0 ) - jack_error ( "Can't activate the network manager client, transport disabled." ); + if (jack_activate(fManagerClient) != 0) { + jack_error("Can't activate the NetManager client, transport disabled"); + } //launch the manager thread - if ( jack_client_create_thread ( fManagerClient, &fManagerThread, 0, 0, NetManagerThread, this ) ) - jack_error ( "Can't create the network manager control thread." ); + if (jack_client_create_thread(fManagerClient, &fManagerThread, 0, 0, NetManagerThread, this)) { + jack_error("Can't create the NetManager control thread"); + } } JackNetMasterManager::~JackNetMasterManager() { - jack_log ( "JackNetMasterManager::~JackNetMasterManager" ); - jack_info ( "Exiting net manager..." ); + jack_log("JackNetMasterManager::~JackNetMasterManager"); + jack_info("Exiting NetManager..."); fRunning = false; - jack_client_kill_thread ( fManagerClient, fManagerThread ); + jack_client_kill_thread(fManagerClient, fManagerThread); master_list_t::iterator it; - for ( it = fMasterList.begin(); it != fMasterList.end(); it++ ) - delete ( *it ); + for (it = fMasterList.begin(); it != fMasterList.end(); it++) { + delete(*it); + } fSocket.Close(); SocketAPIEnd(); } - int JackNetMasterManager::SetSyncCallback ( jack_transport_state_t state, jack_position_t* pos, void* arg ) + int JackNetMasterManager::CountIO(int flags) + { + const char **ports; + int count = 0; + jack_port_t* port; + + ports = jack_get_ports(fManagerClient, NULL, NULL, flags); + if (ports != NULL) { + while (ports[count] + && (port = jack_port_by_name(fManagerClient, ports[count])) + && (strcmp(jack_port_type(port), JACK_DEFAULT_AUDIO_TYPE) == 0)) { + count++; + } + free(ports); + } + return count; + } + + int JackNetMasterManager::SetSyncCallback(jack_transport_state_t state, jack_position_t* pos, void* arg) { - return static_cast ( arg )->SyncCallback ( state, pos ); + return static_cast(arg)->SyncCallback(state, pos); } - int JackNetMasterManager::SyncCallback ( jack_transport_state_t state, jack_position_t* pos ) + int JackNetMasterManager::SyncCallback(jack_transport_state_t state, jack_position_t* pos) { //check if each slave is ready to roll int ret = 1; master_list_it_t it; - for ( it = fMasterList.begin(); it != fMasterList.end(); it++ ) - if ( ! ( *it )->IsSlaveReadyToRoll() ) + for (it = fMasterList.begin(); it != fMasterList.end(); it++) { + if (!(*it)->IsSlaveReadyToRoll()) { ret = 0; - jack_log ( "JackNetMasterManager::SyncCallback returns '%s'", ( ret ) ? "true" : "false" ); + } + } + jack_log("JackNetMasterManager::SyncCallback returns '%s'", (ret) ? "true" : "false"); return ret; } - void* JackNetMasterManager::NetManagerThread ( void* arg ) + void* JackNetMasterManager::NetManagerThread(void* arg) { - JackNetMasterManager* master_manager = static_cast ( arg ); - jack_info ( "Starting Jack Network Manager." ); - jack_info ( "Listening on '%s:%d'", master_manager->fMulticastIP, master_manager->fSocket.GetPort() ); + JackNetMasterManager* master_manager = static_cast(arg); + jack_info("Starting Jack NetManager"); + jack_info("Listening on '%s:%d'", master_manager->fMulticastIP, master_manager->fSocket.GetPort()); master_manager->Run(); return NULL; } void JackNetMasterManager::Run() { - jack_log ( "JackNetMasterManager::Run" ); + jack_log("JackNetMasterManager::Run"); //utility variables int attempt = 0; @@ -580,137 +680,133 @@ namespace Jack JackNetMaster* net_master; //init socket API (win32) - if ( SocketAPIInit() < 0 ) - { - jack_error ( "Can't init Socket API, exiting..." ); + if (SocketAPIInit() < 0) { + jack_error("Can't init Socket API, exiting..."); return; } //socket - if ( fSocket.NewSocket() == SOCKET_ERROR ) - { - jack_error ( "Can't create the network management input socket : %s", StrError ( NET_ERROR_CODE ) ); + if (fSocket.NewSocket() == SOCKET_ERROR) { + jack_error("Can't create NetManager input socket : %s", StrError(NET_ERROR_CODE)); return; } //bind the socket to the local port - if ( fSocket.Bind() == SOCKET_ERROR ) - { - jack_error ( "Can't bind the network manager socket : %s", StrError ( NET_ERROR_CODE ) ); + if (fSocket.Bind() == SOCKET_ERROR) { + jack_error("Can't bind NetManager socket : %s", StrError(NET_ERROR_CODE)); fSocket.Close(); return; } //join multicast group - if ( fSocket.JoinMCastGroup ( fMulticastIP ) == SOCKET_ERROR ) - jack_error ( "Can't join multicast group : %s", StrError ( NET_ERROR_CODE ) ); + if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) { + jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE)); + } //local loop - if ( fSocket.SetLocalLoop() == SOCKET_ERROR ) - jack_error ( "Can't set local loop : %s", StrError ( NET_ERROR_CODE ) ); + if (fSocket.SetLocalLoop() == SOCKET_ERROR) { + jack_error("Can't set local loop : %s", StrError(NET_ERROR_CODE)); + } //set a timeout on the multicast receive (the thread can now be cancelled) - if ( fSocket.SetTimeOut ( 2000000 ) == SOCKET_ERROR ) - jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) ); - - jack_info ( "Waiting for a slave..." ); + if (fSocket.SetTimeOut(MANAGER_INIT_TIMEOUT) == SOCKET_ERROR) { + jack_error("Can't set timeout : %s", StrError(NET_ERROR_CODE)); + } //main loop, wait for data, deal with it and wait again do { session_params_t net_params; - rx_bytes = fSocket.CatchHost ( &net_params, sizeof ( session_params_t ), 0 ); + rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0); SessionParamsNToH(&net_params, &host_params); - if ( ( rx_bytes == SOCKET_ERROR ) && ( fSocket.GetError() != NET_NO_DATA ) ) - { - jack_error ( "Error in receive : %s", StrError ( NET_ERROR_CODE ) ); - if ( ++attempt == 10 ) - { - jack_error ( "Can't receive on the socket, exiting net manager." ); + if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { + jack_error("Error in receive : %s", StrError(NET_ERROR_CODE)); + if (++attempt == 10) { + jack_error("Can't receive on the socket, exiting net manager"); return; } } - if ( rx_bytes == sizeof ( session_params_t ) ) - { - switch ( GetPacketType ( &host_params ) ) + + if (rx_bytes == sizeof(session_params_t)) { + switch (GetPacketType (&host_params)) { case SLAVE_AVAILABLE: - if ( ( net_master = InitMaster ( host_params ) ) ) - SessionParamsDisplay ( &net_master->fParams ); - else - jack_error ( "Can't init new net master..." ); - jack_info ( "Waiting for a slave..." ); + if ((net_master = InitMaster(host_params))) { + SessionParamsDisplay(&net_master->fParams); + } else { + jack_error("Can't init new NetMaster..."); + } + jack_info("Waiting for a slave..."); break; case KILL_MASTER: - if ( KillMaster ( &host_params ) ) - jack_info ( "Waiting for a slave..." ); + if (KillMaster(&host_params)) { + jack_info("Waiting for a slave..."); + } break; default: break; } } } - while ( fRunning ); + while (fRunning); } - JackNetMaster* JackNetMasterManager::InitMaster ( session_params_t& params ) + JackNetMaster* JackNetMasterManager::InitMaster(session_params_t& params) { - jack_log ( "JackNetMasterManager::InitMaster, Slave : %s", params.fName ); + jack_log("JackNetMasterManager::InitMaster, Slave : %s", params.fName); //check MASTER <<==> SLAVE network protocol coherency if (params.fProtocolVersion != MASTER_PROTOCOL) { - jack_error ( "Error : slave is running with a different protocol %s", params.fName ); + jack_error("Error : slave %s is running with a different protocol %d != %d", params.fName, params.fProtocolVersion, MASTER_PROTOCOL); return NULL; } //settings - fSocket.GetName ( params.fMasterNetName ); + fSocket.GetName(params.fMasterNetName); params.fID = ++fGlobalID; - params.fSampleRate = jack_get_sample_rate ( fManagerClient ); - params.fPeriodSize = jack_get_buffer_size ( fManagerClient ); - params.fBitdepth = 0; - SetSlaveName ( params ); + params.fSampleRate = jack_get_sample_rate(fManagerClient); + params.fPeriodSize = jack_get_buffer_size(fManagerClient); + + 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); + } //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; return NULL; } - void JackNetMasterManager::SetSlaveName ( session_params_t& params ) - { - jack_log ( "JackNetMasterManager::SetSlaveName" ); - - master_list_it_t it; - for ( it = fMasterList.begin(); it != fMasterList.end(); it++ ) - if ( strcmp ( ( *it )->fParams.fName, params.fName ) == 0 ) - sprintf ( params.fName, "%s-%u", params.fName, params.fID ); - } - - master_list_it_t JackNetMasterManager::FindMaster ( uint32_t id ) + master_list_it_t JackNetMasterManager::FindMaster(uint32_t id) { - jack_log ( "JackNetMasterManager::FindMaster, ID %u.", id ); + jack_log("JackNetMasterManager::FindMaster ID = %u", id); master_list_it_t it; - for ( it = fMasterList.begin(); it != fMasterList.end(); it++ ) - if ( ( *it )->fParams.fID == id ) + for (it = fMasterList.begin(); it != fMasterList.end(); it++) { + if ((*it)->fParams.fID == id) { return it; + } + } return it; } - int JackNetMasterManager::KillMaster ( session_params_t* params ) + int JackNetMasterManager::KillMaster(session_params_t* params) { - jack_log ( "JackNetMasterManager::KillMaster, ID %u.", params->fID ); + jack_log("JackNetMasterManager::KillMaster ID = %u", params->fID); - master_list_it_t master = FindMaster ( params->fID ); - if ( master != fMasterList.end() ) - { - fMasterList.erase ( master ); + master_list_it_t master = FindMaster(params->fID); + if (master != fMasterList.end()) { + fMasterList.erase(master); delete *master; return 1; } @@ -727,84 +823,64 @@ extern "C" SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() { - jack_driver_desc_t *desc; - desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) ); - - strcpy ( desc->name, "netmanager" ); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 - strcpy ( desc->desc, "netjack multi-cast master component" ); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 - - desc->nparams = 3; - desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); - - int i = 0; - strcpy ( desc->params[i].name, "multicast_ip" ); - desc->params[i].character = 'a'; - desc->params[i].type = JackDriverParamString; - strcpy ( desc->params[i].value.str, DEFAULT_MULTICAST_IP ); - strcpy ( desc->params[i].short_desc, "Multicast Address" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "udp_net_port" ); - desc->params[i].character = 'p'; - desc->params[i].type = JackDriverParamInt; - desc->params[i].value.i = DEFAULT_PORT; - strcpy ( desc->params[i].short_desc, "UDP port" ); - strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); - - i++; - strcpy ( desc->params[i].name, "auto_connect" ); - desc->params[i].character = 'c'; - desc->params[i].type = JackDriverParamBool; - 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 ); + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("netmanager", JackDriverNone, "netjack multi-cast master component", &filler); + + strcpy(value.str, DEFAULT_MULTICAST_IP); + jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast Address", NULL); + + value.i = DEFAULT_PORT; + jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect netmaster to system ports", NULL); return desc; } - SERVER_EXPORT int jack_internal_initialize ( jack_client_t* jack_client, const JSList* params ) + SERVER_EXPORT int jack_internal_initialize(jack_client_t* jack_client, const JSList* params) { - if ( master_manager ) - { - jack_error ( "Master Manager already loaded" ); + if (master_manager) { + jack_error("Master Manager already loaded"); return 1; - } - else - { - jack_log ( "Loading Master Manager" ); - master_manager = new Jack::JackNetMasterManager ( jack_client, params ); - return ( master_manager ) ? 0 : 1; + } else { + jack_log("Loading Master Manager"); + master_manager = new Jack::JackNetMasterManager(jack_client, params); + return (master_manager) ? 0 : 1; } } - SERVER_EXPORT int jack_initialize ( jack_client_t* jack_client, const char* load_init ) + SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); - Jack::JackArgParser parser ( load_init ); - if ( parser.GetArgc() > 0 ) - parse_params = parser.ParseParams ( desc, ¶ms ); + Jack::JackArgParser parser(load_init); + if (parser.GetArgc() > 0) { + parse_params = parser.ParseParams(desc, ¶ms); + } if (parse_params) { - res = jack_internal_initialize ( jack_client, params ); - parser.FreeParams ( params ); + res = jack_internal_initialize(jack_client, params); + parser.FreeParams(params); } return res; } - SERVER_EXPORT void jack_finish ( void* arg ) + SERVER_EXPORT void jack_finish(void* arg) { - if ( master_manager ) - { - jack_log ( "Unloading Master Manager" ); + if (master_manager) { + jack_log ("Unloading Master Manager"); delete master_manager; master_manager = NULL; } } + #ifdef __cplusplus } #endif diff --git a/common/JackNetManager.h b/common/JackNetManager.h index cbeaee9e..60d53b10 100644 --- a/common/JackNetManager.h +++ b/common/JackNetManager.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2008 Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -37,10 +37,12 @@ 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 ); + + 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); //jack client jack_client_t* fJackClient; @@ -70,12 +72,13 @@ namespace Jack void DecodeTransportData(); int Process(); - void TimebaseCallback ( jack_position_t* pos ); + void TimebaseCallback(jack_position_t* pos); void ConnectPorts(); public: - JackNetMaster ( JackNetSocket& socket, session_params_t& params, const char* multicast_ip); - ~JackNetMaster (); + + JackNetMaster(JackNetSocket& socket, session_params_t& params, const char* multicast_ip); + ~JackNetMaster(); bool IsSlaveReadyToRoll(); }; @@ -90,9 +93,11 @@ 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 ); + + static int SetSyncCallback(jack_transport_state_t state, jack_position_t* pos, void* arg); + static void* NetManagerThread(void* arg); jack_client_t* fManagerClient; const char* fManagerName; @@ -105,14 +110,15 @@ namespace Jack bool fAutoConnect; void Run(); - JackNetMaster* InitMaster ( session_params_t& params ); - master_list_it_t FindMaster ( uint32_t client_id ); - int KillMaster ( session_params_t* params ); - void SetSlaveName ( session_params_t& params ); + JackNetMaster* InitMaster(session_params_t& params); + master_list_it_t FindMaster(uint32_t client_id); + int KillMaster(session_params_t* params); + int SyncCallback(jack_transport_state_t state, jack_position_t* pos); + int CountIO(int flags); - int SyncCallback ( jack_transport_state_t state, jack_position_t* pos ); public: - JackNetMasterManager ( jack_client_t* jack_client, const JSList* params); + + JackNetMasterManager(jack_client_t* jack_client, const JSList* params); ~JackNetMasterManager(); }; } diff --git a/common/JackNetOneDriver.cpp b/common/JackNetOneDriver.cpp index 93adecdd..ca012864 100644 --- a/common/JackNetOneDriver.cpp +++ b/common/JackNetOneDriver.cpp @@ -1,6 +1,5 @@ /* -Copyright (C) 2001 Paul Davis -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Torben Horn 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 @@ -23,6 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackNetOneDriver.h" #include "JackEngineControl.h" +#include "JackLockedEngine.h" #include "JackGraphManager.h" #include "JackWaitThreadedDriver.h" #include "JackTools.h" @@ -32,7 +32,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "netjack_packet.h" #if HAVE_SAMPLERATE -#include "samplerate.h" +#include #endif #if HAVE_CELT @@ -45,974 +45,804 @@ using namespace std; namespace Jack { - JackNetOneDriver::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 ) - : JackAudioDriver ( name, alias, engine, table ) - { - jack_log ( "JackNetOneDriver::JackNetOneDriver port %d", port ); - - #ifdef WIN32 - WSADATA wsa; - int rc = WSAStartup(MAKEWORD(2,0),&wsa); - #endif - - netjack_init( & (this->netj), - NULL, // client - name, - capture_ports, - playback_ports, - midi_input_ports, - midi_output_ports, - sample_rate, - period_size, - port, - transport_sync, - resample_factor, - 0, - bitdepth, - use_autoconfig, - latency, - redundancy, - dont_htonl_floats, - always_deadline, - jitter_val); - } +JackNetOneDriver::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) + : JackWaiterDriver(name, alias, engine, table) +{ + jack_log("JackNetOneDriver::JackNetOneDriver port %d", port); - JackNetOneDriver::~JackNetOneDriver() - { - // No destructor yet. - } +#ifdef WIN32 + WSADATA wsa; + int rc = WSAStartup(MAKEWORD(2, 0), &wsa); +#endif -//open, close, attach and detach------------------------------------------------------ - int JackNetOneDriver::Open ( jack_nframes_t buffer_size, jack_nframes_t samplerate, 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 ) - { - if ( JackAudioDriver::Open ( buffer_size, - samplerate, - capturing, - playing, - inchannels, - outchannels, - monitor, - capture_driver_name, - playback_driver_name, - capture_latency, - playback_latency ) == 0 ) - { - fEngineControl->fPeriod = 0; - fEngineControl->fComputation = 500 * 1000; - fEngineControl->fConstraint = 500 * 1000; - return 0; - } - else - { - jack_error( "open fail" ); - return -1; - } - } + netjack_init(& (this->netj), + NULL, // client + name, + capture_ports, + playback_ports, + midi_input_ports, + midi_output_ports, + sample_rate, + period_size, + port, + transport_sync, + resample_factor, + 0, + bitdepth, + use_autoconfig, + latency, + redundancy, + dont_htonl_floats, + always_deadline, + jitter_val); +} - int JackNetOneDriver::Close() - { - FreePorts(); - netjack_release( &netj ); - return JackDriver::Close(); - } +JackNetOneDriver::~JackNetOneDriver() +{ + // No destructor yet. +} - int JackNetOneDriver::Attach() - { - return 0; - } +//open, close, attach and detach------------------------------------------------------ - int JackNetOneDriver::Detach() - { - return 0; - } +int JackNetOneDriver::Close() +{ + // Generic audio driver close + int res = JackWaiterDriver::Close(); - int JackNetOneDriver::AllocPorts() - { - jack_port_id_t port_id; - char buf[64]; - unsigned int chn; + FreePorts(); + netjack_release(&netj); + return res; +} - //if (netj.handle_transport_sync) - // jack_set_sync_callback(netj.client, (JackSyncCallback) net_driver_sync_cb, NULL); +int JackNetOneDriver::Attach() +{ + return 0; +} - for (chn = 0; chn < netj.capture_channels_audio; chn++) { - snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); +int JackNetOneDriver::Detach() +{ + return 0; +} - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, - CaptureDriverFlags, fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", buf ); - return -1; - } - //port = fGraphManager->GetPort ( port_id ); - - netj.capture_ports = jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_id); - - if( netj.bitdepth == CELT_MODE ) { - #if HAVE_CELT - #if HAVE_CELT_API_0_11 - celt_int32 lookahead; - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, netj.period_size, NULL ); - netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create_custom( celt_mode, 1, NULL ) ); - #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 - celt_int32 lookahead; - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, netj.period_size, NULL ); - netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) ); - #else - celt_int32_t lookahead; - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); - netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create( celt_mode ) ); - #endif - celt_mode_info( celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); - netj.codec_latency = 2*lookahead; - #endif - } else { - #if HAVE_SAMPLERATE - netj.capture_srcs = jack_slist_append(netj.capture_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); - #endif - } - } - for (chn = netj.capture_channels_audio; chn < netj.capture_channels; chn++) { - snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); +int JackNetOneDriver::AllocPorts() +{ + jack_port_id_t port_index; + char buf[64]; + unsigned int chn; - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, - CaptureDriverFlags, fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", buf ); - return -1; - } - //port = fGraphManager->GetPort ( port_id ); + //if (netj.handle_transport_sync) + // jack_set_sync_callback(netj.client, (JackSyncCallback) net_driver_sync_cb, NULL); - netj.capture_ports = - jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_id); + for (chn = 0; chn < netj.capture_channels_audio; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); + + if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, + CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", buf); + return -1; } + //port = fGraphManager->GetPort(port_index); - for (chn = 0; chn < netj.playback_channels_audio; chn++) { - snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); + netj.capture_ports = jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_index); - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, - PlaybackDriverFlags, fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", buf ); - return -1; - } - //port = fGraphManager->GetPort ( port_id ); - - netj.playback_ports = jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_id); - if( netj.bitdepth == CELT_MODE ) { - #if HAVE_CELT - #if HAVE_CELT_API_0_11 - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, netj.period_size, NULL ); - netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create_custom( celt_mode, 1, NULL ) ); - #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, netj.period_size, NULL ); - netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); - #else - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); - netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create( celt_mode ) ); - #endif - #endif - } else { - #if HAVE_SAMPLERATE - netj.playback_srcs = jack_slist_append(netj.playback_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); - #endif - } + if (netj.bitdepth == CELT_MODE) { +#if HAVE_CELT +#if HAVE_CELT_API_0_11 + celt_int32 lookahead; + CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); + netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create_custom(celt_mode, 1, NULL)); +#elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 + celt_int32 lookahead; + CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); + netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create(celt_mode, 1, NULL)); +#else + celt_int32_t lookahead; + CELTMode *celt_mode = celt_mode_create(netj.sample_rate, 1, netj.period_size, NULL); + netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create(celt_mode)); +#endif + celt_mode_info(celt_mode, CELT_GET_LOOKAHEAD, &lookahead); + netj.codec_latency = 2 * lookahead; +#endif + } else { +#if HAVE_SAMPLERATE + netj.capture_srcs = jack_slist_append(netj.capture_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); +#endif } - for (chn = netj.playback_channels_audio; chn < netj.playback_channels; chn++) { - snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); + } - if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, - PlaybackDriverFlags, fEngineControl->fBufferSize ) ) == NO_PORT ) - { - jack_error ( "driver: cannot register port for %s", buf ); - return -1; - } - //port = fGraphManager->GetPort ( port_id ); + for (chn = netj.capture_channels_audio; chn < netj.capture_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); - netj.playback_ports = - jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_id); + if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", buf); + return -1; } - return 0; + //port = fGraphManager->GetPort(port_index); + + netj.capture_ports = + jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_index); } -//init and restart-------------------------------------------------------------------- - bool JackNetOneDriver::Initialize() - { - jack_log ( "JackNetOneDriver::Init()" ); + for (chn = 0; chn < netj.playback_channels_audio; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); - FreePorts(); - netjack_release( &netj ); + if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, + PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", buf); + return -1; + } + //port = fGraphManager->GetPort(port_index); - //display some additional infos - jack_info ( "NetOne driver started" ); - if( netjack_startup( &netj ) ) { - return false; + netj.playback_ports = jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_index); + if (netj.bitdepth == CELT_MODE) { +#if HAVE_CELT +#if HAVE_CELT_API_0_11 + CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); + netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create_custom(celt_mode, 1, NULL)); +#elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 + CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); + netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create(celt_mode, 1, NULL)); +#else + CELTMode *celt_mode = celt_mode_create(netj.sample_rate, 1, netj.period_size, NULL); + netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create(celt_mode)); +#endif +#endif + } else { +#if HAVE_SAMPLERATE + netj.playback_srcs = jack_slist_append(netj.playback_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); +#endif } + } + for (chn = netj.playback_channels_audio; chn < netj.playback_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); - //register jack ports - if ( AllocPorts() != 0 ) - { - jack_error ( "Can't allocate ports." ); - return false; + if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("driver: cannot register port for %s", buf); + return -1; } + //port = fGraphManager->GetPort(port_index); + + netj.playback_ports = + jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_index); + } + return 0; +} - //monitor - //driver parametering - JackAudioDriver::SetBufferSize ( netj.period_size ); - JackAudioDriver::SetSampleRate ( netj.sample_rate ); +//init and restart-------------------------------------------------------------------- +bool JackNetOneDriver::Initialize() +{ + jack_log("JackNetOneDriver::Init"); - JackDriver::NotifyBufferSize ( netj.period_size ); - JackDriver::NotifySampleRate ( netj.sample_rate ); + FreePorts(); + netjack_release(&netj); - //transport engine parametering - fEngineControl->fTransport.SetNetworkSync ( true ); - return true; + //display some additional infos + jack_info("NetOne driver started"); + if (netjack_startup(&netj)) { + return false; } + //register jack ports + if (AllocPorts() != 0) { + jack_error("Can't allocate ports."); + return false; + } -//jack ports and buffers-------------------------------------------------------------- + //monitor + //driver parametering + JackTimedDriver::SetBufferSize(netj.period_size); + JackTimedDriver::SetSampleRate(netj.sample_rate); -//driver processes-------------------------------------------------------------------- - int JackNetOneDriver::Read() - { - int delay; - delay = netjack_wait( &netj ); - if( delay ) { - NotifyXRun(fBeginDateUst, (float) delay); - jack_error( "netxruns... duration: %dms", delay/1000 ); - } + JackDriver::NotifyBufferSize(netj.period_size); + JackDriver::NotifySampleRate(netj.sample_rate); - if( (netj.num_lost_packets * netj.period_size / netj.sample_rate) > 2 ) - JackTools::ThrowJackNetException(); + //transport engine parametering + fEngineControl->fTransport.SetNetworkSync(true); + return true; +} - //netjack_read( &netj, netj.period_size ); - JackDriver::CycleTakeBeginTime(); - jack_position_t local_trans_pos; - jack_transport_state_t local_trans_state; +//jack ports and buffers-------------------------------------------------------------- - unsigned int *packet_buf, *packet_bufX; +//driver processes-------------------------------------------------------------------- - if( ! netj.packet_data_valid ) { - jack_log( "data not valid" ); - render_payload_to_jack_ports (netj.bitdepth, NULL, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats ); - return 0; - } - packet_buf = netj.rx_buf; +int JackNetOneDriver::Read() +{ + int delay; + delay = netjack_wait(&netj); + if (delay) { + NotifyXRun(fBeginDateUst, (float) delay); + jack_error("netxruns... duration: %dms", delay / 1000); + } - jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; + if ((netj.num_lost_packets * netj.period_size / netj.sample_rate) > 2) + JackTools::ThrowJackNetException(); - packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); + //netjack_read(&netj, netj.period_size); + JackDriver::CycleTakeBeginTime(); - netj.reply_port = pkthdr->reply_port; - netj.latency = pkthdr->latency; + jack_position_t local_trans_pos; + jack_transport_state_t local_trans_state; - // Special handling for latency=0 - if( netj.latency == 0 ) - netj.resync_threshold = 0; - else - netj.resync_threshold = MIN( 15, pkthdr->latency-1 ); + unsigned int *packet_buf, *packet_bufX; - // check whether, we should handle the transport sync stuff, or leave trnasports untouched. - if (netj.handle_transport_sync) { - #if 1 - unsigned int compensated_tranport_pos = (pkthdr->transport_frame + (pkthdr->latency * netj.period_size) + netj.codec_latency); + if (! netj.packet_data_valid) { + jack_log("data not valid"); + render_payload_to_jack_ports (netj.bitdepth, NULL, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats); + return 0; + } + packet_buf = netj.rx_buf; - // read local transport info.... - //local_trans_state = jack_transport_query(netj.client, &local_trans_pos); + jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; - local_trans_state = fEngineControl->fTransport.Query ( &local_trans_pos ); + packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); - // Now check if we have to start or stop local transport to sync to remote... - switch (pkthdr->transport_state) { + netj.reply_port = pkthdr->reply_port; + netj.latency = pkthdr->latency; - case JackTransportStarting: - // the master transport is starting... so we set our reply to the sync_callback; - if (local_trans_state == JackTransportStopped) { - fEngineControl->fTransport.SetCommand ( TransportCommandStart ); - //jack_transport_start(netj.client); - //last_transport_state = JackTransportStopped; - netj.sync_state = 0; - jack_info("locally stopped... starting..."); - } + // Special handling for latency=0 + if (netj.latency == 0) + netj.resync_threshold = 0; + else + netj.resync_threshold = MIN(15, pkthdr->latency - 1); - if (local_trans_pos.frame != compensated_tranport_pos) { - jack_position_t new_pos = local_trans_pos; - new_pos.frame = compensated_tranport_pos + 2*netj.period_size; - new_pos.valid = (jack_position_bits_t) 0; + // check whether, we should handle the transport sync stuff, or leave trnasports untouched. + if (netj.handle_transport_sync) { +#if 1 + unsigned int compensated_tranport_pos = (pkthdr->transport_frame + (pkthdr->latency * netj.period_size) + netj.codec_latency); + // read local transport info.... + //local_trans_state = jack_transport_query(netj.client, &local_trans_pos); - fEngineControl->fTransport.RequestNewPos ( &new_pos ); - //jack_transport_locate(netj.client, compensated_tranport_pos); - //last_transport_state = JackTransportRolling; - netj.sync_state = 0; - jack_info("starting locate to %d", compensated_tranport_pos ); - } - break; + local_trans_state = fEngineControl->fTransport.Query(&local_trans_pos); - case JackTransportStopped: - netj.sync_state = 1; - if (local_trans_pos.frame != (pkthdr->transport_frame)) { - jack_position_t new_pos = local_trans_pos; - new_pos.frame = pkthdr->transport_frame; - new_pos.valid = (jack_position_bits_t)0; - fEngineControl->fTransport.RequestNewPos ( &new_pos ); - //jack_transport_locate(netj.client, (pkthdr->transport_frame)); - jack_info("transport is stopped locate to %d", pkthdr->transport_frame); - } - if (local_trans_state != JackTransportStopped) - //jack_transport_stop(netj.client); - fEngineControl->fTransport.SetCommand ( TransportCommandStop ); - break; + // Now check if we have to start or stop local transport to sync to remote... + switch (pkthdr->transport_state) { - case JackTransportRolling: - netj.sync_state = 1; - // if(local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * netj.period_size)) { - // jack_transport_locate(netj.client, (pkthdr->transport_frame + (pkthdr->latency + 2) * netj.period_size)); - // jack_info("running locate to %d", pkthdr->transport_frame + (pkthdr->latency)*netj.period_size); - // } - if (local_trans_state != JackTransportRolling) - fEngineControl->fTransport.SetState ( JackTransportRolling ); - break; + case JackTransportStarting: + // the master transport is starting... so we set our reply to the sync_callback; + if (local_trans_state == JackTransportStopped) { + fEngineControl->fTransport.SetCommand(TransportCommandStart); + //jack_transport_start(netj.client); + //last_transport_state = JackTransportStopped; + netj.sync_state = 0; + jack_info("locally stopped... starting..."); + } - case JackTransportLooping: - break; - } -#endif - } + if (local_trans_pos.frame != compensated_tranport_pos) { + jack_position_t new_pos = local_trans_pos; + new_pos.frame = compensated_tranport_pos + 2 * netj.period_size; + new_pos.valid = (jack_position_bits_t) 0; - render_payload_to_jack_ports (netj.bitdepth, packet_bufX, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats ); - packet_cache_release_packet(netj.packcache, netj.expected_framecnt ); - return 0; + + fEngineControl->fTransport.RequestNewPos(&new_pos); + //jack_transport_locate(netj.client, compensated_tranport_pos); + //last_transport_state = JackTransportRolling; + netj.sync_state = 0; + jack_info("starting locate to %d", compensated_tranport_pos); + } + break; + + case JackTransportStopped: + netj.sync_state = 1; + if (local_trans_pos.frame != (pkthdr->transport_frame)) { + jack_position_t new_pos = local_trans_pos; + new_pos.frame = pkthdr->transport_frame; + new_pos.valid = (jack_position_bits_t)0; + fEngineControl->fTransport.RequestNewPos(&new_pos); + //jack_transport_locate(netj.client, (pkthdr->transport_frame)); + jack_info("transport is stopped locate to %d", pkthdr->transport_frame); + } + if (local_trans_state != JackTransportStopped) + //jack_transport_stop(netj.client); + fEngineControl->fTransport.SetCommand(TransportCommandStop); + break; + + case JackTransportRolling: + netj.sync_state = 1; + // if(local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * netj.period_size)) { + // jack_transport_locate(netj.client, (pkthdr->transport_frame + (pkthdr->latency + 2) * netj.period_size)); + // jack_info("running locate to %d", pkthdr->transport_frame + (pkthdr->latency)*netj.period_size); + // } + if (local_trans_state != JackTransportRolling) + fEngineControl->fTransport.SetState(JackTransportRolling); + break; + + case JackTransportLooping: + break; + } +#endif } - int JackNetOneDriver::Write() - { - int syncstate = netj.sync_state | ((fEngineControl->fTransport.GetState() == JackTransportNetStarting) ? 1 : 0 ); - uint32_t *packet_buf, *packet_bufX; + render_payload_to_jack_ports (netj.bitdepth, packet_bufX, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats); + packet_cache_release_packet(netj.packcache, netj.expected_framecnt); + return 0; +} + +int JackNetOneDriver::Write() +{ + int syncstate = netj.sync_state | ((fEngineControl->fTransport.GetState() == JackTransportNetStarting) ? 1 : 0); + uint32_t *packet_buf, *packet_bufX; - int packet_size = get_sample_size(netj.bitdepth) * netj.playback_channels * netj.net_period_up + sizeof(jacknet_packet_header); - jacknet_packet_header *pkthdr; + int packet_size = get_sample_size(netj.bitdepth) * netj.playback_channels * netj.net_period_up + sizeof(jacknet_packet_header); + jacknet_packet_header *pkthdr; - packet_buf = (uint32_t *) alloca(packet_size); - pkthdr = (jacknet_packet_header *)packet_buf; + packet_buf = (uint32_t *) alloca(packet_size); + pkthdr = (jacknet_packet_header *)packet_buf; - if( netj.running_free ) { - return 0; - } + if (netj.running_free) { + return 0; + } - // offset packet_bufX by the packetheader. - packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); + // offset packet_bufX by the packetheader. + packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); - pkthdr->sync_state = syncstate;; - pkthdr->latency = netj.time_to_deadline; - //printf( "time to deadline = %d goodness=%d\n", (int)netj.time_to_deadline, netj.deadline_goodness ); - pkthdr->framecnt = netj.expected_framecnt; + pkthdr->sync_state = syncstate;; + pkthdr->latency = netj.time_to_deadline; + //printf("time to deadline = %d goodness=%d\n", (int)netj.time_to_deadline, netj.deadline_goodness); + pkthdr->framecnt = netj.expected_framecnt; - render_jack_ports_to_payload(netj.bitdepth, netj.playback_ports, netj.playback_srcs, netj.period_size, packet_bufX, netj.net_period_up, netj.dont_htonl_floats ); + render_jack_ports_to_payload(netj.bitdepth, netj.playback_ports, netj.playback_srcs, netj.period_size, packet_bufX, netj.net_period_up, netj.dont_htonl_floats); - packet_header_hton(pkthdr); - if (netj.srcaddress_valid) - { - unsigned int r; - static const int flag = 0; + packet_header_hton(pkthdr); + if (netj.srcaddress_valid) { + unsigned int r; + static const int flag = 0; - if (netj.reply_port) + if (netj.reply_port) netj.syncsource_address.sin_port = htons(netj.reply_port); - for( r=0; rdata; - node = jack_slist_remove_link( node, this_node ); - jack_slist_free_1( this_node ); - fGraphManager->ReleasePort( fClientControl.fRefNum, port_id ); - } - netj.capture_ports = NULL; - - node = netj.playback_ports; - while( node != NULL ) { - JSList *this_node = node; - jack_port_id_t port_id = (jack_port_id_t)(intptr_t) node->data; - node = jack_slist_remove_link( node, this_node ); - jack_slist_free_1( this_node ); - fGraphManager->ReleasePort( fClientControl.fRefNum, port_id ); - } - netj.playback_ports = NULL; +void +JackNetOneDriver::FreePorts () +{ + JSList *node = netj.capture_ports; + + while (node != NULL) { + JSList *this_node = node; + jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; + node = jack_slist_remove_link(node, this_node); + jack_slist_free_1(this_node); + fEngine->PortUnRegister(fClientControl.fRefNum, port_index); + } + netj.capture_ports = NULL; + + node = netj.playback_ports; + while (node != NULL) { + JSList *this_node = node; + jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; + node = jack_slist_remove_link(node, this_node); + jack_slist_free_1(this_node); + fEngine->PortUnRegister(fClientControl.fRefNum, port_index); + } + netj.playback_ports = NULL; - if( netj.bitdepth == CELT_MODE ) { - #if HAVE_CELT + if (netj.bitdepth == CELT_MODE) { +#if HAVE_CELT node = netj.playback_srcs; - while( node != NULL ) { + while (node != NULL) { JSList *this_node = node; CELTEncoder *enc = (CELTEncoder *) node->data; - node = jack_slist_remove_link( node, this_node ); - jack_slist_free_1( this_node ); - celt_encoder_destroy( enc ); + node = jack_slist_remove_link(node, this_node); + jack_slist_free_1(this_node); + celt_encoder_destroy(enc); } netj.playback_srcs = NULL; node = netj.capture_srcs; - while( node != NULL ) { + while (node != NULL) { JSList *this_node = node; CELTDecoder *dec = (CELTDecoder *) node->data; - node = jack_slist_remove_link( node, this_node ); - jack_slist_free_1( this_node ); - celt_decoder_destroy( dec ); + node = jack_slist_remove_link(node, this_node); + jack_slist_free_1(this_node); + celt_decoder_destroy(dec); } netj.capture_srcs = NULL; - #endif - } else { - #if HAVE_SAMPLERATE +#endif + } else { +#if HAVE_SAMPLERATE node = netj.playback_srcs; - while( node != NULL ) { + while (node != NULL) { JSList *this_node = node; SRC_STATE *state = (SRC_STATE *) node->data; - node = jack_slist_remove_link( node, this_node ); - jack_slist_free_1( this_node ); - src_delete( state ); + node = jack_slist_remove_link(node, this_node); + jack_slist_free_1(this_node); + src_delete(state); } netj.playback_srcs = NULL; node = netj.capture_srcs; - while( node != NULL ) { + while (node != NULL) { JSList *this_node = node; SRC_STATE *state = (SRC_STATE *) node->data; - node = jack_slist_remove_link( node, this_node ); - jack_slist_free_1( this_node ); - src_delete( state ); + node = jack_slist_remove_link(node, this_node); + jack_slist_free_1(this_node); + src_delete(state); } netj.capture_srcs = NULL; - #endif - } +#endif } +} //Render functions-------------------------------------------------------------------- // render functions for float - void - JackNetOneDriver::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) - { - uint32_t chn = 0; - JSList *node = capture_ports; - #if HAVE_SAMPLERATE - JSList *src_node = capture_srcs; - #endif +void +JackNetOneDriver::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) +{ + uint32_t chn = 0; + JSList *node = capture_ports; +#if HAVE_SAMPLERATE + JSList *src_node = capture_srcs; +#endif - uint32_t *packet_bufX = (uint32_t *)packet_payload; + uint32_t *packet_bufX = (uint32_t *)packet_payload; - if( !packet_payload ) - return; + if (!packet_payload) + return; - while (node != NULL) - { - unsigned int i; - int_float_t val; - #if HAVE_SAMPLERATE - SRC_DATA src; - #endif - jack_port_id_t port_id = (jack_port_id_t)(intptr_t) node->data; - JackPort *port = fGraphManager->GetPort( port_id ); + while (node != NULL) { + unsigned int i; + int_float_t val; +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; + JackPort *port = fGraphManager->GetPort(port_index); - jack_default_audio_sample_t* buf = - (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); - const char *porttype = port->GetType(); + const char *porttype = port->GetType(); - if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) - { - #if HAVE_SAMPLERATE - // audio port, resample if necessary - if (net_period_down != nframes) - { - SRC_STATE *src_state = (SRC_STATE *)src_node->data; - for (i = 0; i < net_period_down; i++) - { - packet_bufX[i] = ntohl (packet_bufX[i]); - } + if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { +#if HAVE_SAMPLERATE + // audio port, resample if necessary + if (net_period_down != nframes) { + SRC_STATE *src_state = (SRC_STATE *)src_node->data; + for (i = 0; i < net_period_down; i++) { + packet_bufX[i] = ntohl (packet_bufX[i]); + } - src.data_in = (float *) packet_bufX; - src.input_frames = net_period_down; + src.data_in = (float *) packet_bufX; + src.input_frames = net_period_down; - src.data_out = buf; - src.output_frames = nframes; + src.data_out = buf; + src.output_frames = nframes; - src.src_ratio = (float) nframes / (float) net_period_down; - src.end_of_input = 0; + src.src_ratio = (float) nframes / (float) net_period_down; + src.end_of_input = 0; - src_set_ratio (src_state, src.src_ratio); - src_process (src_state, &src); - src_node = jack_slist_next (src_node); - } - else - #endif - { - if( dont_htonl_floats ) - { - memcpy( buf, packet_bufX, net_period_down*sizeof(jack_default_audio_sample_t)); - } - else - { - for (i = 0; i < net_period_down; i++) - { - val.i = packet_bufX[i]; - val.i = ntohl (val.i); - buf[i] = val.f; - } + src_set_ratio (src_state, src.src_ratio); + src_process (src_state, &src); + src_node = jack_slist_next (src_node); + } else +#endif + { + if (dont_htonl_floats) { + memcpy(buf, packet_bufX, net_period_down * sizeof(jack_default_audio_sample_t)); + } else { + for (i = 0; i < net_period_down; i++) { + val.i = packet_bufX[i]; + val.i = ntohl (val.i); + buf[i] = val.f; } } } - else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) - { - // midi port, decode midi events - // convert the data buffer to a standard format (uint32_t based) - unsigned int buffer_size_uint32 = net_period_down; - uint32_t * buffer_uint32 = (uint32_t*)packet_bufX; - decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); - } - packet_bufX = (packet_bufX + net_period_down); - node = jack_slist_next (node); - chn++; + } else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down; + uint32_t * buffer_uint32 = (uint32_t*)packet_bufX; + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } } - void - JackNetOneDriver::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 ) - { - uint32_t chn = 0; - JSList *node = playback_ports; - #if HAVE_SAMPLERATE - JSList *src_node = playback_srcs; - #endif - - uint32_t *packet_bufX = (uint32_t *) packet_payload; - - while (node != NULL) - { - #if HAVE_SAMPLERATE - SRC_DATA src; - #endif - unsigned int i; - int_float_t val; - jack_port_id_t port_id = (jack_port_id_t)(intptr_t) node->data; - JackPort *port = fGraphManager->GetPort( port_id ); - - jack_default_audio_sample_t* buf = - (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); - - const char *porttype = port->GetType(); - - if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) - { - // audio port, resample if necessary +void +JackNetOneDriver::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) +{ + uint32_t chn = 0; + JSList *node = playback_ports; +#if HAVE_SAMPLERATE + JSList *src_node = playback_srcs; +#endif - #if HAVE_SAMPLERATE - if (net_period_up != nframes) { - SRC_STATE *src_state = (SRC_STATE *) src_node->data; - src.data_in = buf; - src.input_frames = nframes; + uint32_t *packet_bufX = (uint32_t *) packet_payload; - src.data_out = (float *) packet_bufX; - src.output_frames = net_period_up; + while (node != NULL) { +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + unsigned int i; + int_float_t val; + jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; + JackPort *port = fGraphManager->GetPort(port_index); - src.src_ratio = (float) net_period_up / (float) nframes; - src.end_of_input = 0; + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); - src_set_ratio (src_state, src.src_ratio); - src_process (src_state, &src); + const char *porttype = port->GetType(); - for (i = 0; i < net_period_up; i++) - { - packet_bufX[i] = htonl (packet_bufX[i]); - } - src_node = jack_slist_next (src_node); + if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { + // audio port, resample if necessary + +#if HAVE_SAMPLERATE + if (net_period_up != nframes) { + SRC_STATE *src_state = (SRC_STATE *) src_node->data; + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = (float *) packet_bufX; + src.output_frames = net_period_up; + + src.src_ratio = (float) net_period_up / (float) nframes; + src.end_of_input = 0; + + src_set_ratio (src_state, src.src_ratio); + src_process (src_state, &src); + + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = htonl (packet_bufX[i]); } - else - #endif - { - if( dont_htonl_floats ) - { - memcpy( packet_bufX, buf, net_period_up*sizeof(jack_default_audio_sample_t) ); - } - else - { - for (i = 0; i < net_period_up; i++) - { - val.f = buf[i]; - val.i = htonl (val.i); - packet_bufX[i] = val.i; - } + src_node = jack_slist_next (src_node); + } else +#endif + { + if (dont_htonl_floats) { + memcpy(packet_bufX, buf, net_period_up * sizeof(jack_default_audio_sample_t)); + } else { + for (i = 0; i < net_period_up; i++) { + val.f = buf[i]; + val.i = htonl (val.i); + packet_bufX[i] = val.i; } } } - else if (strncmp(porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) - { - // encode midi events from port to packet - // convert the data buffer to a standard format (uint32_t based) - unsigned int buffer_size_uint32 = net_period_up; - uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; - encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); - } - packet_bufX = (packet_bufX + net_period_up); - node = jack_slist_next (node); - chn++; + } else if (strncmp(porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; } +} - #if HAVE_CELT - // render functions for celt. - void - JackNetOneDriver::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) - { - uint32_t chn = 0; - JSList *node = capture_ports; - JSList *src_node = capture_srcs; - unsigned char *packet_bufX = (unsigned char *)packet_payload; +#if HAVE_CELT +// render functions for celt. +void +JackNetOneDriver::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) +{ + uint32_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + unsigned char *packet_bufX = (unsigned char *)packet_payload; - while (node != NULL) - { - jack_port_id_t port_id = (jack_port_id_t) (intptr_t)node->data; - JackPort *port = fGraphManager->GetPort( port_id ); + while (node != NULL) { + jack_port_id_t port_index = (jack_port_id_t) (intptr_t)node->data; + JackPort *port = fGraphManager->GetPort(port_index); - jack_default_audio_sample_t* buf = - (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); - const char *portname = port->GetType(); + const char *portname = port->GetType(); - if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) - { - // audio port, decode celt data. - CELTDecoder *decoder = (CELTDecoder *)src_node->data; - - #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 - if( !packet_payload ) - celt_decode_float( decoder, NULL, net_period_down, buf, nframes ); - else - celt_decode_float( decoder, packet_bufX, net_period_down, buf, nframes ); - #else - if( !packet_payload ) - celt_decode_float( decoder, NULL, net_period_down, buf ); - else - celt_decode_float( decoder, packet_bufX, net_period_down, buf ); - #endif + if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { + // audio port, decode celt data. + CELTDecoder *decoder = (CELTDecoder *)src_node->data; - src_node = jack_slist_next (src_node); - } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) - { - // midi port, decode midi events - // convert the data buffer to a standard format (uint32_t based) - unsigned int buffer_size_uint32 = net_period_down / 2; - uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; - if( packet_payload ) +#if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 + if (!packet_payload) + celt_decode_float(decoder, NULL, net_period_down, buf, nframes); + else + celt_decode_float(decoder, packet_bufX, net_period_down, buf, nframes); +#else + if (!packet_payload) + celt_decode_float(decoder, NULL, net_period_down, buf); + else + celt_decode_float(decoder, packet_bufX, net_period_down, buf); +#endif + + src_node = jack_slist_next (src_node); + } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + if (packet_payload) decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); - } - packet_bufX = (packet_bufX + net_period_down); - node = jack_slist_next (node); - chn++; } + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; } +} - void - JackNetOneDriver::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) - { - uint32_t chn = 0; - JSList *node = playback_ports; - JSList *src_node = playback_srcs; +void +JackNetOneDriver::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) +{ + uint32_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; - unsigned char *packet_bufX = (unsigned char *)packet_payload; + unsigned char *packet_bufX = (unsigned char *)packet_payload; - while (node != NULL) - { - jack_port_id_t port_id = (jack_port_id_t) (intptr_t) node->data; - JackPort *port = fGraphManager->GetPort( port_id ); + while (node != NULL) { + jack_port_id_t port_index = (jack_port_id_t) (intptr_t) node->data; + JackPort *port = fGraphManager->GetPort(port_index); - jack_default_audio_sample_t* buf = - (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); - const char *portname = port->GetType(); + const char *portname = port->GetType(); - if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) - { - // audio port, encode celt data. + if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { + // audio port, encode celt data. int encoded_bytes; - jack_default_audio_sample_t *floatbuf = (jack_default_audio_sample_t *)alloca (sizeof(jack_default_audio_sample_t) * nframes ); - memcpy( floatbuf, buf, nframes * sizeof(jack_default_audio_sample_t) ); + jack_default_audio_sample_t *floatbuf = (jack_default_audio_sample_t *)alloca (sizeof(jack_default_audio_sample_t) * nframes); + memcpy(floatbuf, buf, nframes * sizeof(jack_default_audio_sample_t)); CELTEncoder *encoder = (CELTEncoder *)src_node->data; - #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 - encoded_bytes = celt_encode_float( encoder, floatbuf, nframes, packet_bufX, net_period_up ); +#if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 + encoded_bytes = celt_encode_float(encoder, floatbuf, nframes, packet_bufX, net_period_up); #else - encoded_bytes = celt_encode_float( encoder, floatbuf, NULL, packet_bufX, net_period_up ); + encoded_bytes = celt_encode_float(encoder, floatbuf, NULL, packet_bufX, net_period_up); #endif - if( encoded_bytes != (int)net_period_up ) - jack_error( "something in celt changed. netjack needs to be changed to handle this." ); - src_node = jack_slist_next( src_node ); - } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) - { - // encode midi events from port to packet - // convert the data buffer to a standard format (uint32_t based) - unsigned int buffer_size_uint32 = net_period_up / 2; - uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; - encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); - } - packet_bufX = (packet_bufX + net_period_up); - node = jack_slist_next (node); - chn++; + if (encoded_bytes != (int)net_period_up) + jack_error("something in celt changed. netjack needs to be changed to handle this."); + src_node = jack_slist_next(src_node); + } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; } +} - #endif - /* Wrapper functions with bitdepth argument... */ - void - JackNetOneDriver::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) - { - #if HAVE_CELT - if (bitdepth == CELT_MODE) - render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); - else - #endif - render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); - } +#endif +/* Wrapper functions with bitdepth argument... */ +void +JackNetOneDriver::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) +{ +#if HAVE_CELT + if (bitdepth == CELT_MODE) + render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); + else +#endif + render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); +} + +void +JackNetOneDriver::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) +{ +#if HAVE_CELT + if (bitdepth == CELT_MODE) + render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); + else +#endif + render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); +} - void - JackNetOneDriver::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) +//driver loader----------------------------------------------------------------------- + +#ifdef __cplusplus +extern "C" +{ +#endif + SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor () { - #if HAVE_CELT - if (bitdepth == CELT_MODE) - render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); - else - #endif - render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); - } + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; - //driver loader----------------------------------------------------------------------- - - #ifdef __cplusplus - extern "C" - { - #endif - SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor () - { - jack_driver_desc_t* desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) ); - jack_driver_param_desc_t * params; - - strcpy ( desc->name, "netone" ); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 - strcpy ( desc->desc, "netjack one slave backend component" ); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 - - desc->nparams = 18; - params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); - - int i = 0; - strcpy (params[i].name, "audio-ins"); - params[i].character = 'i'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 2U; - strcpy (params[i].short_desc, "Number of capture channels (defaults to 2)"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "audio-outs"); - params[i].character = 'o'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 2U; - strcpy (params[i].short_desc, "Number of playback channels (defaults to 2)"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "midi-ins"); - params[i].character = 'I'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 1U; - strcpy (params[i].short_desc, "Number of midi capture channels (defaults to 1)"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "midi-outs"); - params[i].character = 'O'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 1U; - strcpy (params[i].short_desc, "Number of midi playback channels (defaults to 1)"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "rate"); - params[i].character = 'r'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 48000U; - strcpy (params[i].short_desc, "Sample rate"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "period"); - params[i].character = 'p'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 1024U; - strcpy (params[i].short_desc, "Frames per period"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "num-periods"); - params[i].character = 'n'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 5U; - strcpy (params[i].short_desc, - "Network latency setting in no. of periods"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "listen-port"); - params[i].character = 'l'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 3000U; - strcpy (params[i].short_desc, - "The socket port we are listening on for sync packets"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "factor"); - params[i].character = 'f'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 1U; - strcpy (params[i].short_desc, - "Factor for sample rate reduction"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "upstream-factor"); - params[i].character = 'u'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 0U; - strcpy (params[i].short_desc, - "Factor for sample rate reduction on the upstream"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "celt"); - params[i].character = 'c'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 0U; - strcpy (params[i].short_desc, - "sets celt encoding and number of kbits per channel"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "bit-depth"); - params[i].character = 'b'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 0U; - strcpy (params[i].short_desc, - "Sample bit-depth (0 for float, 8 for 8bit and 16 for 16bit)"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "transport-sync"); - params[i].character = 't'; - params[i].type = JackDriverParamBool; - params[i].value.ui = 1U; - strcpy (params[i].short_desc, - "Whether to slave the transport to the master transport"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "autoconf"); - params[i].character = 'a'; - params[i].type = JackDriverParamBool; - params[i].value.ui = 1U; - strcpy (params[i].short_desc, - "Whether to use Autoconfig, or just start."); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "redundancy"); - params[i].character = 'R'; - params[i].type = JackDriverParamUInt; - params[i].value.ui = 1U; - strcpy (params[i].short_desc, - "Send packets N times"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "native-endian"); - params[i].character = 'e'; - params[i].type = JackDriverParamBool; - params[i].value.ui = 0U; - strcpy (params[i].short_desc, - "Dont convert samples to network byte order."); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "jitterval"); - params[i].character = 'J'; - params[i].type = JackDriverParamInt; - params[i].value.i = 0; - strcpy (params[i].short_desc, - "attempted jitterbuffer microseconds on master"); - strcpy (params[i].long_desc, params[i].short_desc); - - i++; - strcpy (params[i].name, "always-deadline"); - params[i].character = 'D'; - params[i].type = JackDriverParamBool; - params[i].value.ui = 0U; - strcpy (params[i].short_desc, - "always use deadline"); - strcpy (params[i].long_desc, params[i].short_desc); - - desc->params = params; - - return desc; - } + desc = jack_driver_descriptor_construct("netone", JackDriverMaster, "netjack one slave backend component", &filler); - SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize ( Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params ) - { - jack_nframes_t sample_rate = 48000; - jack_nframes_t resample_factor = 1; - jack_nframes_t period_size = 1024; - unsigned int capture_ports = 2; - unsigned int playback_ports = 2; - unsigned int capture_ports_midi = 1; - unsigned int playback_ports_midi = 1; - unsigned int listen_port = 3000; - unsigned int bitdepth = 0; - unsigned int handle_transport_sync = 1; - unsigned int use_autoconfig = 1; - unsigned int latency = 5; - unsigned int redundancy = 1; - unsigned int mtu = 1400; - #if HAVE_SAMPLERATE - unsigned int resample_factor_up = 1; - #endif - int dont_htonl_floats = 0; - int always_deadline = 0; - int jitter_val = 0; - const JSList * node; - const jack_driver_param_t * param; - - for ( node = params; node; node = jack_slist_next ( node ) ) - { - param = ( const jack_driver_param_t* ) node->data; - switch ( param->character ) - { + value.ui = 2U; + jack_driver_descriptor_add_parameter(desc, &filler, "audio-ins", 'i', JackDriverParamUInt, &value, NULL, "Number of capture channels (defaults to 2)", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "audio-outs", 'o', JackDriverParamUInt, &value, NULL, "Number of playback channels (defaults to 2)", NULL); + + value.ui = 1U; + jack_driver_descriptor_add_parameter(desc, &filler, "midi-ins", 'I', JackDriverParamUInt, &value, NULL, "Number of midi capture channels (defaults to 1)", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "midi-outs", 'O', JackDriverParamUInt, &value, NULL, "Number of midi playback channels (defaults to 1)", NULL); + + value.ui = 48000U; + jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); + + value.ui = 1024U; + jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); + + value.ui = 5U; + jack_driver_descriptor_add_parameter(desc, &filler, "num-periods", 'n', JackDriverParamUInt, &value, NULL, "Network latency setting in no. of periods", NULL); + + value.ui = 3000U; + jack_driver_descriptor_add_parameter(desc, &filler, "listen-port", 'l', JackDriverParamUInt, &value, NULL, "The socket port we are listening on for sync packets", NULL); + + value.ui = 1U; + jack_driver_descriptor_add_parameter(desc, &filler, "factor", 'f', JackDriverParamUInt, &value, NULL, "Factor for sample rate reduction", NULL); + + value.ui = 0U; + jack_driver_descriptor_add_parameter(desc, &filler, "upstream-factor", 'u', JackDriverParamUInt, &value, NULL, "Factor for sample rate reduction on the upstream", NULL); + +#if HAVE_CELT + value.ui = 0U; + jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamUInt, &value, NULL, "Set CELT encoding and number of kbits per channel", NULL); +#endif + value.ui = 0U; + jack_driver_descriptor_add_parameter(desc, &filler, "bit-depth", 'b', JackDriverParamUInt, &value, NULL, "Sample bit-depth (0 for float, 8 for 8bit and 16 for 16bit)", NULL); + + value.i = true; + jack_driver_descriptor_add_parameter(desc, &filler, "transport-sync", 't', JackDriverParamBool, &value, NULL, "Whether to slave the transport to the master transport", NULL); + + value.ui = true; + jack_driver_descriptor_add_parameter(desc, &filler, "autoconf", 'a', JackDriverParamBool, &value, NULL, "Whether to use Autoconfig, or just start", NULL); + + value.ui = 1U; + jack_driver_descriptor_add_parameter(desc, &filler, "redundancy", 'R', JackDriverParamUInt, &value, NULL, "Send packets N times", NULL); + + value.ui = false; + jack_driver_descriptor_add_parameter(desc, &filler, "native-endian", 'e', JackDriverParamBool, &value, NULL, "Dont convert samples to network byte order", NULL); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "jitterval", 'J', JackDriverParamInt, &value, NULL, "Attempted jitterbuffer microseconds on master", NULL); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "always-deadline", 'D', JackDriverParamBool, &value, NULL, "Always use deadline", NULL); + + return desc; + } + + SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) + { + jack_nframes_t sample_rate = 48000; + jack_nframes_t resample_factor = 1; + jack_nframes_t period_size = 1024; + unsigned int capture_ports = 2; + unsigned int playback_ports = 2; + unsigned int capture_ports_midi = 1; + unsigned int playback_ports_midi = 1; + unsigned int listen_port = 3000; + unsigned int bitdepth = 0; + unsigned int handle_transport_sync = 1; + unsigned int use_autoconfig = 1; + unsigned int latency = 5; + unsigned int redundancy = 1; + unsigned int mtu = 1400; +#if HAVE_SAMPLERATE + unsigned int resample_factor_up = 1; +#endif + int dont_htonl_floats = 0; + int always_deadline = 0; + int jitter_val = 0; + const JSList * node; + const jack_driver_param_t * param; + + for (node = params; node; node = jack_slist_next(node)) { + param = (const jack_driver_param_t*) node->data; + switch (param->character) { case 'i': capture_ports = param->value.ui; break; @@ -1042,21 +872,21 @@ namespace Jack break; case 'f': - #if HAVE_SAMPLERATE +#if HAVE_SAMPLERATE resample_factor = param->value.ui; - #else - jack_error( "not built with libsamplerate support" ); +#else + jack_error("not built with libsamplerate support"); return NULL; - #endif +#endif break; case 'u': - #if HAVE_SAMPLERATE +#if HAVE_SAMPLERATE resample_factor_up = param->value.ui; - #else - jack_error( "not built with libsamplerate support" ); +#else + jack_error("not built with libsamplerate support"); return NULL; - #endif +#endif break; case 'b': @@ -1064,13 +894,13 @@ namespace Jack break; case 'c': - #if HAVE_CELT +#if HAVE_CELT bitdepth = CELT_MODE; resample_factor = param->value.ui; - #else - jack_error( "not built with celt support" ); +#else + jack_error("not built with celt support"); return NULL; - #endif +#endif break; case 't': @@ -1100,38 +930,31 @@ namespace Jack case 'D': always_deadline = param->value.ui; break; - } - } - - try - { - Jack::JackDriverClientInterface* driver = - new Jack::JackWaitThreadedDriver ( - new Jack::JackNetOneDriver ( "system", "net_pcm", engine, table, listen_port, mtu, - capture_ports_midi, playback_ports_midi, capture_ports, playback_ports, - sample_rate, period_size, resample_factor, - "net_pcm", handle_transport_sync, bitdepth, use_autoconfig, latency, redundancy, - dont_htonl_floats, always_deadline, jitter_val ) ); - - if ( driver->Open ( period_size, sample_rate, 1, 1, capture_ports, playback_ports, - 0, "from_master_", "to_master_", 0, 0 ) == 0 ) - { - return driver; - } - else - { - delete driver; - return NULL; - } + } + } - } - catch ( ... ) - { - return NULL; - } + try { + Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver ( + new Jack::JackNetOneDriver("system", "net_pcm", engine, table, listen_port, mtu, + capture_ports_midi, playback_ports_midi, capture_ports, playback_ports, + sample_rate, period_size, resample_factor, + "net_pcm", handle_transport_sync, bitdepth, use_autoconfig, latency, redundancy, + dont_htonl_floats, always_deadline, jitter_val)); + + if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, + 0, "from_master_", "to_master_", 0, 0) == 0) { + return driver; + } else { + delete driver; + return NULL; } - #ifdef __cplusplus + } catch (...) { + return NULL; } - #endif + } + +#ifdef __cplusplus +} +#endif } diff --git a/common/JackNetOneDriver.h b/common/JackNetOneDriver.h index 348b4628..954a7cba 100644 --- a/common/JackNetOneDriver.h +++ b/common/JackNetOneDriver.h @@ -1,6 +1,5 @@ /* -Copyright (C) 2001 Paul Davis -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Torben Horn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,78 +20,75 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackNetDriver__ #define __JackNetDriver__ -#include "JackAudioDriver.h" +#include "JackTimedDriver.h" #include "netjack.h" #include "netjack_packet.h" 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 JackWaiterDriver +{ + 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 ); +#if 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); + virtual ~JackNetOneDriver(); + + 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 diff --git a/common/JackNetSocket.h b/common/JackNetSocket.h index 7f950802..8df45c41 100644 --- a/common/JackNetSocket.h +++ b/common/JackNetSocket.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +20,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __JackNetSocket__ #define __JackNetSocket__ -#include "JackCompilerDeps.h" #include "JackError.h" #include #include diff --git a/common/JackNetTool.cpp b/common/JackNetTool.cpp index 6adbd471..bd41449c 100644 --- a/common/JackNetTool.cpp +++ b/common/JackNetTool.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -19,22 +19,92 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackNetTool.h" +#ifdef __APPLE__ + +#include + +class HardwareClock +{ + public: + + HardwareClock(); + + void Reset(); + void Update(); + + float GetDeltaTime() const; + double GetTime() const; + + private: + + double m_clockToSeconds; + + uint64_t m_startAbsTime; + uint64_t m_lastAbsTime; + + double m_time; + float m_deltaTime; +}; + +HardwareClock::HardwareClock() +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + m_clockToSeconds = (double)info.numer/info.denom/1000000000.0; + Reset(); +} + +void HardwareClock::Reset() +{ + m_startAbsTime = mach_absolute_time(); + m_lastAbsTime = m_startAbsTime; + m_time = m_startAbsTime*m_clockToSeconds; + m_deltaTime = 1.0f/60.0f; +} + +void HardwareClock::Update() +{ + const uint64_t currentTime = mach_absolute_time(); + const uint64_t dt = currentTime - m_lastAbsTime; + + m_time = currentTime*m_clockToSeconds; + m_deltaTime = (double)dt*m_clockToSeconds; + m_lastAbsTime = currentTime; +} + +float HardwareClock::GetDeltaTime() const +{ + return m_deltaTime; +} + +double HardwareClock::GetTime() const +{ + return m_time; +} + +#endif + using namespace std; namespace Jack { // NetMidiBuffer********************************************************************************** - NetMidiBuffer::NetMidiBuffer ( session_params_t* params, uint32_t nports, char* net_buffer ) + NetMidiBuffer::NetMidiBuffer(session_params_t* params, uint32_t nports, char* net_buffer) { fNPorts = nports; - fMaxBufsize = fNPorts * sizeof ( sample_t ) * params->fPeriodSize ; - fMaxPcktSize = params->fMtu - sizeof ( packet_header_t ); + fMaxBufsize = fNPorts * sizeof(sample_t) * params->fPeriodSize ; + fMaxPcktSize = params->fMtu - sizeof(packet_header_t); fBuffer = new char[fMaxBufsize]; fPortBuffer = new JackMidiBuffer* [fNPorts]; - for ( int port_index = 0; port_index < fNPorts; port_index++ ) + for (int port_index = 0; port_index < fNPorts; port_index++) { fPortBuffer[port_index] = NULL; + } fNetBuffer = net_buffer; + + fCycleBytesSize = params->fMtu + * (max(params->fSendMidiChannels, params->fReturnMidiChannels) + * params->fPeriodSize * sizeof(sample_t) / (params->fMtu - sizeof(packet_header_t))); } NetMidiBuffer::~NetMidiBuffer() @@ -43,30 +113,38 @@ namespace Jack delete[] fPortBuffer; } - size_t NetMidiBuffer::GetSize() + size_t NetMidiBuffer::GetCycleSize() + { + return fCycleBytesSize; + } + + int NetMidiBuffer::GetNumPackets(int data_size, int max_size) { - return fMaxBufsize; + int res1 = data_size % max_size; + int res2 = data_size / max_size; + return (res1) ? res2 + 1 : res2; } - void NetMidiBuffer::SetBuffer ( int index, JackMidiBuffer* buffer ) + void NetMidiBuffer::SetBuffer(int index, JackMidiBuffer* buffer) { fPortBuffer[index] = buffer; } - JackMidiBuffer* NetMidiBuffer::GetBuffer ( int index ) + JackMidiBuffer* NetMidiBuffer::GetBuffer(int index) { return fPortBuffer[index]; } void NetMidiBuffer::DisplayEvents() { - for ( int port_index = 0; port_index < fNPorts; port_index++ ) - { - for ( uint event = 0; event < fPortBuffer[port_index]->event_count; event++ ) - if ( fPortBuffer[port_index]->IsValid() ) - jack_info ( "port %d : midi event %u/%u -> time : %u, size : %u", + for (int port_index = 0; port_index < fNPorts; port_index++) { + for (uint event = 0; event < fPortBuffer[port_index]->event_count; event++) { + if (fPortBuffer[port_index]->IsValid()) { + jack_info("port %d : midi event %u/%u -> time : %u, size : %u", port_index + 1, event + 1, fPortBuffer[port_index]->event_count, - fPortBuffer[port_index]->events[event].time, fPortBuffer[port_index]->events[event].size ); + fPortBuffer[port_index]->events[event].time, fPortBuffer[port_index]->events[event].size); + } + } } } @@ -74,15 +152,15 @@ namespace Jack { int pos = 0; size_t copy_size; - for ( int port_index = 0; port_index < fNPorts; port_index++ ) - { - char* write_pos = fBuffer + pos; - copy_size = sizeof ( JackMidiBuffer ) + fPortBuffer[port_index]->event_count * sizeof ( JackMidiEvent ); - memcpy ( fBuffer + pos, fPortBuffer[port_index], copy_size ); + for (int port_index = 0; port_index < fNPorts; port_index++) { + char* write_pos = fBuffer + pos; + copy_size = sizeof(JackMidiBuffer) + fPortBuffer[port_index]->event_count * sizeof(JackMidiEvent); + memcpy(fBuffer + pos, fPortBuffer[port_index], copy_size); pos += copy_size; - memcpy ( fBuffer + pos, fPortBuffer[port_index] + ( fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos ), - fPortBuffer[port_index]->write_pos ); + memcpy(fBuffer + pos, + fPortBuffer[port_index] + (fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos), + fPortBuffer[port_index]->write_pos); pos += fPortBuffer[port_index]->write_pos; JackMidiBuffer* midi_buffer = reinterpret_cast(write_pos); @@ -91,72 +169,252 @@ namespace Jack return pos; } - int NetMidiBuffer::RenderToJackPorts() + void NetMidiBuffer::RenderToJackPorts() { int pos = 0; - int copy_size; - for ( int port_index = 0; port_index < fNPorts; port_index++ ) - { + size_t copy_size; + + for (int port_index = 0; port_index < fNPorts; port_index++) { JackMidiBuffer* midi_buffer = reinterpret_cast(fBuffer + pos); MidiBufferNToH(midi_buffer, midi_buffer); - - copy_size = sizeof ( JackMidiBuffer ) + reinterpret_cast ( fBuffer + pos )->event_count * sizeof ( JackMidiEvent ); - memcpy ( fPortBuffer[port_index], fBuffer + pos, copy_size ); + copy_size = sizeof(JackMidiBuffer) + reinterpret_cast(fBuffer + pos)->event_count * sizeof(JackMidiEvent); + memcpy(fPortBuffer[port_index], fBuffer + pos, copy_size); pos += copy_size; - memcpy ( fPortBuffer[port_index] + ( fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos ), - fBuffer + pos, fPortBuffer[port_index]->write_pos ); + memcpy(fPortBuffer[port_index] + (fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos), + fBuffer + pos, + fPortBuffer[port_index]->write_pos); pos += fPortBuffer[port_index]->write_pos; } - return pos; } - int NetMidiBuffer::RenderFromNetwork ( int subcycle, size_t copy_size ) + void NetMidiBuffer::RenderFromNetwork(int sub_cycle, size_t copy_size) { - memcpy ( fBuffer + subcycle * fMaxPcktSize, fNetBuffer, copy_size ); - return copy_size; + memcpy(fBuffer + sub_cycle * fMaxPcktSize, fNetBuffer, copy_size); } - int NetMidiBuffer::RenderToNetwork ( int subcycle, size_t total_size ) + int NetMidiBuffer::RenderToNetwork(int sub_cycle, size_t total_size) { - int size = total_size - subcycle * fMaxPcktSize; - int copy_size = ( size <= fMaxPcktSize ) ? size : fMaxPcktSize; - memcpy ( fNetBuffer, fBuffer + subcycle * fMaxPcktSize, copy_size ); + int size = total_size - sub_cycle * fMaxPcktSize; + int copy_size = (size <= fMaxPcktSize) ? size : fMaxPcktSize; + memcpy(fNetBuffer, fBuffer + sub_cycle * fMaxPcktSize, copy_size); return copy_size; } - // net audio buffer ********************************************************************************* - NetAudioBuffer::NetAudioBuffer ( session_params_t* params, uint32_t nports, char* net_buffer ) + NetAudioBuffer::NetAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer) { fNPorts = nports; - fPeriodSize = params->fPeriodSize; - fSubPeriodSize = params->fFramesPerPacket; - fSubPeriodBytesSize = fSubPeriodSize * sizeof ( sample_t ); + fNetBuffer = net_buffer; + fPortBuffer = new sample_t* [fNPorts]; - for ( int port_index = 0; port_index < fNPorts; port_index++ ) + fConnectedPorts = new bool[fNPorts]; + for (int port_index = 0; port_index < fNPorts; port_index++) { fPortBuffer[port_index] = NULL; - fNetBuffer = net_buffer; + fConnectedPorts[port_index] = true; + } } NetAudioBuffer::~NetAudioBuffer() { - delete[] fPortBuffer; + delete [] fConnectedPorts; + delete [] fPortBuffer; } - size_t NetAudioBuffer::GetSize() + void NetAudioBuffer::SetBuffer(int index, sample_t* buffer) { - return fNPorts * fSubPeriodBytesSize; + fPortBuffer[index] = buffer; } - void NetAudioBuffer::SetBuffer ( int index, sample_t* buffer ) + sample_t* NetAudioBuffer::GetBuffer(int index) { - fPortBuffer[index] = buffer; + return fPortBuffer[index]; } - sample_t* NetAudioBuffer::GetBuffer ( int index ) + int NetAudioBuffer::CheckPacket(int cycle, int sub_cycle) { - return fPortBuffer[index]; + int res; + + if (sub_cycle != fLastSubCycle + 1) { + jack_error("Packet(s) missing from... %d %d", fLastSubCycle, sub_cycle); + res = NET_PACKET_ERROR; + } else { + res = 0; + } + + fLastSubCycle = sub_cycle; + return res; + } + + void NetAudioBuffer::NextCycle() + { + // reset for next cycle + fLastSubCycle = -1; + } + + void NetAudioBuffer::Cleanup() + { + for (int port_index = 0; port_index < fNPorts; port_index++) { + if (fPortBuffer[port_index]) { + memset(fPortBuffer[port_index], 0, fPeriodSize * sizeof(sample_t)); + } + } + } + + //network<->buffer + + int NetAudioBuffer::ActivePortsToNetwork(char* net_buffer) + { + int active_ports = 0; + int* active_port_address = (int*)net_buffer; + + for (int port_index = 0; port_index < fNPorts; port_index++) { + // Write the active port number + if (fPortBuffer[port_index]) { + *active_port_address = htonl(port_index); + active_port_address++; + active_ports++; + assert(active_ports < 256); + } + } + + return active_ports; + } + + void NetAudioBuffer::ActivePortsFromNetwork(char* net_buffer, uint32_t port_num) + { + int* active_port_address = (int*)net_buffer; + + for (int port_index = 0; port_index < fNPorts; port_index++) { + fConnectedPorts[port_index] = false; + } + + for (uint port_index = 0; port_index < port_num; port_index++) { + // Use -1 when port is actually connected on other side + int active_port = ntohl(*active_port_address); + if (active_port >= 0 && active_port < fNPorts) { + fConnectedPorts[active_port] = true; + } else { + jack_error("ActivePortsFromNetwork: incorrect port = %d", active_port); + } + active_port_address++; + } + } + + int NetAudioBuffer::RenderFromJackPorts() + { + // Count active ports + int active_ports = 0; + for (int port_index = 0; port_index < fNPorts; port_index++) { + + if (fPortBuffer[port_index]) { + active_ports++; + } + } + //jack_info("active_ports %d", active_ports); + return active_ports; + } + + void NetAudioBuffer::RenderToJackPorts() + { + // Nothing to do + NextCycle(); + } + + // Float converter + + NetFloatAudioBuffer::NetFloatAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer) + : NetAudioBuffer(params, nports, net_buffer) + { + fPeriodSize = params->fPeriodSize; + fPacketSize = PACKET_AVAILABLE_SIZE(params); + + UpdateParams(max(params->fReturnAudioChannels, params->fSendAudioChannels)); + + fSubPeriodBytesSize = fSubPeriodSize * sizeof(sample_t); + + fCycleDuration = float(fSubPeriodSize) / float(params->fSampleRate); + fCycleBytesSize = params->fMtu * (fPeriodSize / fSubPeriodSize); + + fLastSubCycle = -1; + } + + NetFloatAudioBuffer::~NetFloatAudioBuffer() + {} + + // needed size in bytes for an entire cycle + size_t NetFloatAudioBuffer::GetCycleSize() + { + return fCycleBytesSize; + } + + // cycle duration in sec + float NetFloatAudioBuffer::GetCycleDuration() + { + return fCycleDuration; + } + + void NetFloatAudioBuffer::UpdateParams(int active_ports) + { + if (active_ports == 0) { + fSubPeriodSize = fPeriodSize; + } else { + jack_nframes_t period = (int) powf(2.f, (int)(log(float(fPacketSize) / (active_ports * sizeof(sample_t))) / log(2.))); + fSubPeriodSize = (period > fPeriodSize) ? fPeriodSize : period; + } + + fSubPeriodBytesSize = fSubPeriodSize * sizeof(sample_t) + sizeof(int); // The port number in coded on 4 bytes + } + + int NetFloatAudioBuffer::GetNumPackets(int active_ports) + { + UpdateParams(active_ports); + + /* + jack_log("GetNumPackets packet = %d fPeriodSize = %d fSubPeriodSize = %d fSubPeriodBytesSize = %d", + fPeriodSize / fSubPeriodSize, fPeriodSize, fSubPeriodSize, fSubPeriodBytesSize); + */ + return fPeriodSize / fSubPeriodSize; // At least one packet + } + + //jack<->buffer + + int NetFloatAudioBuffer::RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) + { + // Cleanup all JACK ports at the beginning of the cycle + if (sub_cycle == 0) { + Cleanup(); + } + + if (port_num > 0) { + UpdateParams(port_num); + for (uint32_t port_index = 0; port_index < port_num; port_index++) { + // Only copy to active ports : read the active port number then audio data + int* active_port_address = (int*)(fNetBuffer + port_index * fSubPeriodBytesSize); + int active_port = ntohl(*active_port_address); + RenderFromNetwork((char*)(active_port_address + 1), active_port, sub_cycle); + } + } + + return CheckPacket(cycle, sub_cycle); + } + + + int NetFloatAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) + { + int active_ports = 0; + + for (int port_index = 0; port_index < fNPorts; port_index++) { + // Only copy from active ports : write the active port number then audio data + if (fPortBuffer[port_index]) { + int* active_port_address = (int*)(fNetBuffer + active_ports * fSubPeriodBytesSize); + *active_port_address = htonl(port_index); + RenderToNetwork((char*)(active_port_address + 1), port_index, sub_cycle); + active_ports++; + } + } + + return port_num * fSubPeriodBytesSize; } #ifdef __BIG_ENDIAN__ @@ -177,23 +435,23 @@ namespace Jack return dat2.f; } - void NetAudioBuffer::RenderFromJackPorts ( int subcycle ) + void NetFloatAudioBuffer::RenderFromNetwork(char* net_buffer, int active_port, int sub_cycle) { - for ( int port_index = 0; port_index < fNPorts; port_index++ ) { - jack_default_audio_sample_t* src = (jack_default_audio_sample_t*)(fPortBuffer[port_index] + subcycle * fSubPeriodSize); - jack_default_audio_sample_t* dst = (jack_default_audio_sample_t*)(fNetBuffer + port_index * fSubPeriodBytesSize); - for (unsigned int sample = 0; sample < fSubPeriodBytesSize / sizeof(jack_default_audio_sample_t); sample++) { + if (fPortBuffer[active_port]) { + jack_default_audio_sample_t* src = (jack_default_audio_sample_t*)(net_buffer); + jack_default_audio_sample_t* dst = (jack_default_audio_sample_t*)(fPortBuffer[active_port] + sub_cycle * fSubPeriodSize); + for (unsigned int sample = 0; sample < (fSubPeriodBytesSize - sizeof(int)) / sizeof(jack_default_audio_sample_t); sample++) { dst[sample] = SwapFloat(src[sample]); } } } - void NetAudioBuffer::RenderToJackPorts ( int subcycle ) + void NetFloatAudioBuffer::RenderToNetwork(char* net_buffer, int active_port, int sub_cycle) { - for ( int port_index = 0; port_index < fNPorts; port_index++ ) { - jack_default_audio_sample_t* src = (jack_default_audio_sample_t*)(fNetBuffer + port_index * fSubPeriodBytesSize); - jack_default_audio_sample_t* dst = (jack_default_audio_sample_t*)(fPortBuffer[port_index] + subcycle * fSubPeriodSize); - for (unsigned int sample = 0; sample < fSubPeriodBytesSize / sizeof(jack_default_audio_sample_t); sample++) { + for (int port_index = 0; port_index < fNPorts; port_index++ ) { + jack_default_audio_sample_t* src = (jack_default_audio_sample_t*)(fPortBuffer[active_port] + sub_cycle * fSubPeriodSize); + jack_default_audio_sample_t* dst = (jack_default_audio_sample_t*)(net_buffer); + for (unsigned int sample = 0; sample < (fSubPeriodBytesSize - sizeof(int)) / sizeof(jack_default_audio_sample_t); sample++) { dst[sample] = SwapFloat(src[sample]); } } @@ -201,98 +459,470 @@ namespace Jack #else - void NetAudioBuffer::RenderFromJackPorts ( int subcycle ) + void NetFloatAudioBuffer::RenderFromNetwork(char* net_buffer, int active_port, int sub_cycle) { - for ( int port_index = 0; port_index < fNPorts; port_index++ ) - memcpy ( fNetBuffer + port_index * fSubPeriodBytesSize, fPortBuffer[port_index] + subcycle * fSubPeriodSize, fSubPeriodBytesSize ); + if (fPortBuffer[active_port]) { + memcpy(fPortBuffer[active_port] + sub_cycle * fSubPeriodSize, net_buffer, fSubPeriodBytesSize - sizeof(int)); + } } - void NetAudioBuffer::RenderToJackPorts ( int subcycle ) + void NetFloatAudioBuffer::RenderToNetwork(char* net_buffer, int active_port, int sub_cycle) { - for ( int port_index = 0; port_index < fNPorts; port_index++ ) - memcpy ( fPortBuffer[port_index] + subcycle * fSubPeriodSize, fNetBuffer + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize ); + memcpy(net_buffer, fPortBuffer[active_port] + sub_cycle * fSubPeriodSize, fSubPeriodBytesSize - sizeof(int)); } #endif + // Celt audio buffer ********************************************************************************* + +#if HAVE_CELT + + #define KPS 32 + #define KPS_DIV 8 + + NetCeltAudioBuffer::NetCeltAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps) + :NetAudioBuffer(params, nports, net_buffer) + { + fCeltMode = new CELTMode *[fNPorts]; + fCeltEncoder = new CELTEncoder *[fNPorts]; + fCeltDecoder = new CELTDecoder *[fNPorts]; + + memset(fCeltMode, 0, fNPorts * sizeof(CELTMode*)); + memset(fCeltEncoder, 0, fNPorts * sizeof(CELTEncoder*)); + memset(fCeltDecoder, 0, fNPorts * sizeof(CELTDecoder*)); + + int error = CELT_OK; + + for (int i = 0; i < fNPorts; i++) { + fCeltMode[i] = celt_mode_create(params->fSampleRate, params->fPeriodSize, &error); + if (error != CELT_OK) { + goto error; + } + + #if HAVE_CELT_API_0_11 + + fCeltEncoder[i] = celt_encoder_create_custom(fCeltMode[i], 1, &error); + if (error != CELT_OK) { + goto error; + } + celt_encoder_ctl(fCeltEncoder[i], CELT_SET_COMPLEXITY(1)); + + fCeltDecoder[i] = celt_decoder_create_custom(fCeltMode[i], 1, &error); + if (error != CELT_OK) { + goto error; + } + celt_decoder_ctl(fCeltDecoder[i], CELT_SET_COMPLEXITY(1)); + + #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 + + fCeltEncoder[i] = celt_encoder_create(fCeltMode[i], 1, &error); + if (error != CELT_OK) { + goto error; + } + celt_encoder_ctl(fCeltEncoder[i], CELT_SET_COMPLEXITY(1)); + + fCeltDecoder[i] = celt_decoder_create(fCeltMode[i], 1, &error); + if (error != CELT_OK) { + goto error; + } + celt_decoder_ctl(fCeltDecoder[i], CELT_SET_COMPLEXITY(1)); + + #else + + fCeltEncoder[i] = celt_encoder_create(fCeltMode[i]); + if (error != CELT_OK) { + goto error; + } + celt_encoder_ctl(fCeltEncoder[i], CELT_SET_COMPLEXITY(1)); + + fCeltDecoder[i] = celt_decoder_create(fCeltMode[i]); + if (error != CELT_OK) { + goto error; + } + celt_decoder_ctl(fCeltDecoder[i], CELT_SET_COMPLEXITY(1)); + + #endif + } + + { + fPeriodSize = params->fPeriodSize; + + fCompressedSizeByte = (kbps * params->fPeriodSize * 1024) / (params->fSampleRate * 8); + jack_log("NetCeltAudioBuffer fCompressedSizeByte %d", fCompressedSizeByte); + + fCompressedBuffer = new unsigned char* [fNPorts]; + for (int port_index = 0; port_index < fNPorts; port_index++) { + fCompressedBuffer[port_index] = new unsigned char[fCompressedSizeByte]; + memset(fCompressedBuffer[port_index], 0, fCompressedSizeByte * sizeof(char)); + } + + int res1 = (fNPorts * fCompressedSizeByte) % PACKET_AVAILABLE_SIZE(params); + int res2 = (fNPorts * fCompressedSizeByte) / PACKET_AVAILABLE_SIZE(params); + + fNumPackets = (res1) ? (res2 + 1) : res2; + + jack_log("NetCeltAudioBuffer res1 = %d res2 = %d", res1, res2); + + fSubPeriodBytesSize = fCompressedSizeByte / fNumPackets; + fLastSubPeriodBytesSize = fSubPeriodBytesSize + fCompressedSizeByte % fNumPackets; + + jack_log("NetCeltAudioBuffer fNumPackets = %d fSubPeriodBytesSize = %d, fLastSubPeriodBytesSize = %d", fNumPackets, fSubPeriodBytesSize, fLastSubPeriodBytesSize); + + fCycleDuration = float(fSubPeriodBytesSize / sizeof(sample_t)) / float(params->fSampleRate); + fCycleBytesSize = params->fMtu * fNumPackets; + + fLastSubCycle = -1; + return; + } + + error: + + FreeCelt(); + throw std::bad_alloc(); + } + + NetCeltAudioBuffer::~NetCeltAudioBuffer() + { + FreeCelt(); + + for (int port_index = 0; port_index < fNPorts; port_index++) { + delete [] fCompressedBuffer[port_index]; + } + + delete [] fCompressedBuffer; + } + + void NetCeltAudioBuffer::FreeCelt() + { + for (int i = 0; i < fNPorts; i++) { + if (fCeltEncoder[i]) { + celt_encoder_destroy(fCeltEncoder[i]); + } + if (fCeltDecoder[i]) { + celt_decoder_destroy(fCeltDecoder[i]); + } + if (fCeltMode[i]) { + celt_mode_destroy(fCeltMode[i]); + } + } + + delete [] fCeltMode; + delete [] fCeltEncoder; + delete [] fCeltDecoder; + } + + size_t NetCeltAudioBuffer::GetCycleSize() + { + return fCycleBytesSize; + } + + float NetCeltAudioBuffer::GetCycleDuration() + { + return fCycleDuration; + } + + int NetCeltAudioBuffer::GetNumPackets(int active_ports) + { + return fNumPackets; + } + + int NetCeltAudioBuffer::RenderFromJackPorts() + { + float buffer[fPeriodSize]; + + for (int port_index = 0; port_index < fNPorts; port_index++) { + if (fPortBuffer[port_index]) { + memcpy(buffer, fPortBuffer[port_index], fPeriodSize * sizeof(sample_t)); + } else { + memset(buffer, 0, fPeriodSize * sizeof(sample_t)); + } + #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 + int res = celt_encode_float(fCeltEncoder[port_index], buffer, fPeriodSize, fCompressedBuffer[port_index], fCompressedSizeByte); + #else + int res = celt_encode_float(fCeltEncoder[port_index], buffer, NULL, fCompressedBuffer[port_index], fCompressedSizeByte); + #endif + if (res != fCompressedSizeByte) { + jack_error("celt_encode_float error fCompressedSizeByte = %d res = %d", fCompressedSizeByte, res); + } + } + + // All ports active + return fNPorts; + } + + void NetCeltAudioBuffer::RenderToJackPorts() + { + for (int port_index = 0; port_index < fNPorts; port_index++) { + if (fPortBuffer[port_index]) { + #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 + int res = celt_decode_float(fCeltDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizeByte, fPortBuffer[port_index], fPeriodSize); + #else + int res = celt_decode_float(fCeltDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizeByte, fPortBuffer[port_index]); + #endif + if (res != CELT_OK) { + jack_error("celt_decode_float error fCompressedSizeByte = %d res = %d", fCompressedSizeByte, res); + } + } + } + + NextCycle(); + } + + //network<->buffer + int NetCeltAudioBuffer::RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) + { + // Cleanup all JACK ports at the beginning of the cycle + if (sub_cycle == 0) { + Cleanup(); + } + + if (port_num > 0) { + // Last packet of the cycle + if (sub_cycle == fNumPackets - 1) { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fNetBuffer + port_index * fLastSubPeriodBytesSize, fLastSubPeriodBytesSize); + } + } else { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fNetBuffer + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize); + } + } + } + + return CheckPacket(cycle, sub_cycle); + } + + int NetCeltAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) + { + // Last packet of the cycle + if (sub_cycle == fNumPackets - 1) { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fNetBuffer + port_index * fLastSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fLastSubPeriodBytesSize); + } + return fNPorts * fLastSubPeriodBytesSize; + } else { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fNetBuffer + port_index * fSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fSubPeriodBytesSize); + } + return fNPorts * fSubPeriodBytesSize; + } + } + +#endif + + NetIntAudioBuffer::NetIntAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer) + : NetAudioBuffer(params, nports, net_buffer) + { + fPeriodSize = params->fPeriodSize; + + fCompressedSizeByte = (params->fPeriodSize * sizeof(short)); + jack_log("NetIntAudioBuffer fCompressedSizeByte %d", fCompressedSizeByte); + + fIntBuffer = new short* [fNPorts]; + for (int port_index = 0; port_index < fNPorts; port_index++) { + fIntBuffer[port_index] = new short[fPeriodSize]; + memset(fIntBuffer[port_index], 0, fPeriodSize * sizeof(short)); + } + + int res1 = (fNPorts * fCompressedSizeByte) % PACKET_AVAILABLE_SIZE(params); + int res2 = (fNPorts * fCompressedSizeByte) / PACKET_AVAILABLE_SIZE(params); + + jack_log("NetIntAudioBuffer res1 = %d res2 = %d", res1, res2); + + fNumPackets = (res1) ? (res2 + 1) : res2; + + fSubPeriodBytesSize = fCompressedSizeByte / fNumPackets; + fLastSubPeriodBytesSize = fSubPeriodBytesSize + fCompressedSizeByte % fNumPackets; + + fSubPeriodSize = fSubPeriodBytesSize / sizeof(short); + + jack_log("NetIntAudioBuffer fNumPackets = %d fSubPeriodBytesSize = %d, fLastSubPeriodBytesSize = %d", fNumPackets, fSubPeriodBytesSize, fLastSubPeriodBytesSize); + + fCycleDuration = float(fSubPeriodBytesSize / sizeof(sample_t)) / float(params->fSampleRate); + fCycleBytesSize = params->fMtu * fNumPackets; + + fLastSubCycle = -1; + return; + } + + NetIntAudioBuffer::~NetIntAudioBuffer() + { + for (int port_index = 0; port_index < fNPorts; port_index++) { + delete [] fIntBuffer[port_index]; + } + + delete [] fIntBuffer; + } + + size_t NetIntAudioBuffer::GetCycleSize() + { + return fCycleBytesSize; + } + + float NetIntAudioBuffer::GetCycleDuration() + { + return fCycleDuration; + } + + int NetIntAudioBuffer::GetNumPackets(int active_ports) + { + return fNumPackets; + } + + int NetIntAudioBuffer::RenderFromJackPorts() + { + for (int port_index = 0; port_index < fNPorts; port_index++) { + if (fPortBuffer[port_index]) { + for (uint frame = 0; frame < fPeriodSize; frame++) { + fIntBuffer[port_index][frame] = short(fPortBuffer[port_index][frame] * 32768.f); + } + } + } + + // All ports active + return fNPorts; + } + + void NetIntAudioBuffer::RenderToJackPorts() + { + float coef = 1.f / 32768.f; + for (int port_index = 0; port_index < fNPorts; port_index++) { + if (fPortBuffer[port_index]) { + for (uint frame = 0; frame < fPeriodSize; frame++) { + fPortBuffer[port_index][frame] = float(fIntBuffer[port_index][frame] * coef); + } + } + } + + NextCycle(); + } + + //network<->buffer + int NetIntAudioBuffer::RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) + { + // Cleanup all JACK ports at the beginning of the cycle + if (sub_cycle == 0) { + Cleanup(); + } + + if (port_num > 0) { + if (sub_cycle == fNumPackets - 1) { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fIntBuffer[port_index] + sub_cycle * fSubPeriodSize, fNetBuffer + port_index * fLastSubPeriodBytesSize, fLastSubPeriodBytesSize); + } + } else { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fIntBuffer[port_index] + sub_cycle * fSubPeriodSize, fNetBuffer + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize); + } + } + } + + return CheckPacket(cycle, sub_cycle); + } + + int NetIntAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) + { + // Last packet of the cycle + if (sub_cycle == fNumPackets - 1) { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fNetBuffer + port_index * fLastSubPeriodBytesSize, fIntBuffer[port_index] + sub_cycle * fSubPeriodSize, fLastSubPeriodBytesSize); + } + return fNPorts * fLastSubPeriodBytesSize; + } else { + for (int port_index = 0; port_index < fNPorts; port_index++) { + memcpy(fNetBuffer + port_index * fSubPeriodBytesSize, fIntBuffer[port_index] + sub_cycle * fSubPeriodSize, fSubPeriodBytesSize); + } + return fNPorts * fSubPeriodBytesSize; + } + } // SessionParams ************************************************************************************ - SERVER_EXPORT void SessionParamsHToN ( session_params_t* src_params, session_params_t* dst_params ) + SERVER_EXPORT void SessionParamsHToN(session_params_t* src_params, session_params_t* dst_params) { memcpy(dst_params, src_params, sizeof(session_params_t)); - dst_params->fPacketID = htonl ( src_params->fPacketID ); - dst_params->fMtu = htonl ( src_params->fMtu ); - dst_params->fID = htonl ( src_params->fID ); - dst_params->fTransportSync = htonl ( src_params->fTransportSync ); - dst_params->fSendAudioChannels = htonl ( src_params->fSendAudioChannels ); - dst_params->fReturnAudioChannels = htonl ( src_params->fReturnAudioChannels ); - dst_params->fSendMidiChannels = htonl ( src_params->fSendMidiChannels ); - dst_params->fReturnMidiChannels = htonl ( src_params->fReturnMidiChannels ); - dst_params->fSampleRate = htonl ( src_params->fSampleRate ); - dst_params->fPeriodSize = htonl ( src_params->fPeriodSize ); - dst_params->fFramesPerPacket = htonl ( src_params->fFramesPerPacket ); - dst_params->fBitdepth = htonl ( src_params->fBitdepth ); - dst_params->fSlaveSyncMode = htonl ( src_params->fSlaveSyncMode ); - } - - SERVER_EXPORT void SessionParamsNToH ( session_params_t* src_params, session_params_t* dst_params ) + dst_params->fPacketID = htonl(src_params->fPacketID); + dst_params->fMtu = htonl(src_params->fMtu); + dst_params->fID = htonl(src_params->fID); + dst_params->fTransportSync = htonl(src_params->fTransportSync); + dst_params->fSendAudioChannels = htonl(src_params->fSendAudioChannels); + dst_params->fReturnAudioChannels = htonl(src_params->fReturnAudioChannels); + dst_params->fSendMidiChannels = htonl(src_params->fSendMidiChannels); + dst_params->fReturnMidiChannels = htonl(src_params->fReturnMidiChannels); + dst_params->fSampleRate = htonl(src_params->fSampleRate); + dst_params->fPeriodSize = htonl(src_params->fPeriodSize); + dst_params->fSampleEncoder = htonl(src_params->fSampleEncoder); + dst_params->fKBps = htonl(src_params->fKBps); + dst_params->fSlaveSyncMode = htonl(src_params->fSlaveSyncMode); + dst_params->fNetworkLatency = htonl(src_params->fNetworkLatency); + } + + SERVER_EXPORT void SessionParamsNToH(session_params_t* src_params, session_params_t* dst_params) { memcpy(dst_params, src_params, sizeof(session_params_t)); - dst_params->fPacketID = ntohl ( src_params->fPacketID ); - dst_params->fMtu = ntohl ( src_params->fMtu ); - dst_params->fID = ntohl ( src_params->fID ); - dst_params->fTransportSync = ntohl ( src_params->fTransportSync ); - dst_params->fSendAudioChannels = ntohl ( src_params->fSendAudioChannels ); - dst_params->fReturnAudioChannels = ntohl ( src_params->fReturnAudioChannels ); - dst_params->fSendMidiChannels = ntohl ( src_params->fSendMidiChannels ); - dst_params->fReturnMidiChannels = ntohl ( src_params->fReturnMidiChannels ); - dst_params->fSampleRate = ntohl ( src_params->fSampleRate ); - dst_params->fPeriodSize = ntohl ( src_params->fPeriodSize ); - dst_params->fFramesPerPacket = ntohl ( src_params->fFramesPerPacket ); - dst_params->fBitdepth = ntohl ( src_params->fBitdepth ); - dst_params->fSlaveSyncMode = ntohl ( src_params->fSlaveSyncMode ); - } - - SERVER_EXPORT void SessionParamsDisplay ( session_params_t* params ) + dst_params->fPacketID = ntohl(src_params->fPacketID); + dst_params->fMtu = ntohl(src_params->fMtu); + dst_params->fID = ntohl(src_params->fID); + dst_params->fTransportSync = ntohl(src_params->fTransportSync); + dst_params->fSendAudioChannels = ntohl(src_params->fSendAudioChannels); + dst_params->fReturnAudioChannels = ntohl(src_params->fReturnAudioChannels); + dst_params->fSendMidiChannels = ntohl(src_params->fSendMidiChannels); + dst_params->fReturnMidiChannels = ntohl(src_params->fReturnMidiChannels); + dst_params->fSampleRate = ntohl(src_params->fSampleRate); + dst_params->fPeriodSize = ntohl(src_params->fPeriodSize); + dst_params->fSampleEncoder = ntohl(src_params->fSampleEncoder); + dst_params->fKBps = ntohl(src_params->fKBps); + dst_params->fSlaveSyncMode = ntohl(src_params->fSlaveSyncMode); + dst_params->fNetworkLatency = ntohl(src_params->fNetworkLatency); + } + + SERVER_EXPORT void SessionParamsDisplay(session_params_t* params) { - char bitdepth[16]; - ( params->fBitdepth ) ? sprintf ( bitdepth, "%u", params->fBitdepth ) : sprintf ( bitdepth, "%s", "float" ); - char mode[8]; - switch ( params->fNetworkMode ) + char encoder[16]; + switch (params->fSampleEncoder) { - case 's' : - strcpy ( mode, "slow" ); + case JackFloatEncoder: + strcpy(encoder, "float"); break; - case 'n' : - strcpy ( mode, "normal" ); + case JackIntEncoder: + strcpy(encoder, "integer"); break; - case 'f' : - strcpy ( mode, "fast" ); + case JackCeltEncoder: + strcpy(encoder, "CELT"); break; } - jack_info ( "**************** Network parameters ****************" ); - jack_info ( "Name : %s", params->fName ); - jack_info ( "Protocol revision : %d", params->fProtocolVersion ); - jack_info ( "MTU : %u", params->fMtu ); - jack_info ( "Master name : %s", params->fMasterNetName ); - jack_info ( "Slave name : %s", params->fSlaveNetName ); - jack_info ( "ID : %u", params->fID ); - jack_info ( "Transport Sync : %s", ( params->fTransportSync ) ? "yes" : "no" ); - jack_info ( "Send channels (audio - midi) : %d - %d", params->fSendAudioChannels, params->fSendMidiChannels ); - jack_info ( "Return channels (audio - midi) : %d - %d", params->fReturnAudioChannels, params->fReturnMidiChannels ); - jack_info ( "Sample rate : %u frames per second", params->fSampleRate ); - jack_info ( "Period size : %u frames per period", params->fPeriodSize ); - jack_info ( "Frames per packet : %u", params->fFramesPerPacket ); - jack_info ( "Packet per period : %u", (params->fFramesPerPacket != 0) ? params->fPeriodSize / params->fFramesPerPacket : 0); - jack_info ( "Bitdepth : %s", bitdepth ); - jack_info ( "Slave mode : %s", ( params->fSlaveSyncMode ) ? "sync" : "async" ); - jack_info ( "Network mode : %s", mode ); - jack_info ( "****************************************************" ); - } - - SERVER_EXPORT sync_packet_type_t GetPacketType ( session_params_t* params ) - { - switch ( params->fPacketID ) + + jack_info("**************** Network parameters ****************"); + jack_info("Name : %s", params->fName); + jack_info("Protocol revision : %d", params->fProtocolVersion); + jack_info("MTU : %u", params->fMtu); + jack_info("Master name : %s", params->fMasterNetName); + jack_info("Slave name : %s", params->fSlaveNetName); + jack_info("ID : %u", params->fID); + jack_info("Transport Sync : %s", (params->fTransportSync) ? "yes" : "no"); + jack_info("Send channels (audio - midi) : %d - %d", params->fSendAudioChannels, params->fSendMidiChannels); + jack_info("Return channels (audio - midi) : %d - %d", params->fReturnAudioChannels, params->fReturnMidiChannels); + jack_info("Sample rate : %u frames per second", params->fSampleRate); + jack_info("Period size : %u frames per period", params->fPeriodSize); + jack_info("Network latency : %u cycles", params->fNetworkLatency); + switch (params->fSampleEncoder) { + case (JackFloatEncoder): + jack_info("SampleEncoder : %s", "Float"); + break; + case (JackIntEncoder): + jack_info("SampleEncoder : %s", "16 bits integer"); + break; + case (JackCeltEncoder): + jack_info("SampleEncoder : %s", "CELT"); + jack_info("kBits : %d", params->fKBps); + break; + }; + jack_info("Slave mode : %s", (params->fSlaveSyncMode) ? "sync" : "async"); + jack_info("****************************************************"); + } + + SERVER_EXPORT sync_packet_type_t GetPacketType(session_params_t* params) + { + switch (params->fPacketID) { case 0: return SLAVE_AVAILABLE; @@ -308,9 +938,9 @@ namespace Jack return INVALID; } - SERVER_EXPORT int SetPacketType ( session_params_t* params, sync_packet_type_t packet_type ) + SERVER_EXPORT int SetPacketType(session_params_t* params, sync_packet_type_t packet_type) { - switch ( packet_type ) + switch (packet_type) { case INVALID: return -1; @@ -334,59 +964,57 @@ namespace Jack // Packet header ********************************************************************************** - SERVER_EXPORT void PacketHeaderHToN ( packet_header_t* src_header, packet_header_t* dst_header ) + SERVER_EXPORT void PacketHeaderHToN(packet_header_t* src_header, packet_header_t* dst_header) { memcpy(dst_header, src_header, sizeof(packet_header_t)); - dst_header->fID = htonl ( src_header->fID ); - dst_header->fMidiDataSize = htonl ( src_header->fMidiDataSize ); - dst_header->fBitdepth = htonl ( src_header->fBitdepth ); - dst_header->fNMidiPckt = htonl ( src_header->fNMidiPckt ); - dst_header->fPacketSize = htonl ( src_header->fPacketSize ); - dst_header->fCycle = htonl ( src_header->fCycle ); - dst_header->fSubCycle = htonl ( src_header->fSubCycle ); - dst_header->fIsLastPckt = htonl ( src_header->fIsLastPckt ); + dst_header->fID = htonl(src_header->fID); + dst_header->fNumPacket = htonl(src_header->fNumPacket); + dst_header->fPacketSize = htonl(src_header->fPacketSize); + dst_header->fActivePorts = htonl(src_header->fActivePorts); + dst_header->fCycle = htonl(src_header->fCycle); + dst_header->fSubCycle = htonl(src_header->fSubCycle); + dst_header->fIsLastPckt = htonl(src_header->fIsLastPckt); } - SERVER_EXPORT void PacketHeaderNToH ( packet_header_t* src_header, packet_header_t* dst_header ) + SERVER_EXPORT void PacketHeaderNToH(packet_header_t* src_header, packet_header_t* dst_header) { memcpy(dst_header, src_header, sizeof(packet_header_t)); - dst_header->fID = ntohl ( src_header->fID ); - dst_header->fMidiDataSize = ntohl ( src_header->fMidiDataSize ); - dst_header->fBitdepth = ntohl ( src_header->fBitdepth ); - dst_header->fNMidiPckt = ntohl ( src_header->fNMidiPckt ); - dst_header->fPacketSize = ntohl ( src_header->fPacketSize ); - dst_header->fCycle = ntohl ( src_header->fCycle ); - dst_header->fSubCycle = ntohl ( src_header->fSubCycle ); - dst_header->fIsLastPckt = ntohl ( src_header->fIsLastPckt ); + dst_header->fID = ntohl(src_header->fID); + dst_header->fNumPacket = ntohl(src_header->fNumPacket); + dst_header->fPacketSize = ntohl(src_header->fPacketSize); + dst_header->fActivePorts = ntohl(src_header->fActivePorts); + dst_header->fCycle = ntohl(src_header->fCycle); + dst_header->fSubCycle = ntohl(src_header->fSubCycle); + dst_header->fIsLastPckt = ntohl(src_header->fIsLastPckt); } - SERVER_EXPORT void PacketHeaderDisplay ( packet_header_t* header ) + SERVER_EXPORT void PacketHeaderDisplay(packet_header_t* header) { char bitdepth[16]; - ( header->fBitdepth ) ? sprintf ( bitdepth, "%u", header->fBitdepth ) : sprintf ( bitdepth, "%s", "float" ); - jack_info ( "********************Header********************" ); - jack_info ( "Data type : %c", header->fDataType ); - jack_info ( "Data stream : %c", header->fDataStream ); - jack_info ( "ID : %u", header->fID ); - jack_info ( "Cycle : %u", header->fCycle ); - jack_info ( "SubCycle : %u", header->fSubCycle ); - jack_info ( "Midi packets : %u", header->fNMidiPckt ); - jack_info ( "Midi data size : %u", header->fMidiDataSize ); - jack_info ( "Last packet : '%s'", ( header->fIsLastPckt ) ? "yes" : "no" ); - jack_info ( "Bitdepth : %s", bitdepth ); - jack_info ( "**********************************************" ); + jack_info("********************Header********************"); + jack_info("Data type : %c", header->fDataType); + jack_info("Data stream : %c", header->fDataStream); + jack_info("ID : %u", header->fID); + jack_info("Cycle : %u", header->fCycle); + jack_info("SubCycle : %u", header->fSubCycle); + jack_info("Active ports : %u", header->fActivePorts); + jack_info("DATA packets : %u", header->fNumPacket); + jack_info("DATA size : %u", header->fPacketSize); + jack_info("Last packet : '%s'", (header->fIsLastPckt) ? "yes" : "no"); + jack_info("Bitdepth : %s", bitdepth); + jack_info("**********************************************"); } - SERVER_EXPORT void NetTransportDataDisplay ( net_transport_data_t* data ) + SERVER_EXPORT void NetTransportDataDisplay(net_transport_data_t* data) { - jack_info ( "********************Network Transport********************" ); - jack_info ( "Transport new state : %u", data->fNewState ); - jack_info ( "Transport timebase master : %u", data->fTimebaseMaster ); - jack_info ( "Transport cycle state : %u", data->fState ); - jack_info ( "**********************************************" ); + jack_info("********************Network Transport********************"); + jack_info("Transport new state : %u", data->fNewState); + jack_info("Transport timebase master : %u", data->fTimebaseMaster); + jack_info("Transport cycle state : %u", data->fState); + jack_info("**********************************************"); } - SERVER_EXPORT void MidiBufferHToN ( JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer ) + SERVER_EXPORT void MidiBufferHToN(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer) { dst_buffer->magic = htonl(src_buffer->magic); dst_buffer->buffer_size = htonl(src_buffer->buffer_size); @@ -397,7 +1025,7 @@ namespace Jack dst_buffer->mix_index = htonl(src_buffer->mix_index); } - SERVER_EXPORT void MidiBufferNToH ( JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer ) + SERVER_EXPORT void MidiBufferNToH(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer) { dst_buffer->magic = ntohl(src_buffer->magic); dst_buffer->buffer_size = ntohl(src_buffer->buffer_size); @@ -408,7 +1036,7 @@ namespace Jack dst_buffer->mix_index = ntohl(src_buffer->mix_index); } - SERVER_EXPORT void TransportDataHToN ( net_transport_data_t* src_params, net_transport_data_t* dst_params ) + SERVER_EXPORT void TransportDataHToN(net_transport_data_t* src_params, net_transport_data_t* dst_params) { dst_params->fNewState = htonl(src_params->fNewState); dst_params->fTimebaseMaster = htonl(src_params->fTimebaseMaster); @@ -434,7 +1062,7 @@ namespace Jack dst_params->fPosition.unique_2 = htonll(src_params->fPosition.unique_2); } - SERVER_EXPORT void TransportDataNToH ( net_transport_data_t* src_params, net_transport_data_t* dst_params ) + SERVER_EXPORT void TransportDataNToH(net_transport_data_t* src_params, net_transport_data_t* dst_params) { dst_params->fNewState = ntohl(src_params->fNewState); dst_params->fTimebaseMaster = ntohl(src_params->fTimebaseMaster); @@ -465,18 +1093,16 @@ namespace Jack SERVER_EXPORT int SocketAPIInit() { #ifdef WIN32 - WORD wVersionRequested = MAKEWORD ( 2, 2 ); + WORD wVersionRequested = MAKEWORD(2, 2); WSADATA wsaData; - if ( WSAStartup ( wVersionRequested, &wsaData ) != 0 ) - { - jack_error ( "WSAStartup error : %s", strerror ( NET_ERROR_CODE ) ); + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + jack_error("WSAStartup error : %s", strerror(NET_ERROR_CODE)); return -1; } - if ( LOBYTE ( wsaData.wVersion ) != 2 || HIBYTE ( wsaData.wVersion ) != 2 ) - { - jack_error ( "Could not find a useable version of Winsock.dll\n" ); + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { + jack_error("Could not find a useable version of Winsock.dll\n"); WSACleanup(); return -1; } @@ -492,9 +1118,9 @@ namespace Jack return 0; } - SERVER_EXPORT const char* GetTransportState ( int transport_state ) + SERVER_EXPORT const char* GetTransportState(int transport_state) { - switch ( transport_state ) + switch (transport_state) { case JackTransportRolling: return "rolling"; diff --git a/common/JackNetTool.h b/common/JackNetTool.h index cb6f494f..62775443 100644 --- a/common/JackNetTool.h +++ b/common/JackNetTool.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2008 Romain Moret at Grame +Copyright (C) 2008-2011 Romain Moret at Grame 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 @@ -19,7 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackMidiPort.h" #include "JackTools.h" -#include "JackPlatformPlug.h" #include "types.h" #include "transport.h" #ifndef WIN32 @@ -31,14 +30,24 @@ using namespace std; #ifndef htonll #ifdef __BIG_ENDIAN__ -#define htonll(x) (x) -#define ntohll(x) (x) +#define htonll(x) (x) +#define ntohll(x) (x) #else -#define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl(x >> 32)) -#define ntohll(x) ((((uint64_t)ntohl(x)) << 32) + ntohl(x >> 32)) +#define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl(x >> 32)) +#define ntohll(x) ((((uint64_t)ntohl(x)) << 32) + ntohl(x >> 32)) #endif #endif +#define MASTER_PROTOCOL 5 +#define SLAVE_PROTOCOL 5 + +#define NET_PACKET_ERROR -2 + +#define OPTIMIZED_PROTOCOL + +#define HEADER_SIZE (sizeof(packet_header_t)) +#define PACKET_AVAILABLE_SIZE(params) ((params)->fMtu - sizeof(packet_header_t)) + namespace Jack { typedef struct _session_params session_params_t; @@ -48,6 +57,13 @@ namespace Jack typedef struct in_addr address_t; typedef jack_default_audio_sample_t sample_t; + enum JackNetEncoder { + + JackFloatEncoder = 0, + JackIntEncoder = 1, + JackCeltEncoder = 2, + }; + //session params ****************************************************************************** /** @@ -67,9 +83,6 @@ namespace Jack are kept in LITTLE_ENDIAN format (to avoid 2 conversions in the more common LITTLE_ENDIAN <==> LITTLE_ENDIAN connection case). */ - #define MASTER_PROTOCOL 1 - #define SLAVE_PROTOCOL 1 - struct _session_params { char fPacketType[7]; //packet type ('param') @@ -81,16 +94,16 @@ 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 fFramesPerPacket; //complete frames per packet - uint32_t fBitdepth; //samples bitdepth (unused) + uint32_t fSampleEncoder; //samples encoder + uint32_t fKBps; //KB per second for CELT encoder uint32_t fSlaveSyncMode; //is the slave in sync mode ? - char fNetworkMode; //fast, normal or slow mode + uint32_t fNetworkLatency; //network latency }; //net status ********************************************************************************** @@ -154,19 +167,16 @@ namespace Jack struct _packet_header { - char fPacketType[7]; //packet type ( 'headr' ) + char fPacketType[7]; //packet type ('headr') char fDataType; //a for audio, m for midi and s for sync char fDataStream; //s for send, r for return uint32_t fID; //unique ID of the slave - uint32_t fBitdepth; //bitdepth of the data samples - uint32_t fMidiDataSize; //size of midi data in bytes - uint32_t fNMidiPckt; //number of midi packets of the cycle + uint32_t fNumPacket; //number of data packets of the cycle uint32_t fPacketSize; //packet size in bytes + uint32_t fActivePorts; //number of active ports uint32_t fCycle; //process cycle counter uint32_t fSubCycle; //midi/audio subcycle counter uint32_t fIsLastPckt; //is it the last packet of a given cycle ('y' or 'n') - char fASyncWrongCycle; //is the current async cycle wrong (slave's side; 'y' or 'n') - char fFree[26]; //unused }; //net timebase master @@ -200,7 +210,7 @@ namespace Jack jack_position_t fPosition; //current cycle position }; -//midi data *********************************************************************************** + //midi data *********************************************************************************** /** \Brief Midi buffer and operations class @@ -220,89 +230,233 @@ namespace Jack class SERVER_EXPORT NetMidiBuffer { private: + int fNPorts; size_t fMaxBufsize; int fMaxPcktSize; + char* fBuffer; char* fNetBuffer; JackMidiBuffer** fPortBuffer; + size_t fCycleBytesSize; // needed size in bytes ofr an entire cycle + public: - NetMidiBuffer ( session_params_t* params, uint32_t nports, char* net_buffer ); + + NetMidiBuffer(session_params_t* params, uint32_t nports, char* net_buffer); ~NetMidiBuffer(); void Reset(); - size_t GetSize(); + + // needed size in bytes for an entire cycle + size_t GetCycleSize(); + int GetNumPackets(int data_sizen, int max_size); + + void SetBuffer(int index, JackMidiBuffer* buffer); + JackMidiBuffer* GetBuffer(int index); + //utility void DisplayEvents(); + //jack<->buffer int RenderFromJackPorts(); - int RenderToJackPorts(); + void RenderToJackPorts(); + //network<->buffer - int RenderFromNetwork ( int subcycle, size_t copy_size ); - int RenderToNetwork ( int subcycle, size_t total_size ); + void RenderFromNetwork(int sub_cycle, size_t copy_size); + int RenderToNetwork(int sub_cycle, size_t total_size); - void SetBuffer ( int index, JackMidiBuffer* buffer ); - JackMidiBuffer* GetBuffer ( int index ); }; // audio data ********************************************************************************* - /** - \Brief Audio buffer and operations class - - This class is a toolset to manipulate audio buffers. - The manipulation of audio buffers is similar to midi buffer, except those buffers have fixed size. - The interleaving/uninterleaving operations are simplier here because audio buffers have fixed size, - So there is no need of an intermediate buffer as in NetMidiBuffer. - - */ - class SERVER_EXPORT NetAudioBuffer { - private: + + protected: + int fNPorts; + int fLastSubCycle; + + char* fNetBuffer; + sample_t** fPortBuffer; + bool* fConnectedPorts; + jack_nframes_t fPeriodSize; jack_nframes_t fSubPeriodSize; size_t fSubPeriodBytesSize; - char* fNetBuffer; - sample_t** fPortBuffer; + + float fCycleDuration; // in sec + size_t fCycleBytesSize; // needed size in bytes for an entire cycle + + int CheckPacket(int cycle, int sub_cycle); + void NextCycle(); + void Cleanup(); + + public: + + NetAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer); + virtual ~NetAudioBuffer(); + + bool GetConnected(int port_index) { return fConnectedPorts[port_index]; } + void SetConnected(int port_index, bool state) { fConnectedPorts[port_index] = state; } + + // needed syze in bytes ofr an entire cycle + virtual size_t GetCycleSize() = 0; + + // cycle duration in sec + virtual float GetCycleDuration() = 0; + + virtual int GetNumPackets(int active_ports) = 0; + + virtual void SetBuffer(int index, sample_t* buffer); + virtual sample_t* GetBuffer(int index); + + //jack<->buffer + virtual int RenderFromJackPorts(); + virtual void RenderToJackPorts(); + + //network<->buffer + virtual int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) = 0; + virtual int RenderToNetwork(int sub_cycle, uint32_t port_num) = 0; + + virtual void RenderFromNetwork(char* net_buffer, int active_port, int sub_cycle, size_t copy_size) {} + virtual void RenderToNetwork(char* net_buffer, int active_port, int sub_cycle, size_t copy_size) {} + + virtual int ActivePortsToNetwork(char* net_buffer); + virtual void ActivePortsFromNetwork(char* net_buffer, uint32_t port_num); + + }; + + class SERVER_EXPORT NetFloatAudioBuffer : public NetAudioBuffer + { + + private: + + int fPacketSize; + + void UpdateParams(int active_ports); + + public: + + NetFloatAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer); + virtual ~NetFloatAudioBuffer(); + + // needed size in bytes for an entire cycle + size_t GetCycleSize(); + + // cycle duration in sec + float GetCycleDuration(); + int GetNumPackets(int active_ports); + + //jack<->buffer + int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num); + int RenderToNetwork(int sub_cycle, uint32_t port_num); + + void RenderFromNetwork(char* net_buffer, int active_port, int sub_cycle); + void RenderToNetwork(char* net_buffer, int active_port, int sub_cycle); + + }; + +#if HAVE_CELT + +#include + + class SERVER_EXPORT NetCeltAudioBuffer : public NetAudioBuffer + { + private: + + CELTMode** fCeltMode; + CELTEncoder** fCeltEncoder; + CELTDecoder** fCeltDecoder; + + int fCompressedSizeByte; + int fNumPackets; + + size_t fLastSubPeriodBytesSize; + + unsigned char** fCompressedBuffer; + + void FreeCelt(); + public: - NetAudioBuffer ( session_params_t* params, uint32_t nports, char* net_buffer ); - ~NetAudioBuffer(); - size_t GetSize(); + NetCeltAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps); + virtual ~NetCeltAudioBuffer(); + + // needed size in bytes for an entire cycle + size_t GetCycleSize(); + + // cycle duration in sec + float GetCycleDuration(); + int GetNumPackets(int active_ports); + //jack<->buffer - void RenderFromJackPorts ( int subcycle ); - void RenderToJackPorts ( int subcycle ); + int RenderFromJackPorts(); + void RenderToJackPorts(); + + //network<->buffer + int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num); + int RenderToNetwork(int sub_cycle, uint32_t port_num); + }; + +#endif + + class SERVER_EXPORT NetIntAudioBuffer : public NetAudioBuffer + { + private: - void SetBuffer ( int index, sample_t* buffer ); - sample_t* GetBuffer ( int index ); + int fCompressedSizeByte; + int fNumPackets; + + size_t fLastSubPeriodBytesSize; + + short** fIntBuffer; + + public: + + NetIntAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer); + virtual ~NetIntAudioBuffer(); + + // needed size in bytes for an entire cycle + size_t GetCycleSize(); + + // cycle duration in sec + float GetCycleDuration(); + int GetNumPackets(int active_ports); + + //jack<->buffer + int RenderFromJackPorts(); + void RenderToJackPorts(); + + //network<->buffer + int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num); + int RenderToNetwork(int sub_cycle, uint32_t port_num); }; -//utility ************************************************************************************* + //utility ************************************************************************************* //socket API management SERVER_EXPORT int SocketAPIInit(); SERVER_EXPORT int SocketAPIEnd(); //n<-->h functions - SERVER_EXPORT void SessionParamsHToN ( session_params_t* src_params, session_params_t* dst_params ); - SERVER_EXPORT void SessionParamsNToH ( session_params_t* src_params, session_params_t* dst_params ); - SERVER_EXPORT void PacketHeaderHToN ( packet_header_t* src_header, packet_header_t* dst_header ); - SERVER_EXPORT void PacketHeaderNToH ( packet_header_t* src_header, packet_header_t* dst_header ); - SERVER_EXPORT void MidiBufferHToN ( JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer ); - SERVER_EXPORT void MidiBufferNToH ( JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer ); - SERVER_EXPORT void TransportDataHToN ( net_transport_data_t* src_params, net_transport_data_t* dst_params ); - SERVER_EXPORT void TransportDataNToH ( net_transport_data_t* src_params, net_transport_data_t* dst_params ); + SERVER_EXPORT void SessionParamsHToN(session_params_t* src_params, session_params_t* dst_params); + SERVER_EXPORT void SessionParamsNToH(session_params_t* src_params, session_params_t* dst_params); + SERVER_EXPORT void PacketHeaderHToN(packet_header_t* src_header, packet_header_t* dst_header); + SERVER_EXPORT void PacketHeaderNToH(packet_header_t* src_header, packet_header_t* dst_header); + SERVER_EXPORT void MidiBufferHToN(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer); + SERVER_EXPORT void MidiBufferNToH(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer); + SERVER_EXPORT void TransportDataHToN(net_transport_data_t* src_params, net_transport_data_t* dst_params); + SERVER_EXPORT void TransportDataNToH(net_transport_data_t* src_params, net_transport_data_t* dst_params); //display session parameters - SERVER_EXPORT void SessionParamsDisplay ( session_params_t* params ); + SERVER_EXPORT void SessionParamsDisplay(session_params_t* params); //display packet header - SERVER_EXPORT void PacketHeaderDisplay ( packet_header_t* header ); + SERVER_EXPORT void PacketHeaderDisplay(packet_header_t* header); //get the packet type from a sesion parameters - SERVER_EXPORT sync_packet_type_t GetPacketType ( session_params_t* params ); + SERVER_EXPORT sync_packet_type_t GetPacketType(session_params_t* params); //set the packet type in a session parameters - SERVER_EXPORT int SetPacketType ( session_params_t* params, sync_packet_type_t packet_type ); + SERVER_EXPORT int SetPacketType(session_params_t* params, sync_packet_type_t packet_type); //transport utility - SERVER_EXPORT const char* GetTransportState ( int transport_state ); - SERVER_EXPORT void NetTransportDataDisplay ( net_transport_data_t* data ); + SERVER_EXPORT const char* GetTransportState(int transport_state); + SERVER_EXPORT void NetTransportDataDisplay(net_transport_data_t* data); } diff --git a/common/JackPhysicalMidiInput.cpp b/common/JackPhysicalMidiInput.cpp deleted file mode 100644 index 311dad0b..00000000 --- a/common/JackPhysicalMidiInput.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* -Copyright (C) 2009 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include -#include -#include - -#include "JackError.h" -#include "JackPhysicalMidiInput.h" - -namespace Jack { - -JackPhysicalMidiInput::JackPhysicalMidiInput(size_t buffer_size) -{ - size_t datum_size = sizeof(jack_midi_data_t); - assert(buffer_size > 0); - input_ring = jack_ringbuffer_create((buffer_size + 1) * datum_size); - if (! input_ring) { - throw std::bad_alloc(); - } - jack_ringbuffer_mlock(input_ring); - Clear(); - expected_data_bytes = 0; - status_byte = 0; -} - -JackPhysicalMidiInput::~JackPhysicalMidiInput() -{ - jack_ringbuffer_free(input_ring); -} - -void -JackPhysicalMidiInput::Clear() -{ - jack_ringbuffer_reset(input_ring); - buffered_bytes = 0; - unbuffered_bytes = 0; -} - -void -JackPhysicalMidiInput::HandleBufferFailure(size_t unbuffered_bytes, - size_t total_bytes) -{ - jack_error("%d MIDI byte(s) of a %d byte message could not be buffered - " - "message dropped", unbuffered_bytes, total_bytes); -} - -void -JackPhysicalMidiInput::HandleIncompleteMessage(size_t bytes) -{ - jack_error("Discarding %d MIDI byte(s) - incomplete message (cable " - "unplugged?)", bytes); -} - -void -JackPhysicalMidiInput::HandleInvalidStatusByte(jack_midi_data_t status) -{ - jack_error("Dropping invalid MIDI status byte '%x'", - (unsigned int) status); -} - -void -JackPhysicalMidiInput::HandleUnexpectedSysexEnd(size_t bytes) -{ - jack_error("Discarding %d MIDI byte(s) - received sysex end without sysex " - "start (cable unplugged?)", bytes); -} - -void -JackPhysicalMidiInput::HandleWriteFailure(size_t bytes) -{ - jack_error("Failed to write a %d byte MIDI message to the port buffer", - bytes); -} - -void -JackPhysicalMidiInput::Process(jack_nframes_t frames) -{ - assert(port_buffer); - port_buffer->Reset(frames); - jack_nframes_t current_frame = 0; - size_t datum_size = sizeof(jack_midi_data_t); - for (;;) { - jack_midi_data_t datum; - current_frame = Receive(&datum, current_frame, frames); - if (current_frame >= frames) { - break; - } - - jack_log("JackPhysicalMidiInput::Process (%d) - Received '%x' byte", - current_frame, (unsigned int) datum); - - if (datum >= 0xf8) { - // Realtime - if (datum == 0xfd) { - HandleInvalidStatusByte(datum); - } else { - - jack_log("JackPhysicalMidiInput::Process - Writing realtime " - "event."); - - WriteByteEvent(current_frame, datum); - } - continue; - } - if (datum == 0xf7) { - // Sysex end - if (status_byte != 0xf0) { - HandleUnexpectedSysexEnd(buffered_bytes + unbuffered_bytes); - Clear(); - expected_data_bytes = 0; - status_byte = 0; - } else { - - jack_log("JackPhysicalMidiInput::Process - Writing sysex " - "event."); - - WriteBufferedSysexEvent(current_frame); - } - continue; - } - if (datum >= 0x80) { - - // We're handling a non-realtime status byte - - jack_log("JackPhysicalMidiInput::Process - Handling non-realtime " - "status byte."); - - if (buffered_bytes || unbuffered_bytes) { - HandleIncompleteMessage(buffered_bytes + unbuffered_bytes + 1); - Clear(); - } - status_byte = datum; - switch (datum & 0xf0) { - case 0x80: - case 0x90: - case 0xa0: - case 0xb0: - case 0xe0: - // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel - expected_data_bytes = 2; - break; - case 0xc0: - case 0xd0: - // Program Change, Channel Pressure - expected_data_bytes = 1; - break; - case 0xf0: - switch (datum) { - case 0xf0: - // Sysex message - expected_data_bytes = 0; - break; - case 0xf1: - case 0xf3: - // MTC Quarter frame, Song Select - expected_data_bytes = 1; - break; - case 0xf2: - // Song Position - expected_data_bytes = 2; - break; - case 0xf4: - case 0xf5: - // Undefined - HandleInvalidStatusByte(datum); - expected_data_bytes = 0; - status_byte = 0; - break; - case 0xf6: - // Tune Request - WriteByteEvent(current_frame, datum); - expected_data_bytes = 0; - status_byte = 0; - } - break; - } - continue; - } - - // We're handling a data byte - - jack_log("JackPhysicalMidiInput::Process - Buffering data byte."); - - if (jack_ringbuffer_write(input_ring, (const char *) &datum, - datum_size) == datum_size) { - buffered_bytes++; - } else { - unbuffered_bytes++; - } - unsigned long total_bytes = buffered_bytes + unbuffered_bytes; - assert((! expected_data_bytes) || - (total_bytes <= expected_data_bytes)); - if (total_bytes == expected_data_bytes) { - if (! unbuffered_bytes) { - - jack_log("JackPhysicalMidiInput::Process - Writing buffered " - "event."); - - WriteBufferedEvent(current_frame); - } else { - HandleBufferFailure(unbuffered_bytes, total_bytes); - Clear(); - } - if (status_byte >= 0xf0) { - expected_data_bytes = 0; - status_byte = 0; - } - } - } -} - -void -JackPhysicalMidiInput::WriteBufferedEvent(jack_nframes_t frame) -{ - assert(port_buffer && port_buffer->IsValid()); - size_t space = jack_ringbuffer_read_space(input_ring); - jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 1); - if (event) { - jack_ringbuffer_data_t vector[2]; - jack_ringbuffer_get_read_vector(input_ring, vector); - event[0] = status_byte; - size_t data_length_1 = vector[0].len; - memcpy(event + 1, vector[0].buf, data_length_1); - size_t data_length_2 = vector[1].len; - if (data_length_2) { - memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); - } - } else { - HandleWriteFailure(space + 1); - } - Clear(); -} - -void -JackPhysicalMidiInput::WriteBufferedSysexEvent(jack_nframes_t frame) -{ - assert(port_buffer && port_buffer->IsValid()); - size_t space = jack_ringbuffer_read_space(input_ring); - jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 2); - if (event) { - jack_ringbuffer_data_t vector[2]; - jack_ringbuffer_get_read_vector(input_ring, vector); - event[0] = status_byte; - size_t data_length_1 = vector[0].len; - memcpy(event + 1, vector[0].buf, data_length_1); - size_t data_length_2 = vector[1].len; - if (data_length_2) { - memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); - } - event[data_length_1 + data_length_2 + 1] = 0xf7; - } else { - HandleWriteFailure(space + 2); - } - Clear(); -} - -void -JackPhysicalMidiInput::WriteByteEvent(jack_nframes_t frame, - jack_midi_data_t datum) -{ - assert(port_buffer && port_buffer->IsValid()); - jack_midi_data_t *event = port_buffer->ReserveEvent(frame, 1); - if (event) { - event[0] = datum; - } else { - HandleWriteFailure(1); - } -} - -} diff --git a/common/JackPhysicalMidiInput.h b/common/JackPhysicalMidiInput.h deleted file mode 100644 index a05de6d0..00000000 --- a/common/JackPhysicalMidiInput.h +++ /dev/null @@ -1,146 +0,0 @@ -/* -Copyright (C) 2009 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#ifndef __JackPhysicalMidiInput__ -#define __JackPhysicalMidiInput__ - -#include "JackMidiPort.h" -#include "ringbuffer.h" - -namespace Jack { - - class JackPhysicalMidiInput { - - private: - - size_t buffered_bytes; - size_t expected_data_bytes; - jack_ringbuffer_t *input_ring; - JackMidiBuffer *port_buffer; - jack_midi_data_t status_byte; - size_t unbuffered_bytes; - - void - Clear(); - - void - WriteBufferedEvent(jack_nframes_t); - - void - WriteBufferedSysexEvent(jack_nframes_t); - - void - WriteByteEvent(jack_nframes_t, jack_midi_data_t); - - protected: - - /** - * Override to specify how to react when 1 or more bytes of a MIDI - * message are lost because there wasn't enough room in the input - * buffer. The first argument is the amount of bytes that couldn't be - * buffered, and the second argument is the total amount of bytes in - * the MIDI message. The default implementation calls 'jack_error' - * with a basic error message. - */ - - virtual void - HandleBufferFailure(size_t, size_t); - - /** - * Override to specify how to react when a new status byte is received - * before all of the data bytes in a message are received. The - * argument is the number of bytes being discarded. The default - * implementation calls 'jack_error' with a basic error message. - */ - - virtual void - HandleIncompleteMessage(size_t); - - /** - * Override to specify how to react when an invalid status byte (0xf4, - * 0xf5, 0xfd) is received. The argument contains the invalid status - * byte. The default implementation calls 'jack_error' with a basic - * error message. - */ - - virtual void - HandleInvalidStatusByte(jack_midi_data_t); - - /** - * Override to specify how to react when a sysex end byte (0xf7) is - * received without first receiving a sysex start byte (0xf0). The - * argument contains the amount of bytes that will be discarded. The - * default implementation calls 'jack_error' with a basic error - * message. - */ - - virtual void - HandleUnexpectedSysexEnd(size_t); - - /** - * Override to specify how to react when a MIDI message can not be - * written to the port buffer. The argument specifies the length of - * the MIDI message. The default implementation calls 'jack_error' - * with a basic error message. - */ - - virtual void - HandleWriteFailure(size_t); - - /** - * This method *must* be overridden to handle receiving MIDI bytes. - * The first argument is a pointer to the memory location at which the - * MIDI byte should be stored. The second argument is the last frame - * at which a MIDI byte was received, except at the beginning of the - * period when the value is 0. The third argument is the total number - * of frames in the period. The return value is the frame at which the - * MIDI byte is received at, or the value of the third argument is no - * more MIDI bytes can be received in this period. - */ - - virtual jack_nframes_t - Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t) = 0; - - public: - - JackPhysicalMidiInput(size_t buffer_size=1024); - virtual ~JackPhysicalMidiInput(); - - /** - * Called to process MIDI data during a period. - */ - - void - Process(jack_nframes_t); - - /** - * Set the MIDI buffer that will receive incoming messages. - */ - - inline void - SetPortBuffer(JackMidiBuffer *port_buffer) - { - this->port_buffer = port_buffer; - } - - }; - -} - -#endif diff --git a/common/JackPhysicalMidiOutput.cpp b/common/JackPhysicalMidiOutput.cpp deleted file mode 100644 index 8f41d443..00000000 --- a/common/JackPhysicalMidiOutput.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* -Copyright (C) 2009 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include - -#include "JackError.h" -#include "JackPhysicalMidiOutput.h" - -namespace Jack { - -JackPhysicalMidiOutput::JackPhysicalMidiOutput(size_t non_rt_buffer_size, - size_t rt_buffer_size) -{ - size_t datum_size = sizeof(jack_midi_data_t); - assert(non_rt_buffer_size > 0); - assert(rt_buffer_size > 0); - output_ring = jack_ringbuffer_create((non_rt_buffer_size + 1) * - datum_size); - if (! output_ring) { - throw std::bad_alloc(); - } - rt_output_ring = jack_ringbuffer_create((rt_buffer_size + 1) * - datum_size); - if (! rt_output_ring) { - jack_ringbuffer_free(output_ring); - throw std::bad_alloc(); - } - jack_ringbuffer_mlock(output_ring); - jack_ringbuffer_mlock(rt_output_ring); - running_status = 0; -} - -JackPhysicalMidiOutput::~JackPhysicalMidiOutput() -{ - jack_ringbuffer_free(output_ring); - jack_ringbuffer_free(rt_output_ring); -} - -jack_nframes_t -JackPhysicalMidiOutput::Advance(jack_nframes_t frame) -{ - return frame; -} - -inline jack_midi_data_t -JackPhysicalMidiOutput::ApplyRunningStatus(jack_midi_data_t **buffer, - size_t *size) -{ - - // Stolen and modified from alsa/midi_pack.h - - jack_midi_data_t status = (*buffer)[0]; - if ((status >= 0x80) && (status < 0xf0)) { - if (status == running_status) { - (*buffer)++; - (*size)--; - } else { - running_status = status; - } - } else if (status < 0xf8) { - running_status = 0; - } - return status; -} - -void -JackPhysicalMidiOutput::HandleEventLoss(JackMidiEvent *event) -{ - jack_error("%d byte MIDI event lost", event->size); -} - -void -JackPhysicalMidiOutput::Process(jack_nframes_t frames) -{ - assert(port_buffer); - jack_nframes_t current_frame = Advance(0); - jack_nframes_t current_midi_event = 0; - jack_midi_data_t datum; - size_t datum_size = sizeof(jack_midi_data_t); - JackMidiEvent *midi_event; - jack_midi_data_t *midi_event_buffer; - size_t midi_event_size; - jack_nframes_t midi_events = port_buffer->event_count; - - // First, send any realtime MIDI data that's left from last cycle. - - if ((current_frame < frames) && - jack_ringbuffer_read_space(rt_output_ring)) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sending buffered " - "realtime data from last period.", current_frame); - - current_frame = SendBufferedData(rt_output_ring, current_frame, - frames); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", current_frame); - - } - - // Iterate through the events in this cycle. - - for (; (current_midi_event < midi_events) && (current_frame < frames); - current_midi_event++) { - - // Once we're inside this loop, we know that the realtime buffer - // is empty. As long as we don't find a realtime message, we can - // concentrate on sending non-realtime data. - - midi_event = &(port_buffer->events[current_midi_event]); - jack_nframes_t midi_event_time = midi_event->time; - midi_event_buffer = midi_event->GetData(port_buffer); - midi_event_size = midi_event->size; - datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); - if (current_frame < midi_event_time) { - - // We have time before this event is scheduled to be sent. - // Send data in the non-realtime buffer. - - if (jack_ringbuffer_read_space(output_ring)) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " - "buffered non-realtime data from last period.", - current_frame); - - current_frame = SendBufferedData(output_ring, current_frame, - midi_event_time); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", - current_frame); - - } - if (current_frame < midi_event_time) { - - // We _still_ have time before this event is scheduled to - // be sent. Let's send as much of this event as we can - // (save for one byte, which will need to be sent at or - // after its scheduled time). First though, we need to - // make sure that we can buffer this data if we need to. - // Otherwise, we might start sending a message that we - // can't finish. - - if (midi_event_size > 1) { - if (jack_ringbuffer_write_space(output_ring) < - ((midi_event_size - 1) * datum_size)) { - HandleEventLoss(midi_event); - continue; - } - - // Send as much of the event as possible (save for one - // byte). - - do { - - jack_log("JackPhysicalMidiOutput::Process (%d) - " - "Sending unbuffered event byte early.", - current_frame); - - current_frame = Send(current_frame, - *midi_event_buffer); - - jack_log("JackPhysicalMidiOutput::Process (%d) - " - "Sent.", current_frame); - - midi_event_buffer++; - midi_event_size--; - if (current_frame >= midi_event_time) { - - // The event we're processing must be a - // non-realtime event. It has more than one - // byte. - - goto buffer_non_realtime_data; - } - } while (midi_event_size > 1); - } - - jack_log("JackPhysicalMidiOutput::Process (%d) - Advancing to " - ">= %d", current_frame, midi_event_time); - - current_frame = Advance(midi_event_time); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Advanced.", - current_frame); - - } - } - - // If the event is realtime, then we'll send the event now. - // Otherwise, we attempt to put the rest of the event bytes in the - // non-realtime buffer. - - if (datum >= 0xf8) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " - "unbuffered realtime event.", current_frame); - - current_frame = Send(current_frame, datum); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", - current_frame); - - } else if (jack_ringbuffer_write_space(output_ring) >= - (midi_event_size * datum_size)) { - buffer_non_realtime_data: - - jack_log("JackPhysicalMidiOutput::Process (%d) - Buffering %d " - "byte(s) of non-realtime data.", current_frame, - midi_event_size); - - jack_ringbuffer_write(output_ring, - (const char *) midi_event_buffer, - midi_event_size); - } else { - HandleEventLoss(midi_event); - } - } - - if (current_frame < frames) { - - // If we have time left to send data, then we know that all of the - // data in the realtime buffer has been sent, and that all of the - // non-realtime messages have either been sent, or buffered. We - // use whatever time is left to send data in the non-realtime - // buffer. - - if (jack_ringbuffer_read_space(output_ring)) { - - jack_log("JackPhysicalMidiOutput::Process (%d) - All events " - "processed. Sending buffered non-realtime data.", - current_frame); - - current_frame = SendBufferedData(output_ring, current_frame, - frames); - - jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", - current_frame); - - } - } else { - - // Since we have no time left, we need to put all remaining midi - // events in their appropriate buffers, and send them next period. - - for (; current_midi_event < midi_events; current_midi_event++) { - midi_event = &(port_buffer->events[current_midi_event]); - midi_event_buffer = midi_event->GetData(port_buffer); - midi_event_size = midi_event->size; - datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); - if (datum >= 0xf8) { - - // Realtime. - - if (jack_ringbuffer_write_space(rt_output_ring) >= - datum_size) { - - jack_log("JackPhysicalMidiOutput::Process - Buffering " - "realtime event for next period."); - - jack_ringbuffer_write(rt_output_ring, - (const char *) &datum, datum_size); - continue; - } - } else { - - // Non-realtime. - - if (jack_ringbuffer_write_space(output_ring) >= - (midi_event_size * datum_size)) { - - jack_log("JackPhysicalMidiOutput::Process - Buffering " - "non-realtime event for next period."); - - jack_ringbuffer_write(output_ring, - (const char *) midi_event_buffer, - midi_event_size * datum_size); - continue; - } - } - HandleEventLoss(midi_event); - } - } -} - -jack_nframes_t -JackPhysicalMidiOutput::SendBufferedData(jack_ringbuffer_t *buffer, - jack_nframes_t current_frame, - jack_nframes_t boundary) -{ - assert(buffer); - assert(current_frame < boundary); - size_t datum_size = sizeof(jack_midi_data_t); - size_t data_length = jack_ringbuffer_read_space(buffer) / datum_size; - for (size_t i = 0; i < data_length; i++) { - jack_midi_data_t datum; - jack_ringbuffer_read(buffer, (char *) &datum, datum_size); - current_frame = Send(current_frame, datum); - if (current_frame >= boundary) { - break; - } - } - return current_frame; -} - -} diff --git a/common/JackPhysicalMidiOutput.h b/common/JackPhysicalMidiOutput.h deleted file mode 100644 index 9b4804b7..00000000 --- a/common/JackPhysicalMidiOutput.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -Copyright (C) 2009 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#ifndef __JackPhysicalMidiOutput__ -#define __JackPhysicalMidiOutput__ - -#include "JackMidiPort.h" -#include "ringbuffer.h" - -namespace Jack { - - class JackPhysicalMidiOutput { - - private: - - jack_midi_data_t - ApplyRunningStatus(jack_midi_data_t **, size_t *); - - jack_ringbuffer_t *output_ring; - JackMidiBuffer *port_buffer; - jack_ringbuffer_t *rt_output_ring; - jack_midi_data_t running_status; - - protected: - - /** - * Override to specify the next frame at which a midi byte can be sent. - * The returned frame must be greater than or equal to the frame - * argument. The default returns the frame passed to it. - */ - - virtual jack_nframes_t - Advance(jack_nframes_t); - - /** - * Override to customize how to react when a MIDI event can't be - * buffered and can't be sent immediately. The default calls - * 'jack_error' and specifies the number of bytes lost. - */ - - virtual void - HandleEventLoss(JackMidiEvent *); - - /** - * This method *must* be overridden to specify what happens when a MIDI - * byte is sent at the specfied frame. The frame argument specifies - * the frame at which the MIDI byte should be sent, and the second - * argument specifies the byte itself. The return value is the next - * frame at which a MIDI byte can be sent, and must be greater than or - * equal to the frame argument. - */ - - virtual jack_nframes_t - Send(jack_nframes_t, jack_midi_data_t) = 0; - - /** - * Override to optimize behavior when sending MIDI data that's in the - * ringbuffer. The first frame argument is the current frame, and the - * second frame argument is the boundary frame. The function returns - * the next frame at which MIDI data can be sent, regardless of whether - * or not the boundary is reached. The default implementation calls - * 'Send' with each byte in the ringbuffer until either the ringbuffer - * is empty, or a frame beyond the boundary frame is returned by - * 'Send'. - */ - - virtual jack_nframes_t - SendBufferedData(jack_ringbuffer_t *, jack_nframes_t, jack_nframes_t); - - public: - - /** - * The non-realtime buffer size and the realtime buffer size are both - * optional arguments. - */ - - JackPhysicalMidiOutput(size_t non_rt_buffer_size=1024, - size_t rt_buffer_size=64); - virtual ~JackPhysicalMidiOutput(); - - /** - * Called to process MIDI data during a period. - */ - - void - Process(jack_nframes_t); - - /** - * Set the MIDI buffer that will contain the outgoing MIDI messages. - */ - - inline void - SetPortBuffer(JackMidiBuffer *port_buffer) - { - this->port_buffer = port_buffer; - } - - }; - -} - -#endif diff --git a/common/JackPort.cpp b/common/JackPort.cpp index 870d6ee8..a556fef0 100644 --- a/common/JackPort.cpp +++ b/common/JackPort.cpp @@ -35,6 +35,7 @@ JackPort::JackPort() bool JackPort::Allocate(int refnum, const char* port_name, const char* port_type, JackPortFlags flags) { jack_port_type_id_t id = GetPortTypeId(port_type); + assert(id >= 0 && id <= PORT_TYPES_MAX); if (id == PORT_TYPES_MAX) return false; fTypeId = id; @@ -44,6 +45,7 @@ bool JackPort::Allocate(int refnum, const char* port_name, const char* port_type fInUse = true; fLatency = 0; fTotalLatency = 0; + fMonitorRequests = 0; fPlaybackLatency.min = fPlaybackLatency.max = 0; fCaptureLatency.min = fCaptureLatency.max = 0; fTied = NO_PORT; @@ -65,6 +67,8 @@ void JackPort::Release() fLatency = 0; fTotalLatency = 0; fMonitorRequests = 0; + fPlaybackLatency.min = fPlaybackLatency.max = 0; + fCaptureLatency.min = fCaptureLatency.max = 0; fTied = NO_PORT; fAlias1[0] = '\0'; fAlias2[0] = '\0'; @@ -107,7 +111,7 @@ void JackPort::SetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_r if (mode == JackCaptureLatency) { fCaptureLatency = *range; - /* hack to set port->shared->latency up for + /* hack to set latency up for * backend ports */ if ((fFlags & JackPortIsOutput) && (fFlags & JackPortIsPhysical)) @@ -115,7 +119,7 @@ void JackPort::SetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_r } else { fPlaybackLatency = *range; - /* hack to set port->shared->latency up for + /* hack to set latency up for * backend ports */ if ((fFlags & JackPortIsInput) && (fFlags & JackPortIsPhysical)) @@ -221,7 +225,7 @@ void JackPort::SetName(const char* new_name) bool JackPort::NameEquals(const char* target) { - char buf[JACK_PORT_NAME_SIZE + 1]; + char buf[REAL_JACK_PORT_NAME_SIZE]; /* this nasty, nasty kludge is here because between 0.109.0 and 0.109.1, the ALSA audio backend had the name "ALSA", whereas as before and @@ -245,12 +249,12 @@ int JackPort::GetAliases(char* const aliases[2]) int cnt = 0; if (fAlias1[0] != '\0') { - snprintf(aliases[0], JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE, "%s", fAlias1); + snprintf(aliases[0], REAL_JACK_PORT_NAME_SIZE, "%s", fAlias1); cnt++; } if (fAlias2[0] != '\0') { - snprintf(aliases[1], JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE, "%s", fAlias2); + snprintf(aliases[1], REAL_JACK_PORT_NAME_SIZE, "%s", fAlias2); cnt++; } diff --git a/common/JackPort.h b/common/JackPort.h index 8e8c1667..687a5a2a 100644 --- a/common/JackPort.h +++ b/common/JackPort.h @@ -35,6 +35,7 @@ namespace Jack \brief Base class for port. */ +PRE_PACKED_STRUCTURE class SERVER_EXPORT JackPort { @@ -44,9 +45,9 @@ class SERVER_EXPORT JackPort int fTypeId; enum JackPortFlags fFlags; - char fName[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char fAlias1[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char fAlias2[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + char fName[REAL_JACK_PORT_NAME_SIZE]; + char fAlias1[REAL_JACK_PORT_NAME_SIZE]; + char fAlias2[REAL_JACK_PORT_NAME_SIZE]; int fRefNum; jack_nframes_t fLatency; @@ -107,7 +108,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; diff --git a/common/JackProfiler.cpp b/common/JackProfiler.cpp index b74abc13..54b8091f 100644 --- a/common/JackProfiler.cpp +++ b/common/JackProfiler.cpp @@ -206,37 +206,16 @@ 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, "profiler"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 - strcpy(desc->desc, "real-time server profiling"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 - - desc->nparams = 3; - desc->params = (jack_driver_param_desc_t*)calloc(desc->nparams, sizeof(jack_driver_param_desc_t)); - - int i = 0; - strcpy(desc->params[i].name, "cpu-load"); - desc->params[i].character = 'c'; - desc->params[i].type = JackDriverParamBool; - desc->params[i].value.i = FALSE; - strcpy(desc->params[i].short_desc, "Show DSP CPU load"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); - - i++; - strcpy(desc->params[i].name, "driver-period"); - desc->params[i].character = 'p'; - desc->params[i].type = JackDriverParamBool; - desc->params[i].value.i = FALSE; - strcpy(desc->params[i].short_desc, "Show driver period"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); - - i++; - strcpy(desc->params[i].name, "driver-end-time"); - desc->params[i].character = 'e'; - desc->params[i].type = JackDriverParamBool; - desc->params[i].value.i = FALSE; - strcpy(desc->params[i].short_desc, "Show driver end time"); - strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("profiler", JackDriverNone, "real-time server profiling", &filler); + + value.i = FALSE; + jack_driver_descriptor_add_parameter(desc, &filler, "cpu-load", 'c', JackDriverParamBool, &value, NULL, "Show DSP CPU load", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "driver-period", 'p', JackDriverParamBool, &value, NULL, "Show driver period", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "driver-end-time", 'e', JackDriverParamBool, &value, NULL, "Show driver end time", NULL); return desc; } diff --git a/common/JackRequest.h b/common/JackRequest.h index 90d0644a..aa65f602 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -23,15 +23,28 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "JackConstants.h" #include "JackPlatformPlug.h" +#include "JackTime.h" #include "types.h" #include #include +#include #include namespace Jack { -#define CheckRes(exp) { if ((exp) < 0) return -1;} +#define CheckRes(exp) { if ((exp) < 0) return -1; } + +/*! +\brief Session API constants. +*/ + +enum JackSessionReply { + + kImmediateSessionReply = 1, + kPendingSessionReply = 2 + +}; /*! \brief Request from client to server. @@ -78,7 +91,7 @@ struct JackRequest RequestType fType; - JackRequest() + JackRequest(): fType((RequestType)0) {} JackRequest(RequestType type): fType(type) @@ -138,11 +151,12 @@ struct JackClientCheckRequest : public JackRequest int fProtocol; int fOptions; int fUUID; + int fOpen; JackClientCheckRequest() {} - JackClientCheckRequest(const char* name, int protocol, int options, int uuid) - : JackRequest(JackRequest::kClientCheck), fProtocol(protocol), fOptions(options), fUUID(uuid) + JackClientCheckRequest(const char* name, int protocol, int options, int uuid, int open = false) + : JackRequest(JackRequest::kClientCheck), fProtocol(protocol), fOptions(options), fUUID(uuid), fOpen(open) { snprintf(fName, sizeof(fName), "%s", name); } @@ -152,16 +166,18 @@ struct JackClientCheckRequest : public JackRequest CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fProtocol, sizeof(int))); CheckRes(trans->Read(&fOptions, sizeof(int))); - return trans->Read(&fUUID, sizeof(int)); + CheckRes(trans->Read(&fUUID, sizeof(int))); + return trans->Read(&fOpen, sizeof(int)); } int Write(JackChannelTransaction* trans) { CheckRes(JackRequest::Write(trans)); - CheckRes(trans->Write(&fName, sizeof(fName))); + CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fProtocol, sizeof(int))); CheckRes(trans->Write(&fOptions, sizeof(int))); - return trans->Write(&fUUID, sizeof(int)); + CheckRes(trans->Write(&fUUID, sizeof(int))); + return trans->Write(&fOpen, sizeof(int)); } }; @@ -226,7 +242,7 @@ struct JackClientOpenRequest : public JackRequest { CheckRes(trans->Read(&fPID, sizeof(int))); CheckRes(trans->Read(&fUUID, sizeof(int))); - return trans->Read(&fName, sizeof(fName)); + return trans->Read(&fName, sizeof(fName)); } int Write(JackChannelTransaction* trans) @@ -234,7 +250,7 @@ struct JackClientOpenRequest : public JackRequest CheckRes(JackRequest::Write(trans)); CheckRes(trans->Write(&fPID, sizeof(int))); CheckRes(trans->Write(&fUUID, sizeof(int))); - return trans->Write(&fName, sizeof(fName)); + return trans->Write(&fName, sizeof(fName)); } }; @@ -846,14 +862,14 @@ struct JackGetInternalClientNameResult : public JackResult int Read(JackChannelTransaction* trans) { CheckRes(JackResult::Read(trans)); - CheckRes(trans->Read(&fName, sizeof(fName))); + CheckRes(trans->Read(&fName, sizeof(fName))); return 0; } int Write(JackChannelTransaction* trans) { CheckRes(JackResult::Write(trans)); - CheckRes(trans->Write(&fName, sizeof(fName))); + CheckRes(trans->Write(&fName, sizeof(fName))); return 0; } @@ -880,14 +896,14 @@ struct JackInternalClientHandleRequest : public JackRequest int Read(JackChannelTransaction* trans) { CheckRes(trans->Read(&fRefNum, sizeof(int))); - return trans->Read(&fName, sizeof(fName)); + return trans->Read(&fName, sizeof(fName)); } int Write(JackChannelTransaction* trans) { CheckRes(JackRequest::Write(trans)); CheckRes(trans->Write(&fRefNum, sizeof(int))); - return trans->Write(&fName, sizeof(fName)); + return trans->Write(&fName, sizeof(fName)); } }; @@ -958,7 +974,7 @@ struct JackInternalClientLoadRequest : public JackRequest int Read(JackChannelTransaction* trans) { CheckRes(trans->Read(&fRefNum, sizeof(int))); - CheckRes(trans->Read(&fName, sizeof(fName))); + CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fDllName, sizeof(fDllName))); CheckRes(trans->Read(&fLoadInitName, sizeof(fLoadInitName))); CheckRes(trans->Read(&fUUID, sizeof(int))); @@ -969,7 +985,7 @@ struct JackInternalClientLoadRequest : public JackRequest { CheckRes(JackRequest::Write(trans)); CheckRes(trans->Write(&fRefNum, sizeof(int))); - CheckRes(trans->Write(&fName, sizeof(fName))); + CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fDllName, sizeof(fDllName))); CheckRes(trans->Write(&fLoadInitName, sizeof(fLoadInitName))); CheckRes(trans->Write(&fUUID, sizeof(int))); @@ -1119,11 +1135,11 @@ struct JackSessionCommand JackSessionCommand() {} - JackSessionCommand( const char *uuid, const char *clientname, const char *command, jack_session_flags_t flags ) + JackSessionCommand(const char *uuid, const char *clientname, const char *command, jack_session_flags_t flags) { - strncpy( fUUID, uuid, sizeof(fUUID)); - strncpy( fClientName, clientname, sizeof(fClientName)); - strncpy( fCommand, command, sizeof(fCommand)); + strncpy(fUUID, uuid, sizeof(fUUID)); + strncpy(fClientName, clientname, sizeof(fClientName)); + strncpy(fCommand, command, sizeof(fCommand)); fFlags = flags; } }; @@ -1132,17 +1148,23 @@ struct JackSessionNotifyResult : public JackResult { std::list fCommandList; + bool fDone; - JackSessionNotifyResult(): JackResult() + JackSessionNotifyResult(): JackResult(), fDone(false) {} JackSessionNotifyResult(int32_t result) - : JackResult(result) + : JackResult(result), fDone(false) {} int Read(JackChannelTransaction* trans) { + if (trans == NULL) + { + return 0; + } + CheckRes(JackResult::Read(trans)); - while(1) { + while (true) { JackSessionCommand buffer; CheckRes(trans->Read(buffer.fUUID, sizeof(buffer.fUUID))); @@ -1155,16 +1177,25 @@ struct JackSessionNotifyResult : public JackResult fCommandList.push_back(buffer); } + + fDone = true; + return 0; } int Write(JackChannelTransaction* trans) { + if (trans == NULL) + { + fDone = true; + return 0; + } + char terminator[JACK_UUID_SIZE]; terminator[0] = '\0'; CheckRes(JackResult::Write(trans)); - for (std::list::iterator i=fCommandList.begin(); i!=fCommandList.end(); i++) { + for (std::list::iterator i = fCommandList.begin(); i != fCommandList.end(); i++) { CheckRes(trans->Write(i->fUUID, sizeof(i->fUUID))); CheckRes(trans->Write(i->fClientName, sizeof(i->fClientName))); CheckRes(trans->Write(i->fCommand, sizeof(i->fCommand))); @@ -1174,6 +1205,32 @@ struct JackSessionNotifyResult : public JackResult return 0; } + jack_session_command_t* GetCommands() + { + /* TODO: some kind of signal should be used instead */ + while (!fDone) + { + JackSleep(50000); /* 50 ms */ + } + + jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (fCommandList.size() + 1)); + int i = 0; + + for (std::list::iterator ci = fCommandList.begin(); ci != fCommandList.end(); ci++) { + session_command[i].uuid = strdup(ci->fUUID); + session_command[i].client_name = strdup(ci->fClientName); + session_command[i].command = strdup(ci->fCommand); + session_command[i].flags = ci->fFlags; + i += 1; + } + + session_command[i].uuid = NULL; + session_command[i].client_name = NULL; + session_command[i].command = NULL; + session_command[i].flags = (jack_session_flags_t)0; + + return session_command; + } }; /*! @@ -1184,19 +1241,20 @@ struct JackSessionNotifyRequest : public JackRequest { char fPath[JACK_MESSAGE_SIZE + 1]; char fDst[JACK_CLIENT_NAME_SIZE + 1]; - jack_session_event_type_t fEventType; - int fRefNum; + jack_session_event_type_t fEventType; + int fRefNum; JackSessionNotifyRequest() {} - JackSessionNotifyRequest(int refnum, const char *path, jack_session_event_type_t type, const char *dst) + JackSessionNotifyRequest(int refnum, const char* path, jack_session_event_type_t type, const char* dst) : JackRequest(JackRequest::kSessionNotify), fEventType(type), fRefNum(refnum) { snprintf(fPath, sizeof(fPath), "%s", path); - if (dst) + if (dst) { snprintf(fDst, sizeof(fDst), "%s", dst); - else + } else { fDst[0] = '\0'; + } } int Read(JackChannelTransaction* trans) @@ -1276,7 +1334,6 @@ struct JackClientNameResult : public JackResult struct JackUUIDResult : public JackResult { - char fUUID[JACK_UUID_SIZE]; JackUUIDResult(): JackResult() @@ -1453,7 +1510,7 @@ struct JackClientNotification CheckRes(trans->Read(&fValue1, sizeof(int))); CheckRes(trans->Read(&fValue2, sizeof(int))); CheckRes(trans->Read(&fSync, sizeof(int))); - CheckRes(trans->Read(&fMessage, sizeof(fName))); + CheckRes(trans->Read(&fMessage, sizeof(fMessage))); return 0; } @@ -1465,7 +1522,7 @@ struct JackClientNotification CheckRes(trans->Write(&fValue1, sizeof(int))); CheckRes(trans->Write(&fValue2, sizeof(int))); CheckRes(trans->Write(&fSync, sizeof(int))); - CheckRes(trans->Write(&fMessage, sizeof(fName))); + CheckRes(trans->Write(&fMessage, sizeof(fMessage))); return 0; } diff --git a/common/JackResampler.cpp b/common/JackResampler.cpp index 57220295..4077de34 100644 --- a/common/JackResampler.cpp +++ b/common/JackResampler.cpp @@ -24,7 +24,7 @@ namespace Jack { JackResampler::JackResampler() - :fRatio(1),fRingBufferSize(DEFAULT_RB_SIZE) + :fRatio(1), fRingBufferSize(DEFAULT_RB_SIZE) { fRingBuffer = jack_ringbuffer_create(sizeof(jack_default_audio_sample_t) * fRingBufferSize); jack_ringbuffer_read_advance(fRingBuffer, (sizeof(jack_default_audio_sample_t) * fRingBufferSize) / 2); @@ -32,13 +32,15 @@ JackResampler::JackResampler() JackResampler::~JackResampler() { - if (fRingBuffer) + if (fRingBuffer) { jack_ringbuffer_free(fRingBuffer); + } } void JackResampler::Reset(unsigned int new_size) { fRingBufferSize = new_size; + jack_ringbuffer_reset(fRingBuffer); jack_ringbuffer_reset_size(fRingBuffer, sizeof(jack_default_audio_sample_t) * fRingBufferSize); jack_ringbuffer_read_advance(fRingBuffer, (sizeof(jack_default_audio_sample_t) * fRingBufferSize / 2)); } @@ -81,6 +83,34 @@ unsigned int JackResampler::Write(jack_default_audio_sample_t* buffer, unsigned } } +unsigned int JackResampler::Read(void* buffer, unsigned int bytes) +{ + size_t len = jack_ringbuffer_read_space(fRingBuffer); + jack_log("JackResampler::Read input available = %ld", len); + + if (len < bytes) { + jack_error("JackResampler::Read : producer too slow, missing bytes = %d", bytes); + return 0; + } else { + jack_ringbuffer_read(fRingBuffer, (char*)buffer, bytes); + return bytes; + } +} + +unsigned int JackResampler::Write(void* buffer, unsigned int bytes) +{ + size_t len = jack_ringbuffer_write_space(fRingBuffer); + jack_log("JackResampler::Write output available = %ld", len); + + if (len < bytes) { + jack_error("JackResampler::Write : consumer too slow, skip bytes = %d", bytes); + return 0; + } else { + jack_ringbuffer_write(fRingBuffer, (char*)buffer, bytes); + return bytes; + } +} + unsigned int JackResampler::ReadResample(jack_default_audio_sample_t* buffer, unsigned int frames) { return Read(buffer, frames); diff --git a/common/JackResampler.h b/common/JackResampler.h index d5994c8e..89334c77 100644 --- a/common/JackResampler.h +++ b/common/JackResampler.h @@ -61,6 +61,9 @@ class JackResampler virtual unsigned int Read(jack_default_audio_sample_t* buffer, unsigned int frames); virtual unsigned int Write(jack_default_audio_sample_t* buffer, unsigned int frames); + virtual unsigned int Read(void* buffer, unsigned int bytes); + virtual unsigned int Write(void* buffer, unsigned int bytes); + virtual unsigned int ReadSpace(); virtual unsigned int WriteSpace(); diff --git a/common/JackRestartThreadedDriver.cpp b/common/JackRestartThreadedDriver.cpp index faf9eb33..46e75f71 100644 --- a/common/JackRestartThreadedDriver.cpp +++ b/common/JackRestartThreadedDriver.cpp @@ -35,7 +35,7 @@ bool JackRestartThreadedDriver::Execute() return false; } catch (JackNetException& e) { e.PrintMessage(); - jack_log("Driver is restarted"); + jack_info("Driver is restarted"); fThread.DropSelfRealTime(); // Thread in kIniting status again... fThread.SetStatus(JackThread::kIniting); diff --git a/common/JackServer.cpp b/common/JackServer.cpp index a35fa939..e0dc9802 100644 --- a/common/JackServer.cpp +++ b/common/JackServer.cpp @@ -22,7 +22,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackServerGlobals.h" #include "JackTime.h" #include "JackFreewheelDriver.h" -#include "JackDummyDriver.h" #include "JackThreadedDriver.h" #include "JackGlobals.h" #include "JackLockedEngine.h" @@ -58,7 +57,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; @@ -188,6 +187,8 @@ int JackServer::Start() int JackServer::Stop() { jack_log("JackServer::Stop"); + fChannel.Stop(); + if (fFreewheel) { return fThreadedFreewheelDriver->Stop(); } else { @@ -223,13 +224,11 @@ int JackServer::SetBufferSize(jack_nframes_t buffer_size) } if (fAudioDriver->SetBufferSize(buffer_size) == 0) { - fFreewheelDriver->SetBufferSize(buffer_size); fEngine->NotifyBufferSize(buffer_size); return fAudioDriver->Start(); } else { // Failure: try to restore current value jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size); fAudioDriver->SetBufferSize(current_buffer_size); - fFreewheelDriver->SetBufferSize(current_buffer_size); fAudioDriver->Start(); // SetBufferSize actually failed, so return an error... return -1; diff --git a/common/JackServerAPI.cpp b/common/JackServerAPI.cpp index 49cc4a58..56ef70ae 100644 --- a/common/JackServerAPI.cpp +++ b/common/JackServerAPI.cpp @@ -33,17 +33,13 @@ extern "C" { #endif - jack_client_t * jack_client_new_aux (const char *client_name, - jack_options_t options, - jack_status_t *status); - jack_client_t * jack_client_open_aux (const char *client_name, - jack_options_t options, - jack_status_t *status, va_list ap); - EXPORT jack_client_t * jack_client_open (const char *client_name, + jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t options, jack_status_t* status); + + SERVER_EXPORT jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...); - EXPORT int jack_client_close (jack_client_t *client); - EXPORT int jack_get_client_pid (const char *name); + SERVER_EXPORT int jack_client_close (jack_client_t *client); + SERVER_EXPORT int jack_get_client_pid (const char *name); #ifdef __cplusplus } @@ -51,6 +47,10 @@ extern "C" using namespace Jack; +static jack_client_t * jack_client_open_aux (const char *client_name, + jack_options_t options, + jack_status_t *status, va_list ap); + jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t options, jack_status_t* status) { jack_varargs_t va; /* variable arguments */ @@ -153,7 +153,7 @@ jack_client_t* jack_client_open_aux(const char* client_name, jack_options_t opti } } -EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options_t options, jack_status_t* status, ...) +SERVER_EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options_t options, jack_status_t* status, ...) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_open"); @@ -176,7 +176,7 @@ EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options } } -EXPORT int jack_client_close(jack_client_t* ext_client) +SERVER_EXPORT int jack_client_close(jack_client_t* ext_client) { #ifdef __CLIENTDEBUG__ JackGlobals::CheckContext("jack_client_close"); @@ -198,7 +198,7 @@ EXPORT int jack_client_close(jack_client_t* ext_client) return res; } -EXPORT int jack_get_client_pid(const char *name) +SERVER_EXPORT int jack_get_client_pid(const char *name) { return (JackServerGlobals::fInstance != NULL) ? JackServerGlobals::fInstance->GetEngine()->GetClientPID(name) diff --git a/common/JackServerGlobals.cpp b/common/JackServerGlobals.cpp index c6d75834..e5cbb7e3 100644 --- a/common/JackServerGlobals.cpp +++ b/common/JackServerGlobals.cpp @@ -85,7 +85,7 @@ void JackServerGlobals::Delete() int status; int refnum = (*it2).second; if (refnum > 0) { - // Client object is internally kept in JackEngine, and will be desallocated in InternalClientUnload + // Client object is internally kept in JackEngine, and will be deallocated in InternalClientUnload fInstance->GetEngine()->InternalClientUnload(refnum, &status); } } diff --git a/common/JackSession.h b/common/JackSession.h new file mode 100644 index 00000000..af78978a --- /dev/null +++ b/common/JackSession.h @@ -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 + +#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 diff --git a/common/JackShmMem.cpp b/common/JackShmMem.cpp index 11c4f988..4651ecf6 100644 --- a/common/JackShmMem.cpp +++ b/common/JackShmMem.cpp @@ -1,20 +1,20 @@ /* Copyright (C) 2004-2008 Grame - + 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 + along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - + */ #include "JackError.h" @@ -33,7 +33,7 @@ JackShmMem::JackShmMem() JackShmMemAble::Init(); LockMemory(); } - + JackShmMem::~JackShmMem() { UnlockMemory(); @@ -45,7 +45,7 @@ void JackShmMemAble::Init() fInfo.ptr.attached_at = gInfo.ptr.attached_at; fInfo.size = gInfo.size; } - + void* JackShmMem::operator new(size_t size, void* memory) { jack_log("JackShmMem::new placement size = %ld", size); @@ -61,12 +61,12 @@ void* JackShmMem::operator new(size_t size) snprintf(name, sizeof(name), "/jack_shared%d", fSegmentNum++); if (jack_shmalloc(name, size, &info)) { - jack_error("cannot create shared memory segment of size = %d", size, strerror(errno)); + jack_error("Cannot create shared memory segment of size = %d", size, strerror(errno)); goto error; } if (jack_attach_shm(&info)) { - jack_error("cannot attach shared memory segment name = %s err = %s", name, strerror(errno)); + jack_error("Cannot attach shared memory segment name = %s err = %s", name, strerror(errno)); jack_destroy_shm(&info); goto error; } @@ -100,7 +100,7 @@ void JackShmMem::operator delete(void* p, size_t size) } void JackShmMem::operator delete(void* obj) -{ +{ if (obj) { JackShmMem::operator delete(obj, 0); } @@ -111,7 +111,7 @@ void LockMemoryImp(void* ptr, size_t size) if (CHECK_MLOCK((char*)ptr, size)) { jack_log("Succeeded in locking %u byte memory area", size); } else { - jack_error("Cannot lock down memory area (%s)", strerror(errno)); + jack_error("Cannot lock down %u byte memory area (%s)", size, strerror(errno)); } } @@ -121,7 +121,7 @@ void InitLockMemoryImp(void* ptr, size_t size) memset(ptr, 0, size); jack_log("Succeeded in locking %u byte memory area", size); } else { - jack_error("Cannot lock down memory area (%s)", strerror(errno)); + jack_error("Cannot lock down %u byte memory area (%s)", size, strerror(errno)); } } @@ -130,7 +130,7 @@ void UnlockMemoryImp(void* ptr, size_t size) if (CHECK_MUNLOCK((char*)ptr, size)) { jack_log("Succeeded in unlocking %u byte memory area", size); } else { - jack_error("Cannot unlock down memory area (%s)", strerror(errno)); + jack_error("Cannot unlock down %u byte memory area (%s)", size, strerror(errno)); } } diff --git a/common/JackShmMem.h b/common/JackShmMem.h index fdef507b..397434fc 100644 --- a/common/JackShmMem.h +++ b/common/JackShmMem.h @@ -1,20 +1,20 @@ /* Copyright (C) 2004-2008 Grame - + 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 + along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - + */ #ifndef __JackShmMem__ @@ -47,12 +47,12 @@ class JackMem static size_t gSize; protected: - + JackMem(): fSize(gSize) {} ~JackMem() {} - + public: void* operator new(size_t size) @@ -65,7 +65,7 @@ class JackMem { free(ptr); } - + void LockMemory() { LockMemoryImp(this, fSize); @@ -87,11 +87,11 @@ A class which objects possibly want to be allocated in shared memory derives fro class JackShmMemAble { protected: - + jack_shm_info_t fInfo; - + public: - + void Init(); int GetShmIndex() @@ -126,18 +126,18 @@ class SERVER_EXPORT JackShmMem : public JackShmMemAble { protected: - + JackShmMem(); ~JackShmMem(); - + public: - + void* operator new(size_t size); void* operator new(size_t size, void* memory); - + void operator delete(void* p, size_t size); void operator delete(void* p); - + }; /*! @@ -156,11 +156,12 @@ class JackShmReadWritePtr { if (fInfo.index < 0 && index >= 0) { jack_log("JackShmReadWritePtr::Init %ld %ld", index, fInfo.index); - if (jack_initialize_shm(server_name) < 0) - throw - 1; + if (jack_initialize_shm(server_name) < 0) { + throw std::bad_alloc(); + } fInfo.index = index; - if (jack_attach_shm(&fInfo)) { - throw - 2; + if (jack_attach_lib_shm(&fInfo)) { + throw std::bad_alloc(); } GetShmAddress()->LockMemory(); } @@ -184,7 +185,7 @@ class JackShmReadWritePtr if (fInfo.index >= 0) { jack_log("JackShmReadWritePtr::~JackShmReadWritePtr %ld", fInfo.index); GetShmAddress()->UnlockMemory(); - jack_release_shm(&fInfo); + jack_release_lib_shm(&fInfo); fInfo.index = -1; } } @@ -237,19 +238,20 @@ class JackShmReadWritePtr1 { if (fInfo.index < 0 && index >= 0) { jack_log("JackShmReadWritePtr1::Init %ld %ld", index, fInfo.index); - if (jack_initialize_shm(server_name) < 0) - throw - 1; + if (jack_initialize_shm(server_name) < 0) { + throw std::bad_alloc(); + } fInfo.index = index; - if (jack_attach_shm(&fInfo)) { - throw - 2; + if (jack_attach_lib_shm(&fInfo)) { + throw std::bad_alloc(); } + GetShmAddress()->LockMemory(); /* nobody else needs to access this shared memory any more, so destroy it. because we have our own attachment to it, it won't vanish till we exit (and release it). */ jack_destroy_shm(&fInfo); - GetShmAddress()->LockMemory(); } } @@ -271,7 +273,7 @@ class JackShmReadWritePtr1 if (fInfo.index >= 0) { jack_log("JackShmReadWritePtr1::~JackShmReadWritePtr1 %ld", fInfo.index); GetShmAddress()->UnlockMemory(); - jack_release_shm(&fInfo); + jack_release_lib_shm(&fInfo); fInfo.index = -1; } } @@ -324,11 +326,12 @@ class JackShmReadPtr { if (fInfo.index < 0 && index >= 0) { jack_log("JackShmPtrRead::Init %ld %ld", index, fInfo.index); - if (jack_initialize_shm(server_name) < 0) - throw - 1; + if (jack_initialize_shm(server_name) < 0) { + throw std::bad_alloc(); + } fInfo.index = index; - if (jack_attach_shm_read(&fInfo)) { - throw - 2; + if (jack_attach_lib_shm_read(&fInfo)) { + throw std::bad_alloc(); } GetShmAddress()->LockMemory(); } @@ -352,7 +355,7 @@ class JackShmReadPtr if (fInfo.index >= 0) { jack_log("JackShmPtrRead::~JackShmPtrRead %ld", fInfo.index); GetShmAddress()->UnlockMemory(); - jack_release_shm(&fInfo); + jack_release_lib_shm(&fInfo); fInfo.index = -1; } } diff --git a/common/JackThreadedDriver.cpp b/common/JackThreadedDriver.cpp index 88323fe5..a88926f0 100644 --- a/common/JackThreadedDriver.cpp +++ b/common/JackThreadedDriver.cpp @@ -21,6 +21,7 @@ #include "JackSystemDeps.h" #include "JackThreadedDriver.h" #include "JackError.h" +#include "JackTools.h" #include "JackGlobals.h" #include "JackEngineControl.h" @@ -43,16 +44,16 @@ int JackThreadedDriver::Open() } int JackThreadedDriver::Open(jack_nframes_t buffer_size, - jack_nframes_t samplerate, - 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) + jack_nframes_t samplerate, + 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) { return fDriver->Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } @@ -67,11 +68,6 @@ int JackThreadedDriver::Process() return fDriver->Process(); } -int JackThreadedDriver::ProcessNull() -{ - return fDriver->ProcessNull(); -} - int JackThreadedDriver::Attach() { return fDriver->Attach(); @@ -127,9 +123,24 @@ void JackThreadedDriver::RemoveSlave(JackDriverInterface* slave) fDriver->RemoveSlave(slave); } -int JackThreadedDriver::ProcessSlaves() +int JackThreadedDriver::ProcessReadSlaves() +{ + return fDriver->ProcessReadSlaves(); +} + +int JackThreadedDriver::ProcessWriteSlaves() { - return fDriver->ProcessSlaves(); + return fDriver->ProcessWriteSlaves(); +} + +int JackThreadedDriver::ProcessRead() +{ + return fDriver->ProcessRead(); +} + +int JackThreadedDriver::ProcessWrite() +{ + return fDriver->ProcessWrite(); } std::list JackThreadedDriver::GetSlaves() @@ -184,7 +195,6 @@ int JackThreadedDriver::Stop() case JackThread::kIniting: if (fThread.Kill() < 0) { jack_error("Cannot kill thread"); - return -1; } break; @@ -192,7 +202,6 @@ int JackThreadedDriver::Stop() case JackThread::kRunning: if (fThread.Stop() < 0) { jack_error("Cannot stop thread"); - return -1; } break; @@ -215,21 +224,30 @@ bool JackThreadedDriver::Execute() bool JackThreadedDriver::Init() { if (fDriver->Initialize()) { - if (fDriver->IsRealTime()) { - jack_log("JackThreadedDriver::Init IsRealTime"); - // Will do "something" on OSX only... - GetEngineControl()->fPeriod = GetEngineControl()->fConstraint = GetEngineControl()->fPeriodUsecs * 1000; - fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); - if (fThread.AcquireSelfRealTime(GetEngineControl()->fServerPriority) < 0) { - jack_error("AcquireSelfRealTime error"); - } else { - set_threaded_log_function(); - } - } + SetRealTime(); return true; } else { return false; } } +void JackThreadedDriver::SetRealTime() +{ + if (fDriver->IsRealTime()) { + jack_log("JackThreadedDriver::Init real-time"); + // Will do "something" on OSX only... + GetEngineControl()->fPeriod = GetEngineControl()->fConstraint = GetEngineControl()->fPeriodUsecs * 1000; + GetEngineControl()->fComputation = JackTools::ComputationMicroSec(GetEngineControl()->fBufferSize) * 1000; + fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); + if (fThread.AcquireSelfRealTime(GetEngineControl()->fServerPriority) < 0) { + jack_error("AcquireSelfRealTime error"); + } else { + set_threaded_log_function(); + } + } else { + jack_log("JackThreadedDriver::Init non-realtime"); + } +} + + } // end of namespace diff --git a/common/JackThreadedDriver.h b/common/JackThreadedDriver.h index 92ec1d26..12d77471 100644 --- a/common/JackThreadedDriver.h +++ b/common/JackThreadedDriver.h @@ -39,6 +39,8 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi JackThread fThread; JackDriver* fDriver; + void SetRealTime(); + public: JackThreadedDriver(JackDriver* driver); @@ -72,7 +74,6 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi virtual int Close(); virtual int Process(); - virtual int ProcessNull(); virtual int Attach(); virtual int Detach(); @@ -89,10 +90,17 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi virtual void SetMaster(bool onoff); virtual bool GetMaster(); + virtual void AddSlave(JackDriverInterface* slave); virtual void RemoveSlave(JackDriverInterface* slave); + virtual std::list GetSlaves(); - virtual int ProcessSlaves(); + + virtual int ProcessReadSlaves(); + virtual int ProcessWriteSlaves(); + + virtual int ProcessRead(); + virtual int ProcessWrite(); virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); virtual JackClientControl* GetClientControl() const; diff --git a/common/JackTimedDriver.cpp b/common/JackTimedDriver.cpp new file mode 100644 index 00000000..2a6dec89 --- /dev/null +++ b/common/JackTimedDriver.cpp @@ -0,0 +1,89 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2008 Grame + +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 "JackTimedDriver.h" +#include "JackEngineControl.h" +#include "JackTime.h" +#include "JackCompilerDeps.h" +#include +#include +#include + +namespace Jack +{ + +int JackTimedDriver::FirstCycle(jack_time_t cur_time_usec) +{ + fAnchorTimeUsec = cur_time_usec; + return int((double(fEngineControl->fBufferSize) * 1000000) / double(fEngineControl->fSampleRate)); +} + +int JackTimedDriver::CurrentCycle(jack_time_t cur_time_usec) +{ + return int(((double(fCycleCount) * double(fEngineControl->fBufferSize) * 1000000.) / double(fEngineControl->fSampleRate)) - (cur_time_usec - fAnchorTimeUsec)); +} + +int JackTimedDriver::Start() +{ + fCycleCount = 0; + return JackAudioDriver::Start(); +} + +void JackTimedDriver::ProcessWait() +{ + jack_time_t cur_time_usec = GetMicroSeconds(); + int wait_time_usec; + + if (fCycleCount++ == 0) { + wait_time_usec = FirstCycle(cur_time_usec); + } else { + wait_time_usec = CurrentCycle(cur_time_usec); + } + + if (wait_time_usec < 0) { + NotifyXRun(cur_time_usec, float(cur_time_usec - fBeginDateUst)); + fCycleCount = 0; + wait_time_usec = 0; + jack_error("JackTimedDriver::Process XRun = %ld usec", (cur_time_usec - fBeginDateUst)); + } + + //jack_log("JackTimedDriver::Process wait_time = %d", wait_time_usec); + JackSleep(wait_time_usec); +} + +int JackWaiterDriver::ProcessNull() +{ + JackDriver::CycleTakeBeginTime(); + + // Graph processing without Read/Write + if (fEngineControl->fSyncMode) { + ProcessGraphSync(); + } else { + ProcessGraphAsync(); + } + + // Keep end cycle time + JackDriver::CycleTakeEndTime(); + + ProcessWait(); + return 0; +} + +} // end of namespace diff --git a/common/JackTimedDriver.h b/common/JackTimedDriver.h new file mode 100644 index 00000000..e2d26ca1 --- /dev/null +++ b/common/JackTimedDriver.h @@ -0,0 +1,80 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2008 Grame + +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 __JackTimedDriver__ +#define __JackTimedDriver__ + +#include "JackAudioDriver.h" + +namespace Jack +{ + +/*! +\brief The timed driver. +*/ + +class SERVER_EXPORT JackTimedDriver : public JackAudioDriver +{ + protected: + + int fCycleCount; + jack_time_t fAnchorTimeUsec; + + int FirstCycle(jack_time_t cur_time); + int CurrentCycle(jack_time_t cur_time); + + void ProcessWait(); + + public: + + JackTimedDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackAudioDriver(name, alias, engine, table), fCycleCount(0), fAnchorTimeUsec(0) + {} + virtual ~JackTimedDriver() + {} + + // BufferSize can be changed + bool IsFixedBufferSize() + { + return false; + } + + int Start(); + +}; + +class SERVER_EXPORT JackWaiterDriver : public JackTimedDriver +{ + + public: + + JackWaiterDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackTimedDriver(name, alias, engine, table) + {} + virtual ~JackWaiterDriver() + {} + + virtual int ProcessNull(); + +}; + +} // end of namespace + +#endif diff --git a/common/JackTools.cpp b/common/JackTools.cpp index bf21fbf5..b9cec837 100644 --- a/common/JackTools.cpp +++ b/common/JackTools.cpp @@ -43,11 +43,11 @@ namespace Jack { #endif } - void JackTools::ThrowJackNetException() + void JackTools::ThrowJackNetException() { throw JackNetException(); } - + int JackTools::MkDir(const char* path) { #ifdef WIN32 diff --git a/common/JackTools.h b/common/JackTools.h index f7d5ce1e..64be2e23 100644 --- a/common/JackTools.h +++ b/common/JackTools.h @@ -63,13 +63,24 @@ namespace Jack static int MkDir(const char* path); static char* UserDir(); - static char* ServerDir ( const char* server_name, char* server_dir ); + static char* ServerDir(const char* server_name, char* server_dir); static const char* DefaultServerName(); - static void CleanupFiles ( const char* server_name ); + static void CleanupFiles(const char* server_name); static int GetTmpdir(); - static void RewriteName ( const char* name, char* new_name ); - + static void RewriteName(const char* name, char* new_name); static void ThrowJackNetException(); + + // For OSX only + static int ComputationMicroSec(int buffer_size) + { + if (buffer_size < 128) { + return 500; + } else if (buffer_size < 256) { + return 300; + } else { + return 100; + } + } }; /*! @@ -141,7 +152,7 @@ namespace Jack T Add ( T measure_point ) { - return fCurrentMeasure[fMeasureId++] = measure_point; + return fCurrentMeasure[fMeasureId++] = measure_point; } uint32_t AddLast ( T measure_point ) diff --git a/common/JackTransportEngine.cpp b/common/JackTransportEngine.cpp index 19168eea..fdcea0c0 100644 --- a/common/JackTransportEngine.cpp +++ b/common/JackTransportEngine.cpp @@ -13,7 +13,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -38,7 +38,7 @@ JackTransportEngine::JackTransportEngine(): JackAtomicArrayStateGetClientControl(); // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport.... control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling; - control->fTransportSync = true; - control->fTransportTimebase = true; + control->fTransportSync = true; + control->fTransportTimebase = true; jack_log("MakeAllStartingLocating ref = %ld", i); } } @@ -127,8 +127,8 @@ void JackTransportEngine::MakeAllStopping(JackClientInterface** table) if (client) { JackClientControl* control = client->GetClientControl(); control->fTransportState = JackTransportStopped; - control->fTransportSync = false; - control->fTransportTimebase = false; + control->fTransportSync = false; + control->fTransportTimebase = false; jack_log("MakeAllStopping ref = %ld", i); } } @@ -142,8 +142,8 @@ void JackTransportEngine::MakeAllLocating(JackClientInterface** table) if (client) { JackClientControl* control = client->GetClientControl(); control->fTransportState = JackTransportStopped; - control->fTransportSync = true; - control->fTransportTimebase = true; + control->fTransportSync = true; + control->fTransportTimebase = true; jack_log("MakeAllLocating ref = %ld", i); } } diff --git a/common/JackTransportEngine.h b/common/JackTransportEngine.h index a1ff2b97..b6a4ddcf 100644 --- a/common/JackTransportEngine.h +++ b/common/JackTransportEngine.h @@ -13,7 +13,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -48,47 +48,48 @@ We have: The current position can be read by clients. We use a JackAtomicArrayState pattern that allows to manage several "next" states independantly. - + In jack1 implementation, transport code (jack_transport_cycle_end) was not called if the graph could not be locked (see jack_run_one_cycle). Here transport cycle (CycleBegin, CycleEnd) has to run in the RT thread concurrently with code executed from the "command" thread. - + Each client maintains a state in it's shared memory area defined by: - + - it's current transport state - a boolean that is "true" when slow-sync cb has to be called - a boolean that is "true" when timebase cb is called with new_pos on - + Several operations set the "slow-sync cb" flag to true: - + - setting a new cb (client) - activate (client) - transport start (server) - new pos (server) - + Slow-sync cb calls stops when: - + - the cb return true (client) - desactivate (client) - transport stop (server) - + Several operations set the "timebase cb" flag to true: - + - setting a new cb (client) - activate (client) - transport start (server) ?? - new pos (server) - + Timebase cb "new_pos" argument calls stops when: - + - after one cb call with "new_pos" argument true (client) - desactivate (client) - release (client) - transport stop (server) - + */ class JackClientInterface; +PRE_PACKED_STRUCTURE class SERVER_EXPORT JackTransportEngine : public JackAtomicArrayState { @@ -109,7 +110,7 @@ class SERVER_EXPORT JackTransportEngine : public JackAtomicArrayStateProcessNull(); - } - - // Set RT - if (fDriver->IsRealTime()) { - jack_log("JackWaitThreadedDriver::Init IsRealTime"); - // Will do "something" on OSX only... - GetEngineControl()->fPeriod = GetEngineControl()->fConstraint = GetEngineControl()->fPeriodUsecs * 1000; - fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); - if (fThread.AcquireSelfRealTime(GetEngineControl()->fServerPriority) < 0) { - jack_error("AcquireSelfRealTime error"); - } else { - set_threaded_log_function(); - } + // Use base class method + assert(static_cast(fDriver)); + static_cast(fDriver)->ProcessNull(); } // Switch to keep running even in case of error while (fThread.GetStatus() == JackThread::kRunning) { fDriver->Process(); } + return false; + } catch (JackNetException& e) { + e.PrintMessage(); jack_info("Driver is restarted"); fThread.DropSelfRealTime(); + + // Thread has been stopped... + if (fThread.GetStatus() == JackThread::kIdle) { + return false; + } + // Thread in kIniting status again... fThread.SetStatus(JackThread::kIniting); if (Init()) { // Thread in kRunning status again... fThread.SetStatus(JackThread::kRunning); return true; - } else { - return false; } - } + + return false; + } } } // end of namespace diff --git a/common/JackWaitThreadedDriver.h b/common/JackWaitThreadedDriver.h index f81db20c..6eee399a 100644 --- a/common/JackWaitThreadedDriver.h +++ b/common/JackWaitThreadedDriver.h @@ -22,70 +22,74 @@ #define __JackWaitThreadedDriver__ #include "JackThreadedDriver.h" -#include "JackAudioDriver.h" +#include "JackTimedDriver.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 Execute method will call the ProcessNull method until the decorated driver Init method returns. +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 Process method from the base JackTimedDriver, until the decorated driver Init method returns. A helper JackDriverStarter thread is used for that purpose. */ class SERVER_EXPORT JackWaitThreadedDriver : public JackThreadedDriver { private: - - struct SERVER_EXPORT JackDriverStarter : public JackRunnableInterface + + struct JackDriverStarter : public JackRunnableInterface { - + JackDriver* fDriver; JackThread fThread; - bool fRunning; - + volatile bool fRunning; + JackDriverStarter(JackDriver* driver) - :fDriver(driver),fThread(this),fRunning(false) + :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 Init method returns). - 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 diff --git a/common/JackWeakAPI.cpp b/common/JackWeakAPI.c similarity index 94% rename from common/JackWeakAPI.cpp rename to common/JackWeakAPI.c index 6ff5af85..ab907e60 100644 --- a/common/JackWeakAPI.cpp +++ b/common/JackWeakAPI.c @@ -1,10 +1,6 @@ //============================================================================= -// MuseScore -// Linux Music Score Editor -// $Id: // -// jackWeakAPI based on code from Stéphane Letz (Grame) -// partly based on Julien Pommier (PianoTeq : http://www.pianoteq.com/) code. +// jackWeakAPI partly based on Julien Pommier (PianoTeq : http://www.pianoteq.com/) code. // // Copyright (C) 2002-2007 Werner Schweer and others // Copyright (C) 2009 Grame @@ -24,6 +20,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include +#include #include #include #include @@ -31,7 +28,7 @@ #include #endif #include -#include +#include /* dynamically load libjack and forward all registered calls to libjack (similar to what relaytool is trying to do, but more portably..) @@ -40,24 +37,25 @@ typedef void (*print_function)(const char *); typedef void *(*thread_routine)(void*); -using std::cerr; - -int libjack_is_present = 0; // public symbol, similar to what relaytool does. +static int libjack_is_present = 0; // public symbol, similar to what relaytool does. #ifdef WIN32 -HMODULE libjack_handle = 0; +static HMODULE libjack_handle = 0; #else static void *libjack_handle = 0; #endif - static void __attribute__((constructor)) tryload_libjack() { if (getenv("SKIP_LIBJACK") == 0) { // just in case libjack is causing troubles.. #ifdef __APPLE__ libjack_handle = dlopen("libjack.0.dylib", RTLD_LAZY); #elif defined(WIN32) - libjack_handle = LoadLibrary("libjack.dll"); + #ifdef _WIN64 + libjack_handle = LoadLibrary("libjack64.dll"); + #else + libjack_handle = LoadLibrary("libjack.dll"); + #endif #else libjack_handle = dlopen("libjack.so.0", RTLD_LAZY); #endif @@ -235,7 +233,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 +259,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 @@ -289,9 +287,10 @@ DECL_VOID_FUNCTION(jack_free, (void* ptr), (ptr)); // session DECL_FUNCTION(int, jack_set_session_callback, (jack_client_t* ext_client, JackSessionCallback session_callback, void* arg), (ext_client, session_callback, arg)); -DECL_FUNCTION(jack_session_command_t*, jack_session_notify, (jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path), (ext_client, target, ev_type, path)à); -DECL_FUNCTION(int jack_session_reply, (jack_client_t* ext_client, jack_session_event_t *event), (ext_client, event)); +DECL_FUNCTION(jack_session_command_t*, jack_session_notify, (jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path), (ext_client, target, ev_type, path)); +DECL_FUNCTION(int, jack_session_reply, (jack_client_t* ext_client, jack_session_event_t *event), (ext_client, event)); DECL_VOID_FUNCTION(jack_session_event_free, (jack_session_event_t* ev), (ev)); +DECL_FUNCTION(char*, jack_client_get_uuid, (jack_client_t* ext_client),(ext_client)); DECL_FUNCTION(char*, jack_get_uuid_for_client_name, (jack_client_t* ext_client, const char* client_name),(ext_client, client_name)); DECL_FUNCTION(char*, jack_get_client_name_by_uuid, (jack_client_t* ext_client, const char* client_uuid),(ext_client, client_uuid)); DECL_FUNCTION(int, jack_reserve_client_name, (jack_client_t* ext_client, const char* name, const char* uuid),(ext_client, name, uuid)); diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp index 7e57e573..5e647a3d 100644 --- a/common/Jackdmp.cpp +++ b/common/Jackdmp.cpp @@ -115,13 +115,13 @@ static void usage(FILE* file) " [ --version OR -V ]\n" " -d master-backend-name [ ... master-backend args ... ]\n" #ifdef __APPLE__ - " Available master backends may include: coreaudio, dummy or net.\n\n" + " Available master backends may include: coreaudio, dummy, net or netone.\n\n" #endif #ifdef WIN32 - " Available master backends may include: portaudio, dummy or net.\n\n" + " Available master backends may include: portaudio, dummy, net or netone.\n\n" #endif #ifdef __linux__ - " Available master backends may include: alsa, dummy, freebob, firewire or net\n\n" + " Available master backends may include: alsa, dummy, freebob, firewire, net or netone.\n\n" #endif #if defined(__sun__) || defined(sun) " Available master backends may include: boomer, oss, dummy or net.\n\n" @@ -185,13 +185,13 @@ jackctl_get_parameter( return NULL; } -int main(int argc, char* argv[]) +int main(int argc, char** argv) { jackctl_server_t * server_ctl; 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:" "a:" @@ -262,13 +262,6 @@ int main(int argc, char* argv[]) server_parameters = jackctl_server_get_parameters(server_ctl); - // Default setting - param = jackctl_get_parameter(server_parameters, "realtime"); - if (param != NULL) { - value.b = true; - jackctl_parameter_set_value(param, &value); - } - opterr = 0; while (!master_driver_name && (opt = getopt_long(argc, argv, options, @@ -458,6 +451,11 @@ int main(int argc, char* argv[]) goto destroy_server; } + if (jackctl_driver_get_type(master_driver_ctl) != JackMaster) { + fprintf(stderr, "Driver \"%s\" is not a master \n", master_driver_name); + goto destroy_server; + } + if (optind < argc) { master_driver_nargs = 1 + argc - optind; } else { @@ -497,14 +495,20 @@ 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_driver_get_type(slave_driver_ctl) != JackSlave) { + fprintf(stderr, "Driver \"%s\" is not a slave \n", *it); + goto close_server; + } + 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"); @@ -512,9 +516,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 @@ -530,7 +539,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); @@ -541,15 +553,33 @@ int main(int argc, char* argv[]) jackctl_wait_signals(signals); stop_server: - if (! jackctl_server_stop(server_ctl)) { + 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); + if (slave_driver_ctl) + 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); + if (internal_driver_ctl) + 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; } diff --git a/common/driver_interface.h b/common/driver_interface.h index 6c97099c..b67a2546 100644 --- a/common/driver_interface.h +++ b/common/driver_interface.h @@ -41,78 +41,110 @@ extern "C" #define JACK_CONSTRAINT_FLAG_STRICT ((uint32_t)2) /**< if set, constraint is strict, i.e. supplying non-matching value will not work */ #define JACK_CONSTRAINT_FLAG_FAKE_VALUE ((uint32_t)4) /**< if set, values have no user meaningful meaning */ - /** Driver parameter types */ - typedef enum - { - JackDriverParamInt = 1, - JackDriverParamUInt, - JackDriverParamChar, - JackDriverParamString, - JackDriverParamBool - } jack_driver_param_type_t; - - /** Driver parameter value */ - typedef union - { - uint32_t ui; - int32_t i; - char c; - char str[JACK_DRIVER_PARAM_STRING_MAX + 1]; - } jack_driver_param_value_t; - - typedef struct { - jack_driver_param_value_t value; - char short_desc[64]; /**< A short (~30 chars) description for the user */ - } jack_driver_param_value_enum_t; - - typedef struct { - uint32_t flags; /**< JACK_CONSTRAINT_FLAG_XXX */ - - union { - struct { - jack_driver_param_value_t min; - jack_driver_param_value_t max; - } range; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is set */ - - struct { - uint32_t count; - jack_driver_param_value_enum_t * possible_values_array; - } enumeration; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is not set */ - } constraint; - } jack_driver_param_constraint_desc_t; - - /** A driver parameter descriptor */ - typedef struct { - char name[JACK_DRIVER_NAME_MAX + 1]; /**< The parameter's name */ - char character; /**< The parameter's character (for getopt, etc) */ - jack_driver_param_type_t type; /**< The parameter's type */ - jack_driver_param_value_t value; /**< The parameter's (default) value */ - jack_driver_param_constraint_desc_t * constraint; /**< Pointer to parameter constraint descriptor. NULL if there is no constraint */ - char short_desc[64]; /**< A short (~30 chars) description for the user */ - char long_desc[1024]; /**< A longer description for the user */ - } - jack_driver_param_desc_t; - - /** A driver parameter */ - typedef struct { - char character; - jack_driver_param_value_t value; - } - jack_driver_param_t; - - - /** A struct for describing a jack driver */ - typedef struct { - char name[JACK_DRIVER_NAME_MAX + 1]; /**< The driver's canonical name */ - char desc[JACK_DRIVER_PARAM_DESC + 1]; /**< The driver's extended description */ - char file[JACK_PATH_MAX + 1]; /**< The filename of the driver's shared object file */ - uint32_t nparams; /**< The number of parameters the driver has */ - jack_driver_param_desc_t * params; /**< An array of parameter descriptors */ - } - jack_driver_desc_t; - - -SERVER_EXPORT int jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr); +/** Driver parameter types */ +typedef enum +{ + JackDriverParamInt = 1, + JackDriverParamUInt, + JackDriverParamChar, + JackDriverParamString, + JackDriverParamBool +} jack_driver_param_type_t; + +/** Driver types */ +typedef enum +{ + JackDriverMaster = 1, + JackDriverSlave, + JackDriverNone, +} jack_driver_type_t; + +/** Driver parameter value */ +typedef union +{ + uint32_t ui; + int32_t i; + char c; + char str[JACK_DRIVER_PARAM_STRING_MAX + 1]; +} jack_driver_param_value_t; + +typedef struct { + jack_driver_param_value_t value; + char short_desc[64]; /**< A short (~30 chars) description for the user */ +} jack_driver_param_value_enum_t; + +typedef struct { + uint32_t flags; /**< JACK_CONSTRAINT_FLAG_XXX */ + + union { + struct { + jack_driver_param_value_t min; + jack_driver_param_value_t max; + } range; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is set */ + + struct { + uint32_t count; + jack_driver_param_value_enum_t * possible_values_array; + } enumeration; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is not set */ + } constraint; +} jack_driver_param_constraint_desc_t; + +/** A driver parameter descriptor */ +typedef struct { + char name[JACK_DRIVER_NAME_MAX + 1]; /**< The parameter's name */ + char character; /**< The parameter's character (for getopt, etc) */ + jack_driver_param_type_t type; /**< The parameter's type */ + jack_driver_param_value_t value; /**< The parameter's (default) value */ + jack_driver_param_constraint_desc_t * constraint; /**< Pointer to parameter constraint descriptor. NULL if there is no constraint */ + char short_desc[64]; /**< A short (~30 chars) description for the user */ + char long_desc[1024]; /**< A longer description for the user */ +} +jack_driver_param_desc_t; + +/** A driver parameter */ +typedef struct { + char character; + jack_driver_param_value_t value; +} +jack_driver_param_t; + +/** A struct for describing a jack driver */ +typedef struct { + char name[JACK_DRIVER_NAME_MAX + 1]; /**< The driver's canonical name */ + jack_driver_type_t type; /**< The driver's type */ + char desc[JACK_DRIVER_PARAM_DESC + 1]; /**< The driver's extended description */ + char file[JACK_PATH_MAX + 1]; /**< The filename of the driver's shared object file */ + uint32_t nparams; /**< The number of parameters the driver has */ + jack_driver_param_desc_t * params; /**< An array of parameter descriptors */ +} +jack_driver_desc_t; + +typedef struct { + uint32_t size; /* size of the param array, in elements */ +} +jack_driver_desc_filler_t; + +SERVER_EXPORT int jack_parse_driver_params(jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr); + +SERVER_EXPORT jack_driver_desc_t * /* newlly allocated driver descriptor, NULL on failure */ +jack_driver_descriptor_construct( + const char * name, /* driver name */ + jack_driver_type_t type, /* driver type */ + const char * description, /* driver description */ + jack_driver_desc_filler_t * filler); /* Pointer to stack var to be supplied to jack_driver_descriptor_add_parameter() as well. + Can be NULL for drivers that have no parameters. */ + +SERVER_EXPORT int /* 0 on failure */ +jack_driver_descriptor_add_parameter( + jack_driver_desc_t * driver_descr, /* pointer to driver descriptor as returned by jack_driver_descriptor_construct() */ + jack_driver_desc_filler_t * filler, /* Pointer to the stack var that was supplied to jack_driver_descriptor_add_parameter(). */ + const char * name, /* parameter's name */ + char character, /* parameter's character (for getopt, etc) */ + jack_driver_param_type_t type, /* The parameter's type */ + const jack_driver_param_value_t * value_ptr, /* Pointer to parameter's (default) value */ + jack_driver_param_constraint_desc_t * constraint, /* Pointer to parameter constraint descriptor. NULL if there is no constraint */ + const char * short_desc, /* A short (~30 chars) description for the user */ + const char * long_desc); /* A longer description for the user, if NULL short_desc will be used */ #ifdef __cplusplus } diff --git a/common/jack/control.h b/common/jack/control.h index b2c53ab0..61b393de 100644 --- a/common/jack/control.h +++ b/common/jack/control.h @@ -45,6 +45,13 @@ typedef enum JackParamBool, /**< @brief value type is a boolean */ } jackctl_param_type_t; +/** Driver types */ +typedef enum +{ + JackMaster = 1, /**< @brief master driver */ + JackSlave /**< @brief slave driver */ +} jackctl_driver_type_t; + /** @brief Max value that jackctl_param_type_t type can have */ #define JACK_PARAM_MAX (JackParamBool + 1) @@ -82,7 +89,7 @@ extern "C" { #endif /** - * @defgroup ServerControl Controling the server + * @defgroup ControlAPI The API for starting and controlling a JACK server * @{ */ @@ -298,6 +305,18 @@ const char * jackctl_driver_get_name( jackctl_driver_t * driver); +/** + * Call this function to get type of driver. + * + * @param driver driver object handle to get name of + * + * @return driver type. Must not be modified. Always same for same + * driver object. + */ +jackctl_driver_type_t +jackctl_driver_get_type( + jackctl_driver_t * driver); + /** * Call this function to get list of driver parameters. List node data * pointers is a parameter object handle (::jackctl_parameter_t). diff --git a/common/jack/jack.h b/common/jack/jack.h index 40f06ddd..03d601b0 100644 --- a/common/jack/jack.h +++ b/common/jack/jack.h @@ -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 diff --git a/common/jack/midiport.h b/common/jack/midiport.h index 72780740..7e52d112 100644 --- a/common/jack/midiport.h +++ b/common/jack/midiport.h @@ -53,7 +53,7 @@ typedef struct _jack_midi_event * @param port_buffer Port buffer from which to retrieve event. * @return number of events inside @a port_buffer */ -jack_nframes_t +uint32_t jack_midi_get_event_count(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT; @@ -70,8 +70,8 @@ jack_midi_get_event_count(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT; */ int jack_midi_event_get(jack_midi_event_t *event, - void *port_buffer, - jack_nframes_t event_index) JACK_OPTIONAL_WEAK_EXPORT; + void *port_buffer, + uint32_t event_index) JACK_OPTIONAL_WEAK_EXPORT; /** Clear an event buffer. @@ -158,7 +158,7 @@ jack_midi_event_write(void *port_buffer, * @param port_buffer Port to receive count for. * @returns Number of events that could not be written to @a port_buffer. */ -jack_nframes_t +uint32_t jack_midi_get_lost_event_count(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT; /*@}*/ diff --git a/common/jack/net.h b/common/jack/net.h new file mode 100644 index 00000000..4c9a389b --- /dev/null +++ b/common/jack/net.h @@ -0,0 +1,316 @@ +/* + Copyright (C) 2009-2010 Grame + + 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 __net_h__ +#define __net_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#define DEFAULT_MULTICAST_IP "225.3.19.154" +#define DEFAULT_PORT 19000 +#define DEFAULT_MTU 1500 +#define MASTER_NAME_SIZE 256 + +#define SOCKET_ERROR -1 + +enum JackNetEncoder { + + JackFloatEncoder = 0, // samples are transmitted as float + JackIntEncoder = 1, // samples are transmitted as 16 bits integer + JackCeltEncoder = 2, // samples are transmitted using CELT codec (http://www.celt-codec.org/) +}; + +typedef struct { + + int audio_input; // from master or to slave (-1 for get master audio physical outputs) + int audio_output; // to master or from slave (-1 for get master audio physical inputs) + int midi_input; // from master or to slave (-1 for get master MIDI physical outputs) + int midi_output; // to master or from slave (-1 for get master MIDI physical inputs) + int mtu; // network Maximum Transmission Unit + int time_out; // in second, -1 means in infinite + int encoder; // encoder type (one of JackNetEncoder) + int kbps; // KB per second for CELT encoder + int latency; // network latency + +} jack_slave_t; + +typedef struct { + + int audio_input; // master audio physical outputs + int audio_output; // master audio physical inputs + int midi_input; // master MIDI physical outputs + int midi_output; // master MIDI physical inputs + jack_nframes_t buffer_size; // mater buffer size + jack_nframes_t sample_rate; // mater sample rate + char master_name[MASTER_NAME_SIZE]; // master machine name + +} jack_master_t; + +/** + * jack_net_slave_t is an opaque type. You may only access it using the + * API provided. + */ +typedef struct _jack_net_slave jack_net_slave_t; + + /** + * Open a network connection with the master machine. + * @param ip the multicast address of the master + * @param port the connection port + * @param request a connection request structure + * @param result a connection result structure + * + * @return Opaque net handle if successful or NULL in case of error. + */ +jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result); + +/** + * Close the network connection with the master machine. + * @param net the network connection to be closed + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_slave_close(jack_net_slave_t* net); + +/** + * Prototype for Process callback. + * @param nframes buffer size + * @param audio_input number of audio inputs + * @param audio_input_buffer an array of audio input buffers (from master) + * @param midi_input number of MIDI inputs + * @param midi_input_buffer an array of MIDI input buffers (from master) + * @param audio_output number of audio outputs + * @param audio_output_buffer an array of audio output buffers (to master) + * @param midi_output number of MIDI outputs + * @param midi_output_buffer an array of MIDI output buffers (to master) + * @param arg pointer to a client supplied structure supplied by jack_set_net_process_callback() + * + * @return zero on success, non-zero on error + */ +typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size, + int audio_input, + float** audio_input_buffer, + int midi_input, + void** midi_input_buffer, + int audio_output, + float** audio_output_buffer, + int midi_output, + void** midi_output_buffer, + void* data); + +/** + * Set network process callback. + * @param net the network connection + * @param net_callback the process callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_process_callback(jack_net_slave_t * net, JackNetSlaveProcessCallback net_callback, void *arg); + +/** + * Start processing thread, the net_callback will start to be called. + * @param net the network connection + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_slave_activate(jack_net_slave_t* net); + +/** + * Stop processing thread. + * @param net the network connection + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_slave_deactivate(jack_net_slave_t* net); + +/** + * Prototype for BufferSize callback. + * @param nframes buffer size + * @param arg pointer to a client supplied structure supplied by jack_set_net_buffer_size_callback() + * + * @return zero on success, non-zero on error + */ +typedef int (*JackNetSlaveBufferSizeCallback)(jack_nframes_t nframes, void *arg); + +/** + * Prototype for SampleRate callback. + * @param nframes sample rate + * @param arg pointer to a client supplied structure supplied by jack_set_net_sample_rate_callback() + * + * @return zero on success, non-zero on error + */ +typedef int (*JackNetSlaveSampleRateCallback)(jack_nframes_t nframes, void *arg); + +/** + * Set network buffer size callback. + * @param net the network connection + * @param bufsize_callback the buffer size callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg); + +/** + * Set network sample rate callback. + * @param net the network connection + * @param samplerate_callback the sample rate callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg); + +/** + * Prototype for server Shutdown callback (if not set, the client will just restart, waiting for an available master again). + * @param arg pointer to a client supplied structure supplied by jack_set_net_shutdown_callback() + */ +typedef void (*JackNetSlaveShutdownCallback)(void* data); + +/** + * Set network shutdown callback. + * @param net the network connection + * @param shutdown_callback the shutdown callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg); + +/** + * jack_net_master_t is an opaque type, you may only access it using the API provided. + */ +typedef struct _jack_net_master jack_net_master_t; + + /** + * Open a network connection with the slave machine. + * @param ip the multicast address of the master + * @param port the connection port + * @param request a connection request structure + * @param result a connection result structure + * + * @return Opaque net handle if successful or NULL in case of error. + */ +jack_net_master_t* jack_net_master_open(const char* ip, int port, const char* name, jack_master_t* request, jack_slave_t* result); + +/** + * Close the network connection with the slave machine. + * @param net the network connection to be closed + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_master_close(jack_net_master_t* net); + +/** + * Receive sync and data from the network. + * @param net the network connection + * @param audio_input number of audio inputs + * @param audio_input_buffer an array of audio input buffers + * @param midi_input number of MIDI inputs + * @param midi_input_buffer an array of MIDI input buffers + * + * @return zero on success, non-zero on error + */ +int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer); + +/** + * Send sync and data to the network. + * @param net the network connection + * @param audio_output number of audio outputs + * @param audio_output_buffer an array of audio output buffers + * @param midi_output number of MIDI ouputs + * @param midi_output_buffer an array of MIDI output buffers + * + * @return zero on success, non-zero on error + */ +int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer); + +// Experimental Adapter API + +/** + * jack_adapter_t is an opaque type, you may only access it using the API provided. + */ +typedef struct _jack_adapter jack_adapter_t; + +/** + * Create an adapter. + * @param input number of audio inputs + * @param output of audio outputs + * @param host_buffer_size the host buffer size in frames + * @param host_sample_rate the host buffer sample rate + * @param adapted_buffer_size the adapted buffer size in frames + * @param adapted_sample_rate the adapted buffer sample rate + * + * @return 0 on success, otherwise a non-zero error code + */ +jack_adapter_t* jack_create_adapter(int input, int output, + jack_nframes_t host_buffer_size, + jack_nframes_t host_sample_rate, + jack_nframes_t adapted_buffer_size, + jack_nframes_t adapted_sample_rate); + +/** + * Destroy an adapter. + * @param adapter the adapter to be destroyed + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_destroy_adapter(jack_adapter_t* adapter); + +/** + * Flush internal state of an adapter. + * @param adapter the adapter to be flushed + * + * @return 0 on success, otherwise a non-zero error code + */ +void jack_flush_adapter(jack_adapter_t* adapter); + +/** + * Push input to and pull output from adapter ringbuffer. + * @param adapter the adapter + * @param input an array of audio input buffers + * @param output an array of audio ouput buffers + * @param frames number of frames + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); + +/** + * Pull input to and push output from adapter ringbuffer. + * @param adapter the adapter + * @param input an array of audio input buffers + * @param output an array of audio ouput buffers + * @param frames number of frames + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); + +#ifdef __cplusplus +} +#endif + +#endif /* __net_h__ */ diff --git a/common/jack/session.h b/common/jack/session.h index fd0469c6..5d2fad98 100644 --- a/common/jack/session.h +++ b/common/jack/session.h @@ -37,7 +37,7 @@ extern "C" { /** * Session event type. * - * if a client cant save templates, i might just do a normal save. + * If a client cant save templates, i might just do a normal save. * * There is no "quit without saving" event because a client might refuse to * quit when it has unsaved data, but other clients may have already quit. @@ -190,19 +190,20 @@ int jack_session_reply (jack_client_t *client, /** - * free memory used by a jack_session_event_t - * this also frees the memory used by the command_line pointer. - * if its non NULL. + * Free memory used by a jack_session_event_t. + * + * This also frees the memory used by the command_line pointer, if its non NULL. */ void jack_session_event_free (jack_session_event_t *event) JACK_WEAK_EXPORT; /** - * get the assigned uuid for client. - * safe to call from callback and all other threads. - * memory needs to be freed. + * Get the assigned uuid for client. + * Safe to call from callback and all other threads. + * + * The caller is responsible for calling jack_free(3) on any non-NULL + * returned value. */ - char *jack_client_get_uuid (jack_client_t *client) JACK_WEAK_EXPORT; /** @@ -242,7 +243,11 @@ void jack_session_commands_free (jack_session_command_t *cmds) JACK_WEAK_EXPORT; /** * Get the session ID for a client name. + * * The session manager needs this to reassociate a client name to the session_id. + * + * The caller is responsible for calling jack_free(3) on any non-NULL + * returned value. */ char *jack_get_uuid_for_client_name (jack_client_t *client, const char *client_name) JACK_WEAK_EXPORT; @@ -252,6 +257,9 @@ char *jack_get_uuid_for_client_name (jack_client_t *client, * * In order to snapshot the graph connections, the session manager needs to map * session_ids to client names. + * + * The caller is responsible for calling jack_free(3) on any non-NULL + * returned value. */ char *jack_get_client_name_by_uuid (jack_client_t *client, const char *client_uuid ) JACK_WEAK_EXPORT; diff --git a/common/jack/systemdeps.h b/common/jack/systemdeps.h index ee79ad3f..6b1b8a7f 100644 --- a/common/jack/systemdeps.h +++ b/common/jack/systemdeps.h @@ -20,13 +20,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef __jack_systemdeps_h__ #define __jack_systemdeps_h__ -#ifdef WIN32 +#if defined(WIN32) && !defined(__CYGWIN__) && !defined(GNU_WIN32) #include #ifdef _MSC_VER /* Microsoft compiler */ #define __inline__ inline - #ifndef int8_t + #if (!defined(int8_t) && !defined(_STDINT_H)) + #define __int8_t_defined typedef char int8_t; typedef unsigned char uint8_t; typedef short int16_t; @@ -36,28 +37,39 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. typedef LONGLONG int64_t; typedef ULONGLONG uint64_t; #endif - /** - * to make jack API independent of different thread implementations, - * we define jack_native_thread_t to HANDLE here. - */ - typedef HANDLE jack_native_thread_t; #elif __MINGW32__ /* MINGW */ #include #include +#else /* other compilers ...*/ + #include + #include + #include +#endif + +#if !defined(_PTHREAD_H) && !defined(PTHREAD_WIN32) /** * to make jack API independent of different thread implementations, * we define jack_native_thread_t to HANDLE here. */ typedef HANDLE jack_native_thread_t; -#else /* other compilers ...*/ - #include - #include - #include +#else + #ifdef PTHREAD_WIN32 // Added by JE - 10-10-2011 + #include // Makes sure we #include the ptw32 version ! + #endif + /** + * to make jack API independent of different thread implementations, + * we define jack_native_thread_t to pthread_t here. + */ + typedef pthread_t jack_native_thread_t; #endif -#endif /* WIN32 */ +#endif // WIN32 && !__CYGWIN__ && !GNU_WIN32 */ -#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) +#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) + +#if defined(__CYGWIN__) || defined(GNU_WIN32) + #include +#endif #include #include #include diff --git a/common/jack/thread.h b/common/jack/thread.h index 28c8cd10..a4c19cd3 100644 --- a/common/jack/thread.h +++ b/common/jack/thread.h @@ -114,7 +114,7 @@ int jack_drop_real_time_scheduling (jack_native_thread_t thread) JACK_OPTIONAL_W int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT; /** - * Cancel the thread then waits for the thread handler to terminate. + * Kill the thread. * * @param thread POSIX thread ID. * diff --git a/common/jack/transport.h b/common/jack/transport.h index e8ba8cbb..4cec6e03 100644 --- a/common/jack/transport.h +++ b/common/jack/transport.h @@ -1,19 +1,19 @@ /* Copyright (C) 2002 Paul Davis Copyright (C) 2003 Jack O'Quin - + 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 + along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -135,7 +135,7 @@ int jack_set_timebase_callback (jack_client_t *client, * sync_callbacks until ready. This function is realtime-safe. * * @see jack_transport_reposition, jack_set_sync_callback - * + * * @param client the JACK client structure. * @param frame frame number of new transport position. * @@ -170,7 +170,7 @@ jack_transport_state_t jack_transport_query (const jack_client_t *client, * @param client the JACK client structure */ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; - + /** * Request a new transport position. * @@ -181,14 +181,14 @@ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) JA * sync_callbacks until ready. This function is realtime-safe. * * @see jack_transport_locate, jack_set_sync_callback - * + * * @param client the JACK client structure. * @param pos requested new transport position. * * @return 0 if valid request, EINVAL if position structure rejected. */ int jack_transport_reposition (jack_client_t *client, - jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT; + const jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT; /** * Start the JACK transport rolling. @@ -239,7 +239,7 @@ void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT; /*@}*/ - + #ifdef __cplusplus } #endif diff --git a/common/jack/weakjack.h b/common/jack/weakjack.h index 83f05ab2..652d58ab 100644 --- a/common/jack/weakjack.h +++ b/common/jack/weakjack.h @@ -21,17 +21,29 @@ #define __weakjack_h__ /** - * @defgroup WeakLinkage managing support for newer/older versions of JACK - * @{ One challenge faced by developers is that of taking advantage of new features introduced in new versions of [ JACK ] while still - * supporting older versions of the system. Normally, if an application uses a new feature in a library/API, it is unable to run on - * earlier versions of the library/API that do not support that feature. Such applications would either fail to launch or crash when - * an attempt to use the feature was made. This problem cane be solved using weakly-linked symbols. + * @defgroup WeakLinkage Managing support for newer/older versions of JACK + * @{ One challenge faced by developers is that of taking + * advantage of new features introduced in new versions + * of [ JACK ] while still supporting older versions of + * the system. Normally, if an application uses a new + * feature in a library/API, it is unable to run on + * earlier versions of the library/API that do not + * support that feature. Such applications would either + * fail to launch or crash when an attempt to use the + * feature was made. This problem cane be solved using + * weakly-linked symbols. * - * When a symbol in a framework is defined as weakly linked, the symbol does not have to be present at runtime for a process to - * continue running. The static linker identifies a weakly linked symbol as such in any code module that references the symbol. The - * dynamic linker uses this same information at runtime to determine whether a process can continue running. If a weakly linked symbol - * is not present in the framework, the code module can continue to run as long as it does not reference the symbol. However, if the - * symbol is present, the code can use it normally. + * When a symbol in a framework is defined as weakly + * linked, the symbol does not have to be present at + * runtime for a process to continue running. The static + * linker identifies a weakly linked symbol as such in + * any code module that references the symbol. The + * dynamic linker uses this same information at runtime + * to determine whether a process can continue + * running. If a weakly linked symbol is not present in + * the framework, the code module can continue to run as + * long as it does not reference the symbol. However, if + * the symbol is present, the code can use it normally. * * (adapted from: http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html) * @@ -73,6 +85,12 @@ * */ +#ifdef __APPLE__ +#define WEAK_ATTRIBUTE weak_import +#else +#define WEAK_ATTRIBUTE __weak__ +#endif + #ifndef JACK_OPTIONAL_WEAK_EXPORT /* JACK_OPTIONAL_WEAK_EXPORT needs to be a macro which expands into a compiler directive. If non-null, the directive @@ -81,7 +99,7 @@ require linker arguments for the client as well. */ #ifdef __GNUC__ -#define JACK_OPTIONAL_WEAK_EXPORT __attribute__((__weak__)) +#define JACK_OPTIONAL_WEAK_EXPORT __attribute__((WEAK_ATTRIBUTE)) #else /* Add other things here for non-gcc platforms */ #endif @@ -96,7 +114,7 @@ linker arguments for the client as well. */ #ifdef __GNUC__ -#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((__weak__,__deprecated__)) +#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((WEAK_ATTRIBUTE,__deprecated__)) #else /* Add other things here for non-gcc platforms */ #endif diff --git a/common/jack/weakmacros.h b/common/jack/weakmacros.h index d5818b34..9d4959e7 100644 --- a/common/jack/weakmacros.h +++ b/common/jack/weakmacros.h @@ -32,6 +32,12 @@ * before jack.h. *************************************************************/ +#ifdef __APPLE__ +#define WEAK_ATTRIBUTE weak_import +#else +#define WEAK_ATTRIBUTE __weak__ +#endif + #ifndef JACK_WEAK_EXPORT #ifdef __GNUC__ /* JACK_WEAK_EXPORT needs to be a macro which @@ -40,13 +46,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 + #define JACK_WEAK_EXPORT __attribute__((WEAK_ATTRIBUTE)) +#endif + #else -/* Add other things here for non-gcc platforms */ - -#ifdef WIN32 -#define JACK_WEAK_EXPORT -#endif +/* Add other things here for non-gcc platforms */ + +#ifdef WIN32 +#define JACK_WEAK_EXPORT +#endif #endif #endif @@ -59,13 +75,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__ */ + diff --git a/common/netjack.c b/common/netjack.c index e3ec0c33..075b97cd 100644 --- a/common/netjack.c +++ b/common/netjack.c @@ -57,9 +57,7 @@ $Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $ #include "netjack.h" #include "netjack_packet.h" - -// JACK2 -#include "control.h" +#include "JackError.h" #define MIN(x,y) ((x)<(y) ? (x) : (y)) @@ -88,26 +86,25 @@ int netjack_wait( netjack_driver_state_t *netj ) jacknet_packet_header *pkthdr; if( !netj->next_deadline_valid ) { - netj->next_deadline = jack_get_time() + netj->period_usecs; - netj->next_deadline_valid = 1; + netj->next_deadline = jack_get_time() + netj->period_usecs; + netj->next_deadline_valid = 1; } // Increment expected frame here. if( netj->expected_framecnt_valid ) { - netj->expected_framecnt += 1; + netj->expected_framecnt += 1; } else { - // starting up.... lets look into the packetcache, and fetch the highest packet. - packet_cache_drain_socket( netj->packcache, netj->sockfd ); - if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail ) ) { - netj->expected_framecnt = next_frame_avail; - netj->expected_framecnt_valid = 1; - } else { - // no packets there... start normally. - netj->expected_framecnt = 0; - netj->expected_framecnt_valid = 1; - } - + // starting up.... lets look into the packetcache, and fetch the highest packet. + packet_cache_drain_socket( netj->packcache, netj->sockfd ); + if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail ) ) { + netj->expected_framecnt = next_frame_avail; + netj->expected_framecnt_valid = 1; + } else { + // no packets there... start normally. + netj->expected_framecnt = 0; + netj->expected_framecnt_valid = 1; + } } //jack_log( "expect %d", netj->expected_framecnt ); @@ -115,26 +112,26 @@ int netjack_wait( netjack_driver_state_t *netj ) // then poll (have deadline calculated) // then drain socket, rinse and repeat. while(1) { - if( packet_cache_get_next_available_framecnt( netj->packcache, netj->expected_framecnt, &next_frame_avail) ) { - if( next_frame_avail == netj->expected_framecnt ) { - we_have_the_expected_frame = 1; - if( !netj->always_deadline ) - break; - } - } - if( ! netjack_poll_deadline( netj->sockfd, netj->next_deadline ) ) { - break; - } - - packet_cache_drain_socket( netj->packcache, netj->sockfd ); + if( packet_cache_get_next_available_framecnt( netj->packcache, netj->expected_framecnt, &next_frame_avail) ) { + if( next_frame_avail == netj->expected_framecnt ) { + we_have_the_expected_frame = 1; + if( !netj->always_deadline ) + break; + } + } + if( ! netjack_poll_deadline( netj->sockfd, netj->next_deadline ) ) { + break; + } + + packet_cache_drain_socket( netj->packcache, netj->sockfd ); } // check if we know who to send our packets too. if (!netj->srcaddress_valid) - if( netj->packcache->master_address_valid ) { - memcpy (&(netj->syncsource_address), &(netj->packcache->master_address), sizeof( struct sockaddr_in ) ); - netj->srcaddress_valid = 1; - } + if( netj->packcache->master_address_valid ) { + memcpy (&(netj->syncsource_address), &(netj->packcache->master_address), sizeof( struct sockaddr_in ) ); + netj->srcaddress_valid = 1; + } // XXX: switching mode unconditionally is stupid. // if we were running free perhaps we like to behave differently @@ -148,169 +145,168 @@ int netjack_wait( netjack_driver_state_t *netj ) if( we_have_the_expected_frame ) { - jack_time_t now = jack_get_time(); - if( now < netj->next_deadline ) - netj->time_to_deadline = netj->next_deadline - now; - else - netj->time_to_deadline = 0; - - packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize , &packet_recv_time_stamp); - pkthdr = (jacknet_packet_header *) netj->rx_buf; - packet_header_ntoh(pkthdr); - netj->deadline_goodness = (int)pkthdr->sync_state; - netj->packet_data_valid = 1; - - int want_deadline; - if( netj->jitter_val != 0 ) - want_deadline = netj->jitter_val; - else if( netj->latency < 4 ) - want_deadline = -netj->period_usecs/2; - else - want_deadline = (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100); - - if( netj->deadline_goodness != MASTER_FREEWHEELS ) { - if( netj->deadline_goodness < want_deadline ) { - netj->next_deadline -= netj->period_usecs/100; - //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); - } - if( netj->deadline_goodness > want_deadline ) { - netj->next_deadline += netj->period_usecs/100; - //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); - } - } + jack_time_t now = jack_get_time(); + if( now < netj->next_deadline ) + netj->time_to_deadline = netj->next_deadline - now; + else + netj->time_to_deadline = 0; + + packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) & (netj->rx_buf), netj->rx_bufsize , &packet_recv_time_stamp); + pkthdr = (jacknet_packet_header *) netj->rx_buf; + packet_header_ntoh(pkthdr); + netj->deadline_goodness = (int)pkthdr->sync_state; + netj->packet_data_valid = 1; + + int want_deadline; + if( netj->jitter_val != 0 ) + want_deadline = netj->jitter_val; + else if( netj->latency < 4 ) + want_deadline = -netj->period_usecs / 2; + else + want_deadline = (netj->period_usecs / 4 + 10 * (int)netj->period_usecs * netj->latency / 100); + + if( netj->deadline_goodness != MASTER_FREEWHEELS ) { + if( netj->deadline_goodness < want_deadline ) { + netj->next_deadline -= netj->period_usecs / 100; + //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); + } + if( netj->deadline_goodness > want_deadline ) { + netj->next_deadline += netj->period_usecs / 100; + //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); + } + } // if( netj->next_deadline < (netj->period_usecs*70/100) ) { // jack_error( "master is forcing deadline_offset to below 70%% of period_usecs... increase latency setting on master" ); // netj->deadline_offset = (netj->period_usecs*90/100); // } - netj->next_deadline += netj->period_usecs; + netj->next_deadline += netj->period_usecs; } else { - netj->time_to_deadline = 0; - netj->next_deadline += netj->period_usecs; - // bah... the packet is not there. - // either - // - it got lost. - // - its late - // - sync source is not sending anymore. - - // lets check if we have the next packets, we will just run a cycle without data. - // in that case. - - if( packet_cache_get_next_available_framecnt( netj->packcache, netj->expected_framecnt, &next_frame_avail) ) - { - jack_nframes_t offset = next_frame_avail - netj->expected_framecnt; - - //XXX: hmm... i need to remember why resync_threshold wasnt right. - //if( offset < netj->resync_threshold ) - if( offset < 10 ) { - // ok. dont do nothing. we will run without data. - // this seems to be one or 2 lost packets. - // - // this can also be reordered packet jitter. - // (maybe this is not happening in real live) - // but it happens in netem. - - netj->packet_data_valid = 0; - - // I also found this happening, when the packet queue, is too full. - // but wtf ? use a smaller latency. this link can handle that ;S - if( packet_cache_get_fill( netj->packcache, netj->expected_framecnt ) > 80.0 ) - netj->next_deadline -= netj->period_usecs/2; - - - } else { - // the diff is too high. but we have a packet in the future. - // lets resync. - netj->expected_framecnt = next_frame_avail; - packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL ); - pkthdr = (jacknet_packet_header *) netj->rx_buf; - packet_header_ntoh(pkthdr); - //netj->deadline_goodness = 0; - netj->deadline_goodness = (int)pkthdr->sync_state - (int)netj->period_usecs * offset; - netj->next_deadline_valid = 0; - netj->packet_data_valid = 1; - } - - } else { - // no packets in buffer. - netj->packet_data_valid = 0; - - //printf( "frame %d No Packet in queue. num_lost_packets = %d \n", netj->expected_framecnt, netj->num_lost_packets ); - if( netj->num_lost_packets < 5 ) { - // ok. No Packet in queue. The packet was either lost, - // or we are running too fast. - // - // Adjusting the deadline unconditionally resulted in - // too many xruns on master. - // But we need to adjust for the case we are running too fast. - // So lets check if the last packet is there now. - // - // It would not be in the queue anymore, if it had been - // retrieved. This might break for redundancy, but - // i will make the packet cache drop redundant packets, - // that have already been retreived. - // - if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { - if( next_frame_avail == (netj->expected_framecnt - 1) ) { - // Ok. the last packet is there now. - // and it had not been retrieved. - // - // TODO: We are still dropping 2 packets. - // perhaps we can adjust the deadline - // when (num_packets lost == 0) - - // This might still be too much. - netj->next_deadline += netj->period_usecs; - } - } - } else if( (netj->num_lost_packets <= 100) ) { - // lets try adjusting the deadline harder, for some packets, we might have just ran 2 fast. - netj->next_deadline += netj->period_usecs*netj->latency/8; - } else { - - // But now we can check for any new frame available. - // - if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { - netj->expected_framecnt = next_frame_avail; - packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL ); - pkthdr = (jacknet_packet_header *) netj->rx_buf; - packet_header_ntoh(pkthdr); - netj->deadline_goodness = pkthdr->sync_state; - netj->next_deadline_valid = 0; - netj->packet_data_valid = 1; - netj->running_free = 0; - jack_info( "resync after freerun... %d", netj->expected_framecnt ); - } else { - if( netj->num_lost_packets == 101 ) { - jack_info( "master seems gone... entering freerun mode", netj->expected_framecnt ); - } - - netj->running_free = 1; - - // when we really dont see packets. - // reset source address. and open possibility for new master. - // maybe dsl reconnect. Also restart of netsource without fix - // reply address changes port. - if (netj->num_lost_packets > 200 ) { - netj->srcaddress_valid = 0; - packet_cache_reset_master_address( netj->packcache ); - } - } - } - } + netj->time_to_deadline = 0; + netj->next_deadline += netj->period_usecs; + // bah... the packet is not there. + // either + // - it got lost. + // - its late + // - sync source is not sending anymore. + + // lets check if we have the next packets, we will just run a cycle without data. + // in that case. + + if( packet_cache_get_next_available_framecnt( netj->packcache, netj->expected_framecnt, &next_frame_avail) ) { + jack_nframes_t offset = next_frame_avail - netj->expected_framecnt; + + //XXX: hmm... i need to remember why resync_threshold wasnt right. + //if( offset < netj->resync_threshold ) + if( offset < 10 ) { + // ok. dont do nothing. we will run without data. + // this seems to be one or 2 lost packets. + // + // this can also be reordered packet jitter. + // (maybe this is not happening in real live) + // but it happens in netem. + + netj->packet_data_valid = 0; + + // I also found this happening, when the packet queue, is too full. + // but wtf ? use a smaller latency. this link can handle that ;S + if( packet_cache_get_fill( netj->packcache, netj->expected_framecnt ) > 80.0 ) + netj->next_deadline -= netj->period_usecs / 2; + + + } else { + // the diff is too high. but we have a packet in the future. + // lets resync. + netj->expected_framecnt = next_frame_avail; + packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) & (netj->rx_buf), netj->rx_bufsize, NULL ); + pkthdr = (jacknet_packet_header *) netj->rx_buf; + packet_header_ntoh(pkthdr); + //netj->deadline_goodness = 0; + netj->deadline_goodness = (int)pkthdr->sync_state - (int)netj->period_usecs * offset; + netj->next_deadline_valid = 0; + netj->packet_data_valid = 1; + } + + } else { + // no packets in buffer. + netj->packet_data_valid = 0; + + //printf( "frame %d No Packet in queue. num_lost_packets = %d \n", netj->expected_framecnt, netj->num_lost_packets ); + if( netj->num_lost_packets < 5 ) { + // ok. No Packet in queue. The packet was either lost, + // or we are running too fast. + // + // Adjusting the deadline unconditionally resulted in + // too many xruns on master. + // But we need to adjust for the case we are running too fast. + // So lets check if the last packet is there now. + // + // It would not be in the queue anymore, if it had been + // retrieved. This might break for redundancy, but + // i will make the packet cache drop redundant packets, + // that have already been retreived. + // + if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { + if( next_frame_avail == (netj->expected_framecnt - 1) ) { + // Ok. the last packet is there now. + // and it had not been retrieved. + // + // TODO: We are still dropping 2 packets. + // perhaps we can adjust the deadline + // when (num_packets lost == 0) + + // This might still be too much. + netj->next_deadline += netj->period_usecs; + } + } + } else if( (netj->num_lost_packets <= 100) ) { + // lets try adjusting the deadline harder, for some packets, we might have just ran 2 fast. + netj->next_deadline += netj->period_usecs * netj->latency / 8; + } else { + + // But now we can check for any new frame available. + // + if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { + netj->expected_framecnt = next_frame_avail; + packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) & (netj->rx_buf), netj->rx_bufsize, NULL ); + pkthdr = (jacknet_packet_header *) netj->rx_buf; + packet_header_ntoh(pkthdr); + netj->deadline_goodness = pkthdr->sync_state; + netj->next_deadline_valid = 0; + netj->packet_data_valid = 1; + netj->running_free = 0; + jack_info( "resync after freerun... %d", netj->expected_framecnt ); + } else { + if( netj->num_lost_packets == 101 ) { + jack_info( "master seems gone... entering freerun mode", netj->expected_framecnt ); + } + + netj->running_free = 1; + + // when we really dont see packets. + // reset source address. and open possibility for new master. + // maybe dsl reconnect. Also restart of netsource without fix + // reply address changes port. + if (netj->num_lost_packets > 200 ) { + netj->srcaddress_valid = 0; + packet_cache_reset_master_address( netj->packcache ); + } + } + } + } } int retval = 0; if( !netj->packet_data_valid ) { - netj->num_lost_packets += 1; - if( netj->num_lost_packets == 1 ) - retval = netj->period_usecs; + netj->num_lost_packets += 1; + if( netj->num_lost_packets == 1 ) + retval = netj->period_usecs; } else { - if( (netj->num_lost_packets>1) && !netj->running_free ) - retval = (netj->num_lost_packets-1) * netj->period_usecs; + if( (netj->num_lost_packets > 1) && !netj->running_free ) + retval = (netj->num_lost_packets - 1) * netj->period_usecs; - netj->num_lost_packets = 0; + netj->num_lost_packets = 0; } return retval; @@ -340,19 +336,17 @@ void netjack_send_silence( netjack_driver_state_t *netj, int syncstate ) memset(packet_bufX, 0, payload_size); packet_header_hton(tx_pkthdr); - if (netj->srcaddress_valid) - { - int r; - if (netj->reply_port) - netj->syncsource_address.sin_port = htons(netj->reply_port); - - for( r=0; rredundancy; r++ ) - netjack_sendto(netj->outsockfd, (char *)packet_buf, tx_size, - 0, (struct sockaddr*)&(netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu); + if (netj->srcaddress_valid) { + int r; + if (netj->reply_port) + netj->syncsource_address.sin_port = htons(netj->reply_port); + + for( r = 0; r < netj->redundancy; r++ ) + netjack_sendto(netj->outsockfd, (char *)packet_buf, tx_size, + 0, (struct sockaddr*) & (netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu); } } - void netjack_attach( netjack_driver_state_t *netj ) { //puts ("net_driver_attach"); @@ -361,19 +355,17 @@ void netjack_attach( netjack_driver_state_t *netj ) unsigned int chn; int port_flags; - - if( netj->bitdepth == CELT_MODE ) - { + if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT #if HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 - celt_int32 lookahead; - netj->celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); + celt_int32 lookahead; + netj->celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); #else - celt_int32_t lookahead; - netj->celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); + celt_int32_t lookahead; + netj->celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); #endif - celt_mode_info( netj->celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); - netj->codec_latency = 2*lookahead; + celt_mode_info( netj->celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); + netj->codec_latency = 2 * lookahead; #endif } @@ -396,21 +388,21 @@ void netjack_attach( netjack_driver_state_t *netj ) netj->capture_ports = jack_slist_append (netj->capture_ports, port); - if( netj->bitdepth == CELT_MODE ) { + if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT #if HAVE_CELT_API_0_11 - netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create_custom( netj->celt_mode, 1, NULL ) ); + netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create_custom( netj->celt_mode, 1, NULL ) ); #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 - netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode, 1, NULL ) ); + netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode, 1, NULL ) ); #else - netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode ) ); + netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode ) ); #endif #endif - } else { + } else { #if HAVE_SAMPLERATE - netj->capture_srcs = jack_slist_append(netj->capture_srcs, src_new(SRC_LINEAR, 1, NULL)); + netj->capture_srcs = jack_slist_append(netj->capture_srcs, src_new(SRC_LINEAR, 1, NULL)); #endif - } + } } for (chn = netj->capture_channels_audio; chn < netj->capture_channels; chn++) { @@ -444,24 +436,24 @@ void netjack_attach( netjack_driver_state_t *netj ) netj->playback_ports = jack_slist_append (netj->playback_ports, port); - if( netj->bitdepth == CELT_MODE ) { + if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT #if HAVE_CELT_API_0_11 - CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); - netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_decoder_create_custom( celt_mode, 1, NULL ) ); + CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); + netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_decoder_create_custom( celt_mode, 1, NULL ) ); #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 - CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); - netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); + CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); + netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); #else - CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); - netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); + CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); + netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); #endif #endif - } else { + } else { #if HAVE_SAMPLERATE - netj->playback_srcs = jack_slist_append(netj->playback_srcs, src_new(SRC_LINEAR, 1, NULL)); + netj->playback_srcs = jack_slist_append(netj->playback_srcs, src_new(SRC_LINEAR, 1, NULL)); #endif - } + } } for (chn = netj->playback_channels_audio; chn < netj->playback_channels; chn++) { snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1); @@ -494,15 +486,12 @@ void netjack_detach( netjack_driver_state_t *netj ) jack_slist_free (netj->capture_ports); netj->capture_ports = NULL; - for (node = netj->capture_srcs; node; node = jack_slist_next (node)) - { + for (node = netj->capture_srcs; node; node = jack_slist_next (node)) { #if HAVE_CELT - if( netj->bitdepth == CELT_MODE ) - { + if( netj->bitdepth == CELT_MODE ) { CELTDecoder * decoder = node->data; celt_decoder_destroy(decoder); - } - else + } else #endif { #if HAVE_SAMPLERATE @@ -521,15 +510,12 @@ void netjack_detach( netjack_driver_state_t *netj ) jack_slist_free (netj->playback_ports); netj->playback_ports = NULL; - for (node = netj->playback_srcs; node; node = jack_slist_next (node)) - { + for (node = netj->playback_srcs; node; node = jack_slist_next (node)) { #if HAVE_CELT - if( netj->bitdepth == CELT_MODE ) - { + if( netj->bitdepth == CELT_MODE ) { CELTEncoder * encoder = node->data; celt_encoder_destroy(encoder); - } - else + } else #endif { #if HAVE_SAMPLERATE @@ -542,32 +528,32 @@ void netjack_detach( netjack_driver_state_t *netj ) netj->playback_srcs = NULL; #if HAVE_CELT - if( netj->bitdepth == CELT_MODE ) - celt_mode_destroy(netj->celt_mode); + if( netj->bitdepth == CELT_MODE ) + celt_mode_destroy(netj->celt_mode); #endif } netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, - jack_client_t * client, - const char *name, - unsigned int capture_ports, - unsigned int playback_ports, - unsigned int capture_ports_midi, - unsigned int playback_ports_midi, - jack_nframes_t sample_rate, - jack_nframes_t period_size, - unsigned int listen_port, - unsigned int transport_sync, - unsigned int resample_factor, - unsigned int resample_factor_up, - unsigned int bitdepth, - unsigned int use_autoconfig, - unsigned int latency, - unsigned int redundancy, - int dont_htonl_floats, - int always_deadline, - int jitter_val ) + jack_client_t * client, + const char *name, + unsigned int capture_ports, + unsigned int playback_ports, + unsigned int capture_ports_midi, + unsigned int playback_ports_midi, + jack_nframes_t sample_rate, + jack_nframes_t period_size, + unsigned int listen_port, + unsigned int transport_sync, + unsigned int resample_factor, + unsigned int resample_factor_up, + unsigned int bitdepth, + unsigned int use_autoconfig, + unsigned int latency, + unsigned int redundancy, + int dont_htonl_floats, + int always_deadline, + int jitter_val ) { // Fill in netj values. @@ -602,8 +588,7 @@ netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, netj->client = client; - if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != CELT_MODE)) - { + if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != CELT_MODE)) { jack_info ("Invalid bitdepth: %d (8, 16 or 0 for float) !!!", bitdepth); return NULL; } @@ -649,8 +634,7 @@ netjack_startup( netjack_driver_state_t *netj ) address.sin_family = AF_INET; address.sin_port = htons(netj->listen_port); address.sin_addr.s_addr = htonl(INADDR_ANY); - if (bind (netj->sockfd, (struct sockaddr *) &address, sizeof (address)) < 0) - { + if (bind (netj->sockfd, (struct sockaddr *) &address, sizeof (address)) < 0) { jack_info("bind error"); return -1; } @@ -666,14 +650,13 @@ netjack_startup( netjack_driver_state_t *netj ) return -1; } netj->srcaddress_valid = 0; - if (netj->use_autoconfig) - { + if (netj->use_autoconfig) { jacknet_packet_header *first_packet = alloca (sizeof (jacknet_packet_header)); - #ifdef WIN32 +#ifdef WIN32 int address_size = sizeof( struct sockaddr_in ); - #else +#else socklen_t address_size = sizeof (struct sockaddr_in); - #endif +#endif //jack_info ("Waiting for an incoming packet !!!"); //jack_info ("*** IMPORTANT *** Dont connect a client to jackd until the driver is attached to a clock source !!!"); @@ -683,51 +666,44 @@ netjack_startup( netjack_driver_state_t *netj ) return -1; } first_pack_len = recvfrom (netj->sockfd, (char *)first_packet, sizeof (jacknet_packet_header), 0, (struct sockaddr*) & netj->syncsource_address, &address_size); - #ifdef WIN32 - if( first_pack_len == -1 ) { - first_pack_len = sizeof(jacknet_packet_header); - break; - } - #else - if (first_pack_len == sizeof (jacknet_packet_header)) - break; - #endif +#ifdef WIN32 + if( first_pack_len == -1 ) { + first_pack_len = sizeof(jacknet_packet_header); + break; + } +#else + if (first_pack_len == sizeof (jacknet_packet_header)) + break; +#endif } netj->srcaddress_valid = 1; - if (first_pack_len == sizeof (jacknet_packet_header)) - { + if (first_pack_len == sizeof (jacknet_packet_header)) { packet_header_ntoh (first_packet); jack_info ("AutoConfig Override !!!"); - if (netj->sample_rate != first_packet->sample_rate) - { + if (netj->sample_rate != first_packet->sample_rate) { jack_info ("AutoConfig Override: Master JACK sample rate = %d", first_packet->sample_rate); netj->sample_rate = first_packet->sample_rate; - } + } - if (netj->period_size != first_packet->period_size) - { + if (netj->period_size != first_packet->period_size) { jack_info ("AutoConfig Override: Master JACK period size is %d", first_packet->period_size); netj->period_size = first_packet->period_size; - } - if (netj->capture_channels_audio != first_packet->capture_channels_audio) - { + } + if (netj->capture_channels_audio != first_packet->capture_channels_audio) { jack_info ("AutoConfig Override: capture_channels_audio = %d", first_packet->capture_channels_audio); netj->capture_channels_audio = first_packet->capture_channels_audio; } - if (netj->capture_channels_midi != first_packet->capture_channels_midi) - { + if (netj->capture_channels_midi != first_packet->capture_channels_midi) { jack_info ("AutoConfig Override: capture_channels_midi = %d", first_packet->capture_channels_midi); netj->capture_channels_midi = first_packet->capture_channels_midi; } - if (netj->playback_channels_audio != first_packet->playback_channels_audio) - { + if (netj->playback_channels_audio != first_packet->playback_channels_audio) { jack_info ("AutoConfig Override: playback_channels_audio = %d", first_packet->playback_channels_audio); netj->playback_channels_audio = first_packet->playback_channels_audio; - } - if (netj->playback_channels_midi != first_packet->playback_channels_midi) - { + } + if (netj->playback_channels_midi != first_packet->playback_channels_midi) { jack_info ("AutoConfig Override: playback_channels_midi = %d", first_packet->playback_channels_midi); netj->playback_channels_midi = first_packet->playback_channels_midi; } @@ -741,24 +717,24 @@ netjack_startup( netjack_driver_state_t *netj ) netj->playback_channels = netj->playback_channels_audio + netj->playback_channels_midi; if( (netj->capture_channels * netj->period_size * netj->latency * 4) > 100000000 ) { - jack_error( "autoconfig requests more than 100MB packet cache... bailing out" ); - exit(1); + jack_error( "autoconfig requests more than 100MB packet cache... bailing out" ); + exit(1); } if( netj->playback_channels > 1000 ) { - jack_error( "autoconfig requests more than 1000 playback channels... bailing out" ); - exit(1); + jack_error( "autoconfig requests more than 1000 playback channels... bailing out" ); + exit(1); } - if( netj->mtu < (2*sizeof( jacknet_packet_header )) ) { - jack_error( "bullshit mtu requested by autoconfig" ); - exit(1); + if( netj->mtu < (2 * sizeof( jacknet_packet_header )) ) { + jack_error( "bullshit mtu requested by autoconfig" ); + exit(1); } if( netj->sample_rate == 0 ) { - jack_error( "sample_rate 0 requested by autoconfig" ); - exit(1); + jack_error( "sample_rate 0 requested by autoconfig" ); + exit(1); } // After possible Autoconfig: do all calculations... @@ -767,15 +743,15 @@ netjack_startup( netjack_driver_state_t *netj ) * 1000000.0f); if( netj->latency == 0 ) - netj->deadline_offset = 50*netj->period_usecs; + netj->deadline_offset = 50 * netj->period_usecs; else - netj->deadline_offset = netj->period_usecs + 10*netj->latency*netj->period_usecs/100; + netj->deadline_offset = netj->period_usecs + 10 * netj->latency * netj->period_usecs / 100; if( netj->bitdepth == CELT_MODE ) { // celt mode. // TODO: this is a hack. But i dont want to change the packet header. - netj->resample_factor = (netj->resample_factor * netj->period_size * 1024 / netj->sample_rate / 8)&(~1); - netj->resample_factor_up = (netj->resample_factor_up * netj->period_size * 1024 / netj->sample_rate / 8)&(~1); + netj->resample_factor = (netj->resample_factor * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); + netj->resample_factor_up = (netj->resample_factor_up * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); netj->net_period_down = netj->resample_factor; netj->net_period_up = netj->resample_factor_up; @@ -797,7 +773,7 @@ netjack_startup( netjack_driver_state_t *netj ) if( netj->latency == 0 ) netj->resync_threshold = 0; else - netj->resync_threshold = MIN( 15, netj->latency-1 ); + netj->resync_threshold = MIN( 15, netj->latency - 1 ); netj->running_free = 0; diff --git a/common/netjack.h b/common/netjack.h index bb745829..2ff3c299 100644 --- a/common/netjack.h +++ b/common/netjack.h @@ -24,130 +24,126 @@ #include #include -//#include #include #include - #include "jack/jslist.h" #if HAVE_CELT #include #endif -//#include - #ifdef __cplusplus extern "C" { #endif -struct _packet_cache; + struct _packet_cache; -typedef struct _netjack_driver_state netjack_driver_state_t; + typedef struct _netjack_driver_state netjack_driver_state_t; -struct _netjack_driver_state { - jack_nframes_t net_period_up; - jack_nframes_t net_period_down; + struct _netjack_driver_state { + jack_nframes_t net_period_up; + jack_nframes_t net_period_down; - jack_nframes_t sample_rate; - jack_nframes_t bitdepth; - jack_nframes_t period_size; - jack_time_t period_usecs; - int dont_htonl_floats; - int always_deadline; + jack_nframes_t sample_rate; + jack_nframes_t bitdepth; + jack_nframes_t period_size; + jack_time_t period_usecs; + int dont_htonl_floats; + int always_deadline; - jack_nframes_t codec_latency; + jack_nframes_t codec_latency; - unsigned int listen_port; + unsigned int listen_port; - unsigned int capture_channels; - unsigned int playback_channels; - unsigned int capture_channels_audio; - unsigned int playback_channels_audio; - unsigned int capture_channels_midi; - unsigned int playback_channels_midi; + unsigned int capture_channels; + unsigned int playback_channels; + unsigned int capture_channels_audio; + unsigned int playback_channels_audio; + unsigned int capture_channels_midi; + unsigned int playback_channels_midi; - JSList *capture_ports; - JSList *playback_ports; - JSList *playback_srcs; - JSList *capture_srcs; + JSList *capture_ports; + JSList *playback_ports; + JSList *playback_srcs; + JSList *capture_srcs; - jack_client_t *client; + jack_client_t *client; #ifdef WIN32 - SOCKET sockfd; - SOCKET outsockfd; + SOCKET sockfd; + SOCKET outsockfd; #else - int sockfd; - int outsockfd; + int sockfd; + int outsockfd; #endif - struct sockaddr_in syncsource_address; - - int reply_port; - int srcaddress_valid; - - int sync_state; - unsigned int handle_transport_sync; - - unsigned int *rx_buf; - unsigned int rx_bufsize; - //unsigned int tx_bufsize; - unsigned int mtu; - unsigned int latency; - unsigned int redundancy; - - jack_nframes_t expected_framecnt; - int expected_framecnt_valid; - unsigned int num_lost_packets; - jack_time_t next_deadline; - jack_time_t deadline_offset; - int next_deadline_valid; - int packet_data_valid; - int resync_threshold; - int running_free; - int deadline_goodness; - jack_time_t time_to_deadline; - unsigned int use_autoconfig; - unsigned int resample_factor; - unsigned int resample_factor_up; - int jitter_val; - struct _packet_cache * packcache; + struct sockaddr_in syncsource_address; + + int reply_port; + int srcaddress_valid; + + int sync_state; + unsigned int handle_transport_sync; + + unsigned int *rx_buf; + unsigned int rx_bufsize; + //unsigned int tx_bufsize; + unsigned int mtu; + unsigned int latency; + unsigned int redundancy; + + jack_nframes_t expected_framecnt; + int expected_framecnt_valid; + unsigned int num_lost_packets; + jack_time_t next_deadline; + jack_time_t deadline_offset; + int next_deadline_valid; + int packet_data_valid; + int resync_threshold; + int running_free; + int deadline_goodness; + jack_time_t time_to_deadline; + unsigned int use_autoconfig; + unsigned int resample_factor; + unsigned int resample_factor_up; + int jitter_val; + struct _packet_cache * packcache; #if HAVE_CELT - CELTMode *celt_mode; + CELTMode *celt_mode; #endif -}; - -int netjack_wait( netjack_driver_state_t *netj ); -void netjack_send_silence( netjack_driver_state_t *netj, int syncstate ); -void netjack_read( netjack_driver_state_t *netj, jack_nframes_t nframes ) ; -void netjack_write( netjack_driver_state_t *netj, jack_nframes_t nframes, int syncstate ); -void netjack_attach( netjack_driver_state_t *netj ); -void netjack_detach( netjack_driver_state_t *netj ); - -netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, - jack_client_t * client, - const char *name, - unsigned int capture_ports, - unsigned int playback_ports, - unsigned int capture_ports_midi, - unsigned int playback_ports_midi, - jack_nframes_t sample_rate, - jack_nframes_t period_size, - unsigned int listen_port, - unsigned int transport_sync, - unsigned int resample_factor, - unsigned int resample_factor_up, - unsigned int bitdepth, - unsigned int use_autoconfig, - unsigned int latency, - unsigned int redundancy, - int dont_htonl_floats, - int always_deadline, - int jitter_val ); - -void netjack_release( netjack_driver_state_t *netj ); -int netjack_startup( netjack_driver_state_t *netj ); + }; + + int netjack_wait( netjack_driver_state_t *netj ); + void netjack_send_silence( netjack_driver_state_t *netj, int syncstate ); + void netjack_read( netjack_driver_state_t *netj, jack_nframes_t nframes ) ; + void netjack_write( netjack_driver_state_t *netj, jack_nframes_t nframes, int syncstate ); + void netjack_attach( netjack_driver_state_t *netj ); + void netjack_detach( netjack_driver_state_t *netj ); + + netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, + jack_client_t * client, + const char *name, + unsigned int capture_ports, + unsigned int playback_ports, + unsigned int capture_ports_midi, + unsigned int playback_ports_midi, + jack_nframes_t sample_rate, + jack_nframes_t period_size, + unsigned int listen_port, + unsigned int transport_sync, + unsigned int resample_factor, + unsigned int resample_factor_up, + unsigned int bitdepth, + unsigned int use_autoconfig, + unsigned int latency, + unsigned int redundancy, + int dont_htonl_floats, + int always_deadline, + int jitter_val ); + + void netjack_release( netjack_driver_state_t *netj ); + int netjack_startup( netjack_driver_state_t *netj ); #ifdef __cplusplus } diff --git a/common/netjack_packet.c b/common/netjack_packet.c index 638e3d82..9434985c 100644 --- a/common/netjack_packet.c +++ b/common/netjack_packet.c @@ -73,9 +73,7 @@ #endif #include "netjack_packet.h" - -// JACK2 specific. -#include "control.h" +#include "JackError.h" #ifdef NO_JACK_ERROR #define jack_error printf @@ -154,13 +152,12 @@ packet_cache int i, fragment_number; if( pkt_size == sizeof(jacknet_packet_header) ) - fragment_number = 1; + fragment_number = 1; else - fragment_number = (pkt_size - sizeof (jacknet_packet_header) - 1) / fragment_payload_size + 1; + fragment_number = (pkt_size - sizeof (jacknet_packet_header) - 1) / fragment_payload_size + 1; packet_cache *pcache = malloc (sizeof (packet_cache)); - if (pcache == NULL) - { + if (pcache == NULL) { jack_error ("could not allocate packet cache (1)"); return NULL; } @@ -171,14 +168,12 @@ packet_cache pcache->last_framecnt_retreived = 0; pcache->last_framecnt_retreived_valid = 0; - if (pcache->packets == NULL) - { + if (pcache->packets == NULL) { jack_error ("could not allocate packet cache (2)"); return NULL; } - for (i = 0; i < num_packets; i++) - { + for (i = 0; i < num_packets; i++) { pcache->packets[i].valid = 0; pcache->packets[i].num_fragments = fragment_number; pcache->packets[i].packet_size = pkt_size; @@ -186,8 +181,7 @@ packet_cache pcache->packets[i].framecnt = 0; pcache->packets[i].fragment_array = malloc (sizeof (char) * fragment_number); pcache->packets[i].packet_buf = malloc (pkt_size); - if ((pcache->packets[i].fragment_array == NULL) || (pcache->packets[i].packet_buf == NULL)) - { + if ((pcache->packets[i].fragment_array == NULL) || (pcache->packets[i].packet_buf == NULL)) { jack_error ("could not allocate packet cache (3)"); return NULL; } @@ -202,10 +196,9 @@ packet_cache_free (packet_cache *pcache) { int i; if( pcache == NULL ) - return; + return; - for (i = 0; i < pcache->size; i++) - { + for (i = 0; i < pcache->size; i++) { free (pcache->packets[i].fragment_array); free (pcache->packets[i].packet_buf); } @@ -220,8 +213,7 @@ cache_packet int i; cache_packet *retval; - for (i = 0; i < pcache->size; i++) - { + for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) return &(pcache->packets[i]); } @@ -230,8 +222,7 @@ cache_packet // find a free packet. retval = packet_cache_get_free_packet (pcache); - if (retval != NULL) - { + if (retval != NULL) { cache_packet_set_framecnt (retval, framecnt); return retval; } @@ -259,10 +250,8 @@ cache_packet cache_packet *retval = &(pcache->packets[0]); int i; - for (i = 0; i < pcache->size; i++) - { - if (pcache->packets[i].valid && (pcache->packets[i].framecnt < minimal_frame)) - { + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt < minimal_frame)) { minimal_frame = pcache->packets[i].framecnt; retval = &(pcache->packets[i]); } @@ -276,8 +265,7 @@ cache_packet { int i; - for (i = 0; i < pcache->size; i++) - { + for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid == 0) return &(pcache->packets[i]); } @@ -322,28 +310,23 @@ cache_packet_add_fragment (cache_packet *pack, char *packet_buf, int rcv_len) jack_nframes_t fragment_nr = ntohl (pkthdr->fragment_nr); jack_nframes_t framecnt = ntohl (pkthdr->framecnt); - if (framecnt != pack->framecnt) - { + if (framecnt != pack->framecnt) { jack_error ("errror. framecnts dont match"); return; } - if (fragment_nr == 0) - { + if (fragment_nr == 0) { memcpy (pack->packet_buf, packet_buf, rcv_len); pack->fragment_array[0] = 1; return; } - if ((fragment_nr < pack->num_fragments) && (fragment_nr > 0)) - { - if ((fragment_nr * fragment_payload_size + rcv_len - sizeof (jacknet_packet_header)) <= (pack->packet_size - sizeof (jacknet_packet_header))) - { + if ((fragment_nr < pack->num_fragments) && (fragment_nr > 0)) { + if ((fragment_nr * fragment_payload_size + rcv_len - sizeof (jacknet_packet_header)) <= (pack->packet_size - sizeof (jacknet_packet_header))) { memcpy (packet_bufX + fragment_nr * fragment_payload_size, dataX, rcv_len - sizeof (jacknet_packet_header)); pack->fragment_array[fragment_nr] = 1; - } - else + } else jack_error ("too long packet received..."); } } @@ -375,11 +358,11 @@ netjack_poll_deadline (int sockfd, jack_time_t deadline) jack_time_t now = jack_get_time(); if( now >= deadline ) - return 0; + return 0; - if( (deadline-now) >= 1000000 ) { - jack_error( "deadline more than 1 second in the future, trimming it." ); - deadline = now+500000; + if( (deadline - now) >= 1000000 ) { + jack_error( "deadline more than 1 second in the future, trimming it." ); + deadline = now + 500000; } #if HAVE_PPOLL timeout_spec.tv_nsec = (deadline - now) * 1000; @@ -396,25 +379,23 @@ netjack_poll_deadline (int sockfd, jack_time_t deadline) poll_err = poll (&fds, 1, timeout); #endif - if (poll_err == -1) - { - switch (errno) - { + if (poll_err == -1) { + switch (errno) { case EBADF: - jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); - break; + jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); + break; case EFAULT: - jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); - break; + jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); + break; case EINTR: - jack_error ("Error %d: A signal occurred before any requested event", errno); - break; + jack_error ("Error %d: A signal occurred before any requested event", errno); + break; case EINVAL: - jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); - break; + jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); + break; case ENOMEM: - jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); - break; + jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); + break; } } return poll_err; @@ -429,17 +410,17 @@ netjack_poll (int sockfd, int timeout) struct sigaction action; sigemptyset(&sigmask); - sigaddset(&sigmask, SIGHUP); - sigaddset(&sigmask, SIGINT); - sigaddset(&sigmask, SIGQUIT); - sigaddset(&sigmask, SIGPIPE); - sigaddset(&sigmask, SIGTERM); - sigaddset(&sigmask, SIGUSR1); - sigaddset(&sigmask, SIGUSR2); - - action.sa_handler = SIG_DFL; - action.sa_mask = sigmask; - action.sa_flags = SA_RESTART; + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGPIPE); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + sigaddset(&sigmask, SIGUSR2); + + action.sa_handler = SIG_DFL; + action.sa_mask = sigmask; + action.sa_flags = SA_RESTART; for (i = 1; i < NSIG; i++) if (sigismember (&sigmask, i)) @@ -449,31 +430,28 @@ netjack_poll (int sockfd, int timeout) fds.events = POLLIN; sigprocmask(SIG_UNBLOCK, &sigmask, &rsigmask); - while (poll_err == 0) - { + while (poll_err == 0) { poll_err = poll (&fds, 1, timeout); } sigprocmask(SIG_SETMASK, &rsigmask, NULL); - if (poll_err == -1) - { - switch (errno) - { + if (poll_err == -1) { + switch (errno) { case EBADF: - jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); - break; + jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); + break; case EFAULT: - jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); - break; + jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); + break; case EINTR: - jack_error ("Error %d: A signal occurred before any requested event", errno); - break; + jack_error ("Error %d: A signal occurred before any requested event", errno); + break; case EINVAL: - jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); - break; + jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); + break; case ENOMEM: - jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); - break; + jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); + break; } return 0; } @@ -498,10 +476,10 @@ netjack_poll_deadline (int sockfd, jack_time_t deadline) while( 1 ) { jack_time_t now = jack_get_time(); if( now >= deadline ) - return 0; + return 0; int timeout_usecs = (deadline - now); - //jack_error( "timeout = %d", timeout_usecs ); + //jack_error( "timeout = %d", timeout_usecs ); timeout.tv_sec = 0; timeout.tv_usec = (timeout_usecs < 500) ? 500 : timeout_usecs; timeout.tv_usec = (timeout_usecs > 1000000) ? 500000 : timeout_usecs; @@ -533,36 +511,35 @@ packet_cache_drain_socket( packet_cache *pcache, int sockfd ) #else socklen_t senderlen = sizeof( struct sockaddr_in ); #endif - while (1) - { + while (1) { #ifdef WIN32 rcv_len = recvfrom (sockfd, rx_packet, pcache->mtu, 0, - (struct sockaddr*) &sender_address, &senderlen); + (struct sockaddr*) &sender_address, &senderlen); #else rcv_len = recvfrom (sockfd, rx_packet, pcache->mtu, MSG_DONTWAIT, - (struct sockaddr*) &sender_address, &senderlen); + (struct sockaddr*) &sender_address, &senderlen); #endif if (rcv_len < 0) return; - if (pcache->master_address_valid) { - // Verify its from our master. - if (memcmp (&sender_address, &(pcache->master_address), senderlen) != 0) - continue; - } else { - // Setup this one as master - //printf( "setup master...\n" ); - memcpy ( &(pcache->master_address), &sender_address, senderlen ); - pcache->master_address_valid = 1; - } + if (pcache->master_address_valid) { + // Verify its from our master. + if (memcmp (&sender_address, &(pcache->master_address), senderlen) != 0) + continue; + } else { + // Setup this one as master + //printf( "setup master...\n" ); + memcpy ( &(pcache->master_address), &sender_address, senderlen ); + pcache->master_address_valid = 1; + } framecnt = ntohl (pkthdr->framecnt); - if( pcache->last_framecnt_retreived_valid && (framecnt <= pcache->last_framecnt_retreived )) - continue; + if( pcache->last_framecnt_retreived_valid && (framecnt <= pcache->last_framecnt_retreived )) + continue; cpack = packet_cache_get_packet (pcache, framecnt); cache_packet_add_fragment (cpack, rx_packet, rcv_len); - cpack->recv_timestamp = jack_get_time(); + cpack->recv_timestamp = jack_get_time(); } } @@ -579,10 +556,8 @@ packet_cache_clear_old_packets (packet_cache *pcache, jack_nframes_t framecnt ) { int i; - for (i = 0; i < pcache->size; i++) - { - if (pcache->packets[i].valid && (pcache->packets[i].framecnt < framecnt)) - { + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt < framecnt)) { cache_packet_reset (&(pcache->packets[i])); } } @@ -656,8 +631,7 @@ packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt ) int num_packets_before_us = 0; int i; - for (i = 0; i < pcache->size; i++) - { + for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); if (cpack->valid && cache_packet_is_complete( cpack )) if( cpack->framecnt >= expected_framecnt ) @@ -672,11 +646,10 @@ int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ) { int i; - jack_nframes_t best_offset = JACK_MAX_FRAMES/2-1; + jack_nframes_t best_offset = JACK_MAX_FRAMES / 2 - 1; int retval = 0; - for (i = 0; i < pcache->size; i++) - { + for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); @@ -690,13 +663,13 @@ packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t e if( (cpack->framecnt - expected_framecnt) > best_offset ) { continue; - } + } - best_offset = cpack->framecnt - expected_framecnt; - retval = 1; + best_offset = cpack->framecnt - expected_framecnt; + retval = 1; - if (best_offset == 0) - break; + if (best_offset == 0) + break; } if (retval && framecnt) *framecnt = expected_framecnt + best_offset; @@ -711,22 +684,21 @@ packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_ jack_nframes_t best_value = 0; int retval = 0; - for (i = 0; i < pcache->size; i++) - { + for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); if (!cpack->valid || !cache_packet_is_complete( cpack )) { //printf( "invalid\n" ); continue; - } + } - if (cpack->framecnt < best_value) { - continue; - } + if (cpack->framecnt < best_value) { + continue; + } - best_value = cpack->framecnt; - retval = 1; + best_value = cpack->framecnt; + retval = 1; } if (retval && framecnt) @@ -743,25 +715,24 @@ packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecn jack_nframes_t best_offset = 0; int retval = 0; - for (i = 0; i < pcache->size; i++) - { + for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); if (!cpack->valid || !cache_packet_is_complete( cpack )) { //printf( "invalid\n" ); continue; - } + } - if ((cpack->framecnt - expected_framecnt) < best_offset) { - continue; - } + if ((cpack->framecnt - expected_framecnt) < best_offset) { + continue; + } - best_offset = cpack->framecnt - expected_framecnt; - retval = 1; + best_offset = cpack->framecnt - expected_framecnt; + retval = 1; - if( best_offset == 0 ) - break; + if( best_offset == 0 ) + break; } if (retval && framecnt) *framecnt = JACK_MAX_FRAMES - best_offset; @@ -787,13 +758,11 @@ netjack_sendto (int sockfd, char *packet_buf, int pkt_size, int flags, struct so pkthdr = (jacknet_packet_header *) packet_buf; pkthdr->fragment_nr = htonl (0); err = sendto(sockfd, packet_buf, pkt_size, flags, addr, addr_size); - if( err<0 ) { + if( err < 0 ) { //printf( "error in send\n" ); perror( "send" ); } - } - else - { + } else { int err; // Copy the packet header to the tx pack first. memcpy(tx_packet, packet_buf, sizeof (jacknet_packet_header)); @@ -801,8 +770,7 @@ netjack_sendto (int sockfd, char *packet_buf, int pkt_size, int flags, struct so // Now loop and send all char *packet_bufX = packet_buf + sizeof (jacknet_packet_header); - while (packet_bufX < (packet_buf + pkt_size - fragment_payload_size)) - { + while (packet_bufX < (packet_buf + pkt_size - fragment_payload_size)) { pkthdr->fragment_nr = htonl (frag_cnt++); memcpy (dataX, packet_bufX, fragment_payload_size); sendto (sockfd, tx_packet, mtu, flags, addr, addr_size); @@ -816,7 +784,7 @@ netjack_sendto (int sockfd, char *packet_buf, int pkt_size, int flags, struct so // sendto(last_pack_size); err = sendto(sockfd, tx_packet, last_payload_size + sizeof(jacknet_packet_header), flags, addr, addr_size); - if( err<0 ) { + if( err < 0 ) { //printf( "error in send\n" ); perror( "send" ); } @@ -828,24 +796,21 @@ decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, ja { int i; jack_midi_clear_buffer (buf); - for (i = 0; i < buffer_size_uint32 - 3;) - { + for (i = 0; i < buffer_size_uint32 - 3;) { uint32_t payload_size; payload_size = buffer_uint32[i]; payload_size = ntohl (payload_size); - if (payload_size) - { + if (payload_size) { jack_midi_event_t event; - event.time = ntohl (buffer_uint32[i+1]); - event.size = ntohl (buffer_uint32[i+2]); - event.buffer = (jack_midi_data_t*) (&(buffer_uint32[i+3])); + event.time = ntohl (buffer_uint32[i + 1]); + event.size = ntohl (buffer_uint32[i + 2]); + event.buffer = (jack_midi_data_t*) (&(buffer_uint32[i + 3])); jack_midi_event_write (buf, event.time, event.buffer, event.size); // skip to the next event - unsigned int nb_data_quads = (((event.size-1) & ~0x3) >> 2)+1; - i += 3+nb_data_quads; - } - else + unsigned int nb_data_quads = (((event.size - 1) & ~0x3) >> 2) + 1; + i += 3 + nb_data_quads; + } else break; // no events can follow an empty event, we're done } } @@ -857,38 +822,34 @@ encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, ja unsigned int written = 0; // midi port, encode midi events unsigned int nevents = jack_midi_get_event_count (buf); - for (i = 0; i < nevents; ++i) - { + for (i = 0; i < nevents; ++i) { jack_midi_event_t event; jack_midi_event_get (&event, buf, i); unsigned int nb_data_quads = (((event.size - 1) & ~0x3) >> 2) + 1; unsigned int payload_size = 3 + nb_data_quads; // only write if we have sufficient space for the event // otherwise drop it - if (written + payload_size < buffer_size_uint32 - 1) - { + if (written + payload_size < buffer_size_uint32 - 1) { // write header - buffer_uint32[written]=htonl (payload_size); + buffer_uint32[written] = htonl (payload_size); written++; - buffer_uint32[written]=htonl (event.time); + buffer_uint32[written] = htonl (event.time); written++; - buffer_uint32[written]=htonl (event.size); + buffer_uint32[written] = htonl (event.size); written++; // write data jack_midi_data_t* tmpbuff = (jack_midi_data_t*)(&(buffer_uint32[written])); memcpy (tmpbuff, event.buffer, event.size); written += nb_data_quads; - } - else - { + } else { // buffer overflow jack_error ("midi buffer overflow"); break; } } // now put a netjack_midi 'no-payload' event, signaling EOF - buffer_uint32[written]=0; + buffer_uint32[written] = 0; } // render functions for float @@ -906,8 +867,7 @@ render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_pe if (!packet_payload) return; - while (node != NULL) - { + while (node != NULL) { int i; int_float_t val; #if HAVE_SAMPLERATE @@ -919,15 +879,12 @@ render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_pe const char *porttype = jack_port_type (port); - if (jack_port_is_audio (porttype)) - { + if (jack_port_is_audio (porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary - if (net_period_down != nframes) - { + if (net_period_down != nframes) { SRC_STATE *src_state = src_node->data; - for (i = 0; i < net_period_down; i++) - { + for (i = 0; i < net_period_down; i++) { packet_bufX[i] = ntohl (packet_bufX[i]); } @@ -943,27 +900,20 @@ render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_pe src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); src_node = jack_slist_next (src_node); - } - else + } else #endif { - if( dont_htonl_floats ) - { - memcpy( buf, packet_bufX, net_period_down*sizeof(jack_default_audio_sample_t)); - } - else - { - for (i = 0; i < net_period_down; i++) - { - val.i = packet_bufX[i]; - val.i = ntohl (val.i); - buf[i] = val.f; + if( dont_htonl_floats ) { + memcpy( buf, packet_bufX, net_period_down * sizeof(jack_default_audio_sample_t)); + } else { + for (i = 0; i < net_period_down; i++) { + val.i = packet_bufX[i]; + val.i = ntohl (val.i); + buf[i] = val.f; } } } - } - else if (jack_port_is_midi (porttype)) - { + } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down; @@ -987,8 +937,7 @@ render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_src uint32_t *packet_bufX = (uint32_t *) packet_payload; - while (node != NULL) - { + while (node != NULL) { #if HAVE_SAMPLERATE SRC_DATA src; #endif @@ -999,8 +948,7 @@ render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_src const char *porttype = jack_port_type (port); - if (jack_port_is_audio (porttype)) - { + if (jack_port_is_audio (porttype)) { // audio port, resample if necessary #if HAVE_SAMPLERATE @@ -1018,32 +966,24 @@ render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_src src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); - for (i = 0; i < net_period_up; i++) - { + for (i = 0; i < net_period_up; i++) { packet_bufX[i] = htonl (packet_bufX[i]); } src_node = jack_slist_next (src_node); - } - else + } else #endif { - if( dont_htonl_floats ) - { - memcpy( packet_bufX, buf, net_period_up*sizeof(jack_default_audio_sample_t) ); - } - else - { - for (i = 0; i < net_period_up; i++) - { - val.f = buf[i]; - val.i = htonl (val.i); - packet_bufX[i] = val.i; + if( dont_htonl_floats ) { + memcpy( packet_bufX, buf, net_period_up * sizeof(jack_default_audio_sample_t) ); + } else { + for (i = 0; i < net_period_up; i++) { + val.f = buf[i]; + val.i = htonl (val.i); + packet_bufX[i] = val.i; } } } - } - else if (jack_port_is_midi (porttype)) - { + } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up; @@ -1069,10 +1009,9 @@ render_payload_to_jack_ports_16bit (void *packet_payload, jack_nframes_t net_per uint16_t *packet_bufX = (uint16_t *)packet_payload; if( !packet_payload ) - return; + return; - while (node != NULL) - { + while (node != NULL) { int i; //uint32_t val; #if HAVE_SAMPLERATE @@ -1087,16 +1026,13 @@ render_payload_to_jack_ports_16bit (void *packet_payload, jack_nframes_t net_per #endif const char *porttype = jack_port_type (port); - if (jack_port_is_audio (porttype)) - { + if (jack_port_is_audio (porttype)) { // audio port, resample if necessary #if HAVE_SAMPLERATE - if (net_period_down != nframes) - { + if (net_period_down != nframes) { SRC_STATE *src_state = src_node->data; - for (i = 0; i < net_period_down; i++) - { + for (i = 0; i < net_period_down; i++) { floatbuf[i] = ((float) ntohs(packet_bufX[i])) / 32767.0 - 1.0; } @@ -1112,14 +1048,11 @@ render_payload_to_jack_ports_16bit (void *packet_payload, jack_nframes_t net_per src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); src_node = jack_slist_next (src_node); - } - else + } else #endif for (i = 0; i < net_period_down; i++) buf[i] = ((float) ntohs (packet_bufX[i])) / 32768.0 - 1.0; - } - else if (jack_port_is_midi (porttype)) - { + } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; @@ -1143,8 +1076,7 @@ render_jack_ports_to_payload_16bit (JSList *playback_ports, JSList *playback_src uint16_t *packet_bufX = (uint16_t *)packet_payload; - while (node != NULL) - { + while (node != NULL) { #if HAVE_SAMPLERATE SRC_DATA src; #endif @@ -1153,13 +1085,11 @@ render_jack_ports_to_payload_16bit (JSList *playback_ports, JSList *playback_src jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); - if (jack_port_is_audio (porttype)) - { + if (jack_port_is_audio (porttype)) { // audio port, resample if necessary #if HAVE_SAMPLERATE - if (net_period_up != nframes) - { + if (net_period_up != nframes) { SRC_STATE *src_state = src_node->data; float *floatbuf = alloca (sizeof(float) * net_period_up); @@ -1176,19 +1106,15 @@ render_jack_ports_to_payload_16bit (JSList *playback_ports, JSList *playback_src src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); - for (i = 0; i < net_period_up; i++) - { + for (i = 0; i < net_period_up; i++) { packet_bufX[i] = htons (((uint16_t)((floatbuf[i] + 1.0) * 32767.0))); } src_node = jack_slist_next (src_node); - } - else + } else #endif for (i = 0; i < net_period_up; i++) packet_bufX[i] = htons(((uint16_t)((buf[i] + 1.0) * 32767.0))); - } - else if (jack_port_is_midi (porttype)) - { + } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 2; @@ -1217,8 +1143,7 @@ render_payload_to_jack_ports_8bit (void *packet_payload, jack_nframes_t net_peri if (!packet_payload) return; - while (node != NULL) - { + while (node != NULL) { int i; //uint32_t val; #if HAVE_SAMPLERATE @@ -1233,12 +1158,10 @@ render_payload_to_jack_ports_8bit (void *packet_payload, jack_nframes_t net_peri #endif const char *porttype = jack_port_type (port); - if (jack_port_is_audio(porttype)) - { + if (jack_port_is_audio(porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary - if (net_period_down != nframes) - { + if (net_period_down != nframes) { SRC_STATE *src_state = src_node->data; for (i = 0; i < net_period_down; i++) floatbuf[i] = ((float) packet_bufX[i]) / 127.0; @@ -1255,14 +1178,11 @@ render_payload_to_jack_ports_8bit (void *packet_payload, jack_nframes_t net_peri src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); src_node = jack_slist_next (src_node); - } - else + } else #endif for (i = 0; i < net_period_down; i++) buf[i] = ((float) packet_bufX[i]) / 127.0; - } - else if (jack_port_is_midi (porttype)) - { + } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; @@ -1286,8 +1206,7 @@ render_jack_ports_to_payload_8bit (JSList *playback_ports, JSList *playback_srcs int8_t *packet_bufX = (int8_t *)packet_payload; - while (node != NULL) - { + while (node != NULL) { #if HAVE_SAMPLERATE SRC_DATA src; #endif @@ -1297,12 +1216,10 @@ render_jack_ports_to_payload_8bit (JSList *playback_ports, JSList *playback_srcs jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); - if (jack_port_is_audio (porttype)) - { + if (jack_port_is_audio (porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary - if (net_period_up != nframes) - { + if (net_period_up != nframes) { SRC_STATE *src_state = src_node->data; @@ -1323,14 +1240,11 @@ render_jack_ports_to_payload_8bit (JSList *playback_ports, JSList *playback_srcs for (i = 0; i < net_period_up; i++) packet_bufX[i] = floatbuf[i] * 127.0; src_node = jack_slist_next (src_node); - } - else + } else #endif for (i = 0; i < net_period_up; i++) packet_bufX[i] = buf[i] * 127.0; - } - else if (jack_port_is_midi (porttype)) - { + } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 4; @@ -1354,33 +1268,29 @@ render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_peri unsigned char *packet_bufX = (unsigned char *)packet_payload; - while (node != NULL) - { + while (node != NULL) { jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); - if (jack_port_is_audio (porttype)) - { + if (jack_port_is_audio (porttype)) { // audio port, decode celt data. CELTDecoder *decoder = src_node->data; - #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 +#if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 if( !packet_payload ) celt_decode_float( decoder, NULL, net_period_down, buf, nframes ); else celt_decode_float( decoder, packet_bufX, net_period_down, buf, nframes ); - #else +#else if( !packet_payload ) celt_decode_float( decoder, NULL, net_period_down, buf ); else celt_decode_float( decoder, packet_bufX, net_period_down, buf ); - #endif +#endif src_node = jack_slist_next (src_node); - } - else if (jack_port_is_midi (porttype)) - { + } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; @@ -1403,31 +1313,27 @@ render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs unsigned char *packet_bufX = (unsigned char *)packet_payload; - while (node != NULL) - { + while (node != NULL) { jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); - if (jack_port_is_audio (porttype)) - { + if (jack_port_is_audio (porttype)) { // audio port, encode celt data. - int encoded_bytes; - float *floatbuf = alloca (sizeof(float) * nframes ); - memcpy( floatbuf, buf, nframes*sizeof(float) ); - CELTEncoder *encoder = src_node->data; + int encoded_bytes; + float *floatbuf = alloca (sizeof(float) * nframes ); + memcpy( floatbuf, buf, nframes * sizeof(float) ); + CELTEncoder *encoder = src_node->data; #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 - encoded_bytes = celt_encode_float( encoder, floatbuf, nframes, packet_bufX, net_period_up ); + encoded_bytes = celt_encode_float( encoder, floatbuf, nframes, packet_bufX, net_period_up ); #else - encoded_bytes = celt_encode_float( encoder, floatbuf, NULL, packet_bufX, net_period_up ); + encoded_bytes = celt_encode_float( encoder, floatbuf, NULL, packet_bufX, net_period_up ); #endif - if( encoded_bytes != net_period_up ) - printf( "something in celt changed. netjack needs to be changed to handle this.\n" ); - src_node = jack_slist_next( src_node ); - } - else if (jack_port_is_midi (porttype)) - { + if( encoded_bytes != net_period_up ) + printf( "something in celt changed. netjack needs to be changed to handle this.\n" ); + src_node = jack_slist_next( src_node ); + } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 2; diff --git a/common/netjack_packet.h b/common/netjack_packet.h index c60f7bec..1f88ae32 100644 --- a/common/netjack_packet.h +++ b/common/netjack_packet.h @@ -28,132 +28,120 @@ #define __JACK_NET_PACKET_H__ #ifdef __cplusplus - extern "C" - { +extern "C" +{ #endif #include #include -//#include #include - #include -//#include // The Packet Header. #define CELT_MODE 1000 // Magic bitdepth value that indicates CELT compression #define MASTER_FREEWHEELS 0x80000000 -typedef struct _jacknet_packet_header jacknet_packet_header; - -struct _jacknet_packet_header -{ - // General AutoConf Data - jack_nframes_t capture_channels_audio; - jack_nframes_t playback_channels_audio; - jack_nframes_t capture_channels_midi; - jack_nframes_t playback_channels_midi; - jack_nframes_t period_size; - jack_nframes_t sample_rate; - - // Transport Sync - jack_nframes_t sync_state; - jack_nframes_t transport_frame; - jack_nframes_t transport_state; - - // Packet loss Detection, and latency reduction - jack_nframes_t framecnt; - jack_nframes_t latency; - - jack_nframes_t reply_port; - jack_nframes_t mtu; - jack_nframes_t fragment_nr; -}; - -typedef union _int_float int_float_t; - -union _int_float -{ - uint32_t i; - float f; -}; - -// fragment reorder cache. -typedef struct _cache_packet cache_packet; - -struct _cache_packet -{ - int valid; - int num_fragments; - int packet_size; - int mtu; - jack_time_t recv_timestamp; - jack_nframes_t framecnt; - char * fragment_array; - char * packet_buf; -}; - -typedef struct _packet_cache packet_cache; - -struct _packet_cache -{ - int size; - cache_packet *packets; - int mtu; - struct sockaddr_in master_address; - int master_address_valid; - jack_nframes_t last_framecnt_retreived; - int last_framecnt_retreived_valid; -}; - -// fragment cache function prototypes -// XXX: Some of these are private. -packet_cache *packet_cache_new(int num_packets, int pkt_size, int mtu); -void packet_cache_free(packet_cache *pkt_cache); - -cache_packet *packet_cache_get_packet(packet_cache *pkt_cache, jack_nframes_t framecnt); -cache_packet *packet_cache_get_oldest_packet(packet_cache *pkt_cache); -cache_packet *packet_cache_get_free_packet(packet_cache *pkt_cache); - -void cache_packet_reset(cache_packet *pack); -void cache_packet_set_framecnt(cache_packet *pack, jack_nframes_t framecnt); -void cache_packet_add_fragment(cache_packet *pack, char *packet_buf, int rcv_len); -int cache_packet_is_complete(cache_packet *pack); - -void packet_cache_drain_socket( packet_cache *pcache, int sockfd ); -void packet_cache_reset_master_address( packet_cache *pcache ); -float packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt ); -int packet_cache_retreive_packet_pointer( packet_cache *pcache, jack_nframes_t framecnt, char **packet_buf, int pkt_size, jack_time_t *timestamp ); -int packet_cache_release_packet( packet_cache *pcache, jack_nframes_t framecnt ); -int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); -int packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_t *framecnt ); -int packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); -// Function Prototypes - -int netjack_poll_deadline (int sockfd, jack_time_t deadline); - -void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu); - - -int get_sample_size(int bitdepth); -void packet_header_hton(jacknet_packet_header *pkthdr); - -void packet_header_ntoh(jacknet_packet_header *pkthdr); - -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 ); - - -// XXX: This is sort of deprecated: -// This one waits forever. an is not using ppoll -int netjack_poll(int sockfd, int timeout); + typedef struct _jacknet_packet_header jacknet_packet_header; + + struct _jacknet_packet_header { + // General AutoConf Data + jack_nframes_t capture_channels_audio; + jack_nframes_t playback_channels_audio; + jack_nframes_t capture_channels_midi; + jack_nframes_t playback_channels_midi; + jack_nframes_t period_size; + jack_nframes_t sample_rate; + + // Transport Sync + jack_nframes_t sync_state; + jack_nframes_t transport_frame; + jack_nframes_t transport_state; + + // Packet loss Detection, and latency reduction + jack_nframes_t framecnt; + jack_nframes_t latency; + + jack_nframes_t reply_port; + jack_nframes_t mtu; + jack_nframes_t fragment_nr; + }; + + typedef union _int_float int_float_t; + + union _int_float { + uint32_t i; + float f; + }; + + // fragment reorder cache. + typedef struct _cache_packet cache_packet; + + struct _cache_packet { + int valid; + int num_fragments; + int packet_size; + int mtu; + jack_time_t recv_timestamp; + jack_nframes_t framecnt; + char * fragment_array; + char * packet_buf; + }; + + typedef struct _packet_cache packet_cache; + + struct _packet_cache { + int size; + cache_packet *packets; + int mtu; + struct sockaddr_in master_address; + int master_address_valid; + jack_nframes_t last_framecnt_retreived; + int last_framecnt_retreived_valid; + }; + + // fragment cache function prototypes + // XXX: Some of these are private. + packet_cache *packet_cache_new(int num_packets, int pkt_size, int mtu); + void packet_cache_free(packet_cache *pkt_cache); + + cache_packet *packet_cache_get_packet(packet_cache *pkt_cache, jack_nframes_t framecnt); + cache_packet *packet_cache_get_oldest_packet(packet_cache *pkt_cache); + cache_packet *packet_cache_get_free_packet(packet_cache *pkt_cache); + + void cache_packet_reset(cache_packet *pack); + void cache_packet_set_framecnt(cache_packet *pack, jack_nframes_t framecnt); + void cache_packet_add_fragment(cache_packet *pack, char *packet_buf, int rcv_len); + int cache_packet_is_complete(cache_packet *pack); + + void packet_cache_drain_socket( packet_cache *pcache, int sockfd ); + void packet_cache_reset_master_address( packet_cache *pcache ); + float packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt ); + int packet_cache_retreive_packet_pointer( packet_cache *pcache, jack_nframes_t framecnt, char **packet_buf, int pkt_size, jack_time_t *timestamp ); + int packet_cache_release_packet( packet_cache *pcache, jack_nframes_t framecnt ); + int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); + int packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_t *framecnt ); + int packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); + + // Function Prototypes + + int netjack_poll_deadline (int sockfd, jack_time_t deadline); + void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu); + int get_sample_size(int bitdepth); + void packet_header_hton(jacknet_packet_header *pkthdr); + void packet_header_ntoh(jacknet_packet_header *pkthdr); + 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 ); + + // XXX: This is sort of deprecated: + // This one waits forever. an is not using ppoll + int netjack_poll(int sockfd, int timeout); + + void decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); + void encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); -void decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); -void encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); #ifdef __cplusplus - } +} #endif #endif diff --git a/common/ringbuffer.c b/common/ringbuffer.c index 45ee27b3..9de844f6 100644 --- a/common/ringbuffer.c +++ b/common/ringbuffer.c @@ -43,39 +43,39 @@ typedef struct { } jack_ringbuffer_t ; -EXPORT jack_ringbuffer_t *jack_ringbuffer_create(size_t sz); -EXPORT void jack_ringbuffer_free(jack_ringbuffer_t *rb); -EXPORT void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, +LIB_EXPORT jack_ringbuffer_t *jack_ringbuffer_create(size_t sz); +LIB_EXPORT void jack_ringbuffer_free(jack_ringbuffer_t *rb); +LIB_EXPORT void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); -EXPORT void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, +LIB_EXPORT void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); -EXPORT size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt); -EXPORT size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt); -EXPORT void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt); -EXPORT size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb); -EXPORT int jack_ringbuffer_mlock(jack_ringbuffer_t *rb); -EXPORT void jack_ringbuffer_reset(jack_ringbuffer_t *rb); -EXPORT void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz); -EXPORT size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, +LIB_EXPORT size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt); +LIB_EXPORT size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt); +LIB_EXPORT void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt); +LIB_EXPORT size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb); +LIB_EXPORT int jack_ringbuffer_mlock(jack_ringbuffer_t *rb); +LIB_EXPORT void jack_ringbuffer_reset(jack_ringbuffer_t *rb); +LIB_EXPORT void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz); +LIB_EXPORT size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, size_t cnt); void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt); -size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb); +size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb); /* Create a new ringbuffer to hold at least `sz' bytes of data. The actual buffer size is rounded up to the next power of two. */ -EXPORT jack_ringbuffer_t * +LIB_EXPORT jack_ringbuffer_t * jack_ringbuffer_create (size_t sz) { int power_of_two; jack_ringbuffer_t *rb; - + if ((rb = (jack_ringbuffer_t *) malloc (sizeof (jack_ringbuffer_t))) == NULL) { return NULL; } - + for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); - + rb->size = 1 << power_of_two; rb->size_mask = rb->size; rb->size_mask -= 1; @@ -86,13 +86,13 @@ jack_ringbuffer_create (size_t sz) return NULL; } rb->mlocked = 0; - + return rb; } /* Free all data associated with the ringbuffer `rb'. */ -EXPORT void +LIB_EXPORT void jack_ringbuffer_free (jack_ringbuffer_t * rb) { #ifdef USE_MLOCK @@ -106,7 +106,7 @@ jack_ringbuffer_free (jack_ringbuffer_t * rb) /* Lock the data block of `rb' using the system call 'mlock'. */ -EXPORT int +LIB_EXPORT int jack_ringbuffer_mlock (jack_ringbuffer_t * rb) { #ifdef USE_MLOCK @@ -121,17 +121,18 @@ jack_ringbuffer_mlock (jack_ringbuffer_t * rb) /* Reset the read and write pointers to zero. This is not thread safe. */ -EXPORT void +LIB_EXPORT void jack_ringbuffer_reset (jack_ringbuffer_t * rb) { rb->read_ptr = 0; rb->write_ptr = 0; + memset(rb->buf, 0, rb->size); } /* Reset the read and write pointers to zero. This is not thread safe. */ -EXPORT void +LIB_EXPORT void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz) { rb->size = sz; @@ -145,14 +146,14 @@ jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz) number of bytes in front of the read pointer and behind the write pointer. */ -EXPORT size_t +LIB_EXPORT size_t jack_ringbuffer_read_space (const jack_ringbuffer_t * rb) { size_t w, r; - + w = rb->write_ptr; r = rb->read_ptr; - + if (w > r) { return w - r; } else { @@ -164,7 +165,7 @@ jack_ringbuffer_read_space (const jack_ringbuffer_t * rb) number of bytes in front of the write pointer and behind the read pointer. */ -EXPORT size_t +LIB_EXPORT size_t jack_ringbuffer_write_space (const jack_ringbuffer_t * rb) { size_t w, r; @@ -184,7 +185,7 @@ jack_ringbuffer_write_space (const jack_ringbuffer_t * rb) /* The copying data reader. Copy at most `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ -EXPORT size_t +LIB_EXPORT size_t jack_ringbuffer_read (jack_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; @@ -219,11 +220,11 @@ jack_ringbuffer_read (jack_ringbuffer_t * rb, char *dest, size_t cnt) return to_read; } -/* The copying data reader w/o read pointer advance. Copy at most - `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes +/* The copying data reader w/o read pointer advance. Copy at most + `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ -EXPORT size_t +LIB_EXPORT size_t jack_ringbuffer_peek (jack_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; @@ -263,7 +264,7 @@ jack_ringbuffer_peek (jack_ringbuffer_t * rb, char *dest, size_t cnt) /* The copying data writer. Copy at most `cnt' bytes to `rb' from `src'. Returns the actual number of bytes copied. */ -EXPORT size_t +LIB_EXPORT size_t jack_ringbuffer_write (jack_ringbuffer_t * rb, const char *src, size_t cnt) { size_t free_cnt; @@ -300,7 +301,7 @@ jack_ringbuffer_write (jack_ringbuffer_t * rb, const char *src, size_t cnt) /* Advance the read pointer `cnt' places. */ -EXPORT void +LIB_EXPORT void jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt) { size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; @@ -309,7 +310,7 @@ jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt) /* Advance the write pointer `cnt' places. */ -EXPORT void +LIB_EXPORT void jack_ringbuffer_write_advance (jack_ringbuffer_t * rb, size_t cnt) { size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; @@ -321,7 +322,7 @@ jack_ringbuffer_write_advance (jack_ringbuffer_t * rb, size_t cnt) the readable data is in one segment the second segment has zero length. */ -EXPORT void +LIB_EXPORT void jack_ringbuffer_get_read_vector (const jack_ringbuffer_t * rb, jack_ringbuffer_data_t * vec) { @@ -365,7 +366,7 @@ jack_ringbuffer_get_read_vector (const jack_ringbuffer_t * rb, the writeable data is in one segment the second segment has zero length. */ -EXPORT void +LIB_EXPORT void jack_ringbuffer_get_write_vector (const jack_ringbuffer_t * rb, jack_ringbuffer_data_t * vec) { diff --git a/common/shm.c b/common/shm.c index 2cb1d40e..f4543b2d 100644 --- a/common/shm.c +++ b/common/shm.c @@ -12,21 +12,21 @@ /* Copyright (C) 2001-2003 Paul Davis - + 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 + along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - + */ #include "JackConstants.h" @@ -143,6 +143,31 @@ static int semid = -1; #ifdef WIN32 +#include +#include + +static BOOL check_process_running(DWORD process_id) +{ + DWORD aProcesses[2048], cbNeeded, cProcesses; + unsigned int i; + + // Enumerate all processes + if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) { + return FALSE; + } + + // Calculate how many process identifiers were returned. + cProcesses = cbNeeded / sizeof(DWORD); + + for (i = 0; i < cProcesses; i++) { + if (aProcesses[i] == process_id) { + // Process process_id is running... + return TRUE; + } + } + return FALSE; +} + static int semaphore_init () {return 0;} @@ -289,8 +314,16 @@ jack_shm_validate_registry () static void jack_set_server_prefix (const char *server_name) { - snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), +#ifdef WIN32 + char buffer[UNLEN+1]={0}; + DWORD len = UNLEN+1; + GetUserName(buffer, &len); + snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), + "jack-%s:%s:", buffer, server_name); +#else + snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), "jack-%d:%s:", GetUID(), server_name); +#endif } /* gain server addressability to shared memory registration segment @@ -481,7 +514,12 @@ jack_register_server (const char *server_name, int new_registry) } /* see if server still exists */ - #ifndef WIN32 // steph TO CHECK + #ifdef WIN32 + if (check_process_running(jack_shm_header->server[i].pid)) { + res = EEXIST; /* other server running */ + goto unlock; + } + #else if (kill (jack_shm_header->server[i].pid, 0) == 0) { res = EEXIST; /* other server running */ goto unlock; @@ -572,7 +610,7 @@ jack_cleanup_shm () /* see if allocator still exists */ #ifdef WIN32 // steph - jack_info("TODO: kill API not available !!"); + //jack_info("TODO: kill API not available !!"); #else if (kill (r->allocator, 0)) { if (errno == ESRCH) { @@ -636,6 +674,24 @@ jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) return jack_attach_shm (si); } +int +jack_attach_lib_shm (jack_shm_info_t* si) +{ + int res = jack_attach_shm(si); + if (res == 0) + si->size = jack_shm_registry[si->index].size; // Keep size in si struct + return res; +} + +int +jack_attach_lib_shm_read (jack_shm_info_t* si) +{ + int res = jack_attach_shm_read(si); + if (res == 0) + si->size = jack_shm_registry[si->index].size; // Keep size in si struct + return res; +} + #ifdef USE_POSIX_SHM /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -759,6 +815,15 @@ jack_release_shm (jack_shm_info_t* si) /* registry may or may not be locked */ if (si->ptr.attached_at != MAP_FAILED) { munmap (si->ptr.attached_at, jack_shm_registry[si->index].size); + } +} + +void +jack_release_lib_shm (jack_shm_info_t* si) +{ + /* registry may or may not be locked */ + if (si->ptr.attached_at != MAP_FAILED) { + munmap (si->ptr.attached_at, si->size); } } @@ -961,6 +1026,12 @@ jack_release_shm (jack_shm_info_t* si) } } +void +jack_release_lib_shm (jack_shm_info_t* si) +{ + jack_release_shm(si); +} + int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si) { @@ -1160,6 +1231,12 @@ jack_release_shm (jack_shm_info_t* si) } } +void +jack_release_lib_shm (jack_shm_info_t* si) +{ + jack_release_shm(si); +} + int jack_shmalloc (const char* name_not_used, jack_shmsize_t size, jack_shm_info_t* si) @@ -1210,3 +1287,4 @@ jack_attach_shm (jack_shm_info_t* si) } #endif /* !USE_POSIX_SHM */ + diff --git a/common/shm.h b/common/shm.h index ed5a953f..2a6d6096 100644 --- a/common/shm.h +++ b/common/shm.h @@ -116,6 +116,7 @@ extern "C" * attached to the address space. */ + PRE_PACKED_STRUCTURE typedef struct _jack_shm_info { jack_shm_registry_index_t index; /* offset into the registry */ uint32_t size; @@ -124,9 +125,13 @@ extern "C" char ptr_size[8]; } ptr; /* a "pointer" that has the same 8 bytes size when compling in 32 or 64 bits */ } +#ifdef _MSC_VER + jack_shm_info_t; POST_PACKED_STRUCTURE +#else POST_PACKED_STRUCTURE jack_shm_info_t; +#endif - /* utility functions used only within JACK */ + /* utility functions used only within JACK */ void jack_shm_copy_from_registry (jack_shm_info_t*, jack_shm_registry_index_t); @@ -147,9 +152,12 @@ extern "C" int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result); void jack_release_shm (jack_shm_info_t*); + void jack_release_lib_shm (jack_shm_info_t*); void jack_destroy_shm (jack_shm_info_t*); int jack_attach_shm (jack_shm_info_t*); + int jack_attach_lib_shm (jack_shm_info_t*); int jack_attach_shm_read (jack_shm_info_t*); + int jack_attach_lib_shm_read (jack_shm_info_t*); int jack_resize_shm (jack_shm_info_t*, jack_shmsize_t size); #ifdef __cplusplus diff --git a/common/wscript b/common/wscript index 31542b17..0f98c444 100644 --- a/common/wscript +++ b/common/wscript @@ -11,6 +11,7 @@ def configure(conf): if conf.is_defined('HAVE_SAMPLERATE'): conf.env['LIB_SAMPLERATE'] = ['samplerate'] + conf.env['BUILD_NETLIB'] = conf.is_defined('HAVE_SAMPLERATE') conf.env['BUILD_ADAPTER'] = conf.is_defined('HAVE_SAMPLERATE') def create_jack_process_obj(bld, target, sources, uselib = None): @@ -30,8 +31,8 @@ def create_jack_process_obj(bld, target, sources, uselib = None): if bld.env['IS_LINUX']: process.env.append_value("CPPFLAGS", "-fvisibility=hidden") if bld.env['IS_MACOSX']: - process.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc") - #process.env.append_value("LINKFLAGS", "-arch i386 -arch ppc") + process.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") + #process.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") process.env.append_value("CPPFLAGS", "-fvisibility=hidden") process.install_path = '${ADDON_DIR}/' process.uselib_local = uselib.name @@ -65,7 +66,7 @@ def build(bld): ] includes = ['.', './jack', '..'] - uselib = ["PTHREAD"] + uselib = ["PTHREAD", "CELT"] if bld.env['IS_LINUX']: common_libsources += [ @@ -113,6 +114,7 @@ def build(bld): serverlib.source = [] + common_libsources serverlib.source += [ 'JackAudioDriver.cpp', + 'JackTimedDriver.cpp', 'JackMidiDriver.cpp', 'JackDriver.cpp', 'JackEngine.cpp', @@ -130,8 +132,17 @@ def build(bld): 'JackNetTool.cpp', 'JackNetInterface.cpp', 'JackArgParser.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']: @@ -165,14 +176,48 @@ def build(bld): if bld.env['IS_MACOSX']: serverlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") - serverlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc") - #serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module -arch i386 -arch ppc") + serverlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") + #serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module -arch i386 -arch ppc -arch x86_64") serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework CoreFoundation -framework vecLib -single_module") serverlib.env.append_value("LINKFLAGS", "-compatibility_version 1 -current_version 1") if bld.env['IS_SUN']: serverlib.env.append_value("LINKFLAGS", "-lnsl -lsocket") + if bld.env['BUILD_NETLIB'] == True: + netlib = bld.new_task_gen('cxx', 'shlib') + netlib.features.append('cc') + netlib.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] + netlib.includes = includes + netlib.name = 'netlib' + netlib.target = 'jacknet' + netlib.uselib = ['SAMPLERATE', 'CELT', 'PTHREAD' , 'RT'] + netlib.install_path = '${LIBDIR}' + netlib.source = [ + 'JackNetAPI.cpp', + 'JackNetInterface.cpp', + 'JackNetTool.cpp', + 'JackAudioAdapterInterface.cpp', + 'JackLibSampleRateResampler.cpp', + 'JackResampler.cpp', + 'JackGlobals.cpp', + 'ringbuffer.c'] + + if bld.env['IS_LINUX']: + netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../linux/JackLinuxTime.c'] + netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + + if bld.env['IS_SUN']: + netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../solaris/JackSolarisTime.c'] + netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + + + if bld.env['IS_MACOSX']: + netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../macosx/JackMachThread.cpp', '../macosx/JackMachTime.c'] + netlib.env.append_value("LINKFLAGS", "-framework CoreAudio -single_module") + + netlib.vnum = bld.env['JACK_API_VERSION'] + clientlib = bld.new_task_gen('cxx', 'shlib') clientlib.features.append('cc') clientlib.defines = 'HAVE_CONFIG_H' @@ -214,8 +259,8 @@ def build(bld): if bld.env['IS_MACOSX']: clientlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") - clientlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc") - #clientlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module -arch i386 -arch ppc" + clientlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") + #clientlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module -arch i386 -arch ppc -arch x86_64" clientlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module") clientlib.env.append_value("LINKFLAGS", "-compatibility_version 1 -current_version 1") diff --git a/dbus/controller.c b/dbus/controller.c index 77acef39..cada1f07 100644 --- a/dbus/controller.c +++ b/dbus/controller.c @@ -1,6 +1,6 @@ /* -*- Mode: C ; c-basic-offset: 4 -*- */ /* - Copyright (C) 2007,2008,2010 Nedko Arnaudov + Copyright (C) 2007,2008,2010,2011 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include "controller.h" #include "controller_internal.h" @@ -38,10 +41,12 @@ struct jack_dbus_interface_descriptor * g_jackcontroller_interfaces[] = &g_jack_controller_iface_control, &g_jack_controller_iface_configure, &g_jack_controller_iface_patchbay, + &g_jack_controller_iface_session_manager, &g_jack_controller_iface_transport, NULL }; +static jackctl_driver_t * jack_controller_find_driver( jackctl_server_t *server, @@ -64,6 +69,80 @@ jack_controller_find_driver( return NULL; } +static bool jack_controller_check_slave_driver(struct jack_controller * controller_ptr, const char * name) +{ + 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 (strcmp(name, driver_ptr->name) == 0) + { + return true; + } + } + + return false; +} + +static bool jack_controller_load_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); + + assert(driver_ptr->handle != NULL); + assert(!driver_ptr->loaded); + + if (!jackctl_server_add_slave(controller_ptr->server, driver_ptr->handle)) + { + jack_error("Driver \"%s\" cannot be loaded", driver_ptr->name); + return false; + } + + driver_ptr->loaded = true; + } + + return true; +} + +static void jack_controller_unload_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->loaded) + { + jackctl_server_remove_slave(controller_ptr->server, driver_ptr->handle); + driver_ptr->loaded = false; + } + } +} + +static void jack_controller_remove_slave_drivers(struct jack_controller * controller_ptr) +{ + struct jack_controller_slave_driver * driver_ptr; + + while (!list_empty(&controller_ptr->slave_drivers)) + { + driver_ptr = list_entry(controller_ptr->slave_drivers.next, struct jack_controller_slave_driver, siblings); + assert(!driver_ptr->loaded); + list_del(&driver_ptr->siblings); + free(driver_ptr->name); + free(driver_ptr); + } + + controller_ptr->slave_drivers_vparam_value.str[0] = 0; +} + +static jackctl_internal_t * jack_controller_find_internal( jackctl_server_t *server, @@ -86,42 +165,18 @@ jack_controller_find_internal( return NULL; } -jackctl_parameter_t * -jack_controller_find_parameter( - const JSList * parameters_list, - const char * parameter_name) -{ - while (parameters_list) - { - if (strcmp(jackctl_parameter_get_name((jackctl_parameter_t *)parameters_list->data), parameter_name) == 0) - { - return parameters_list->data; - } - - parameters_list = jack_slist_next(parameters_list); - } - - return NULL; -} - bool jack_controller_select_driver( struct jack_controller * controller_ptr, const char * driver_name) { - jackctl_driver_t *driver; - - driver = jack_controller_find_driver(controller_ptr->server, driver_name); - if (driver == NULL) + if (!jack_params_set_driver(controller_ptr->params, driver_name)) { return false; } jack_info("driver \"%s\" selected", driver_name); - controller_ptr->driver = driver; - controller_ptr->driver_set = true; - return true; } @@ -145,22 +200,18 @@ jack_controller_start_server( assert(!controller_ptr->started); /* should be ensured by caller */ - if (controller_ptr->driver == NULL) - { - jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Select driver first!"); - goto fail; - } - controller_ptr->xruns = 0; if (!jackctl_server_open( controller_ptr->server, - controller_ptr->driver)) + jack_params_get_driver(controller_ptr->params))) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to open server"); goto fail; } + jack_controller_load_slave_drivers(controller_ptr); + if (!jackctl_server_start( controller_ptr->server)) { @@ -221,6 +272,8 @@ fail_stop_server: } fail_close_server: + jack_controller_unload_slave_drivers(controller_ptr); + if (!jackctl_server_close(controller_ptr->server)) { jack_error("failed to close jack server"); @@ -237,6 +290,15 @@ jack_controller_stop_server( { int ret; + pthread_mutex_lock(&controller_ptr->lock); + if (!list_empty(&controller_ptr->session_pending_commands)) + { + pthread_mutex_unlock(&controller_ptr->lock); + jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Refusing to stop JACK server because of pending session commands"); + return false; + } + pthread_mutex_unlock(&controller_ptr->lock); + jack_info("Stopping jack server..."); assert(controller_ptr->started); /* should be ensured by caller */ @@ -263,6 +325,8 @@ jack_controller_stop_server( return FALSE; } + jack_controller_unload_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"); @@ -279,11 +343,14 @@ jack_controller_switch_master( struct jack_controller * controller_ptr, void *dbus_call_context_ptr) { + assert(controller_ptr->started); /* should be ensured by caller */ + if (!jackctl_server_switch_master( controller_ptr->server, - controller_ptr->driver)) + jack_params_get_driver(controller_ptr->params))) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to switch master"); + controller_ptr->started = false; return FALSE; } @@ -338,10 +405,10 @@ on_device_release(const char * device_name) // Look for corresponding reserved device for (i = 0; i < DEVICE_MAX; i++) { - if (strcmp(g_reserved_device[i].device_name, device_name) == 0) + if (strcmp(g_reserved_device[i].device_name, device_name) == 0) break; } - + if (i < DEVICE_MAX) { jack_info("Released audio card %s", device_name); rd_release(g_reserved_device[i].reserved_device); @@ -352,16 +419,109 @@ on_device_release(const char * device_name) g_device_count--; } +#define controller_ptr ((struct jack_controller *)obj) + +static bool slave_drivers_parameter_is_set(void * obj) +{ + return controller_ptr->slave_drivers_set; +} + +static bool slave_drivers_parameter_reset(void * obj) +{ + if (controller_ptr->started) + { + jack_error("Cannot modify slave-drivers when server is started"); + return false; + } + + jack_controller_remove_slave_drivers(controller_ptr); + controller_ptr->slave_drivers_set = false; + return true; +} + +static union jackctl_parameter_value slave_drivers_parameter_get_value(void * obj) +{ + return controller_ptr->slave_drivers_vparam_value; +} + +static bool slave_drivers_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) +{ + char * buffer; + char * save; + const char * token; + struct list_head old_list; + struct list_head new_list; + union jackctl_parameter_value old_value; + union jackctl_parameter_value new_value; + bool old_set; + + if (controller_ptr->started) + { + jack_error("Cannot modify slave-drivers when server is started"); + return false; + } + + old_set = controller_ptr->slave_drivers_set; + old_value = controller_ptr->slave_drivers_vparam_value; + controller_ptr->slave_drivers_vparam_value.str[0] = 0; + old_list = controller_ptr->slave_drivers; + INIT_LIST_HEAD(&controller_ptr->slave_drivers); + + buffer = strdup(value_ptr->str); + if (buffer == NULL) + { + jack_error("strdup() failed."); + return false; + } + + token = strtok_r(buffer, ",", &save); + while (token) + { + //jack_info("slave driver '%s'", token); + if (!jack_controller_add_slave_driver(controller_ptr, token)) + { + jack_controller_remove_slave_drivers(controller_ptr); + controller_ptr->slave_drivers = old_list; + controller_ptr->slave_drivers_vparam_value = old_value; + controller_ptr->slave_drivers_set = old_set; + + free(buffer); + + return false; + } + + token = strtok_r(NULL, ",", &save); + } + + new_value = controller_ptr->slave_drivers_vparam_value; + new_list = controller_ptr->slave_drivers; + controller_ptr->slave_drivers = old_list; + jack_controller_remove_slave_drivers(controller_ptr); + controller_ptr->slave_drivers_vparam_value = new_value; + controller_ptr->slave_drivers = new_list; + controller_ptr->slave_drivers_set = true; + + free(buffer); + + return true; +} + +static union jackctl_parameter_value slave_drivers_parameter_get_default_value(void * obj) +{ + union jackctl_parameter_value value; + value.str[0] = 0; + return value; +} + +#undef controller_ptr + void * jack_controller_create( DBusConnection *connection) { + int error; struct jack_controller *controller_ptr; - const JSList * node_ptr; - const char ** driver_name_target; - const char ** internal_name_target; - JSList * drivers; - JSList * internals; + const char * address[PARAM_ADDRESS_SIZE]; DBusObjectPathVTable vtable = { jack_dbus_message_handler_unregister, @@ -376,60 +536,55 @@ jack_controller_create( goto fail; } + error = pthread_mutex_init(&controller_ptr->lock, NULL); + if (error != 0) + { + jack_error("Failed to initialize mutex. error %d", error); + goto fail_free; + } + + INIT_LIST_HEAD(&controller_ptr->session_pending_commands); + controller_ptr->server = jackctl_server_create(on_device_acquire, on_device_release); if (controller_ptr->server == NULL) { jack_error("Failed to create server object"); - goto fail_free; + goto fail_uninit_mutex; } - controller_ptr->client = NULL; - controller_ptr->started = false; - controller_ptr->driver = NULL; - controller_ptr->driver_set = false; - - drivers = (JSList *)jackctl_server_get_drivers_list(controller_ptr->server); - controller_ptr->drivers_count = jack_slist_length(drivers); - controller_ptr->driver_names = malloc(controller_ptr->drivers_count * sizeof(const char *)); - if (controller_ptr->driver_names == NULL) + controller_ptr->params = jack_params_create(controller_ptr->server); + if (controller_ptr->params == NULL) { - jack_error("Ran out of memory trying to allocate driver names array"); + jack_error("Failed to initialize parameter tree"); goto fail_destroy_server; } - driver_name_target = controller_ptr->driver_names; - node_ptr = jackctl_server_get_drivers_list(controller_ptr->server); - while (node_ptr != NULL) - { - *driver_name_target = jackctl_driver_get_name((jackctl_driver_t *)node_ptr->data); + controller_ptr->client = NULL; + controller_ptr->started = false; - /* select default driver */ - if (controller_ptr->driver == NULL && strcmp(*driver_name_target, DEFAULT_DRIVER) == 0) - { - controller_ptr->driver = (jackctl_driver_t *)node_ptr->data; - } + controller_ptr->pending_save = 0; - node_ptr = jack_slist_next(node_ptr); - driver_name_target++; - } - - internals = (JSList *)jackctl_server_get_internals_list(controller_ptr->server); - controller_ptr->internals_count = jack_slist_length(internals); - controller_ptr->internal_names = malloc(controller_ptr->internals_count * sizeof(const char *)); - if (controller_ptr->internal_names == NULL) - { - jack_error("Ran out of memory trying to allocate internals names array"); - goto fail_free_driver_names_array; - } + INIT_LIST_HEAD(&controller_ptr->slave_drivers); + controller_ptr->slave_drivers_set = false; + controller_ptr->slave_drivers_vparam_value.str[0] = 0; - internal_name_target = controller_ptr->internal_names; - node_ptr = jackctl_server_get_internals_list(controller_ptr->server); - while (node_ptr != NULL) - { - *internal_name_target = jackctl_internal_get_name((jackctl_internal_t *)node_ptr->data); - node_ptr = jack_slist_next(node_ptr); - internal_name_target++; - } + controller_ptr->slave_drivers_vparam.obj = controller_ptr; + + controller_ptr->slave_drivers_vparam.vtable.is_set = slave_drivers_parameter_is_set; + controller_ptr->slave_drivers_vparam.vtable.reset = slave_drivers_parameter_reset; + controller_ptr->slave_drivers_vparam.vtable.get_value = slave_drivers_parameter_get_value; + controller_ptr->slave_drivers_vparam.vtable.set_value = slave_drivers_parameter_set_value; + controller_ptr->slave_drivers_vparam.vtable.get_default_value = slave_drivers_parameter_get_default_value; + + controller_ptr->slave_drivers_vparam.type = JackParamString; + controller_ptr->slave_drivers_vparam.name = "slave-drivers"; + controller_ptr->slave_drivers_vparam.short_decr = "Slave drivers to use"; + controller_ptr->slave_drivers_vparam.long_descr = "A comma separated list of slave drivers"; + controller_ptr->slave_drivers_vparam.constraint_flags = 0; + + address[0] = PTNODE_ENGINE; + address[1] = NULL; + jack_params_add_parameter(controller_ptr->params, address, true, &controller_ptr->slave_drivers_vparam); controller_ptr->dbus_descriptor.context = controller_ptr; controller_ptr->dbus_descriptor.interfaces = g_jackcontroller_interfaces; @@ -441,22 +596,22 @@ jack_controller_create( &controller_ptr->dbus_descriptor)) { jack_error("Ran out of memory trying to register D-Bus object path"); - goto fail_free_internal_names_array; + goto fail_destroy_params; } jack_controller_settings_load(controller_ptr); return controller_ptr; -fail_free_internal_names_array: - free(controller_ptr->internal_names); - -fail_free_driver_names_array: - free(controller_ptr->driver_names); +fail_destroy_params: + jack_params_destroy(controller_ptr->params); fail_destroy_server: jackctl_server_destroy(controller_ptr->server); +fail_uninit_mutex: + pthread_mutex_destroy(&controller_ptr->lock); + fail_free: free(controller_ptr); @@ -465,41 +620,106 @@ 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; + jackctl_driver_t * driver; + struct jack_controller_slave_driver * driver_ptr; + size_t len_old; + size_t len_new; + + len_old = strlen(controller_ptr->slave_drivers_vparam_value.str); + len_new = strlen(driver_name); + if (len_old + len_new + 2 > sizeof(controller_ptr->slave_drivers_vparam_value.str)) + { + jack_error("No more space for slave drivers."); + return false; + } driver = jack_controller_find_driver(controller_ptr->server, driver_name); - if (driver == NULL) { + jack_error("Unknown driver \"%s\"", driver_name); return false; } - jack_info("driver \"%s\" selected", driver_name); + if (jack_controller_check_slave_driver(controller_ptr, driver_name)) + { + jack_info("Driver \"%s\" is already slave", driver_name); + return true; + } - return jackctl_server_add_slave(controller_ptr->server, driver); + 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; + } + + 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 = driver; + driver_ptr->loaded = false; + + jack_info("driver \"%s\" set as slave", driver_name); + + list_add_tail(&driver_ptr->siblings, &controller_ptr->slave_drivers); + + if (len_old != 0) + { + controller_ptr->slave_drivers_vparam_value.str[len_old++] = ','; + } + + memcpy(controller_ptr->slave_drivers_vparam_value.str + len_old, driver_name, len_new + 1); + controller_ptr->slave_drivers_set = true; + + 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) + { + list_del(&driver_ptr->siblings); + free(driver_ptr->name); + free(driver_ptr); + + /* update the slave-drivers param value */ + controller_ptr->slave_drivers_vparam_value.str[0] = 0; + list_for_each(node_ptr, &controller_ptr->slave_drivers) + { + driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); + if (controller_ptr->slave_drivers_vparam_value.str[0] != 0) + { + strcat(controller_ptr->slave_drivers_vparam_value.str, ","); + } + + strcat(controller_ptr->slave_drivers_vparam_value.str, driver_ptr->name); + } + + jack_info("driver \"%s\" is not slave anymore", driver_name); + + return true; + } } - jack_info("driver \"%s\" selected", driver_name); - - return jackctl_server_remove_slave(controller_ptr->server, driver); + return false; } bool @@ -546,14 +766,59 @@ jack_controller_destroy( { if (controller_ptr->started) { - jack_controller_stop_server(controller_ptr, NULL); + while (!jack_controller_stop_server(controller_ptr, NULL)) + { + jack_info("jack server failed to stop, retrying in 3 seconds..."); + usleep(3000000); + } } - free(controller_ptr->driver_names); - free(controller_ptr->internal_names); - + jack_controller_remove_slave_drivers(controller_ptr); + jack_params_destroy(controller_ptr->params); jackctl_server_destroy(controller_ptr->server); - + pthread_mutex_destroy(&controller_ptr->lock); free(controller_ptr); } +void +jack_controller_run( + void * context) +{ + struct sysinfo si; + + if (controller_ptr->pending_save == 0) + { + return; + } + + if (sysinfo(&si) != 0) + { + jack_error("sysinfo() failed with %d", errno); + } + else if (si.uptime < controller_ptr->pending_save + 2) /* delay save by two seconds */ + { + return; + } + + controller_ptr->pending_save = 0; + jack_controller_settings_save_auto(controller_ptr); +} + +#undef controller_ptr + +void +jack_controller_pending_save( + struct jack_controller * controller_ptr) +{ + struct sysinfo si; + + if (sysinfo(&si) != 0) + { + jack_error("sysinfo() failed with %d.", errno); + controller_ptr->pending_save = 0; + jack_controller_settings_save_auto(controller_ptr); + return; + } + + controller_ptr->pending_save = si.uptime; +} diff --git a/dbus/controller.h b/dbus/controller.h index 19887b4f..1db79ecd 100644 --- a/dbus/controller.h +++ b/dbus/controller.h @@ -24,6 +24,10 @@ void * jack_controller_create( DBusConnection *connection); +void +jack_controller_run( + void *controller_ptr); + void jack_controller_destroy( void *controller_ptr); diff --git a/dbus/controller_iface_configure.c b/dbus/controller_iface_configure.c index dfa56703..a5e67f30 100644 --- a/dbus/controller_iface_configure.c +++ b/dbus/controller_iface_configure.c @@ -1,6 +1,6 @@ /* -*- Mode: C ; c-basic-offset: 4 -*- */ /* - Copyright (C) 2007,2008 Nedko Arnaudov + Copyright (C) 2007,2008,2011 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify @@ -31,24 +31,6 @@ #include "controller_internal.h" #include "xml.h" -#define PTNODE_ENGINE "engine" -#define PTNODE_DRIVER "driver" -#define PTNODE_DRIVERS "drivers" -#define PTNODE_INTERNALS "internals" - -#define ENGINE_DRIVER_PARAMETER_NAME "driver" -#define ENGINE_DRIVER_PARAMETER_TYPE JackParamString -#define ENGINE_DRIVER_PARAMETER_SHORT_DESCR "Driver to use" -#define ENGINE_DRIVER_PARAMETER_LONG_DESCR "" - -struct parameter_info -{ - unsigned char type; - const char * name; - const char * short_decr; - const char * long_descr; -}; - unsigned char jack_controller_dbus_types[JACK_PARAM_MAX] = { [JackParamInt] = DBUS_TYPE_INT32, @@ -238,12 +220,13 @@ jack_controller_dbus_get_parameter_address_ex( index = 0; while ((type = dbus_message_iter_get_arg_type(&array_iter)) != DBUS_TYPE_INVALID) { - if (index == 3) + if (index == PARAM_ADDRESS_SIZE) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid arguments to method '%s'. Parameter address array must contain not more than three elements.", + "Invalid arguments to method '%s'. Parameter address array must contain not more than %u elements.", + (unsigned int)PARAM_ADDRESS_SIZE, call->method_name); return false; } @@ -267,7 +250,7 @@ jack_controller_dbus_get_parameter_address_ex( index++; } - while (index < 3) + while (index < PARAM_ADDRESS_SIZE) { address_array[index] = NULL; index++; @@ -301,45 +284,9 @@ jack_controller_dbus_get_parameter_address( #define controller_ptr ((struct jack_controller *)call->context) -static -bool -jack_controller_fill_parameter_names( - struct jack_dbus_method_call * call, - DBusMessageIter * iter_ptr, - const char * special_first, - const JSList * parameters_list) +static bool append_node_name(void * context, const char * name) { - DBusMessageIter array_iter; - const char * param_name; - - if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_ARRAY, "s", &array_iter)) - { - return false; - } - - if (special_first != NULL) - { - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &special_first)) - { - dbus_message_iter_close_container(iter_ptr, &array_iter); - return false; - } - } - - /* Append parameter descriptions to the array. */ - while (parameters_list != NULL) - { - param_name = jackctl_parameter_get_name(parameters_list->data); - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, ¶m_name)) - { - dbus_message_iter_close_container(iter_ptr, &array_iter); - return false; - } - - parameters_list = jack_slist_next(parameters_list); - } - - return dbus_message_iter_close_container(iter_ptr, &array_iter); + return dbus_message_iter_append_basic(context, DBUS_TYPE_STRING, &name); } static @@ -347,14 +294,10 @@ void jack_controller_dbus_read_container( struct jack_dbus_method_call * call) { - const char * address[3]; + const char * address[PARAM_ADDRESS_SIZE]; dbus_bool_t leaf; DBusMessageIter iter; DBusMessageIter array_iter; - const char * child_name; - unsigned int index; - jackctl_internal_t * internal; - jackctl_driver_t * driver; //jack_info("jack_controller_dbus_read_container() called"); @@ -377,218 +320,37 @@ jack_controller_dbus_read_container( dbus_message_iter_init_append(call->reply, &iter); - if (address[0] == NULL) /* root node */ + if (!jack_params_check_address(controller_ptr->params, address, false)) { - //jack_info("reading root container"); - - leaf = false; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &leaf)) - { - goto oom_unref; - } - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) - { - goto oom_unref; - } - - child_name = PTNODE_ENGINE; - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &child_name)) - { - goto oom_close_unref; - } - - child_name = PTNODE_DRIVER; - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &child_name)) - { - goto oom_close_unref; - } - - child_name = PTNODE_DRIVERS; - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &child_name)) - { - goto oom_close_unref; - } - - child_name = PTNODE_INTERNALS; - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &child_name)) - { - goto oom_close_unref; - } - - dbus_message_iter_close_container(&iter, &array_iter); - + jack_dbus_error( + call, + JACK_DBUS_ERROR_INVALID_ARGS, + "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", + address[0], + address[1], + address[2], + call->method_name); return; } - if (address[0] != NULL && - address[1] == NULL && - strcmp(address[0], PTNODE_ENGINE) == 0) /* engine parameters requested */ + leaf = jack_params_is_leaf_container(controller_ptr->params, address); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &leaf)) { - //jack_info("reading engine params container"); - - leaf = true; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &leaf)) - { - goto oom_unref; - } - - if (!jack_controller_fill_parameter_names( - call, - &iter, - ENGINE_DRIVER_PARAMETER_NAME, - jackctl_server_get_parameters(controller_ptr->server))) - { - goto oom_unref; - } - - return; + goto oom_unref; } - if (address[0] != NULL && - address[1] == NULL && - strcmp(address[0], PTNODE_DRIVER) == 0) /* current driver parameters requested */ + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) { - //jack_info("reading current driver params container"); - - leaf = true; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &leaf)) - { - goto oom_unref; - } - - if (!jack_controller_fill_parameter_names( - call, - &iter, - NULL, - jackctl_driver_get_parameters(controller_ptr->driver))) - { - goto oom_unref; - } - - return; + goto oom_unref; } - if (address[0] != NULL && - strcmp(address[0], PTNODE_DRIVERS) == 0) - { - leaf = address[1] != NULL; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &leaf)) - { - goto oom_unref; - } - - if (!leaf) /* available drivers requested */ - { - //jack_info("reading drivers container"); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) - { - goto oom_unref; - } - - for (index = 0; index < controller_ptr->drivers_count; index++) - { - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, controller_ptr->driver_names + index)) - { - goto oom_close_unref; - } - } - - dbus_message_iter_close_container(&iter, &array_iter); - } - else /* specified driver parameters requested */ - { - //jack_info("reading driver '%s' params container", address[1]); - - driver = jack_controller_find_driver(controller_ptr->server, address[1]); - if (driver == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown driver '%s'", - address[1]); - return; - } - if (!jack_controller_fill_parameter_names( - call, - &iter, - NULL, - jackctl_driver_get_parameters(driver))) - { - goto oom_unref; - } - } - - return; - } - - if (address[0] != NULL && - strcmp(address[0], PTNODE_INTERNALS) == 0) + if (!jack_params_iterate_container(controller_ptr->params, address, append_node_name, &array_iter)) { - leaf = address[1] != NULL; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &leaf)) - { - goto oom_unref; - } - - if (!leaf) /* available internals requested */ - { - //jack_info("reading internals container"); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) - { - goto oom_unref; - } - - for (index = 0; index < controller_ptr->internals_count; index++) - { - if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, controller_ptr->internal_names + index)) - { - goto oom_close_unref; - } - } - - dbus_message_iter_close_container(&iter, &array_iter); - } - else /* specified driver parameters requested */ - { - //jack_info("reading internal '%s' params container", address[1]); - - internal = jack_controller_find_internal(controller_ptr->server, address[1]); - if (internal == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown internal '%s'", - address[1]); - return; - } - - if (!jack_controller_fill_parameter_names( - call, - &iter, - NULL, - jackctl_internal_get_parameters(internal))) - { - goto oom_unref; - } - } - - return; + goto oom_close_unref; } - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", - address[0], - address[1], - address[2], - call->method_name); + dbus_message_iter_close_container(&iter, &array_iter); return; @@ -603,115 +365,107 @@ oom: jack_error ("Ran out of memory trying to construct method return"); } -static -void -jack_controller_get_parameters_info( - struct jack_dbus_method_call * call, - struct parameter_info * special_parameter_info_ptr, - const JSList * parameters_list) +static bool append_parameter(void * context, const struct jack_parameter * param_ptr) { - DBusMessageIter iter, array_iter, struct_iter; + DBusMessageIter struct_iter; unsigned char type; - const char *str; - call->reply = dbus_message_new_method_return (call->message); - if (!call->reply) + /* Open the struct. */ + if (!dbus_message_iter_open_container(context, DBUS_TYPE_STRUCT, NULL, &struct_iter)) { goto fail; } - dbus_message_iter_init_append (call->reply, &iter); + /* Append parameter type. */ + type = PARAM_TYPE_JACK_TO_DBUS(param_ptr->type); + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &type)) + { + goto fail_close; + } - /* Open the array. */ - if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ysss)", &array_iter)) + /* Append parameter name. */ + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¶m_ptr->name)) { - goto fail_unref; + goto fail_close; } - if (special_parameter_info_ptr != NULL) + /* Append parameter short description. */ + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¶m_ptr->short_decr)) { - /* Open the struct. */ - if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) - { - goto fail_close_unref; - } + goto fail_close; + } - /* 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 long description. */ + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¶m_ptr->long_descr)) + { + goto fail_close; + } - /* Append parameter name. */ - if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &special_parameter_info_ptr->name)) - { - goto fail_close2_unref; - } + /* Close the struct. */ + if (!dbus_message_iter_close_container(context, &struct_iter)) + { + goto fail; + } - /* 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; - } + return true; - /* 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; - } +fail_close: + dbus_message_iter_close_container(context, &struct_iter); - /* Close the struct. */ - if (!dbus_message_iter_close_container (&array_iter, &struct_iter)) - { - goto fail_close_unref; - } - } +fail: + return false; +} + +static +void +jack_controller_dbus_get_parameters_info( + struct jack_dbus_method_call * call) +{ + const char * address[PARAM_ADDRESS_SIZE]; + DBusMessageIter iter, array_iter; + + //jack_info("jack_controller_dbus_get_parameters_info() called"); - /* Append parameter descriptions to the array. */ - while (parameters_list != NULL) + if (!jack_controller_dbus_get_parameter_address(call, address)) { - /* Open the struct. */ - if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) - { - goto fail_close_unref; - } + /* The method call had invalid arguments meaning that + * jack_controller_dbus_get_parameter_address() has + * constructed an error for us. */ + return; + } - /* 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; - } + //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - /* 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; - } + if (!jack_params_check_address(controller_ptr->params, address, true)) + { + jack_dbus_error( + call, + JACK_DBUS_ERROR_INVALID_ARGS, + "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", + address[0], + address[1], + address[2], + call->method_name); + return; + } - /* 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; - } + call->reply = dbus_message_new_method_return (call->message); + if (!call->reply) + { + goto fail; + } - /* 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; - } + dbus_message_iter_init_append (call->reply, &iter); - /* Close the struct. */ - if (!dbus_message_iter_close_container (&array_iter, &struct_iter)) - { - goto fail_close_unref; - } + /* Open the array. */ + if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ysss)", &array_iter)) + { + goto fail_unref; + } - parameters_list = jack_slist_next(parameters_list); + if (!jack_params_iterate_params(controller_ptr->params, address, append_parameter, &array_iter)) + { + goto fail_close_unref; } /* Close the array. */ @@ -722,9 +476,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); @@ -738,15 +489,14 @@ fail: static void -jack_controller_dbus_get_parameters_info( +jack_controller_dbus_get_parameter_info( struct jack_dbus_method_call * call) { - const char * address[3]; - jackctl_internal_t * internal; - jackctl_driver_t * driver; - struct parameter_info driver_parameter_info; + const char * address[PARAM_ADDRESS_SIZE]; + const struct jack_parameter * param_ptr; + DBusMessageIter iter; - //jack_info("jack_controller_dbus_get_parameters_info() called"); + //jack_info("jack_controller_dbus_get_parameter_info() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { @@ -758,152 +508,35 @@ jack_controller_dbus_get_parameters_info( //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - if (address[0] != NULL && - address[1] == NULL && - strcmp(address[0], PTNODE_ENGINE) == 0) /* engine parameters requested */ + param_ptr = jack_params_get_parameter(controller_ptr->params, address); + if (param_ptr == NULL) { - driver_parameter_info.type = ENGINE_DRIVER_PARAMETER_TYPE; - driver_parameter_info.name = ENGINE_DRIVER_PARAMETER_NAME; - driver_parameter_info.short_decr = ENGINE_DRIVER_PARAMETER_SHORT_DESCR; - driver_parameter_info.long_descr = ENGINE_DRIVER_PARAMETER_LONG_DESCR; - - jack_controller_get_parameters_info( + jack_dbus_error( call, - &driver_parameter_info, - jackctl_server_get_parameters(controller_ptr->server)); - + JACK_DBUS_ERROR_INVALID_ARGS, + "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", + address[0], + address[1], + address[2], + call->method_name); return; } - if (address[0] != NULL && - address[1] == NULL && - strcmp(address[0], PTNODE_DRIVER) == 0) /* current driver parameters requested */ + call->reply = dbus_message_new_method_return(call->message); + if (!call->reply) { - jack_controller_get_parameters_info( - call, - NULL, - jackctl_driver_get_parameters(controller_ptr->driver)); - - return; + goto fail; } - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_DRIVERS) == 0) - { - driver = jack_controller_find_driver(controller_ptr->server, address[1]); - if (driver == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown driver '%s'", - address[1]); - return; - } - - jack_controller_get_parameters_info( - call, - NULL, - jackctl_driver_get_parameters(driver)); + dbus_message_iter_init_append(call->reply, &iter); - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_INTERNALS) == 0) - { - internal = jack_controller_find_internal(controller_ptr->server, address[1]); - if (internal == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown internal '%s'", - address[1]); - return; - } - - jack_controller_get_parameters_info( - call, - NULL, - jackctl_internal_get_parameters(internal)); - - return; - } - - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", - address[0], - address[1], - address[2], - call->method_name); -} - -static -void -jack_controller_get_parameter_info_ex( - struct jack_dbus_method_call * call, - struct parameter_info * info_ptr) -{ - DBusMessageIter iter, struct_iter; - unsigned char type; - - call->reply = dbus_message_new_method_return(call->message); - if (!call->reply) - { - goto fail; - } - - 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)) - { - goto fail_unref; + if (!append_parameter(&iter, param_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; @@ -914,32 +547,17 @@ fail: static void -jack_controller_get_parameter_info( - struct jack_dbus_method_call * call, - jackctl_parameter_t * parameter) -{ - 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_get_parameter_info_ex(call, &info); -} - -static -void -jack_controller_dbus_get_parameter_info( +jack_controller_dbus_get_parameter_constraint( struct jack_dbus_method_call * call) { - const char * address[3]; - jackctl_internal_t * internal; - jackctl_driver_t * driver; - jackctl_parameter_t * parameter; - struct parameter_info driver_parameter_info; + const char * address[PARAM_ADDRESS_SIZE]; + const struct jack_parameter * param_ptr; + uint32_t index; + DBusMessageIter iter, array_iter, struct_iter; + const char * descr; + message_arg_t value; - //jack_info("jack_controller_dbus_get_parameter_info() called"); + //jack_info("jack_controller_dbus_get_parameter_constraint() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { @@ -951,178 +569,57 @@ jack_controller_dbus_get_parameter_info( //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_ENGINE) == 0) /* engine parameters requested */ + param_ptr = jack_params_get_parameter(controller_ptr->params, address); + if (param_ptr == NULL) { - if (strcmp(address[1], ENGINE_DRIVER_PARAMETER_NAME) == 0) - { - driver_parameter_info.type = ENGINE_DRIVER_PARAMETER_TYPE; - driver_parameter_info.name = ENGINE_DRIVER_PARAMETER_NAME; - driver_parameter_info.short_decr = ENGINE_DRIVER_PARAMETER_SHORT_DESCR; - driver_parameter_info.long_descr = ENGINE_DRIVER_PARAMETER_LONG_DESCR; - - jack_controller_get_parameter_info_ex(call, &driver_parameter_info); - - return; - } - - parameter = jack_controller_find_parameter(jackctl_server_get_parameters(controller_ptr->server), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown engine parameter '%s'", - address[1]); - return; - } - - jack_controller_get_parameter_info(call, parameter); - + jack_dbus_error( + call, + JACK_DBUS_ERROR_INVALID_ARGS, + "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", + address[0], + address[1], + address[2], + call->method_name); return; } - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_DRIVER) == 0) /* current driver parameters requested */ + call->reply = dbus_message_new_method_return(call->message); + if (!call->reply) { - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(controller_ptr->driver), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[1], - jackctl_driver_get_name(controller_ptr->driver)); - return; - } + goto fail; + } - jack_controller_get_parameter_info(call, parameter); + dbus_message_iter_init_append(call->reply, &iter); - return; + if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0) + { + value.boolean = param_ptr->constraint_range; } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_DRIVERS) == 0) + else { - driver = jack_controller_find_driver(controller_ptr->server, address[1]); - if (driver == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown driver '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(driver), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[2], - address[1]); - return; - } - - jack_controller_get_parameter_info(call, parameter); - - return; + value.boolean = false; } - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_INTERNALS) == 0) + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value)) { - internal = jack_controller_find_internal(controller_ptr->server, address[1]); - if (internal == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown internal '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_internal_get_parameters(internal), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for internal '%s'", - address[2], - address[1]); - return; - } - - jack_controller_get_parameter_info(call, parameter); - - return; + goto fail_unref; } - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", - address[0], - address[1], - address[2], - call->method_name); -} - -static -void -jack_controller_get_parameter_constraint( - struct jack_dbus_method_call * call, - jackctl_parameter_t * parameter) -{ - uint32_t index; - uint32_t count; - union jackctl_parameter_value min; - union jackctl_parameter_value max; - union jackctl_parameter_value jackctl_value; - DBusMessageIter iter, array_iter, struct_iter; - const char * descr; - jackctl_param_type_t type; - message_arg_t value; - bool is_range; - - type = jackctl_parameter_get_type(parameter); - - call->reply = dbus_message_new_method_return(call->message); - if (!call->reply) + if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0) { - goto fail; + value.boolean = (param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_STRICT) != 0; } - dbus_message_iter_init_append(call->reply, &iter); - - is_range = jackctl_parameter_has_range_constraint(parameter); - value.boolean = is_range; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value)) { goto fail_unref; } - value.boolean = jackctl_parameter_constraint_is_strict(parameter); - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value)) + if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0) { - goto fail_unref; + value.boolean = (param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_FAKE_VALUE) != 0; } - value.boolean = jackctl_parameter_constraint_is_fake_value(parameter); if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value)) { goto fail_unref; @@ -1134,19 +631,26 @@ jack_controller_get_parameter_constraint( goto fail_unref; } - if (is_range) + if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) == 0) { - jackctl_parameter_get_range_constraint(parameter, &min, &max); + goto close; + } + if (param_ptr->constraint_range) + { /* Open the struct. */ if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) { goto fail_close_unref; } - jack_controller_jack_to_dbus_variant(type, &min, &value); + jack_controller_jack_to_dbus_variant(param_ptr->type, ¶m_ptr->constraint.range.min, &value); - if (!jack_dbus_message_append_variant(&struct_iter, PARAM_TYPE_JACK_TO_DBUS(type), PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(type), &value)) + if (!jack_dbus_message_append_variant( + &struct_iter, + PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), + PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), + &value)) { goto fail_close2_unref; } @@ -1170,9 +674,13 @@ jack_controller_get_parameter_constraint( goto fail_close_unref; } - jack_controller_jack_to_dbus_variant(type, &max, &value); + jack_controller_jack_to_dbus_variant(param_ptr->type, ¶m_ptr->constraint.range.max, &value); - if (!jack_dbus_message_append_variant(&struct_iter, PARAM_TYPE_JACK_TO_DBUS(type), PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(type), &value)) + if (!jack_dbus_message_append_variant( + &struct_iter, + PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), + PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), + &value)) { goto fail_close2_unref; } @@ -1192,15 +700,12 @@ jack_controller_get_parameter_constraint( } else { - count = jackctl_parameter_get_enum_constraints_count(parameter); - /* Append enum values to the array. */ - for (index = 0 ; index < count ; index++) + for (index = 0 ; index < param_ptr->constraint.enumeration.count ; index++) { - jackctl_value = jackctl_parameter_get_enum_constraint_value(parameter, index); - descr = jackctl_parameter_get_enum_constraint_description(parameter, index); + descr = param_ptr->constraint.enumeration.possible_values_array[index].short_desc; - jack_controller_jack_to_dbus_variant(type, &jackctl_value, &value); + jack_controller_jack_to_dbus_variant(param_ptr->type, ¶m_ptr->constraint.enumeration.possible_values_array[index].value, &value); /* Open the struct. */ if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) @@ -1208,7 +713,11 @@ jack_controller_get_parameter_constraint( goto fail_close_unref; } - if (!jack_dbus_message_append_variant(&struct_iter, PARAM_TYPE_JACK_TO_DBUS(type), PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(type), &value)) + if (!jack_dbus_message_append_variant( + &struct_iter, + PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), + PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), + &value)) { goto fail_close2_unref; } @@ -1226,6 +735,7 @@ jack_controller_get_parameter_constraint( } } +close: /* Close the array. */ if (!dbus_message_iter_close_container(&iter, &array_iter)) { @@ -1248,120 +758,75 @@ fail: jack_error ("Ran out of memory trying to construct method return"); } -static -void -jack_controller_get_parameter_constraint_engine_driver( +static void +jack_controller_dbus_get_parameter_value( struct jack_dbus_method_call * call) { - unsigned int index; - DBusMessageIter iter, array_iter, struct_iter; - jackctl_param_type_t type; - dbus_bool_t bval; + const char * address[PARAM_ADDRESS_SIZE]; + const struct jack_parameter * param_ptr; + union jackctl_parameter_value jackctl_value; + union jackctl_parameter_value jackctl_default_value; message_arg_t value; + message_arg_t default_value; - type = ENGINE_DRIVER_PARAMETER_TYPE; - - call->reply = dbus_message_new_method_return(call->message); - if (!call->reply) - { - goto fail; - } - - dbus_message_iter_init_append(call->reply, &iter); - - /* is_range */ - bval = false; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &bval)) - { - goto fail_unref; - } - - /* is_strict */ - bval = true; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &bval)) - { - goto fail_unref; - } - - /* is_fake_value */ - bval = true; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &bval)) - { - goto fail_unref; - } + //jack_info("jack_controller_dbus_get_parameter_value() called"); - /* Open the array. */ - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(vs)", &array_iter)) + if (!jack_controller_dbus_get_parameter_address(call, address)) { - goto fail_unref; + /* The method call had invalid arguments meaning that + * jack_controller_dbus_get_parameter_address() has + * constructed an error for us. */ + return; } - /* Append enum values to the array. */ - for (index = 0 ; index < controller_ptr->drivers_count ; index++) - { - /* Open the struct. */ - if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) - { - goto fail_close_unref; - } - - value.string = controller_ptr->driver_names[index]; - if (!jack_dbus_message_append_variant( - &struct_iter, - PARAM_TYPE_JACK_TO_DBUS(type), - PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(type), - &value)) - { - goto fail_close2_unref; - } - - if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &value)) - { - goto fail_close2_unref; - } - - /* Close the struct. */ - if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) - { - goto fail_close_unref; - } - } + //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - /* Close the array. */ - if (!dbus_message_iter_close_container(&iter, &array_iter)) + param_ptr = jack_params_get_parameter(controller_ptr->params, address); + if (param_ptr == NULL) { - goto fail_unref; + jack_dbus_error( + call, + JACK_DBUS_ERROR_INVALID_ARGS, + "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", + address[0], + address[1], + address[2], + call->method_name); + return; } - return; - -fail_close2_unref: - dbus_message_iter_close_container(&array_iter, &struct_iter); - -fail_close_unref: - dbus_message_iter_close_container(&iter, &array_iter); + jackctl_default_value = param_ptr->vtable.get_default_value(param_ptr->obj); + jackctl_value = param_ptr->vtable.get_value(param_ptr->obj); -fail_unref: - dbus_message_unref(call->reply); - call->reply = NULL; + jack_controller_jack_to_dbus_variant(param_ptr->type, &jackctl_value, &value); + jack_controller_jack_to_dbus_variant(param_ptr->type, &jackctl_default_value, &default_value); -fail: - jack_error ("Ran out of memory trying to construct method return"); + /* Construct the reply. */ + jack_dbus_construct_method_return_parameter( + call, + (dbus_bool_t)(param_ptr->vtable.is_set(param_ptr->obj) ? TRUE : FALSE), + PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), + PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), + default_value, + value); } static void -jack_controller_dbus_get_parameter_constraint( +jack_controller_dbus_set_parameter_value( struct jack_dbus_method_call * call) { - const char * address[3]; - jackctl_internal_t * internal; - jackctl_driver_t * driver; - jackctl_parameter_t * parameter; + const char * address[PARAM_ADDRESS_SIZE]; + const struct jack_parameter * param_ptr; + DBusMessageIter iter; + DBusMessageIter variant_iter; + message_arg_t arg; + int arg_type; + union jackctl_parameter_value value; - //jack_info("jack_controller_dbus_get_parameter_constraint() called"); + //jack_info("jack_controller_dbus_set_parameter_value() called"); - if (!jack_controller_dbus_get_parameter_address(call, address)) + if (!jack_controller_dbus_get_parameter_address_ex(call, &iter, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has @@ -1371,447 +836,20 @@ jack_controller_dbus_get_parameter_constraint( //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_ENGINE) == 0) /* engine parameters requested */ - { - if (strcmp(address[1], ENGINE_DRIVER_PARAMETER_NAME) == 0) - { - jack_controller_get_parameter_constraint_engine_driver(call); - - return; - } - - parameter = jack_controller_find_parameter(jackctl_server_get_parameters(controller_ptr->server), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown engine parameter '%s'", - address[1]); - return; - } - - jack_controller_get_parameter_constraint(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_DRIVER) == 0) /* current driver parameters requested */ - { - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(controller_ptr->driver), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[1], - jackctl_driver_get_name(controller_ptr->driver)); - return; - } - - jack_controller_get_parameter_constraint(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_DRIVERS) == 0) - { - driver = jack_controller_find_driver(controller_ptr->server, address[1]); - if (driver == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown driver '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(driver), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[2], - address[1]); - return; - } - - jack_controller_get_parameter_constraint(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_INTERNALS) == 0) - { - internal = jack_controller_find_internal(controller_ptr->server, address[1]); - if (internal == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown internal '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_internal_get_parameters(internal), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for internal '%s'", - address[2], - address[1]); - return; - } - - jack_controller_get_parameter_constraint(call, parameter); - - return; - } - - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", - address[0], - address[1], - address[2], - call->method_name); -} - -static -void -jack_controller_get_parameter_value( - struct jack_dbus_method_call * call, - jackctl_parameter_t * parameter) -{ - int type; - union jackctl_parameter_value jackctl_value; - union jackctl_parameter_value jackctl_default_value; - message_arg_t value; - message_arg_t default_value; - - type = jackctl_parameter_get_type(parameter); - jackctl_default_value = jackctl_parameter_get_default_value(parameter); - jackctl_value = jackctl_parameter_get_value(parameter); - - jack_controller_jack_to_dbus_variant(type, &jackctl_value, &value); - jack_controller_jack_to_dbus_variant(type, &jackctl_default_value, &default_value); - - /* Construct the reply. */ - jack_dbus_construct_method_return_parameter( - call, - (dbus_bool_t)(jackctl_parameter_is_set(parameter) ? TRUE : FALSE), - PARAM_TYPE_JACK_TO_DBUS(type), - PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(type), - default_value, - value); -} - -static -void -jack_controller_get_parameter_value_engine_driver( - struct jack_dbus_method_call * call) -{ - message_arg_t value; - message_arg_t default_value; - - default_value.string = DEFAULT_DRIVER; - value.string = jackctl_driver_get_name(controller_ptr->driver); - - /* Construct the reply. */ - jack_dbus_construct_method_return_parameter( - call, - controller_ptr->driver_set, - DBUS_TYPE_STRING, - DBUS_TYPE_STRING_AS_STRING, - default_value, - value); -} - - -static void -jack_controller_dbus_get_parameter_value( - struct jack_dbus_method_call * call) -{ - const char * address[3]; - jackctl_internal_t * internal; - jackctl_driver_t * driver; - jackctl_parameter_t * parameter; - - //jack_info("jack_controller_dbus_get_parameter_value() called"); - - if (!jack_controller_dbus_get_parameter_address(call, address)) - { - /* The method call had invalid arguments meaning that - * jack_controller_dbus_get_parameter_address() has - * constructed an error for us. */ - return; - } - - //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_ENGINE) == 0) /* engine parameters requested */ - { - if (strcmp(address[1], ENGINE_DRIVER_PARAMETER_NAME) == 0) - { - jack_controller_get_parameter_value_engine_driver(call); - - return; - } - - parameter = jack_controller_find_parameter(jackctl_server_get_parameters(controller_ptr->server), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown engine parameter '%s'", - address[1]); - return; - } - - jack_controller_get_parameter_value(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_DRIVER) == 0) /* current driver parameters requested */ - { - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(controller_ptr->driver), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[1], - jackctl_driver_get_name(controller_ptr->driver)); - return; - } - - jack_controller_get_parameter_value(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_DRIVERS) == 0) - { - driver = jack_controller_find_driver(controller_ptr->server, address[1]); - if (driver == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown driver '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(driver), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[2], - address[1]); - return; - } - - jack_controller_get_parameter_value(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_INTERNALS) == 0) - { - internal = jack_controller_find_internal(controller_ptr->server, address[1]); - if (internal == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown internal '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_internal_get_parameters(internal), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for internal '%s'", - address[2], - address[1]); - return; - } - - jack_controller_get_parameter_value(call, parameter); - - return; - } - - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", - address[0], - address[1], - address[2], - call->method_name); -} - -static -void -jack_controller_set_parameter_value( - struct jack_dbus_method_call * call, - jackctl_parameter_t * parameter, - message_arg_t * arg_ptr, - int arg_type) -{ - jackctl_param_type_t type; - union jackctl_parameter_value value; - - type = jackctl_parameter_get_type(parameter); - - if (PARAM_TYPE_JACK_TO_DBUS(type) != arg_type) + param_ptr = jack_params_get_parameter(controller_ptr->params, address); + if (param_ptr == NULL) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, - "Parameter value type mismatch: was expecting '%c', got '%c'", - (char)PARAM_TYPE_JACK_TO_DBUS(type), - (char)arg_type); - return; - } - - if (!jack_controller_dbus_to_jack_variant( - arg_type, - arg_ptr, - &value)) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Cannot convert parameter value"); - return; - } - - jackctl_parameter_set_value(parameter, &value); - - jack_controller_settings_save_auto(controller_ptr); - - jack_dbus_construct_method_return_empty(call); -} - -static -void -jack_controller_set_parameter_value_engine_driver( - struct jack_dbus_method_call * call, - message_arg_t * arg_ptr, - int arg_type) -{ - union jackctl_parameter_value value; - - if (arg_type != DBUS_TYPE_STRING) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Engine parameter value type mismatch: was expecting '%c', got '%c'", - (char)DBUS_TYPE_STRING, - (char)arg_type); - return; - } - - if (!jack_controller_dbus_to_jack_variant( - arg_type, - arg_ptr, - &value)) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Cannot convert engine parameter value"); - return; - } - - if (!jack_controller_select_driver(controller_ptr, value.str)) - { - /* Couldn't find driver with the specified name. */ - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_DRIVER, - "Unknown driver '%s'", - value.str); - return; - } - - jack_controller_settings_save_auto(controller_ptr); - - jack_dbus_construct_method_return_empty(call); -} - -static -void -jack_controller_dbus_set_parameter_value( - struct jack_dbus_method_call * call) -{ - const char * address[3]; - DBusMessageIter iter; - DBusMessageIter variant_iter; - message_arg_t arg; - int arg_type; - jackctl_internal_t * internal; - jackctl_driver_t * driver; - jackctl_parameter_t * parameter; - - //jack_info("jack_controller_dbus_set_parameter_value() called"); - - if (!jack_controller_dbus_get_parameter_address_ex(call, &iter, address)) - { - /* The method call had invalid arguments meaning that - * jack_controller_dbus_get_parameter_address() has - * constructed an error for us. */ + "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", + address[0], + address[1], + address[2], + call->method_name); return; } - //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - dbus_message_iter_next(&iter); if (dbus_message_iter_has_next(&iter)) @@ -1840,166 +878,35 @@ jack_controller_dbus_set_parameter_value( //jack_info("argument of type '%c'", arg_type); - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_ENGINE) == 0) /* engine parameters requested */ - { - if (strcmp(address[1], ENGINE_DRIVER_PARAMETER_NAME) == 0) - { - jack_controller_set_parameter_value_engine_driver(call, &arg, arg_type); - - return; - } - - parameter = jack_controller_find_parameter(jackctl_server_get_parameters(controller_ptr->server), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown engine parameter '%s'", - address[1]); - return; - } - - jack_controller_set_parameter_value(call, parameter, &arg, arg_type); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_DRIVER) == 0) /* current driver parameters requested */ + if (PARAM_TYPE_JACK_TO_DBUS(param_ptr->type) != arg_type) { - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(controller_ptr->driver), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[1], - jackctl_driver_get_name(controller_ptr->driver)); - return; - } - - jack_controller_set_parameter_value(call, parameter, &arg, arg_type); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_DRIVERS) == 0) - { - driver = jack_controller_find_driver(controller_ptr->server, address[1]); - if (driver == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown driver '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(driver), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[2], - address[1]); - return; - } - - jack_controller_set_parameter_value(call, parameter, &arg, arg_type); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_INTERNALS) == 0) - { - internal = jack_controller_find_internal(controller_ptr->server, address[1]); - if (internal == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown internal '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_internal_get_parameters(internal), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for internal '%s'", - address[2], - address[1]); - return; - } - - jack_controller_set_parameter_value(call, parameter, &arg, arg_type); - + jack_dbus_error( + call, + JACK_DBUS_ERROR_INVALID_ARGS, + "Parameter value type mismatch: was expecting '%c', got '%c'", + (char)PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), + (char)arg_type); return; } - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", - address[0], - address[1], - address[2], - call->method_name); -} - -static -void -jack_controller_reset_parameter_value( - struct jack_dbus_method_call * call, - jackctl_parameter_t * parameter) -{ - jackctl_parameter_reset(parameter); - - jack_controller_settings_save_auto(controller_ptr); - - jack_dbus_construct_method_return_empty(call); -} - -static -void -jack_controller_reset_parameter_value_engine_driver( - struct jack_dbus_method_call * call) -{ - if (!jack_controller_select_driver(controller_ptr, DEFAULT_DRIVER)) + if (!jack_controller_dbus_to_jack_variant( + arg_type, + &arg, + &value)) { - /* Couldn't find driver with the specified name. */ jack_dbus_error( call, - JACK_DBUS_ERROR_UNKNOWN_DRIVER, - "Default driver '%s' is unknown", - DEFAULT_DRIVER); + JACK_DBUS_ERROR_INVALID_ARGS, + "Cannot convert parameter value"); return; } - controller_ptr->driver_set = false; + param_ptr->vtable.set_value(param_ptr->obj, &value); - jack_controller_settings_save_auto(controller_ptr); + jack_controller_pending_save(controller_ptr); jack_dbus_construct_method_return_empty(call); + } static @@ -2007,10 +914,8 @@ void jack_controller_dbus_reset_parameter_value( struct jack_dbus_method_call * call) { - const char * address[3]; - jackctl_internal_t * internal; - jackctl_driver_t * driver; - jackctl_parameter_t * parameter; + const char * address[PARAM_ADDRESS_SIZE]; + const struct jack_parameter * param_ptr; //jack_info("jack_controller_dbus_reset_parameter_value() called"); @@ -2024,130 +929,25 @@ jack_controller_dbus_reset_parameter_value( //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_ENGINE) == 0) /* engine parameters requested */ - { - if (strcmp(address[1], ENGINE_DRIVER_PARAMETER_NAME) == 0) - { - jack_controller_reset_parameter_value_engine_driver(call); - - return; - } - - parameter = jack_controller_find_parameter(jackctl_server_get_parameters(controller_ptr->server), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown engine parameter '%s'", - address[1]); - return; - } - - jack_controller_reset_parameter_value(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] == NULL && - strcmp(address[0], PTNODE_DRIVER) == 0) /* current driver parameters requested */ - { - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(controller_ptr->driver), address[1]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[1], - jackctl_driver_get_name(controller_ptr->driver)); - return; - } - - jack_controller_reset_parameter_value(call, parameter); - - return; - } - - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_DRIVERS) == 0) + param_ptr = jack_params_get_parameter(controller_ptr->params, address); + if (param_ptr == NULL) { - driver = jack_controller_find_driver(controller_ptr->server, address[1]); - if (driver == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown driver '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(driver), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for driver '%s'", - address[2], - address[1]); - return; - } - - jack_controller_reset_parameter_value(call, parameter); - + jack_dbus_error( + call, + JACK_DBUS_ERROR_INVALID_ARGS, + "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", + address[0], + address[1], + address[2], + call->method_name); return; } - if (address[0] != NULL && - address[1] != NULL && - address[2] != NULL && - strcmp(address[0], PTNODE_INTERNALS) == 0) - { - internal = jack_controller_find_internal(controller_ptr->server, address[1]); - if (internal == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_INTERNAL, - "Unknown internal '%s'", - address[1]); - return; - } - - parameter = jack_controller_find_parameter(jackctl_internal_get_parameters(internal), address[2]); - if (parameter == NULL) - { - jack_dbus_error( - call, - JACK_DBUS_ERROR_UNKNOWN_PARAMETER, - "Unknown parameter '%s' for internal '%s'", - address[2], - address[1]); - return; - } + param_ptr->vtable.reset(param_ptr->obj); - jack_controller_reset_parameter_value(call, parameter); + jack_controller_pending_save(controller_ptr); - return; - } - - jack_dbus_error( - call, - JACK_DBUS_ERROR_INVALID_ARGS, - "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", - address[0], - address[1], - address[2], - call->method_name); + jack_dbus_construct_method_return_empty(call); } #undef controller_ptr diff --git a/dbus/controller_iface_control.c b/dbus/controller_iface_control.c index cd0187da..05d8bf77 100644 --- a/dbus/controller_iface_control.c +++ b/dbus/controller_iface_control.c @@ -30,6 +30,7 @@ #include "jackdbus.h" #include "controller_internal.h" +#include "xml.h" #define JACK_DBUS_IFACE_NAME "org.jackaudio.JackControl" @@ -121,14 +122,19 @@ jack_control_run_method( } else if (strcmp (call->method_name, "SwitchMaster") == 0) { - if (!jack_controller_switch_master(controller_ptr, call)) + if (!controller_ptr->started) { - /* the reply is set by the failed function */ - assert(call->reply != NULL); - return true; + goto not_started; + } + else + { + if (!jack_controller_switch_master(controller_ptr, call)) + { + /* the reply is set by the failed function */ + assert(call->reply != NULL); + return true; + } } - - jack_controller_control_send_signal_server_stopped(); } else if (strcmp (call->method_name, "GetLoad") == 0) { @@ -221,7 +227,7 @@ jack_control_run_method( */ goto exit; } - + if (!jack_controller_load_internal(controller_ptr, internal_name)) { jack_dbus_error( call, @@ -229,10 +235,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 @@ -240,18 +251,28 @@ 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 + { + jack_controller_pending_save(controller_ptr); } } - 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 @@ -259,12 +280,17 @@ 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 + { + jack_controller_pending_save(controller_ptr); } } else if (strcmp (call->method_name, "UnloadInternal") == 0) @@ -278,7 +304,7 @@ jack_control_run_method( */ goto exit; } - + if (!jack_controller_unload_internal(controller_ptr, internal_name)) { jack_dbus_error( call, @@ -293,8 +319,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 +327,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 +395,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 +418,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) diff --git a/dbus/controller_iface_session_manager.c b/dbus/controller_iface_session_manager.c new file mode 100644 index 00000000..1ea89593 --- /dev/null +++ b/dbus/controller_iface_session_manager.c @@ -0,0 +1,572 @@ +/* -*- Mode: C ; c-basic-offset: 4 -*- */ +/* + Copyright (C) 2011 Nedko Arnaudov + + 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. + + 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. + +*/ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "jackdbus.h" +#include "controller_internal.h" +#include "jack/session.h" +#include "common/JackError.h" + +#define JACK_DBUS_IFACE_NAME "org.jackaudio.SessionManager" + +static +void +jack_controller_control_send_signal_session_state_changed( + jack_session_event_type_t type, + const char * target) +{ + dbus_uint32_t u32; + + u32 = type; + if (target == NULL) + { + target = ""; + } + + jack_dbus_send_signal( + JACK_CONTROLLER_OBJECT_PATH, + JACK_DBUS_IFACE_NAME, + "StateChanged", + DBUS_TYPE_UINT32, + &u32, + DBUS_TYPE_STRING, + &target, + DBUS_TYPE_INVALID); +} + +static bool start_detached_thread(void * (* start_routine)(void *), void * arg) +{ + int ret; + static pthread_attr_t attr; + pthread_t tid; + + ret = pthread_attr_init(&attr); + if (ret != 0) + { + jack_error("pthread_attr_init() failed with %d", ret); + goto exit; + } + + ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ret != 0) + { + jack_error("pthread_attr_setdetachstate() failed with %d", ret); + goto destroy_attr; + } + + ret = pthread_create(&tid, &attr, start_routine, arg); + if (ret != 0) + { + jack_error("pthread_create() failed with %d", ret); + goto destroy_attr; + } + + jack_log("Detached thread %d created", (int)tid); + +destroy_attr: + pthread_attr_destroy(&attr); +exit: + return ret == 0; +} + +static void send_session_notify_reply(struct jack_session_pending_command * pending_cmd_ptr, jack_session_command_t * commands) +{ + struct jack_dbus_method_call call; + const jack_session_command_t * cmd_ptr; + DBusMessageIter top_iter, array_iter, struct_iter; + dbus_uint32_t u32; + + /* jack_dbus_error() wants call struct */ + call.message = pending_cmd_ptr->message; + call.connection = pending_cmd_ptr->connection; + + if (commands == NULL) + { + jack_dbus_error(&call, JACK_DBUS_ERROR_GENERIC, "jack_session_notify() failed"); + goto send_reply; + } + + jack_info("Session notify complete, commands follow:"); + + call.reply = dbus_message_new_method_return(pending_cmd_ptr->message); + if (call.reply == NULL) + { + goto oom; + } + + dbus_message_iter_init_append(call.reply, &top_iter); + + if (!dbus_message_iter_open_container(&top_iter, DBUS_TYPE_ARRAY, "(sssu)", &array_iter)) + { + goto unref; + } + + for (cmd_ptr = commands; cmd_ptr->uuid != NULL; cmd_ptr++) + { + if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) + { + goto close_array; + } + + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->uuid)) + { + goto close_struct; + } + + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->client_name)) + { + goto close_struct; + } + + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->command)) + { + goto close_struct; + } + + u32 = cmd_ptr->flags; + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &u32)) + { + goto close_struct; + } + + jack_info("uuid='%s', client='%s', command='%s', flags=0x%"PRIX32, cmd_ptr->uuid, cmd_ptr->client_name, cmd_ptr->command, u32); + + if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) + { + goto close_array; + } + } + + jack_info("End of session commands."); + + if (!dbus_message_iter_close_container(&top_iter, &array_iter)) + { + goto unref; + } + + goto send_reply; + +close_struct: + dbus_message_iter_close_container(&array_iter, &struct_iter); +close_array: + dbus_message_iter_close_container(&top_iter, &array_iter); +unref: + dbus_message_unref(call.reply); + goto oom; + +send_reply: + if (call.reply != NULL) + { + if (!dbus_connection_send(pending_cmd_ptr->connection, call.reply, NULL)) + { + jack_error("Ran out of memory trying to queue method return"); + } + + dbus_connection_flush(pending_cmd_ptr->connection); + dbus_message_unref(call.reply); + } + else + { +oom: + jack_error("Ran out of memory trying to construct method return"); + } +} + +#define controller_ptr ((struct jack_controller *)context) +void * jack_controller_process_session_command_thread(void * context) +{ + struct jack_session_pending_command * pending_cmd_ptr; + jack_session_command_t * commands; + + jack_log("jack_controller_process_session_command_thread enter"); + + pthread_mutex_lock(&controller_ptr->lock); +loop: + /* get next command */ + assert(!list_empty(&controller_ptr->session_pending_commands)); + pending_cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings); + pthread_mutex_unlock(&controller_ptr->lock); + + jack_info("Session notify initiated. target='%s', type=%d, path='%s'", pending_cmd_ptr->target, (int)pending_cmd_ptr->type, pending_cmd_ptr->path); + + jack_controller_control_send_signal_session_state_changed(pending_cmd_ptr->type, pending_cmd_ptr->target); + + commands = jack_session_notify(controller_ptr->client, pending_cmd_ptr->target, pending_cmd_ptr->type, pending_cmd_ptr->path); + send_session_notify_reply(pending_cmd_ptr, commands); + if (commands != NULL) + { + jack_session_commands_free(commands); + } + + pthread_mutex_lock(&controller_ptr->lock); + + /* keep state consistent by sending signal after to lock */ + /* otherwise the main thread may receive not-to-be-queued request and fail */ + jack_controller_control_send_signal_session_state_changed(0, NULL); + + /* remove the head of the list (queue) */ + assert(!list_empty(&controller_ptr->session_pending_commands)); + assert(pending_cmd_ptr == list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings)); + list_del(&pending_cmd_ptr->siblings); + + /* command cleanup */ + dbus_message_unref(pending_cmd_ptr->message); + dbus_connection_ref(pending_cmd_ptr->connection); + free(pending_cmd_ptr); + + /* If there are more commands, process them. Otherwise - exit the thread */ + if (!list_empty(&controller_ptr->session_pending_commands)) + { + goto loop; + } + + pthread_mutex_unlock(&controller_ptr->lock); + + jack_log("jack_controller_process_session_command_thread exit"); + return NULL; +} + +#undef controller_ptr +#define controller_ptr ((struct jack_controller *)call->context) + +static +void +jack_controller_dbus_session_notify( + struct jack_dbus_method_call * call) +{ + dbus_bool_t queue; + const char * target; + dbus_uint32_t u32; + const char * path; + jack_session_event_type_t type; + struct jack_session_pending_command * cmd_ptr; + + if (!controller_ptr->started) + { + jack_dbus_only_error(call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute method '%s' with stopped JACK server", call->method_name); + return; + } + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_BOOLEAN, + &queue, + DBUS_TYPE_STRING, + &target, + DBUS_TYPE_UINT32, + &u32, + DBUS_TYPE_STRING, + &path, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + if (*target == 0) + { + target = NULL; + } + + type = (jack_session_event_type_t)u32; + + if (type != JackSessionSave && + type != JackSessionSaveAndQuit && + type != JackSessionSaveTemplate) + { + jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid session event type %" PRIu32, u32); + return; + } + + pthread_mutex_lock(&controller_ptr->lock); + if (list_empty(&controller_ptr->session_pending_commands)) + { + if (!start_detached_thread(jack_controller_process_session_command_thread, controller_ptr)) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Cannot start thread to process the command"); + goto unlock; + } + + jack_log("Session notify thread started"); + } + else if (!queue) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Busy"); + goto unlock; + } + + cmd_ptr = malloc(sizeof(struct jack_session_pending_command)); + if (cmd_ptr == NULL) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "malloc() failed for jack_session_pending_command struct"); + goto unlock; + } + + cmd_ptr->message = dbus_message_ref(call->message); + call->message = NULL; /* mark that reply will be sent asynchronously */ + cmd_ptr->connection = dbus_connection_ref(call->connection); + + /* it is safe to use the retrived pointers because we already made an additional message reference */ + cmd_ptr->type = type; + cmd_ptr->target = target; + cmd_ptr->path = path; + + list_add_tail(&cmd_ptr->siblings, &controller_ptr->session_pending_commands); + + jack_log("Session notify scheduled. target='%s', type=%"PRIu32", path='%s'", target, u32, path); + +unlock: + pthread_mutex_unlock(&controller_ptr->lock); +} + +static +void +jack_controller_dbus_get_uuid_for_client_name( + struct jack_dbus_method_call * call) +{ + const char * client_name; + char * client_uuid; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_name, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + client_uuid = jack_get_uuid_for_client_name(controller_ptr->client, client_name); + if (client_uuid == NULL) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_uuid_for_client_name(\"%s\") failed", client_name); + return; + } + + jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_uuid); + free(client_uuid); +} + +static +void +jack_controller_dbus_get_client_name_by_uuid( + struct jack_dbus_method_call * call) +{ + const char * client_uuid; + char * client_name; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_uuid, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + client_name = jack_get_client_name_by_uuid(controller_ptr->client, client_uuid); + if (client_name == NULL) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_client_name_by_uuid(\"%s\") failed", client_uuid); + return; + } + + jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_name); + free(client_name); +} + +static +void +jack_controller_dbus_reserve_client_name( + struct jack_dbus_method_call * call) +{ + int ret; + const char * client_name; + const char * client_uuid; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_name, + DBUS_TYPE_STRING, + &client_uuid, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + ret = jack_reserve_client_name(controller_ptr->client, client_name, client_uuid); + if (ret < 0) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_reserve_client_name(name=\"%s\", uuid=\"%s\") failed (%d)", client_name, client_uuid, ret); + return; + } + + jack_dbus_construct_method_return_empty(call); +} + +static +void +jack_controller_dbus_has_session_callback( + struct jack_dbus_method_call * call) +{ + int ret; + const char * client_name; + message_arg_t retval; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_name, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + ret = jack_client_has_session_callback(controller_ptr->client, client_name); + if (ret < 0) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_client_has_session_callback(\"%s\") failed (%d)", client_name, ret); + return; + } + + retval.boolean = ret; + jack_dbus_construct_method_return_single(call, DBUS_TYPE_BOOLEAN, retval); +} + +static +void +jack_controller_dbus_get_session_state( + struct jack_dbus_method_call * call) +{ + DBusMessageIter iter; + struct jack_session_pending_command * cmd_ptr; + const char * target; + dbus_uint32_t type; + bool append_failed; + + call->reply = dbus_message_new_method_return(call->message); + if (call->reply == NULL) + { + goto oom; + } + + dbus_message_iter_init_append(call->reply, &iter); + + pthread_mutex_lock(&controller_ptr->lock); + + if (list_empty(&controller_ptr->session_pending_commands)) + { + type = 0; + target = ""; + } + else + { + cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings); + type = (dbus_uint32_t)cmd_ptr->type; + target = cmd_ptr->target; + } + + append_failed = + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &type) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &target); + + pthread_mutex_unlock(&controller_ptr->lock); + + if (!append_failed) + { + return; + } + + dbus_message_unref(call->reply); + call->reply = NULL; +oom: + jack_error("Ran out of memory trying to construct method return"); +} + +#undef controller_ptr + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(Notify) + JACK_DBUS_METHOD_ARGUMENT("queue", DBUS_TYPE_BOOLEAN_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("path", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("result", "a(sssu)", true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetUuidForClientName) + JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetClientNameByUuid) + JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ReserveClientName) + JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(HasSessionCallback) + JACK_DBUS_METHOD_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("has_session_callback", DBUS_TYPE_BOOLEAN_AS_STRING, true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetState) + JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, true) + JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(StateChanged) + JACK_DBUS_SIGNAL_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING) + JACK_DBUS_SIGNAL_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING) +JACK_DBUS_SIGNAL_ARGUMENTS_END + +JACK_DBUS_METHODS_BEGIN + JACK_DBUS_METHOD_DESCRIBE(Notify, jack_controller_dbus_session_notify) + JACK_DBUS_METHOD_DESCRIBE(GetUuidForClientName, jack_controller_dbus_get_uuid_for_client_name) + JACK_DBUS_METHOD_DESCRIBE(GetClientNameByUuid, jack_controller_dbus_get_client_name_by_uuid) + JACK_DBUS_METHOD_DESCRIBE(ReserveClientName, jack_controller_dbus_reserve_client_name) + JACK_DBUS_METHOD_DESCRIBE(HasSessionCallback, jack_controller_dbus_has_session_callback) + JACK_DBUS_METHOD_DESCRIBE(GetState, jack_controller_dbus_get_session_state) +JACK_DBUS_METHODS_END + +JACK_DBUS_SIGNALS_BEGIN + JACK_DBUS_SIGNAL_DESCRIBE(StateChanged) +JACK_DBUS_SIGNALS_END + +JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_session_manager, JACK_DBUS_IFACE_NAME) + JACK_DBUS_IFACE_EXPOSE_METHODS + JACK_DBUS_IFACE_EXPOSE_SIGNALS +JACK_DBUS_IFACE_END diff --git a/dbus/controller_internal.h b/dbus/controller_internal.h index a7711a8d..9e9fb52f 100644 --- a/dbus/controller_internal.h +++ b/dbus/controller_internal.h @@ -1,6 +1,6 @@ /* -*- Mode: C ; c-basic-offset: 4 -*- */ /* - Copyright (C) 2007,2008 Nedko Arnaudov + Copyright (C) 2007,2008,2011 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify @@ -25,11 +25,33 @@ #include "jslist.h" #include "jack/control.h" #include "jack/jack.h" +#include "jack/session.h" #include "jackdbus.h" +#include "list.h" +#include "params.h" + +struct jack_controller_slave_driver +{ + struct list_head siblings; + char * name; + jackctl_driver_t * handle; + bool loaded; +}; + +struct jack_session_pending_command +{ + struct list_head siblings; + DBusConnection * connection; + DBusMessage * message; + jack_session_event_type_t type; + const char * target; + const char * path; +}; struct jack_controller { jackctl_server_t *server; + jack_params_handle params; void *patchbay_context; @@ -37,16 +59,17 @@ struct jack_controller jack_client_t *client; unsigned int xruns; - const char **driver_names; - unsigned int drivers_count; - - const char **internal_names; - unsigned int internals_count; - - jackctl_driver_t *driver; - bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */ + struct list_head slave_drivers; + bool slave_drivers_set; + struct jack_parameter slave_drivers_vparam; + union jackctl_parameter_value slave_drivers_vparam_value; struct jack_dbus_object_descriptor dbus_descriptor; + + pthread_mutex_t lock; + struct list_head session_pending_commands; + + long pending_save; /* uptime seconds */ }; #define DEFAULT_DRIVER "dummy" @@ -56,20 +79,9 @@ struct jack_controller "You probably don't want to edit this because\n" \ "it will be overwritten next time jackdbus saves.\n" -jackctl_driver_t * -jack_controller_find_driver( - jackctl_server_t *server, - const char *driver_name); - -jackctl_internal_t * -jack_controller_find_internal( - jackctl_server_t *server, - const char *internal_name); - -jackctl_parameter_t * -jack_controller_find_parameter( - const JSList *parameters_list, - const char *parameter_name); +void +jack_controller_pending_save( + struct jack_controller *controller_ptr); bool jack_controller_start_server( @@ -87,12 +99,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); @@ -112,47 +124,15 @@ jack_controller_unload_internal( const char * internal_name); void -jack_controller_settings_set_driver_option( - jackctl_driver_t *driver, - const char *option_name, - const char *option_value); - -void -jack_controller_settings_set_internal_option( - jackctl_internal_t *internal, - const char *option_name, - const char *option_value); - -void -jack_controller_settings_set_engine_option( - struct jack_controller *controller_ptr, - const char *option_name, - const char *option_value); - -bool -jack_controller_settings_save_engine_options( - void *context, +jack_controller_deserialize_parameter_value( struct jack_controller *controller_ptr, - void *dbus_call_context_ptr); + const char * const * address, + const char * value); -bool -jack_controller_settings_write_option( - void *context, - const char *name, - const char *content, - void *dbus_call_context_ptr); - -bool -jack_controller_settings_save_driver_options( - void *context, - jackctl_driver_t *driver, - void *dbus_call_context_ptr); - -bool -jack_controller_settings_save_internal_options( - void *context, - jackctl_internal_t *internal, - void *dbus_call_context_ptr); +void +jack_controller_serialize_parameter_value( + const struct jack_parameter * param_ptr, + char * value_buffer); bool jack_controller_patchbay_init( @@ -223,6 +203,7 @@ extern struct jack_dbus_interface_descriptor g_jack_controller_iface_introspecta extern struct jack_dbus_interface_descriptor g_jack_controller_iface_control; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_configure; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_patchbay; +extern struct jack_dbus_interface_descriptor g_jack_controller_iface_session_manager; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_transport; #endif /* #ifndef CONTROLLER_INTERNAL_H__04D54D51_3D79_49A2_A1DA_F8587E9E7F42__INCLUDED */ diff --git a/dbus/jackdbus.c b/dbus/jackdbus.c index cb9fd14d..a03c8e3c 100644 --- a/dbus/jackdbus.c +++ b/dbus/jackdbus.c @@ -105,6 +105,12 @@ void jack_dbus_send_method_return( struct jack_dbus_method_call * call) { + if (call->message == NULL) + { + /* async call */ + return; + } + if (call->reply) { retry_send: @@ -942,7 +948,10 @@ main (int argc, char **argv) jack_info("Listening for D-Bus messages"); g_exit_command = FALSE; - while (!g_exit_command && dbus_connection_read_write_dispatch (g_connection, 200)); + while (!g_exit_command && dbus_connection_read_write_dispatch (g_connection, 200)) + { + jack_controller_run(controller_ptr); + } jack_controller_destroy(controller_ptr); diff --git a/dbus/jackdbus.h b/dbus/jackdbus.h index d920cf06..c0ab8861 100644 --- a/dbus/jackdbus.h +++ b/dbus/jackdbus.h @@ -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" diff --git a/dbus/params.c b/dbus/params.c new file mode 100644 index 00000000..5acbe19b --- /dev/null +++ b/dbus/params.c @@ -0,0 +1,726 @@ +/* -*- Mode: C ; c-basic-offset: 4 -*- */ +/* + Copyright (C) 2011 Nedko Arnaudov + + 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. + + 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. + +*/ + +/* + * Parameter addresses: + * + * "engine" + * "engine", "driver" + * "engine", "realtime" + * "engine", ...more engine parameters + * + * "driver", "device" + * "driver", ...more driver parameters + * + * "drivers", "alsa", "device" + * "drivers", "alsa", ...more alsa driver parameters + * + * "drivers", ...more drivers + * + * "internals", "netmanager", "multicast_ip" + * "internals", "netmanager", ...more netmanager parameters + * + * "internals", ...more internals + * + */ + +#include +#include +#include +#include + +#include "params.h" +#include "controller_internal.h" + +#define PTNODE_ENGINE "engine" +#define PTNODE_DRIVER "driver" +#define PTNODE_DRIVERS "drivers" +#define PTNODE_INTERNALS "internals" + +struct jack_parameter_container +{ + struct list_head siblings; + char * name; + struct jack_parameter_container * symlink; + bool leaf; + struct list_head children; + void * obj; +}; + +struct jack_params +{ + jackctl_server_t * server; + struct jack_parameter_container root; + struct list_head * drivers_ptr; + uint32_t drivers_count; + struct jack_parameter_container * driver_ptr; + bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */ +}; + +static bool controlapi_parameter_is_set(void * obj) +{ + return jackctl_parameter_is_set((jackctl_parameter_t *)obj); +} + +static bool controlapi_parameter_reset(void * obj) +{ + return jackctl_parameter_reset((jackctl_parameter_t *)obj); +} + +union jackctl_parameter_value controlapi_parameter_get_value(void * obj) +{ + return jackctl_parameter_get_value((jackctl_parameter_t *)obj); +} + +bool controlapi_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) +{ + return jackctl_parameter_set_value((jackctl_parameter_t *)obj, value_ptr); +} + +union jackctl_parameter_value controlapi_parameter_get_default_value(void * obj) +{ + return jackctl_parameter_get_default_value((jackctl_parameter_t *)obj); +} + +static struct jack_parameter_container * create_container(struct list_head * parent_list_ptr, const char * name) +{ + struct jack_parameter_container * container_ptr; + + container_ptr = malloc(sizeof(struct jack_parameter_container)); + if (container_ptr == NULL) + { + jack_error("Ran out of memory trying to allocate struct jack_parameter_container"); + goto fail; + } + + container_ptr->name = strdup(name); + if (container_ptr->name == NULL) + { + jack_error("Ran out of memory trying to strdup parameter container name"); + goto free; + } + + container_ptr->leaf = false; + container_ptr->symlink = NULL; + container_ptr->obj = NULL; + INIT_LIST_HEAD(&container_ptr->children); + list_add_tail(&container_ptr->siblings, parent_list_ptr); + + return container_ptr; + +free: + free(container_ptr); +fail: + return NULL; +} + +static bool add_controlapi_param(struct list_head * parent_list_ptr, jackctl_parameter_t * param) +{ + struct jack_parameter * param_ptr; + uint32_t i; + + param_ptr = malloc(sizeof(struct jack_parameter)); + if (param_ptr == NULL) + { + jack_error("Ran out of memory trying to allocate struct jack_parameter"); + goto fail; + } + + param_ptr->ext = false; + param_ptr->obj = param; + param_ptr->vtable.is_set = controlapi_parameter_is_set; + param_ptr->vtable.reset = controlapi_parameter_reset; + param_ptr->vtable.get_value = controlapi_parameter_get_value; + param_ptr->vtable.set_value = controlapi_parameter_set_value; + param_ptr->vtable.get_default_value = controlapi_parameter_get_default_value; + + param_ptr->type = jackctl_parameter_get_type(param); + param_ptr->name = jackctl_parameter_get_name(param); + param_ptr->short_decr = jackctl_parameter_get_short_description(param); + param_ptr->long_descr = jackctl_parameter_get_long_description(param); + + if (jackctl_parameter_has_range_constraint(param)) + { + param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; + param_ptr->constraint_range = true; + jackctl_parameter_get_range_constraint(param, ¶m_ptr->constraint.range.min, ¶m_ptr->constraint.range.max); + } + else if (jackctl_parameter_has_enum_constraint(param)) + { + param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; + param_ptr->constraint_range = false; + param_ptr->constraint.enumeration.count = jackctl_parameter_get_enum_constraints_count(param); + param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * param_ptr->constraint.enumeration.count); + if (param_ptr->constraint.enumeration.possible_values_array == NULL) + { + goto free; + } + + for (i = 0; i < param_ptr->constraint.enumeration.count; i++) + { + param_ptr->constraint.enumeration.possible_values_array[i].value = jackctl_parameter_get_enum_constraint_value(param, i); + param_ptr->constraint.enumeration.possible_values_array[i].short_desc = jackctl_parameter_get_enum_constraint_description(param, i); + } + } + else + { + param_ptr->constraint_flags = 0; + goto add; + } + + if (jackctl_parameter_constraint_is_strict(param)) + { + param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_STRICT; + } + + if (jackctl_parameter_constraint_is_fake_value(param)) + { + param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_FAKE_VALUE; + } + +add: + list_add_tail(¶m_ptr->siblings, parent_list_ptr); + return true; + +free: + free(param_ptr); +fail: + return false; +} + +static void free_params(struct list_head * parent_list_ptr) +{ + struct jack_parameter * param_ptr; + + while (!list_empty(parent_list_ptr)) + { + param_ptr = list_entry(parent_list_ptr->next, struct jack_parameter, siblings); + list_del(¶m_ptr->siblings); + + if (param_ptr->ext) + { + continue; + } + + if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0 && + !param_ptr->constraint_range && + param_ptr->constraint.enumeration.possible_values_array != NULL) + { + free(param_ptr->constraint.enumeration.possible_values_array); + } + + free(param_ptr); + } +} + +static void free_containers(struct list_head * parent_list_ptr) +{ + struct jack_parameter_container * container_ptr; + + while (!list_empty(parent_list_ptr)) + { + container_ptr = list_entry(parent_list_ptr->next, struct jack_parameter_container, siblings); + list_del(&container_ptr->siblings); + + if (container_ptr->leaf) + { + free_params(&container_ptr->children); + } + else + { + free_containers(&container_ptr->children); + } + + free(container_ptr->name); + free(container_ptr); + } +} + +static struct jack_parameter_container * find_container(struct jack_parameter_container * parent_ptr, const char * const * address, int max_depth) +{ + struct list_head * node_ptr; + struct jack_parameter_container * container_ptr; + + if (max_depth == 0 || *address == NULL) + { + return parent_ptr; + } + + if (parent_ptr->leaf) + { + return NULL; + } + + if (max_depth > 0) + { + max_depth--; + } + + list_for_each(node_ptr, &parent_ptr->children) + { + container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); + if (strcmp(container_ptr->name, *address) == 0) + { + if (container_ptr->symlink != NULL) + { + container_ptr = container_ptr->symlink; + } + + return find_container(container_ptr, address + 1, max_depth); + } + } + + return NULL; +} + +static bool init_leaf(struct list_head * parent_list_ptr, const char * name, const JSList * params_list, void * obj) +{ + struct jack_parameter_container * container_ptr; + + container_ptr = create_container(parent_list_ptr, name); + if (container_ptr == NULL) + { + return false; + } + + container_ptr->leaf = true; + container_ptr->obj = obj; + + while (params_list) + { + if (!add_controlapi_param(&container_ptr->children, params_list->data)) + { + return false; + } + + params_list = jack_slist_next(params_list); + } + + return true; +} + +static bool init_engine(struct jack_params * params_ptr) +{ + return init_leaf(¶ms_ptr->root.children, PTNODE_ENGINE, jackctl_server_get_parameters(params_ptr->server), NULL); +} + +static bool init_drivers(struct jack_params * params_ptr) +{ + const JSList * list; + struct jack_parameter_container * container_ptr; + + container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVERS); + if (container_ptr == NULL) + { + return false; + } + + params_ptr->drivers_ptr = &container_ptr->children; + params_ptr->drivers_count = 0; + + list = jackctl_server_get_drivers_list(params_ptr->server); + while (list) + { + if (!init_leaf(&container_ptr->children, jackctl_driver_get_name(list->data), jackctl_driver_get_parameters(list->data), list->data)) + { + return false; + } + + params_ptr->drivers_count++; + + list = jack_slist_next(list); + } + + return true; +} + +static bool init_internals(struct jack_params * params_ptr) +{ + const JSList * list; + struct jack_parameter_container * container_ptr; + + container_ptr = create_container(¶ms_ptr->root.children, PTNODE_INTERNALS); + if (container_ptr == NULL) + { + return false; + } + + list = jackctl_server_get_internals_list(params_ptr->server); + while (list) + { + if (!init_leaf(&container_ptr->children, jackctl_internal_get_name(list->data), jackctl_internal_get_parameters(list->data), NULL)) + { + return false; + } + + list = jack_slist_next(list); + } + + return true; +} + +static bool init_driver(struct jack_params * params_ptr) +{ + struct jack_parameter_container * container_ptr; + + container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVER); + if (container_ptr == NULL) + { + return false; + } + + params_ptr->driver_ptr = container_ptr; + + return true; +} + +#define params_ptr ((struct jack_params *)obj) + +static bool engine_driver_parameter_is_set(void * obj) +{ + return params_ptr->driver_set; +} + +static bool engine_driver_parameter_reset(void * obj) +{ + if (!jack_params_set_driver(obj, DEFAULT_DRIVER)) + { + return false; + } + + params_ptr->driver_set = false; + + return true; +} + +union jackctl_parameter_value engine_driver_parameter_get_value(void * obj) +{ + union jackctl_parameter_value value; + + strcpy(value.str, params_ptr->driver_ptr->symlink->name); + + return value; +} + +bool engine_driver_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) +{ + return jack_params_set_driver(obj, value_ptr->str); +} + +union jackctl_parameter_value engine_driver_parameter_get_default_value(void * obj) +{ + union jackctl_parameter_value value; + + strcpy(value.str, DEFAULT_DRIVER); + + return value; +} + +#undef params_ptr + +static bool add_engine_driver_enum_constraint(void * context, const char * name) +{ + strcpy((*((struct jack_parameter_enum **)context))->value.str, name); + (*((struct jack_parameter_enum **)context))->short_desc = name; + (*((struct jack_parameter_enum **)context))++; + return true; +} + +static bool init_engine_driver_parameter(struct jack_params * params_ptr) +{ + struct jack_parameter * param_ptr; + const char * address[PARAM_ADDRESS_SIZE] = {PTNODE_ENGINE, NULL}; + struct jack_parameter_container * engine_ptr; + struct jack_parameter_enum * possible_value; + + engine_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); + if (engine_ptr == NULL) + { + return false; + } + + param_ptr = malloc(sizeof(struct jack_parameter)); + if (param_ptr == NULL) + { + jack_error("Ran out of memory trying to allocate struct jack_parameter"); + goto fail; + } + + param_ptr->ext = false; + param_ptr->obj = params_ptr; + param_ptr->vtable.is_set = engine_driver_parameter_is_set; + param_ptr->vtable.reset = engine_driver_parameter_reset; + param_ptr->vtable.get_value = engine_driver_parameter_get_value; + param_ptr->vtable.set_value = engine_driver_parameter_set_value; + param_ptr->vtable.get_default_value = engine_driver_parameter_get_default_value; + + param_ptr->type = JackParamString; + param_ptr->name = "driver"; + param_ptr->short_decr = "Driver to use"; + param_ptr->long_descr = ""; + + param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID | JACK_CONSTRAINT_FLAG_STRICT | JACK_CONSTRAINT_FLAG_FAKE_VALUE; + param_ptr->constraint_range = false; + param_ptr->constraint.enumeration.count = params_ptr->drivers_count; + param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * params_ptr->drivers_count); + if (param_ptr->constraint.enumeration.possible_values_array == NULL) + { + goto free; + } + + address[0] = PTNODE_DRIVERS; + possible_value = param_ptr->constraint.enumeration.possible_values_array; + jack_params_iterate_container((jack_params_handle)params_ptr, address, add_engine_driver_enum_constraint, &possible_value); + + list_add(¶m_ptr->siblings, &engine_ptr->children); + return true; + +free: + free(param_ptr); +fail: + return false; +} + +jack_params_handle jack_params_create(jackctl_server_t * server) +{ + struct jack_params * params_ptr; + + params_ptr = malloc(sizeof(struct jack_params)); + if (params_ptr == NULL) + { + jack_error("Ran out of memory trying to allocate struct jack_params"); + return NULL; + } + + params_ptr->server = server; + INIT_LIST_HEAD(¶ms_ptr->root.children); + params_ptr->root.leaf = false; + params_ptr->root.name = NULL; + + if (!init_engine(params_ptr) || + !init_drivers(params_ptr) || + !init_driver(params_ptr) || + !init_engine_driver_parameter(params_ptr) || + !jack_params_set_driver((jack_params_handle)params_ptr, DEFAULT_DRIVER) || + !init_internals(params_ptr)) + { + jack_params_destroy((jack_params_handle)params_ptr); + return NULL; + } + + params_ptr->driver_set = false; + + assert(strcmp(params_ptr->driver_ptr->symlink->name, DEFAULT_DRIVER) == 0); + + return (jack_params_handle)params_ptr; +} + +#define params_ptr ((struct jack_params *)params) + +void jack_params_destroy(jack_params_handle params) +{ + free_containers(¶ms_ptr->root.children); + free(params); +} + +bool jack_params_set_driver(jack_params_handle params, const char * name) +{ + struct list_head * node_ptr; + struct jack_parameter_container * container_ptr; + + list_for_each(node_ptr, params_ptr->drivers_ptr) + { + container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); + if (strcmp(container_ptr->name, name) == 0) + { + params_ptr->driver_ptr->symlink = container_ptr; + params_ptr->driver_set = true; + return true; + } + } + + return false; +} + +jackctl_driver_t * jack_params_get_driver(jack_params_handle params) +{ + return params_ptr->driver_ptr->symlink->obj; +} + +bool jack_params_check_address(jack_params_handle params, const char * const * address, bool want_leaf) +{ + struct jack_parameter_container * container_ptr; + + container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); + if (container_ptr == NULL) + { + return false; + } + + if (want_leaf && !container_ptr->leaf) + { + return false; + } + + return true; +} + +bool jack_params_is_leaf_container(jack_params_handle params, const char * const * address) +{ + struct jack_parameter_container * container_ptr; + + container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); + if (container_ptr == NULL) + { + assert(false); + return false; + } + + return container_ptr->leaf; +} + +bool +jack_params_iterate_container( + jack_params_handle params, + const char * const * address, + bool (* callback)(void * context, const char * name), + void * context) +{ + struct jack_parameter_container * container_ptr; + struct list_head * node_ptr; + const char * name; + + container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); + if (container_ptr == NULL) + { + assert(false); + return true; + } + + list_for_each(node_ptr, &container_ptr->children) + { + if (container_ptr->leaf) + { + name = list_entry(node_ptr, struct jack_parameter, siblings)->name; + } + else + { + name = list_entry(node_ptr, struct jack_parameter_container, siblings)->name; + } + + if (!callback(context, name)) + { + return false; + } + } + + return true; +} + +bool +jack_params_iterate_params( + jack_params_handle params, + const char * const * address, + bool (* callback)(void * context, const struct jack_parameter * param_ptr), + void * context) +{ + struct jack_parameter_container * container_ptr; + struct list_head * node_ptr; + struct jack_parameter * param_ptr; + + container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); + if (container_ptr == NULL || !container_ptr->leaf) + { + assert(false); + return true; + } + + list_for_each(node_ptr, &container_ptr->children) + { + param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); + if (!callback(context, param_ptr)) + { + return false; + } + } + + return true; +} + +const struct jack_parameter * jack_params_get_parameter(jack_params_handle params, const char * const * address) +{ + int depth; + struct jack_parameter_container * container_ptr; + struct list_head * node_ptr; + struct jack_parameter * param_ptr; + + for (depth = 0; depth < PARAM_ADDRESS_SIZE; depth++) + { + if (address[depth] == NULL) + { + break; + } + } + + depth--; + + container_ptr = find_container(¶ms_ptr->root, address, depth); + if (container_ptr == NULL || !container_ptr->leaf) + { + return NULL; + } + + list_for_each(node_ptr, &container_ptr->children) + { + param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); + if (strcmp(param_ptr->name, address[depth]) == 0) + { + return param_ptr; + } + } + + return NULL; +} + +void jack_params_add_parameter(jack_params_handle params, const char * const * address, bool end, struct jack_parameter * param_ptr) +{ + struct jack_parameter_container * container_ptr; + + container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); + if (container_ptr == NULL || !container_ptr->leaf) + { + assert(false); + return; + } + + param_ptr->ext = true; + + if (end) + { + list_add_tail(¶m_ptr->siblings, &container_ptr->children); + } + else + { + list_add(¶m_ptr->siblings, &container_ptr->children); + } + + return; +} + +#undef params_ptr diff --git a/dbus/params.h b/dbus/params.h new file mode 100644 index 00000000..ad9b727b --- /dev/null +++ b/dbus/params.h @@ -0,0 +1,111 @@ +/* -*- Mode: C ; c-basic-offset: 4 -*- */ +/* + Copyright (C) 2011 Nedko Arnaudov + + 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. + + 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 PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED +#define PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED + +#include "jack/control.h" +#include "list.h" + +#define PARAM_ADDRESS_SIZE 3 + +#define PTNODE_ENGINE "engine" +#define PTNODE_DRIVER "driver" +#define PTNODE_DRIVERS "drivers" +#define PTNODE_INTERNALS "internals" + +struct jack_parameter_vtable +{ + bool (* is_set)(void * obj); + bool (* reset)(void * obj); + union jackctl_parameter_value (* get_value)(void * obj); + bool (* set_value)(void * obj, const union jackctl_parameter_value * value_ptr); + union jackctl_parameter_value (* get_default_value)(void * obj); +}; + +#define JACK_CONSTRAINT_FLAG_VALID ((uint32_t)1) /**< if not set, there is no constraint */ +#define JACK_CONSTRAINT_FLAG_STRICT ((uint32_t)2) /**< if set, constraint is strict, i.e. supplying non-matching value will not work */ +#define JACK_CONSTRAINT_FLAG_FAKE_VALUE ((uint32_t)4) /**< if set, values have no user meaningful meaning */ + +struct jack_parameter_enum +{ + union jackctl_parameter_value value; + const char * short_desc; +}; + +struct jack_parameter +{ + void * obj; + struct jack_parameter_vtable vtable; + struct list_head siblings; + bool ext; + jackctl_param_type_t type; + const char * name; + const char * short_decr; + const char * long_descr; + + uint32_t constraint_flags; /**< JACK_CONSTRAINT_FLAG_XXX */ + bool constraint_range; /**< if true, constraint is a range (min-max), otherwise it is an enumeration */ + + union + { + struct + { + union jackctl_parameter_value min; + union jackctl_parameter_value max; + } range; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is set */ + + struct + { + uint32_t count; + struct jack_parameter_enum * possible_values_array; + } enumeration; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is not set */ + } constraint; +}; + +typedef struct _jack_params { int unused; } * jack_params_handle; + +jack_params_handle jack_params_create(jackctl_server_t * server); +void jack_params_destroy(jack_params_handle params); + +bool jack_params_set_driver(jack_params_handle params, const char * name); +jackctl_driver_t * jack_params_get_driver(jack_params_handle params); + +bool jack_params_check_address(jack_params_handle params, const char * const * address, bool want_leaf); +bool jack_params_is_leaf_container(jack_params_handle params, const char * const * address); + +bool +jack_params_iterate_container( + jack_params_handle params, + const char * const * address, + bool (* callback)(void * context, const char * name), + void * context); + +bool +jack_params_iterate_params( + jack_params_handle params, + const char * const * address, + bool (* callback)(void * context, const struct jack_parameter * param_ptr), + void * context); + +const struct jack_parameter * jack_params_get_parameter(jack_params_handle params, const char * const * address); + +void jack_params_add_parameter(jack_params_handle params, const char * const * address, bool end, struct jack_parameter * param_ptr); + +#endif /* #ifndef PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED */ diff --git a/dbus/reserve.c b/dbus/reserve.c index bc698a6a..88e91b2b 100644 --- a/dbus/reserve.c +++ b/dbus/reserve.c @@ -369,8 +369,7 @@ int rd_acquire( DBusError _error; DBusMessage *m = NULL, *reply = NULL; dbus_bool_t good; - - vtable.message_function = object_handler; + vtable.message_function = object_handler; if (!error) error = &_error; @@ -430,7 +429,7 @@ int rd_acquire( goto fail; } - if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER || k == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER) goto success; if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) { diff --git a/dbus/wscript b/dbus/wscript index a4066b57..703c52c7 100644 --- a/dbus/wscript +++ b/dbus/wscript @@ -49,17 +49,19 @@ def build(bld): sysdeps_dbus_include = ['../macosx', '../posix'] obj.includes = sysdeps_dbus_include + ['.', '../', '../common', '../common/jack'] + obj.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] obj.source = [ 'jackdbus.c', 'controller.c', + 'params.c', 'controller_iface_configure.c', 'controller_iface_control.c', 'controller_iface_introspectable.c', 'controller_iface_patchbay.c', + 'controller_iface_session_manager.c', 'controller_iface_transport.c', 'xml.c', 'xml_expat.c', - #'xml_libxml.c', #'xml_nop.c', 'xml_write_raw.c', 'sigsegv.c', diff --git a/dbus/xml.c b/dbus/xml.c index b506c5d5..c7e11377 100644 --- a/dbus/xml.c +++ b/dbus/xml.c @@ -1,6 +1,6 @@ /* -*- Mode: C ; c-basic-offset: 4 -*- */ /* - Copyright (C) 2007,2008 Nedko Arnaudov + Copyright (C) 2007,2008,2011 Nedko Arnaudov 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 @@ -25,358 +25,116 @@ #include #include #include +#include #include #include "controller_internal.h" void -jack_controller_settings_set_bool_option( - const char *value_str, - int *value_ptr) -{ - if (strcmp(value_str, "true") == 0) - { - *value_ptr = true; - } - else if (strcmp(value_str, "false") == 0) - { - *value_ptr = false; - } - else - { - jack_error("ignoring unknown bool value \"%s\"", value_str); - } -} - -void -jack_controller_settings_set_sint_option( - const char *value_str, - int *value_ptr) -{ - *value_ptr = atoi(value_str); -} - -void -jack_controller_settings_set_uint_option( - const char *value_str, - unsigned int *value_ptr) -{ - *value_ptr = strtoul(value_str, NULL, 10); -} - -void -jack_controller_settings_set_char_option( - const char *value_str, - char *value_ptr) -{ - if (value_str[0] == 0 || value_str[1] != 0) - { - jack_error("invalid char option value \"%s\"", value_str); - return; - } - - *value_ptr = *value_str; -} - -void -jack_controller_settings_set_string_option( - const char *value_str, - char *value_ptr, - size_t max_size) -{ - size_t size; - - size = strlen(value_str); - - if (size >= max_size) - { - jack_error("string option value \"%s\" is too long, max is %u chars (including terminating zero)", value_str, (unsigned int)max_size); - return; - } - - strcpy(value_ptr, value_str); -} - -void -jack_controller_settings_set_driver_option( - jackctl_driver_t *driver, - const char *option_name, - const char *option_value) +jack_controller_deserialize_parameter_value( + struct jack_controller *controller_ptr, + const char * const * address, + const char * option_value) { - jackctl_parameter_t *parameter; - jackctl_param_type_t type; - int value_int = 0; - unsigned int value_uint = 0; + const struct jack_parameter * param_ptr; union jackctl_parameter_value value; + size_t size; - jack_info("setting driver option \"%s\" to value \"%s\"", option_name, option_value); - - parameter = jack_controller_find_parameter(jackctl_driver_get_parameters(driver), option_name); - if (parameter == NULL) + param_ptr = jack_params_get_parameter(controller_ptr->params, address); + if (param_ptr == NULL) { - jack_error( - "Unknown parameter \"%s\" of driver \"%s\"", - option_name, - jackctl_driver_get_name(driver)); - return; + jack_error("Unknown parameter"); + goto ignore; } - type = jackctl_parameter_get_type(parameter); + jack_info("setting parameter '%s':'%s':'%s' to value \"%s\"", address[0], address[1], address[2], option_value); - switch (type) + switch (param_ptr->type) { case JackParamInt: - jack_controller_settings_set_sint_option(option_value, &value_int); - value.i = value_int; + value.i = atoi(option_value); break; case JackParamUInt: - jack_controller_settings_set_uint_option(option_value, &value_uint); - value.ui = value_uint; + value.ui = strtoul(option_value, NULL, 10); break; case JackParamChar: - jack_controller_settings_set_char_option(option_value, &value.c); + if (option_value[0] == 0 || option_value[1] != 0) + { + jack_error("invalid char option value \"%s\"", option_value); + goto ignore; + } + value.c = *option_value; break; case JackParamString: - jack_controller_settings_set_string_option(option_value, value.str, sizeof(value.str)); + size = strlen(option_value); + if (size >= sizeof(value.str)) + { + jack_error("string option value \"%s\" is too long, max is %zu chars (including terminating zero)", option_value, sizeof(value.str)); + goto ignore; + } + + strcpy(value.str, option_value); break; case JackParamBool: - jack_controller_settings_set_bool_option(option_value, &value_int); - value.i = value_int; + if (strcmp(option_value, "true") == 0) + { + value.b = true; + } + else if (strcmp(option_value, "false") == 0) + { + value.b = false; + } + else + { + jack_error("ignoring unknown bool value \"%s\"", option_value); + goto ignore; + } break; default: - jack_error("Parameter \"%s\" of driver \"%s\" is of unknown type %d", - jackctl_parameter_get_name(parameter), - jackctl_driver_get_name(driver), - type); + jack_error("Unknown type %d", (int)param_ptr->type); + goto ignore; } - jackctl_parameter_set_value(parameter, &value); -} - -void -jack_controller_settings_set_internal_option( - jackctl_internal_t *internal, - const char *option_name, - const char *option_value) -{ - jackctl_parameter_t *parameter; - jackctl_param_type_t type; - int value_int = 0; - unsigned int value_uint = 0; - union jackctl_parameter_value value; - - jack_info("setting internal option \"%s\" to value \"%s\"", option_name, option_value); - - parameter = jack_controller_find_parameter(jackctl_internal_get_parameters(internal), option_name); - if (parameter == NULL) + if (param_ptr->vtable.set_value(param_ptr->obj, &value)) { - jack_error( - "Unknown parameter \"%s\" of internal \"%s\"", - option_name, - jackctl_internal_get_name(internal)); return; } - type = jackctl_parameter_get_type(parameter); - - switch (type) - { - case JackParamInt: - jack_controller_settings_set_sint_option(option_value, &value_int); - value.i = value_int; - break; - case JackParamUInt: - jack_controller_settings_set_uint_option(option_value, &value_uint); - value.ui = value_uint; - break; - case JackParamChar: - jack_controller_settings_set_char_option(option_value, &value.c); - break; - case JackParamString: - jack_controller_settings_set_string_option(option_value, value.str, sizeof(value.str)); - break; - case JackParamBool: - jack_controller_settings_set_bool_option(option_value, &value_int); - value.i = value_int; - break; - default: - jack_error("Parameter \"%s\" of internal \"%s\" is of unknown type %d", - jackctl_parameter_get_name(parameter), - jackctl_internal_get_name(internal), - type); - } + jack_error("Parameter set failed"); - jackctl_parameter_set_value(parameter, &value); +ignore: + jack_error("Ignoring restore attempt of parameter '%s':'%s':'%s'", address[0], address[1], address[2]); } void -jack_controller_settings_set_engine_option( - struct jack_controller *controller_ptr, - const char *option_name, - const char *option_value) +jack_controller_serialize_parameter_value( + const struct jack_parameter * param_ptr, + char * value_buffer) { - jackctl_parameter_t *parameter; - jackctl_param_type_t type; - int value_int = 0; - unsigned int value_uint = 0; union jackctl_parameter_value value; - jack_info("setting engine option \"%s\" to value \"%s\"", option_name, option_value); + value = param_ptr->vtable.get_value(param_ptr->obj); - if (strcmp(option_name, "driver") == 0) - { - if (!jack_controller_select_driver(controller_ptr, option_value)) - { - jack_error("unknown driver '%s'", option_value); - } - - return; - } - - parameter = jack_controller_find_parameter(jackctl_server_get_parameters(controller_ptr->server), option_name); - if (parameter == NULL) - { - jack_error( - "Unknown engine parameter \"%s\"", - option_name); - return; - } - - type = jackctl_parameter_get_type(parameter); - - switch (type) + switch (param_ptr->type) { case JackParamInt: - jack_controller_settings_set_sint_option(option_value, &value_int); - value.i = value_int; - break; + sprintf(value_buffer, "%d", (int)value.i); + return; case JackParamUInt: - jack_controller_settings_set_uint_option(option_value, &value_uint); - value.ui = value_uint; - break; + sprintf(value_buffer, "%u", (unsigned int)value.ui); + return; case JackParamChar: - jack_controller_settings_set_char_option(option_value, &value.c); - break; + sprintf(value_buffer, "%c", (char)value.c); + return; case JackParamString: - jack_controller_settings_set_string_option(option_value, value.str, sizeof(value.str)); - break; + strcpy(value_buffer, value.str); + return; case JackParamBool: - jack_controller_settings_set_bool_option(option_value, &value_int); - value.i = value_int; - break; - default: - jack_error("Engine parameter \"%s\" is of unknown type %d", - jackctl_parameter_get_name(parameter), - type); - } - - jackctl_parameter_set_value(parameter, &value); -} - -static -bool -jack_controller_settings_save_options( - void *context, - const JSList * parameters_list, - void *dbus_call_context_ptr) -{ - jackctl_parameter_t *parameter; - jackctl_param_type_t type; - union jackctl_parameter_value value; - const char * name; - char value_str[50]; - - while (parameters_list != NULL) - { - parameter = (jackctl_parameter_t *)parameters_list->data; - - if (jackctl_parameter_is_set(parameter)) - { - type = jackctl_parameter_get_type(parameter); - value = jackctl_parameter_get_value(parameter); - name = jackctl_parameter_get_name(parameter); - - switch (type) - { - case JackParamInt: - sprintf(value_str, "%d", (int)value.i); - if (!jack_controller_settings_write_option(context, name, value_str, dbus_call_context_ptr)) - { - return false; - } - break; - case JackParamUInt: - sprintf(value_str, "%u", (unsigned int)value.ui); - if (!jack_controller_settings_write_option(context, name, value_str, dbus_call_context_ptr)) - { - return false; - } - break; - case JackParamChar: - sprintf(value_str, "%c", (char)value.c); - if (!jack_controller_settings_write_option(context, name, value_str, dbus_call_context_ptr)) - { - return false; - } - break; - case JackParamString: - if (!jack_controller_settings_write_option(context, name, value.str, dbus_call_context_ptr)) - { - return false; - } - break; - case JackParamBool: - if (!jack_controller_settings_write_option(context, name, value.b ? "true" : "false", dbus_call_context_ptr)) - { - return false; - } - break; - default: - jack_error("parameter of unknown type %d", type); - } - } - - parameters_list = jack_slist_next(parameters_list); - } - - return true; -} - -bool -jack_controller_settings_save_engine_options( - void *context, - struct jack_controller *controller_ptr, - void *dbus_call_context_ptr) -{ - if (controller_ptr->driver != NULL) - { - if (!jack_controller_settings_write_option( - context, - "driver", - jackctl_driver_get_name(controller_ptr->driver), - dbus_call_context_ptr)) - { - return false; - } + strcpy(value_buffer, value.b ? "true" : "false"); + return; } - return jack_controller_settings_save_options(context, jackctl_server_get_parameters(controller_ptr->server), dbus_call_context_ptr); -} - -bool -jack_controller_settings_save_driver_options( - void *context, - jackctl_driver_t *driver, - void *dbus_call_context_ptr) -{ - return jack_controller_settings_save_options(context, jackctl_driver_get_parameters(driver), dbus_call_context_ptr); -} - -bool -jack_controller_settings_save_internal_options( - void *context, - jackctl_internal_t *internal, - void *dbus_call_context_ptr) -{ - return jack_controller_settings_save_options(context, jackctl_internal_get_parameters(internal), dbus_call_context_ptr); + jack_error("parameter of unknown type %d", (int)param_ptr->type); + assert(false); + *value_buffer = 0; } diff --git a/dbus/xml_expat.c b/dbus/xml_expat.c index 1ee1bd2e..f5a5aa68 100644 --- a/dbus/xml_expat.c +++ b/dbus/xml_expat.c @@ -1,6 +1,6 @@ /* -*- Mode: C ; c-basic-offset: 4 -*- */ /* - Copyright (C) 2007,2008 Nedko Arnaudov + Copyright (C) 2007,2008,2011 Nedko Arnaudov 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 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -46,27 +47,16 @@ jack_controller_settings_uninit() { } -#define PARSE_CONTEXT_ROOT 0 -#define PARSE_CONTEXT_JACK 1 -#define PARSE_CONTEXT_ENGINE 1 -#define PARSE_CONTEXT_DRIVERS 2 -#define PARSE_CONTEXT_DRIVER 3 -#define PARSE_CONTEXT_OPTION 4 -#define PARSE_CONTEXT_INTERNALS 5 -#define PARSE_CONTEXT_INTERNAL 6 - -#define MAX_STACK_DEPTH 10 - struct parse_context { struct jack_controller *controller_ptr; XML_Bool error; - unsigned int element[MAX_STACK_DEPTH]; - signed int depth; - jackctl_driver_t *driver; - jackctl_internal_t *internal; + bool option_value_capture; char option[JACK_PARAM_STRING_MAX+1]; int option_used; + const char * address[PARAM_ADDRESS_SIZE]; + int address_index; + char * container; char *name; }; @@ -80,7 +70,7 @@ jack_controller_settings_callback_chrdata(void *data, const XML_Char *s, int len return; } - if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_OPTION) + if (context_ptr->option_value_capture) { if (context_ptr->option_used + len >= JACK_PARAM_STRING_MAX) { @@ -97,107 +87,69 @@ jack_controller_settings_callback_chrdata(void *data, const XML_Char *s, int len void jack_controller_settings_callback_elstart(void *data, const char *el, const char **attr) { - jackctl_driver_t *driver; - jackctl_internal_t *internal; - if (context_ptr->error) { return; } - if (context_ptr->depth + 1 >= MAX_STACK_DEPTH) + if (context_ptr->address_index >= PARAM_ADDRESS_SIZE) { - jack_error("xml parse max stack depth reached"); + assert(context_ptr->address_index == PARAM_ADDRESS_SIZE); + jack_error("xml param address max depth reached"); context_ptr->error = XML_TRUE; return; } + //jack_info("<%s>", el); + if (strcmp(el, "jack") == 0) { - //jack_info(""); - context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_JACK; return; } - if (strcmp(el, "engine") == 0) + if (strcmp(el, PTNODE_ENGINE) == 0) { - //jack_info(""); - context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_ENGINE; + context_ptr->address[context_ptr->address_index++] = PTNODE_ENGINE; return; } - if (strcmp(el, "drivers") == 0) + if (strcmp(el, PTNODE_DRIVERS) == 0) { - //jack_info(""); - context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_DRIVERS; + context_ptr->address[context_ptr->address_index++] = PTNODE_DRIVERS; return; } - if (strcmp(el, "internals") == 0) + if (strcmp(el, PTNODE_INTERNALS) == 0) { - //jack_info(""); - context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_INTERNALS; + context_ptr->address[context_ptr->address_index++] = PTNODE_INTERNALS; return; } - if (strcmp(el, "driver") == 0) + if (strcmp(el, "driver") == 0 || + strcmp(el, "internal") == 0) { if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) { - jack_error(" XML element must contain exactly one attribute, named \"name\""); + jack_error("<%s> XML element must contain exactly one attribute, named \"name\"", el); context_ptr->error = XML_TRUE; return; } - //jack_info(""); - context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_DRIVER; - - driver = jack_controller_find_driver(context_ptr->controller_ptr->server, attr[1]); - if (driver == NULL) - { - jack_error("ignoring settings for unknown driver \"%s\"", attr[1]); - } - else - { - jack_info("setting for driver \"%s\" found", attr[1]); - } - - context_ptr->driver = driver; - - return; - } - - if (strcmp(el, "internal") == 0) - { - if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) + context_ptr->container = strdup(attr[1]); + if (context_ptr->container == NULL) { - jack_error(" XML element must contain exactly one attribute, named \"name\""); + jack_error("strdup() failed"); context_ptr->error = XML_TRUE; return; } - //jack_info(""); - context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_INTERNAL; - - internal = jack_controller_find_internal(context_ptr->controller_ptr->server, attr[1]); - if (internal == NULL) - { - jack_error("ignoring settings for unknown internal \"%s\"", attr[1]); - } - else - { - jack_info("setting for internal \"%s\" found", attr[1]); - } - - context_ptr->internal = internal; + context_ptr->address[context_ptr->address_index++] = context_ptr->container; return; } - - + if (strcmp(el, "option") == 0) { - //jack_info("