Browse Source

rebase from trunk 4238:4306

git-svn-id: http://subversion.jackaudio.org/jack/jack2/branches/libjacknet@4307 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/1.9.8
sletz 14 years ago
parent
commit
ecb84b5f9f
100 changed files with 6026 additions and 2352 deletions
  1. +25
    -0
      ChangeLog
  2. +2
    -2
      README
  3. +89
    -33
      common/JackAudioDriver.cpp
  4. +7
    -2
      common/JackAudioDriver.h
  5. +2
    -1
      common/JackChannel.h
  6. +0
    -1
      common/JackClient.h
  7. +1
    -1
      common/JackClientControl.h
  8. +2
    -2
      common/JackConstants.h
  9. +23
    -8
      common/JackControlAPI.cpp
  10. +14
    -13
      common/JackControlAPI.h
  11. +50
    -10
      common/JackDriver.cpp
  12. +18
    -3
      common/JackDriver.h
  13. +18
    -4
      common/JackDriverLoader.cpp
  14. +1
    -1
      common/JackDriverLoader.h
  15. +1
    -0
      common/JackDummyDriver.cpp
  16. +1
    -0
      common/JackExternalClient.cpp
  17. +60
    -14
      common/JackFreewheelDriver.cpp
  18. +10
    -0
      common/JackFreewheelDriver.h
  19. +8
    -0
      common/JackLibGlobals.h
  20. +2
    -2
      common/JackLibSampleRateResampler.cpp
  21. +48
    -7
      common/JackLoopbackDriver.cpp
  22. +11
    -2
      common/JackLoopbackDriver.h
  23. +103
    -0
      common/JackMidiAsyncQueue.cpp
  24. +98
    -0
      common/JackMidiAsyncQueue.h
  25. +85
    -0
      common/JackMidiAsyncWaitQueue.cpp
  26. +99
    -0
      common/JackMidiAsyncWaitQueue.h
  27. +67
    -0
      common/JackMidiBufferReadQueue.cpp
  28. +60
    -0
      common/JackMidiBufferReadQueue.h
  29. +65
    -0
      common/JackMidiBufferWriteQueue.cpp
  30. +62
    -0
      common/JackMidiBufferWriteQueue.h
  31. +90
    -28
      common/JackMidiDriver.cpp
  32. +18
    -8
      common/JackMidiDriver.h
  33. +4
    -3
      common/JackMidiPort.cpp
  34. +300
    -0
      common/JackMidiRawInputWriteQueue.cpp
  35. +170
    -0
      common/JackMidiRawInputWriteQueue.h
  36. +228
    -0
      common/JackMidiRawOutputWriteQueue.cpp
  37. +147
    -0
      common/JackMidiRawOutputWriteQueue.h
  38. +27
    -0
      common/JackMidiReadQueue.cpp
  39. +55
    -0
      common/JackMidiReadQueue.h
  40. +27
    -0
      common/JackMidiReceiveQueue.cpp
  41. +42
    -0
      common/JackMidiReceiveQueue.h
  42. +34
    -0
      common/JackMidiSendQueue.cpp
  43. +52
    -0
      common/JackMidiSendQueue.h
  44. +120
    -0
      common/JackMidiUtil.cpp
  45. +102
    -0
      common/JackMidiUtil.h
  46. +27
    -0
      common/JackMidiWriteQueue.cpp
  47. +82
    -0
      common/JackMidiWriteQueue.h
  48. +23
    -23
      common/JackNetAdapter.cpp
  49. +31
    -27
      common/JackNetAdapter.h
  50. +61
    -33
      common/JackNetDriver.cpp
  51. +6
    -3
      common/JackNetDriver.h
  52. +45
    -45
      common/JackNetInterface.cpp
  53. +22
    -16
      common/JackNetInterface.h
  54. +34
    -11
      common/JackNetManager.cpp
  55. +9
    -0
      common/JackNetManager.h
  56. +778
    -821
      common/JackNetOneDriver.cpp
  57. +63
    -66
      common/JackNetOneDriver.h
  58. +2
    -0
      common/JackNetTool.cpp
  59. +4
    -4
      common/JackNetTool.h
  60. +0
    -287
      common/JackPhysicalMidiInput.cpp
  61. +0
    -146
      common/JackPhysicalMidiInput.h
  62. +0
    -320
      common/JackPhysicalMidiOutput.cpp
  63. +0
    -118
      common/JackPhysicalMidiOutput.h
  64. +1
    -1
      common/JackPort.h
  65. +1
    -3
      common/JackServer.cpp
  66. +69
    -0
      common/JackSession.h
  67. +17
    -4
      common/JackThreadedDriver.cpp
  68. +8
    -1
      common/JackThreadedDriver.h
  69. +26
    -22
      common/JackWaitThreadedDriver.h
  70. +6
    -6
      common/JackWeakAPI.cpp
  71. +32
    -9
      common/Jackdmp.cpp
  72. +3
    -3
      common/jack/jack.h
  73. +22
    -11
      common/jack/weakmacros.h
  74. +2
    -3
      common/netjack.c
  75. +2
    -3
      common/netjack_packet.c
  76. +15
    -2
      common/wscript
  77. +92
    -18
      dbus/controller.c
  78. +75
    -118
      dbus/controller_iface_configure.c
  79. +32
    -14
      dbus/controller_iface_control.c
  80. +11
    -2
      dbus/controller_internal.h
  81. +1
    -0
      dbus/jackdbus.h
  82. +1
    -1
      doxyfile
  83. +22
    -0
      example-clients/jack_control
  84. +9
    -8
      example-clients/latent_client.c
  85. +777
    -0
      example-clients/midi_latency_test.c
  86. +1
    -1
      example-clients/showtime.c
  87. +1
    -1
      example-clients/thru_client.c
  88. +23
    -23
      example-clients/tw.c
  89. +1
    -1
      example-clients/wait.c
  90. +1
    -0
      example-clients/wscript
  91. +9
    -9
      example-clients/zombie.c
  92. +4
    -4
      linux/alsa/JackAlsaAdapter.cpp
  93. +34
    -17
      linux/alsa/JackAlsaDriver.cpp
  94. +2
    -0
      linux/alsa/JackAlsaDriver.h
  95. +2
    -2
      linux/alsa/alsa_driver.c
  96. +614
    -0
      linux/alsarawmidi/JackALSARawMidiDriver.cpp
  97. +98
    -0
      linux/alsarawmidi/JackALSARawMidiDriver.h
  98. +143
    -0
      linux/alsarawmidi/JackALSARawMidiInputPort.cpp
  99. +62
    -0
      linux/alsarawmidi/JackALSARawMidiInputPort.h
  100. +172
    -0
      linux/alsarawmidi/JackALSARawMidiOutputPort.cpp

+ 25
- 0
ChangeLog View File

@@ -34,6 +34,31 @@ Valerio Pilo
Jackdmp changes log
---------------------------

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

* Correct driver lifetime management.

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

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

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

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

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

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

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

* Version 1.9.8 started.

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

* Synchronize JackWeakAPI.cpp with new APIs.


+ 2
- 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,7 @@ Note : To experiment with the -S option, jackdmp must be launched in a console.
1.9.4 : Solaris boomer backend now working in capture or playback only mode. Add a -G parameter in CoreAudio backend (the computation value in RT thread expressed as percent of period). Use SNDCTL_DSP_SYNCGROUP/SNDCTL_DSP_SYNCSTART API to synchronize input and output in Solaris boomer backend. Big endian bug fix in memops.c. Fix issues in JackNetDriver::DecodeTransportData and JackNetDriver::Initialize. Correct CPU timing in JackNetDriver, now take cycle begin time after Read. Simplify transport in NetJack2: master only can control transport. Change CoreAudio notification thread setup for OSX Snow Leopard. Correct server temporary mode : now set a global and quit after server/client message handling is finished. Add a string parameter to server ==> client notification, add a new JackInfoShutdownCallback type. CoreAudio backend now issue a JackInfoShutdownCallback when an unrecoverable error is detected (sampling rate change, stream configuration changeÉ). Correct jackdmp.cpp (failures case were not correct..). Improve JackCoreAudioDriver code. Raise default port number to 2048. Correct JackProcessSync::LockedTimedWait. Correct JACK_MESSAGE_SIZE value, particularly in OSX RPC code. Now start server channel thread only when backend has been started (so in JackServer::Start). Should solve race conditions at start time. jack_verbose moved to JackGlobals class. Improve aggregate device management in JackCoreAudioDriver : now a "private" device only and cleanup properly. Aggregate device code added to JackCoreAudioAdapter. Implement "hog mode" (exclusive access of the audio device) in JackCoreAudioDriver. Fix jack_set_sample_rate_callback to have he same behavior as in JACK1. Dynamic system version detection in JackCoreAudioDriver to either create public or private aggregate device. In JackCoreAudioDriver, force the SR value to the wanted one *before* creating aggregate device (otherwise creation will fail). In JackCoreAudioDriver, better cleanup of AD when intermediate open failure. In JackCoreAudioDriver::Start, wait for the audio driver to effectively start (use the MeasureCallback). In JackCoreAudioDriver, improve management of input/output channels: -1 is now used internally to indicate a wanted max value. In JackCoreAudioDriver::OpenAUHAL, correct stream format setup and cleanup. Correct crash bug in JackAudioAdapterInterface when not input is used in adapter (temporary fixÉ). Sync JackCoreAudioAdapter code on JackCoreAudioDriver one. JACK_SCHED_POLICY switched to SCHED_FIFO. Now can aggregate device that are themselves AD. No reason to make jack_on_shutdown deprecated, so revert the incorrect change. Thread AcquireRealTime and DropRealTime were (incorrectly) using fThread field. Use pthread_self()) (or GetCurrentThread() on Windows) to get the calling thread. Correctly save and restore RT mode state in freewheel mode. Correct freewheel code on client side. Fix AcquireRealTime and DropRealTime: now distinguish when called from another thread (AcquireRealTime/DropRealTime) and from the thread itself (AcquireSelfRealTime/DropSelfRealTime). Correct JackPosixThread::StartImp : thread priority setting now done in the RT case only. Correct JackGraphManager::GetBuffer for the "client loop with one connection" case : buffer must be copied. Correct JackInfoShutdownCallback prototype, two new JackClientProcessFailure and JackClientZombie JackStatus code. Correct JackCoreAudio driver when empty strings are given as -C, -P or -d parameter. Better memory allocation error checking on client (library) side. Better memory allocation error checking in ringbuffer.c, weak import improvements. Memory allocation error checking for jack_client_new and jack_client_open (server and client side). Memory allocation error checking in server for RPC. Simplify server temporary mode : now use a JackTemporaryException. Lock/Unlock shared memory segments (to test...). Sync with JACK1 : -r parameter now used for no-realtime, realtime (-R) is now default, usable backend given vie platform. In JackCoreAudio driver, (possibly) clock drift compensation when needed in aggregated devices. In JackCoreAudio driver, clock drift compensation in aggregated devices working. In JackCoreAudio driver, clock drift compensation semantic changed a bit : when on, does not activate if not needed (same clock domain). Sync JackCoreAudioAdapter code with JackCoreAudioDriver.
1.9.5 : Dynamic choice of maximum port number. More robust sample rate change handling code in JackCoreAudioDriver. Devin Anderson patch for Jack FFADO driver issues with lost MIDI bytes between periods (and more). Fix port_rename callback : now both old name and new name are given as parameters. Special code in JackCoreAudio driver to handle completely buggy Digidesign CoreAudio user-land driver. Ensure that client-side message buffer thread calls thread_init callback if/when it is set by the client (backport of JACK1 rev 3838). Check dynamic port-max value. Fix JackCoreMidiDriver::ReadProcAux when ring buffer is full (thanks Devin Anderson). Josh Green ALSA driver capture only patch. When threads are cancelled, the exception has to be rethrown. Use a QUIT notification to properly quit the server channel, the server channel thread can then be 'stopped' instead of 'canceled'. Mario Lang alsa_io time calculation overflow patch. Shared memory manager was calling abort in case of fatal error, now return an error in caller. Change JackEngineProfiling and JackAudioAdapterInterface gnuplot scripts to output SVG instead of PDF.
1.9.6 : Improve JackCoreAudioDriver and JackCoreAudioAdapter : when no devices are described, takes default input and output and aggregate them.Correct JackGraphManager::DeactivatePort. Correct JackMachServerChannel::Execute : keep running even in error cases. Raise JACK_PROTOCOL_VERSION number. Arnold Krille firewire patch. Raise JACK_DRIVER_PARAM_STRING_MAX and JACK_PARAM_STRING_MAX to 127 otherwise some audio drivers cannot be loaded on OSX. Fix some file header to have library side code use LGPL. On Windows, now use TRE library for regexp (BSD license instead of GPL license). ffado-portname-sync.patch from ticket #163 applied. Remove call to exit in library code. Make jack_connect/jack_disconnect wait for effective port connection/disconnection. Add tests to validate intclient.h API. On Linux, inter-process synchronization primitive switched to POSIX semaphore. In JackCoreAudioDriver, move code called in MeasureCallback to be called once in IO thread. David Garcia Garzon netone patch. Fix from Fernando Lopez-Lezcano for compilation on fc13. Fix JackPosixSemaphore::TimedWait : same behavior as JackPosixSemaphore::Wait regarding EINTR. David Garcia Garzon unused_pkt_buf_field_jack2 netone patch. Arnold Krille firewire snooping patch. Jan Engelhardt patch for get_cycles on SPARC. Adrian Knoth hurd.patch, kfreebsd-fix.patch and alpha_ia64-sigsegv.patch from ticket 177. Adrian Knoth fix for linux cycle.h (ticket 188). In JackCoreAudioDriver, fix an issue when no value is given for input.
1.9.7 : Sync JackAlsaDriver::alsa_driver_check_card_type with JACK1 backend. Correct JackServer::Open to avoid a race when control API is used on OSX. Improve backend error handling: fatal error returned by Read/Write now cause a Process failure (so a thread exit for blocking backends). Recoverable ones (XRuns..) are now treated internally in ALSA, FreeBob and FFADO backends. In jackdmp.cpp, jackctl_setup_signals moved before jackctl_server_start. Correct symbols export in backends on OSX. ALSA backend : suspend/resume handling. Correct dummy driver. Adrian Knoth jack_lsp patch. Remove JackPortIsActive flag. New latency API implementation. ComputeTotalLatencies now a client/server call. Add latent test client for latency API. Also print playback and capture latency in jack_lsp. jack_client_has_session_callback implementation. Check requested buffer size and limit to 1..8192 - avoids weird behaviour caused by jack_bufsize foobar. jack_port_type_get_buffer_size implementation. Stop using alloca and allocate buffer on the heap for alsa_io. Rename jdelay to jack_iodelay as per Fons' request. Call buffer size callback in activate (actually this is done on client side in the RT thread Init method). Add jack_midi_dump client. Synchronize net JACK1 with JACK1 version. Synchronize jack_connect/jack_disconnect with JACK1 version. Correct JackNetMaster::SetBufferSize. Use jack_default_audio_sample_t instead of float consistently, fix ticket #201. -X now allows to add several slave backends, add -I to load several internal clients. Rework internal slave driver management, JackServerGlobals now handle same parameters as jackdmp. Correct JackEngine::NotifyGraphReorder, update JackDebugClient with latest API. Devin Anderson server-ctl-proposal branch merged on trunk: improved control API, slave backend reworked. Implement renaming in JackDriver::Open to avoid name collision (thanks Devin Anderson). Correct alsa_driver_restart (thanks Devin Anderson). Correction of jack_connect/jack_disconnect: use of jack_activate and volatile keyword for thread shared variable. Correction of JackNetOneDriver for latest CELT API. Synchronize JackWeakAPI.cpp with new APIs.
1.9.7 : Sync JackAlsaDriver::alsa_driver_check_card_type with JACK1 backend. Correct JackServer::Open to avoid a race when control API is used on OSX. Improve backend error handling: fatal error returned by Read/Write now cause a Process failure (so a thread exit for blocking backends). Recoverable ones (XRuns..) are now treated internally in ALSA, FreeBob and FFADO backends. In jackdmp.cpp, jackctl_setup_signals moved before jackctl_server_start. Correct symbols export in backends on OSX. ALSA backend : suspend/resume handling. Correct dummy driver. Adrian Knoth jack_lsp patch. Remove JackPortIsActive flag. New latency API implementation. ComputeTotalLatencies now a client/server call. Add latent test client for latency API. Also print playback and capture latency in jack_lsp. jack_client_has_session_callback implementation. Check requested buffer size and limit to 1..8192 - avoids weird behaviour caused by jack_bufsize foobar. jack_port_type_get_buffer_size implementation. Stop using alloca and allocate buffer on the heap for alsa_io. Rename jdelay to jack_iodelay as per Fons' request. Call buffer size callback in activate (actually this is done on client side in the RT thread Init method). Add jack_midi_dump client. Synchronize net JACK1 with JACK1 version. Synchronize jack_connect/jack_disconnect with JACK1 version. Correct JackNetMaster::SetBufferSize. Use jack_default_audio_sample_t instead of float consistently, fix ticket #201. -X now allows to add several slave backends, add -I to load several internal clients. Rework internal slave driver management, JackServerGlobals now handle same parameters as jackdmp. Correct JackEngine::NotifyGraphReorder, update JackDebugClient with latest API. Devin Anderson server-ctl-proposal branch merged on trunk: improved control API, slave backend reworked. Implement renaming in JackDriver::Open to avoid name collision (thanks Devin Anderson). Correct alsa_driver_restart (thanks Devin Anderson). Correction of jack_connect/jack_disconnect: use of jack_activate and volatile keyword for thread shared variable. Correction of JackNetOneDriver for latest CELT API. Synchronize JackWeakAPI.cpp with new APIs.
This is a work in progress but the implementation is now stable enough to be tested. jackdmp has been used successfully with the following applications : Ardour, Hydrogen, Jamin, QjackCtl, Jack-Rack, SooperLooper, AlsaPlayer...


+ 89
- 33
common/JackAudioDriver.cpp View File

@@ -44,12 +44,17 @@ JackAudioDriver::~JackAudioDriver()

int JackAudioDriver::SetBufferSize(jack_nframes_t buffer_size)
{
// Update engine and graph manager state
fEngineControl->fBufferSize = buffer_size;
fGraphManager->SetBufferSize(buffer_size);
fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec
if (!fEngineControl->fTimeOut)
fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs);
return 0;

UpdateLatencies();

// Redirect on slaves drivers...
return JackDriver::SetBufferSize(buffer_size);
}

int JackAudioDriver::SetSampleRate(jack_nframes_t sample_rate)
@@ -58,7 +63,8 @@ int JackAudioDriver::SetSampleRate(jack_nframes_t sample_rate)
fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // in microsec
if (!fEngineControl->fTimeOut)
fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs);
return 0;

return JackDriver::SetSampleRate(sample_rate);
}

int JackAudioDriver::Open(jack_nframes_t buffer_size,
@@ -76,6 +82,9 @@ int JackAudioDriver::Open(jack_nframes_t buffer_size,
fCaptureChannels = inchannels;
fPlaybackChannels = outchannels;
fWithMonitorPorts = monitor;
memset(fCapturePortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM);
memset(fPlaybackPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM);
memset(fMonitorPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM);
return JackDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency);
}

@@ -92,16 +101,39 @@ int JackAudioDriver::Open(bool capturing,
fCaptureChannels = inchannels;
fPlaybackChannels = outchannels;
fWithMonitorPorts = monitor;
memset(fCapturePortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM);
memset(fPlaybackPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM);
memset(fMonitorPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM);
return JackDriver::Open(capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency);
}

void JackAudioDriver::UpdateLatencies()
{
jack_latency_range_t range;

for (int i = 0; i < fCaptureChannels; i++) {
range.max = range.min = fEngineControl->fBufferSize;
fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range);
}

for (int i = 0; i < fPlaybackChannels; i++) {
if (! fEngineControl->fSyncMode) {
range.max = range.min = fEngineControl->fBufferSize * 2;
}
fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range);
if (fWithMonitorPorts) {
range.min = range.max = fEngineControl->fBufferSize;
fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &range);
}
}
}

int JackAudioDriver::Attach()
{
JackPort* port;
jack_port_id_t port_index;
char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
jack_latency_range_t range;
int i;

jack_log("JackAudioDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate);
@@ -115,8 +147,6 @@ int JackAudioDriver::Attach()
}
port = fGraphManager->GetPort(port_index);
port->SetAlias(alias);
range.min = range.max = fEngineControl->fBufferSize + fCaptureLatency;
port->SetLatencyRange(JackCaptureLatency, &range);
fCapturePortList[i] = port_index;
jack_log("JackAudioDriver::Attach fCapturePortList[i] port_index = %ld", port_index);
}
@@ -130,9 +160,6 @@ int JackAudioDriver::Attach()
}
port = fGraphManager->GetPort(port_index);
port->SetAlias(alias);
// Add more latency if "async" mode is used...
range.min = range.max = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + fPlaybackLatency;
port->SetLatencyRange(JackPlaybackLatency, &range);
fPlaybackPortList[i] = port_index;
jack_log("JackAudioDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index);

@@ -144,14 +171,12 @@ int JackAudioDriver::Attach()
jack_error("Cannot register monitor port for %s", name);
return -1;
} else {
port = fGraphManager->GetPort(port_index);
range.min = range.max = fEngineControl->fBufferSize;
port->SetLatencyRange(JackCaptureLatency, &range);
fMonitorPortList[i] = port_index;
fMonitorPortList[i] = port_index;
}
}
}

UpdateLatencies();
return 0;
}

@@ -193,9 +218,9 @@ int JackAudioDriver::ProcessNull()
JackDriver::CycleTakeBeginTime();

if (fEngineControl->fSyncMode) {
ProcessGraphSync();
ProcessGraphSyncMaster();
} else {
ProcessGraphAsync();
ProcessGraphAsyncMaster();
}

// Keep end cycle time
@@ -230,9 +255,9 @@ int JackAudioDriver::ProcessAsync()

// Process graph
if (fIsMaster) {
ProcessGraphAsync();
ProcessGraphAsyncMaster();
} else {
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable);
ProcessGraphAsyncSlave();
}

// Keep end cycle time
@@ -255,12 +280,12 @@ int JackAudioDriver::ProcessSync()

// Process graph
if (fIsMaster) {
if (ProcessGraphSync() < 0) {
if (ProcessGraphSyncMaster() < 0) {
jack_error("JackAudioDriver::ProcessSync: process error, skip cycle...");
goto end;
}
} else {
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) {
if (ProcessGraphSyncSlave() < 0) {
jack_error("JackAudioDriver::ProcessSync: process error, skip cycle...");
goto end;
}
@@ -279,27 +304,50 @@ end:
return 0;
}

void JackAudioDriver::ProcessGraphAsync()
void JackAudioDriver::ProcessGraphAsyncMaster()
{
// fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle
if (!fEngine->Process(fBeginDateUst, fEndDateUst))
jack_error("JackAudioDriver::ProcessGraphAsync: Process error");
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable);
if (ProcessSlaves() < 0)
jack_error("JackAudioDriver::ProcessGraphAsync: ProcessSlaves error");
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: Process error");

if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0)
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ResumeRefNum error");

if (ProcessReadSlaves() < 0)
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessReadSlaves error");

if (ProcessWriteSlaves() < 0)
jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessWriteSlaves error");
}

void JackAudioDriver::ProcessGraphAsyncSlave()
{
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0)
jack_error("JackAudioDriver::ProcessGraphAsyncSlave: ResumeRefNum error");
}

int JackAudioDriver::ProcessGraphSync()
int JackAudioDriver::ProcessGraphSyncMaster()
{
int res = 0;

// fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle
if (fEngine->Process(fBeginDateUst, fEndDateUst)) {
fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable);
if (ProcessSlaves() < 0) {
jack_error("JackAudioDriver::ProcessGraphSync: ProcessSlaves error, engine may now behave abnormally!!");
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) {
jack_error("JackAudioDriver::ProcessGraphSyncMaster: ResumeRefNum error");
res = -1;
}

if (ProcessReadSlaves() < 0) {
jack_error("JackAudioDriver::ProcessGraphSync: ProcessReadSlaves error, engine may now behave abnormally!!");
res = -1;
}

if (ProcessWriteSlaves() < 0) {
jack_error("JackAudioDriver::ProcessGraphSync: ProcessWriteSlaves error, engine may now behave abnormally!!");
res = -1;
}

if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs) < 0) {
jack_error("JackAudioDriver::ProcessGraphSync: SuspendRefNum error, engine may now behave abnormally!!");
res = -1;
@@ -312,6 +360,11 @@ int JackAudioDriver::ProcessGraphSync()
return res;
}

int JackAudioDriver::ProcessGraphSyncSlave()
{
return fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable);
}

int JackAudioDriver::Start()
{
int res = JackDriver::Start();
@@ -342,20 +395,23 @@ void JackAudioDriver::WaitUntilNextCycle()

jack_default_audio_sample_t* JackAudioDriver::GetInputBuffer(int port_index)
{
assert(fCapturePortList[port_index]);
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize);
return fCapturePortList[port_index]
? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize)
: NULL;
}

jack_default_audio_sample_t* JackAudioDriver::GetOutputBuffer(int port_index)
{
assert(fPlaybackPortList[port_index]);
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize);
return fPlaybackPortList[port_index]
? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize)
: NULL;
}

jack_default_audio_sample_t* JackAudioDriver::GetMonitorBuffer(int port_index)
{
assert(fPlaybackPortList[port_index]);
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize);
return fPlaybackPortList[port_index]
? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize)
: NULL;
}

int JackAudioDriver::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)


+ 7
- 2
common/JackAudioDriver.h View File

@@ -35,8 +35,12 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver

protected:

void ProcessGraphAsync();
int ProcessGraphSync();
void ProcessGraphAsyncMaster();
void ProcessGraphAsyncSlave();

int ProcessGraphSyncMaster();
int ProcessGraphSyncSlave();

void WaitUntilNextCycle();

virtual int ProcessAsync();
@@ -58,6 +62,7 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver
jack_default_audio_sample_t* GetMonitorBuffer(int port_index);

void HandleLatencyCallback(int status);
void UpdateLatencies();

public:



+ 2
- 1
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
{


+ 0
- 1
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>



+ 1
- 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
{


+ 2
- 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

@@ -71,7 +71,7 @@
#define SOCKET_TIME_OUT 5 // in sec
#define DRIVER_OPEN_TIMEOUT 5 // in sec
#define FREEWHEEL_DRIVER_TIMEOUT 10 // in sec
#define DRIVER_TIMEOUT_FACTOR 10
#define DRIVER_TIMEOUT_FACTOR 10


#define NO_PORT 0xFFFE


+ 23
- 8
common/JackControlAPI.cpp View File

@@ -101,7 +101,7 @@ struct jackctl_driver
jack_driver_desc_t * desc_ptr;
JSList * parameters;
JSList * set_parameters;
JackDriverInfo* info;
JSList * infos;
};

struct jackctl_internal
@@ -215,7 +215,8 @@ bool
jackctl_add_driver_parameters(
struct jackctl_driver * driver_ptr)
{
uint32_t i;
unsigned int i;

union jackctl_parameter_value jackctl_value;
jackctl_param_type_t jackctl_type;
struct jackctl_parameter * parameter_ptr;
@@ -308,6 +309,7 @@ jackctl_drivers_load(
driver_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data;
driver_ptr->parameters = NULL;
driver_ptr->set_parameters = NULL;
driver_ptr->infos = NULL;

if (!jackctl_add_driver_parameters(driver_ptr))
{
@@ -377,6 +379,7 @@ jackctl_internals_load(
internal_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data;
internal_ptr->parameters = NULL;
internal_ptr->set_parameters = NULL;
internal_ptr->refnum = -1;

if (!jackctl_add_driver_parameters((struct jackctl_driver *)internal_ptr))
{
@@ -1214,8 +1217,13 @@ EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver
jack_error("cannot add a slave in a running server");
return false;
} else {
driver_ptr->info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, driver_ptr->set_parameters);
return (driver_ptr->info != 0);
JackDriverInfo* info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, driver_ptr->set_parameters);
if (info) {
driver_ptr->infos = jack_slist_append(driver_ptr->infos, info);
return true;
} else {
return false;
}
}
} else {
return false;
@@ -1228,10 +1236,17 @@ EXPORT bool jackctl_server_remove_slave(jackctl_server * server_ptr, jackctl_dri
if (server_ptr->engine->IsRunning()) {
jack_error("cannot remove a slave from a running server");
return false;
} else {
server_ptr->engine->RemoveSlave(driver_ptr->info);
delete driver_ptr->info;
return true;
} else {
if (driver_ptr->infos) {
JackDriverInfo* info = (JackDriverInfo*)driver_ptr->infos->data;
assert(info);
driver_ptr->infos = jack_slist_remove(driver_ptr->infos, info);
server_ptr->engine->RemoveSlave(info);
delete info;
return true;
} else {
return false;
}
}
} else {
return false;


+ 14
- 13
common/JackControlAPI.h View File

@@ -27,7 +27,8 @@

#ifdef WIN32
#ifdef __MINGW32__
#include <sys/types.h>
#include <sys/types.h>
typedef _sigset_t sigset_t;
#else
typedef HANDLE sigset_t;
#endif
@@ -169,11 +170,11 @@ jackctl_parameter_set_value(
EXPORT union jackctl_parameter_value
jackctl_parameter_get_default_value(
jackctl_parameter_t * parameter);
EXPORT union jackctl_parameter_value
EXPORT union jackctl_parameter_value
jackctl_parameter_get_default_value(
jackctl_parameter *parameter_ptr);
EXPORT bool
jackctl_parameter_has_range_constraint(
jackctl_parameter_t * parameter_ptr);
@@ -210,33 +211,33 @@ EXPORT bool
jackctl_parameter_constraint_is_fake_value(
jackctl_parameter_t * parameter_ptr);

EXPORT const JSList *
EXPORT const JSList *
jackctl_server_get_internals_list(
jackctl_server *server_ptr);
EXPORT const char *
EXPORT const char *
jackctl_internal_get_name(
jackctl_internal *internal_ptr);
EXPORT const JSList *
EXPORT const JSList *
jackctl_internal_get_parameters(
jackctl_internal *internal_ptr);
EXPORT bool jackctl_server_load_internal(
jackctl_server * server,
jackctl_internal * internal);
EXPORT bool jackctl_server_unload_internal(
jackctl_server * server,
jackctl_internal * internal);
EXPORT bool jackctl_server_add_slave(jackctl_server_t * server,
jackctl_driver_t * driver);

EXPORT bool jackctl_server_remove_slave(jackctl_server_t * server,
jackctl_driver_t * driver);

EXPORT bool
EXPORT bool
jackctl_server_switch_master(jackctl_server_t * server,
jackctl_driver_t * driver);



+ 50
- 10
common/JackDriver.cpp View File

@@ -56,6 +56,7 @@ JackDriver::JackDriver()
fEngine = NULL;
fGraphManager = NULL;
fBeginDateUst = 0;
fDelayedUsecs = 0.f;
fIsMaster = true;
fIsRunning = false;
}
@@ -300,19 +301,42 @@ void JackDriver::RemoveSlave(JackDriverInterface* slave)
fSlaveList.remove(slave);
}

int JackDriver::ProcessSlaves()
int JackDriver::ProcessReadSlaves()
{
int res = 0;
list<JackDriverInterface*>::const_iterator it;
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) {
JackDriverInterface* slave = *it;
if (slave->Process() < 0)
if (slave->ProcessRead() < 0)
res = -1;

}
return res;
}

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

}
return res;
}

int JackDriver::ProcessRead()
{
return 0;
}

int JackDriver::ProcessWrite()
{
return 0;
}

int JackDriver::Process()
{
return 0;
@@ -352,6 +376,12 @@ int JackDriver::Start()
return 0;
}

int JackDriver::Stop()
{
fIsRunning = false;
return 0;
}

int JackDriver::StartSlaves()
{
int res = 0;
@@ -370,12 +400,6 @@ int JackDriver::StartSlaves()
return res;
}

int JackDriver::Stop()
{
fIsRunning = false;
return 0;
}

int JackDriver::StopSlaves()
{
int res = 0;
@@ -395,12 +419,28 @@ bool JackDriver::IsFixedBufferSize()

int JackDriver::SetBufferSize(jack_nframes_t buffer_size)
{
return 0;
int res = 0;

list<JackDriverInterface*>::const_iterator it;
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) {
JackDriverInterface* slave = *it;
if (slave->SetBufferSize(buffer_size) < 0)
res = -1;
}

return res;
}

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

bool JackDriver::Initialize()


+ 18
- 3
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.
@@ -91,10 +92,17 @@ class SERVER_EXPORT JackDriverInterface

virtual void SetMaster(bool onoff) = 0;
virtual bool GetMaster() = 0;

virtual void AddSlave(JackDriverInterface* slave) = 0;
virtual void RemoveSlave(JackDriverInterface* slave) = 0;

virtual std::list<JackDriverInterface*> GetSlaves() = 0;
virtual int ProcessSlaves() = 0;

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

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

virtual bool IsRealTime() const = 0;
virtual bool IsRunning() const = 0;
@@ -159,11 +167,11 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface

void AddSlave(JackDriverInterface* slave);
void RemoveSlave(JackDriverInterface* slave);

std::list<JackDriverInterface*> GetSlaves()
{
return fSlaveList;
}
int ProcessSlaves();

virtual int Open();

@@ -200,10 +208,17 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface
virtual int Write();

virtual int Start();
virtual int StartSlaves();
virtual int Stop();

virtual int StartSlaves();
virtual int StopSlaves();

int ProcessReadSlaves();
int ProcessWriteSlaves();

int ProcessRead();
int ProcessWrite();

virtual bool IsFixedBufferSize();
virtual int SetBufferSize(jack_nframes_t buffer_size);
virtual int SetSampleRate(jack_nframes_t sample_rate);


+ 18
- 4
common/JackDriverLoader.cpp View File

@@ -531,7 +531,7 @@ jack_drivers_load (JSList * drivers) {
HANDLE file;
const char * ptr = NULL;
JSList * driver_list = NULL;
jack_driver_desc_t * desc;
jack_driver_desc_t * desc = NULL;

if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
// for WIN32 ADDON_DIR is defined in JackConstants.h as relative path
@@ -551,6 +551,11 @@ jack_drivers_load (JSList * drivers) {
}

do {
/* check the filename is of the right format */
if (strncmp ("jack_", filedata.cFileName, 5) != 0) {
continue;
}

ptr = strrchr (filedata.cFileName, '.');
if (!ptr) {
continue;
@@ -560,6 +565,11 @@ jack_drivers_load (JSList * drivers) {
continue;
}

/* check if dll is an internal client */
if (check_symbol(filedata.cFileName, "jack_internal_initialize")) {
continue;
}

desc = jack_get_descriptor (drivers, filedata.cFileName, "driver_get_descriptor");
if (desc) {
driver_list = jack_slist_append (driver_list, desc);
@@ -586,7 +596,7 @@ jack_drivers_load (JSList * drivers) {
const char * ptr;
int err;
JSList * driver_list = NULL;
jack_driver_desc_t * desc;
jack_driver_desc_t * desc = NULL;

const char* driver_dir;
if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
@@ -618,8 +628,12 @@ jack_drivers_load (JSList * drivers) {
continue;
}

desc = jack_get_descriptor (drivers, dir_entry->d_name, "driver_get_descriptor");
/* check if dll is an internal client */
if (check_symbol(dir_entry->d_name, "jack_internal_initialize")) {
continue;
}

desc = jack_get_descriptor (drivers, dir_entry->d_name, "driver_get_descriptor");
if (desc) {
driver_list = jack_slist_append (driver_list, desc);
} else {
@@ -669,7 +683,7 @@ jack_internals_load (JSList * internals) {
file = (HANDLE )FindFirstFile(dll_filename, &filedata);

if (file == INVALID_HANDLE_VALUE) {
jack_error("error");
jack_error("could not open driver directory %s", driver_dir);
return NULL;
}



+ 1
- 1
common/JackDriverLoader.h View File

@@ -30,7 +30,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
typedef jack_driver_desc_t * (*JackDriverDescFunction) ();
typedef Jack::JackDriverClientInterface* (*driverInitialize) (Jack::JackLockedEngine*, Jack::JackSynchro*, const JSList*);

class JackDriverInfo
class SERVER_EXPORT JackDriverInfo
{

private:


+ 1
- 0
common/JackDummyDriver.cpp View File

@@ -78,6 +78,7 @@ int JackDummyDriver::Process()

int JackDummyDriver::SetBufferSize(jack_nframes_t buffer_size)
{
// Generic change, never fails
JackAudioDriver::SetBufferSize(buffer_size);
fWaitTime = (unsigned long)((((float)buffer_size) / ((float)fEngineControl->fSampleRate)) * 1000000.0f);
return 0;


+ 1
- 0
common/JackExternalClient.cpp View File

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


+ 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


+ 8
- 0
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
{


+ 2
- 2
common/JackLibSampleRateResampler.cpp View File

@@ -148,7 +148,7 @@ unsigned int JackLibSampleRateResampler::WriteResample(jack_default_audio_sample

res = src_process(fResampler, &src_data);
if (res != 0) {
jack_error("JackLibSampleRateResampler::ReadResample ratio = %f err = %s", fRatio, src_strerror(res));
jack_error("JackLibSampleRateResampler::WriteResample ratio = %f err = %s", fRatio, src_strerror(res));
return 0;
}

@@ -167,7 +167,7 @@ unsigned int JackLibSampleRateResampler::WriteResample(jack_default_audio_sample

if (read_frames < frames) {
jack_error("Input available = %ld", available_frames);
jack_error("JackLibSampleRateResampler::ReadResample error read_frames = %ld", read_frames);
jack_error("JackLibSampleRateResampler::WriteResample error read_frames = %ld", read_frames);
}

return read_frames;


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



+ 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


+ 103
- 0
common/JackMidiAsyncQueue.cpp View File

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

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

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

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

*/

#include <new>

#include "JackMidiAsyncQueue.h"

using Jack::JackMidiAsyncQueue;

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

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

jack_midi_event_t *
JackMidiAsyncQueue::DequeueEvent()
{
jack_midi_event_t *event = 0;
if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) {
event = &dequeue_event;
jack_ringbuffer_read(info_ring, (char *) &(event->time),
sizeof(jack_nframes_t));
size_t size;
jack_ringbuffer_read(info_ring, (char *) &size, sizeof(size_t));
event->buffer = data_buffer;
event->size = size;
jack_ringbuffer_data_t vector[2];
jack_ringbuffer_get_read_vector(byte_ring, vector);
size_t size1 = vector[0].len;
memcpy(data_buffer, vector[0].buf, size1 * sizeof(jack_midi_data_t));
if (size1 < size) {
memcpy(data_buffer + size1, vector[1].buf,
(size - size1) * sizeof(jack_midi_data_t));
}
jack_ringbuffer_read_advance(byte_ring,
size * sizeof(jack_midi_data_t));
}
return event;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
if (size > max_bytes) {
return BUFFER_TOO_SMALL;
}
if (! ((jack_ringbuffer_write_space(info_ring) >= INFO_SIZE) &&
(jack_ringbuffer_write_space(byte_ring) >=
(size * sizeof(jack_midi_data_t))))) {
return BUFFER_FULL;
}
jack_ringbuffer_write(byte_ring, (const char *) buffer,
size * sizeof(jack_midi_data_t));
jack_ringbuffer_write(info_ring, (const char *) (&time),
sizeof(jack_nframes_t));
jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t));
return OK;
}

size_t
JackMidiAsyncQueue::GetAvailableSpace()
{
return jack_ringbuffer_write_space(info_ring) < INFO_SIZE ? 0 :
max_bytes - jack_ringbuffer_read_space(byte_ring);
}

+ 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 two threads to pass MIDI
* messages between two threads (though it can also be used to buffer
* events internally). This is especially useful if the MIDI API
* you're attempting to interface with doesn't provide the ability to
* schedule MIDI events ahead of time and/or has blocking send/receive
* calls, as it allows a separate thread to handle input/output while the
* JACK process thread copies events from a `JackMidiBufferReadQueue` to
* this queue, or from this queue to a `JackMidiBufferWriteQueue`.
*/

class SERVER_EXPORT JackMidiAsyncQueue:
public JackMidiReadQueue, public JackMidiWriteQueue {

private:

static const size_t INFO_SIZE =
sizeof(jack_nframes_t) + sizeof(size_t);

jack_ringbuffer_t *byte_ring;
jack_midi_data_t *data_buffer;
jack_midi_event_t dequeue_event;
jack_ringbuffer_t *info_ring;
size_t max_bytes;

public:

using JackMidiWriteQueue::EnqueueEvent;

/**
* Creates a new asynchronous MIDI message queue. The queue can store
* up to `max_messages` MIDI messages and up to `max_bytes` of MIDI
* data before it starts rejecting messages.
*/

JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024);

virtual ~JackMidiAsyncQueue();

/**
* Dequeues and returns a MIDI event. Returns '0' if there are no MIDI
* events available. This method may be overridden.
*/

virtual jack_midi_event_t *
DequeueEvent();

/**
* Enqueues the MIDI event specified by the arguments. The return
* value indiciates whether or not the event was successfully enqueued.
* This method may be overridden.
*/

virtual EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

/**
* Returns the maximum size event that can be enqueued right *now*.
*/

size_t
GetAvailableSpace();

};

}

#endif

+ 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

+ 90
- 28
common/JackMidiDriver.cpp View File

@@ -34,19 +34,10 @@ JackMidiDriver::JackMidiDriver(const char* name, const char* alias, JackLockedEn
: JackDriver(name, alias, engine, table),
fCaptureChannels(0),
fPlaybackChannels(0)
{
for (int i = 0; i < DRIVER_PORT_NUM; i++) {
fRingBuffer[i] = NULL;
}
}
{}

JackMidiDriver::~JackMidiDriver()
{
for (int i = 0; i < fCaptureChannels; i++) {
if (fRingBuffer[i])
jack_ringbuffer_free(fRingBuffer[i]);
}
}
{}

int JackMidiDriver::Open(bool capturing,
bool playing,
@@ -60,11 +51,6 @@ int JackMidiDriver::Open(bool capturing,
{
fCaptureChannels = inchannels;
fPlaybackChannels = outchannels;

for (int i = 0; i < fCaptureChannels; i++) {
fRingBuffer[i] = jack_ringbuffer_create(sizeof(jack_default_audio_sample_t) * BUFFER_SIZE_MAX);
}

return JackDriver::Open(capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency);
}

@@ -104,6 +90,7 @@ int JackMidiDriver::Attach()
jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index);
}

UpdateLatencies();
return 0;
}

@@ -133,33 +120,108 @@ int JackMidiDriver::Write()
return 0;
}

void JackMidiDriver::UpdateLatencies()
{
jack_latency_range_t range;

for (int i = 0; i < fCaptureChannels; i++) {
range.max = range.min = fEngineControl->fBufferSize;
fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range);
}

for (int i = 0; i < fPlaybackChannels; i++) {
if (! fEngineControl->fSyncMode) {
range.max = range.min = fEngineControl->fBufferSize * 2;
}
fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range);
}
}

int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size)
{
UpdateLatencies();
return 0;
}

int JackMidiDriver::ProcessNull()
{
return 0;
}

int JackMidiDriver::Process()
int JackMidiDriver::ProcessRead()
{
return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync();
}

int JackMidiDriver::ProcessWrite()
{
return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync();
}

int JackMidiDriver::ProcessReadSync()
{
int res = 0;

// Read input buffers for the current cycle
if (Read() < 0) {
jack_error("JackMidiDriver::Process: read error, skip cycle");
return 0; // Skip cycle, but continue processing...
jack_error("JackMidiDriver::ProcessReadSync: read error, skip cycle");
res = -1;
}

fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable);
if (fEngineControl->fSyncMode) {
if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) {
jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error");
return -1;
}
if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) {
jack_error("JackMidiDriver::ProcessReadSync - ResumeRefNum error");
res = -1;
}

return res;
}

int JackMidiDriver::ProcessWriteSync()
{
int res = 0;

if (fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable,
DRIVER_TIMEOUT_FACTOR *
fEngineControl->fTimeOutUsecs) < 0) {
jack_error("JackMidiDriver::ProcessWriteSync - SuspendRefNum error");
res = -1;
}

// Write output buffers for the current cycle
// Write output buffers from the current cycle
if (Write() < 0) {
jack_error("JackMidiDriver::Process: write error, skip cycle");
return 0; // Skip cycle, but continue processing...
jack_error("JackMidiDriver::ProcessWriteSync - Write error");
res = -1;
}

return res;
}

int JackMidiDriver::ProcessReadAsync()
{
int res = 0;

// Read input buffers for the current cycle
if (Read() < 0) {
jack_error("JackMidiDriver::ProcessReadAsync: read error, skip cycle");
res = -1;
}

// Write output buffers from the previous cycle
if (Write() < 0) {
jack_error("JackMidiDriver::ProcessReadAsync - Write error");
res = -1;
}

if (fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable) < 0) {
jack_error("JackMidiDriver::ProcessReadAsync - ResumeRefNum error");
res = -1;
}

return res;
}

int JackMidiDriver::ProcessWriteAsync()
{
return 0;
}



+ 18
- 8
common/JackMidiDriver.h View File

@@ -39,15 +39,21 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver

int fCaptureChannels;
int fPlaybackChannels;
jack_ringbuffer_t* fRingBuffer[DRIVER_PORT_NUM];

jack_port_id_t fCapturePortList[DRIVER_PORT_NUM];
jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM];
JackMidiBuffer* GetInputBuffer(int port_index);
JackMidiBuffer* GetOutputBuffer(int port_index);

virtual int ProcessReadSync();
virtual int ProcessWriteSync();

virtual int ProcessReadAsync();
virtual int ProcessWriteAsync();

virtual void UpdateLatencies();

public:

JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table);
@@ -62,16 +68,20 @@ class SERVER_EXPORT JackMidiDriver : public JackDriver
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency);
virtual int Process();

virtual int SetBufferSize(jack_nframes_t buffer_size);

virtual int ProcessRead();
virtual int ProcessWrite();

virtual int ProcessNull();

virtual int Attach();
virtual int Detach();
virtual int Read();
virtual int Write();
};

} // end of namespace


+ 4
- 3
common/JackMidiPort.cpp View File

@@ -55,7 +55,6 @@ SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time
lost_events++;
return 0;
}

JackMidiEvent* event = &events[event_count++];
event->time = time;
event->size = size;
@@ -90,7 +89,7 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count
{
JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer);
if (!mix->IsValid()) {
jack_error("MIDI: invalid mix buffer");
jack_error("Jack::MidiBufferMixdown - invalid mix buffer");
return;
}
mix->Reset(nframes);
@@ -98,8 +97,10 @@ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count
int event_count = 0;
for (int i = 0; i < src_count; ++i) {
JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
if (!buf->IsValid())
if (!buf->IsValid()) {
jack_error("Jack::MidiBufferMixdown - invalid source buffer");
return;
}
buf->mix_index = 0;
event_count += buf->event_count;
mix->lost_events += buf->lost_events;


+ 300
- 0
common/JackMidiRawInputWriteQueue.cpp View File

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

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

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

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

*/

#include <cassert>
#include <memory>
#include <new>

#include "JackMidiRawInputWriteQueue.h"

using Jack::JackMidiRawInputWriteQueue;

JackMidiRawInputWriteQueue::
JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue,
size_t max_packet_data, size_t max_packets)
{
packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets);
std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue);
input_ring = jack_ringbuffer_create(max_packet_data + 1);
if (! input_ring) {
throw std::bad_alloc();
}
jack_ringbuffer_mlock(input_ring);
Clear();
expected_bytes = 0;
event_pending = false;
packet = 0;
status_byte = 0;
this->write_queue = write_queue;
packet_queue_ptr.release();
}

JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue()
{
jack_ringbuffer_free(input_ring);
delete packet_queue;
}

void
JackMidiRawInputWriteQueue::Clear()
{
jack_ringbuffer_reset(input_ring);
total_bytes = 0;
unbuffered_bytes = 0;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
return packet_queue->EnqueueEvent(time, size, buffer);
}

void
JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes,
size_t total_bytes)
{
jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI "
"byte(s) of a %d byte message could not be buffered. The "
"message has been dropped.", unbuffered_bytes, total_bytes);
}

void
JackMidiRawInputWriteQueue::HandleEventLoss(jack_midi_event_t *event)
{
jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI "
"event scheduled for frame '%d' could not be processed because "
"the write queue cannot accomodate an event of that size. The "
"event has been discarded.", event->size, event->time);
}

void
JackMidiRawInputWriteQueue::HandleIncompleteMessage(size_t total_bytes)
{
jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - "
"Discarding %d MIDI byte(s) of an incomplete message. The "
"MIDI cable may have been unplugged.", total_bytes);
}

void
JackMidiRawInputWriteQueue::HandleInvalidStatusByte(jack_midi_data_t byte)
{
jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - "
"Dropping invalid MIDI status byte '%x'.", (unsigned int) byte);
}

void
JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd(size_t total_bytes)
{
jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - "
"Received a sysex end byte without first receiving a sysex "
"start byte. Discarding %d MIDI byte(s). The cable may have "
"been unplugged.", total_bytes);
}

bool
JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time)
{
bool result = ! unbuffered_bytes;
if (! result) {
HandleBufferFailure(unbuffered_bytes, total_bytes);
} else {
size_t size = jack_ringbuffer_read_space(input_ring);
jack_ringbuffer_data_t vector[2];
jack_ringbuffer_get_read_vector(input_ring, vector);
// We don't worry about the second part of the vector, as we reset the
// ringbuffer after each parsed message.
PrepareEvent(time, size, (jack_midi_data_t *) vector[0].buf);
}
Clear();
if (status_byte >= 0xf0) {
expected_bytes = 0;
status_byte = 0;
}
return result;
}

bool
JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time,
jack_midi_data_t byte)
{
event_byte = byte;
PrepareEvent(time, 1, &event_byte);
return true;
}

void
JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
event.buffer = buffer;
event.size = size;
event.time = time;
event_pending = true;
}

jack_nframes_t
JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame)
{
if (event_pending) {
if (! WriteEvent(boundary_frame)) {
return event.time;
}
}
if (! packet) {
packet = packet_queue->DequeueEvent();
}
for (; packet; packet = packet_queue->DequeueEvent()) {
for (; packet->size; (packet->buffer)++, (packet->size)--) {
if (ProcessByte(packet->time, *(packet->buffer))) {
if (! WriteEvent(boundary_frame)) {
(packet->buffer)++;
(packet->size)--;
return event.time;
}
}
}
}
return 0;
}

bool
JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time,
jack_midi_data_t byte)
{
if (byte >= 0xf8) {
// Realtime
if (byte == 0xfd) {
HandleInvalidStatusByte(byte);
return false;
}
return PrepareByteEvent(time, byte);
}
if (byte == 0xf7) {
// Sysex end
if (status_byte == 0xf0) {
RecordByte(byte);
return PrepareBufferedEvent(time);
}
HandleUnexpectedSysexEnd(total_bytes);
Clear();
expected_bytes = 0;
status_byte = 0;
return false;
}
if (byte >= 0x80) {
// Non-realtime status byte
if (total_bytes) {
HandleIncompleteMessage(total_bytes);
Clear();
}
status_byte = byte;
switch (byte & 0xf0) {
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
// Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
expected_bytes = 3;
break;
case 0xc0:
case 0xd0:
// Program Change, Channel Pressure
expected_bytes = 2;
break;
case 0xf0:
switch (byte) {
case 0xf0:
// Sysex
expected_bytes = 0;
break;
case 0xf1:
case 0xf3:
// MTC Quarter Frame, Song Select
expected_bytes = 2;
break;
case 0xf2:
// Song Position
expected_bytes = 3;
break;
case 0xf4:
case 0xf5:
// Undefined
HandleInvalidStatusByte(byte);
expected_bytes = 0;
status_byte = 0;
return false;
case 0xf6:
// Tune Request
bool result = PrepareByteEvent(time, byte);
if (result) {
expected_bytes = 0;
status_byte = 0;
}
return result;
}
}
RecordByte(byte);
return false;
}
// Data byte
if (! status_byte) {
// Data bytes without a status will be discarded.
total_bytes++;
unbuffered_bytes++;
return false;
}
if (! total_bytes) {
// Apply running status.
RecordByte(status_byte);
}
RecordByte(byte);
return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) :
false;
}

void
JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte)
{
if (jack_ringbuffer_write(input_ring, (const char *) &byte, 1) != 1) {
unbuffered_bytes++;
}
total_bytes++;
}

bool
JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame)
{
if ((! boundary_frame) || (event.time < boundary_frame)) {
switch (write_queue->EnqueueEvent(&event)) {
case BUFFER_TOO_SMALL:
HandleEventLoss(&event);
// Fallthrough on purpose
case OK:
event_pending = false;
return true;
default:
// This is here to stop compilers from warning us about not
// handling enumeration values.
;
}
}
return false;
}

+ 170
- 0
common/JackMidiRawInputWriteQueue.h View File

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

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

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

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

*/

#ifndef __JackMidiRawInputWriteQueue__
#define __JackMidiRawInputWriteQueue__

#include "JackMidiAsyncQueue.h"
#include "JackMidiWriteQueue.h"
#include "ringbuffer.h"

namespace Jack {

/**
* This queue enqueues raw, unparsed MIDI packets, and outputs complete
* MIDI messages to a write queue.
*
* Use this queue if the MIDI API you're interfacing with gives you raw
* MIDI bytes that must be parsed.
*/

class SERVER_EXPORT JackMidiRawInputWriteQueue: public JackMidiWriteQueue {

private:

jack_midi_event_t event;
jack_midi_data_t event_byte;
bool event_pending;
size_t expected_bytes;
jack_ringbuffer_t *input_ring;
jack_midi_event_t *packet;
JackMidiAsyncQueue *packet_queue;
jack_midi_data_t status_byte;
size_t total_bytes;
size_t unbuffered_bytes;
JackMidiWriteQueue *write_queue;

void
Clear();

bool
PrepareBufferedEvent(jack_nframes_t time);

bool
PrepareByteEvent(jack_nframes_t time, jack_midi_data_t byte);

void
PrepareEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

bool
ProcessByte(jack_nframes_t time, jack_midi_data_t byte);

void
RecordByte(jack_midi_data_t byte);

bool
WriteEvent(jack_nframes_t boundary_frame);

protected:

/**
* Override this method to specify what happens when there isn't enough
* room in the ringbuffer to contain a parsed event. The default
* method outputs an error message.
*/

virtual void
HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes);

/**
* Override this method to specify what happens when a parsed event
* can't be written to the write queue because the event's size exceeds
* the total possible space in the write queue. The default method
* outputs an error message.
*/

virtual void
HandleEventLoss(jack_midi_event_t *event);

/**
* Override this method to specify what happens when an incomplete MIDI
* message is parsed. The default method outputs an error message.
*/

virtual void
HandleIncompleteMessage(size_t total_bytes);

/**
* Override this method to specify what happens when an invalid MIDI
* status byte is parsed. The default method outputs an error message.
*/

virtual void
HandleInvalidStatusByte(jack_midi_data_t byte);

/**
* Override this method to specify what happens when a sysex end byte
* is parsed without first parsing a sysex begin byte. The default
* method outputs an error message.
*/

virtual void
HandleUnexpectedSysexEnd(size_t total_bytes);

public:

using JackMidiWriteQueue::EnqueueEvent;

/**
* Called to create a new raw input write queue. The `write_queue`
* argument is the queue to write parsed messages to. The optional
* `max_packets` argument specifies the number of packets that can be
* enqueued in the internal queue. The optional `max_packet_data`
* argument specifies the total number of MIDI bytes that can be put in
* the internal queue, AND the maximum size for an event that can be
* written to the write queue.
*/

JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue,
size_t max_packet_data=4096,
size_t max_packets=1024);

~JackMidiRawInputWriteQueue();

EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

/**
* The `Process()` method should be called each time the
* `EnqueueEvent()` method returns `OK`. The `Process()` method will
* return the next frame at which an event should be sent. The return
* value from `Process()` depends upon the result of writing bytes to
* the write queue:
*
* -If the return value is '0', then all *complete* events have been
* sent successfully to the write queue. Don't call `Process()` again
* until another event has been enqueued.
*
* -If the return value is a non-zero value, then it specifies the
* frame that a pending event is scheduled to sent at. If the frame is
* in the future, then `Process()` should be called again at that time;
* otherwise, `Process()` should be called as soon as the write queue
* will accept events again.
*/

jack_nframes_t
Process(jack_nframes_t boundary_frame=0);

};

}

#endif

+ 228
- 0
common/JackMidiRawOutputWriteQueue.cpp View File

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

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

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

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

*/

#include <memory>
#include <new>

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

using Jack::JackMidiRawOutputWriteQueue;

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

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

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

bool
JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent()
{
non_rt_event = non_rt_queue->DequeueEvent();
bool result = non_rt_event != 0;
if (result) {
non_rt_event_time = non_rt_event->time;
running_status = ApplyRunningStatus(non_rt_event, running_status);
}
return result;
}

bool
JackMidiRawOutputWriteQueue::DequeueRealtimeEvent()
{
rt_event = rt_queue->DequeueEvent();
bool result = rt_event != 0;
if (result) {
rt_event_time = rt_event->time;
}
return result;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue :
non_rt_queue;
EnqueueResult result = queue->EnqueueEvent(time, size, buffer);
if (result == OK) {
last_enqueued_message_time = time;
}
return result;
}

void
JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time,
jack_midi_data_t byte)
{
jack_error("JackMidiRawOutputWriteQueue::HandleWriteQueueBug - **BUG** "
"The write queue told us that it couldn't enqueue a 1-byte "
"MIDI event scheduled for frame '%d'. This is probably a bug "
"in the write queue implementation.", time);
}

jack_nframes_t
JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame)
{
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
while (STILL_TIME(current_frame, boundary_frame)) {
if (! non_rt_event) {
DequeueNonRealtimeEvent();
}
if (! rt_event) {
DequeueRealtimeEvent();
}
if (! (non_rt_event || rt_event)) {
return 0;
}
if (! WriteRealtimeEvents(boundary_frame)) {
break;
}
jack_nframes_t non_rt_boundary =
rt_event && STILL_TIME(rt_event_time, boundary_frame) ?
rt_event_time : boundary_frame;
if (! WriteNonRealtimeEvents(non_rt_boundary)) {
break;
}
current_frame = send_queue->GetNextScheduleFrame();
}

// If we get here, that means there is some sort of message available, and
// that either we can't currently write to the write queue or we have
// reached the boundary frame. Return the earliest time that a message is
// scheduled to be sent.

return ! non_rt_event ? rt_event_time :
non_rt_event->size > 1 ? current_frame :
! rt_event ? non_rt_event_time :
non_rt_event_time < rt_event_time ? non_rt_event_time : rt_event_time;
}

bool
JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time,
jack_midi_data_t byte)
{
switch (send_queue->EnqueueEvent(time, 1, &byte)) {
case BUFFER_TOO_SMALL:
HandleWriteQueueBug(time, byte);
case OK:
return true;
default:
// This is here to stop compilers from warning us about not handling
// enumeration values.
;
}
return false;
}

bool
JackMidiRawOutputWriteQueue::
WriteNonRealtimeEvents(jack_nframes_t boundary_frame)
{
if (! non_rt_event) {
if (! DequeueNonRealtimeEvent()) {
return true;
}
}
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
do {

// Send out as much of the non-realtime buffer as we can, save for one
// byte which we will send out when the message is supposed to arrive.

for (; non_rt_event->size > 1;
(non_rt_event->size)--, (non_rt_event->buffer)++) {
if (! STILL_TIME(current_frame, boundary_frame)) {
return true;
}
if (! SendByte(current_frame, *(non_rt_event->buffer))) {
return false;
}
current_frame = send_queue->GetNextScheduleFrame();
}
if (! (STILL_TIME(current_frame, boundary_frame) &&
STILL_TIME(non_rt_event_time, boundary_frame))) {
return true;
}

// There's still time. Try to send the byte.

if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) {
return false;
}
current_frame = send_queue->GetNextScheduleFrame();
if (! DequeueNonRealtimeEvent()) {
break;
}
} while (STILL_TIME(current_frame, boundary_frame));
return true;
}

bool
JackMidiRawOutputWriteQueue::WriteRealtimeEvents(jack_nframes_t boundary_frame)
{
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
if (! rt_event) {
if (! DequeueRealtimeEvent()) {
return true;
}
}
for (;;) {
if (! STILL_TIME(current_frame, boundary_frame)) {
return false;
}

// If:
// -there's still time before we need to send the realtime event
// -there's a non-realtime event available for sending
// -non-realtime data can be scheduled before this event

if ((rt_event_time > current_frame) && non_rt_event &&
((non_rt_event->size > 1) ||
(non_rt_event_time < rt_event_time))) {
return true;
}
if (! SendByte(rt_event_time, *(rt_event->buffer))) {
return false;
}
current_frame = send_queue->GetNextScheduleFrame();
if (! DequeueRealtimeEvent()) {
return true;
}
}
}

+ 147
- 0
common/JackMidiRawOutputWriteQueue.h View File

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

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

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

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

*/

#ifndef __JackMidiRawOutputWriteQueue__
#define __JackMidiRawOutputWriteQueue__

#include "JackMidiAsyncQueue.h"
#include "JackMidiSendQueue.h"

namespace Jack {

/**
* This queue enqueues valid MIDI events and modifies them for raw output
* to a write queue. It has a number of advantages over straight MIDI
* event copying:
*
* -Running status: Status bytes can be omitted when the status byte of the
* current MIDI message is the same as the status byte of the last sent
* MIDI message.
*
* -Realtime messages: Realtime messages are given priority over
* non-realtime messages. Realtime bytes are interspersed with
* non-realtime bytes so that realtime messages can be sent as close as
* possible to the time they're scheduled for sending.
*
* -Time optimization: Bytes in non-realtime messages are sent out early
* when possible, with the last byte of the message being sent out as close
* to the specified event time as possible.
*
* Use this queue if the MIDI API you're interfacing with allows you to
* send raw MIDI bytes.
*/

class SERVER_EXPORT JackMidiRawOutputWriteQueue:
public JackMidiWriteQueue {

private:

jack_nframes_t last_enqueued_message_time;
jack_midi_event_t *non_rt_event;
jack_nframes_t non_rt_event_time;
JackMidiAsyncQueue *non_rt_queue;
jack_midi_event_t *rt_event;
jack_nframes_t rt_event_time;
JackMidiAsyncQueue *rt_queue;
jack_midi_data_t running_status;
JackMidiSendQueue *send_queue;

bool
DequeueNonRealtimeEvent();

bool
DequeueRealtimeEvent();

bool
SendByte(jack_nframes_t time, jack_midi_data_t byte);

bool
WriteNonRealtimeEvents(jack_nframes_t boundary_frame);

bool
WriteRealtimeEvents(jack_nframes_t boundary_frame);

protected:

/**
* Override this method to specify what happens when the write queue
* says that a 1-byte event is too large for its buffer. Basically,
* this should never happen.
*/

virtual void
HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte);

public:

using JackMidiWriteQueue::EnqueueEvent;

/**
* Called to create a new raw write queue. The `send_queue` argument
* is the queue to write raw bytes to. The optional `max_rt_messages`
* argument specifies the number of messages that can be enqueued in
* the internal realtime queue. The optional `max_non_rt_messages`
* argument specifies the number of messages that can be enqueued in
* the internal non-realtime queue. The optional `non_rt_size`
* argument specifies the total number of MIDI bytes that can be put in
* the non-realtime queue.
*/

JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue,
size_t non_rt_size=4096,
size_t max_non_rt_messages=1024,
size_t max_rt_messages=128);

~JackMidiRawOutputWriteQueue();

EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

/**
* The `Process()` method should be called each time the
* `EnqueueEvent()` method returns 'OK'. The `Process()` method will
* return the next frame at which an event should be sent. The return
* value from `Process()` depends upon the result of writing bytes to
* the write queue:
*
* -If the return value is '0', then all events that have been enqueued
* in this queue have been sent successfully to the write queue. Don't
* call `Process()` again until another event has been enqueued.
*
* -If the return value is an earlier frame or the current frame, it
* means that the write queue returned 'BUFFER_FULL', 'ERROR', or
* 'EVENT_EARLY' when this queue attempted to send the next byte, and
* that the byte should have already been sent, or is scheduled to be
* sent *now*. `Process()` should be called again when the write queue
* can enqueue events again successfully. How to determine when this
* will happen is left up to the caller.
*
* -If the return value is in the future, then `Process()` should be
* called again at that time, or after another event is enqueued.
*/

jack_nframes_t
Process(jack_nframes_t boundary_frame=0);

};

}

#endif

+ 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

+ 120
- 0
common/JackMidiUtil.cpp View File

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

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

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

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

*/

#include "JackEngineControl.h"
#include "JackFrameTimer.h"
#include "JackGlobals.h"
#include "JackMidiUtil.h"
#include "JackTime.h"

jack_midi_data_t
Jack::ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer,
jack_midi_data_t running_status)
{

// Stolen and modified from alsa/midi_pack.h

jack_midi_data_t status = **buffer;
if ((status >= 0x80) && (status < 0xf0)) {
if (status == running_status) {
(*buffer)++;
(*size)--;
} else {
running_status = status;
}
} else if (status < 0xf8) {
running_status = 0;
}
return running_status;
}

jack_midi_data_t
Jack::ApplyRunningStatus(jack_midi_event_t *event,
jack_midi_data_t running_status)
{
return ApplyRunningStatus(&(event->size), &(event->buffer),
running_status);
}

jack_nframes_t
Jack::GetCurrentFrame()
{
JackEngineControl *control = GetEngineControl();
JackTimer timer;
control->ReadFrameTime(&timer);
return timer.Time2Frames(GetMicroSeconds(), control->fBufferSize);
}

jack_nframes_t
Jack::GetFramesFromTime(jack_time_t time)
{
JackEngineControl* control = GetEngineControl();
JackTimer timer;
control->ReadFrameTime(&timer);
return timer.Time2Frames(time, control->fBufferSize);
}

jack_nframes_t
Jack::GetLastFrame()
{
return GetEngineControl()->fFrameTimer.ReadCurrentState()->CurFrame();
}

int
Jack::GetMessageLength(jack_midi_data_t status_byte)
{
switch (status_byte & 0xf0) {
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
return 3;
case 0xc0:
case 0xd0:
return 2;
case 0xf0:
switch (status_byte) {
case 0xf0:
return 0;
case 0xf1:
case 0xf3:
return 2;
case 0xf2:
return 3;
case 0xf4:
case 0xf5:
case 0xf7:
case 0xfd:
break;
default:
return 1;
}
}
return -1;
}

jack_time_t
Jack::GetTimeFromFrames(jack_nframes_t frames)
{
JackEngineControl* control = GetEngineControl();
JackTimer timer;
control->ReadFrameTime(&timer);
return timer.Frames2Time(frames, control->fBufferSize);
}

+ 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

+ 23
- 23
common/JackNetAdapter.cpp View File

@@ -164,6 +164,7 @@ namespace Jack
#ifdef JACK_MONITOR
fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize);
#endif
fSocket.Close();

switch ( fThread.GetStatus() )
{
@@ -177,7 +178,7 @@ namespace Jack
}
break;
// Stop when the thread cycle is finished
case JackThread::kRunning:
if ( fThread.Stop() < 0 )
{
@@ -185,11 +186,11 @@ namespace Jack
return -1;
}
break;
default:
break;
}
fSocket.Close();
return 0;
}

@@ -242,16 +243,16 @@ namespace Jack
//set audio adapter parameters
SetAdaptedBufferSize ( fParams.fPeriodSize );
SetAdaptedSampleRate ( fParams.fSampleRate );
// Will do "something" on OSX only...
fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) {
jack_error("AcquireSelfRealTime error");
} else {
set_threaded_log_function();
}
//init done, display parameters
SessionParamsDisplay ( &fParams );
return true;
@@ -294,13 +295,13 @@ namespace Jack
jack_transport_stop ( fJackClient );
jack_info ( "NetMaster : transport stops." );
break;
case JackTransportStarting :
jack_transport_reposition ( fJackClient, &fSendTransportData.fPosition );
jack_transport_start ( fJackClient );
jack_info ( "NetMaster : transport starts." );
break;
case JackTransportRolling :
//TODO , we need to :
// - find a way to call TransportEngine->SetNetworkSync()
@@ -362,7 +363,7 @@ namespace Jack
int JackNetAdapter::Write()
{
EncodeSyncPacket();
if ( SyncSend() == SOCKET_ERROR )
return SOCKET_ERROR;

@@ -376,7 +377,7 @@ namespace Jack
//in case of fatal network error, stop the process
if (Read() == SOCKET_ERROR)
return SOCKET_ERROR;
PushAndPull(fSoftCaptureBuffer, fSoftPlaybackBuffer, fAdaptedBufferSize);

//then write data to network
@@ -386,7 +387,7 @@ namespace Jack

return 0;
}
} // namespace Jack

//loader------------------------------------------------------------------------------
@@ -403,10 +404,10 @@ extern "C"
SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor()
{
jack_driver_desc_t* desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) );
strcpy(desc->name, "netadapter"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1
strcpy(desc->desc, "netjack net <==> audio backend adapter"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1
desc->nparams = 11;
desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) );

@@ -473,7 +474,7 @@ extern "C"
strcpy ( desc->params[i].value.str, "slow" );
strcpy ( desc->params[i].short_desc, "Slow, Normal or Fast mode." );
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc );
i++;
strcpy(desc->params[i].name, "quality");
desc->params[i].character = 'q';
@@ -481,7 +482,7 @@ extern "C"
desc->params[i].value.ui = 0;
strcpy(desc->params[i].short_desc, "Resample algorithm quality (0 - 4)");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
i++;
strcpy(desc->params[i].name, "ring-buffer");
desc->params[i].character = 'g';
@@ -489,7 +490,7 @@ extern "C"
desc->params[i].value.ui = 32768;
strcpy(desc->params[i].short_desc, "Fixed ringbuffer size");
strcpy(desc->params[i].long_desc, "Fixed ringbuffer size (if not set => automatic adaptative)");
i++;
strcpy ( desc->params[i].name, "auto-connect" );
desc->params[i].character = 'c';
@@ -497,7 +498,7 @@ extern "C"
desc->params[i].value.i = false;
strcpy ( desc->params[i].short_desc, "Auto connect netmaster to system ports" );
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc );
return desc;
}

@@ -508,20 +509,19 @@ extern "C"
Jack::JackAudioAdapter* adapter;
jack_nframes_t buffer_size = jack_get_buffer_size ( jack_client );
jack_nframes_t sample_rate = jack_get_sample_rate ( jack_client );
try {
adapter = new Jack::JackAudioAdapter(jack_client, new Jack::JackNetAdapter(jack_client, buffer_size, sample_rate, params), params, false);
assert ( adapter );

if ( adapter->Open() == 0 )
if (adapter->Open() == 0) {
return 0;
else
{
} else {
delete adapter;
return 1;
}
} catch (...) {
return 1;
}


+ 31
- 27
common/JackNetAdapter.h View File

@@ -30,44 +30,48 @@ namespace Jack
\brief Net adapter.
*/

class JackNetAdapter : public JackAudioAdapterInterface, public JackNetSlaveInterface, public JackRunnableInterface
class JackNetAdapter : public JackAudioAdapterInterface,
public JackNetSlaveInterface,
public JackRunnableInterface
{
private:
//jack data
jack_client_t* fJackClient;

//transport data
int fLastTransportState;
int fLastTimebaseMaster;
//sample buffers
sample_t** fSoftCaptureBuffer;
sample_t** fSoftPlaybackBuffer;
private:

//adapter thread
JackThread fThread;
//jack data
jack_client_t* fJackClient;

//transport
void EncodeTransportData();
void DecodeTransportData();
//transport data
int fLastTransportState;
int fLastTimebaseMaster;

public:
//sample buffers
sample_t** fSoftCaptureBuffer;
sample_t** fSoftPlaybackBuffer;

JackNetAdapter ( jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params );
~JackNetAdapter();
//adapter thread
JackThread fThread;

int Open();
int Close();
//transport
void EncodeTransportData();
void DecodeTransportData();

int SetBufferSize ( jack_nframes_t buffer_size );
public:

bool Init();
bool Execute();
JackNetAdapter(jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params);
~JackNetAdapter();

int Read();
int Write();
int Open();
int Close();

int Process();
int SetBufferSize(jack_nframes_t buffer_size);

bool Init();
bool Execute();

int Read();
int Write();

int Process();
};
}



+ 61
- 33
common/JackNetDriver.cpp View File

@@ -125,6 +125,7 @@ namespace Jack
bool JackNetDriver::Initialize()
{
jack_log("JackNetDriver::Initialize()");
FreePorts();

//new loading, but existing socket, restart the driver
if (fSocket.IsSocket()) {
@@ -143,7 +144,7 @@ namespace Jack

//init network
if (!JackNetSlaveInterface::Init()) {
jack_error("JackNetSlaveInterface::Init() error..." );
jack_error("Starting network fails...");
return false;
}

@@ -153,21 +154,32 @@ namespace Jack
return false;
}
// If -1 at conection time, in/out channels count is sent by the master
fCaptureChannels = fParams.fSendAudioChannels;
fPlaybackChannels = fParams.fReturnAudioChannels;

//allocate midi ports lists
fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels];
fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels];
assert ( fMidiCapturePortList );
assert ( fMidiPlaybackPortList );

assert(fMidiCapturePortList);
assert(fMidiPlaybackPortList);

for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
fMidiCapturePortList[midi_port_index] = 0;
}
for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
fMidiPlaybackPortList[midi_port_index] = 0;
}

//register jack ports
if ( AllocPorts() != 0 )
{
jack_error ( "Can't allocate ports." );
if (AllocPorts() != 0) {
jack_error("Can't allocate ports.");
return false;
}

//init done, display parameters
SessionParamsDisplay ( &fParams );
SessionParamsDisplay(&fParams);

//monitor
#ifdef JACK_MONITOR
@@ -257,7 +269,7 @@ namespace Jack
char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
unsigned long port_flags;
int audio_port_index;
uint midi_port_index;
int midi_port_index;
jack_latency_range_t range;

//audio
@@ -365,22 +377,38 @@ namespace Jack

int JackNetDriver::FreePorts()
{
jack_log ( "JackNetDriver::FreePorts" );
jack_log("JackNetDriver::FreePorts");

int audio_port_index;
uint midi_port_index;
for ( audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++ )
if (fCapturePortList[audio_port_index] > 0)
fGraphManager->ReleasePort ( fClientControl.fRefNum, fCapturePortList[audio_port_index] );
for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ )
if (fPlaybackPortList[audio_port_index] > 0)
fGraphManager->ReleasePort ( fClientControl.fRefNum, fPlaybackPortList[audio_port_index] );
for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ )
if (fMidiCapturePortList[midi_port_index] > 0)
fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiCapturePortList[midi_port_index] );
for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ )
if (fMidiPlaybackPortList[midi_port_index] > 0)
fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index] );
for (int audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++) {
if (fCapturePortList[audio_port_index] > 0) {
fGraphManager->ReleasePort(fClientControl.fRefNum, fCapturePortList[audio_port_index]);
fCapturePortList[audio_port_index] = 0;
}
}

for (int audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) {
if (fPlaybackPortList[audio_port_index] > 0) {
fGraphManager->ReleasePort(fClientControl.fRefNum, fPlaybackPortList[audio_port_index]);
fPlaybackPortList[audio_port_index] = 0;
}
}

for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
if (fMidiCapturePortList && fMidiCapturePortList[midi_port_index] > 0) {
fGraphManager->ReleasePort(fClientControl.fRefNum, fMidiCapturePortList[midi_port_index]);
fMidiCapturePortList[midi_port_index] = 0;
}
}

for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
if (fMidiPlaybackPortList && fMidiPlaybackPortList[midi_port_index] > 0) {
fGraphManager->ReleasePort(fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index]);
fMidiPlaybackPortList[midi_port_index] = 0;
}
}
// Clear MIDI channels
fParams.fSendMidiChannels = 0;
fParams.fReturnMidiChannels = 0;
return 0;
}

@@ -479,8 +507,8 @@ namespace Jack
//driver processes--------------------------------------------------------------------
int JackNetDriver::Read()
{
uint midi_port_index;
uint audio_port_index;
int midi_port_index;
int audio_port_index;

//buffers
for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ )
@@ -524,7 +552,7 @@ namespace Jack

int JackNetDriver::Write()
{
uint midi_port_index;
int midi_port_index;
int audio_port_index;

//buffers
@@ -603,17 +631,17 @@ namespace Jack
strcpy ( desc->params[i].name, "input_ports" );
desc->params[i].character = 'C';
desc->params[i].type = JackDriverParamInt;
desc->params[i].value.i = 2;
desc->params[i].value.i = -1;
strcpy ( desc->params[i].short_desc, "Number of audio input ports" );
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc );
strcpy ( desc->params[i].long_desc, "Number of audio input ports. If -1, audio physical input from the master");

i++;
strcpy ( desc->params[i].name, "output_ports" );
desc->params[i].character = 'P';
desc->params[i].type = JackDriverParamInt;
desc->params[i].value.i = 2;
desc->params[i].value.i = -1;
strcpy ( desc->params[i].short_desc, "Number of audio output ports" );
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc );
strcpy ( desc->params[i].long_desc, "Number of audio output ports. If -1, audio physical output from the master");

i++;
strcpy ( desc->params[i].name, "midi_in_ports" );
@@ -626,7 +654,7 @@ namespace Jack
i++;
strcpy ( desc->params[i].name, "midi_out_ports" );
desc->params[i].character = 'o';
desc->params[i].type = JackDriverParamUInt;
desc->params[i].type = JackDriverParamInt;
desc->params[i].value.i = 0;
strcpy ( desc->params[i].short_desc, "Number of midi output ports" );
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc );
@@ -668,8 +696,8 @@ namespace Jack
uint transport_sync = 1;
jack_nframes_t period_size = 128;
jack_nframes_t sample_rate = 48000;
int audio_capture_ports = 2;
int audio_playback_ports = 2;
int audio_capture_ports = -1;
int audio_playback_ports = -1;
int midi_input_ports = 0;
int midi_output_ports = 0;
bool monitor = false;


+ 6
- 3
common/JackNetDriver.h View File

@@ -36,15 +36,17 @@ namespace Jack

class JackNetDriver : public JackAudioDriver, public JackNetSlaveInterface
{

private:

//jack data
jack_port_id_t* fMidiCapturePortList;
jack_port_id_t* fMidiPlaybackPortList;
//transport
int fLastTransportState;
int fLastTimebaseMaster;
//monitoring
#ifdef JACK_MONITOR
JackGnuPlotMonitor<float>* fNetTimeMon;
@@ -53,7 +55,7 @@ namespace Jack

bool Initialize();
void FreeAll();
int AllocPorts();
int FreePorts();

@@ -65,6 +67,7 @@ namespace Jack
JackMidiBuffer* GetMidiOutputBuffer ( int port_index );

public:

JackNetDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports,
char* net_name, uint transport_sync, char network_master_mode );


+ 45
- 45
common/JackNetInterface.cpp View File

@@ -27,9 +27,9 @@ using namespace std;
#define PACKET_AVAILABLE_SIZE (fParams.fMtu - sizeof(packet_header_t))
#define HEADER_SIZE (sizeof(packet_header_t))

/*
TODO : since midi buffers now uses up to BUFFER_SIZE_MAX frames,
probably also use BUFFER_SIZE_MAX in everything related to MIDI events
/*
TODO : since midi buffers now uses up to BUFFER_SIZE_MAX frames,
probably also use BUFFER_SIZE_MAX in everything related to MIDI events
handling (see MidiBufferInit in JackMidiPort.cpp)
*/

@@ -169,7 +169,7 @@ namespace Jack
//timeout on receive (for init)
if ( fSocket.SetTimeOut ( MASTER_INIT_TIMEOUT ) < 0 )
jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) );
//connect
if ( fSocket.Connect() == SOCKET_ERROR ) {
jack_error ( "Can't connect : %s", StrError ( NET_ERROR_CODE ) );
@@ -184,17 +184,17 @@ namespace Jack
memset(&net_params, 0, sizeof ( session_params_t ));
SetPacketType ( &fParams, SLAVE_SETUP );
SessionParamsHToN(&fParams, &net_params);
if ( fSocket.Send ( &net_params, sizeof ( session_params_t ), 0 ) == SOCKET_ERROR )
jack_error ( "Error in send : ", StrError ( NET_ERROR_CODE ) );
memset(&net_params, 0, sizeof (session_params_t));
if (((rx_bytes = fSocket.Recv(&net_params, sizeof(session_params_t), 0)) == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA))
{
jack_error ( "Problem with network." );
return false;
}
SessionParamsNToH(&net_params, &host_params);
}
while ( ( GetPacketType ( &host_params ) != START_MASTER ) && ( ++attempt < SLAVE_SETUP_RETRY ) );
@@ -320,7 +320,7 @@ namespace Jack
jack_info ( "Exiting '%s'", fParams.fName );
SetPacketType ( &fParams, KILL_MASTER );
JackNetSocket mcast_socket ( fMulticastIP, fSocket.GetPort() );
session_params_t net_params;
memset(&net_params, 0, sizeof ( session_params_t ));
SessionParamsHToN(&fParams, &net_params);
@@ -329,7 +329,7 @@ namespace Jack
jack_error ( "Can't create socket : %s", StrError ( NET_ERROR_CODE ) );
if ( mcast_socket.SendTo ( &net_params, sizeof ( session_params_t ), 0, fMulticastIP ) == SOCKET_ERROR )
jack_error ( "Can't send suicide request : %s", StrError ( NET_ERROR_CODE ) );
mcast_socket.Close();
}

@@ -347,32 +347,32 @@ namespace Jack
jack_error ( "'%s' : %s, exiting.", fParams.fName, StrError(NET_ERROR_CODE));
//ask to the manager to properly remove the master
Exit();
// UGLY temporary way to be sure the thread does not call code possibly causing a deadlock in JackEngine.
ThreadExit();
} else {
jack_error ( "Error in master receive : %s", StrError(NET_ERROR_CODE));
}
}
packet_header_t* header = reinterpret_cast<packet_header_t*>(fRxBuffer);
PacketHeaderNToH(header, header);
return rx_bytes;
}
int JackNetMasterInterface::Send(size_t size, int flags)
{
int tx_bytes;
packet_header_t* header = reinterpret_cast<packet_header_t*>(fTxBuffer);
PacketHeaderHToN(header, header);
if (((tx_bytes = fSocket.Send(fTxBuffer, size, flags)) == SOCKET_ERROR) && fRunning) {
net_error_t error = fSocket.GetError();
if (error == NET_CONN_ERROR) {
//fatal connection issue, exit
jack_error ("'%s' : %s, exiting.", fParams.fName, StrError (NET_ERROR_CODE));
Exit();
// UGLY temporary way to be sure the thread does not call code possibly causing a deadlock in JackEngine.
ThreadExit();
} else {
@@ -381,7 +381,7 @@ namespace Jack
}
return tx_bytes;
}
bool JackNetMasterInterface::IsSynched()
{
if (fParams.fNetworkMode == 's') {
@@ -390,7 +390,7 @@ namespace Jack
return true;
}
}
int JackNetMasterInterface::SyncSend()
{
fTxHeader.fCycle++;
@@ -452,12 +452,12 @@ namespace Jack
{
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer );
int rx_bytes = Recv(HEADER_SIZE, MSG_PEEK);
if ( ( rx_bytes == 0 ) || ( rx_bytes == SOCKET_ERROR ) )
return rx_bytes;

fCycleOffset = fTxHeader.fCycle - rx_head->fCycle;
switch ( fParams.fNetworkMode )
{
case 's' :
@@ -468,12 +468,12 @@ namespace Jack
//the slow mode is the safest mode because it wait twice the bandwidth relative time (send/return + process)
/*
if (fCycleOffset < CYCLE_OFFSET_SLOW) {
return 0;
} else {
rx_bytes = Recv ( rx_head->fPacketSize, 0 );
}
*/
if (fCycleOffset < CYCLE_OFFSET_SLOW) {
return 0;
} else {
rx_bytes = Recv ( rx_head->fPacketSize, 0 );
}
*/
rx_bytes = Recv ( rx_head->fPacketSize, 0 );
@@ -481,7 +481,7 @@ namespace Jack
jack_info("Warning : '%s' runs in slow network mode, but data received too late (%d cycle(s) offset)", fParams.fName, fCycleOffset);
fLastfCycleOffset = fCycleOffset;
break;
case 'n' :
//normal use of the network :
// - extra latency is set to one cycle, what is the time needed to receive streams using full network bandwidth
@@ -492,12 +492,12 @@ namespace Jack
} else {
rx_bytes = Recv ( rx_head->fPacketSize, 0 );
}
if (fCycleOffset > CYCLE_OFFSET_NORMAL) {
jack_info("'%s' can't run in normal network mode, data received too late (%d cycle(s) offset)", fParams.fName, fCycleOffset);
}
break;
case 'f' :
//fast mode suppose the network bandwith is larger than required for the transmission (only a few channels for example)
// - packets can be quickly received, quickly is here relative to the cycle duration
@@ -514,7 +514,7 @@ namespace Jack
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt;
return rx_bytes;
}
int JackNetMasterInterface::DataRecv()
{
int rx_bytes = 0;
@@ -522,12 +522,12 @@ namespace Jack
uint recvd_audio_pckt = 0;
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer );
while ( !fRxHeader.fIsLastPckt )
{
//how much data is queued on the rx buffer ?
rx_bytes = Recv(HEADER_SIZE, MSG_PEEK);
//error here, problem with recv, just skip the cycle (return -1)
if ( rx_bytes == SOCKET_ERROR )
return rx_bytes;
@@ -569,7 +569,7 @@ namespace Jack
return rx_bytes;
}
void JackNetMasterInterface::EncodeSyncPacket()
{
//this method contains every step of sync packet informations coding
@@ -637,9 +637,9 @@ namespace Jack

return true;
}
// Separate the connection protocol into two separated step
bool JackNetSlaveInterface::InitConnection(int time_out)
{
jack_log("JackNetSlaveInterface::InitConnection()");
@@ -659,10 +659,10 @@ namespace Jack
return false;
}
while (status != NET_CONNECTED && --try_count > 0);
return (try_count != 0);
}
bool JackNetSlaveInterface::InitRendering()
{
jack_log("JackNetSlaveInterface::InitRendering()");
@@ -676,8 +676,8 @@ namespace Jack
if (status == NET_ERROR)
return false;
}
while (status != NET_ROLLING);
while (status != NET_ROLLING);
return true;
}

@@ -701,7 +701,7 @@ namespace Jack
}

//timeout on receive
if ( fSocket.SetTimeOut ( SLAVE_INIT_TIMEOUT ) == SOCKET_ERROR )
if ( fSocket.SetTimeOut ( SLAVE_INIT_TIMEOUT ) == SOCKET_ERROR )
jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) );

//disable local loop
@@ -718,7 +718,7 @@ namespace Jack
SessionParamsHToN(&fParams, &net_params);
if ( fSocket.SendTo ( &net_params, sizeof ( session_params_t ), 0, fMulticastIP ) == SOCKET_ERROR )
jack_error ( "Error in data send : %s", StrError ( NET_ERROR_CODE ) );
//filter incoming packets : don't exit while no error is detected
memset(&net_params, 0, sizeof ( session_params_t ));
rx_bytes = fSocket.CatchHost ( &net_params, sizeof ( session_params_t ), 0 );
@@ -768,7 +768,7 @@ namespace Jack

bool JackNetSlaveInterface::SetParams()
{
jack_log ( "JackNetSlaveInterface::SetParams" );
jack_log("JackNetSlaveInterface::SetParams");

JackNetInterface::SetParams();

@@ -861,7 +861,7 @@ namespace Jack
jack_error ( "Fatal error in slave receive : %s", StrError ( NET_ERROR_CODE ) );
}
}
packet_header_t* header = reinterpret_cast<packet_header_t*>(fRxBuffer);
PacketHeaderNToH(header, header);
return rx_bytes;
@@ -872,7 +872,7 @@ namespace Jack
packet_header_t* header = reinterpret_cast<packet_header_t*>(fTxBuffer);
PacketHeaderHToN(header, header);
int tx_bytes = fSocket.Send ( fTxBuffer, size, flags );
//handle errors
if ( tx_bytes == SOCKET_ERROR )
{
@@ -902,7 +902,7 @@ namespace Jack
return rx_bytes;
}
while ((strcmp(rx_head->fPacketType, "header") != 0) && (rx_head->fDataType != 's'));
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt;
return rx_bytes;
}
@@ -1020,7 +1020,7 @@ namespace Jack
}
return 0;
}
//network sync------------------------------------------------------------------------
void JackNetSlaveInterface::EncodeSyncPacket()
{
@@ -1037,7 +1037,7 @@ namespace Jack
//then others
//...
}
void JackNetSlaveInterface::DecodeSyncPacket()
{
//this method contains every step of sync packet informations decoding process


+ 22
- 16
common/JackNetInterface.h View File

@@ -31,12 +31,10 @@ namespace Jack

class SERVER_EXPORT JackNetInterface
{
private:
protected:
void Initialize();
protected:
session_params_t fParams;
JackNetSocket fSocket;
char fMulticastIP[32];
@@ -44,7 +42,7 @@ namespace Jack
//headers
packet_header_t fTxHeader;
packet_header_t fRxHeader;
// transport
net_transport_data_t fSendTransportData;
net_transport_data_t fReturnTransportData;
@@ -90,7 +88,9 @@ namespace Jack
JackNetInterface ( session_params_t& params, JackNetSocket& socket, const char* multicast_ip );

public:

virtual ~JackNetInterface();

};

/**
@@ -99,7 +99,9 @@ namespace Jack

class SERVER_EXPORT JackNetMasterInterface : public JackNetInterface
{

protected:

bool fRunning;
int fCycleOffset;
int fLastfCycleOffset;
@@ -107,22 +109,22 @@ namespace Jack
bool Init();
int SetRxTimeout();
bool SetParams();
void Exit();
int SyncRecv();
int SyncSend();
int DataRecv();
int DataSend();
//sync packet
void EncodeSyncPacket();
void DecodeSyncPacket();

int Send ( size_t size, int flags );
int Recv ( size_t size, int flags );
bool IsSynched();

public:
@@ -141,25 +143,26 @@ namespace Jack

class SERVER_EXPORT JackNetSlaveInterface : public JackNetInterface
{

protected:
static uint fSlaveCounter;
bool Init();
bool InitConnection(int time_out);
bool InitRendering();
net_status_t SendAvailableToMaster(long count = LONG_MAX); // long here (and not int...)
net_status_t SendStartToMaster();
bool SetParams();
int SyncRecv();
int SyncSend();
int DataRecv();
int DataSend();
//sync packet
void EncodeSyncPacket();
void DecodeSyncPacket();
@@ -168,6 +171,7 @@ namespace Jack
int Send ( size_t size, int flags );

public:

JackNetSlaveInterface() : JackNetInterface()
{
//open Socket API with the first slave
@@ -180,6 +184,7 @@ namespace Jack
}
}
}

JackNetSlaveInterface ( const char* ip, int port ) : JackNetInterface ( ip, port )
{
//open Socket API with the first slave
@@ -192,6 +197,7 @@ namespace Jack
}
}
}

~JackNetSlaveInterface()
{
//close Socket API with the last slave


+ 34
- 11
common/JackNetManager.cpp View File

@@ -37,7 +37,7 @@ namespace Jack
fSendTransportData.fState = -1;
fReturnTransportData.fState = -1;
fLastTransportState = -1;
uint port_index;
int port_index;

//jack audio ports
fAudioCapturePorts = new jack_port_t* [fParams.fSendAudioChannels];
@@ -169,7 +169,7 @@ namespace Jack
//jack ports--------------------------------------------------------------------------
int JackNetMaster::AllocPorts()
{
uint i;
int i;
char name[24];
jack_nframes_t port_latency = jack_get_buffer_size ( fJackClient );
jack_latency_range_t range;
@@ -251,7 +251,7 @@ namespace Jack

ports = jack_get_ports(fJackClient, NULL, NULL, JackPortIsPhysical | JackPortIsOutput);
if (ports != NULL) {
for (unsigned int i = 0; i < fParams.fSendAudioChannels && ports[i]; i++) {
for (int i = 0; i < fParams.fSendAudioChannels && ports[i]; i++) {
jack_connect(fJackClient, ports[i], jack_port_name(fAudioCapturePorts[i]));
}
free(ports);
@@ -259,7 +259,7 @@ namespace Jack

ports = jack_get_ports(fJackClient, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
if (ports != NULL) {
for (unsigned int i = 0; i < fParams.fReturnAudioChannels && ports[i]; i++) {
for (int i = 0; i < fParams.fReturnAudioChannels && ports[i]; i++) {
jack_connect(fJackClient, jack_port_name(fAudioPlaybackPorts[i]), ports[i]);
}
free(ports);
@@ -270,7 +270,7 @@ namespace Jack
{
jack_log ( "JackNetMaster::FreePorts, ID %u", fParams.fID );

uint port_index;
int port_index;
for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ )
if ( fAudioCapturePorts[port_index] )
jack_port_unregister ( fJackClient, fAudioCapturePorts[port_index] );
@@ -396,7 +396,7 @@ namespace Jack
{
JackNetMaster* obj = static_cast<JackNetMaster*>(arg);
if (nframes != obj->fParams.fPeriodSize) {
jack_error("Cannot handle bufer size change, so JackNetMaster proxy will be removed...");
jack_error("Cannot handle buffer size change, so JackNetMaster proxy will be removed...");
obj->Exit();
}
return 0;
@@ -413,7 +413,7 @@ namespace Jack
if ( !fRunning )
return 0;

uint port_index;
int port_index;
int res = 0;

#ifdef JACK_MONITOR
@@ -546,6 +546,19 @@ namespace Jack
SocketAPIEnd();
}

int JackNetMasterManager::CountIO(int flags)
{
const char **ports;
int count = 0;

ports = jack_get_ports(fManagerClient, NULL, NULL, flags);
if (ports != NULL) {
while(ports[count]) count++;
free(ports);
}
return count;
}

int JackNetMasterManager::SetSyncCallback ( jack_transport_state_t state, jack_position_t* pos, void* arg )
{
return static_cast<JackNetMasterManager*> ( arg )->SyncCallback ( state, pos );
@@ -670,13 +683,23 @@ namespace Jack
params.fID = ++fGlobalID;
params.fSampleRate = jack_get_sample_rate ( fManagerClient );
params.fPeriodSize = jack_get_buffer_size ( fManagerClient );
SetSlaveName ( params );

if (params.fSendAudioChannels == -1) {
params.fSendAudioChannels = CountIO(JackPortIsPhysical | JackPortIsOutput);
jack_info("Takes physical %d inputs for client", params.fSendAudioChannels);
}

if (params.fReturnAudioChannels == -1) {
params.fReturnAudioChannels = CountIO(JackPortIsPhysical | JackPortIsInput);
jack_info("Takes physical %d outputs for client", params.fReturnAudioChannels);
}

SetSlaveName (params);

//create a new master and add it to the list
JackNetMaster* master = new JackNetMaster(fSocket, params, fMulticastIP);
if ( master->Init(fAutoConnect) )
{
fMasterList.push_back ( master );
if (master->Init(fAutoConnect)) {
fMasterList.push_back(master);
return master;
}
delete master;


+ 9
- 0
common/JackNetManager.h View File

@@ -37,7 +37,9 @@ namespace Jack
class JackNetMaster : public JackNetMasterInterface
{
friend class JackNetMasterManager;

private:

static int SetProcess ( jack_nframes_t nframes, void* arg );
static int SetBufferSize (jack_nframes_t nframes, void* arg);
static void SetTimebaseCallback ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void* arg );
@@ -74,6 +76,7 @@ namespace Jack
void ConnectPorts();

public:

JackNetMaster ( JackNetSocket& socket, session_params_t& params, const char* multicast_ip);
~JackNetMaster ();

@@ -90,7 +93,9 @@ namespace Jack
class JackNetMasterManager
{
friend class JackNetMaster;

private:

static int SetSyncCallback ( jack_transport_state_t state, jack_position_t* pos, void* arg );
static void* NetManagerThread ( void* arg );

@@ -111,7 +116,11 @@ namespace Jack
void SetSlaveName ( session_params_t& params );

int SyncCallback ( jack_transport_state_t state, jack_position_t* pos );

int CountIO(int flags);

public:

JackNetMasterManager ( jack_client_t* jack_client, const JSList* params);
~JackNetMasterManager();
};


+ 778
- 821
common/JackNetOneDriver.cpp
File diff suppressed because it is too large
View File


+ 63
- 66
common/JackNetOneDriver.h View File

@@ -27,72 +27,69 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

namespace Jack
{
/**
\Brief This class describes the Net Backend
*/

class JackNetOneDriver : public JackAudioDriver
{
private:

netjack_driver_state_t netj;

void
render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats);
void
render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats );
#ifdef HAVE_CELT
void
render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes);
void
render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up);
#endif
void
render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats);
void
render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats);

public:

JackNetOneDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports,
int sample_rate, int period_size, int resample_factor,
const char* net_name, uint transport_sync, int bitdepth, int use_autoconfig,
int latency, int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val );
~JackNetOneDriver();

int Open ( jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing,
int inchannels, int outchannels, bool monitor, const char* capture_driver_name,
const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency );

int Close();
int Attach();
int Detach();

int Read();
int Write();

bool Initialize();
int AllocPorts();
void FreePorts();

// BufferSize can't be changed
bool IsFixedBufferSize()
{
return true;
}

int SetBufferSize ( jack_nframes_t buffer_size )
{
return -1;
}

int SetSampleRate ( jack_nframes_t sample_rate )
{
return -1;
}

};
/**
\Brief This class describes the Net Backend
*/

class JackNetOneDriver : public JackAudioDriver
{
private:

netjack_driver_state_t netj;

void
render_payload_to_jack_ports_float(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats);
void
render_jack_ports_to_payload_float(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats );
#ifdef HAVE_CELT
void
render_payload_to_jack_ports_celt(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes);
void
render_jack_ports_to_payload_celt(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up);
#endif
void
render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats);
void
render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats);

public:

JackNetOneDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports,
int sample_rate, int period_size, int resample_factor,
const char* net_name, uint transport_sync, int bitdepth, int use_autoconfig,
int latency, int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val);
~JackNetOneDriver();

int Open(jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing,
int inchannels, int outchannels, bool monitor, const char* capture_driver_name,
const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency);

int Close();
int Attach();
int Detach();

int Read();
int Write();

bool Initialize();
int AllocPorts();
void FreePorts();

// BufferSize can't be changed
bool IsFixedBufferSize() {
return true;
}

int SetBufferSize(jack_nframes_t buffer_size) {
return -1;
}

int SetSampleRate(jack_nframes_t sample_rate) {
return -1;
}

};
}

#endif

+ 2
- 0
common/JackNetTool.cpp View File

@@ -374,11 +374,13 @@ namespace Jack

void NetCeltAudioBuffer::SetBuffer(int index, sample_t* buffer)
{
assert(fPortBuffer);
fPortBuffer[index] = buffer;
}

sample_t* NetCeltAudioBuffer::GetBuffer(int index)
{
assert(fPortBuffer);
return fPortBuffer[index];
}



+ 4
- 4
common/JackNetTool.h View File

@@ -88,10 +88,10 @@ namespace Jack
uint32_t fMtu; //connection mtu
uint32_t fID; //slave's ID
uint32_t fTransportSync; //is the transport synced ?
uint32_t fSendAudioChannels; //number of master->slave channels
uint32_t fReturnAudioChannels; //number of slave->master channels
uint32_t fSendMidiChannels; //number of master->slave midi channels
uint32_t fReturnMidiChannels; //number of slave->master midi channels
int32_t fSendAudioChannels; //number of master->slave channels
int32_t fReturnAudioChannels; //number of slave->master channels
int32_t fSendMidiChannels; //number of master->slave midi channels
int32_t fReturnMidiChannels; //number of slave->master midi channels
uint32_t fSampleRate; //session sample rate
uint32_t fPeriodSize; //period size
uint32_t fSampleEncoder; //samples encoder


+ 0
- 287
common/JackPhysicalMidiInput.cpp View File

@@ -1,287 +0,0 @@
/*
Copyright (C) 2009 Devin Anderson

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

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

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

*/

#include <cassert>
#include <cstring>
#include <new>

#include "JackError.h"
#include "JackPhysicalMidiInput.h"

namespace Jack {

JackPhysicalMidiInput::JackPhysicalMidiInput(size_t buffer_size)
{
size_t datum_size = sizeof(jack_midi_data_t);
assert(buffer_size > 0);
input_ring = jack_ringbuffer_create((buffer_size + 1) * datum_size);
if (! input_ring) {
throw std::bad_alloc();
}
jack_ringbuffer_mlock(input_ring);
Clear();
expected_data_bytes = 0;
status_byte = 0;
}

JackPhysicalMidiInput::~JackPhysicalMidiInput()
{
jack_ringbuffer_free(input_ring);
}

void
JackPhysicalMidiInput::Clear()
{
jack_ringbuffer_reset(input_ring);
buffered_bytes = 0;
unbuffered_bytes = 0;
}

void
JackPhysicalMidiInput::HandleBufferFailure(size_t unbuffered_bytes,
size_t total_bytes)
{
jack_error("%d MIDI byte(s) of a %d byte message could not be buffered - "
"message dropped", unbuffered_bytes, total_bytes);
}

void
JackPhysicalMidiInput::HandleIncompleteMessage(size_t bytes)
{
jack_error("Discarding %d MIDI byte(s) - incomplete message (cable "
"unplugged?)", bytes);
}

void
JackPhysicalMidiInput::HandleInvalidStatusByte(jack_midi_data_t status)
{
jack_error("Dropping invalid MIDI status byte '%x'",
(unsigned int) status);
}

void
JackPhysicalMidiInput::HandleUnexpectedSysexEnd(size_t bytes)
{
jack_error("Discarding %d MIDI byte(s) - received sysex end without sysex "
"start (cable unplugged?)", bytes);
}

void
JackPhysicalMidiInput::HandleWriteFailure(size_t bytes)
{
jack_error("Failed to write a %d byte MIDI message to the port buffer",
bytes);
}

void
JackPhysicalMidiInput::Process(jack_nframes_t frames)
{
assert(port_buffer);
port_buffer->Reset(frames);
jack_nframes_t current_frame = 0;
size_t datum_size = sizeof(jack_midi_data_t);
for (;;) {
jack_midi_data_t datum;
current_frame = Receive(&datum, current_frame, frames);
if (current_frame >= frames) {
break;
}

jack_log("JackPhysicalMidiInput::Process (%d) - Received '%x' byte",
current_frame, (unsigned int) datum);

if (datum >= 0xf8) {
// Realtime
if (datum == 0xfd) {
HandleInvalidStatusByte(datum);
} else {

jack_log("JackPhysicalMidiInput::Process - Writing realtime "
"event.");

WriteByteEvent(current_frame, datum);
}
continue;
}
if (datum == 0xf7) {
// Sysex end
if (status_byte != 0xf0) {
HandleUnexpectedSysexEnd(buffered_bytes + unbuffered_bytes);
Clear();
expected_data_bytes = 0;
status_byte = 0;
} else {

jack_log("JackPhysicalMidiInput::Process - Writing sysex "
"event.");

WriteBufferedSysexEvent(current_frame);
}
continue;
}
if (datum >= 0x80) {

// We're handling a non-realtime status byte

jack_log("JackPhysicalMidiInput::Process - Handling non-realtime "
"status byte.");

if (buffered_bytes || unbuffered_bytes) {
HandleIncompleteMessage(buffered_bytes + unbuffered_bytes + 1);
Clear();
}
status_byte = datum;
switch (datum & 0xf0) {
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
// Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
expected_data_bytes = 2;
break;
case 0xc0:
case 0xd0:
// Program Change, Channel Pressure
expected_data_bytes = 1;
break;
case 0xf0:
switch (datum) {
case 0xf0:
// Sysex message
expected_data_bytes = 0;
break;
case 0xf1:
case 0xf3:
// MTC Quarter frame, Song Select
expected_data_bytes = 1;
break;
case 0xf2:
// Song Position
expected_data_bytes = 2;
break;
case 0xf4:
case 0xf5:
// Undefined
HandleInvalidStatusByte(datum);
expected_data_bytes = 0;
status_byte = 0;
break;
case 0xf6:
// Tune Request
WriteByteEvent(current_frame, datum);
expected_data_bytes = 0;
status_byte = 0;
}
break;
}
continue;
}

// We're handling a data byte

jack_log("JackPhysicalMidiInput::Process - Buffering data byte.");

if (jack_ringbuffer_write(input_ring, (const char *) &datum,
datum_size) == datum_size) {
buffered_bytes++;
} else {
unbuffered_bytes++;
}
unsigned long total_bytes = buffered_bytes + unbuffered_bytes;
assert((! expected_data_bytes) ||
(total_bytes <= expected_data_bytes));
if (total_bytes == expected_data_bytes) {
if (! unbuffered_bytes) {

jack_log("JackPhysicalMidiInput::Process - Writing buffered "
"event.");

WriteBufferedEvent(current_frame);
} else {
HandleBufferFailure(unbuffered_bytes, total_bytes);
Clear();
}
if (status_byte >= 0xf0) {
expected_data_bytes = 0;
status_byte = 0;
}
}
}
}

void
JackPhysicalMidiInput::WriteBufferedEvent(jack_nframes_t frame)
{
assert(port_buffer && port_buffer->IsValid());
size_t space = jack_ringbuffer_read_space(input_ring);
jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 1);
if (event) {
jack_ringbuffer_data_t vector[2];
jack_ringbuffer_get_read_vector(input_ring, vector);
event[0] = status_byte;
size_t data_length_1 = vector[0].len;
memcpy(event + 1, vector[0].buf, data_length_1);
size_t data_length_2 = vector[1].len;
if (data_length_2) {
memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2);
}
} else {
HandleWriteFailure(space + 1);
}
Clear();
}

void
JackPhysicalMidiInput::WriteBufferedSysexEvent(jack_nframes_t frame)
{
assert(port_buffer && port_buffer->IsValid());
size_t space = jack_ringbuffer_read_space(input_ring);
jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 2);
if (event) {
jack_ringbuffer_data_t vector[2];
jack_ringbuffer_get_read_vector(input_ring, vector);
event[0] = status_byte;
size_t data_length_1 = vector[0].len;
memcpy(event + 1, vector[0].buf, data_length_1);
size_t data_length_2 = vector[1].len;
if (data_length_2) {
memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2);
}
event[data_length_1 + data_length_2 + 1] = 0xf7;
} else {
HandleWriteFailure(space + 2);
}
Clear();
}

void
JackPhysicalMidiInput::WriteByteEvent(jack_nframes_t frame,
jack_midi_data_t datum)
{
assert(port_buffer && port_buffer->IsValid());
jack_midi_data_t *event = port_buffer->ReserveEvent(frame, 1);
if (event) {
event[0] = datum;
} else {
HandleWriteFailure(1);
}
}

}

+ 0
- 146
common/JackPhysicalMidiInput.h View File

@@ -1,146 +0,0 @@
/*
Copyright (C) 2009 Devin Anderson

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

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

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

*/

#ifndef __JackPhysicalMidiInput__
#define __JackPhysicalMidiInput__

#include "JackMidiPort.h"
#include "ringbuffer.h"

namespace Jack {

class JackPhysicalMidiInput {

private:

size_t buffered_bytes;
size_t expected_data_bytes;
jack_ringbuffer_t *input_ring;
JackMidiBuffer *port_buffer;
jack_midi_data_t status_byte;
size_t unbuffered_bytes;

void
Clear();

void
WriteBufferedEvent(jack_nframes_t);

void
WriteBufferedSysexEvent(jack_nframes_t);

void
WriteByteEvent(jack_nframes_t, jack_midi_data_t);

protected:

/**
* Override to specify how to react when 1 or more bytes of a MIDI
* message are lost because there wasn't enough room in the input
* buffer. The first argument is the amount of bytes that couldn't be
* buffered, and the second argument is the total amount of bytes in
* the MIDI message. The default implementation calls 'jack_error'
* with a basic error message.
*/

virtual void
HandleBufferFailure(size_t, size_t);

/**
* Override to specify how to react when a new status byte is received
* before all of the data bytes in a message are received. The
* argument is the number of bytes being discarded. The default
* implementation calls 'jack_error' with a basic error message.
*/

virtual void
HandleIncompleteMessage(size_t);

/**
* Override to specify how to react when an invalid status byte (0xf4,
* 0xf5, 0xfd) is received. The argument contains the invalid status
* byte. The default implementation calls 'jack_error' with a basic
* error message.
*/

virtual void
HandleInvalidStatusByte(jack_midi_data_t);

/**
* Override to specify how to react when a sysex end byte (0xf7) is
* received without first receiving a sysex start byte (0xf0). The
* argument contains the amount of bytes that will be discarded. The
* default implementation calls 'jack_error' with a basic error
* message.
*/

virtual void
HandleUnexpectedSysexEnd(size_t);

/**
* Override to specify how to react when a MIDI message can not be
* written to the port buffer. The argument specifies the length of
* the MIDI message. The default implementation calls 'jack_error'
* with a basic error message.
*/

virtual void
HandleWriteFailure(size_t);

/**
* This method *must* be overridden to handle receiving MIDI bytes.
* The first argument is a pointer to the memory location at which the
* MIDI byte should be stored. The second argument is the last frame
* at which a MIDI byte was received, except at the beginning of the
* period when the value is 0. The third argument is the total number
* of frames in the period. The return value is the frame at which the
* MIDI byte is received at, or the value of the third argument is no
* more MIDI bytes can be received in this period.
*/

virtual jack_nframes_t
Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t) = 0;

public:

JackPhysicalMidiInput(size_t buffer_size=1024);
virtual ~JackPhysicalMidiInput();

/**
* Called to process MIDI data during a period.
*/

void
Process(jack_nframes_t);

/**
* Set the MIDI buffer that will receive incoming messages.
*/

inline void
SetPortBuffer(JackMidiBuffer *port_buffer)
{
this->port_buffer = port_buffer;
}

};

}

#endif

+ 0
- 320
common/JackPhysicalMidiOutput.cpp View File

@@ -1,320 +0,0 @@
/*
Copyright (C) 2009 Devin Anderson

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

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

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

*/

#include <cassert>

#include "JackError.h"
#include "JackPhysicalMidiOutput.h"

namespace Jack {

JackPhysicalMidiOutput::JackPhysicalMidiOutput(size_t non_rt_buffer_size,
size_t rt_buffer_size)
{
size_t datum_size = sizeof(jack_midi_data_t);
assert(non_rt_buffer_size > 0);
assert(rt_buffer_size > 0);
output_ring = jack_ringbuffer_create((non_rt_buffer_size + 1) *
datum_size);
if (! output_ring) {
throw std::bad_alloc();
}
rt_output_ring = jack_ringbuffer_create((rt_buffer_size + 1) *
datum_size);
if (! rt_output_ring) {
jack_ringbuffer_free(output_ring);
throw std::bad_alloc();
}
jack_ringbuffer_mlock(output_ring);
jack_ringbuffer_mlock(rt_output_ring);
running_status = 0;
}

JackPhysicalMidiOutput::~JackPhysicalMidiOutput()
{
jack_ringbuffer_free(output_ring);
jack_ringbuffer_free(rt_output_ring);
}

jack_nframes_t
JackPhysicalMidiOutput::Advance(jack_nframes_t frame)
{
return frame;
}

inline jack_midi_data_t
JackPhysicalMidiOutput::ApplyRunningStatus(jack_midi_data_t **buffer,
size_t *size)
{

// Stolen and modified from alsa/midi_pack.h

jack_midi_data_t status = (*buffer)[0];
if ((status >= 0x80) && (status < 0xf0)) {
if (status == running_status) {
(*buffer)++;
(*size)--;
} else {
running_status = status;
}
} else if (status < 0xf8) {
running_status = 0;
}
return status;
}

void
JackPhysicalMidiOutput::HandleEventLoss(JackMidiEvent *event)
{
jack_error("%d byte MIDI event lost", event->size);
}

void
JackPhysicalMidiOutput::Process(jack_nframes_t frames)
{
assert(port_buffer);
jack_nframes_t current_frame = Advance(0);
jack_nframes_t current_midi_event = 0;
jack_midi_data_t datum;
size_t datum_size = sizeof(jack_midi_data_t);
JackMidiEvent *midi_event;
jack_midi_data_t *midi_event_buffer;
size_t midi_event_size;
jack_nframes_t midi_events = port_buffer->event_count;

// First, send any realtime MIDI data that's left from last cycle.

if ((current_frame < frames) &&
jack_ringbuffer_read_space(rt_output_ring)) {

jack_log("JackPhysicalMidiOutput::Process (%d) - Sending buffered "
"realtime data from last period.", current_frame);

current_frame = SendBufferedData(rt_output_ring, current_frame,
frames);

jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", current_frame);

}

// Iterate through the events in this cycle.

for (; (current_midi_event < midi_events) && (current_frame < frames);
current_midi_event++) {

// Once we're inside this loop, we know that the realtime buffer
// is empty. As long as we don't find a realtime message, we can
// concentrate on sending non-realtime data.

midi_event = &(port_buffer->events[current_midi_event]);
jack_nframes_t midi_event_time = midi_event->time;
midi_event_buffer = midi_event->GetData(port_buffer);
midi_event_size = midi_event->size;
datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size);
if (current_frame < midi_event_time) {

// We have time before this event is scheduled to be sent.
// Send data in the non-realtime buffer.

if (jack_ringbuffer_read_space(output_ring)) {

jack_log("JackPhysicalMidiOutput::Process (%d) - Sending "
"buffered non-realtime data from last period.",
current_frame);

current_frame = SendBufferedData(output_ring, current_frame,
midi_event_time);

jack_log("JackPhysicalMidiOutput::Process (%d) - Sent",
current_frame);

}
if (current_frame < midi_event_time) {

// We _still_ have time before this event is scheduled to
// be sent. Let's send as much of this event as we can
// (save for one byte, which will need to be sent at or
// after its scheduled time). First though, we need to
// make sure that we can buffer this data if we need to.
// Otherwise, we might start sending a message that we
// can't finish.

if (midi_event_size > 1) {
if (jack_ringbuffer_write_space(output_ring) <
((midi_event_size - 1) * datum_size)) {
HandleEventLoss(midi_event);
continue;
}

// Send as much of the event as possible (save for one
// byte).

do {

jack_log("JackPhysicalMidiOutput::Process (%d) - "
"Sending unbuffered event byte early.",
current_frame);

current_frame = Send(current_frame,
*midi_event_buffer);

jack_log("JackPhysicalMidiOutput::Process (%d) - "
"Sent.", current_frame);

midi_event_buffer++;
midi_event_size--;
if (current_frame >= midi_event_time) {

// The event we're processing must be a
// non-realtime event. It has more than one
// byte.

goto buffer_non_realtime_data;
}
} while (midi_event_size > 1);
}

jack_log("JackPhysicalMidiOutput::Process (%d) - Advancing to "
">= %d", current_frame, midi_event_time);

current_frame = Advance(midi_event_time);

jack_log("JackPhysicalMidiOutput::Process (%d) - Advanced.",
current_frame);

}
}

// If the event is realtime, then we'll send the event now.
// Otherwise, we attempt to put the rest of the event bytes in the
// non-realtime buffer.

if (datum >= 0xf8) {

jack_log("JackPhysicalMidiOutput::Process (%d) - Sending "
"unbuffered realtime event.", current_frame);

current_frame = Send(current_frame, datum);

jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.",
current_frame);

} else if (jack_ringbuffer_write_space(output_ring) >=
(midi_event_size * datum_size)) {
buffer_non_realtime_data:

jack_log("JackPhysicalMidiOutput::Process (%d) - Buffering %d "
"byte(s) of non-realtime data.", current_frame,
midi_event_size);

jack_ringbuffer_write(output_ring,
(const char *) midi_event_buffer,
midi_event_size);
} else {
HandleEventLoss(midi_event);
}
}

if (current_frame < frames) {

// If we have time left to send data, then we know that all of the
// data in the realtime buffer has been sent, and that all of the
// non-realtime messages have either been sent, or buffered. We
// use whatever time is left to send data in the non-realtime
// buffer.

if (jack_ringbuffer_read_space(output_ring)) {

jack_log("JackPhysicalMidiOutput::Process (%d) - All events "
"processed. Sending buffered non-realtime data.",
current_frame);

current_frame = SendBufferedData(output_ring, current_frame,
frames);

jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.",
current_frame);

}
} else {

// Since we have no time left, we need to put all remaining midi
// events in their appropriate buffers, and send them next period.

for (; current_midi_event < midi_events; current_midi_event++) {
midi_event = &(port_buffer->events[current_midi_event]);
midi_event_buffer = midi_event->GetData(port_buffer);
midi_event_size = midi_event->size;
datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size);
if (datum >= 0xf8) {

// Realtime.

if (jack_ringbuffer_write_space(rt_output_ring) >=
datum_size) {

jack_log("JackPhysicalMidiOutput::Process - Buffering "
"realtime event for next period.");

jack_ringbuffer_write(rt_output_ring,
(const char *) &datum, datum_size);
continue;
}
} else {

// Non-realtime.

if (jack_ringbuffer_write_space(output_ring) >=
(midi_event_size * datum_size)) {

jack_log("JackPhysicalMidiOutput::Process - Buffering "
"non-realtime event for next period.");

jack_ringbuffer_write(output_ring,
(const char *) midi_event_buffer,
midi_event_size * datum_size);
continue;
}
}
HandleEventLoss(midi_event);
}
}
}

jack_nframes_t
JackPhysicalMidiOutput::SendBufferedData(jack_ringbuffer_t *buffer,
jack_nframes_t current_frame,
jack_nframes_t boundary)
{
assert(buffer);
assert(current_frame < boundary);
size_t datum_size = sizeof(jack_midi_data_t);
size_t data_length = jack_ringbuffer_read_space(buffer) / datum_size;
for (size_t i = 0; i < data_length; i++) {
jack_midi_data_t datum;
jack_ringbuffer_read(buffer, (char *) &datum, datum_size);
current_frame = Send(current_frame, datum);
if (current_frame >= boundary) {
break;
}
}
return current_frame;
}

}

+ 0
- 118
common/JackPhysicalMidiOutput.h View File

@@ -1,118 +0,0 @@
/*
Copyright (C) 2009 Devin Anderson

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

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

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

*/

#ifndef __JackPhysicalMidiOutput__
#define __JackPhysicalMidiOutput__

#include "JackMidiPort.h"
#include "ringbuffer.h"

namespace Jack {

class JackPhysicalMidiOutput {

private:

jack_midi_data_t
ApplyRunningStatus(jack_midi_data_t **, size_t *);

jack_ringbuffer_t *output_ring;
JackMidiBuffer *port_buffer;
jack_ringbuffer_t *rt_output_ring;
jack_midi_data_t running_status;

protected:

/**
* Override to specify the next frame at which a midi byte can be sent.
* The returned frame must be greater than or equal to the frame
* argument. The default returns the frame passed to it.
*/

virtual jack_nframes_t
Advance(jack_nframes_t);

/**
* Override to customize how to react when a MIDI event can't be
* buffered and can't be sent immediately. The default calls
* 'jack_error' and specifies the number of bytes lost.
*/

virtual void
HandleEventLoss(JackMidiEvent *);

/**
* This method *must* be overridden to specify what happens when a MIDI
* byte is sent at the specfied frame. The frame argument specifies
* the frame at which the MIDI byte should be sent, and the second
* argument specifies the byte itself. The return value is the next
* frame at which a MIDI byte can be sent, and must be greater than or
* equal to the frame argument.
*/

virtual jack_nframes_t
Send(jack_nframes_t, jack_midi_data_t) = 0;

/**
* Override to optimize behavior when sending MIDI data that's in the
* ringbuffer. The first frame argument is the current frame, and the
* second frame argument is the boundary frame. The function returns
* the next frame at which MIDI data can be sent, regardless of whether
* or not the boundary is reached. The default implementation calls
* 'Send' with each byte in the ringbuffer until either the ringbuffer
* is empty, or a frame beyond the boundary frame is returned by
* 'Send'.
*/

virtual jack_nframes_t
SendBufferedData(jack_ringbuffer_t *, jack_nframes_t, jack_nframes_t);

public:

/**
* The non-realtime buffer size and the realtime buffer size are both
* optional arguments.
*/

JackPhysicalMidiOutput(size_t non_rt_buffer_size=1024,
size_t rt_buffer_size=64);
virtual ~JackPhysicalMidiOutput();

/**
* Called to process MIDI data during a period.
*/

void
Process(jack_nframes_t);

/**
* Set the MIDI buffer that will contain the outgoing MIDI messages.
*/

inline void
SetPortBuffer(JackMidiBuffer *port_buffer)
{
this->port_buffer = port_buffer;
}

};

}

#endif

+ 1
- 1
common/JackPort.h View File

@@ -107,7 +107,7 @@ class SERVER_EXPORT JackPort
// Since we are in shared memory, the resulting pointer cannot be cached, so align it here...
jack_default_audio_sample_t* GetBuffer()
{
return (jack_default_audio_sample_t*)((long)fBuffer & ~15L) + 4;
return (jack_default_audio_sample_t*)((uintptr_t)fBuffer & ~15L) + 4;
}

int GetRefNum() const;


+ 1
- 3
common/JackServer.cpp View File

@@ -58,7 +58,7 @@ JackServer::JackServer(bool sync, bool temporary, int timeout, bool rt, int prio
new JackFreewheelDriver(fEngine, GetSynchroTable());
fThreadedFreewheelDriver = new JackThreadedDriver(freewheelDriver);

fFreewheelDriver = freewheelDriver;
fFreewheelDriver = freewheelDriver;
fDriverInfo = new JackDriverInfo();
fAudioDriver = NULL;
fFreewheel = false;
@@ -223,13 +223,11 @@ int JackServer::SetBufferSize(jack_nframes_t buffer_size)
}

if (fAudioDriver->SetBufferSize(buffer_size) == 0) {
fFreewheelDriver->SetBufferSize(buffer_size);
fEngine->NotifyBufferSize(buffer_size);
return fAudioDriver->Start();
} else { // Failure: try to restore current value
jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size);
fAudioDriver->SetBufferSize(current_buffer_size);
fFreewheelDriver->SetBufferSize(current_buffer_size);
fAudioDriver->Start();
// SetBufferSize actually failed, so return an error...
return -1;


+ 69
- 0
common/JackSession.h View File

@@ -0,0 +1,69 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004 Jack O'Quin
Copyright (C) 2010 Torben Hohn

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

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

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

#ifndef __jack_session_int_h__
#define __jack_session_int_h__

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

enum JackSessionEventType {
JackSessionSave = 1,
JackSessionSaveAndQuit = 2,
JackSessionSaveTemplate = 3
};

typedef enum JackSessionEventType jack_session_event_type_t;

enum JackSessionFlags {
JackSessionSaveError = 0x01,
JackSessionNeedTerminal = 0x02
};

typedef enum JackSessionFlags jack_session_flags_t;

struct _jack_session_event {
jack_session_event_type_t type;
const char *session_dir;
const char *client_uuid;
char *command_line;
jack_session_flags_t flags;
uint32_t future;
};

typedef struct _jack_session_event jack_session_event_t;

typedef void (*JackSessionCallback)(jack_session_event_t *event,
void *arg);

typedef struct {
const char *uuid;
const char *client_name;
const char *command;
jack_session_flags_t flags;
} jack_session_command_t;

#ifdef __cplusplus
}
#endif
#endif

+ 17
- 4
common/JackThreadedDriver.cpp View File

@@ -127,9 +127,24 @@ void JackThreadedDriver::RemoveSlave(JackDriverInterface* slave)
fDriver->RemoveSlave(slave);
}

int JackThreadedDriver::ProcessSlaves()
int JackThreadedDriver::ProcessReadSlaves()
{
return fDriver->ProcessSlaves();
return fDriver->ProcessReadSlaves();
}

int JackThreadedDriver::ProcessWriteSlaves()
{
return fDriver->ProcessWriteSlaves();
}

int JackThreadedDriver::ProcessRead()
{
return fDriver->ProcessRead();
}

int JackThreadedDriver::ProcessWrite()
{
return fDriver->ProcessWrite();
}

std::list<JackDriverInterface*> JackThreadedDriver::GetSlaves()
@@ -184,7 +199,6 @@ int JackThreadedDriver::Stop()
case JackThread::kIniting:
if (fThread.Kill() < 0) {
jack_error("Cannot kill thread");
return -1;
}
break;

@@ -192,7 +206,6 @@ int JackThreadedDriver::Stop()
case JackThread::kRunning:
if (fThread.Stop() < 0) {
jack_error("Cannot stop thread");
return -1;
}
break;



+ 8
- 1
common/JackThreadedDriver.h View File

@@ -89,10 +89,17 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi

virtual void SetMaster(bool onoff);
virtual bool GetMaster();

virtual void AddSlave(JackDriverInterface* slave);
virtual void RemoveSlave(JackDriverInterface* slave);

virtual std::list<JackDriverInterface*> GetSlaves();
virtual int ProcessSlaves();

virtual int ProcessReadSlaves();
virtual int ProcessWriteSlaves();

virtual int ProcessRead();
virtual int ProcessWrite();

virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2);
virtual JackClientControl* GetClientControl() const;


+ 26
- 22
common/JackWaitThreadedDriver.h View File

@@ -22,15 +22,15 @@
#define __JackWaitThreadedDriver__

#include "JackThreadedDriver.h"
#include "JackAudioDriver.h"
#include "JackDriver.h"

namespace Jack
{
/*!
\brief To be used as a wrapper of JackNetDriver.
\brief To be used as a wrapper of JackNetDriver.

The idea is to behave as the "dummy" driver, until the network connection is really started and processing starts.
The idea is to behave as the "dummy" driver, until the network connection is really started and processing starts.
The Execute method will call the ProcessNull method until the decorated driver Init method returns.
A helper JackDriverStarter thread is used for that purpose.
*/
@@ -38,54 +38,58 @@ A helper JackDriverStarter thread is used for that purpose.
class SERVER_EXPORT JackWaitThreadedDriver : public JackThreadedDriver
{
private:
struct SERVER_EXPORT JackDriverStarter : public JackRunnableInterface
struct SERVER_EXPORT JackDriverStarter : public JackRunnableInterface
{
JackDriver* fDriver;
JackThread fThread;
bool fRunning;
volatile bool fRunning;
JackDriverStarter(JackDriver* driver)
:fDriver(driver),fThread(this),fRunning(false)
{}
~JackDriverStarter()
{
fThread.Kill();
}
int Start()
{
fRunning = false;
return fThread.Start();
}
// JackRunnableInterface interface
bool Execute()
{
// Blocks until decorated driver is started (that is when it's Initialize method returns true).
while (!fDriver->Initialize()) {}
fRunning = true;
// Blocks until decorated driver is started (that is when it's Initialize method returns).
if (fDriver->Initialize()) {
fRunning = true;
} else {
jack_error("Initing net driver fails...");
}
return false;
}
};
JackDriverStarter fStarter;
public:

JackWaitThreadedDriver(JackDriver* netdriver)
:JackThreadedDriver(netdriver),fStarter(netdriver)
JackWaitThreadedDriver(JackDriver* net_driver)
:JackThreadedDriver(net_driver), fStarter(net_driver)
{}
virtual ~JackWaitThreadedDriver()
{}
// JackRunnableInterface interface
bool Init();
bool Execute();
};
};


} // end of namespace


+ 6
- 6
common/JackWeakAPI.cpp View File

@@ -235,7 +235,7 @@ DECL_FUNCTION(jack_time_t, jack_frames_to_time, (const jack_client_t *client, ja
DECL_FUNCTION(jack_nframes_t, jack_frame_time, (const jack_client_t *client), (client));
DECL_FUNCTION(jack_nframes_t, jack_last_frame_time, (const jack_client_t *client), (client));
DECL_FUNCTION(float, jack_cpu_load, (jack_client_t *client), (client));
DECL_FUNCTION_NULL(pthread_t, jack_client_thread_id, (jack_client_t *client), (client));
DECL_FUNCTION_NULL(jack_native_thread_t, jack_client_thread_id, (jack_client_t *client), (client));
DECL_VOID_FUNCTION(jack_set_error_function, (print_function fun), (fun));
DECL_VOID_FUNCTION(jack_set_info_function, (print_function fun), (fun));

@@ -261,17 +261,17 @@ DECL_VOID_FUNCTION(jack_set_transport_info, (jack_client_t *client, jack_transpo

DECL_FUNCTION(int, jack_client_real_time_priority, (jack_client_t* client), (client));
DECL_FUNCTION(int, jack_client_max_real_time_priority, (jack_client_t* client), (client));
DECL_FUNCTION(int, jack_acquire_real_time_scheduling, (pthread_t thread, int priority), (thread, priority));
DECL_FUNCTION(int, jack_acquire_real_time_scheduling, (jack_native_thread_t thread, int priority), (thread, priority));
DECL_FUNCTION(int, jack_client_create_thread, (jack_client_t* client,
pthread_t *thread,
jack_native_thread_t *thread,
int priority,
int realtime, // boolean
thread_routine routine,
void *arg), (client, thread, priority, realtime, routine, arg));
DECL_FUNCTION(int, jack_drop_real_time_scheduling, (pthread_t thread), (thread));
DECL_FUNCTION(int, jack_drop_real_time_scheduling, (jack_native_thread_t thread), (thread));

DECL_FUNCTION(int, jack_client_stop_thread, (jack_client_t* client, pthread_t thread), (client, thread));
DECL_FUNCTION(int, jack_client_kill_thread, (jack_client_t* client, pthread_t thread), (client, thread));
DECL_FUNCTION(int, jack_client_stop_thread, (jack_client_t* client, jack_native_thread_t thread), (client, thread));
DECL_FUNCTION(int, jack_client_kill_thread, (jack_client_t* client, jack_native_thread_t thread), (client, thread));
#ifndef WIN32
DECL_VOID_FUNCTION(jack_set_thread_creator, (jack_thread_creator_t jtc), (jtc));
#endif


+ 32
- 9
common/Jackdmp.cpp View File

@@ -191,7 +191,7 @@ int main(int argc, char* argv[])
const JSList * server_parameters;
const char* server_name = "default";
jackctl_driver_t * master_driver_ctl;
jackctl_driver_t * loopback_driver_ctl;
jackctl_driver_t * loopback_driver_ctl = NULL;
int replace_registry = 0;

const char *options = "-d:X:I:P:uvshVrRL:STFl:t:mn:p:"
@@ -476,14 +476,16 @@ int main(int argc, char* argv[])
fprintf(stderr, "Unknown driver \"%s\"\n", *it);
goto close_server;
}
jackctl_server_add_slave(server_ctl, slave_driver_ctl);
if (!jackctl_server_add_slave(server_ctl, slave_driver_ctl)) {
fprintf(stderr, "Driver \"%s\" cannot be loaded\n", *it);
goto close_server;
}
}

// Loopback driver
if (loopback > 0) {
loopback_driver_ctl = jackctl_server_get_driver(server_ctl, "loopback");

// XX: What if this fails?
if (loopback_driver_ctl != NULL) {
const JSList * loopback_parameters = jackctl_driver_get_parameters(loopback_driver_ctl);
param = jackctl_get_parameter(loopback_parameters, "channels");
@@ -491,9 +493,14 @@ int main(int argc, char* argv[])
value.ui = loopback;
jackctl_parameter_set_value(param, &value);
}
jackctl_server_add_slave(server_ctl, loopback_driver_ctl);
if (!jackctl_server_add_slave(server_ctl, loopback_driver_ctl)) {
fprintf(stderr, "Driver \"loopback\" cannot be loaded\n");
goto close_server;
}
} else {
fprintf(stderr, "Driver \"loopback\" not found\n");
goto close_server;
}

}

// Start the server
@@ -509,7 +516,10 @@ int main(int argc, char* argv[])
fprintf(stderr, "Unknown internal \"%s\"\n", *it);
goto stop_server;
}
jackctl_server_load_internal(server_ctl, internal_driver_ctl);
if (!jackctl_server_load_internal(server_ctl, internal_driver_ctl)) {
fprintf(stderr, "Internal client \"%s\" cannot be loaded\n", *it);
goto stop_server;
}
}

notify_server_start(server_name);
@@ -523,12 +533,25 @@ int main(int argc, char* argv[])
if (! jackctl_server_stop(server_ctl)) {
fprintf(stderr, "Cannot stop server...\n");
}
if (notify_sent) {
notify_server_stop(server_name);
}
close_server:
if (loopback > 0 && loopback_driver_ctl) {
jackctl_server_remove_slave(server_ctl, loopback_driver_ctl);
}
// Slave drivers
for (it = slaves_list.begin(); it != slaves_list.end(); it++) {
jackctl_driver_t * slave_driver_ctl = jackctl_server_get_driver(server_ctl, *it);
jackctl_server_remove_slave(server_ctl, slave_driver_ctl);
}
// Internal clients
for (it = internals_list.begin(); it != internals_list.end(); it++) {
jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it);
jackctl_server_unload_internal(server_ctl, internal_driver_ctl);
}
jackctl_server_close(server_ctl);
destroy_server:
jackctl_server_destroy(server_ctl);
if (notify_sent) {
notify_server_stop(server_name);
}
return return_value;
}

+ 3
- 3
common/jack/jack.h View File

@@ -233,7 +233,7 @@ jack_nframes_t jack_thread_wait (jack_client_t*, int status) JACK_OPTIONAL_WEAK_
*
* @return the number of frames of data to process
*/
jack_nframes_t jack_cycle_wait (jack_client_t* client) JACK_OPTIONAL_WEAK_EXPORT;
jack_nframes_t jack_cycle_wait (jack_client_t* client) JACK_OPTIONAL_WEAK_EXPORT;

/**
* Signal next clients in the graph.
@@ -310,7 +310,7 @@ int jack_set_thread_init_callback (jack_client_t *client,
* jack_on_info_shutdown() will.
*/
void jack_on_shutdown (jack_client_t *client,
JackShutdownCallback shutdown_callback, void *arg) JACK_WEAK_EXPORT;
JackShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;

/**
* @param client pointer to JACK client structure.
@@ -336,7 +336,7 @@ void jack_on_shutdown (jack_client_t *client,
* jack_on_info_shutdown() will.
*/
void jack_on_info_shutdown (jack_client_t *client,
JackInfoShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
JackInfoShutdownCallback shutdown_callback, void *arg) JACK_WEAK_EXPORT;

