Description: added JACK support for examples
 Add LINK_BUILD_JACK option to build examples applications
 with JACK support on Linux.
 This patch also includes the follow-up commits:
 - ed850404fb321df5c2f4ecc2231e83897560b79c (fix redundant samplerate setting)
 - 808e3a79b42227ec2fc7308571b39eec4146d561 (fix crash with blocksize!=512)
Author: rncbc <rncbc@gamma.rncbc.lan>
Origin: upstream
Applied-Upstream: https://github.com/Ableton/link/commit/d4fb22bc80f19f1b022da55a05c6f8df1e35cf5a
Last-Update: 2016-10-26
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- ableton-link.orig/CMakeLists.txt
+++ ableton-link/CMakeLists.txt
@@ -16,6 +16,7 @@
 if(UNIX)
   set(LINK_WORD_SIZE "native" CACHE STRING "Set the word size (must be either 32 or 64 (or 'native')")
   option(LINK_ENABLE_ASAN "Build with Address Sanitizier (ASan)" OFF)
+  option(LINK_BUILD_JACK "Build example applications with JACK support" ON)
 endif()
 
 if(WIN32)
--- ableton-link.orig/examples/CMakeLists.txt
+++ ableton-link/examples/CMakeLists.txt
@@ -72,10 +72,17 @@
     )
   endif()
 elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-  set(linkhut_audio_SOURCES
-    linkaudio/AudioPlatform_Portaudio.hpp
-    linkaudio/AudioPlatform_Portaudio.cpp
-  )
+  if(LINK_BUILD_JACK)
+    set(linkhut_audio_SOURCES
+      linkaudio/AudioPlatform_Jack.hpp
+      linkaudio/AudioPlatform_Jack.cpp
+    )
+  else()
+    set(linkhut_audio_SOURCES
+      linkaudio/AudioPlatform_Portaudio.hpp
+      linkaudio/AudioPlatform_Portaudio.cpp
+    )
+  endif()
 endif()
 
 include_directories(linkaudio)
@@ -105,10 +112,17 @@
       -DLINKHUT_AUDIO_PLATFORM_COREAUDIO=1
     )
   elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-    target_link_libraries(${target} asound portaudio)
-    target_compile_definitions(${target} PRIVATE
-      -DLINKHUT_AUDIO_PLATFORM_PORTAUDIO=1
-    )
+    if(LINK_BUILD_JACK)
+      target_link_libraries(${target} jack)
+      target_compile_definitions(${target} PRIVATE
+        -DLINKHUT_AUDIO_PLATFORM_JACK=1
+      )
+    else()
+      target_link_libraries(${target} asound portaudio)
+      target_compile_definitions(${target} PRIVATE
+        -DLINKHUT_AUDIO_PLATFORM_PORTAUDIO=1
+      )
+    endif()
   elseif(WIN32)
     if(LINK_BUILD_ASIO)
       # ASIO uses lots of old-school string APIs from the C stdlib
--- ableton-link.orig/examples/linkaudio/AudioPlatform.hpp
+++ ableton-link/examples/linkaudio/AudioPlatform.hpp
@@ -31,6 +31,10 @@
 #include "AudioPlatform_Dummy.hpp"
 #endif
 
+#if LINKHUT_AUDIO_PLATFORM_JACK
+#include "AudioPlatform_Jack.hpp"
+#endif
+
 #if LINKHUT_AUDIO_PLATFORM_PORTAUDIO
 #include "AudioPlatform_Portaudio.hpp"
 #endif
@@ -38,3 +42,7 @@
 #if LINKHUT_AUDIO_PLATFORM_WASAPI
 #include "AudioPlatform_Wasapi.hpp"
 #endif
