Skip to content
Snippets Groups Projects
Commit 71882bc4 authored by Guillaume Roguez's avatar Guillaume Roguez
Browse files

portaudio: C++11'ish the code


Use many C++11 stuffs to make the code clearer and well strutured:

* pimpl idiom to remove the portaudio.h dependency from header file
* array and for-range
* algorithm

(And various little changes for coding rules compliance)

Reviewed-by: default avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Change-Id: Iaa11e16aecb1bd36200c820e4cb1aa657479eb60
parent f31d9133
Branches
Tags
No related merge requests found
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Copyright (C) 2004-2016 Savoir-faire Linux Inc. * Copyright (C) 2004-2016 Savoir-faire Linux Inc.
* *
* Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com> * Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com>
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -25,48 +26,80 @@ ...@@ -25,48 +26,80 @@
#include "audio/ringbufferpool.h" #include "audio/ringbufferpool.h"
#include "audio/ringbuffer.h" #include "audio/ringbuffer.h"
#include <portaudio.h>
#include <algorithm>
#include <cmath>
namespace ring { namespace ring {
PortAudioLayer::PortAudioLayer(const AudioPreference &pref) enum Direction {Input=0, Output=1, End=2};
: AudioLayer(pref)
, indexIn_(pref.getAlsaCardin())
, indexOut_(pref.getAlsaCardout())
, indexRing_(pref.getAlsaCardring())
, playbackBuff_(0, audioFormat_)
, mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
{
init();
}
PortAudioLayer::~PortAudioLayer() struct PortAudioLayer::PortAudioLayerImpl
{ {
terminate(); PortAudioLayerImpl(PortAudioLayer&, const AudioPreference&);
} ~PortAudioLayerImpl();
void init(PortAudioLayer&);
void terminate() const;
void initStream(PortAudioLayer&);
std::vector<std::string> getDeviceByType(bool) const;
PaDeviceIndex indexIn_;
PaDeviceIndex indexOut_;
PaDeviceIndex indexRing_;
AudioBuffer playbackBuff_;
std::shared_ptr<RingBuffer> mainRingBuffer_;
std::array<PaStream*, static_cast<int>(Direction::End)> streams_;
int paOutputCallback(PortAudioLayer& parent,
const AudioSample* inputBuffer,
AudioSample* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags);
int paInputCallback(PortAudioLayer& parent,
const AudioSample* inputBuffer,
AudioSample* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags);
};
//##################################################################################################
PortAudioLayer::PortAudioLayer(const AudioPreference& pref)
: AudioLayer {pref}
, pimpl_ {new PortAudioLayerImpl(*this, pref)}
{}
std::vector<std::string> std::vector<std::string>
PortAudioLayer::getCaptureDeviceList() const PortAudioLayer::getCaptureDeviceList() const
{ {
return this->getDeviceByType(false); return pimpl_->getDeviceByType(false);
} }
std::vector<std::string> std::vector<std::string>
PortAudioLayer::getPlaybackDeviceList() const PortAudioLayer::getPlaybackDeviceList() const
{ {
return this->getDeviceByType(true); return pimpl_->getDeviceByType(true);
} }
int int
PortAudioLayer::getAudioDeviceIndex(const std::string& name, PortAudioLayer::getAudioDeviceIndex(const std::string& name, DeviceType type) const
DeviceType type) const
{ {
int numDevices = 0; int numDevices = 0;
(void) type; (void) type;
numDevices = Pa_GetDeviceCount(); numDevices = Pa_GetDeviceCount();
if (numDevices < 0) if (numDevices < 0) {
RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(numDevices)); RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(numDevices));
else { } else {
const PaDeviceInfo* deviceInfo; const PaDeviceInfo* deviceInfo;
for (int i = 0; i < numDevices; i++) { for (int i = 0; i < numDevices; i++) {
deviceInfo = Pa_GetDeviceInfo(i); deviceInfo = Pa_GetDeviceInfo(i);
...@@ -89,19 +122,19 @@ PortAudioLayer::getAudioDeviceName(int index, DeviceType type) const ...@@ -89,19 +122,19 @@ PortAudioLayer::getAudioDeviceName(int index, DeviceType type) const
int int
PortAudioLayer::getIndexCapture() const PortAudioLayer::getIndexCapture() const
{ {
return this->indexIn_; return pimpl_->indexIn_;
} }
int int
PortAudioLayer::getIndexPlayback() const PortAudioLayer::getIndexPlayback() const
{ {
return this->indexOut_; return pimpl_->indexOut_;
} }
int int
PortAudioLayer::getIndexRingtone() const PortAudioLayer::getIndexRingtone() const
{ {
return this->indexRing_; return pimpl_->indexRing_;
} }
void void
...@@ -113,7 +146,10 @@ PortAudioLayer::startStream() ...@@ -113,7 +146,10 @@ PortAudioLayer::startStream()
return; return;
status_ = Status::Started; status_ = Status::Started;
} }
this->initStream(); pimpl_->initStream(*this);
flushUrgent();
flushMain();
} }
void void
...@@ -124,34 +160,33 @@ PortAudioLayer::stopStream() ...@@ -124,34 +160,33 @@ PortAudioLayer::stopStream()
RING_DBG("Stop PortAudio Streams"); RING_DBG("Stop PortAudio Streams");
for (int i = 0; i < Direction::End; i++) { for (auto& st_ptr : pimpl_->streams_) {
auto err = Pa_StopStream(streams[i]); auto err = Pa_StopStream(st_ptr);
if (err != paNoError) if (err != paNoError)
RING_ERR("Pa_StopStream error : %s", Pa_GetErrorText(err)); RING_ERR("Pa_StopStream error : %s", Pa_GetErrorText(err));
err = Pa_CloseStream(streams[i]); err = Pa_CloseStream(st_ptr);
if (err != paNoError) if (err != paNoError)
RING_ERR("Pa_StopStream error : %s", Pa_GetErrorText(err)); RING_ERR("Pa_StopStream error : %s", Pa_GetErrorText(err));
} }
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock {mutex_};
status_ = Status::Idle; status_ = Status::Idle;
} }
/* Flush the ring buffers */ // Flush the ring buffers
flushUrgent(); flushUrgent();
flushMain(); flushMain();
} }
void void
PortAudioLayer::updatePreference(AudioPreference &preference, PortAudioLayer::updatePreference(AudioPreference& preference, int index, DeviceType type)
int index, DeviceType type)
{ {
switch (type) { switch (type) {
case DeviceType::PLAYBACK: case DeviceType::PLAYBACK:
{ {
auto playbackList = getDeviceByType(true); auto playbackList = pimpl_->getDeviceByType(true);
if (playbackList.size() > (size_t) index) { if (playbackList.size() > (size_t) index) {
auto realIdx = getAudioDeviceIndex(playbackList.at(index), type); auto realIdx = getAudioDeviceIndex(playbackList.at(index), type);
preference.setAlsaCardout(realIdx); preference.setAlsaCardout(realIdx);
...@@ -161,7 +196,7 @@ PortAudioLayer::updatePreference(AudioPreference &preference, ...@@ -161,7 +196,7 @@ PortAudioLayer::updatePreference(AudioPreference &preference,
case DeviceType::CAPTURE: case DeviceType::CAPTURE:
{ {
auto captureList = getDeviceByType(false); auto captureList = pimpl_->getDeviceByType(false);
if (captureList.size() > (size_t) index) { if (captureList.size() > (size_t) index) {
auto realIdx = getAudioDeviceIndex(captureList.at(index), type); auto realIdx = getAudioDeviceIndex(captureList.at(index), type);
preference.setAlsaCardin(realIdx); preference.setAlsaCardin(realIdx);
...@@ -178,8 +213,25 @@ PortAudioLayer::updatePreference(AudioPreference &preference, ...@@ -178,8 +213,25 @@ PortAudioLayer::updatePreference(AudioPreference &preference,
} }
} }
//##################################################################################################
PortAudioLayer::PortAudioLayerImpl::PortAudioLayerImpl(PortAudioLayer& parent, const AudioPreference& pref)
: indexIn_ {pref.getAlsaCardin()}
, indexOut_ {pref.getAlsaCardout()}
, indexRing_ {pref.getAlsaCardring()}
, playbackBuff_ {0, parent.audioFormat_}
, mainRingBuffer_ {Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID)}
{
init(parent);
}
PortAudioLayer::PortAudioLayerImpl::~PortAudioLayerImpl()
{
terminate();
}
std::vector<std::string> std::vector<std::string>
PortAudioLayer::getDeviceByType(const bool& playback) const PortAudioLayer::PortAudioLayerImpl::getDeviceByType(bool playback) const
{ {
std::vector<std::string> ret; std::vector<std::string> ret;
int numDevices = 0; int numDevices = 0;
...@@ -203,42 +255,35 @@ PortAudioLayer::getDeviceByType(const bool& playback) const ...@@ -203,42 +255,35 @@ PortAudioLayer::getDeviceByType(const bool& playback) const
} }
int int
PortAudioLayer::paOutputCallback(const void *inputBuffer, void *outputBuffer, PortAudioLayer::PortAudioLayerImpl::paOutputCallback(PortAudioLayer& parent,
const AudioSample* inputBuffer,
AudioSample* outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags)
void *userData)
{ {
// unused arguments
(void) inputBuffer; (void) inputBuffer;
(void) timeInfo; (void) timeInfo;
(void) statusFlags; (void) statusFlags;
auto ref = (PortAudioLayer*)userData; AudioFormat mainBufferAudioFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
auto out = (AudioSample*)outputBuffer; bool resample = parent.audioFormat_.sample_rate != mainBufferAudioFormat.sample_rate;
auto urgentFramesToGet = parent.urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
AudioFormat mainBufferAudioFormat =
Manager::instance().getRingBufferPool().getInternalAudioFormat();
bool resample =
ref->audioFormat_.sample_rate != mainBufferAudioFormat.sample_rate;
auto urgentFramesToGet =
ref->urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
if (urgentFramesToGet > 0) { if (urgentFramesToGet > 0) {
RING_WARN("Getting urgent frames."); RING_WARN("Getting urgent frames.");
size_t totSample = std::min(framesPerBuffer, size_t totSample = std::min(framesPerBuffer, (unsigned long)urgentFramesToGet);
(unsigned long)urgentFramesToGet);
ref->playbackBuff_.setFormat(ref->audioFormat_); playbackBuff_.setFormat(parent.audioFormat_);
ref->playbackBuff_.resize(totSample); playbackBuff_.resize(totSample);
ref->urgentRingBuffer_.get(ref->playbackBuff_, RingBufferPool::DEFAULT_ID); parent.urgentRingBuffer_.get(playbackBuff_, RingBufferPool::DEFAULT_ID);
ref->playbackBuff_.applyGain(ref->isPlaybackMuted_ ? 0.0 : ref->playbackGain_); playbackBuff_.applyGain(parent.isPlaybackMuted_ ? 0.0 : parent.playbackGain_);
ref->playbackBuff_.interleave(out); playbackBuff_.interleave(outputBuffer);
Manager::instance().getRingBufferPool().discard(totSample, Manager::instance().getRingBufferPool().discard(totSample, RingBufferPool::DEFAULT_ID);
RingBufferPool::DEFAULT_ID);
} }
unsigned normalFramesToGet = unsigned normalFramesToGet =
...@@ -248,119 +293,107 @@ PortAudioLayer::paOutputCallback(const void *inputBuffer, void *outputBuffer, ...@@ -248,119 +293,107 @@ PortAudioLayer::paOutputCallback(const void *inputBuffer, void *outputBuffer,
unsigned readableSamples = framesPerBuffer; unsigned readableSamples = framesPerBuffer;
if (resample) { if (resample) {
resampleFactor = resampleFactor = static_cast<double>(parent.audioFormat_.sample_rate)
static_cast<double>(ref->audioFormat_.sample_rate)
/ mainBufferAudioFormat.sample_rate; / mainBufferAudioFormat.sample_rate;
readableSamples = std::ceil(framesPerBuffer / resampleFactor); readableSamples = std::ceil(framesPerBuffer / resampleFactor);
} }
readableSamples = std::min(readableSamples, normalFramesToGet); readableSamples = std::min(readableSamples, normalFramesToGet);
ref->playbackBuff_.setFormat(ref->audioFormat_); playbackBuff_.setFormat(parent.audioFormat_);
ref->playbackBuff_.resize(readableSamples); playbackBuff_.resize(readableSamples);
Manager::instance().getRingBufferPool().getData(ref->playbackBuff_, Manager::instance().getRingBufferPool().getData(playbackBuff_, RingBufferPool::DEFAULT_ID);
RingBufferPool::DEFAULT_ID); playbackBuff_.applyGain(parent.isPlaybackMuted_ ? 0.0 : parent.playbackGain_);
ref->playbackBuff_.applyGain(ref->isPlaybackMuted_ ? 0.0 : ref->playbackGain_);
if (resample) { if (resample) {
AudioBuffer resampledOutput(readableSamples, ref->audioFormat_); AudioBuffer resampledOutput(readableSamples, parent.audioFormat_);
ref->resampler_->resample(ref->playbackBuff_, resampledOutput); parent.resampler_->resample(playbackBuff_, resampledOutput);
resampledOutput.interleave(out); resampledOutput.interleave(outputBuffer);
} else { } else {
ref->playbackBuff_.interleave(out); playbackBuff_.interleave(outputBuffer);
} }
} }
if (normalFramesToGet <= 0) { if (normalFramesToGet <= 0) {
auto tone = Manager::instance().getTelephoneTone(); auto tone = Manager::instance().getTelephoneTone();
auto file_tone = Manager::instance().getTelephoneFile(); auto file_tone = Manager::instance().getTelephoneFile();
ref->playbackBuff_.setFormat(ref->audioFormat_); playbackBuff_.setFormat(parent.audioFormat_);
ref->playbackBuff_.resize(framesPerBuffer); playbackBuff_.resize(framesPerBuffer);
if (tone) { if (tone) {
tone->getNext(ref->playbackBuff_, ref->playbackGain_); tone->getNext(playbackBuff_, parent.playbackGain_);
} else if (file_tone) { } else if (file_tone) {
file_tone->getNext(ref->playbackBuff_, ref->playbackGain_); file_tone->getNext(playbackBuff_, parent.playbackGain_);
} else { } else {
//RING_WARN("No tone or file_tone!"); //RING_WARN("No tone or file_tone!");
ref->playbackBuff_.reset(); playbackBuff_.reset();
} }
ref->playbackBuff_.interleave(out); playbackBuff_.interleave(outputBuffer);
} }
return paContinue; return paContinue;
} }
int int
PortAudioLayer::paInputCallback(const void *inputBuffer, void *outputBuffer, PortAudioLayer::PortAudioLayerImpl::paInputCallback(PortAudioLayer& parent,
const AudioSample* inputBuffer,
AudioSample* outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags)
void *userData)
{ {
// unused arguments
(void) outputBuffer; (void) outputBuffer;
(void) timeInfo; (void) timeInfo;
(void) statusFlags; (void) statusFlags;
auto ref = (PortAudioLayer*)userData;
auto in = (AudioSample*)inputBuffer;
if (framesPerBuffer == 0) { if (framesPerBuffer == 0) {
RING_WARN("No frames for input."); RING_WARN("No frames for input.");
return paContinue; return paContinue;
} }
const auto mainBufferFormat = const auto mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
Manager::instance().getRingBufferPool().getInternalAudioFormat(); bool resample = parent.audioInputFormat_.sample_rate != mainBufferFormat.sample_rate;
bool resample = AudioBuffer inBuff(framesPerBuffer, parent.audioInputFormat_);
ref->audioInputFormat_.sample_rate != mainBufferFormat.sample_rate;
AudioBuffer inBuff(framesPerBuffer, ref->audioInputFormat_); inBuff.deinterleave(inputBuffer, framesPerBuffer, parent.audioInputFormat_.nb_channels);
inBuff.deinterleave(in, framesPerBuffer, ref->audioInputFormat_.nb_channels); inBuff.applyGain(parent.isCaptureMuted_ ? 0.0 : parent.captureGain_);
inBuff.applyGain(ref->isCaptureMuted_ ? 0.0 : ref->captureGain_);
if (resample) { if (resample) {
auto outSamples = auto outSamples =
framesPerBuffer framesPerBuffer
/ (static_cast<double>(ref->audioInputFormat_.sample_rate) / (static_cast<double>(parent.audioInputFormat_.sample_rate)
/ mainBufferFormat.sample_rate); / mainBufferFormat.sample_rate);
AudioBuffer out(outSamples, mainBufferFormat); AudioBuffer out(outSamples, mainBufferFormat);
ref->inputResampler_->resample(inBuff, out); parent.inputResampler_->resample(inBuff, out);
ref->dcblocker_.process(out); parent.dcblocker_.process(out);
ref->mainRingBuffer_->put(out); mainRingBuffer_->put(out);
} else { } else {
ref->dcblocker_.process(inBuff); parent.dcblocker_.process(inBuff);
ref->mainRingBuffer_->put(inBuff); mainRingBuffer_->put(inBuff);
} }
return paContinue; return paContinue;
} }
void void
PortAudioLayer::init() PortAudioLayer::PortAudioLayerImpl::init(PortAudioLayer& parent)
{ {
RING_DBG("Init PortAudioLayer"); RING_DBG("Init PortAudioLayer");
const auto err = Pa_Initialize(); const auto err = Pa_Initialize();
if (err != paNoError) { if (err != paNoError) {
RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err)); RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err));
this->terminate(); terminate();
} }
#ifdef RING_UWP
indexRing_ = indexOut_ = Pa_GetDefaultOutputDevice();
indexIn_ = Pa_GetDefaultInputDevice();
#else
indexRing_ = indexOut_ = indexOut_ == paNoDevice ? Pa_GetDefaultOutputDevice() : indexOut_; indexRing_ = indexOut_ = indexOut_ == paNoDevice ? Pa_GetDefaultOutputDevice() : indexOut_;
indexIn_ = indexIn_ == paNoDevice ? Pa_GetDefaultInputDevice() : indexIn_; indexIn_ = indexIn_ == paNoDevice ? Pa_GetDefaultInputDevice() : indexIn_;
#endif
if (indexOut_ != paNoDevice) { if (indexOut_ != paNoDevice) {
if (const auto outputDeviceInfo = Pa_GetDeviceInfo(indexOut_)) { if (const auto outputDeviceInfo = Pa_GetDeviceInfo(indexOut_)) {
audioFormat_.nb_channels = outputDeviceInfo->maxOutputChannels; parent.audioFormat_.nb_channels = outputDeviceInfo->maxOutputChannels;
audioFormat_.sample_rate = outputDeviceInfo->defaultSampleRate; parent.audioFormat_.sample_rate = outputDeviceInfo->defaultSampleRate;
hardwareFormatAvailable(audioFormat_); parent.hardwareFormatAvailable(parent.audioFormat_);
} else { } else {
indexOut_ = paNoDevice; indexOut_ = paNoDevice;
} }
...@@ -368,20 +401,19 @@ PortAudioLayer::init() ...@@ -368,20 +401,19 @@ PortAudioLayer::init()
if (indexIn_ != paNoDevice) { if (indexIn_ != paNoDevice) {
if (const auto inputDeviceInfo = Pa_GetDeviceInfo(indexIn_)) { if (const auto inputDeviceInfo = Pa_GetDeviceInfo(indexIn_)) {
audioInputFormat_.nb_channels = inputDeviceInfo->maxInputChannels; parent.audioInputFormat_.nb_channels = inputDeviceInfo->maxInputChannels;
audioInputFormat_.sample_rate = inputDeviceInfo->defaultSampleRate; parent.audioInputFormat_.sample_rate = inputDeviceInfo->defaultSampleRate;
hardwareInputFormatAvailable(audioInputFormat_); parent.hardwareInputFormatAvailable(parent.audioInputFormat_);
} else { } else {
indexIn_ = paNoDevice; indexIn_ = paNoDevice;
} }
} }
for (int i = 0; i < Direction::End; i++) std::fill(std::begin(streams_), std::end(streams_), nullptr);
streams[i] = nullptr;
} }
void void
PortAudioLayer::terminate() const PortAudioLayer::PortAudioLayerImpl::terminate() const
{ {
RING_DBG("PortAudioLayer terminate."); RING_DBG("PortAudioLayer terminate.");
const auto err = Pa_Terminate(); const auto err = Pa_Terminate();
...@@ -390,9 +422,9 @@ PortAudioLayer::terminate() const ...@@ -390,9 +422,9 @@ PortAudioLayer::terminate() const
} }
void void
PortAudioLayer::initStream() PortAudioLayer::PortAudioLayerImpl::initStream(PortAudioLayer& parent)
{ {
dcblocker_.reset(); parent.dcblocker_.reset();
RING_DBG("Open PortAudio Output Stream"); RING_DBG("Open PortAudio Output Stream");
PaStreamParameters outputParameters; PaStreamParameters outputParameters;
...@@ -401,23 +433,33 @@ PortAudioLayer::initStream() ...@@ -401,23 +433,33 @@ PortAudioLayer::initStream()
if (outputParameters.device == paNoDevice) { if (outputParameters.device == paNoDevice) {
RING_ERR("Error: No valid output device. There will be no sound."); RING_ERR("Error: No valid output device. There will be no sound.");
} else { } else {
const auto outputDeviceInfo = const auto outputDeviceInfo = Pa_GetDeviceInfo(outputParameters.device);
Pa_GetDeviceInfo(outputParameters.device); outputParameters.channelCount = parent.audioFormat_.nb_channels = outputDeviceInfo->maxOutputChannels;
outputParameters.channelCount =
audioFormat_.nb_channels = outputDeviceInfo->maxOutputChannels;
outputParameters.sampleFormat = paInt16; outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = outputDeviceInfo->defaultLowOutputLatency; outputParameters.suggestedLatency = outputDeviceInfo->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL; outputParameters.hostApiSpecificStreamInfo = nullptr;
auto err = Pa_OpenStream( auto err = Pa_OpenStream(
&streams[Direction::Output], &streams_[Direction::Output],
NULL, nullptr,
&outputParameters, &outputParameters,
outputDeviceInfo->defaultSampleRate, outputDeviceInfo->defaultSampleRate,
paFramesPerBufferUnspecified, paFramesPerBufferUnspecified,
paNoFlag, paNoFlag,
&PortAudioLayer::paOutputCallback, [](const void* inputBuffer, void* outputBuffer,
this); unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData) -> int {
auto layer = static_cast<PortAudioLayer*>(userData);
return layer->pimpl_->paOutputCallback(*layer,
static_cast<const AudioSample*>(inputBuffer),
static_cast<AudioSample*>(outputBuffer),
framesPerBuffer,
timeInfo,
statusFlags);
},
&parent);
if(err != paNoError) if(err != paNoError)
RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err)); RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err));
} }
...@@ -425,40 +467,49 @@ PortAudioLayer::initStream() ...@@ -425,40 +467,49 @@ PortAudioLayer::initStream()
RING_DBG("Open PortAudio Input Stream"); RING_DBG("Open PortAudio Input Stream");
PaStreamParameters inputParameters; PaStreamParameters inputParameters;
inputParameters.device = indexIn_; inputParameters.device = indexIn_;
if (inputParameters.device == paNoDevice) { if (inputParameters.device == paNoDevice) {
RING_ERR("Error: No valid input device. There will be no mic."); RING_ERR("Error: No valid input device. There will be no mic.");
} else { } else {
const auto inputDeviceInfo = const auto inputDeviceInfo = Pa_GetDeviceInfo(inputParameters.device);
Pa_GetDeviceInfo(inputParameters.device); inputParameters.channelCount = parent.audioInputFormat_.nb_channels = inputDeviceInfo->maxInputChannels;
inputParameters.channelCount =
audioInputFormat_.nb_channels = inputDeviceInfo->maxInputChannels;
inputParameters.sampleFormat = paInt16; inputParameters.sampleFormat = paInt16;
inputParameters.suggestedLatency = inputDeviceInfo->defaultLowInputLatency; inputParameters.suggestedLatency = inputDeviceInfo->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL; inputParameters.hostApiSpecificStreamInfo = nullptr;
auto err = Pa_OpenStream( auto err = Pa_OpenStream(
&streams[Direction::Input], &streams_[Direction::Input],
&inputParameters, &inputParameters,
NULL, nullptr,
inputDeviceInfo->defaultSampleRate, inputDeviceInfo->defaultSampleRate,
paFramesPerBufferUnspecified, paFramesPerBufferUnspecified,
paNoFlag, paNoFlag,
&PortAudioLayer::paInputCallback, [](const void* inputBuffer, void* outputBuffer,
this); unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData) -> int {
auto layer = static_cast<PortAudioLayer*>(userData);
return layer->pimpl_->paInputCallback(*layer,
static_cast<const AudioSample*>(inputBuffer),
static_cast<AudioSample*>(outputBuffer),
framesPerBuffer,
timeInfo,
statusFlags);
},
&parent);
if (err != paNoError) if (err != paNoError)
RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err)); RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err));
} }
RING_DBG("Start PortAudio Streams"); RING_DBG("Start PortAudio Streams");
for (int i = 0; i < Direction::End; i++) { for (auto& st_ptr : streams_) {
if (streams[i]) { if (st_ptr) {
auto err = Pa_StartStream(streams[i]); auto err = Pa_StartStream(st_ptr);
if (err != paNoError) if (err != paNoError)
RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err)); RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err));
} }
} }
flushUrgent();
flushMain();
} }
} // namespace ring } // namespace ring
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Copyright (C) 2004-2016 Savoir-faire Linux Inc. * Copyright (C) 2004-2016 Savoir-faire Linux Inc.
* *
* Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com> * Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com>
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -18,25 +19,24 @@ ...@@ -18,25 +19,24 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef PORTAUDIO_LAYER_H #pragma once
#define PORTAUDIO_LAYER_H
#include <portaudio.h>
#include "audio/audiolayer.h" #include "audio/audiolayer.h"
#include "noncopyable.h" #include "noncopyable.h"
#include <memory>
#include <array>
namespace ring { namespace ring {
class PortAudioLayer : public AudioLayer { class PortAudioLayer : public AudioLayer {
public: public:
PortAudioLayer(const AudioPreference& pref); PortAudioLayer(const AudioPreference& pref);
~PortAudioLayer(); virtual ~PortAudioLayer() = default;
virtual std::vector<std::string> getCaptureDeviceList() const; virtual std::vector<std::string> getCaptureDeviceList() const;
virtual std::vector<std::string> getPlaybackDeviceList() const; virtual std::vector<std::string> getPlaybackDeviceList() const;
virtual int getAudioDeviceIndex(const std::string& name, DeviceType type) const; virtual int getAudioDeviceIndex(const std::string& name, DeviceType type) const;
virtual std::string getAudioDeviceName(int index, DeviceType type) const; virtual std::string getAudioDeviceName(int index, DeviceType type) const;
virtual int getIndexCapture() const; virtual int getIndexCapture() const;
...@@ -60,34 +60,8 @@ public: ...@@ -60,34 +60,8 @@ public:
private: private:
NON_COPYABLE(PortAudioLayer); NON_COPYABLE(PortAudioLayer);
void init(void); struct PortAudioLayerImpl;
void terminate(void) const; std::unique_ptr<PortAudioLayerImpl> pimpl_;
void initStream(void);
std::vector<std::string> getDeviceByType(const bool& playback) const;
PaDeviceIndex indexIn_;
PaDeviceIndex indexOut_;
PaDeviceIndex indexRing_;
AudioBuffer playbackBuff_;
std::shared_ptr<RingBuffer> mainRingBuffer_;
enum Direction {Input=0, Output=1, End=2};
PaStream* streams[(int)Direction::End];
static int paOutputCallback(const void *inputBuffer, void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
static int paInputCallback(const void *inputBuffer, void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
}; };
}
#endif } // namespace ring
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment