Skip to content
Snippets Groups Projects
Commit ef9d7bf2 authored by Adrien Béraud's avatar Adrien Béraud
Browse files

ringbuffer, audio layer: use AudioFrame

* RingBuffer: handle AudioFrame instead of raw samples
* RingBuffer: input resampler, frame resizer
* AudioLayer: use new RingBuffer API

Change-Id: I39bfcf7706e872dde23aea668885ede6d8abe752
parent fd93525e
No related branches found
No related tags found
No related merge requests found
Showing
with 402 additions and 858 deletions
......@@ -91,24 +91,25 @@ void AlsaThread::initAudioLayer(void)
}
if (not alsa_->is_capture_open_) {
alsa_->is_capture_open_ = alsa_->openDevice(&alsa_->captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE);
alsa_->is_capture_open_ = alsa_->openDevice(&alsa_->captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE, alsa_->audioInputFormat_);
if (not alsa_->is_capture_open_)
emitSignal<DRing::ConfigurationSignal::Error>(ALSA_CAPTURE_DEVICE);
}
if (not alsa_->is_playback_open_) {
alsa_->is_playback_open_ = alsa_->openDevice(&alsa_->playbackHandle_, pcmp, SND_PCM_STREAM_PLAYBACK);
alsa_->is_playback_open_ = alsa_->openDevice(&alsa_->playbackHandle_, pcmp, SND_PCM_STREAM_PLAYBACK, alsa_->audioFormat_);
if (not alsa_->is_playback_open_)
emitSignal<DRing::ConfigurationSignal::Error>(ALSA_PLAYBACK_DEVICE);
if (alsa_->getIndexPlayback() != alsa_->getIndexRingtone())
if (!alsa_->openDevice(&alsa_->ringtoneHandle_, pcmr, SND_PCM_STREAM_PLAYBACK))
if (!alsa_->openDevice(&alsa_->ringtoneHandle_, pcmr, SND_PCM_STREAM_PLAYBACK, alsa_->audioFormat_))
emitSignal<DRing::ConfigurationSignal::Error>(ALSA_PLAYBACK_DEVICE);
}
alsa_->hardwareFormatAvailable(alsa_->getFormat());
alsa_->hardwareInputFormatAvailable(alsa_->audioInputFormat_);
alsa_->prepareCaptureStream();
alsa_->preparePlaybackStream();
......@@ -172,7 +173,7 @@ AlsaLayer::~AlsaLayer()
}
// Retry approach taken from pa_linux_alsa.c, part of PortAudio
bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream)
bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream, AudioFormat& format)
{
RING_DBG("Alsa: Opening %s", dev.c_str());
......@@ -199,7 +200,7 @@ bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stre
return false;
}
if (!alsa_set_params(*pcm)) {
if (!alsa_set_params(*pcm, format)) {
snd_pcm_close(*pcm);
return false;
}
......@@ -334,7 +335,7 @@ void AlsaLayer::preparePlaybackStream()
is_playback_prepared_ = true;
}
bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle, AudioFormat& format)
{
#define TRY(call, error) do { \
if (ALSA_CALL(call, error) < 0) \
......@@ -364,11 +365,12 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
TRY(snd_pcm_hw_params_set_format(HW, SND_PCM_FORMAT_S16_LE), "sample format");
TRY(snd_pcm_hw_params_set_rate_resample(HW, 0), "hardware sample rate"); /* prevent software resampling */
TRY(snd_pcm_hw_params_set_rate_near(HW, &audioFormat_.sample_rate, nullptr), "sample rate");
TRY(snd_pcm_hw_params_set_rate_near(HW, &format.sample_rate, nullptr), "sample rate");
// TODO: use snd_pcm_query_chmaps or similar to get hardware channel num
audioFormat_.nb_channels = 2;
TRY(snd_pcm_hw_params_set_channels_near(HW, &audioFormat_.nb_channels), "channel count");
format.nb_channels = 2;
TRY(snd_pcm_hw_params_set_channels_near(HW, &format.nb_channels), "channel count");
snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
......@@ -388,8 +390,8 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
snd_pcm_hw_params_get_period_size(hwparams, &period_size, nullptr);
snd_pcm_hw_params_get_rate(hwparams, &audioFormat_.sample_rate, nullptr);
snd_pcm_hw_params_get_channels(hwparams, &audioFormat_.nb_channels);
snd_pcm_hw_params_get_rate(hwparams, &format.sample_rate, nullptr);
snd_pcm_hw_params_get_channels(hwparams, &format.nb_channels);
RING_DBG("Was set period_size = %lu", period_size);
RING_DBG("Was set buffer_size = %lu", buffer_size);
......@@ -402,7 +404,7 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
RING_DBG("%s using format %s",
(snd_pcm_stream(pcm_handle) == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
audioFormat_.toString().c_str() );
format.toString().c_str() );
snd_pcm_sw_params_t *swparams = nullptr;
snd_pcm_sw_params_alloca(&swparams);
......@@ -421,13 +423,9 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
// TODO first frame causes broken pipe (underrun) because not enough data is sent
// we should wait until the handle is ready
void
AlsaLayer::write(AudioSample* buffer, int frames, snd_pcm_t * handle)
AlsaLayer::write(const AudioFrame& buffer, snd_pcm_t * handle)
{
// Skip empty buffers
if (!frames)
return;
int err = snd_pcm_writei(handle, (const void*)buffer, frames);
int err = snd_pcm_writei(handle, (const void*)buffer.pointer()->data[0], buffer.pointer()->nb_samples);
if (err < 0)
snd_pcm_recover(handle, err, 0);
......@@ -450,7 +448,7 @@ AlsaLayer::write(AudioSample* buffer, int frames, snd_pcm_t * handle)
startPlaybackStream();
}
ALSA_CALL(snd_pcm_writei(handle, (const void*)buffer, frames), "XRUN handling failed");
ALSA_CALL(snd_pcm_writei(handle, (const void*)buffer.pointer()->data[0], buffer.pointer()->nb_samples), "XRUN handling failed");
break;
}
......@@ -481,18 +479,21 @@ AlsaLayer::write(AudioSample* buffer, int frames, snd_pcm_t * handle)
}
}
int
AlsaLayer::read(AudioSample* buffer, int frames)
std::unique_ptr<AudioFrame>
AlsaLayer::read(unsigned frames)
{
if (snd_pcm_state(captureHandle_) == SND_PCM_STATE_XRUN) {
prepareCaptureStream();
startCaptureStream();
}
int err = snd_pcm_readi(captureHandle_, (void*)buffer, frames);
auto ret = std::make_unique<AudioFrame>(audioInputFormat_, frames);
int err = snd_pcm_readi(captureHandle_, ret->pointer()->data[0], frames);
if (err >= 0)
return err;
if (err >= 0) {
ret->pointer()->nb_samples = err;
return ret;
}
switch (err) {
case -EPIPE:
......@@ -537,7 +538,7 @@ AlsaLayer::buildDeviceTopo(const std::string &plugin, int card)
}
static bool
safeUpdate(snd_pcm_t *handle, int &samples)
safeUpdate(snd_pcm_t *handle, long &samples)
{
samples = snd_pcm_avail_update(handle);
......@@ -557,6 +558,7 @@ static std::vector<std::string>
getValues(const std::vector<HwIDPair> &deviceMap)
{
std::vector<std::string> audioDeviceList;
audioDeviceList.reserve(deviceMap.size());
for (const auto & dev : deviceMap)
audioDeviceList.push_back(dev.second);
......@@ -634,18 +636,14 @@ AlsaLayer::getAudioDeviceIndexMap(bool getCapture) const
bool
AlsaLayer::soundCardIndexExists(int card, DeviceType stream)
{
snd_pcm_info_t *pcminfo;
snd_pcm_info_alloca(&pcminfo);
std::string name("hw:");
std::stringstream ss;
ss << card;
name.append(ss.str());
const std::string name("hw:" + std::to_string(card));
snd_ctl_t* handle;
if (snd_ctl_open(&handle, name.c_str(), 0) != 0)
return false;
snd_pcm_info_t* pcminfo;
snd_pcm_info_alloca(&pcminfo);
snd_pcm_info_set_stream(pcminfo, stream == DeviceType::PLAYBACK ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE);
bool ret = snd_ctl_pcm_info(handle, pcminfo) >= 0;
snd_ctl_close(handle);
......@@ -690,65 +688,36 @@ void AlsaLayer::capture()
if (!captureHandle_ or !is_capture_running_)
return;
AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
int toGetFrames = snd_pcm_avail_update(captureHandle_);
if (toGetFrames < 0)
RING_ERR("Audio: Mic error: %s", snd_strerror(toGetFrames));
if (toGetFrames <= 0)
return;
const int framesPerBufferAlsa = 2048;
toGetFrames = std::min(framesPerBufferAlsa, toGetFrames);
captureIBuff_.resize(toGetFrames * audioFormat_.nb_channels);
if (read(captureIBuff_.data(), toGetFrames) != toGetFrames) {
if (auto r = read(toGetFrames)) {
//captureBuff_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
//dcblocker_.process(captureBuff_);
mainRingBuffer_->put(std::move(r));
} else
RING_ERR("ALSA MIC : Couldn't read!");
return;
}
captureBuff_.deinterleave(captureIBuff_, audioFormat_);
captureBuff_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
if (audioFormat_.nb_channels != mainBufferFormat.nb_channels) {
captureBuff_.setChannelNum(mainBufferFormat.nb_channels, true);
}
if (audioFormat_.sample_rate != mainBufferFormat.sample_rate) {
int outFrames = toGetFrames * (static_cast<double>(audioFormat_.sample_rate) / mainBufferFormat.sample_rate);
AudioBuffer rsmpl_in(outFrames, mainBufferFormat);
resampler_->resample(captureBuff_, rsmpl_in);
dcblocker_.process(rsmpl_in);
mainRingBuffer_->put(rsmpl_in);
} else {
dcblocker_.process(captureBuff_);
mainRingBuffer_->put(captureBuff_);
}
}
void AlsaLayer::playback()
{
if (!playbackHandle_)
return;
snd_pcm_wait(playbackHandle_, 20);
int maxFrames = 0;
long maxFrames = 0;
if (not safeUpdate(playbackHandle_, maxFrames))
return;
auto& ringBuff = getToRing(audioFormat_, maxFrames);
auto& playBuff = getToPlay(audioFormat_, maxFrames);
auto& toPlay = ringBuff.frames() > 0 ? ringBuff : playBuff;
if (!(toPlay.frames() > 0))
return;
toPlay.interleave(playbackIBuff_);
write(playbackIBuff_.data(), toPlay.frames(), playbackHandle_);
if (auto toPlay = getToPlay(audioFormat_, maxFrames)) {
write(*toPlay, playbackHandle_);
}
}
void AlsaLayer::ringtone()
......@@ -756,22 +725,13 @@ void AlsaLayer::ringtone()
if (!ringtoneHandle_)
return;
auto file_tone = Manager::instance().getTelephoneFile();
int ringtoneAvailFrames = 0;
long ringtoneAvailFrames = 0;
if (not safeUpdate(ringtoneHandle_, ringtoneAvailFrames))
return;
playbackBuff_.setFormat(audioFormat_);
playbackBuff_.resize(ringtoneAvailFrames);
if (file_tone) {
RING_DBG("playback gain %.3f", playbackGain_);
file_tone->getNext(playbackBuff_, playbackGain_);
if (auto toRing = getToRing(audioFormat_, ringtoneAvailFrames)) {
write(*toRing, ringtoneHandle_);
}
playbackBuff_.interleave(playbackIBuff_);
write(playbackIBuff_.data(), ringtoneAvailFrames, ringtoneHandle_);
}
void AlsaLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
......
......@@ -154,7 +154,7 @@ class AlsaLayer : public AudioLayer {
* will often hold on to a device temporarily after it has been opened
* and closed.
*/
bool openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream);
bool openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream, AudioFormat& format);
/**
* Number of audio cards on which capture stream has been opened
......@@ -187,7 +187,7 @@ class AlsaLayer : public AudioLayer {
void startPlaybackStream();
void preparePlaybackStream();
bool alsa_set_params(snd_pcm_t *pcm_handle);
bool alsa_set_params(snd_pcm_t *pcm_handle, AudioFormat& format);
/**
* Copy a data buffer in the internal ring buffer
......@@ -195,7 +195,7 @@ class AlsaLayer : public AudioLayer {
* @param buffer The non-interleaved data to be copied
* @param frames Frames in the buffer
*/
void write(AudioSample* buffer, int frames, snd_pcm_t *handle);
void write(const AudioFrame& buffer, snd_pcm_t *handle);
/**
* Read data from the internal ring buffer
......@@ -204,7 +204,7 @@ class AlsaLayer : public AudioLayer {
* @param frames The number of frames to get
* @return int The number of frames actually read
*/
int read(AudioSample* buffer, int frames);
std::unique_ptr<AudioFrame> read(unsigned frames);
virtual void updatePreference(AudioPreference &pref, int index, DeviceType type);
......
......@@ -64,8 +64,8 @@ void
AudioFrameResizer::setFormat(const AudioFormat& format, int size)
{
if (format != format_) {
if (auto discaded = samples())
RING_WARN("Discarding %d samples", discaded);
if (auto discarded = samples())
RING_WARN("Discarding %d samples", discarded);
av_audio_fifo_free(queue_);
format_ = format;
queue_ = av_audio_fifo_alloc(format.sampleFormat, format.nb_channels, frameSize_);
......
......@@ -92,36 +92,22 @@ AudioInput::nextFromDevice()
auto& mainBuffer = Manager::instance().getRingBufferPool();
auto bufferFormat = mainBuffer.getInternalAudioFormat();
// compute number of samples contained in a frame with duration MS_PER_PACKET
const auto samplesPerPacket = MS_PER_PACKET * bufferFormat.sample_rate;
const std::size_t samplesToGet = std::chrono::duration_cast<std::chrono::seconds>(samplesPerPacket).count();
if (mainBuffer.availableForGet(id_) < samplesToGet
&& not mainBuffer.waitForDataAvailable(id_, samplesToGet, MS_PER_PACKET)) {
if (not mainBuffer.waitForDataAvailable(id_, MS_PER_PACKET)) {
return;
}
// getData resets the format to internal hardware format, will have to be resampled
micData_.setFormat(bufferFormat);
micData_.resize(samplesToGet);
const auto samples = mainBuffer.getData(micData_, id_);
if (samples != samplesToGet)
auto samples = mainBuffer.getData(id_);
if (not samples)
return;
if (muteState_) // audio is muted, set samples to 0
micData_.reset();
//if (muteState_) // audio is muted, set samples to 0
// micData_.reset();
// TODO handle mute
std::lock_guard<std::mutex> lk(fmtMutex_);
AudioBuffer resampled;
resampled.setFormat(format_);
if (bufferFormat != format_) {
resampler_->resample(micData_, resampled);
} else {
resampled = micData_;
{
std::lock_guard<std::mutex> lk(fmtMutex_);
resizer_->enqueue(resampler_->resample(std::move(samples), format_));
}
auto audioFrame = resampled.toAVFrame();
resizer_->enqueue(std::move(audioFrame));
}
void
......@@ -145,16 +131,7 @@ AudioInput::nextFromFile()
createDecoder();
break;
case MediaDecoder::Status::FrameFinished:
if (inFmt != format_) {
AudioFrame out;
out.pointer()->format = format_.sampleFormat;
out.pointer()->sample_rate = format_.sample_rate;
out.pointer()->channel_layout = av_get_default_channel_layout(format_.nb_channels);
out.pointer()->channels = format_.nb_channels;
resampler_->resample(frame->pointer(), out.pointer());
frame->copyFrom(out);
}
resizer_->enqueue(std::move(frame));
resizer_->enqueue(resampler_->resample(std::move(frame), format_));
break;
case MediaDecoder::Status::Success:
default:
......@@ -298,9 +275,7 @@ AudioInput::setFormat(const AudioFormat& fmt)
{
std::lock_guard<std::mutex> lk(fmtMutex_);
format_ = fmt;
frameSize_ = format_.sample_rate * MS_PER_PACKET.count() / 1000;
resizer_.reset(new AudioFrameResizer(format_, frameSize_,
[this](std::shared_ptr<AudioFrame>&& f){ frameResized(std::move(f)); }));
resizer_->setFormat(format_, format_.sample_rate * MS_PER_PACKET.count() / 1000);
}
void
......
......@@ -93,12 +93,13 @@ void
AudioReceiveThread::process()
{
AudioFormat mainBuffFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
auto decodedFrame = std::make_shared<AudioFrame>();
auto decodedFrame = std::make_unique<AudioFrame>();
auto sharedFrame = std::make_shared<AudioFrame>();
switch (audioDecoder_->decode(*decodedFrame)) {
case MediaDecoder::Status::FrameFinished:
audioDecoder_->writeToRingBuffer(*decodedFrame, *ringbuffer_,
mainBuffFormat);
notify(std::static_pointer_cast<MediaFrame>(decodedFrame));
sharedFrame->copyFrom(*decodedFrame);
audioDecoder_->writeToRingBuffer(std::move(decodedFrame), *ringbuffer_, mainBuffFormat);
notify(std::static_pointer_cast<MediaFrame>(sharedFrame));
return;
case MediaDecoder::Status::DecodeError:
RING_WARN("decoding failure, trying to reset decoder...");
......
......@@ -48,8 +48,6 @@
namespace ring {
constexpr static auto NEWPARAMS_TIMEOUT = std::chrono::milliseconds(1000);
AudioRtpSession::AudioRtpSession(const std::string& id)
: RtpSession(id)
{
......
......@@ -96,10 +96,10 @@ struct AudioFormat {
}
static const constexpr unsigned DEFAULT_SAMPLE_RATE = 48000;
static const AudioFormat DEFAULT() { return AudioFormat{16000, 1}; }
static const AudioFormat NONE() { return AudioFormat{0, 0}; }
static const AudioFormat MONO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 1}; }
static const AudioFormat STEREO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 2}; }
static const constexpr AudioFormat DEFAULT() { return AudioFormat{16000, 1}; }
static const constexpr AudioFormat NONE() { return AudioFormat{0, 0}; }
static const constexpr AudioFormat MONO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 1}; }
static const constexpr AudioFormat STEREO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 2}; }
};
std::ostream& operator <<(std::ostream& stream, const AudioFormat& f);
......
......@@ -56,7 +56,7 @@ void AudioLayer::hardwareFormatAvailable(AudioFormat playback)
std::lock_guard<std::mutex> lock(mutex_);
RING_DBG("Hardware audio format available : %s", playback.toString().c_str());
audioFormat_ = Manager::instance().hardwareAudioFormatChanged(playback);
urgentRingBuffer_.setFormat(audioFormat_);
urgentRingBuffer_.setFormat(playback);
}
void AudioLayer::hardwareInputFormatAvailable(AudioFormat capture)
......@@ -85,7 +85,7 @@ void AudioLayer::flushUrgent()
void AudioLayer::putUrgent(AudioBuffer& buffer)
{
std::lock_guard<std::mutex> lock(mutex_);
urgentRingBuffer_.put(buffer);
urgentRingBuffer_.put(buffer.toAVFrame());
}
// Notify (with a beep) an incoming call when there is already a call in progress
......@@ -107,7 +107,7 @@ void AudioLayer::notifyIncomingCall()
return;
Tone tone("440/160", getSampleRate());
unsigned int nbSample = tone.getSize();
size_t nbSample = tone.getSize();
AudioBuffer buf(nbSample, AudioFormat::MONO());
tone.getNext(buf, 1.0);
......@@ -116,99 +116,56 @@ void AudioLayer::notifyIncomingCall()
putUrgent(buf);
}
const AudioBuffer& AudioLayer::getToRing(AudioFormat format, size_t writableSamples)
std::shared_ptr<AudioFrame>
AudioLayer::getToRing(AudioFormat format, size_t writableSamples)
{
ringtoneBuffer_.resize(0);
auto fileToPlay = Manager::instance().getTelephoneFile();
if (fileToPlay) {
if (auto fileToPlay = Manager::instance().getTelephoneFile()) {
auto fileformat = fileToPlay->getFormat();
bool resample = format.sample_rate != fileformat.sample_rate;
bool resample = format != fileformat;
size_t readableSamples = resample
? fileformat.sample_rate * (double) writableSamples / (double) audioFormat_.sample_rate
? (rational<size_t>(writableSamples, audioFormat_.sample_rate) * (size_t)fileformat.sample_rate).real<size_t>()
: writableSamples;
ringtoneBuffer_.setFormat(fileformat);
ringtoneBuffer_.resize(readableSamples);
fileToPlay->getNext(ringtoneBuffer_, isRingtoneMuted_ ? 0. : 1.);
ringtoneBuffer_.setChannelNum(format.nb_channels, true);
AudioBuffer* out;
if (resample) {
ringtoneResampleBuffer_.setSampleRate(format.sample_rate);
resampler_->resample(ringtoneBuffer_, ringtoneResampleBuffer_);
out = &ringtoneResampleBuffer_;
} else {
out = &ringtoneBuffer_;
}
return *out;
return resampler_->resample(ringtoneBuffer_.toAVFrame(), format);
}
return ringtoneBuffer_;
return {};
}
const AudioBuffer& AudioLayer::getToPlay(AudioFormat format, size_t writableSamples)
std::shared_ptr<AudioFrame>
AudioLayer::getToPlay(AudioFormat format, size_t writableSamples)
{
playbackBuffer_.resize(0);
playbackResampleBuffer_.resize(0);
notifyIncomingCall();
auto& bufferPool = Manager::instance().getRingBufferPool();
size_t urgentSamples = std::min(urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID), writableSamples);
if (urgentSamples) {
playbackBuffer_.setFormat(format);
playbackBuffer_.resize(urgentSamples);
urgentRingBuffer_.get(playbackBuffer_, RingBufferPool::DEFAULT_ID); // retrive only the first sample_spec->channels channels
playbackBuffer_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
// Consume the regular one as well (same amount of samples)
Manager::instance().getRingBufferPool().discard(urgentSamples, RingBufferPool::DEFAULT_ID);
return playbackBuffer_;
}
if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
playbackBuffer_.setFormat(format);
playbackBuffer_.resize(writableSamples);
toneToPlay->getNext(playbackBuffer_, playbackGain_); // retrive only n_channels
return playbackBuffer_;
if (auto urgentSamples = urgentRingBuffer_.get(RingBufferPool::DEFAULT_ID)) {
bufferPool.discard(1, RingBufferPool::DEFAULT_ID);
return urgentSamples;
}
// flush remaining samples in _urgentRingBuffer
flushUrgent();
flushUrgent(); // flush remaining samples in _urgentRingBuffer
size_t availSamples = Manager::instance().getRingBufferPool().availableForGet(RingBufferPool::DEFAULT_ID);
if (not availSamples)
return playbackBuffer_;
// how many samples we want to read from the buffer
size_t readableSamples = writableSamples;
AudioFormat mainBufferAudioFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
bool resample = audioFormat_.sample_rate != mainBufferAudioFormat.sample_rate;
double resampleFactor = 1.;
if (resample) {
resampleFactor = (double) audioFormat_.sample_rate / mainBufferAudioFormat.sample_rate;
readableSamples = (double) readableSamples / resampleFactor;
if (not playbackQueue_)
playbackQueue_.reset(new AudioFrameResizer(format, writableSamples));
else
playbackQueue_->setFrameSize(writableSamples);
std::shared_ptr<AudioFrame> playbackBuf {};
while (!(playbackBuf = playbackQueue_->dequeue())) {
if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
playbackQueue_->enqueue(resampler_->resample(toneToPlay->getNext(), format));
} else if (auto buf = bufferPool.getData(RingBufferPool::DEFAULT_ID)) {
playbackQueue_->enqueue(resampler_->resample(std::move(buf), format));
} else {
break;
}
}
readableSamples = std::min(readableSamples, availSamples);
size_t nResampled = (double) readableSamples * resampleFactor;
playbackBuffer_.setFormat(mainBufferAudioFormat);
playbackBuffer_.resize(readableSamples);
Manager::instance().getRingBufferPool().getData(playbackBuffer_, RingBufferPool::DEFAULT_ID);
playbackBuffer_.setChannelNum(format.nb_channels, true);
if (resample) {
playbackResampleBuffer_.setFormat(format);
playbackResampleBuffer_.resize(nResampled);
resampler_->resample(playbackBuffer_, playbackResampleBuffer_);
playbackResampleBuffer_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
return playbackResampleBuffer_;
} else {
playbackBuffer_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
return playbackBuffer_;
}
return playbackBuf;
}
} // namespace ring
......@@ -28,6 +28,7 @@
#include "ringbuffer.h"
#include "dcblocker.h"
#include "noncopyable.h"
#include "audio_frame_resizer.h"
#include <chrono>
#include <mutex>
......@@ -228,9 +229,15 @@ class AudioLayer {
void devicesChanged();
const AudioBuffer& getToPlay(AudioFormat format, size_t writableSamples);
std::shared_ptr<AudioFrame> getToPlay(AudioFormat format, size_t writableSamples);
const AudioBuffer& getToRing(AudioFormat format, size_t writableSamples);
std::shared_ptr<AudioFrame> getToRing(AudioFormat format, size_t writableSamples);
std::shared_ptr<AudioFrame> getPlayback(AudioFormat format, size_t samples) {
const auto& ringBuff = getToRing(format, samples);
const auto& playBuff = getToPlay(format, samples);
return ringBuff ? ringBuff : playBuff;
}
/**
* True if capture is not to be used
......@@ -264,6 +271,7 @@ class AudioLayer {
AudioBuffer playbackResampleBuffer_;
AudioBuffer ringtoneBuffer_;
AudioBuffer ringtoneResampleBuffer_;
std::unique_ptr<AudioFrameResizer> playbackQueue_;
/**
* Whether or not the audio layer stream is started
......
......@@ -71,22 +71,28 @@ AudioLoop::getNext(AudioBuffer& output, double gain)
while (total_samples != 0) {
size_t samples = std::min(total_samples, buf_samples - pos);
output.copy(*buffer_, samples, pos, output_pos);
output_pos += samples;
pos = (pos + samples) % buf_samples;
total_samples -= samples;
}
output.applyGain(gain);
pos_ = pos;
onBufferFinish();
}
void AudioLoop::onBufferFinish() {}
std::unique_ptr<AudioFrame>
AudioLoop::getNext(size_t samples)
{
if (samples == 0) {
samples = buffer_->getSampleRate() / 50;
}
AudioBuffer buff(samples, buffer_->getFormat());
getNext(buff, 1);
return buff.toAVFrame();
}
} // namespace ring
......@@ -56,6 +56,7 @@ class AudioLoop {
* @param gain The gain [-1.0, 1.0]
*/
void getNext(AudioBuffer& output, double gain);
std::unique_ptr<AudioFrame> getNext(size_t samples = 0);
void seek(double relative_position);
......
......@@ -156,9 +156,7 @@ CoreLayer::setupOutputBus() {
&size,
&outSampleRate);
outputASBD.mSampleRate = outSampleRate;
audioFormat_ = {static_cast<unsigned int>(outputASBD.mSampleRate),
static_cast<unsigned int>(outputASBD.mChannelsPerFrame)};
outSampleRate_ = outputASBD.mSampleRate;
size = sizeof(outputASBD);
checkErr(AudioUnitGetProperty(ioUnit_,
......@@ -169,7 +167,7 @@ CoreLayer::setupOutputBus() {
&size));
// Only change sample rate.
outputASBD.mSampleRate = audioFormat_.sample_rate;
outputASBD.mSampleRate = outSampleRate_;
outputASBD.mFormatID = kAudioFormatLinearPCM;
outputASBD.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
......@@ -184,7 +182,8 @@ CoreLayer::setupOutputBus() {
&outputASBD,
size));
hardwareFormatAvailable(audioFormat_);
hardwareFormatAvailable({static_cast<unsigned int>(outputASBD.mSampleRate),
static_cast<unsigned int>(outputASBD.mChannelsPerFrame)});
}
void
......@@ -223,13 +222,6 @@ CoreLayer::setupInputBus() {
inputASBD.mFormatID = kAudioFormatLinearPCM;
inputASBD.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
audioInputFormat_ = {static_cast<unsigned int>(inputASBD.mSampleRate),
static_cast<unsigned int>(inputASBD.mChannelsPerFrame)};
hardwareInputFormatAvailable(audioInputFormat_);
// Keep some values to not ask them every time the read callback is fired up
inSampleRate_ = inputASBD.mSampleRate;
inChannelsPerFrame_ = inputASBD.mChannelsPerFrame;
// Set format on output *SCOPE* in input *BUS*.
checkErr(AudioUnitGetProperty(ioUnit_,
......@@ -239,8 +231,13 @@ CoreLayer::setupInputBus() {
&inputASBD,
&size));
// Keep everything else and change only sample rate (or else SPLOSION!!!)
inputASBD.mSampleRate = audioInputFormat_.sample_rate;
audioInputFormat_ = {static_cast<unsigned int>(inputASBD.mSampleRate),
static_cast<unsigned int>(inputASBD.mChannelsPerFrame)};
hardwareInputFormatAvailable(audioInputFormat_);
// Keep some values to not ask them every time the read callback is fired up
inSampleRate_ = inputASBD.mSampleRate;
inChannelsPerFrame_ = inputASBD.mChannelsPerFrame;
size = sizeof(inputASBD);
checkErr(AudioUnitSetProperty(ioUnit_,
......@@ -265,7 +262,7 @@ CoreLayer::setupInputBus() {
AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
&size,
&bufferDuration);
UInt32 bufferSizeFrames = audioInputFormat_.sample_rate * bufferDuration;
UInt32 bufferSizeFrames = inSampleRate_ * bufferDuration;
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
size = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * inputASBD.mChannelsPerFrame);
rawBuff_.reset(new Byte[size + bufferSizeBytes * inputASBD.mChannelsPerFrame]);
......@@ -380,73 +377,14 @@ CoreLayer::write(AudioUnitRenderActionFlags* ioActionFlags,
(void) inTimeStamp;
(void) inBusNumber;
auto& manager = Manager::instance();
auto& bufferPool = manager.getRingBufferPool();
auto mainBufferFormat = bufferPool.getInternalAudioFormat();
const AudioFormat currentOutFormat = { static_cast<unsigned int>(outSampleRate_),
static_cast<unsigned int>(outChannelsPerFrame_)};
auto resample = currentOutFormat.sample_rate != mainBufferFormat.sample_rate;
auto normalFramesToGet = bufferPool.availableForGet(RingBufferPool::DEFAULT_ID);
auto urgentFramesToGet = urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
double resampleFactor;
decltype(normalFramesToGet) readableSamples;
decltype(urgentFramesToGet) readableUrgentSamples;
AudioFormat currentOutFormat { static_cast<unsigned>(outSampleRate_),
static_cast<unsigned>(outChannelsPerFrame_),
AV_SAMPLE_FMT_FLTP};
if (resample) {
resampleFactor = mainBufferFormat.sample_rate / static_cast<double>(currentOutFormat.sample_rate);
readableSamples = std::ceil(inNumberFrames * resampleFactor);
} else {
readableSamples = inNumberFrames;
}
// incoming call during call
if (urgentFramesToGet > 0) {
readableUrgentSamples = std::min(readableSamples, urgentFramesToGet);
playbackBuff_.setFormat(currentOutFormat);
playbackBuff_.resize(readableUrgentSamples);
urgentRingBuffer_.get(playbackBuff_, RingBufferPool::DEFAULT_ID);
playbackBuff_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
for (unsigned i = 0; i < currentOutFormat.nb_channels; ++i) {
playbackBuff_.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
}
manager.getRingBufferPool().discard(readableUrgentSamples, RingBufferPool::DEFAULT_ID);
}
if (normalFramesToGet > 0) {
readableSamples = std::min(readableSamples, normalFramesToGet);
}
auto& ringBuff = getToRing(audioFormat_, readableSamples);
auto& playBuff = getToPlay(audioFormat_, readableSamples);
auto& toPlay = ringBuff.frames() > 0 ? ringBuff : playBuff;
if (toPlay.frames() == 0) {
// clear buffer
for (unsigned i = 0; i < currentOutFormat.nb_channels; ++i) {
std::fill_n(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData),
ioData->mBuffers[i].mDataByteSize / sizeof(Float32), 0);
}
} else if (resample) {
// resample
playbackBuff_.setFormat(currentOutFormat);
playbackBuff_.resize(readableSamples);
resampler_->resample(toPlay, playbackBuff_);
playbackBuff_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
for (unsigned i = 0; i < audioFormat_.nb_channels; ++i) {
playbackBuff_.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
}
} else {
// normal play
const_cast<AudioBuffer&>(toPlay).applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
for (unsigned i = 0; i < audioFormat_.nb_channels; ++i) {
toPlay.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
if (auto toPlay = getPlayback(currentOutFormat, inNumberFrames)) {
const auto& frame = *toPlay->pointer();
for (unsigned i = 0; i < frame.channels; ++i) {
std::copy_n((Float32*)frame.extended_data[i], inNumberFrames, (Float32*)ioData->mBuffers[i].mData);
}
}
}
......@@ -485,31 +423,13 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
inNumberFrames,
captureBuff_));
// Add them to Ring ringbuffer.
const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
bool resample = inSampleRate_ != mainBufferFormat.sample_rate;
// FIXME: Performance! There *must* be a better way. This is testing only.
auto inBuff = AudioBuffer {inNumberFrames, audioInputFormat_};
for (unsigned i = 0; i < inChannelsPerFrame_; ++i) {
auto data = reinterpret_cast<Float32*>(captureBuff_->mBuffers[i].mData);
for (unsigned j = 0; j < inNumberFrames; ++j) {
(*inBuff.getChannel(i))[j] = static_cast<AudioSample>(data[j] * 32768);
}
}
if (resample) {
//FIXME: May be a multiplication, check alsa vs pulse implementation.
UInt32 outSamples = inNumberFrames * (mainBufferFormat.sample_rate / static_cast<double>(audioInputFormat_.sample_rate));
auto out = AudioBuffer {outSamples, mainBufferFormat};
inputResampler_->resample(inBuff, out);
dcblocker_.process(out);
mainRingBuffer_->put(out);
} else {
dcblocker_.process(inBuff);
mainRingBuffer_->put(inBuff);
}
auto format = audioInputFormat_;
format.sampleFormat = AV_SAMPLE_FMT_FLTP;
auto inBuff = std::make_unique<AudioFrame>(audioInputFormat_, inNumberFrames);
auto& in = *inBuff->pointer();
for (unsigned i = 0; i < inChannelsPerFrame_; ++i)
std::copy_n((Float32*)captureBuff_->mBuffers[i].mData, inNumberFrames, (Float32*)in.extended_data[i]);
mainRingBuffer_->put(std::move(inBuff));
}
void CoreLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
......
......@@ -91,7 +91,6 @@ CoreLayer::initAudioLayerIO()
AudioUnitScope outputBus = 0;
AudioUnitScope inputBus = 1;
UInt32 size = sizeof(UInt32);
AudioComponentDescription desc = {0};
desc.componentType = kAudioUnitType_Output;
// kAudioOutputUnitProperty_EnableIO is ON and read-only
......@@ -109,7 +108,7 @@ CoreLayer::initAudioLayerIO()
// Set stream format
AudioStreamBasicDescription info;
size = sizeof(info);
UInt32 size = sizeof(info);
checkErr(AudioUnitGetProperty(ioUnit_,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
......@@ -291,18 +290,15 @@ CoreLayer::write(AudioUnitRenderActionFlags* ioActionFlags,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
auto& ringBuff = getToRing(audioFormat_, inNumberFrames);
auto& playBuff = getToPlay(audioFormat_, inNumberFrames);
auto& toPlay = ringBuff.frames() > 0 ? ringBuff : playBuff;
if (toPlay.frames() == 0) {
for (int i = 0; i < audioFormat_.nb_channels; ++i)
std::fill_n(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData),
ioData->mBuffers[i].mDataByteSize/sizeof(Float32), 0);
auto format = audioFormat_;
format.sampleFormat = AV_SAMPLE_FMT_FLTP;
if (auto toPlay = getPlayback(format, inNumberFrames)) {
for (int i = 0; i < format.nb_channels; ++i) {
std::copy_n((Float32*)toPlay->pointer()->extended_data[i], inNumberFrames, (Float32*)ioData->mBuffers[i].mData);
}
} else {
for (int i = 0; i < audioFormat_.nb_channels; ++i)
toPlay.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
for (int i = 0; i < format.nb_channels; ++i)
std::fill_n(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), inNumberFrames, 0);
}
}
......@@ -339,34 +335,13 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
inNumberFrames,
captureBuff_));
// Add them to Ring ringbuffer.
const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
bool resample = inSampleRate_ != mainBufferFormat.sample_rate;
// FIXME: Performance! There *must* be a better way. This is testing only.
auto inBuff = AudioBuffer {inNumberFrames, audioInputFormat_};
for (int i = 0; i < inChannelsPerFrame_; ++i) {
auto data = reinterpret_cast<Float32*>(captureBuff_->mBuffers[i].mData);
for (int j = 0; j < inNumberFrames; ++j) {
(*inBuff.getChannel(i))[j] = static_cast<AudioSample>(data[j] / .000030517578125f);
}
}
if (resample) {
//RING_WARN("Resampling Input.");
//FIXME: May be a multiplication, check alsa vs pulse implementation.
UInt32 outSamples = inNumberFrames / (static_cast<double>(audioInputFormat_.sample_rate) / mainBufferFormat.sample_rate);
auto out = AudioBuffer {outSamples, mainBufferFormat};
inputResampler_->resample(inBuff, out);
dcblocker_.process(out);
mainRingBuffer_->put(out);
} else {
dcblocker_.process(inBuff);
mainRingBuffer_->put(inBuff);
}
auto format = audioInputFormat_;
audioInputFormat_.sampleFormat = AV_SAMPLE_FMT_FLTP;
auto inBuff = std::make_unique<AudioFrame>(audioInputFormat_, inNumberFrames);
auto& in = *inBuff->pointer();
for (unsigned i = 0; i < inChannelsPerFrame_; ++i)
std::copy_n((Float32*)captureBuff_->mBuffers[i].mData, inNumberFrames, (Float32*)in.extended_data[i]);
mainRingBuffer_->put(std::move(inBuff));
}
void CoreLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
......
......@@ -23,8 +23,7 @@
#endif
#include "jacklayer.h"
#include <cassert>
#include <climits>
#include "logger.h"
#include "audio/resampler.h"
#include "audio/ringbufferpool.h"
......@@ -35,6 +34,9 @@
#include <unistd.h>
#include <cassert>
#include <climits>
/* TODO
* implement shutdown callback
* auto connect optional
......@@ -48,6 +50,8 @@ void connectPorts(jack_client_t *client, int portType, const std::vector<jack_po
{
const char **physical_ports = jack_get_ports(client, NULL, NULL, portType | JackPortIsPhysical);
for (unsigned i = 0; physical_ports[i]; ++i) {
if (i >= ports.size())
break;
const char *port = jack_port_name(ports[i]);
if (portType & JackPortIsInput) {
if (jack_connect(client, port, physical_ports[i])) {
......@@ -61,7 +65,7 @@ void connectPorts(jack_client_t *client, int portType, const std::vector<jack_po
}
}
}
free(physical_ports);
jack_free(physical_ports);
}
bool ringbuffer_ready_for_read(const jack_ringbuffer_t *rb)
......@@ -71,167 +75,68 @@ bool ringbuffer_ready_for_read(const jack_ringbuffer_t *rb)
}
}
void JackLayer::fillWithUrgent(AudioBuffer &buffer, size_t samplesToGet)
{
// Urgent data (dtmf, incoming call signal) come first.
samplesToGet = std::min(samplesToGet, hardwareBufferSize_);
buffer.resize(samplesToGet);
urgentRingBuffer_.get(buffer, RingBufferPool::DEFAULT_ID);
buffer.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
// Consume the regular one as well (same amount of samples)
Manager::instance().getRingBufferPool().discard(samplesToGet, RingBufferPool::DEFAULT_ID);
}
void JackLayer::fillWithVoice(AudioBuffer &buffer, size_t samplesAvail)
{
RingBufferPool &mainBuffer = Manager::instance().getRingBufferPool();
buffer.resize(samplesAvail);
mainBuffer.getData(buffer, RingBufferPool::DEFAULT_ID);
buffer.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
if (audioFormat_.sample_rate != (unsigned) mainBuffer.getInternalSamplingRate()) {
RING_DBG("fillWithVoice sample_rate != mainBuffer.getInternalSamplingRate() \n");
AudioBuffer out(buffer, false);
out.setSampleRate(audioFormat_.sample_rate);
resampler_->resample(buffer, out);
buffer = out;
}
}
void JackLayer::fillWithToneOrRingtone(AudioBuffer &buffer)
{
buffer.resize(hardwareBufferSize_);
auto tone = Manager::instance().getTelephoneTone();
auto file_tone = Manager::instance().getTelephoneFile();
// In case of a dtmf, the pointers will be set to nullptr once the dtmf length is
// reached. For this reason we need to fill audio buffer with zeros if pointer is nullptr
if (tone) {
tone->getNext(buffer, playbackGain_);
} else if (file_tone) {
file_tone->getNext(buffer, playbackGain_);
} else {
buffer.reset();
}
}
void
JackLayer::playback()
{
notifyIncomingCall();
const size_t samplesToGet = Manager::instance().getRingBufferPool().availableForGet(RingBufferPool::DEFAULT_ID);
const size_t urgentSamplesToGet = urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
if (urgentSamplesToGet > 0) {
fillWithUrgent(playbackBuffer_, urgentSamplesToGet);
} else {
if (samplesToGet > 0) {
fillWithVoice(playbackBuffer_, samplesToGet);
} else {
fillWithToneOrRingtone(playbackBuffer_);
}
auto format = audioFormat_;
format.sampleFormat = AV_SAMPLE_FMT_FLTP;
if (auto toPlay = getPlayback(format, writeSpace())) {
write(*toPlay);
}
playbackFloatBuffer_.resize(playbackBuffer_.frames());
write(playbackBuffer_, playbackFloatBuffer_);
}
void
JackLayer::capture()
{
// get audio from jack ringbuffer
read(captureBuffer_);
const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
const bool resample = mainBufferFormat.sample_rate != audioFormat_.sample_rate;
captureBuffer_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
if (resample) {
int outSamples = captureBuffer_.frames() * (static_cast<double>(audioFormat_.sample_rate) / mainBufferFormat.sample_rate);
AudioBuffer out(outSamples, mainBufferFormat);
resampler_->resample(captureBuffer_, out);
dcblocker_.process(out);
mainRingBuffer_->put(out);
} else {
dcblocker_.process(captureBuffer_);
mainRingBuffer_->put(captureBuffer_);
}
}
static void
convertToFloat(const std::vector<AudioSample> &src, std::vector<float> &dest)
{
static const float INV_SHORT_MAX = 1 / (float) SHRT_MAX;
if (dest.size() != src.size()) {
RING_ERR("MISMATCH");
return;
}
for (size_t i = 0; i < dest.size(); ++i)
dest[i] = src[i] * INV_SHORT_MAX;
if (auto buf = read())
mainRingBuffer_->put(std::move(buf));
}
static void
convertFromFloat(std::vector<float> &src, std::vector<AudioSample> &dest)
size_t
JackLayer::writeSpace()
{
if (dest.size() != src.size()) {
RING_ERR("MISMATCH");
return;
if (out_ringbuffers_.empty())
return 0;
size_t toWrite {std::numeric_limits<size_t>::max()};
for (unsigned i = 0; i < out_ringbuffers_.size(); ++i) {
toWrite = std::min(toWrite, jack_ringbuffer_write_space(out_ringbuffers_[i]));
}
for (size_t i = 0; i < dest.size(); ++i)
dest[i] = src[i] * SHRT_MAX;
return std::min<size_t>(toWrite / sizeof(float), audioFormat_.sample_rate / 25);
}
void
JackLayer::write(AudioBuffer &buffer, std::vector<float> &floatBuffer)
JackLayer::write(const AudioFrame& buffer)
{
for (unsigned i = 0; i < out_ringbuffers_.size(); ++i) {
const unsigned inChannel = std::min(i, buffer.channels() - 1);
convertToFloat(*buffer.getChannel(inChannel), floatBuffer);
// write to output
const size_t to_ringbuffer = jack_ringbuffer_write_space(out_ringbuffers_[i]);
const size_t write_bytes = std::min(buffer.frames() * sizeof(floatBuffer[0]), to_ringbuffer);
// FIXME: while we have samples to write AND while we have space to write them
const size_t written_bytes = jack_ringbuffer_write(out_ringbuffers_[i],
(const char *) floatBuffer.data(), write_bytes);
if (written_bytes < write_bytes)
RING_WARN("Dropped %zu bytes for channel %u", write_bytes - written_bytes, i);
auto num_samples = buffer.pointer()->nb_samples;
auto num_bytes = num_samples * sizeof(float);
auto channels = std::min<size_t>(out_ringbuffers_.size(), buffer.pointer()->channels);
for (size_t i = 0; i < channels; ++i) {
jack_ringbuffer_write(out_ringbuffers_[i], (const char*)buffer.pointer()->extended_data[i], num_bytes);
}
}
void
JackLayer::read(AudioBuffer &buffer)
std::unique_ptr<AudioFrame>
JackLayer::read()
{
for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
const size_t incomingSamples = jack_ringbuffer_read_space(in_ringbuffers_[i]) / sizeof(captureFloatBuffer_[0]);
if (!incomingSamples)
continue;
if (in_ringbuffers_.empty())
return {};
captureFloatBuffer_.resize(incomingSamples);
buffer.resize(incomingSamples);
size_t toRead {std::numeric_limits<size_t>::max()};
for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
toRead = std::min(toRead, jack_ringbuffer_read_space(in_ringbuffers_[i]));
}
if (not toRead)
return {};
// write to output
const size_t from_ringbuffer = jack_ringbuffer_read_space(in_ringbuffers_[i]);
const size_t expected_bytes = std::min(incomingSamples * sizeof(captureFloatBuffer_[0]), from_ringbuffer);
// FIXME: while we have samples to write AND while we have space to write them
const size_t read_bytes = jack_ringbuffer_read(in_ringbuffers_[i],
(char *) captureFloatBuffer_.data(), expected_bytes);
if (read_bytes < expected_bytes) {
RING_WARN("Dropped %zu bytes", expected_bytes - read_bytes);
break;
}
auto format = audioInputFormat_;
format.sampleFormat = AV_SAMPLE_FMT_FLTP;
auto buffer = std::make_unique<AudioFrame>(format, toRead / sizeof(jack_default_audio_sample_t));
/* Write the data one frame at a time. This is
* inefficient, but makes things simpler. */
// FIXME: this is braindead, we should write blocks of samples at a time
// convert a vector of samples from 1 channel to a float vector
convertFromFloat(captureFloatBuffer_, *buffer.getChannel(i));
for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
jack_ringbuffer_read(in_ringbuffers_[i], (char *) buffer->pointer()->extended_data[i], toRead);
}
return buffer;
}
/* This thread can lock, do whatever it wants, and read from/write to the jack
......@@ -262,8 +167,6 @@ JackLayer::ringbuffer_worker()
// is rather arbitrary. We should wait until ring has/needs data
// and jack has/needs data.
data_ready_.wait(lock, [&] {
// Note: lock is released while waiting, and held when woken
// up, so this predicate is called while holding the lock
return status_ != Status::Started
or ringbuffer_ready_for_read(in_ringbuffers_[0]);
});
......@@ -274,10 +177,11 @@ void
createPorts(jack_client_t *client, std::vector<jack_port_t *> &ports,
bool playback, std::vector<jack_ringbuffer_t *> &ringbuffers)
{
const char **physical_ports = jack_get_ports(client, NULL, NULL,
playback ? JackPortIsInput : JackPortIsOutput | JackPortIsPhysical);
for (unsigned i = 0; physical_ports[i]; ++i) {
if (i == 2)
break;
char port_name[32] = {0};
if (playback)
snprintf(port_name, sizeof(port_name), "out_%d", i + 1);
......@@ -298,7 +202,7 @@ createPorts(jack_client_t *client, std::vector<jack_port_t *> &ports,
throw std::runtime_error("Could not lock JACK ringbuffer in memory");
ringbuffers.push_back(rb);
}
free(physical_ports);
jack_free(physical_ports);
}
......@@ -306,19 +210,6 @@ JackLayer::JackLayer(const AudioPreference &p) :
AudioLayer(p),
captureClient_(nullptr),
playbackClient_(nullptr),
out_ports_(),
in_ports_(),
out_ringbuffers_(),
in_ringbuffers_(),
ringbuffer_thread_(),
//workerAlive_(false),
ringbuffer_thread_mutex_(),
data_ready_(),
playbackBuffer_(0, audioFormat_),
playbackFloatBuffer_(),
captureBuffer_(0, audioFormat_),
captureFloatBuffer_(),
hardwareBufferSize_(0),
mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
{
playbackClient_ = jack_client_open(PACKAGE_NAME,
......@@ -339,20 +230,10 @@ JackLayer::JackLayer(const AudioPreference &p) :
const auto playRate = jack_get_sample_rate(playbackClient_);
const auto captureRate = jack_get_sample_rate(captureClient_);
if (playRate != captureRate)
RING_ERR("Mismatch between capture rate %u and playback rate %u", playRate, captureRate);
hardwareBufferSize_ = jack_get_buffer_size(playbackClient_);
auto update_buffer = [] (AudioBuffer &buf, size_t size, unsigned rate, unsigned nbChannels) {
buf.setSampleRate(rate);
buf.resize(size);
buf.setChannelNum(nbChannels);
};
update_buffer(playbackBuffer_, hardwareBufferSize_, playRate, out_ports_.size());
update_buffer(captureBuffer_, hardwareBufferSize_, captureRate, in_ports_.size());
audioInputFormat_ = {captureRate, in_ringbuffers_.size()};
hardwareFormatAvailable(AudioFormat(playRate, out_ringbuffers_.size()));
hardwareInputFormatAvailable(audioInputFormat_);
jack_on_shutdown(playbackClient_, onShutdown, this);
}
......@@ -476,15 +357,11 @@ JackLayer::startStream()
}
dcblocker_.reset();
const auto hardwareFormat = AudioFormat(playbackBuffer_.getSampleRate(), out_ports_.size());
hardwareFormatAvailable(hardwareFormat);
if (jack_activate(playbackClient_) or jack_activate(captureClient_)) {
RING_ERR("Could not activate JACK client");
return;
}
ringbuffer_thread_ = std::thread(&JackLayer::ringbuffer_worker, this);
connectPorts(playbackClient_, JackPortIsInput, out_ports_);
connectPorts(captureClient_, JackPortIsOutput, in_ports_);
}
......
/*
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
*
* Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
*
* 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 3 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef JACK_LAYER_H_
#define JACK_LAYER_H_
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
*
* Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
* Adrien Beraud <adrien.beraud@savoirfairelinux.com>
*
* 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 3 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "noncopyable.h"
#include "audio/audiolayer.h"
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include <thread>
#include <mutex>
#include <vector>
#include <condition_variable>
#include "noncopyable.h"
#include "audio/audiolayer.h"
#include <memory>
namespace ring {
......@@ -37,69 +38,55 @@ namespace ring {
class RingBuffer;
class JackLayer : public AudioLayer {
private:
NON_COPYABLE(JackLayer);
jack_client_t *captureClient_;
jack_client_t *playbackClient_;
std::vector<jack_port_t *> out_ports_;
std::vector<jack_port_t *> in_ports_;
std::vector<jack_ringbuffer_t *> out_ringbuffers_;
std::vector<jack_ringbuffer_t *> in_ringbuffers_;
std::thread ringbuffer_thread_;
std::mutex ringbuffer_thread_mutex_;
std::condition_variable data_ready_;
AudioBuffer playbackBuffer_;
std::vector<float> playbackFloatBuffer_;
AudioBuffer captureBuffer_;
std::vector<float> captureFloatBuffer_;
size_t hardwareBufferSize_;
std::shared_ptr<RingBuffer> mainRingBuffer_;
static int process_capture(jack_nframes_t frames, void *arg);
static int process_playback(jack_nframes_t frames, void *arg);
// separate thread
void ringbuffer_worker();
// called from ringbuffer_worker()
void playback();
void capture();
void fillWithUrgent(AudioBuffer &buffer, size_t samplesToGet);
void fillWithVoice(AudioBuffer &buffer, size_t samplesAvail);
void fillWithToneOrRingtone(AudioBuffer &buffer);
void read(AudioBuffer &buffer);
void write(AudioBuffer &buffer, std::vector<float> &floatBuffer);
std::vector<std::string> getCaptureDeviceList() const;
std::vector<std::string> getPlaybackDeviceList() const;
int getAudioDeviceIndex(const std::string& name, DeviceType type) const;
std::string getAudioDeviceName(int index, DeviceType type) const;
int getIndexCapture() const;
int getIndexPlayback() const;
int getIndexRingtone() const;
void updatePreference(AudioPreference &pref, int index, DeviceType type);
/**
* Start the capture and playback.
*/
void startStream();
/**
* Stop playback and capture.
*/
void stopStream();
static void onShutdown(void *data);
public:
JackLayer(const AudioPreference &);
~JackLayer();
private:
NON_COPYABLE(JackLayer);
jack_client_t *captureClient_;
jack_client_t *playbackClient_;
std::vector<jack_port_t *> out_ports_;
std::vector<jack_port_t *> in_ports_;
std::vector<jack_ringbuffer_t *> out_ringbuffers_;
std::vector<jack_ringbuffer_t *> in_ringbuffers_;
std::thread ringbuffer_thread_;
std::mutex ringbuffer_thread_mutex_;
std::condition_variable data_ready_;
std::shared_ptr<RingBuffer> mainRingBuffer_;
static int process_capture(jack_nframes_t frames, void *arg);
static int process_playback(jack_nframes_t frames, void *arg);
void ringbuffer_worker();
void playback();
void capture();
std::unique_ptr<AudioFrame> read();
void write(const AudioFrame& buffer);
size_t writeSpace();
std::vector<std::string> getCaptureDeviceList() const;
std::vector<std::string> getPlaybackDeviceList() const;
int getAudioDeviceIndex(const std::string& name, DeviceType type) const;
std::string getAudioDeviceName(int index, DeviceType type) const;
int getIndexCapture() const;
int getIndexPlayback() const;
int getIndexRingtone() const;
void updatePreference(AudioPreference &pref, int index, DeviceType type);
/**
* Start the capture and playback.
*/
void startStream();
/**
* Stop playback and capture.
*/
void stopStream();
static void onShutdown(void *data);
public:
JackLayer(const AudioPreference &);
~JackLayer();
};
}
#endif // JACK_LAYER_H_
......@@ -215,9 +215,9 @@ OpenSLLayer::engineServicePlay(bool waiting) {
}
sample_buf* buf;
while (player_ and freePlayBufQueue_.front(&buf)) {
const AudioBuffer& dat = getToPlay(hardwareFormat_, hardwareBuffSize_);
if (dat.frames() != 0) {
buf->size_ = dat.interleave((AudioSample*)buf->buf_) * sizeof(AudioSample);
if (auto dat = getToPlay(hardwareFormat_, hardwareBuffSize_)) {
buf->size_ = dat->pointer()->nb_samples * dat->pointer()->channels * sizeof(AudioSample);
std::copy_n((const AudioSample*)dat->pointer()->data[0], dat->pointer()->nb_samples, (AudioSample*)buf->buf_);
if (!playBufQueue_.push(buf)) {
RING_WARN("playThread player_ PLAY_KICKSTART_BUFFER_COUNT 1");
break;
......@@ -237,9 +237,9 @@ OpenSLLayer::engineServiceRing(bool waiting) {
sample_buf* buf;
while (ringtone_ and freeRingBufQueue_.front(&buf)) {
freeRingBufQueue_.pop();
const AudioBuffer& dat = getToRing(hardwareFormat_, hardwareBuffSize_);
if (dat.frames() != 0) {
buf->size_ = dat.interleave((AudioSample*)buf->buf_) * sizeof(AudioSample);
if (auto dat = getToRing(hardwareFormat_, hardwareBuffSize_)) {
buf->size_ = dat->pointer()->nb_samples * dat->pointer()->channels * sizeof(AudioSample);
std::copy_n((const AudioSample*)dat->pointer()->data[0], dat->pointer()->nb_samples, (AudioSample*)buf->buf_);
if (!ringBufQueue_.push(buf)) {
RING_WARN("playThread ringtone_ PLAY_KICKSTART_BUFFER_COUNT 1");
freeRingBufQueue_.push(buf);
......@@ -348,8 +348,10 @@ OpenSLLayer::startAudioCapture()
break;
recBufQueue_.pop();
if (buf->size_ > 0) {
AudioBuffer dat {(const AudioSample*)buf->buf_, buf->size_ / hardwareFormat_.getBytesPerFrame(), hardwareFormat_};
audioCaptureFillBuffer(dat);
auto nb_samples = buf->size_ / hardwareFormat_.getBytesPerFrame();
auto out = std::make_unique<AudioFrame>(hardwareFormat_, nb_samples);
std::copy_n((const AudioSample*)buf->buf_, nb_samples, (AudioSample*)out->pointer()->data[0]);
audioCaptureFillBuffer(std::move(out));
}
buf->size_ = 0;
freeRecBufQueue_.push(buf);
......@@ -474,24 +476,13 @@ void
OpenSLLayer::updatePreference(AudioPreference& /*preference*/, int /*index*/, DeviceType /*type*/)
{}
void OpenSLLayer::audioCaptureFillBuffer(AudioBuffer &buffer)
void OpenSLLayer::audioCaptureFillBuffer(std::unique_ptr<AudioFrame>&& frame)
{
RingBufferPool &mbuffer = Manager::instance().getRingBufferPool();
const AudioFormat mainBufferFormat = mbuffer.getInternalAudioFormat();
const bool resample = mainBufferFormat.sample_rate != audioFormat_.sample_rate;
buffer.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
if (resample) {
int outSamples = buffer.frames() * (static_cast<double>(audioFormat_.sample_rate) / mainBufferFormat.sample_rate);
AudioBuffer out(outSamples, mainBufferFormat);
resampler_->resample(buffer, out);
dcblocker_.process(out);
mainRingBuffer_->put(out);
} else {
dcblocker_.process(buffer);
mainRingBuffer_->put(buffer);
}
// buffer.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
// dcblocker_.process(buffer);
mainRingBuffer_->put(resampler_->resample(std::move(frame), mbuffer.getInternalAudioFormat()));
}
void dumpAvailableEngineInterfaces()
......
......@@ -118,7 +118,7 @@ class OpenSLLayer : public AudioLayer {
void engineServiceRec(bool waiting);
private:
void audioCaptureFillBuffer(AudioBuffer &buffer);
void audioCaptureFillBuffer(std::unique_ptr<AudioFrame>&& buffer);
/**
* Get the index of the audio card for capture
......
......@@ -268,71 +268,13 @@ PortAudioLayer::PortAudioLayerImpl::paOutputCallback(PortAudioLayer& parent,
(void) timeInfo;
(void) statusFlags;
auto& manager = Manager::instance();
auto& buffer_pool = manager.getRingBufferPool();
auto mainbuffer_format = buffer_pool.getInternalAudioFormat();
auto layer_format = parent.audioFormat_;
auto resample = layer_format.sample_rate != mainbuffer_format.sample_rate;
auto urgentFramesToGet = parent.urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
if (urgentFramesToGet > 0) {
RING_WARN("Getting urgent frames");
auto totSample = std::min(framesPerBuffer, (unsigned long)urgentFramesToGet);
playbackBuff_.setFormat(layer_format);
playbackBuff_.resize(totSample);
parent.urgentRingBuffer_.get(playbackBuff_, RingBufferPool::DEFAULT_ID);
playbackBuff_.applyGain(parent.isPlaybackMuted_ ? 0.0 : parent.playbackGain_);
playbackBuff_.interleave(outputBuffer);
manager.getRingBufferPool().discard(totSample, RingBufferPool::DEFAULT_ID);
}
auto normalFramesToGet = buffer_pool.availableForGet(RingBufferPool::DEFAULT_ID);
if (normalFramesToGet > 0) {
double resampleFactor;
decltype(normalFramesToGet) readableSamples;
if (resample) {
resampleFactor = static_cast<double>(layer_format.sample_rate) / mainbuffer_format.sample_rate;
readableSamples = std::ceil(framesPerBuffer / resampleFactor);
} else {
resampleFactor = 1.0;
readableSamples = framesPerBuffer;
}
readableSamples = std::min(readableSamples, normalFramesToGet);
playbackBuff_.setFormat(parent.audioFormat_);
playbackBuff_.resize(readableSamples);
buffer_pool.getData(playbackBuff_, RingBufferPool::DEFAULT_ID);
playbackBuff_.applyGain(parent.isPlaybackMuted_ ? 0.0 : parent.playbackGain_);
if (resample) {
AudioBuffer resampledOutput(readableSamples, parent.audioFormat_);
parent.resampler_->resample(playbackBuff_, resampledOutput);
resampledOutput.interleave(outputBuffer);
} else {
playbackBuff_.interleave(outputBuffer);
}
} else {
auto tone = manager.getTelephoneTone();
auto file_tone = manager.getTelephoneFile();
const auto& ringBuff = parent.getToRing(parent.audioFormat_, framesPerBuffer);
const auto& playBuff = parent.getToPlay(parent.audioFormat_, framesPerBuffer);
auto toPlay = ringBuff ? ringBuff : playBuff;
if (!toPlay)
return paContinue;
playbackBuff_.setFormat(parent.audioFormat_);
playbackBuff_.resize(framesPerBuffer);
if (tone) {
tone->getNext(playbackBuff_, parent.playbackGain_);
} else if (file_tone) {
file_tone->getNext(playbackBuff_, parent.playbackGain_);
} else {
playbackBuff_.reset();
}
playbackBuff_.interleave(outputBuffer);
}
std::copy_n((AudioSample*)toPlay->pointer()->extended_data[0], toPlay->pointer()->nb_samples, outputBuffer);
return paContinue;
}
......@@ -355,25 +297,10 @@ PortAudioLayer::PortAudioLayerImpl::paInputCallback(PortAudioLayer& parent,
return paContinue;
}
const auto mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
bool resample = parent.audioInputFormat_.sample_rate != mainBufferFormat.sample_rate;
AudioBuffer inBuff(framesPerBuffer, parent.audioInputFormat_);
inBuff.deinterleave(inputBuffer, framesPerBuffer, parent.audioInputFormat_.nb_channels);
inBuff.applyGain(parent.isCaptureMuted_ ? 0.0 : parent.captureGain_);
if (resample) {
auto sample_factor = static_cast<double>(parent.audioInputFormat_.sample_rate) / mainBufferFormat.sample_rate;
auto outSamples = framesPerBuffer / sample_factor;
AudioBuffer out(outSamples, mainBufferFormat);
parent.inputResampler_->resample(inBuff, out);
parent.dcblocker_.process(out);
mainRingBuffer_->put(out);
} else {
parent.dcblocker_.process(inBuff);
mainRingBuffer_->put(inBuff);
}
auto inBuff = std::make_unique<AudioFrame>(parent.audioInputFormat_, framesPerBuffer);
std::copy_n(inputBuffer, framesPerBuffer, (AudioSample*)inBuff->pointer()->extended_data[0]);
//inBuff.applyGain(parent.isCaptureMuted_ ? 0.0 : parent.captureGain_);
mainRingBuffer_->put(std::move(inBuff));
return paContinue;
}
......
......@@ -26,7 +26,6 @@
#include "pulselayer.h"
#include "audio/resampler.h"
#include "audio/dcblocker.h"
#include "audio/resampler.h"
#include "audio/ringbufferpool.h"
#include "audio/ringbuffer.h"
#include "logger.h"
......@@ -38,6 +37,7 @@
#include <unistd.h>
#include <cstdlib>
#include <fstream>
#include <cstring>
// Std-C++11 regex feature implemented only since GCC 4.9
// Using pcre library as replacement
......@@ -435,15 +435,15 @@ void PulseLayer::writeToSpeaker()
if (writableBytes == 0)
return;
auto& buff = getToPlay(playback_->format(), writableBytes / playback_->frameSize());
const auto& buff = getToPlay(playback_->format(), writableBytes / playback_->frameSize());
AudioSample* data = nullptr;
pa_stream_begin_write(playback_->stream(), (void**)&data, &writableBytes);
if (buff.frames() == 0)
if (!buff)
memset(data, 0, writableBytes);
else
buff.interleave(data);
std::memcpy(data, buff->pointer()->data[0], buff->pointer()->nb_samples * playback_->frameSize());
pa_stream_write(playback_->stream(), data, writableBytes, nullptr, 0, PA_SEEK_RELATIVE);
}
......@@ -458,37 +458,22 @@ void PulseLayer::readFromMic()
if (pa_stream_peek(record_->stream() , (const void**) &data , &bytes) < 0 or !data)
return;
if (bytes == 0)
return;
size_t sample_size = record_->frameSize();
const AudioFormat format = record_->format();
assert(format.nb_channels);
assert(sample_size);
const size_t samples = bytes / sample_size;
micBuffer_.setFormat(format);
micBuffer_.resize(samples);
micBuffer_.deinterleave((AudioSample*)data, samples, format.nb_channels);
micBuffer_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
auto mainBufferAudioFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
bool resample = format.sample_rate != mainBufferAudioFormat.sample_rate;
AudioBuffer* out;
if (resample) {
micResampleBuffer_.setSampleRate(mainBufferAudioFormat.sample_rate);
resampler_->resample(micBuffer_, micResampleBuffer_);
out = &micResampleBuffer_;
} else {
out = &micBuffer_;
}
dcblocker_.process(*out);
out->applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
mainRingBuffer_->put(*out);
auto out = std::make_unique<AudioFrame>(record_->format(), samples);
std::memcpy(out->pointer()->data[0], data, bytes);
if (pa_stream_drop(record_->stream()) < 0)
RING_ERR("Capture stream drop failed: %s" , pa_strerror(pa_context_errno(context_)));
}
//dcblocker_.process(*out);
//out->applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
mainRingBuffer_->put(std::move(out));
}
void PulseLayer::ringtoneToSpeaker()
{
......@@ -499,15 +484,15 @@ void PulseLayer::ringtoneToSpeaker()
if (bytes == 0)
return;
auto& buff = getToRing(ringtone_->format(), bytes / ringtone_->frameSize());
const auto& buff = getToRing(ringtone_->format(), bytes / ringtone_->frameSize());
AudioSample* data;
pa_stream_begin_write(ringtone_->stream(), (void**)&data, &bytes);
if (buff.frames() == 0)
if (!buff)
memset(data, 0, bytes);
else
buff.interleave(data);
std::memcpy(data, buff->pointer()->data[0], buff->pointer()->nb_samples * playback_->frameSize());
pa_stream_write(ringtone_->stream(), data, bytes, nullptr, 0, PA_SEEK_RELATIVE);
}
......
......@@ -26,8 +26,10 @@
#include "ringbuffer.h"
#include "logger.h"
#include "media_buffer.h"
#include "libav_deps.h"
#include <chrono>
#include <utility> // for std::pair
#include <cstdlib>
#include <cstring>
#include <algorithm>
......@@ -37,15 +39,16 @@ namespace ring {
// corresponds to 160 ms (about 5 rtp packets)
static const size_t MIN_BUFFER_SIZE = 1024;
// Create a ring buffer with 'size' bytes
RingBuffer::RingBuffer(const std::string& rbuf_id, size_t size,
AudioFormat format /* = MONO */)
RingBuffer::RingBuffer(const std::string& rbuf_id, size_t size, AudioFormat format)
: id(rbuf_id)
, endPos_(0)
, buffer_(std::max(size, MIN_BUFFER_SIZE), format)
, lock_()
, not_empty_()
, readoffsets_()
, format_(format)
, resizer_(format_, format_.sample_rate / 50, [this](std::shared_ptr<AudioFrame>&& frame){
putToBuffer(std::move(frame));
})
{}
void
......@@ -54,29 +57,26 @@ RingBuffer::flush(const std::string &call_id)
storeReadOffset(endPos_, call_id);
}
void
RingBuffer::flushAll()
{
ReadOffset::iterator iter;
for (iter = readoffsets_.begin(); iter != readoffsets_.end(); ++iter)
iter->second = endPos_;
for (auto& offset : readoffsets_)
offset.second.offset = endPos_;
}
size_t
RingBuffer::putLength() const
{
const size_t buffer_size = buffer_.frames();
const size_t buffer_size = buffer_.size();
if (buffer_size == 0)
return 0;
const size_t startPos = (not readoffsets_.empty()) ? getSmallestReadOffset() : 0;
const size_t startPos = getSmallestReadOffset();
return (endPos_ + buffer_size - startPos) % buffer_size;
}
size_t RingBuffer::getLength(const std::string &call_id) const
{
const size_t buffer_size = buffer_.frames();
const size_t buffer_size = buffer_.size();
if (buffer_size == 0)
return 0;
return (endPos_ + buffer_size - getReadOffset(call_id)) % buffer_size;
......@@ -85,13 +85,13 @@ size_t RingBuffer::getLength(const std::string &call_id) const
void
RingBuffer::debug()
{
RING_DBG("Start=%zu; End=%zu; BufferSize=%zu", getSmallestReadOffset(), endPos_, buffer_.frames());
RING_DBG("Start=%zu; End=%zu; BufferSize=%zu", getSmallestReadOffset(), endPos_, buffer_.size());
}
size_t RingBuffer::getReadOffset(const std::string &call_id) const
{
ReadOffset::const_iterator iter = readoffsets_.find(call_id);
return (iter != readoffsets_.end()) ? iter->second : 0;
auto iter = readoffsets_.find(call_id);
return (iter != readoffsets_.end()) ? iter->second.offset : 0;
}
size_t
......@@ -99,30 +99,29 @@ RingBuffer::getSmallestReadOffset() const
{
if (hasNoReadOffsets())
return 0;
size_t smallest = buffer_.frames();
size_t smallest = buffer_.size();
for(auto const& iter : readoffsets_)
smallest = std::min(smallest, iter.second);
smallest = std::min(smallest, iter.second.offset);
return smallest;
}
void
RingBuffer::storeReadOffset(size_t offset, const std::string &call_id)
{
ReadOffset::iterator iter = readoffsets_.find(call_id);
ReadOffsetMap::iterator iter = readoffsets_.find(call_id);
if (iter != readoffsets_.end())
iter->second = offset;
iter->second.offset = offset;
else
RING_ERR("RingBuffer::storeReadOffset() failed: unknown call '%s'", call_id.c_str());
}
void
RingBuffer::createReadOffset(const std::string &call_id)
{
std::lock_guard<std::mutex> l(lock_);
if (!hasThisReadOffset(call_id))
readoffsets_.insert(std::pair<std::string, int> (call_id, endPos_));
readoffsets_.emplace(call_id, ReadOffset {endPos_});
}
......@@ -130,13 +129,11 @@ void
RingBuffer::removeReadOffset(const std::string &call_id)
{
std::lock_guard<std::mutex> l(lock_);
ReadOffset::iterator iter = readoffsets_.find(call_id);
auto iter = readoffsets_.find(call_id);
if (iter != readoffsets_.end())
readoffsets_.erase(iter);
}
bool
RingBuffer::hasThisReadOffset(const std::string &call_id) const
{
......@@ -153,40 +150,36 @@ bool RingBuffer::hasNoReadOffsets() const
// For the writer only:
//
void RingBuffer::put(std::shared_ptr<AudioFrame>&& data)
{
resizer_.enqueue(resampler_.resample(std::move(data), format_));
}
// This one puts some data inside the ring buffer.
void RingBuffer::put(AudioBuffer& buf)
void RingBuffer::putToBuffer(std::shared_ptr<AudioFrame>&& data)
{
std::lock_guard<std::mutex> l(lock_);
const size_t sample_num = buf.frames();
const size_t buffer_size = buffer_.frames();
const size_t buffer_size = buffer_.size();
if (buffer_size == 0)
return;
size_t len = putLength();
if (buffer_size - len < sample_num)
discard(sample_num);
size_t toCopy = sample_num;
// Add more channels if the input buffer holds more channels than the ring.
if (buffer_.channels() < buf.channels())
buffer_.setChannelNum(buf.channels());
size_t len = buffer_size - putLength();
if (len == 0)
discard(1);
size_t in_pos = 0;
size_t pos = endPos_;
while (toCopy) {
size_t block = toCopy;
buffer_[pos] = std::move(data);
const auto& newBuf = buffer_[pos];
pos = (pos + 1) % buffer_size;
if (block > buffer_size - pos) // Wrap block around ring ?
block = buffer_size - pos; // Fill in to the end of the buffer
endPos_ = pos;
buffer_.copy(buf, block, in_pos, pos);
in_pos += block;
pos = (pos + block) % buffer_size;
toCopy -= block;
for (auto& offset : readoffsets_) {
if (offset.second.callback)
offset.second.callback(newBuf);
}
endPos_ = pos;
not_empty_.notify_all();
}
......@@ -201,64 +194,45 @@ RingBuffer::availableForGet(const std::string &call_id) const
return getLength(call_id);
}
size_t RingBuffer::get(AudioBuffer& buf, const std::string &call_id)
std::shared_ptr<AudioFrame>
RingBuffer::get(const std::string& call_id)
{
std::lock_guard<std::mutex> l(lock_);
if (not hasThisReadOffset(call_id))
return 0;
auto offset = readoffsets_.find(call_id);
if (offset == readoffsets_.end())
return {};
const size_t buffer_size = buffer_.frames();
const size_t buffer_size = buffer_.size();
if (buffer_size == 0)
return 0;
size_t len = getLength(call_id);
const size_t sample_num = buf.frames();
size_t toCopy = std::min(sample_num, len);
if (toCopy and toCopy != sample_num) {
RING_DBG("Partial get: %zu/%zu", toCopy, sample_num);
}
const size_t copied = toCopy;
size_t dest = 0;
size_t startPos = getReadOffset(call_id);
while (toCopy > 0) {
size_t block = toCopy;
return {};
if (block > buffer_size - startPos)
block = buffer_size - startPos;
size_t startPos = offset->second.offset;
size_t len = (endPos_ + buffer_size - startPos) % buffer_size;
if (len == 0)
return {};
buf.copy(buffer_, block, startPos, dest);
dest += block;
startPos = (startPos + block) % buffer_size;
toCopy -= block;
}
storeReadOffset(startPos, call_id);
return copied;
auto ret = buffer_[startPos];
offset->second.offset = (startPos + 1) % buffer_size;
return ret;
}
size_t RingBuffer::waitForDataAvailable(const std::string &call_id, size_t min_data_length, const time_point& deadline) const
size_t RingBuffer::waitForDataAvailable(const std::string &call_id, const time_point& deadline) const
{
std::unique_lock<std::mutex> l(lock_);
const size_t buffer_size = buffer_.frames();
if (buffer_size < min_data_length) return 0;
ReadOffset::const_iterator read_ptr = readoffsets_.find(call_id);
if (read_ptr == readoffsets_.end()) return 0;
if (buffer_.empty()) return 0;
if (readoffsets_.find(call_id) == readoffsets_.end()) return 0;
size_t getl = 0;
auto check = [=, &getl] {
// Re-find read_ptr: it may be destroyed during the wait
const size_t buffer_size = buffer_.size();
const auto read_ptr = readoffsets_.find(call_id);
if (read_ptr == readoffsets_.end())
if (buffer_size == 0 || read_ptr == readoffsets_.end())
return true;
getl = (endPos_ + buffer_size - read_ptr->second) % buffer_size;
return getl >= min_data_length;
getl = (endPos_ + buffer_size - read_ptr->second.offset) % buffer_size;
return getl != 0;
};
if (deadline == time_point::max()) {
......@@ -272,34 +246,36 @@ size_t RingBuffer::waitForDataAvailable(const std::string &call_id, size_t min_d
}
size_t
RingBuffer::discard(size_t toDiscard, const std::string &call_id)
RingBuffer::discard(size_t toDiscard, const std::string& call_id)
{
std::lock_guard<std::mutex> l(lock_);
const size_t buffer_size = buffer_.frames();
const size_t buffer_size = buffer_.size();
if (buffer_size == 0)
return 0;
size_t len = getLength(call_id);
if (toDiscard > len)
toDiscard = len;
auto offset = readoffsets_.find(call_id);
if (offset == readoffsets_.end())
return 0;
size_t len = (endPos_ + buffer_size - offset->second.offset) % buffer_size;
toDiscard = std::min(toDiscard, len);
size_t startPos = (getReadOffset(call_id) + toDiscard) % buffer_size;
storeReadOffset(startPos, call_id);
offset->second.offset = (offset->second.offset + toDiscard) % buffer_size;
return toDiscard;
}
size_t
RingBuffer::discard(size_t toDiscard)
{
const size_t buffer_size = buffer_.frames();
const size_t buffer_size = buffer_.size();
if (buffer_size == 0)
return 0;
for (auto & r : readoffsets_) {
size_t dst = (r.second + buffer_size - endPos_) % buffer_size;
for (auto& r : readoffsets_) {
size_t dst = (r.second.offset + buffer_size - endPos_) % buffer_size;
if (dst < toDiscard)
r.second = (r.second + toDiscard - dst) % buffer_size;
r.second.offset = (r.second.offset + toDiscard - dst) % buffer_size;
}
return toDiscard;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment