Browse Source

Merge branch 'master' into no-self-connect

Conflicts:
	common/JackControlAPI.cpp
tags/v1.9.10
Nedko Arnaudov 14 years ago
parent
commit
77fdffaf2f
100 changed files with 7498 additions and 3454 deletions
  1. +269
    -54
      ChangeLog
  2. +3
    -2
      README
  3. +14
    -14
      README_NETJACK2
  4. +319
    -278
      common/JackAPI.cpp
  5. +2
    -2
      common/JackActivationCount.cpp
  6. +1
    -0
      common/JackActivationCount.h
  7. +2
    -1
      common/JackAtomicArrayState.h
  8. +2
    -0
      common/JackAtomicState.h
  9. +151
    -121
      common/JackAudioAdapter.cpp
  10. +9
    -5
      common/JackAudioAdapter.h
  11. +13
    -13
      common/JackAudioAdapterFactory.cpp
  12. +100
    -81
      common/JackAudioAdapterInterface.cpp
  13. +54
    -32
      common/JackAudioAdapterInterface.h
  14. +142
    -91
      common/JackAudioDriver.cpp
  15. +11
    -19
      common/JackAudioDriver.h
  16. +3
    -2
      common/JackChannel.h
  17. +19
    -13
      common/JackClient.cpp
  18. +2
    -3
      common/JackClient.h
  19. +2
    -1
      common/JackClientControl.h
  20. +6
    -0
      common/JackConnectionManager.h
  21. +4
    -2
      common/JackConstants.h
  22. +252
    -166
      common/JackControlAPI.cpp
  23. +59
    -46
      common/JackControlAPI.h
  24. +2
    -2
      common/JackDebugClient.cpp
  25. +1
    -1
      common/JackDebugClient.h
  26. +121
    -20
      common/JackDriver.cpp
  27. +54
    -21
      common/JackDriver.h
  28. +235
    -100
      common/JackDriverLoader.cpp
  29. +17
    -8
      common/JackDriverLoader.h
  30. +33
    -123
      common/JackDummyDriver.cpp
  31. +13
    -26
      common/JackDummyDriver.h
  32. +39
    -31
      common/JackEngine.cpp
  33. +4
    -4
      common/JackEngine.h
  34. +22
    -8
      common/JackEngineControl.cpp
  35. +7
    -6
      common/JackEngineControl.h
  36. +61
    -61
      common/JackEngineProfiling.cpp
  37. +4
    -0
      common/JackEngineProfiling.h
  38. +10
    -10
      common/JackError.cpp
  39. +10
    -10
      common/JackError.h
  40. +16
    -9
      common/JackException.h
  41. +5
    -4
      common/JackExternalClient.cpp
  42. +2
    -2
      common/JackExternalClient.h
  43. +66
    -53
      common/JackFilters.h
  44. +1
    -1
      common/JackFrameTimer.cpp
  45. +2
    -0
      common/JackFrameTimer.h
  46. +60
    -14
      common/JackFreewheelDriver.cpp
  47. +10
    -0
      common/JackFreewheelDriver.h
  48. +6
    -6
      common/JackGlobals.cpp
  49. +8
    -7
      common/JackGlobals.h
  50. +8
    -8
      common/JackGraphManager.cpp
  51. +1
    -0
      common/JackGraphManager.h
  52. +7
    -6
      common/JackInternalClient.cpp
  53. +37
    -2
      common/JackInternalClientChannel.h
  54. +20
    -17
      common/JackLibAPI.cpp
  55. +5
    -8
      common/JackLibClient.cpp
  56. +1
    -1
      common/JackLibClient.h
  57. +9
    -1
      common/JackLibGlobals.h
  58. +4
    -3
      common/JackLibSampleRateResampler.cpp
  59. +0
    -2
      common/JackLibSampleRateResampler.h
  60. +19
    -9
      common/JackLockedEngine.h
  61. +55
    -23
      common/JackLoopbackDriver.cpp
  62. +11
    -2
      common/JackLoopbackDriver.h
  63. +43
    -27
      common/JackMessageBuffer.cpp
  64. +13
    -13
      common/JackMessageBuffer.h
  65. +49
    -30
      common/JackMidiAPI.cpp
  66. +96
    -0
      common/JackMidiAsyncQueue.cpp
  67. +98
    -0
      common/JackMidiAsyncQueue.h
  68. +85
    -0
      common/JackMidiAsyncWaitQueue.cpp
  69. +99
    -0
      common/JackMidiAsyncWaitQueue.h
  70. +67
    -0
      common/JackMidiBufferReadQueue.cpp
  71. +60
    -0
      common/JackMidiBufferReadQueue.h
  72. +65
    -0
      common/JackMidiBufferWriteQueue.cpp
  73. +62
    -0
      common/JackMidiBufferWriteQueue.h
  74. +93
    -46
      common/JackMidiDriver.cpp
  75. +15
    -16
      common/JackMidiDriver.h
  76. +6
    -3
      common/JackMidiPort.cpp
  77. +299
    -0
      common/JackMidiRawInputWriteQueue.cpp
  78. +177
    -0
      common/JackMidiRawInputWriteQueue.h
  79. +158
    -0
      common/JackMidiRawOutputWriteQueue.cpp
  80. +139
    -0
      common/JackMidiRawOutputWriteQueue.h
  81. +27
    -0
      common/JackMidiReadQueue.cpp
  82. +55
    -0
      common/JackMidiReadQueue.h
  83. +27
    -0
      common/JackMidiReceiveQueue.cpp
  84. +42
    -0
      common/JackMidiReceiveQueue.h
  85. +34
    -0
      common/JackMidiSendQueue.cpp
  86. +52
    -0
      common/JackMidiSendQueue.h
  87. +121
    -0
      common/JackMidiUtil.cpp
  88. +102
    -0
      common/JackMidiUtil.h
  89. +27
    -0
      common/JackMidiWriteQueue.cpp
  90. +82
    -0
      common/JackMidiWriteQueue.h
  91. +6
    -6
      common/JackMutex.h
  92. +1043
    -0
      common/JackNetAPI.cpp
  93. +225
    -266
      common/JackNetAdapter.cpp
  94. +32
    -28
      common/JackNetAdapter.h
  95. +370
    -386
      common/JackNetDriver.cpp
  96. +21
    -22
      common/JackNetDriver.h
  97. +632
    -612
      common/JackNetInterface.cpp
  98. +111
    -86
      common/JackNetInterface.h
  99. +419
    -343
      common/JackNetManager.cpp
  100. +21
    -15
      common/JackNetManager.h

+ 269
- 54
ChangeLog View File

@@ -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 <letz@grame.fr>

* More robust dynamic port management in JACK/CoreMidi bridge.
* Correct jack_port_name_size API.

2011-11-24 Stephane Letz <letz@grame.fr>

* Dynamic port management in JACK/CoreMidi bridge.
* Correct jack_client_create_thread (when realtime in on).

2011-11-21 Stephane Letz <letz@grame.fr>

* John Emmas third auto-launch server on Windows patch.

2011-11-07 Stephane Letz <letz@grame.fr>

* John Emmas first auto-launch server on Windows patch.
* John Emmas second auto-launch server on Windows patch.

2011-11-06 Stephane Letz <letz@grame.fr>

* Enable local access in NetJack2 code.

2011-11-04 Stephane Letz <letz@grame.fr>

* Fix jack_set_port_name API.

2011-11-03 Stephane Letz <letz@grame.fr>

* Add missing jack_client_get_uuid API.
* John Emmas Windows server launching patch (1).

2011-10-28 Stephane Letz <letz@grame.fr>

* John Emmas POST_PACKED_STRUCTURE patch.

2011-10-27 Stephane Letz <letz@grame.fr>

* Gabriel Beddingfield patch (r4541) reverted.

2011-10-10 Stephane Letz <letz@grame.fr>

* John Emmas patch for DSP CPU computation.

2011-09-27 Stephane Letz <letz@grame.fr>

* Gabriel Beddingfield patch for ALSA driver: error when source is non-native byte-order float.

2011-08-31 Stephane Letz <letz@grame.fr>

* Correct Start/Stop for Control API.

2011-08-30 Stephane Letz <letz@grame.fr>

* Check driver type in jackdmp.cpp.

2011-08-28 Stephane Letz <letz@grame.fr>

* Correct JackBasePosixMutex::Trylock.
* Correct JackMessageBuffer::Execute.

2011-08-26 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

* New JackTimedDriver class to be used by JackDummyDriver, JackNetDriver and JackNetOneDriver classes.

2011-07-28 Stephane Letz <letz@grame.fr>

* Enable explicit channel mapping in CoreAudio driver.

2011-07-25 Stephane Letz <letz@grame.fr>

* NetJack2: no more timeout, correct JackWaitThreadedDriver::Execute.

2011-07-25 Stephane Letz <letz@grame.fr>

* NetJack2: improve latency management, cleanup.

2011-07-23 Stephane Letz <letz@grame.fr>

* Possible fix for http://trac.jackaudio.org/ticket/193.

2011-07-22 Stephane Letz <letz@grame.fr>

* NetJack2: improve error reporting.

2011-07-16 Stephane Letz <letz@grame.fr>

* Error in JackActivationCount::Signal now uses jack_log instead of jack_error.
* EXPORT macro renamed to LIB_EXPORT.

2011-07-12 Stephane Letz <letz@grame.fr>

* NetJack2 now only send data on network only is ports are connected both sides.

2011-07-11 Stephane Letz <letz@grame.fr>

* Add JACK_NETJACK_PORT and JACK_NETJACK_MULTICAST environment variables for NetJack2.

2011-07-08 Stephane Letz <letz@grame.fr>

* NetJack2 now only send data on network for connected ports.

2011-07-03 Stephane Letz <letz@grame.fr>

* More debug code in JackMMCSS class.

2011-07-03 Stephane Letz <letz@grame.fr>

* -l in JackCoreAudioDriver now display devices names and then quit.

2011-07-01 Stephane Letz <letz@grame.fr>

* Fix bugs in JackNetAdapter.

2011-06-29 Stephane Letz <letz@grame.fr>

* Special CATCH_CLOSE_EXCEPTION_RETURN to handle Close API calls.

2011-06-28 Stephane Letz <letz@grame.fr>

* Another round of code improvements to handle completely buggy Digidesign CoreAudio user-land driver.

2011-06-20 Stephane Letz <letz@grame.fr>

* Correct Dummy driver.

2011-06-17 Stephane Letz <letz@grame.fr>

* NetJack2: connection error handling.

2011-06-16 Stephane Letz <letz@grame.fr>

* Changes in NetJack2 connection management: no more timeout, any transmission error considered as fatal.
* NetJack2: timeout again...

2011-06-11 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

* SaveConnections/RestoreConnections in NetDriver.
* SaveConnections/RestoreConnections moved in JackAudioDriver.

2011-06-09 Stephane Letz <letz@grame.fr>

* Correct NetJack2 connection handling.

2011-05-27 Stephane Letz <letz@grame.fr>

* Correct rd_acquire in dbus code.

2011-05-16 Stephane Letz <letz@grame.fr>

* Correct OSX real-time thread setup.

2011-05-11 Stephane Letz <letz@grame.fr>

* Correct MIDI in NetJack2.

2011-05-05 Stephane Letz <letz@grame.fr>

* Libjacknet in progress.

2011-05-02 Stephane Letz <letz@grame.fr>

* Merge branch switch-master-port-registration-notifications: correct driver port registration.

2011-04-21 Stephane Letz <letz@grame.fr>

* CELT code for NetJack2.

2011-04-20 Stephane Letz <letz@grame.fr>

* Add XRun detection in PortAudio driver.

2011-04-18 Stephane Letz <letz@grame.fr>

* JackWeakAPI.cpp renamed in JackWeakAPI.c.

2011-04-04 Stephane Letz <letz@grame.fr>

* Correct driver lifetime management.

2011-04-03 Stephane Letz <letz@grame.fr>

* Fix in JackCoreAudioDriver::Read when there is no inputs.

2011-04-02 Stephane Letz <letz@grame.fr>

* NetDriver can now ask for in/out values from the master (in progress).
* Correct drivers parameter settings.

2011-04-01 Stephane Letz <letz@grame.fr>

* Merge newer-midi branch (Devin Anderson redesign of the MIDI drivers: alsarawmidi, ffado, coremidi and winmme).
* Cleanup JackThreadedDriver::Stop.
* Correct JackNetOneDriver::Close.
* Correction in jackdmp.cpp: notify_server_stop should be done after server destruction.
* Improve error management in JackNetDriver.

2011-03-30 Stephane Letz <letz@grame.fr>

* Version 1.9.8 started.

2011-03-29 Stephane Letz <letz@grame.fr>

* Synchronize JackWeakAPI.cpp with new APIs.
@@ -117,7 +332,7 @@ Valerio Pilo

2010-11-17 Stephane Letz <letz@grame.fr>

* ALSA backend : suspend/resume handling (jack1 r4075).
* ALSA backend: suspend/resume handling (jack1 r4075).
* Correct dummy driver.

2010-11-05 Stephane Letz <letz@grame.fr>
@@ -169,7 +384,7 @@ Valerio Pilo

2010-06-13 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -216,7 +431,7 @@ Valerio Pilo

2010-03-04 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>
@@ -225,7 +440,7 @@ Valerio Pilo

2010-03-02 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -267,7 +482,7 @@ Valerio Pilo

2009-12-01 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -297,7 +512,7 @@ Valerio Pilo
2009-11-17 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -305,14 +520,14 @@ Valerio Pilo

2009-11-14 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

* 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 <letz@grame.fr>
@@ -329,12 +544,12 @@ Valerio Pilo

2009-11-09 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -375,7 +590,7 @@ Valerio Pilo

2009-10-25 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>
@@ -396,7 +611,7 @@ Valerio Pilo

2009-10-17 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -477,12 +692,12 @@ Valerio Pilo

2009-06-30 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -582,7 +797,7 @@ Valerio Pilo

2009-03-11 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -797,7 +1012,7 @@ Valerio Pilo

2008-10-08 Stephane Letz <letz@grame.fr>
* 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 <letz@grame.fr>
@@ -812,7 +1027,7 @@ Valerio Pilo

2008-10-10 Stephane Letz <letz@grame.fr>
* 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 <letz@grame.fr>
@@ -821,7 +1036,7 @@ Valerio Pilo

2008-10-08 Stephane Letz <letz@grame.fr>
* 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 <letz@grame.fr>
@@ -938,7 +1153,7 @@ Valerio Pilo
2008-07-08 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -1011,7 +1226,7 @@ Valerio Pilo

2008-06-02 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -1038,12 +1253,12 @@ Valerio Pilo

2008-05-27 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -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 <letz@grame.fr>

@@ -1074,7 +1289,7 @@ Valerio Pilo
2008-05-19 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -1103,7 +1318,7 @@ Valerio Pilo

2008-05-05 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>
@@ -1144,7 +1359,7 @@ Valerio Pilo
2008-03-29 Stephane Letz <letz@grame.fr>
* 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 <letz@grame.fr>
@@ -1334,7 +1549,7 @@ Valerio Pilo

2008-01-03 Stephane Letz <letz@grame.fr>

* 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 <letz@grame.fr>

@@ -1741,4 +1956,4 @@ Valerio Pilo

2006-09-03 Stephane Letz <letz@grame.fr>

* First import of version 0.58 base code
* First import of version 0.58 base code

+ 3
- 2
README View File

@@ -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...


+ 14
- 14
README_NETJACK2 View File

@@ -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.

+ 319
- 278
common/JackAPI.cpp
File diff suppressed because it is too large
View File


+ 2
- 2
common/JackActivationCount.cpp View File

@@ -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();


+ 1
- 0
common/JackActivationCount.h View File

@@ -33,6 +33,7 @@ struct JackClientControl;
\brief Client activation counter.
*/

PRE_PACKED_STRUCTURE
class JackActivationCount
{



+ 2
- 1
common/JackAtomicArrayState.h View File

@@ -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 T>
class JackAtomicArrayState
{


+ 2
- 0
common/JackAtomicState.h View File

@@ -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 T>
class JackAtomicState
{


+ 151
- 121
common/JackAudioAdapter.cpp View File

@@ -32,158 +32,188 @@ using namespace std;
namespace Jack
{

//static methods ***********************************************************
int JackAudioAdapter::Process (jack_nframes_t frames, void* arg)
{
JackAudioAdapter* adapter = static_cast<JackAudioAdapter*>(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<JackAudioAdapter*>(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<JackAudioAdapter*> ( 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<JackAudioAdapter*> ( 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<JackAudioAdapter*>(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<JackAudioAdapter*>(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<JackAudioAdapter*>(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

+ 9
- 5
common/JackAudioAdapter.h View File

@@ -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<JackPortFlags>(JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal)
#define PlaybackDriverFlags static_cast<JackPortFlags>(JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal)

#endif

+ 13
- 13
common/JackAudioAdapterFactory.cpp View File

@@ -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, &params );
Jack::JackArgParser parser(load_init);
if (parser.GetArgc() > 0) {
parse_params = parser.ParseParams(desc, &params);
}

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;
}


+ 100
- 81
common/JackAudioAdapterInterface.cpp View File

@@ -17,9 +17,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifdef __APPLE__
#include <TargetConditionals.h>
#endif

#include "JackAudioAdapter.h"
#ifndef MY_TARGET_OS_IPHONE
#include "JackLibSampleRateResampler.h"
#include "JackTime.h"
#endif
#include "JackTime.h"
#include <stdio.h>

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

+ 54
- 32
common/JackAudioAdapterInterface.h View File

@@ -22,7 +22,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "JackResampler.h"
#include "JackFilters.h"
#include "JackConstants.h"
#include <stdio.h>

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);

};



+ 142
- 91
common/JackAudioDriver.cpp View File

@@ -29,14 +29,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "JackException.h"
#include <assert.h>

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)


+ 11
- 19
common/JackAudioDriver.h View File

@@ -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();


+ 3
- 2
common/JackChannel.h View File

@@ -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)
{}


+ 19
- 13
common/JackClient.cpp View File

@@ -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;
}



+ 2
- 3
common/JackClient.h View File

@@ -27,7 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "JackSynchro.h"
#include "JackPlatformPlug.h"
#include "JackChannel.h"
#include "session.h"
#include "varargs.h"
#include <list>

@@ -94,7 +93,7 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable
JackSynchro* fSynchroTable;
std::list<jack_port_id_t> 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();



+ 2
- 1
common/JackClientControl.h View File

@@ -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];


+ 6
- 0
common/JackConnectionManager.h View File

@@ -36,6 +36,7 @@ struct JackClientControl;
\brief Utility class.
*/

PRE_PACKED_STRUCTURE
template <int SIZE>
class JackFixedArray
{
@@ -122,6 +123,7 @@ class JackFixedArray
\brief Utility class.
*/

PRE_PACKED_STRUCTURE
template <int SIZE>
class JackFixedArray1 : public JackFixedArray<SIZE>
{
@@ -158,6 +160,7 @@ class JackFixedArray1 : public JackFixedArray<SIZE>
\brief Utility class.
*/

PRE_PACKED_STRUCTURE
template <int SIZE>
class JackFixedMatrix
{
@@ -253,6 +256,7 @@ class JackFixedMatrix
\brief Utility class.
*/

PRE_PACKED_STRUCTURE
template <int SIZE>
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
</UL>
*/

PRE_PACKED_STRUCTURE
class SERVER_EXPORT JackConnectionManager
{



+ 4
- 2
common/JackConstants.h View File

@@ -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


+ 252
- 166
common/JackControlAPI.cpp View File

@@ -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 = &parameter_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;
}
}



+ 59
- 46
common/JackControlAPI.h View File

@@ -28,6 +28,7 @@
#ifdef WIN32
#ifdef __MINGW32__
#include <sys/types.h>
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


+ 2
- 2
common/JackDebugClient.cpp View File

@@ -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);
}



+ 1
- 1
common/JackDebugClient.h View File

@@ -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:



+ 121
- 20
common/JackDriver.cpp View File

@@ -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<JackDriverInterface*>::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<JackDriverInterface*>::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<JackDriverInterface*>::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<JackDriverInterface*>::const_iterator it;
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) {
JackDriverInterface* slave = *it;
if (slave->SetBufferSize(buffer_size) < 0) {
res = -1;
}
}
return res;
}

int JackDriver::SetSampleRate(jack_nframes_t sample_rate)
{
return 0;
int res = 0;
list<JackDriverInterface*>::const_iterator it;
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) {
JackDriverInterface* slave = *it;
if (slave->SetSampleRate(sample_rate) < 0) {
res = -1;
}
}
return res;
}

bool JackDriver::Initialize()
@@ -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<pair<string, string> >::const_iterator it;

for (it = fConnections.begin(); it != fConnections.end(); it++) {
pair<string, string> 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

+ 54
- 21
common/JackDriver.h View File

@@ -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<JackDriverInterface*> GetSlaves() = 0;
virtual int ProcessSlaves() = 0;

virtual int ProcessReadSlaves() = 0;
virtual int ProcessWriteSlaves() = 0;

virtual int ProcessRead() = 0;
virtual int ProcessWrite() = 0;

virtual bool IsRealTime() const = 0;
virtual bool IsRunning() const = 0;
@@ -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<std::pair<std::string, std::string> > 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<JackDriverInterface*> 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);


+ 235
- 100
common/JackDriverLoader.cpp View File

@@ -25,14 +25,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include <getopt.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#ifndef WIN32
#include <dirent.h>
#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;
}