/**
* Tell the Jack server to call @a process_callback whenever there is


+ 22
- 11
common/jack/weakmacros.h View File

@@ -40,13 +40,23 @@
the symbol it used with. For this to work full may
require linker arguments in the client as well.
*/
#define JACK_WEAK_EXPORT __attribute__((weak))

#ifdef WIN32
/*
Not working with __declspec(dllexport) so normal linking
Linking with JackWeakAPI.cpp will be the preferred way.
*/
#define JACK_WEAK_EXPORT
#else
/* Add other things here for non-gcc platforms */
#ifdef WIN32
#define JACK_WEAK_EXPORT
#endif
#define JACK_WEAK_EXPORT __attribute__((weak))
#endif

#else
/* Add other things here for non-gcc platforms */

#ifdef WIN32
#define JACK_WEAK_EXPORT
#endif

#endif
#endif
@@ -59,13 +69,14 @@
#ifdef __GNUC__
#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((__deprecated__))
#else
/* Add other things here for non-gcc platforms */
#ifdef WIN32
#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
#endif
/* Add other things here for non-gcc platforms */
#ifdef WIN32
#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
#endif

#endif /* __GNUC__ */
#endif

#endif /* __weakmacros_h__ */


+ 2
- 3
common/netjack.c View File

@@ -55,12 +55,11 @@ $Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $
#include <samplerate.h>
#endif

#include "JackError.h"

#include "netjack.h"
#include "netjack_packet.h"

// JACK2
#include "control.h"

#define MIN(x,y) ((x)<(y) ? (x) : (y))

static int sync_state = 1;


+ 2
- 3
common/netjack_packet.c View File

@@ -68,15 +68,14 @@
#include <samplerate.h>
#endif

#include "JackError.h"

#if HAVE_CELT
#include <celt/celt.h>
#endif

#include "netjack_packet.h"

// JACK2 specific.
#include "control.h"

#ifdef NO_JACK_ERROR
#define jack_error printf
#endif


+ 15
- 2
common/wscript View File

@@ -130,8 +130,21 @@ def build(bld):
'JackNetTool.cpp',
'JackNetInterface.cpp',
'JackArgParser.cpp',
'JackPhysicalMidiInput.cpp',
'JackPhysicalMidiOutput.cpp',

#'JackPhysicalMidiInput.cpp',
#'JackPhysicalMidiOutput.cpp',

'JackMidiAsyncQueue.cpp',
'JackMidiAsyncWaitQueue.cpp',
'JackMidiBufferReadQueue.cpp',
'JackMidiBufferWriteQueue.cpp',
'JackMidiRawInputWriteQueue.cpp',
'JackMidiRawOutputWriteQueue.cpp',
'JackMidiReadQueue.cpp',
'JackMidiReceiveQueue.cpp',
'JackMidiSendQueue.cpp',
'JackMidiUtil.cpp',
'JackMidiWriteQueue.cpp'
]

if bld.env['IS_LINUX']:


+ 92
- 18
dbus/controller.c View File

@@ -64,6 +64,56 @@ jack_controller_find_driver(
return NULL;
}

bool
jack_controller_add_slave_drivers(
struct jack_controller * controller_ptr)
{
struct list_head * node_ptr;
struct jack_controller_slave_driver * driver_ptr;

list_for_each(node_ptr, &controller_ptr->slave_drivers)
{
driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings);
driver_ptr->handle = jack_controller_find_driver(controller_ptr->server, driver_ptr->name);

if (driver_ptr->handle == NULL)
{
jack_error("Unknown driver \"%s\"", driver_ptr->name);
goto fail;
}

if (!jackctl_server_add_slave(controller_ptr->server, driver_ptr->handle))
{
jack_error("Driver \"%s\" cannot be loaded", driver_ptr->name);
goto fail;
}
}

return true;

fail:
driver_ptr->handle = NULL;
return false;
}

void
jack_controller_remove_slave_drivers(
struct jack_controller * controller_ptr)
{
struct list_head * node_ptr;
struct jack_controller_slave_driver * driver_ptr;

list_for_each(node_ptr, &controller_ptr->slave_drivers)
{
driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings);
if (driver_ptr->handle != NULL)
{
jackctl_server_remove_slave(controller_ptr->server, driver_ptr->handle);
driver_ptr->handle = NULL;
}
}
}

jackctl_internal_t *
jack_controller_find_internal(
jackctl_server_t *server,
@@ -161,6 +211,8 @@ jack_controller_start_server(
goto fail;
}

jack_controller_add_slave_drivers(controller_ptr);

if (!jackctl_server_start(
controller_ptr->server))
{
@@ -221,6 +273,8 @@ fail_stop_server:
}

fail_close_server:
jack_controller_remove_slave_drivers(controller_ptr);

if (!jackctl_server_close(controller_ptr->server))
{
jack_error("failed to close jack server");
@@ -263,6 +317,8 @@ jack_controller_stop_server(
return FALSE;
}

jack_controller_remove_slave_drivers(controller_ptr);

if (!jackctl_server_close(controller_ptr->server))
{
jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to close server");
@@ -387,6 +443,7 @@ jack_controller_create(
controller_ptr->started = false;
controller_ptr->driver = NULL;
controller_ptr->driver_set = false;
INIT_LIST_HEAD(&controller_ptr->slave_drivers);

drivers = (JSList *)jackctl_server_get_drivers_list(controller_ptr->server);
controller_ptr->drivers_count = jack_slist_length(drivers);
@@ -465,41 +522,58 @@ fail:
}

bool
jack_controller_add_slave(
struct jack_controller *controller_ptr,
jack_controller_add_slave_driver(
struct jack_controller * controller_ptr,
const char * driver_name)
{
jackctl_driver_t *driver;
struct jack_controller_slave_driver * driver_ptr;

driver = jack_controller_find_driver(controller_ptr->server, driver_name);
if (driver == NULL)
driver_ptr = malloc(sizeof(struct jack_controller_slave_driver));
if (driver_ptr == NULL)
{
jack_error("malloc() failed to allocate jack_controller_slave_driver struct");
return false;
}

jack_info("driver \"%s\" selected", driver_name);
driver_ptr->name = strdup(driver_name);
if (driver_ptr->name == NULL)
{
jack_error("strdup() failed for slave driver name \"%s\"", driver_name);
free(driver_ptr);
return false;
}

driver_ptr->handle = NULL;

return jackctl_server_add_slave(controller_ptr->server, driver);
jack_info("slave driver \"%s\" added", driver_name);

list_add_tail(&driver_ptr->siblings, &controller_ptr->slave_drivers);

return true;
}

bool
jack_controller_remove_slave(
struct jack_controller *controller_ptr,
jack_controller_remove_slave_driver(
struct jack_controller * controller_ptr,
const char * driver_name)
{
jackctl_driver_t *driver;
struct list_head * node_ptr;
struct jack_controller_slave_driver * driver_ptr;

driver = jack_controller_find_driver(controller_ptr->server, driver_name);
if (driver == NULL)
list_for_each(node_ptr, &controller_ptr->slave_drivers)
{
return false;
driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings);
if (strcmp(driver_ptr->name, driver_name) == 0)
{
jack_info("slave driver \"%s\" removed", driver_name);
list_del(&driver_ptr->siblings);
free(driver_ptr->name);
free(driver_ptr);
return true;
}
}

jack_info("driver \"%s\" selected", driver_name);

return jackctl_server_remove_slave(controller_ptr->server, driver);
return false;
}

bool


+ 75
- 118
dbus/controller_iface_configure.c View File

@@ -603,6 +603,73 @@ oom:
jack_error ("Ran out of memory trying to construct method return");
}

static
void
jack_controller_fill_parameter_info(
jackctl_parameter_t * parameter,
struct parameter_info * info_ptr)
{
info_ptr->type = jackctl_parameter_get_type(parameter);
info_ptr->name = jackctl_parameter_get_name(parameter);
info_ptr->short_decr = jackctl_parameter_get_short_description(parameter);
info_ptr->long_descr = jackctl_parameter_get_long_description(parameter);
}

static
bool
jack_controller_append_parameter_info_struct(
DBusMessageIter * iter_ptr,
struct parameter_info * info_ptr)
{
DBusMessageIter struct_iter;
unsigned char type;

/* Open the struct. */
if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_STRUCT, NULL, &struct_iter))
{
goto fail;
}

/* Append parameter type. */
type = PARAM_TYPE_JACK_TO_DBUS(info_ptr->type);
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &type))
{
goto fail_close;
}

/* Append parameter name. */
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->name))
{
goto fail_close;
}

/* Append parameter short description. */
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->short_decr))
{
goto fail_close;
}

/* Append parameter long description. */
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->long_descr))
{
goto fail_close;
}

/* Close the struct. */
if (!dbus_message_iter_close_container(iter_ptr, &struct_iter))
{
goto fail;
}

return true;

fail_close:
dbus_message_iter_close_container(iter_ptr, &struct_iter);

fail:
return false;
}

static
void
jack_controller_get_parameters_info(
@@ -610,9 +677,8 @@ jack_controller_get_parameters_info(
struct parameter_info * special_parameter_info_ptr,
const JSList * parameters_list)
{
DBusMessageIter iter, array_iter, struct_iter;
unsigned char type;
const char *str;
DBusMessageIter iter, array_iter;
struct parameter_info info;

call->reply = dbus_message_new_method_return (call->message);
if (!call->reply)
@@ -630,39 +696,7 @@ jack_controller_get_parameters_info(

if (special_parameter_info_ptr != NULL)
{
/* Open the struct. */
if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
{
goto fail_close_unref;
}

/* Append parameter type. */
type = PARAM_TYPE_JACK_TO_DBUS(special_parameter_info_ptr->type);
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BYTE, &type))
{
goto fail_close2_unref;
}

/* Append parameter name. */
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &special_parameter_info_ptr->name))
{
goto fail_close2_unref;
}

/* Append parameter short description. */
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &special_parameter_info_ptr->short_decr))
{
goto fail_close2_unref;
}

/* Append parameter long description. */
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &special_parameter_info_ptr->long_descr))
{
goto fail_close2_unref;
}

/* Close the struct. */
if (!dbus_message_iter_close_container (&array_iter, &struct_iter))
if (!jack_controller_append_parameter_info_struct(&array_iter, special_parameter_info_ptr))
{
goto fail_close_unref;
}
@@ -671,42 +705,8 @@ jack_controller_get_parameters_info(
/* Append parameter descriptions to the array. */
while (parameters_list != NULL)
{
/* Open the struct. */
if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
{
goto fail_close_unref;
}

/* Append parameter type. */
type = PARAM_TYPE_JACK_TO_DBUS(jackctl_parameter_get_type(parameters_list->data));
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BYTE, &type))
{
goto fail_close2_unref;
}

/* Append parameter name. */
str = jackctl_parameter_get_name(parameters_list->data);
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &str))
{
goto fail_close2_unref;
}

/* Append parameter short description. */
str = jackctl_parameter_get_short_description(parameters_list->data);
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &str))
{
goto fail_close2_unref;
}

/* Append parameter long description. */
str = jackctl_parameter_get_long_description(parameters_list->data);
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &str))
{
goto fail_close2_unref;
}

/* Close the struct. */
if (!dbus_message_iter_close_container (&array_iter, &struct_iter))
jack_controller_fill_parameter_info(parameters_list->data, &info);
if (!jack_controller_append_parameter_info_struct(&array_iter, &info))
{
goto fail_close_unref;
}
@@ -722,9 +722,6 @@ jack_controller_get_parameters_info(

return;

fail_close2_unref:
dbus_message_iter_close_container (&iter, &struct_iter);

fail_close_unref:
dbus_message_iter_close_container (&iter, &array_iter);

@@ -851,8 +848,7 @@ jack_controller_get_parameter_info_ex(
struct jack_dbus_method_call * call,
struct parameter_info * info_ptr)
{
DBusMessageIter iter, struct_iter;
unsigned char type;
DBusMessageIter iter;

call->reply = dbus_message_new_method_return(call->message);
if (!call->reply)
@@ -862,48 +858,13 @@ jack_controller_get_parameter_info_ex(

dbus_message_iter_init_append(call->reply, &iter);

/* Open the struct. */
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
{
goto fail_unref;
}

/* Append parameter type. */
type = PARAM_TYPE_JACK_TO_DBUS(info_ptr->type);
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &type))
{
goto fail_close_unref;
}

/* Append parameter name. */
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->name))
{
goto fail_close_unref;
}

/* Append parameter short description. */
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->short_decr))
{
goto fail_close_unref;
}

/* Append parameter long description. */
if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &info_ptr->long_descr))
{
goto fail_close_unref;
}

/* Close the struct. */
if (!dbus_message_iter_close_container(&iter, &struct_iter))
if (!jack_controller_append_parameter_info_struct(&iter, info_ptr))
{
goto fail_unref;
}

return;

fail_close_unref:
dbus_message_iter_close_container(&iter, &struct_iter);

fail_unref:
dbus_message_unref(call->reply);
call->reply = NULL;
@@ -920,11 +881,7 @@ jack_controller_get_parameter_info(
{
struct parameter_info info;

info.type = jackctl_parameter_get_type(parameter);
info.name = jackctl_parameter_get_name(parameter);
info.short_decr = jackctl_parameter_get_short_description(parameter);
info.long_descr = jackctl_parameter_get_long_description(parameter);

jack_controller_fill_parameter_info(parameter, &info);
jack_controller_get_parameter_info_ex(call, &info);
}



+ 32
- 14
dbus/controller_iface_control.c View File

@@ -229,10 +229,15 @@ jack_control_run_method(
"jack_controller_load_internal failed for internal (%s)", internal_name);
}
}
else if (strcmp (call->method_name, "AddSlave") == 0)
else if (strcmp (call->method_name, "AddSlaveDriver") == 0)
{
const char *driver_name;

if (controller_ptr->started)
{
goto fail_started;
}

if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &driver_name, DBUS_TYPE_INVALID))
{
/* The method call had invalid arguments meaning that
@@ -241,17 +246,22 @@ jack_control_run_method(
goto exit;
}
if (!jack_controller_add_slave(controller_ptr, driver_name)) {
if (!jack_controller_add_slave_driver(controller_ptr, driver_name)) {
jack_dbus_error(
call,
JACK_DBUS_ERROR_GENERIC,
"jack_controller_add_slave failed for driver (%s)", driver_name);
"jack_controller_add_slave_driver failed for driver (%s)", driver_name);
}
}
else if (strcmp (call->method_name, "RemoveSlave") == 0)
else if (strcmp (call->method_name, "RemoveSlaveDriver") == 0)
{
const char *driver_name;

if (controller_ptr->started)
{
goto fail_started;
}

if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &driver_name, DBUS_TYPE_INVALID))
{
/* The method call had invalid arguments meaning that
@@ -260,11 +270,11 @@ jack_control_run_method(
goto exit;
}
if (!jack_controller_remove_slave(controller_ptr, driver_name)) {
if (!jack_controller_remove_slave_driver(controller_ptr, driver_name)) {
jack_dbus_error(
call,
JACK_DBUS_ERROR_GENERIC,
"jack_controller_remove_slave failed for driver (%s)", driver_name);
"jack_controller_remove_slave_driver failed for driver (%s)", driver_name);
}
}
else if (strcmp (call->method_name, "UnloadInternal") == 0)
@@ -293,8 +303,7 @@ jack_control_run_method(
}

jack_dbus_construct_method_return_single(call, type, arg);

return true;
goto exit;

not_started:
jack_dbus_only_error(
@@ -302,6 +311,15 @@ not_started:
JACK_DBUS_ERROR_SERVER_NOT_RUNNING,
"Can't execute method '%s' with stopped JACK server",
call->method_name);
goto exit;

fail_started:
jack_dbus_only_error(
call,
JACK_DBUS_ERROR_SERVER_RUNNING,
"Can't execute method '%s' with started JACK server",
call->method_name);
goto exit;

exit:
return true;
@@ -361,12 +379,12 @@ JACK_DBUS_METHOD_ARGUMENTS_BEGIN(UnloadInternal)
JACK_DBUS_METHOD_ARGUMENT("internal", "s", false)
JACK_DBUS_METHOD_ARGUMENTS_END

JACK_DBUS_METHOD_ARGUMENTS_BEGIN(AddSlave)
JACK_DBUS_METHOD_ARGUMENT("internal", "s", false)
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(AddSlaveDriver)
JACK_DBUS_METHOD_ARGUMENT("driver_name", "s", false)
JACK_DBUS_METHOD_ARGUMENTS_END

JACK_DBUS_METHOD_ARGUMENTS_BEGIN(RemoveSlave)
JACK_DBUS_METHOD_ARGUMENT("internal", "s", false)
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(RemoveSlaveDriver)
JACK_DBUS_METHOD_ARGUMENT("driver_name", "s", false)
JACK_DBUS_METHOD_ARGUMENTS_END

JACK_DBUS_METHODS_BEGIN
@@ -384,8 +402,8 @@ JACK_DBUS_METHODS_BEGIN
JACK_DBUS_METHOD_DESCRIBE(ResetXruns, NULL)
JACK_DBUS_METHOD_DESCRIBE(LoadInternal, NULL)
JACK_DBUS_METHOD_DESCRIBE(UnloadInternal, NULL)
JACK_DBUS_METHOD_DESCRIBE(AddSlave, NULL)
JACK_DBUS_METHOD_DESCRIBE(RemoveSlave, NULL)
JACK_DBUS_METHOD_DESCRIBE(AddSlaveDriver, NULL)
JACK_DBUS_METHOD_DESCRIBE(RemoveSlaveDriver, NULL)
JACK_DBUS_METHODS_END

JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(ServerStarted)


+ 11
- 2
dbus/controller_internal.h View File

@@ -26,6 +26,14 @@
#include "jack/control.h"
#include "jack/jack.h"
#include "jackdbus.h"
#include "list.h"

struct jack_controller_slave_driver
{
struct list_head siblings;
char * name;
jackctl_driver_t * handle;
};

struct jack_controller
{
@@ -45,6 +53,7 @@ struct jack_controller

jackctl_driver_t *driver;
bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */
struct list_head slave_drivers;

struct jack_dbus_object_descriptor dbus_descriptor;
};
@@ -87,12 +96,12 @@ jack_controller_switch_master(
void *dbus_call_context_ptr);
bool
jack_controller_add_slave(
jack_controller_add_slave_driver(
struct jack_controller *controller_ptr,
const char * driver_name);
bool
jack_controller_remove_slave(
jack_controller_remove_slave_driver(
struct jack_controller *controller_ptr,
const char * driver_name);



+ 1
- 0
dbus/jackdbus.h View File

@@ -46,6 +46,7 @@ jack_controller_settings_uninit();

#define JACK_DBUS_ERROR_UNKNOWN_METHOD "org.jackaudio.Error.UnknownMethod"
#define JACK_DBUS_ERROR_SERVER_NOT_RUNNING "org.jackaudio.Error.ServerNotRunning"
#define JACK_DBUS_ERROR_SERVER_RUNNING "org.jackaudio.Error.ServerRunning"
#define JACK_DBUS_ERROR_UNKNOWN_DRIVER "org.jackaudio.Error.UnknownDriver"
#define JACK_DBUS_ERROR_UNKNOWN_INTERNAL "org.jackaudio.Error.UnknownInternal"
#define JACK_DBUS_ERROR_UNKNOWN_PARAMETER "org.jackaudio.Error.UnknownParameter"


+ 1
- 1
doxyfile View File

@@ -31,7 +31,7 @@ PROJECT_NAME = "Jack2"
# This could be handy for archiving the generated documentation or
# if some version control system is used.

PROJECT_NUMBER = 1.9.7
PROJECT_NUMBER = 1.9.8

# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.


+ 22
- 0
example-clients/jack_control View File

@@ -116,6 +116,8 @@ def main():
print " dp - get parameters of currently selected driver"
print " dpd <param> - get long description for driver parameter"
print " dps <param> <value> - set driver parameter"
print " asd <driver> - add slave driver"
print " rsd <driver> - remove slave driver"
print " il - get list of available internals"
print " ip <name> - get parameters of given internal"
print " ipd <name> <param> - get long description for internal parameter"
@@ -305,6 +307,26 @@ def main():
name = sys.argv[index]
index += 1
result = control_iface.UnloadInternal(name)
elif arg == 'asd':
print "--- add slave driver"

if index >= len(sys.argv):
print "add slave driver command requires driver name argument"
sys.exit()

name = sys.argv[index]
index += 1
result = control_iface.AddSlaveDriver(name)
elif arg == 'rsd':
print "--- remove slave driver"

if index >= len(sys.argv):
print "remove slave driver command requires driver name argument"
sys.exit()

name = sys.argv[index]
index += 1
result = control_iface.RemoveSlaveDriver(name)
else:
print "Unknown command '%s'" % arg
except dbus.DBusException, e:


+ 9
- 8
example-clients/latent_client.c View File

@@ -1,4 +1,4 @@
/** @file simple_client.c
/** @file latent_client.c
*
* @brief This simple client demonstrates the most basic features of JACK
* as they would be used by many applications.
@@ -8,7 +8,7 @@
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <inttypes.h>

#include <jack/jack.h>
@@ -19,12 +19,12 @@ jack_client_t *client;

jack_default_audio_sample_t *delay_line;
jack_nframes_t delay_index;
jack_nframes_t latency = 1024;
#ifdef WIN32
#define jack_sleep(val) Sleep((val))
#else
#define jack_sleep(val) usleep((val) * 1000)
jack_nframes_t latency = 1024;
#ifdef WIN32
#define jack_sleep(val) Sleep((val))
#else
#define jack_sleep(val) usleep((val) * 1000)
#endif

/**
@@ -213,3 +213,4 @@ main (int argc, char *argv[])
jack_client_close (client);
exit (0);
}


+ 777
- 0
example-clients/midi_latency_test.c View File

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

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

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

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

*/

/*
* This program is used to measure MIDI latency and jitter. It writes MIDI
* messages to one port and calculates how long it takes before it reads the
* same MIDI message over another port. It was written to calculate the
* latency and jitter of hardware and JACK hardware drivers, but might have
* other practical applications.
*
* The latency results of the program include the latency introduced by the
* JACK system. Because JACK has sample accurate MIDI, the same latency
* imposed on audio is also imposed on MIDI going through the system. Make
* sure you take this into account before complaining to me or (*especially*)
* other JACK developers about reported MIDI latency.
*
* The jitter results are a little more interesting. The program attempts to
* calculate 'average jitter' and 'peak jitter', as defined here:
*
* http://openmuse.org/transport/fidelity.html
*
* It also outputs a jitter plot, which gives you a more specific idea about
* the MIDI jitter for the ports you're testing. This is useful for catching
* extreme jitter values, and for analyzing the amount of truth in the
* technical specifications for your MIDI interface(s). :)
*
* This program is loosely based on 'alsa-midi-latency-test' in the ALSA test
* suite.
*
* To port this program to non-POSIX platforms, you'll have to include
* implementations for semaphores and command-line argument handling.
*/

#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <getopt.h>

#include <jack/jack.h>
#include <jack/midiport.h>

#ifdef WIN32
#include <windows.h>
#include <unistd.h>
#else
#include <semaphore.h>
#endif

#include <signal.h>

#define ABS(x) (((x) >= 0) ? (x) : (-(x)))

#ifdef WIN32
typedef HANDLE semaphore_t;
#else
typedef sem_t *semaphore_t;
#endif

const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer";
const char *ERROR_SHUTDOWN = "the JACK server has been shutdown";
const char *ERROR_TIMEOUT1 = "timed out while waiting for MIDI message";

const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve";
const char *SOURCE_PROCESS = "handle_process";
const char *SOURCE_SHUTDOWN = "handle_shutdown";
const char *SOURCE_SIGNAL_SEMAPHORE = "signal_semaphore";
const char *SOURCE_WAIT_SEMAPHORE = "wait_semaphore";

jack_client_t *client;
const char *error_message;
const char *error_source;
jack_nframes_t highest_latency;
jack_time_t highest_latency_time;
jack_latency_range_t in_latency_range;
jack_port_t *in_port;
semaphore_t init_semaphore;
jack_nframes_t last_activity;
jack_time_t last_activity_time;
jack_time_t *latency_time_values;
jack_nframes_t *latency_values;
jack_nframes_t lowest_latency;
jack_time_t lowest_latency_time;
jack_midi_data_t *message_1;
jack_midi_data_t *message_2;
size_t messages_received;
size_t messages_sent;
size_t message_size;
jack_latency_range_t out_latency_range;
jack_port_t *out_port;
semaphore_t process_semaphore;
int process_state;
char *program_name;
jack_port_t *remote_in_port;
jack_port_t *remote_out_port;
size_t samples;
int timeout;
jack_nframes_t total_latency;
jack_time_t total_latency_time;
size_t unexpected_messages;
size_t xrun_count;

static void signal_handler(int sig)
{
jack_client_close(client);
fprintf(stderr, "signal received, exiting ...\n");
exit(0);
}

#ifdef WIN32
char semaphore_error_msg[1024];
#endif

static void
output_error(const char *source, const char *message);

static void
output_usage();

static void
set_process_error(const char *source, const char *message);

static int
signal_semaphore(semaphore_t semaphore);

static int
wait_semaphore(semaphore_t semaphore, int block);

static semaphore_t
create_semaphore(int id)
{
semaphore_t semaphore;

#ifdef WIN32
semaphore = CreateSemaphore(NULL, 0, 1, NULL);
#elif defined (__APPLE__)
char name[128];
sprintf(name, "midi_sem_%d", id);
semaphore = sem_open(name, O_CREAT, 0777, 0);
if (semaphore == (sem_t *) SEM_FAILED) {
semaphore = NULL;
}
#else
semaphore = malloc(sizeof(semaphore_t));
if (semaphore != NULL) {
if (sem_init(semaphore, 0, 0)) {
free(semaphore);
semaphore = NULL;
}
}
#endif

return semaphore;
}

static void
destroy_semaphore(semaphore_t semaphore, int id)
{

#ifdef WIN32
CloseHandle(semaphore);
#else
sem_destroy(semaphore);
#ifdef __APPLE__
{
char name[128];
sprintf(name, "midi_sem_%d", id);
sem_unlink(name);
}
#else
free(semaphore);
#endif
#endif

}

static void
die(const char *source, const char *error_message)
{
output_error(source, error_message);
output_usage();
exit(EXIT_FAILURE);
}

static const char *
get_semaphore_error()
{

#ifdef WIN32
DWORD error = GetLastError();
if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
semaphore_error_msg, 1024, NULL)) {
snprintf(semaphore_error_msg, 1023, "Unknown OS error code '%d'",
error);
}
return semaphore_error_msg;
#else
return strerror(errno);
#endif

}

static void
handle_info(const char *message)
{
/* Suppress info */
}

