/* 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 . * * If you would like to incorporate Link into a proprietary software application, * please contact . */ #include "AudioPlatform_Jack.hpp" #include #include #include namespace ableton { namespace linkaudio { AudioPlatform::AudioPlatform(Link& link) : mEngine(link) , mSampleTime(0.) , mpJackClient(nullptr) , mpJackPorts(nullptr) { initialize(); start(); } AudioPlatform::~AudioPlatform() { stop(); uninitialize(); } int AudioPlatform::audioCallback(jack_nframes_t nframes, void* pvUserData) { AudioPlatform* pAudioPlatform = static_cast(pvUserData); return pAudioPlatform->audioCallback(nframes); } void AudioPlatform::latencyCallback(jack_latency_callback_mode_t, void* pvUserData) { AudioPlatform* pAudioPlatform = static_cast(pvUserData); pAudioPlatform->updateLatency(); } void AudioPlatform::updateLatency() { jack_latency_range_t latencyRange; jack_port_get_latency_range(mpJackPorts[0], JackPlaybackLatency, &latencyRange); mEngine.mOutputLatency.store( std::chrono::microseconds(llround(1.0e6 * latencyRange.max / mEngine.mSampleRate))); } 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.load(); engine.audioCallback(bufferBeginAtOutput, nframes); for (int k = 0; k < 2; ++k) { float* buffer = static_cast(jack_port_get_buffer(mpJackPorts[k], nframes)); for (unsigned long i = 0; i < nframes; ++i) buffer[i] = static_cast(engine.mBuffer[i]); } return 0; } void AudioPlatform::initialize() { jack_status_t status = JackFailure; mpJackClient = jack_client_open("LinkHut", JackNullOption, &status); if (mpJackClient == nullptr) { 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(); } const double bufferSize = jack_get_buffer_size(mpJackClient); const double sampleRate = jack_get_sample_rate(mpJackClient); mEngine.setBufferSize(static_cast(bufferSize)); mEngine.setSampleRate(sampleRate); jack_set_latency_callback(mpJackClient, AudioPlatform::latencyCallback, this); 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] == nullptr) { std::cerr << "Could not get Audio Device. " << std::endl; jack_client_close(mpJackClient); std::terminate(); } } jack_set_process_callback(mpJackClient, AudioPlatform::audioCallback, this); } void AudioPlatform::uninitialize() { for (int k = 0; k < 2; ++k) { jack_port_unregister(mpJackClient, mpJackPorts[k]); mpJackPorts[k] = nullptr; } delete[] mpJackPorts; mpJackPorts = nullptr; jack_client_close(mpJackClient); mpJackClient = nullptr; } void AudioPlatform::start() { jack_activate(mpJackClient); const char** playback_ports = jack_get_ports( mpJackClient, nullptr, 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