#include <chrono>
#include <iostream>
#include <string>
namespace ableton
{
namespace linkaudio
{
template <typename Link>
AudioPlatform<Link>::AudioPlatform(Link& link)
: mEngine(link)
, mSampleTime(0.)
, mpJackClient(nullptr)
, mpJackPorts(nullptr)
{
initialize();
start();
}
template <typename Link>
AudioPlatform<Link>::~AudioPlatform<Link>()
{
stop();
uninitialize();
}
template <typename Link>
int AudioPlatform<Link>::audioCallback(jack_nframes_t nframes, void* pvUserData)
{
auto pAudioPlatform = static_cast<AudioPlatform*>(pvUserData);
return pAudioPlatform->audioCallback(nframes);
}
template <typename Link>
void AudioPlatform<Link>::latencyCallback(jack_latency_callback_mode_t, void* pvUserData)
{
auto pAudioPlatform = static_cast<AudioPlatform*>(pvUserData);
pAudioPlatform->updateLatency();
}
template <typename Link>
void AudioPlatform<Link>::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)));
}
template <typename Link>
int AudioPlatform<Link>::audioCallback(jack_nframes_t nframes)
{
using namespace std::chrono;
AudioEngine<Link>& engine = mEngine;
const auto hostTime = mHostTimeFilter.sampleTimeToHostTime(mSampleTime);
mSampleTime += nframes;
const auto bufferBeginAtOutput = hostTime + engine.mOutputLatency.load();
engine.audioCallback(bufferBeginAtOutput, nframes);
for (std::size_t k = 0; k < 2; ++k)
{
auto buffer = static_cast<float*>(jack_port_get_buffer(mpJackPorts[k], nframes));
for (std::size_t i = 0; i < nframes; ++i)
{
buffer[i] = static_cast<float>(engine.mBuffers[k][i]);
}
}
return 0;
}
template <typename Link>
void AudioPlatform<Link>::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.setNumFrames(static_cast<std::size_t>(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);
}
template <typename Link>
void AudioPlatform<Link>::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;
}
template <typename Link>
void AudioPlatform<Link>::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);
}
}
template <typename Link>
void AudioPlatform<Link>::stop()
{
jack_deactivate(mpJackClient);
}
} }