+ 17
- 8
common/JackDriverLoader.h View File

@@ -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


+ 33
- 123
common/JackDummyDriver.cpp View File

@@ -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 <iostream>
#include <unistd.h>
#include <math.h>

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;


+ 13
- 26
common/JackDummyDriver.h View File

@@ -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


+ 39
- 31
common/JackEngine.cpp View File

@@ -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;
}


+ 4
- 4
common/JackEngine.h View File

@@ -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<int,std::string> 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);
};




+ 22
- 8
common/JackEngineControl.cpp View File

@@ -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()


+ 7
- 6
common/JackEngineControl.h View File

@@ -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;


+ 61
- 61
common/JackEngineProfiling.cpp View File

@@ -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 << " <div class='center'><object class='center' type='image/svg+xml' data='Timing5.svg'>Timing5</object></div>";
fStream6 << " </body>\n";
fStream6 << "</html>\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

+ 4
- 0
common/JackEngineProfiling.h View File

@@ -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
{



+ 10
- 10
common/JackError.cpp View File

@@ -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;

+ 10
- 10
common/JackError.h View File

@@ -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
}


+ 16
- 9
common/JackException.h View File

@@ -23,12 +23,18 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include <stdexcept>
#include <iostream>
#include <string>
#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.
*/


+ 5
- 4
common/JackExternalClient.cpp View File

@@ -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<JackShmMemAble*>(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();


+ 2
- 2
common/JackExternalClient.h View File

@@ -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:



+ 66
- 53
common/JackFilters.h View File

@@ -20,33 +20,42 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#ifndef __JackFilters__
#define __JackFilters__

#ifdef __APPLE__
#include <TargetConditionals.h>
#endif

#include "jack.h"
#ifndef MY_TARGET_OS_IPHONE
#include "JackAtomicState.h"
#endif
#include <math.h>
#include <stdlib.h>

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<JackDelayLockedLoop>
{
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;
}
}
};

}


+ 1
- 1
common/JackFrameTimer.cpp View File

@@ -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.

*/


+ 2
- 0
common/JackFrameTimer.h View File

@@ -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<JackTimer>
{



+ 60
- 14
common/JackFreewheelDriver.cpp View File

@@ -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

+ 10
- 0
common/JackFreewheelDriver.h View File

@@ -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


+ 6
- 6
common/JackGlobals.cpp View File

@@ -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

+ 8
- 7
common/JackGlobals.h View File

@@ -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 <iostream>
#include <fstream>
#include <string>
#include <time.h>
#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



+ 8
- 8
common/JackGraphManager.cpp View File

@@ -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;
}
}


+ 1
- 0
common/JackGraphManager.h View File

@@ -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<JackConnectionManager>
{



+ 7
- 6
common/JackInternalClient.cpp View File

@@ -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;
}



+ 37
- 2
common/JackInternalClientChannel.h View File

@@ -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);
}




+ 20
- 17
common/JackLibAPI.cpp View File

@@ -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;


+ 5
- 8
common/JackLibClient.cpp View File

@@ -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;
}


+ 1
- 1
common/JackLibClient.h View File

@@ -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:


+ 9
- 1
common/JackLibGlobals.h View File

@@ -32,6 +32,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <assert.h>
#include <signal.h>

#ifdef WIN32
#ifdef __MINGW32__
#include <sys/types.h>
typedef _sigset_t sigset_t;
#else
typedef HANDLE sigset_t;
#endif
#endif

namespace Jack
{
@@ -42,7 +50,7 @@ class JackClient;
\brief Global library static structure: singleton kind of pattern.
*/

struct SERVER_EXPORT JackLibGlobals
struct LIB_EXPORT JackLibGlobals
{
JackShmReadWritePtr<JackGraphManager> fGraphManager; /*! Shared memory Port manager */
JackShmReadWritePtr<JackEngineControl> fEngineControl; /*! Shared engine control */ // transport engine has to be writable


+ 4
- 3
common/JackLibSampleRateResampler.cpp View File

@@ -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;


+ 0
- 2
common/JackLibSampleRateResampler.h View File

@@ -21,8 +21,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#define __JackLibSampleRateResampler__

#include "JackResampler.h"
#include "types.h"

#include <samplerate.h>

namespace Jack


+ 19
- 9
common/JackLockedEngine.h View File

@@ -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
}
};


+ 55
- 23
common/JackLoopbackDriver.cpp View File

@@ -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;
}


+ 11
- 2
common/JackLoopbackDriver.h View File

@@ -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


+ 43
- 27
common/JackMessageBuffer.cpp View File

@@ -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");
}
}

};


+ 13
- 13
common/JackMessageBuffer.h View File

@@ -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

+ 49
- 30
common/JackMidiAPI.cpp View File

@@ -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 <errno.h>
#include <string.h>
#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())


+ 96
- 0
common/JackMidiAsyncQueue.cpp View File

@@ -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 <new>

#include "JackMidiAsyncQueue.h"

using Jack::JackMidiAsyncQueue;

JackMidiAsyncQueue::JackMidiAsyncQueue(size_t max_bytes, size_t max_messages)
{
data_buffer = new jack_midi_data_t[max_bytes];
byte_ring = jack_ringbuffer_create((max_bytes * sizeof(jack_midi_data_t)) +
1);
if (byte_ring) {
info_ring = jack_ringbuffer_create((max_messages * INFO_SIZE) + 1);
if (info_ring) {
jack_ringbuffer_mlock(byte_ring);
jack_ringbuffer_mlock(info_ring);
this->max_bytes = max_bytes;
return;
}
jack_ringbuffer_free(byte_ring);
}
delete data_buffer;
throw std::bad_alloc();
}

JackMidiAsyncQueue::~JackMidiAsyncQueue()
{
jack_ringbuffer_free(byte_ring);
jack_ringbuffer_free(info_ring);
delete[] data_buffer;
}

jack_midi_event_t *
JackMidiAsyncQueue::DequeueEvent()
{
jack_midi_event_t *event = 0;
if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) {
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);
}

+ 98
- 0
common/JackMidiAsyncQueue.h View File

@@ -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

+ 85
- 0
common/JackMidiAsyncWaitQueue.cpp View File

@@ -0,0 +1,85 @@
/*
Copyright (C) 2010 Devin Anderson

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include <new>

#include "JackMidiAsyncWaitQueue.h"
#include "JackMidiUtil.h"
#include "JackTime.h"

using Jack::JackMidiAsyncWaitQueue;

JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes,
size_t max_messages):
JackMidiAsyncQueue(max_bytes, max_messages)
{
if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) {
throw std::bad_alloc();
}
}

JackMidiAsyncWaitQueue::~JackMidiAsyncWaitQueue()
{
semaphore.Destroy();
}

jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent()
{
return DequeueEvent((long) 0);
}

jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame)
{

// XXX: I worry about timer resolution on Solaris and Windows. When the
// resolution for the `JackSynchro` object is milliseconds, the worst-case
// scenario for processor objects is that the wait time becomes less than a
// millisecond, and the processor object continually calls this method,
// expecting to wait a certain amount of microseconds, and ends up not
// waiting at all each time, essentially busy-waiting until the current
// frame is reached. Perhaps there should be a #define that indicates the
// wait time resolution for `JackSynchro` objects so that we can wait a
// little longer if necessary.

jack_time_t frame_time = GetTimeFromFrames(frame);
jack_time_t current_time = GetMicroSeconds();
return DequeueEvent((frame_time < current_time) ? 0 :
(long) (frame_time - current_time));
}

jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent(long usec)
{
return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ?
JackMidiAsyncQueue::DequeueEvent() : 0;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiAsyncWaitQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
EnqueueResult result = JackMidiAsyncQueue::EnqueueEvent(time, size,
buffer);
if (result == OK) {
semaphore.Signal();
}
return result;
}

+ 99
- 0
common/JackMidiAsyncWaitQueue.h View File

@@ -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

+ 67
- 0
common/JackMidiBufferReadQueue.cpp View File

@@ -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();
}
}

+ 60
- 0
common/JackMidiBufferReadQueue.h View File

@@ -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

+ 65
- 0
common/JackMidiBufferWriteQueue.cpp View File

@@ -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;
}
}

+ 62
- 0
common/JackMidiBufferWriteQueue.h View File

@@ -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

+ 93
- 46
common/JackMidiDriver.cpp View File

@@ -27,26 +27,17 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "JackException.h"
#include <assert.h>

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;
}



+ 15
- 16
common/JackMidiDriver.h View File

@@ -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


+ 6
- 3
common/JackMidiPort.cpp View File

@@ -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<JackMidiBuffer*>(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<JackMidiBuffer*>(src_buffers[i]);
if (!buf->IsValid())
if (!buf->IsValid()) {
jack_error("Jack::MidiBufferMixdown - invalid source buffer");
return;
}
buf->mix_index = 0;
event_count += buf->event_count;
mix->lost_events += buf->lost_events;


+ 299
- 0
common/JackMidiRawInputWriteQueue.cpp View File

@@ -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 <cassert>
#include <memory>
#include <new>

#include "JackMidiRawInputWriteQueue.h"

using Jack::JackMidiRawInputWriteQueue;

JackMidiRawInputWriteQueue::
JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue,
size_t max_packet_data, size_t max_packets)
{
packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets);
std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue);
input_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;
}

+ 177
- 0
common/JackMidiRawInputWriteQueue.h View File

@@ -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

+ 158
- 0
common/JackMidiRawOutputWriteQueue.cpp View File

@@ -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 <memory>
#include <new>

#include "JackError.h"
#include "JackMidiRawOutputWriteQueue.h"
#include "JackMidiUtil.h"

using Jack::JackMidiRawOutputWriteQueue;

#define STILL_TIME(c, b) ((! (b)) || ((c) < (b)))

JackMidiRawOutputWriteQueue::
JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size,
size_t max_non_rt_messages, size_t max_rt_messages)
{
non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages);
std::auto_ptr<JackMidiAsyncQueue> non_rt_ptr(non_rt_queue);
rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages);
std::auto_ptr<JackMidiAsyncQueue> rt_ptr(rt_queue);
non_rt_event = 0;
rt_event = 0;
running_status = 0;
this->send_queue = send_queue;
rt_ptr.release();
non_rt_ptr.release();
}

JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue()
{
delete non_rt_queue;
delete rt_queue;
}

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;
}

+ 139
- 0
common/JackMidiRawOutputWriteQueue.h View File

@@ -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

+ 27
- 0
common/JackMidiReadQueue.cpp View File

@@ -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
}

+ 55
- 0
common/JackMidiReadQueue.h View File

@@ -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

+ 27
- 0
common/JackMidiReceiveQueue.cpp View File

@@ -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
}

+ 42
- 0
common/JackMidiReceiveQueue.h View File

@@ -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

+ 34
- 0
common/JackMidiSendQueue.cpp View File

@@ -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();
}

+ 52
- 0
common/JackMidiSendQueue.h View File

@@ -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

+ 121
- 0
common/JackMidiUtil.cpp View File

@@ -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);
}

+ 102
- 0
common/JackMidiUtil.h View File

@@ -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

+ 27
- 0
common/JackMidiWriteQueue.cpp View File

@@ -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
}

+ 82
- 0
common/JackMidiWriteQueue.h View File

@@ -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

+ 6
- 6
common/JackMutex.h View File

@@ -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();
}

};


+ 1043
- 0
common/JackNetAPI.cpp
File diff suppressed because it is too large
View File


+ 225
- 266
common/JackNetAdapter.cpp View File

@@ -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, &params );
Jack::JackArgParser parser(load_init);
if (parser.GetArgc() > 0) {
parse_params = parser.ParseParams(desc, &params);
}

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<Jack::JackAudioAdapter*> ( arg );
Jack::JackAudioAdapter* adapter = static_cast<Jack::JackAudioAdapter*>(arg);

if (adapter) {
jack_log ( "Unloading netadapter" );
jack_log("Unloading netadapter");
adapter->Close();
delete adapter;
}


+ 32
- 28
common/JackNetAdapter.h View File

@@ -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();
};
}



+ 370
- 386
common/JackNetDriver.cpp
File diff suppressed because it is too large
View File


+ 21
- 22
common/JackNetDriver.h View File

@@ -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<float>* 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;
}


+ 632
- 612
common/JackNetInterface.cpp
File diff suppressed because it is too large
View File


+ 111
- 86
common/JackNetInterface.h View File

@@ -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

+ 419
- 343
common/JackNetManager.cpp
File diff suppressed because it is too large
View File


+ 21
- 15
common/JackNetManager.h View File

@@ -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();
};
}


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save