static int
handle_process(jack_nframes_t frames, void *arg)
{
jack_midi_data_t *buffer;
jack_midi_event_t event;
jack_nframes_t event_count;
jack_nframes_t event_time;
jack_nframes_t frame;
size_t i;
jack_nframes_t last_frame_time;
jack_midi_data_t *message;
jack_time_t microseconds;
void *port_buffer;
jack_time_t time;
jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames));
switch (process_state) {

case 0:
/* State: initializing */
switch (wait_semaphore(init_semaphore, 0)) {
case -1:
set_process_error(SOURCE_WAIT_SEMAPHORE, get_semaphore_error());
// Fallthrough on purpose
case 0:
return 0;
}
highest_latency = 0;
lowest_latency = 0;
messages_received = 0;
messages_sent = 0;
process_state = 1;
total_latency = 0;
total_latency_time = 0;
unexpected_messages = 0;
xrun_count = 0;
jack_port_get_latency_range(remote_in_port, JackCaptureLatency,
&in_latency_range);
jack_port_get_latency_range(remote_out_port, JackPlaybackLatency,
&out_latency_range);
goto send_message;

case 1:
/* State: processing */
port_buffer = jack_port_get_buffer(in_port, frames);
event_count = jack_midi_get_event_count(port_buffer);
last_frame_time = jack_last_frame_time(client);
for (i = 0; i < event_count; i++) {
jack_midi_event_get(&event, port_buffer, i);
message = (messages_received % 2) ? message_2 : message_1;
if ((event.size == message_size) &&
(! memcmp(message, event.buffer,
message_size * sizeof(jack_midi_data_t)))) {
goto found_message;
}
unexpected_messages++;
}
microseconds = jack_frames_to_time(client, last_frame_time) -
last_activity_time;
if ((microseconds / 1000000) >= timeout) {
set_process_error(SOURCE_PROCESS, ERROR_TIMEOUT1);
}
break;
found_message:
event_time = last_frame_time + event.time;
frame = event_time - last_activity;
time = jack_frames_to_time(client, event_time) - last_activity_time;
if ((! highest_latency) || (frame > highest_latency)) {
highest_latency = frame;
highest_latency_time = time;
}
if ((! lowest_latency) || (frame < lowest_latency)) {
lowest_latency = frame;
lowest_latency_time = time;
}
latency_time_values[messages_received] = time;
latency_values[messages_received] = frame;
total_latency += frame;
total_latency_time += time;
messages_received++;
if (messages_received == samples) {
process_state = 2;
if (! signal_semaphore(process_semaphore)) {
// Sigh ...
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
}
break;
}
send_message:
frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames);
if (frame >= frames) {
frame = frames - 1;
}
port_buffer = jack_port_get_buffer(out_port, frames);
buffer = jack_midi_event_reserve(port_buffer, frame, message_size);
if (buffer == NULL) {
set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE);
break;
}
message = (messages_sent % 2) ? message_2 : message_1;
memcpy(buffer, message, message_size * sizeof(jack_midi_data_t));
last_activity = jack_last_frame_time(client) + frame;
last_activity_time = jack_frames_to_time(client, last_activity);
messages_sent++;

case 2:
/* State: finished - do nothing */

case -1:
/* State: error - do nothing */
;

}
return 0;
}

static void
handle_shutdown(void *arg)
{
set_process_error(SOURCE_SHUTDOWN, ERROR_SHUTDOWN);
}

static int
handle_xrun(void *arg)
{
xrun_count++;
return 0;
}

static void
output_error(const char *source, const char *message)
{
fprintf(stderr, "%s: %s: %s\n", program_name, source, message);
}

static void
output_usage()
{
fprintf(stderr, "Usage: %s [options] out-port-name in-port-name\n\n"
"\t-h, --help print program usage\n"
"\t-m, --message-size=size set size of MIDI messages to send\n"
"\t-s, --samples=n number of MIDI messages to send\n"
"\t-t, --timeout=seconds wait time before giving up on message\n"
"\n", program_name);
}

static unsigned long
parse_positive_number_arg(char *s, char *name)
{
char *end_ptr;
unsigned long result;
errno = 0;
result = strtoul(s, &end_ptr, 10);
if (errno) {
die(name, strerror(errno));
}
if (*s == '\0') {
die(name, "argument value cannot be empty");
}
if (*end_ptr != '\0') {
die(name, "invalid value");
}
if (! result) {
die(name, "must be a positive number");
}
return result;
}

static void
set_process_error(const char *source, const char *message)
{
error_source = source;
error_message = message;
process_state = -1;
if (! signal_semaphore(process_semaphore)) {
// Sigh
output_error(source, message);
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
}
}

static int
signal_semaphore(semaphore_t semaphore)
{

#ifdef WIN32
return ReleaseSemaphore(semaphore, 1, NULL);
#else
return ! sem_post(semaphore);
#endif

}

static int
wait_semaphore(semaphore_t semaphore, int block)
{

#ifdef WIN32
DWORD result = WaitForSingleObject(semaphore, block ? INFINITE : 0);
switch (result) {
case WAIT_OBJECT_0:
return 1;
case WAIT_TIMEOUT:
return 0;
}
return -1;
#else
if (block) {
while (sem_wait(semaphore)) {
if (errno != EINTR) {
return -1;
}
}
} else {
while (sem_trywait(semaphore)) {
switch (errno) {
case EAGAIN:
return 0;
case EINTR:
continue;
default:
return -1;
}
}
}
return 1;
#endif

}

int
main(int argc, char **argv)
{
size_t jitter_plot[101];
size_t latency_plot[101];
int long_index = 0;
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"message-size", 1, NULL, 'm'},
{"samples", 1, NULL, 's'},
{"timeout", 1, NULL, 't'}
};
char *option_string = "hm:s:t:";
int show_usage = 0;
error_message = NULL;
message_size = 3;
program_name = argv[0];
samples = 1024;
timeout = 5;

for (;;) {
char c = getopt_long(argc, argv, option_string, long_options,
&long_index);
switch (c) {
case 'h':
show_usage = 1;
break;
case 'm':
message_size = parse_positive_number_arg(optarg, "message-size");
break;
case 's':
samples = parse_positive_number_arg(optarg, "samples");
break;
case 't':
timeout = parse_positive_number_arg(optarg, "timeout");
break;
default:
{
char *s = "'- '";
s[2] = c;
die(s, "invalid switch");
}
case -1:
if (show_usage) {
output_usage();
exit(EXIT_SUCCESS);
}
goto parse_port_names;
case 1:
/* end of switch :) */
;
}
}
parse_port_names:
if ((argc - optind) != 2) {
output_usage();
return EXIT_FAILURE;
}
latency_values = malloc(sizeof(jack_nframes_t) * samples);
if (latency_values == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto show_error;
}
latency_time_values = malloc(sizeof(jack_time_t) * samples);
if (latency_time_values == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto free_latency_values;
}
message_1 = malloc(message_size * sizeof(jack_midi_data_t));
if (message_1 == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto free_latency_time_values;
}
message_2 = malloc(message_size * sizeof(jack_midi_data_t));
if (message_2 == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto free_message_1;
}
switch (message_size) {
case 1:
message_1[0] = 0xf6;
message_2[0] = 0xfe;
break;
case 2:
message_1[0] = 0xc0;
message_1[1] = 0x00;
message_2[0] = 0xd0;
message_2[1] = 0x7f;
break;
case 3:
message_1[0] = 0x80;
message_1[1] = 0x00;
message_1[2] = 0x00;
message_2[0] = 0x90;
message_2[1] = 0x7f;
message_2[2] = 0x7f;
break;
default:
message_1[0] = 0xf0;
memset(message_1 + 1, 0,
(message_size - 2) * sizeof(jack_midi_data_t));
message_1[message_size - 1] = 0xf7;
message_2[0] = 0xf0;
memset(message_2 + 1, 0x7f,
(message_size - 2) * sizeof(jack_midi_data_t));
message_2[message_size - 1] = 0xf7;
}

/* install a signal handler to properly quits jack client */
#ifdef WIN32
signal(SIGINT, signal_handler);
signal(SIGABRT, signal_handler);
signal(SIGTERM, signal_handler);
#else
signal(SIGQUIT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);
#endif

client = jack_client_open(program_name, JackNullOption, NULL);
if (client == NULL) {
error_message = "failed to open JACK client";
error_source = "jack_client_open";
goto free_message_2;
}
remote_in_port = jack_port_by_name(client, argv[optind + 1]);
if (remote_in_port == NULL) {
error_message = "invalid port name";
error_source = argv[optind + 1];
goto close_client;
}
remote_out_port = jack_port_by_name(client, argv[optind]);
if (remote_out_port == NULL) {
error_message = "invalid port name";
error_source = argv[optind];
goto close_client;
}
in_port = jack_port_register(client, "in", JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput, 0);
if (in_port == NULL) {
error_message = "failed to register MIDI-in port";
error_source = "jack_port_register";
goto close_client;
}
out_port = jack_port_register(client, "out", JACK_DEFAULT_MIDI_TYPE,
JackPortIsOutput, 0);
if (out_port == NULL) {
error_message = "failed to register MIDI-out port";
error_source = "jack_port_register";
goto unregister_in_port;
}
if (jack_set_process_callback(client, handle_process, NULL)) {
error_message = "failed to set process callback";
error_source = "jack_set_process_callback";
goto unregister_out_port;
}
if (jack_set_xrun_callback(client, handle_xrun, NULL)) {
error_message = "failed to set xrun callback";
error_source = "jack_set_xrun_callback";
goto unregister_out_port;
}
jack_on_shutdown(client, handle_shutdown, NULL);
jack_set_info_function(handle_info);
process_state = 0;
init_semaphore = create_semaphore(0);
if (init_semaphore == NULL) {
error_message = get_semaphore_error();
error_source = "create_semaphore";
goto unregister_out_port;
}
process_semaphore = create_semaphore(1);
if (process_semaphore == NULL) {
error_message = get_semaphore_error();
error_source = "create_semaphore";
goto destroy_init_semaphore;
}
if (jack_activate(client)) {
error_message = "could not activate client";
error_source = "jack_activate";
goto destroy_process_semaphore;
}
if (jack_connect(client, jack_port_name(out_port),
jack_port_name(remote_out_port))) {
error_message = "could not connect MIDI out port";
error_source = "jack_connect";
goto deactivate_client;
}
if (jack_connect(client, jack_port_name(remote_in_port),
jack_port_name(in_port))) {
error_message = "could not connect MIDI in port";
error_source = "jack_connect";
goto deactivate_client;
}
if (! signal_semaphore(init_semaphore)) {
error_message = get_semaphore_error();
error_source = "post_semaphore";
goto deactivate_client;
}
if (wait_semaphore(process_semaphore, 1) == -1) {
error_message = get_semaphore_error();
error_source = "wait_semaphore";
goto deactivate_client;
}
if (process_state == 2) {
double average_latency = ((double) total_latency) / samples;
double average_latency_time = total_latency_time / samples;
size_t i;
double latency_plot_offset =
floor(((double) lowest_latency_time) / 100.0) / 10.0;
double sample_rate = (double) jack_get_sample_rate(client);
jack_nframes_t total_jitter = 0;
jack_time_t total_jitter_time = 0;
for (i = 0; i <= 100; i++) {
jitter_plot[i] = 0;
latency_plot[i] = 0;
}
for (i = 0; i < samples; i++) {
double latency_time_value = (double) latency_time_values[i];
double latency_plot_time =
(latency_time_value / 1000.0) - latency_plot_offset;
double jitter_time = ABS(average_latency_time -
latency_time_value);
if (latency_plot_time >= 10.0) {
(latency_plot[100])++;
} else {
(latency_plot[(int) (latency_plot_time * 10.0)])++;
}
if (jitter_time >= 10000.0) {
(jitter_plot[100])++;
} else {
(jitter_plot[(int) (jitter_time / 100.0)])++;
}
total_jitter += ABS(average_latency -
((double) latency_values[i]));
total_jitter_time += jitter_time;
}
printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n"
"Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n"
"Average latency: %.2f ms (%.2f frames)\n"
"Lowest latency: %.2f ms (%u frames)\n"
"Highest latency: %.2f ms (%u frames)\n"
"Peak MIDI jitter: %.2f ms (%u frames)\n"
"Average MIDI jitter: %.2f ms (%.2f frames)\n",
(out_latency_range.min / sample_rate) * 1000.0,
(out_latency_range.max / sample_rate) * 1000.0,
out_latency_range.min, out_latency_range.max,
(in_latency_range.min / sample_rate) * 1000.0,
(in_latency_range.max / sample_rate) * 1000.0,
in_latency_range.min, in_latency_range.max,
average_latency_time / 1000.0, average_latency,
lowest_latency_time / 1000.0, lowest_latency,
highest_latency_time / 1000.0, highest_latency,
(highest_latency_time - lowest_latency_time) / 1000.0,
highest_latency - lowest_latency,
(total_jitter_time / 1000.0) / samples,
((double) total_jitter) / samples);
printf("\nJitter Plot:\n");
for (i = 0; i < 100; i++) {
if (jitter_plot[i]) {
printf("%.1f - %.1f ms: %u\n", ((float) i) / 10.0,
((float) (i + 1)) / 10.0, jitter_plot[i]);
}
}
if (jitter_plot[100]) {
printf(" > 10 ms: %u\n", jitter_plot[100]);
}
printf("\nLatency Plot:\n");
for (i = 0; i < 100; i++) {
if (latency_plot[i]) {
printf("%.1f - %.1f ms: %u\n",
latency_plot_offset + (((float) i) / 10.0),
latency_plot_offset + (((float) (i + 1)) / 10.0),
latency_plot[i]);
}
}
if (latency_plot[100]) {
printf(" > %.1f ms: %u\n", latency_plot_offset + 10.0,
latency_plot[100]);
}
}
printf("\nMessages sent: %d\n"
"Messages received: %d\n",
messages_sent, messages_received);
if (unexpected_messages) {
printf("Unexpected messages received: %d\n", unexpected_messages);
}
if (xrun_count) {
printf("Xruns: %d (messages may have been lost)\n", xrun_count);
}
deactivate_client:
jack_deactivate(client);
destroy_process_semaphore:
destroy_semaphore(process_semaphore, 1);
destroy_init_semaphore:
destroy_semaphore(init_semaphore, 0);
unregister_out_port:
jack_port_unregister(client, out_port);
unregister_in_port:
jack_port_unregister(client, in_port);
close_client:
jack_client_close(client);
free_message_2:
free(message_2);
free_message_1:
free(message_1);
free_latency_time_values:
free(latency_time_values);
free_latency_values:
free(latency_values);
if (error_message != NULL) {
show_error:
output_error(error_source, error_message);
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}

+ 1
- 1
example-clients/showtime.c View File

@@ -35,7 +35,7 @@ showtime ()
transport_state = jack_transport_query (client, &current);
frame_time = jack_frame_time (client);
printf ("frame = %u frame_time = %u usecs = %lld \t", current.frame, frame_time, current.usecs);
printf ("frame = %u frame_time = %u usecs = %ld \t", current.frame, frame_time, current.usecs);

switch (transport_state) {
case JackTransportStopped:


+ 1
- 1
example-clients/thru_client.c View File

@@ -1,4 +1,4 @@
/** @file simple_client.c
/** @file thru_client.c
*
* @brief This simple through client demonstrates the basic features of JACK
* as they would be used by many applications.


+ 23
- 23
example-clients/tw.c View File

@@ -1,4 +1,4 @@
/** @file simple_client.c
/** @file tw.c
*
* @brief This simple client demonstrates the basic features of JACK
* as they would be used by many applications.
@@ -42,56 +42,56 @@ _process (jack_nframes_t nframes)
{
jack_default_audio_sample_t *in, *out;
jack_transport_state_t ts = jack_transport_query(client, NULL);
if (ts == JackTransportRolling) {
if (client_state == Init)
client_state = Run;
in = jack_port_get_buffer (input_port, nframes);
out = jack_port_get_buffer (output_port, nframes);
memcpy (out, in,
sizeof (jack_default_audio_sample_t) * nframes);
} else if (ts == JackTransportStopped) {
if (client_state == Run) {
client_state = Exit;
return -1; // to stop the thread
}
}

return 0;
return 0;
}

static void* jack_thread(void *arg)
static void* jack_thread(void *arg)
{
jack_client_t* client = (jack_client_t*) arg;
while (1) {
jack_nframes_t frames = jack_cycle_wait (client);
int status = _process(frames);
jack_cycle_signal (client, status);
/*
Possibly do something else after signaling next clients in the graph
*/
/* End condition */
if (status != 0)
return 0;
return 0;
}
/* not reached*/
return 0;
}

/*
static void* jack_thread(void *arg)
static void* jack_thread(void *arg)
{
jack_client_t* client = (jack_client_t*) arg;
while (1) {
jack_nframes_t frames;
int status;
@@ -103,7 +103,7 @@ static void* jack_thread(void *arg)
frames = jack_cycle_wait (client);
status = _process(frames);
jack_cycle_signal (client, status);
// cycle 3
// cycle 3
frames = jack_cycle_wait (client);
status = _process(frames);
jack_cycle_signal (client, status);
@@ -112,7 +112,7 @@ static void* jack_thread(void *arg)
status = _process(frames);
jack_cycle_signal (client, status);
}
return 0;
}
*/
@@ -172,8 +172,8 @@ main (int argc, char *argv[])

/* tell the JACK server to call `process()' whenever
there is work to be done.
*/
if (jack_set_process_thread(client, jack_thread, client) < 0)
*/
if (jack_set_process_thread(client, jack_thread, client) < 0)
exit(1);

/* tell the JACK server to call `jack_shutdown()' if
@@ -183,7 +183,7 @@ main (int argc, char *argv[])

jack_on_shutdown (client, jack_shutdown, 0);

/* display the current sample rate.
/* display the current sample rate.
*/

printf ("engine sample rate: %" PRIu32 "\n",
@@ -231,7 +231,7 @@ main (int argc, char *argv[])
}

free (ports);
ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical|JackPortIsInput);
if (ports == NULL) {
@@ -244,7 +244,7 @@ main (int argc, char *argv[])
}

free (ports);
/* install a signal handler to properly quits jack client */
signal(SIGQUIT, signal_handler);
signal(SIGTERM, signal_handler);


+ 1
- 1
example-clients/wait.c View File

@@ -40,7 +40,7 @@ main (int argc, char *argv[])
int wait_timeout = 0;
time_t start_timestamp;

struct option long_options[] = {
{ "server", 1, 0, 's' },
{ "wait", 0, 0, 'w' },


+ 1
- 0
example-clients/wscript View File

@@ -29,6 +29,7 @@ example_programs = {
'jack_net_master' : 'netmaster.c',
'jack_latent_client' : 'latent_client.c',
'jack_midi_dump' : 'midi_dump.c',
'jack_midi_latency_test' : 'midi_latency_test.c'
}

example_libs = {


+ 9
- 9
example-clients/zombie.c View File

@@ -1,6 +1,6 @@
/*
Copyright (C) 2002 Jeremy Hall
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -30,18 +30,18 @@ int count = 0;
jack_port_t* output_port;

static int
process(jack_nframes_t nframes, void* arg)
process(jack_nframes_t nframes, void* arg)
{
if (count++ == 1000) {
printf("process block\n");
//while (1) {}
sleep(1);
}
return 0;
}

static void
static void
shutdown (void *arg)
{
printf("shutdown \n");
@@ -57,7 +57,7 @@ main (int argc, char *argv[])
fprintf (stderr, "jack server not running?\n");
goto error;
}
jack_set_process_callback (client, process, NULL);
jack_on_shutdown(client, shutdown, NULL);
output_port = jack_port_register (client, "port1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
@@ -67,9 +67,9 @@ main (int argc, char *argv[])
fprintf (stderr, "cannot activate client");
goto error;
}
jack_connect(client, jack_port_name(output_port), "coreaudio:Built-in Audio:in2");
while (running) {
sleep(1);
printf ("run\n");
@@ -78,9 +78,9 @@ main (int argc, char *argv[])
jack_deactivate (client);
jack_client_close (client);
return 0;
error:
if (client)
if (client)
jack_client_close (client);
return 1;
}


+ 4
- 4
linux/alsa/JackAlsaAdapter.cpp View File

@@ -259,7 +259,7 @@ extern "C"
i++;
strcpy ( desc->params[i].name, "inchannels" );
desc->params[i].character = 'i';
desc->params[i].type = JackDriverParamUInt;
desc->params[i].type = JackDriverParamInt;
desc->params[i].value.i = 0;
strcpy ( desc->params[i].short_desc,
"Number of capture channels (defaults to hardware max)" );
@@ -268,7 +268,7 @@ extern "C"
i++;
strcpy ( desc->params[i].name, "outchannels" );
desc->params[i].character = 'o';
desc->params[i].type = JackDriverParamUInt;
desc->params[i].type = JackDriverParamInt;
desc->params[i].value.i = 0;
strcpy ( desc->params[i].short_desc,
"Number of playback channels (defaults to hardware max)" );
@@ -277,7 +277,7 @@ extern "C"
i++;
strcpy(desc->params[i].name, "quality");
desc->params[i].character = 'q';
desc->params[i].type = JackDriverParamInt;
desc->params[i].type = JackDriverParamUInt;
desc->params[i].value.ui = 0;
strcpy(desc->params[i].short_desc, "Resample algorithm quality (0 - 4)");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
@@ -285,7 +285,7 @@ extern "C"
i++;
strcpy(desc->params[i].name, "ring-buffer");
desc->params[i].character = 'g';
desc->params[i].type = JackDriverParamInt;
desc->params[i].type = JackDriverParamUInt;
desc->params[i].value.ui = 32768;
strcpy(desc->params[i].short_desc, "Fixed ringbuffer size");
strcpy(desc->params[i].long_desc, "Fixed ringbuffer size (if not set => automatic adaptative)");


+ 34
- 17
linux/alsa/JackAlsaDriver.cpp View File

@@ -55,8 +55,11 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size)
((alsa_driver_t *)fDriver)->frame_rate);

if (res == 0) { // update fEngineControl and fGraphManager
JackAudioDriver::SetBufferSize(buffer_size); // never fails
JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails
// ALSA specific
UpdateLatencies();
} else {
// Restore old values
alsa_driver_reset_parameters((alsa_driver_t *)fDriver, fEngineControl->fBufferSize,
((alsa_driver_t *)fDriver)->user_nperiods,
((alsa_driver_t *)fDriver)->frame_rate);
@@ -65,6 +68,29 @@ int JackAlsaDriver::SetBufferSize(jack_nframes_t buffer_size)
return res;
}

void JackAlsaDriver::UpdateLatencies()
{
jack_latency_range_t range;
alsa_driver_t* alsa_driver = (alsa_driver_t*)fDriver;

for (int i = 0; i < fCaptureChannels; i++) {
range.min = range.max = alsa_driver->frames_per_cycle + alsa_driver->capture_frame_latency;
fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range);
}

for (int i = 0; i < fPlaybackChannels; i++) {
// Add one buffer more latency if "async" mode is used...
range.min = range.max = (alsa_driver->frames_per_cycle * (alsa_driver->user_nperiods - 1)) +
((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + alsa_driver->playback_frame_latency;
fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range);
// Monitor port
if (fWithMonitorPorts) {
range.min = range.max = alsa_driver->frames_per_cycle;
fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &range);
}
}
}

int JackAlsaDriver::Attach()
{
JackPort* port;
@@ -72,7 +98,6 @@ int JackAlsaDriver::Attach()
unsigned long port_flags = (unsigned long)CaptureDriverFlags;
char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
jack_latency_range_t range;

assert(fCaptureChannels < DRIVER_PORT_NUM);
assert(fPlaybackChannels < DRIVER_PORT_NUM);
@@ -97,8 +122,6 @@ int JackAlsaDriver::Attach()
}
port = fGraphManager->GetPort(port_index);
port->SetAlias(alias);
range.min = range.max = alsa_driver->frames_per_cycle + alsa_driver->capture_frame_latency;
port->SetLatencyRange(JackCaptureLatency, &range);
fCapturePortList[i] = port_index;
jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_index);
}
@@ -114,11 +137,6 @@ int JackAlsaDriver::Attach()
}
port = fGraphManager->GetPort(port_index);
port->SetAlias(alias);
// Add one buffer more latency if "async" mode is used...
range.min = range.max = (alsa_driver->frames_per_cycle * (alsa_driver->user_nperiods - 1)) +
((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + alsa_driver->playback_frame_latency;

port->SetLatencyRange(JackPlaybackLatency, &range);
fPlaybackPortList[i] = port_index;
jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", port_index);

@@ -129,14 +147,13 @@ int JackAlsaDriver::Attach()
if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) {
jack_error ("ALSA: cannot register monitor port for %s", name);
} else {
port = fGraphManager->GetPort(port_index);
range.min = range.max = alsa_driver->frames_per_cycle;
port->SetLatencyRange(JackCaptureLatency, &range);
fMonitorPortList[i] = port_index;
}
}
}

UpdateLatencies();

if (alsa_driver->midi) {
int err = (alsa_driver->midi->attach)(alsa_driver->midi);
if (err)
@@ -275,7 +292,7 @@ int JackAlsaDriver::Open(jack_nframes_t nframes,
}
}

fDriver = alsa_driver_new ("alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name,
fDriver = alsa_driver_new ((char*)"alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name,
NULL,
nframes,
user_nperiods,
@@ -828,7 +845,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor ()
i++;
strcpy (params[i].name, "inchannels");
params[i].character = 'i';
params[i].type = JackDriverParamUInt;
params[i].type = JackDriverParamInt;
params[i].value.i = 0;
strcpy (params[i].short_desc,
"Number of capture channels (defaults to hardware max)");
@@ -837,7 +854,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor ()
i++;
strcpy (params[i].name, "outchannels");
params[i].character = 'o';
params[i].type = JackDriverParamUInt;
params[i].type = JackDriverParamInt;
params[i].value.i = 0;
strcpy (params[i].short_desc,
"Number of playback channels (defaults to hardware max)");
@@ -855,7 +872,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor ()
strcpy (params[i].name, "input-latency");
params[i].character = 'I';
params[i].type = JackDriverParamUInt;
params[i].value.i = 0;
params[i].value.ui = 0;
strcpy (params[i].short_desc, "Extra input latency (frames)");
strcpy (params[i].long_desc, params[i].short_desc);

@@ -863,7 +880,7 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor ()
strcpy (params[i].name, "output-latency");
params[i].character = 'O';
params[i].type = JackDriverParamUInt;
params[i].value.i = 0;
params[i].value.ui = 0;
strcpy (params[i].short_desc, "Extra output latency (frames)");
strcpy (params[i].long_desc, params[i].short_desc);



+ 2
- 0
linux/alsa/JackAlsaDriver.h View File

@@ -42,6 +42,8 @@ class JackAlsaDriver : public JackAudioDriver
int fReservedCaptureDevice;
int fReservedPlaybackDevice;

void UpdateLatencies();

public:

JackAlsaDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table)


+ 2
- 2
linux/alsa/alsa_driver.c View File

@@ -204,8 +204,8 @@ alsa_driver_ice1712_hardware (alsa_driver_t *driver)
static int
alsa_driver_usx2y_hardware (alsa_driver_t *driver)
{
driver->hw = jack_alsa_usx2y_hw_new (driver);
return 0;
driver->hw = jack_alsa_usx2y_hw_new (driver);
return 0;
}
*/



+ 614
- 0
linux/alsarawmidi/JackALSARawMidiDriver.cpp View File

@@ -0,0 +1,614 @@
/*
Copyright (C) 2011 Devin Anderson

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

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

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <memory>
#include <new>
#include <stdexcept>

#include <alsa/asoundlib.h>

#include "JackALSARawMidiDriver.h"
#include "JackEngineControl.h"
#include "JackError.h"
#include "JackMidiUtil.h"

using Jack::JackALSARawMidiDriver;

JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name,
const char *alias,
JackLockedEngine *engine,
JackSynchro *table):
JackMidiDriver(name, alias, engine, table)
{
thread = new JackThread(this);
fCaptureChannels = 0;
fds[0] = -1;
fds[1] = -1;
fPlaybackChannels = 0;
input_ports = 0;
output_ports = 0;
poll_fds = 0;
}

JackALSARawMidiDriver::~JackALSARawMidiDriver()
{
delete thread;
}

int
JackALSARawMidiDriver::Attach()
{
const char *alias;
jack_nframes_t buffer_size = fEngineControl->fBufferSize;
jack_port_id_t index;
jack_nframes_t latency = buffer_size;
jack_latency_range_t latency_range;
const char *name;
JackPort *port;
latency_range.max = latency;
latency_range.min = latency;
for (int i = 0; i < fCaptureChannels; i++) {
JackALSARawMidiInputPort *input_port = input_ports[i];
name = input_port->GetName();
index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
JACK_DEFAULT_MIDI_TYPE,
CaptureDriverFlags, buffer_size);
if (index == NO_PORT) {
jack_error("JackALSARawMidiDriver::Attach - cannot register input "
"port with name '%s'.", name);
// X: Do we need to deallocate ports?
return -1;
}
alias = input_port->GetAlias();
port = fGraphManager->GetPort(index);
port->SetAlias(alias);
port->SetLatencyRange(JackCaptureLatency, &latency_range);
fCapturePortList[i] = index;

jack_info("JackALSARawMidiDriver::Attach - input port registered "
"(name='%s', alias='%s').", name, alias);

}
if (! fEngineControl->fSyncMode) {
latency += buffer_size;
latency_range.max = latency;
latency_range.min = latency;
}
for (int i = 0; i < fPlaybackChannels; i++) {
JackALSARawMidiOutputPort *output_port = output_ports[i];
name = output_port->GetName();
index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
JACK_DEFAULT_MIDI_TYPE,
PlaybackDriverFlags, buffer_size);
if (index == NO_PORT) {
jack_error("JackALSARawMidiDriver::Attach - cannot register "
"output port with name '%s'.", name);
// X: Do we need to deallocate ports?
return -1;
}
alias = output_port->GetAlias();
port = fGraphManager->GetPort(index);
port->SetAlias(alias);
port->SetLatencyRange(JackPlaybackLatency, &latency_range);
fPlaybackPortList[i] = index;

jack_info("JackALSARawMidiDriver::Attach - output port registered "
"(name='%s', alias='%s').", name, alias);

}
return 0;
}

int
JackALSARawMidiDriver::Close()
{
// Generic MIDI driver close
int result = JackMidiDriver::Close();

if (input_ports) {
for (int i = 0; i < fCaptureChannels; i++) {
delete input_ports[i];
}
delete[] input_ports;
input_ports = 0;
}
if (output_ports) {
for (int i = 0; i < fPlaybackChannels; i++) {
delete output_ports[i];
}
delete[] output_ports;
output_ports = 0;
}
return result;
}

bool
JackALSARawMidiDriver::Execute()
{
jack_nframes_t timeout_frame = 0;
for (;;) {
jack_nframes_t process_frame;
unsigned short revents;
jack_nframes_t *timeout_frame_ptr;
if (! timeout_frame) {
timeout_frame_ptr = 0;
} else {
timeout_frame_ptr = &timeout_frame;
}
if (Poll(timeout_frame_ptr) == -1) {
if (errno == EINTR) {
continue;
}
jack_error("JackALSARawMidiDriver::Execute - poll error: %s",
strerror(errno));
break;
}
revents = poll_fds[0].revents;
if (revents & POLLHUP) {
// Driver is being stopped.
break;
}
if (revents & (~ POLLIN)) {
jack_error("JackALSARawMidiDriver::Execute - unexpected poll "
"event on pipe file descriptor.");
break;
}
timeout_frame = 0;
for (int i = 0; i < fCaptureChannels; i++) {
if (! input_ports[i]->ProcessALSA(&process_frame)) {
jack_error("JackALSARawMidiDriver::Execute - a fatal error "
"occurred while processing ALSA input events.");
goto cleanup;
}
if (process_frame && ((! timeout_frame) ||
(process_frame < timeout_frame))) {
timeout_frame = process_frame;
}
}
for (int i = 0; i < fPlaybackChannels; i++) {
if (! output_ports[i]->ProcessALSA(fds[0], &process_frame)) {
jack_error("JackALSARawMidiDriver::Execute - a fatal error "
"occurred while processing ALSA output events.");
goto cleanup;
}
if (process_frame && ((! timeout_frame) ||
(process_frame < timeout_frame))) {
timeout_frame = process_frame;
}
}
}
cleanup:
close(fds[0]);
fds[0] = -1;

jack_info("JackALSARawMidiDriver::Execute - ALSA thread exiting.");

return false;
}

void
JackALSARawMidiDriver::
GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info,
std::vector<snd_rawmidi_info_t *> *info_list)
{
snd_rawmidi_info_set_subdevice(info, 0);
int code = snd_ctl_rawmidi_info(control, info);
if (code) {
if (code != -ENOENT) {
HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
}
return;
}
unsigned int count = snd_rawmidi_info_get_subdevices_count(info);
for (unsigned int i = 0; i < count; i++) {
snd_rawmidi_info_set_subdevice(info, i);
int code = snd_ctl_rawmidi_info(control, info);
if (code) {
HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
continue;
}
snd_rawmidi_info_t *info_copy;
code = snd_rawmidi_info_malloc(&info_copy);
if (code) {
HandleALSAError("GetDeviceInfo", "snd_rawmidi_info_malloc", code);
continue;
}
snd_rawmidi_info_copy(info_copy, info);
try {
info_list->push_back(info_copy);
} catch (std::bad_alloc &e) {
snd_rawmidi_info_free(info_copy);
jack_error("JackALSARawMidiDriver::GetDeviceInfo - "
"std::vector::push_back: %s", e.what());
}
}
}

void
JackALSARawMidiDriver::HandleALSAError(const char *driver_func,
const char *alsa_func, int code)
{
jack_error("JackALSARawMidiDriver::%s - %s: %s", driver_func, alsa_func,
snd_strerror(code));
}

bool
JackALSARawMidiDriver::Init()
{
set_threaded_log_function();
if (thread->AcquireSelfRealTime(fEngineControl->fServerPriority + 1)) {
jack_error("JackALSARawMidiDriver::Init - could not acquire realtime "
"scheduling. Continuing anyway.");
}
return true;
}

int
JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels,
int out_channels, bool monitor,
const char *capture_driver_name,
const char *playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency)
{
snd_rawmidi_info_t *info;
int code = snd_rawmidi_info_malloc(&info);
if (code) {
HandleALSAError("Open", "snd_rawmidi_info_malloc", code);
return -1;
}
std::vector<snd_rawmidi_info_t *> in_info_list;
std::vector<snd_rawmidi_info_t *> out_info_list;
for (int card = -1;;) {
int code = snd_card_next(&card);
if (code) {
HandleALSAError("Open", "snd_card_next", code);
continue;
}
if (card == -1) {
break;
}
char name[32];
snprintf(name, sizeof(name), "hw:%d", card);
snd_ctl_t *control;
code = snd_ctl_open(&control, name, SND_CTL_NONBLOCK);
if (code) {
HandleALSAError("Open", "snd_ctl_open", code);
continue;
}
for (int device = -1;;) {
code = snd_ctl_rawmidi_next_device(control, &device);
if (code) {
HandleALSAError("Open", "snd_ctl_rawmidi_next_device", code);
continue;
}
if (device == -1) {
break;
}
snd_rawmidi_info_set_device(info, device);
snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
GetDeviceInfo(control, info, &in_info_list);
snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT);
GetDeviceInfo(control, info, &out_info_list);
}
snd_ctl_close(control);
}
snd_rawmidi_info_free(info);
size_t potential_inputs = in_info_list.size();
size_t potential_outputs = out_info_list.size();
if (! (potential_inputs || potential_outputs)) {
jack_error("JackALSARawMidiDriver::Open - no ALSA raw MIDI input or "
"output ports found.");
return -1;
}

// XXX: Can't use auto_ptr here. These are arrays, and require the
// delete[] operator.
std::auto_ptr<JackALSARawMidiInputPort *> input_ptr;
if (potential_inputs) {
input_ports = new JackALSARawMidiInputPort *[potential_inputs];
input_ptr.reset(input_ports);
}
std::auto_ptr<JackALSARawMidiOutputPort *> output_ptr;
if (potential_outputs) {
output_ports = new JackALSARawMidiOutputPort *[potential_outputs];
output_ptr.reset(output_ports);
}

size_t num_inputs = 0;
size_t num_outputs = 0;
for (size_t i = 0; i < potential_inputs; i++) {
snd_rawmidi_info_t *info = in_info_list.at(i);
try {
input_ports[num_inputs] = new JackALSARawMidiInputPort(info, i);

jack_info("JackALSARawMidiDriver::Open - Input port: card=%d, "
"device=%d, subdevice=%d, id=%s, name=%s, subdevice "
"name=%s",
snd_rawmidi_info_get_card(info),
snd_rawmidi_info_get_device(info),
snd_rawmidi_info_get_subdevice(info),
snd_rawmidi_info_get_id(info),
snd_rawmidi_info_get_name(info),
snd_rawmidi_info_get_subdevice_name(info));

num_inputs++;
} catch (std::exception e) {
jack_error("JackALSARawMidiDriver::Open - while creating new "
"JackALSARawMidiInputPort: %s", e.what());
}
snd_rawmidi_info_free(info);
}
for (size_t i = 0; i < potential_outputs; i++) {
snd_rawmidi_info_t *info = out_info_list.at(i);
try {
output_ports[num_outputs] = new JackALSARawMidiOutputPort(info, i);

jack_info("JackALSARawMidiDriver::Open - Output port: card=%d, "
"device=%d, subdevice=%d, id=%s, name=%s, subdevice "
"name=%s",
snd_rawmidi_info_get_card(info),
snd_rawmidi_info_get_device(info),
snd_rawmidi_info_get_subdevice(info),
snd_rawmidi_info_get_id(info),
snd_rawmidi_info_get_name(info),
snd_rawmidi_info_get_subdevice_name(info));

num_outputs++;
} catch (std::exception e) {
jack_error("JackALSARawMidiDriver::Open - while creating new "
"JackALSARawMidiOutputPort: %s", e.what());
}
snd_rawmidi_info_free(info);
}
if (num_inputs || num_outputs) {
if (! JackMidiDriver::Open(capturing, playing, num_inputs, num_outputs,
monitor, capture_driver_name,
playback_driver_name, capture_latency,
playback_latency)) {
if (potential_inputs) {
input_ptr.release();
}
if (potential_outputs) {
output_ptr.release();
}
return 0;
}
jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error");
} else {
jack_error("JackALSARawMidiDriver::Open - none of the potential "
"inputs or outputs were successfully opened.");
}
Close();
return -1;
}

int
JackALSARawMidiDriver::Poll(const jack_nframes_t *wakeup_frame)
{
struct timespec timeout;
struct timespec *timeout_ptr;
if (! wakeup_frame) {
timeout_ptr = 0;
} else {
timeout_ptr = &timeout;
jack_time_t next_time = GetTimeFromFrames(*wakeup_frame);
jack_time_t now = GetMicroSeconds();
if (next_time <= now) {
timeout.tv_sec = 0;
timeout.tv_nsec = 0;
} else {
jack_time_t wait_time = next_time - now;
timeout.tv_sec = wait_time / 1000000;
timeout.tv_nsec = (wait_time % 1000000) * 1000;
}
}
return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0);
}

int
JackALSARawMidiDriver::Read()
{
jack_nframes_t buffer_size = fEngineControl->fBufferSize;
for (int i = 0; i < fCaptureChannels; i++) {
if (! input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size)) {
return -1;
}
}
return 0;
}

int
JackALSARawMidiDriver::Start()
{

jack_info("JackALSARawMidiDriver::Start - Starting 'alsarawmidi' driver.");

JackMidiDriver::Start();
poll_fd_count = 1;
for (int i = 0; i < fCaptureChannels; i++) {
poll_fd_count += input_ports[i]->GetPollDescriptorCount();
}
for (int i = 0; i < fPlaybackChannels; i++) {
poll_fd_count += output_ports[i]->GetPollDescriptorCount();
}
try {
poll_fds = new pollfd[poll_fd_count];
} catch (std::bad_alloc e) {
jack_error("JackALSARawMidiDriver::Start - creating poll descriptor "
"structures failed: %s", e.what());
return -1;
}
int flags;
struct pollfd *poll_fd_iter;
if (pipe(fds) == -1) {
jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: "
"%s", strerror(errno));
goto free_poll_descriptors;
}
flags = fcntl(fds[0], F_GETFL);
if (flags == -1) {
jack_error("JackALSARawMidiDriver::Start = while getting flags for "
"read file descriptor: %s", strerror(errno));
goto close_fds;
}
if (fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) == -1) {
jack_error("JackALSARawMidiDriver::Start - while setting non-blocking "
"mode for read file descriptor: %s", strerror(errno));
goto close_fds;
}
flags = fcntl(fds[1], F_GETFL);
if (flags == -1) {
jack_error("JackALSARawMidiDriver::Start = while getting flags for "
"write file descriptor: %s", strerror(errno));
goto close_fds;
}
if (fcntl(fds[1], F_SETFL, flags | O_NONBLOCK) == -1) {
jack_error("JackALSARawMidiDriver::Start - while setting non-blocking "
"mode for write file descriptor: %s", strerror(errno));
goto close_fds;
}
poll_fds[0].events = POLLERR | POLLIN | POLLNVAL;
poll_fds[0].fd = fds[0];
poll_fd_iter = poll_fds + 1;
for (int i = 0; i < fCaptureChannels; i++) {
JackALSARawMidiInputPort *input_port = input_ports[i];
input_port->PopulatePollDescriptors(poll_fd_iter);
poll_fd_iter += input_port->GetPollDescriptorCount();
}
for (int i = 0; i < fPlaybackChannels; i++) {
JackALSARawMidiOutputPort *output_port = output_ports[i];
output_port->PopulatePollDescriptors(poll_fd_iter);
poll_fd_iter += output_port->GetPollDescriptorCount();
}

jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ...");

if (! thread->StartSync()) {

jack_info("JackALSARawMidiDriver::Start - started ALSA thread.");

return 0;
}
jack_error("JackALSARawMidiDriver::Start - failed to start MIDI "
"processing thread.");
close_fds:
close(fds[1]);
fds[1] = -1;
close(fds[0]);
fds[0] = -1;
free_poll_descriptors:
delete[] poll_fds;
poll_fds = 0;
return -1;
}

int
JackALSARawMidiDriver::Stop()
{
jack_info("JackALSARawMidiDriver::Stop - stopping 'alsarawmidi' driver.");

if (fds[1] != -1) {
close(fds[1]);
fds[1] = -1;
}
int result;
const char *verb;
switch (thread->GetStatus()) {
case JackThread::kIniting:
case JackThread::kStarting:
result = thread->Kill();
verb = "kill";
break;
case JackThread::kRunning:
result = thread->Stop();
verb = "stop";
break;
default:
result = 0;
verb = 0;
}
if (fds[0] != -1) {
close(fds[0]);
fds[0] = -1;
}
if (poll_fds) {
delete[] poll_fds;
poll_fds = 0;
}
if (result) {
jack_error("JackALSARawMidiDriver::Stop - could not %s MIDI "
"processing thread.", verb);
}
return result;
}

int
JackALSARawMidiDriver::Write()
{
jack_nframes_t buffer_size = fEngineControl->fBufferSize;
int write_fd = fds[1];
for (int i = 0; i < fPlaybackChannels; i++) {
if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size,
write_fd)) {
return -1;
}
}
return 0;
}

#ifdef __cplusplus
extern "C" {
#endif

SERVER_EXPORT jack_driver_desc_t *
driver_get_descriptor()
{
jack_driver_desc_t *desc =
(jack_driver_desc_t *) malloc(sizeof(jack_driver_desc_t));
if (desc) {
strcpy(desc->desc, "Alternative ALSA raw MIDI backend.");
strcpy(desc->name, "alsarawmidi");

// X: There could be parameters here regarding setting I/O buffer
// sizes. I don't think MIDI drivers can accept parameters right
// now without being set as the main driver.
desc->nparams = 0;
desc->params = 0;
}
return desc;
}

SERVER_EXPORT Jack::JackDriverClientInterface *
driver_initialize(Jack::JackLockedEngine *engine, Jack::JackSynchro *table,
const JSList *params)
{
Jack::JackDriverClientInterface *driver =
new Jack::JackALSARawMidiDriver("system_midi", "alsarawmidi",
engine, table);
if (driver->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0)) {
delete driver;
driver = 0;
}
return driver;
}

#ifdef __cplusplus
}
#endif

+ 98
- 0
linux/alsarawmidi/JackALSARawMidiDriver.h View File

@@ -0,0 +1,98 @@
/*
Copyright (C) 2011 Devin Anderson

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

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

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackALSARawMidiDriver__
#define __JackALSARawMidiDriver__

#include <vector>

#include <alsa/asoundlib.h>
#include <poll.h>

#include "JackALSARawMidiInputPort.h"
#include "JackALSARawMidiOutputPort.h"
#include "JackMidiDriver.h"
#include "JackThread.h"

namespace Jack {

class JackALSARawMidiDriver:
public JackMidiDriver, public JackRunnableInterface {

private:

int fds[2];
JackALSARawMidiInputPort **input_ports;
JackALSARawMidiOutputPort **output_ports;
nfds_t poll_fd_count;
struct pollfd *poll_fds;
JackThread *thread;

void
GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info,
std::vector<snd_rawmidi_info_t *> *info_list);

void
HandleALSAError(const char *driver_func, const char *alsa_func,
int code);

int
Poll(const jack_nframes_t *wakeup_frame);

public:

JackALSARawMidiDriver(const char *name, const char *alias,
JackLockedEngine *engine, JackSynchro *table);
~JackALSARawMidiDriver();

int
Attach();

int
Close();

bool
Execute();

bool
Init();

int
Open(bool capturing, bool playing, int in_channels, int out_channels,
bool monitoring, const char *capture_driver_name,
const char *playback_driver_name, jack_nframes_t capture_latency,
jack_nframes_t playback_latency);

int
Read();

int
Start();

int
Stop();

int
Write();

};

}

#endif

+ 143
- 0
linux/alsarawmidi/JackALSARawMidiInputPort.cpp View File

@@ -0,0 +1,143 @@
/*
Copyright (C) 2011 Devin Anderson

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

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

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <memory>

#include "JackALSARawMidiInputPort.h"
#include "JackMidiUtil.h"

using Jack::JackALSARawMidiInputPort;

JackALSARawMidiInputPort::JackALSARawMidiInputPort(snd_rawmidi_info_t *info,
size_t index,
size_t max_bytes,
size_t max_messages):
JackALSARawMidiPort(info, index)
{
alsa_event = 0;
jack_event = 0;
receive_queue = new JackALSARawMidiReceiveQueue(rawmidi, max_bytes);
std::auto_ptr<JackALSARawMidiReceiveQueue> receive_ptr(receive_queue);
thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue);
write_queue = new JackMidiBufferWriteQueue();
std::auto_ptr<JackMidiBufferWriteQueue> write_ptr(write_queue);
raw_queue = new JackMidiRawInputWriteQueue(thread_queue, max_bytes,
max_messages);
write_ptr.release();
thread_ptr.release();
receive_ptr.release();
}

JackALSARawMidiInputPort::~JackALSARawMidiInputPort()
{
delete raw_queue;
delete receive_queue;
delete thread_queue;
delete write_queue;
}

jack_nframes_t
JackALSARawMidiInputPort::EnqueueALSAEvent()
{
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_FULL:
// Processing events early might free up some space in the raw queue.
raw_queue->Process();
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiInputPort::Process - **BUG** "
"JackMidiRawInputWriteQueue::EnqueueEvent returned "
"`BUFFER_FULL` and then returned `BUFFER_TOO_SMALL` "
"after a `Process()` call.");
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
return 0;
default:
;
}
break;
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiInputPort::Execute - The thread queue "
"couldn't enqueue a %d-byte packet. Dropping event.",
alsa_event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
return 0;
default:
;
}
jack_nframes_t alsa_time = alsa_event->time;
jack_nframes_t next_time = raw_queue->Process();
return (next_time < alsa_time) ? next_time : alsa_time;
}

bool
JackALSARawMidiInputPort::ProcessALSA(jack_nframes_t *frame)
{
unsigned short revents;
if (! ProcessPollEvents(&revents)) {
return false;
}
if (alsa_event) {
*frame = EnqueueALSAEvent();
if (*frame) {
return true;
}
}
if (revents & POLLIN) {
for (alsa_event = receive_queue->DequeueEvent(); alsa_event;
alsa_event = receive_queue->DequeueEvent()) {
*frame = EnqueueALSAEvent();
if (*frame) {
return true;
}
}
}
*frame = raw_queue->Process();
return true;
}

bool
JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer,
jack_nframes_t frames)
{
write_queue->ResetMidiBuffer(port_buffer, frames);
if (! jack_event) {
jack_event = thread_queue->DequeueEvent();
}
for (; jack_event; jack_event = thread_queue->DequeueEvent()) {

// We add `frames` so that MIDI events align with audio as closely as
// possible.
switch (write_queue->EnqueueEvent(jack_event, frames)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiInputPort::ProcessJack - The write "
"queue couldn't enqueue a %d-byte event. Dropping "
"event.", jack_event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
;
}
break;
}
return true;
}

+ 62
- 0
linux/alsarawmidi/JackALSARawMidiInputPort.h View File

@@ -0,0 +1,62 @@
/*
Copyright (C) 2011 Devin Anderson

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

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

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackALSARawMidiInputPort__
#define __JackALSARawMidiInputPort__

#include "JackALSARawMidiPort.h"
#include "JackALSARawMidiReceiveQueue.h"
#include "JackMidiAsyncQueue.h"
#include "JackMidiBufferWriteQueue.h"
#include "JackMidiRawInputWriteQueue.h"

namespace Jack {

class JackALSARawMidiInputPort: public JackALSARawMidiPort {

private:

jack_midi_event_t *alsa_event;
jack_midi_event_t *jack_event;
JackMidiRawInputWriteQueue *raw_queue;
JackALSARawMidiReceiveQueue *receive_queue;
JackMidiAsyncQueue *thread_queue;
JackMidiBufferWriteQueue *write_queue;

jack_nframes_t
EnqueueALSAEvent();

public:

JackALSARawMidiInputPort(snd_rawmidi_info_t *info, size_t index,
size_t max_bytes=4096,
size_t max_messages=1024);
~JackALSARawMidiInputPort();

bool
ProcessALSA(jack_nframes_t *frame);

bool
ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames);

};

}

#endif

+ 172
- 0
linux/alsarawmidi/JackALSARawMidiOutputPort.cpp View File

@@ -0,0 +1,172 @@
/*
Copyright (C) 2011 Devin Anderson

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

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

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <memory>

#include "JackALSARawMidiOutputPort.h"

using Jack::JackALSARawMidiOutputPort;

JackALSARawMidiOutputPort::JackALSARawMidiOutputPort(snd_rawmidi_info_t *info,
size_t index,
size_t max_bytes,
size_t max_messages):
JackALSARawMidiPort(info, index)
{
alsa_event = 0;
blocked = false;
read_queue = new JackMidiBufferReadQueue();
std::auto_ptr<JackMidiBufferReadQueue> read_ptr(read_queue);
send_queue = new JackALSARawMidiSendQueue(rawmidi);
std::auto_ptr<JackALSARawMidiSendQueue> send_ptr(send_queue);
thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue);
raw_queue = new JackMidiRawOutputWriteQueue(send_queue, max_bytes,
max_messages, max_messages);
thread_ptr.release();
send_ptr.release();
read_ptr.release();
}

JackALSARawMidiOutputPort::~JackALSARawMidiOutputPort()
{
delete raw_queue;
delete read_queue;
delete send_queue;
delete thread_queue;
}

jack_midi_event_t *
JackALSARawMidiOutputPort::DequeueALSAEvent(int read_fd)
{
jack_midi_event_t *event = thread_queue->DequeueEvent();
if (event) {
char c;
ssize_t result = read(read_fd, &c, 1);
if (! result) {
jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - **BUG** "
"An event was dequeued from the thread queue, but no "
"byte was available for reading from the pipe file "
"descriptor.");
} else if (result < 0) {
jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - error "
"reading a byte from the pipe file descriptor: %s",
strerror(errno));
}
}
return event;
}

bool
JackALSARawMidiOutputPort::ProcessALSA(int read_fd, jack_nframes_t *frame)
{
unsigned short revents;
if (! ProcessPollEvents(&revents)) {
return false;
}
if (blocked) {
if (! (revents & POLLOUT)) {
*frame = 0;
return true;
}
blocked = false;
}
if (! alsa_event) {
alsa_event = DequeueALSAEvent(read_fd);
}
for (; alsa_event; alsa_event = DequeueALSAEvent(read_fd)) {
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_FULL:
// Try to free up some space by processing events early.
raw_queue->Process();
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiOutputPort::ProcessALSA - **BUG** "
"JackMidiRawOutputWriteQueue::EnqueueEvent "
"returned `BUFFER_FULL`, and then returned "
"`BUFFER_TOO_SMALL` after a Process() call.");
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
;
}
goto process_events;
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiOutputPort::ProcessALSA - The raw "
"output queue couldn't enqueue a %d-byte event. "
"Dropping event.", alsa_event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
;
}
break;
}
process_events:
*frame = raw_queue->Process();
blocked = send_queue->IsBlocked();
if (blocked) {

jack_info("JackALSARawMidiOutputPort::ProcessALSA - MIDI port is "
"blocked");

SetPollEventMask(POLLERR | POLLNVAL | POLLOUT);
*frame = 0;
} else {
SetPollEventMask(POLLERR | POLLNVAL);
}
return true;
}

bool
JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
jack_nframes_t frames, int write_fd)
{
read_queue->ResetMidiBuffer(port_buffer);
for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
event = read_queue->DequeueEvent()) {
if (event->size > thread_queue->GetAvailableSpace()) {
jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread "
"queue doesn't have enough room to enqueue a %d-byte "
"event. Dropping event.", event->size);
continue;
}
char c = 1;
ssize_t result = write(write_fd, &c, 1);
assert(result <= 1);
if (result < 0) {
jack_error("JackALSARawMidiOutputPort::ProcessJack - error "
"writing a byte to the pipe file descriptor: %s",
strerror(errno));
return false;
}
if (! result) {
// Recoverable.
jack_error("JackALSARawMidiOutputPort::ProcessJack - Couldn't "
"write a byte to the pipe file descriptor. Dropping "
"event.");
} else {
assert(thread_queue->EnqueueEvent(event, frames) ==
JackMidiWriteQueue::OK);
}
}
return true;
}

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

Loading…
Cancel
Save