/* 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_Portaudio.hpp"
#include
#include
namespace ableton
{
namespace linkaudio
{
AudioPlatform::AudioPlatform(Link& link)
: mEngine(link)
, mSampleTime(0.)
{
mEngine.setSampleRate(44100.);
mEngine.setBufferSize(512);
initialize();
start();
}
AudioPlatform::~AudioPlatform()
{
stop();
uninitialize();
}
int AudioPlatform::audioCallback(const void* /*inputBuffer*/,
void* outputBuffer,
unsigned long inNumFrames,
const PaStreamCallbackTimeInfo* /*timeInfo*/,
PaStreamCallbackFlags /*statusFlags*/,
void* userData)
{
using namespace std::chrono;
float* buffer = static_cast(outputBuffer);
AudioPlatform& platform = *static_cast(userData);
AudioEngine& engine = platform.mEngine;
const auto hostTime =
platform.mHostTimeFilter.sampleTimeToHostTime(platform.mSampleTime);
platform.mSampleTime += static_cast(inNumFrames);
const auto bufferBeginAtOutput = hostTime + engine.mOutputLatency.load();
engine.audioCallback(bufferBeginAtOutput, inNumFrames);
for (unsigned long i = 0; i < inNumFrames; ++i)
{
buffer[i * 2] = static_cast(engine.mBuffer[i]);
buffer[i * 2 + 1] = static_cast(engine.mBuffer[i]);
}
return paContinue;
}
void AudioPlatform::initialize()
{
PaError result = Pa_Initialize();
if (result)
{
std::cerr << "Could not initialize Audio Engine. " << result << std::endl;
std::terminate();
}
PaStreamParameters outputParameters;
outputParameters.device = Pa_GetDefaultOutputDevice();
if (outputParameters.device == paNoDevice)
{
std::cerr << "Could not get Audio Device. " << std::endl;
std::terminate();
}
outputParameters.channelCount = 2;
outputParameters.sampleFormat = paFloat32;
outputParameters.suggestedLatency =
Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = nullptr;
mEngine.mOutputLatency.store(
std::chrono::microseconds(llround(outputParameters.suggestedLatency * 1.0e6)));
result = Pa_OpenStream(&pStream, nullptr, &outputParameters, mEngine.mSampleRate,
mEngine.mBuffer.size(), paClipOff, &audioCallback, this);
if (result)
{
std::cerr << "Could not open stream. " << result << std::endl;
std::terminate();
}
if (!pStream)
{
std::cerr << "No valid audio stream." << std::endl;
std::terminate();
}
}
void AudioPlatform::uninitialize()
{
PaError result = Pa_CloseStream(pStream);
if (result)
{
std::cerr << "Could not close Audio Stream. " << result << std::endl;
}
Pa_Terminate();
if (!pStream)
{
std::cerr << "No valid audio stream." << std::endl;
std::terminate();
}
}
void AudioPlatform::start()
{
PaError result = Pa_StartStream(pStream);
if (result)
{
std::cerr << "Could not start Audio Stream. " << result << std::endl;
}
}
void AudioPlatform::stop()
{
if (pStream == nullptr)
{
return;
}
PaError result = Pa_StopStream(pStream);
if (result)
{
std::cerr << "Could not stop Audio Stream. " << result << std::endl;
std::terminate();
}
}
} // namespace linkaudio
} // namespace ableton