+
+#if LINKHUT_AUDIO_PLATFORM_JACK
+#include "AudioPlatform_Jack.hpp"
+#endif
--- /dev/null
+++ ableton-link/examples/linkaudio/AudioPlatform_Jack.cpp
@@ -0,0 +1,175 @@
+/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ *
+ *  If you would like to incorporate Link into a proprietary software application,
+ *  please contact <link-devs@ableton.com>.
+ */
+
+#include "AudioPlatform_Jack.hpp"
+#include <chrono>
+#include <iostream>
+#include <string>
+
+namespace ableton
+{
+namespace linkaudio
+{
+
+AudioPlatform::AudioPlatform(Link& link)
+  : mEngine(link)
+  , mSampleTime(0.)
+  , mpJackClient(NULL)
+  , mpJackPorts(NULL)
+{
+  initialize();
+  start();
+}
+
+AudioPlatform::~AudioPlatform()
+{
+  stop();
+  uninitialize();
+}
+
+int AudioPlatform::audioCallback(jack_nframes_t nframes, void* pvUserData)
+{
+  AudioPlatform* pAudioPlatform = static_cast<AudioPlatform*>(pvUserData);
+  return pAudioPlatform->audioCallback(nframes);
+}
+
+int AudioPlatform::audioCallback(jack_nframes_t nframes)
+{
+  using namespace std::chrono;
+  AudioEngine& engine = mEngine;
+
+  const auto hostTime = mHostTimeFilter.sampleTimeToHostTime(mSampleTime);
+
+  mSampleTime += nframes;
+
+  const auto bufferBeginAtOutput = hostTime + engine.mOutputLatency;
+
+  engine.audioCallback(bufferBeginAtOutput, nframes);
+
+  for (int k = 0; k < 2; ++k)
+  {
+    float* buffer = static_cast<float*>(jack_port_get_buffer(mpJackPorts[k], nframes));
+    for (unsigned long i = 0; i < nframes; ++i)
+      buffer[i] = engine.mBuffer[i];
+  }
+
+  return 0;
+}
+
+void AudioPlatform::initialize()
+{
+  jack_status_t status = JackFailure;
+  mpJackClient = jack_client_open("LinkHut", JackNullOption, &status);
+  if (mpJackClient == NULL)
+  {
+    std::cerr << "Could not initialize Audio Engine. ";
+    std::cerr << "JACK: " << std::endl;
+    if (status & JackFailure)
+      std::cerr << "Overall operation failed." << std::endl;
+    if (status & JackInvalidOption)
+      std::cerr << "Invalid or unsupported option." << std::endl;
+    if (status & JackNameNotUnique)
+      std::cerr << "Client name not unique." << std::endl;
+    if (status & JackServerStarted)
+      std::cerr << "Server is started." << std::endl;
+    if (status & JackServerFailed)
+      std::cerr << "Unable to connect to server." << std::endl;
+    if (status & JackServerError)
+      std::cerr << "Server communication error." << std::endl;
+    if (status & JackNoSuchClient)
+      std::cerr << "Client does not exist." << std::endl;
+    if (status & JackLoadFailure)
+      std::cerr << "Unable to load internal client." << std::endl;
+    if (status & JackInitFailure)
+      std::cerr << "Unable to initialize client." << std::endl;
+    if (status & JackShmFailure)
+      std::cerr << "Unable to access shared memory." << std::endl;
+    if (status & JackVersionError)
+      std::cerr << "Client protocol version mismatch." << std::endl;
+    std::cerr << std::endl;
+    std::terminate();
+  };
+
+  mpJackPorts = new jack_port_t*[2];
+  for (int k = 0; k < 2; ++k)
+  {
+    const std::string port_name = "out_" + std::to_string(k + 1);
+    mpJackPorts[k] = jack_port_register(
+      mpJackClient, port_name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+    if (mpJackPorts[k] == NULL)
+    {
+      std::cerr << "Could not get Audio Device. " << std::endl;
+      jack_client_close(mpJackClient);
+      std::terminate();
+    }
+  }
+
+  jack_set_process_callback(mpJackClient, AudioPlatform::audioCallback, this);
+
+  const double bufferSize = jack_get_buffer_size(mpJackClient);
+  const double sampleRate = jack_get_sample_rate(mpJackClient);
+
+  mEngine.setBufferSize(static_cast<std::size_t>(bufferSize));
+  mEngine.setSampleRate(sampleRate);
+
+  mEngine.mOutputLatency =
+    std::chrono::microseconds(llround(1.0e6 * bufferSize / sampleRate));
+}
+
+void AudioPlatform::uninitialize()
+{
+  for (int k = 0; k < 2; ++k)
+  {
+    jack_port_unregister(mpJackClient, mpJackPorts[k]);
+    mpJackPorts[k] = NULL;
+  }
+  delete[] mpJackPorts;
+  mpJackPorts = NULL;
+
+  jack_client_close(mpJackClient);
+  mpJackClient = NULL;
+}
+
+void AudioPlatform::start()
+{
+  jack_activate(mpJackClient);
+
+  const char** playback_ports = jack_get_ports(
+    mpJackClient, 0, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsPhysical);
+
+  if (playback_ports)
+  {
+    const std::string client_name = jack_get_client_name(mpJackClient);
+    for (int k = 0; k < 2; ++k)
+    {
+      const std::string port_name = "out_" + std::to_string(k + 1);
+      const std::string client_port = client_name + ':' + port_name;
+      jack_connect(mpJackClient, client_port.c_str(), playback_ports[k]);
+    }
+    jack_free(playback_ports);
+  }
+}
+
+void AudioPlatform::stop()
+{
+  jack_deactivate(mpJackClient);
+}
+
+} // namespace linkaudio
+} // namespace ableton
--- /dev/null
+++ ableton-link/examples/linkaudio/AudioPlatform_Jack.hpp
@@ -0,0 +1,55 @@
+/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ *
+ *  If you would like to incorporate Link into a proprietary software application,
+ *  please contact <link-devs@ableton.com>.
+ */
+
+#pragma once
+
+#include "AudioEngine.hpp"
+#include <ableton/link/HostTimeFilter.hpp>
+#include <jack/jack.h>
+
+namespace ableton
+{
+namespace linkaudio
+{
+
+class AudioPlatform
+{
+public:
+  AudioPlatform(Link& link);
+  ~AudioPlatform();
+
+  AudioEngine mEngine;
+
+private:
+  static int audioCallback(jack_nframes_t nframes, void* pvUserData);
+  int audioCallback(jack_nframes_t nframes);
+
+  void initialize();
+  void uninitialize();
+  void start();
+  void stop();
+
+  link::HostTimeFilter<platforms::stl::Clock> mHostTimeFilter;
+  double mSampleTime;
+  jack_client_t* mpJackClient;
+  jack_port_t** mpJackPorts;
+};
+
+} // namespace linkaudio
+} // namespace ableton
