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

android audio refactoring

* better abstraction of player/recorder
* extract higher-level logic to AudioLayer

Tuleap: #102
Change-Id: I1e81ecb220b7e01907154188ced531eb368b3311
parent 3364bd33
Branches
Tags
No related merge requests found
...@@ -109,4 +109,101 @@ void AudioLayer::notifyIncomingCall() ...@@ -109,4 +109,101 @@ void AudioLayer::notifyIncomingCall()
putUrgent(buf); putUrgent(buf);
} }
const AudioBuffer& AudioLayer::getToRing(AudioFormat format, size_t writableSamples)
{
ringtoneBuffer_.resize(0);
AudioLoop *fileToPlay = Manager::instance().getTelephoneFile();
if (fileToPlay) {
auto fileformat = fileToPlay->getFormat();
bool resample = format.sample_rate != fileformat.sample_rate;
size_t readableSamples = resample
? fileformat.sample_rate * (double) writableSamples / (double) audioFormat_.sample_rate
: writableSamples;
ringtoneBuffer_.setFormat(fileformat);
ringtoneBuffer_.resize(readableSamples);
fileToPlay->getNext(ringtoneBuffer_, 1.0);
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 ringtoneBuffer_;
}
const AudioBuffer& AudioLayer::getToPlay(AudioFormat format, size_t writableSamples)
{
playbackBuffer_.resize(0);
playbackResampleBuffer_.resize(0);
notifyIncomingCall();
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_;
}
// FIXME: not thread safe! we only lock the mutex when we get the
// pointer, we have no guarantee that it will stay safe to use
if (AudioLoop* toneToPlay = Manager::instance().getTelephoneTone()) {
playbackBuffer_.setFormat(format);
playbackBuffer_.resize(writableSamples);
toneToPlay->getNext(playbackBuffer_, playbackGain_); // retrive only n_channels
return playbackBuffer_;
}
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;
}
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_;
}
}
} // namespace ring } // namespace ring
...@@ -219,6 +219,11 @@ class AudioLayer { ...@@ -219,6 +219,11 @@ class AudioLayer {
*/ */
void hardwareInputFormatAvailable(AudioFormat capture); void hardwareInputFormatAvailable(AudioFormat capture);
const AudioBuffer& getToPlay(AudioFormat format, size_t writableSamples);
const AudioBuffer& getToRing(AudioFormat format, size_t writableSamples);
/** /**
* True if capture is not to be used * True if capture is not to be used
*/ */
...@@ -239,6 +244,14 @@ class AudioLayer { ...@@ -239,6 +244,14 @@ class AudioLayer {
*/ */
double playbackGain_; double playbackGain_;
/**
* Buffers for audio processing
*/
AudioBuffer playbackBuffer_;
AudioBuffer playbackResampleBuffer_;
AudioBuffer ringtoneBuffer_;
AudioBuffer ringtoneResampleBuffer_;
/** /**
* Whether or not the audio layer stream is started * Whether or not the audio layer stream is started
*/ */
......
...@@ -4,8 +4,8 @@ if BUILD_OPENSL ...@@ -4,8 +4,8 @@ if BUILD_OPENSL
noinst_LTLIBRARIES = libopensl.la noinst_LTLIBRARIES = libopensl.la
libopensl_la_SOURCES = opensllayer.cpp libopensl_la_SOURCES = opensllayer.cpp audio_player.cpp audio_recorder.cpp
noinst_HEADERS = opensllayer.h noinst_HEADERS = opensllayer.h audio_common.h audio_player.h audio_recorder.h buf_manager.h
endif endif
/*
* Copyright 2015 The Android Open Source Project
* Copyright 2015 Savoir-faire Linux Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <SLES/OpenSLES_Android.h>
#include "buf_manager.h"
namespace ring {
namespace opensl {
/*
* Sample Buffer Controls...
*/
#define RECORD_DEVICE_KICKSTART_BUF_COUNT 2
#define PLAY_KICKSTART_BUFFER_COUNT 8
#define DEVICE_SHADOW_BUFFER_QUEUE_LEN 4
#define BUF_COUNT 16
inline SLDataFormat_PCM convertToSLSampleFormat(const ring::AudioFormat& infos)
{
return SLDataFormat_PCM {
.formatType = SL_DATAFORMAT_PCM,
.numChannels = infos.nb_channels <= 1 ? 1u : 2u,
.samplesPerSec = infos.sample_rate * 1000,
.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16,
.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16,
.channelMask = infos.nb_channels <= 1 ? SL_SPEAKER_FRONT_CENTER : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
.endianness = SL_BYTEORDER_LITTLEENDIAN,
};
}
#define SLASSERT(x) { \
if(SL_RESULT_SUCCESS != (x))\
throw std::runtime_error("OpenSLES error");\
}
/*
* Interface for player and recorder to communicate with engine
*/
#define ENGINE_SERVICE_MSG_KICKSTART_PLAYER 1
#define ENGINE_SERVICE_MSG_RETRIEVE_DUMP_BUFS 2
using EngineCallback = std::function<void(bool waiting)>;
}}
/*
* Copyright 2015 The Android Open Source Project
* Copyright 2015 Savoir-faire Linux Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "audio_player.h"
#include "logger.h"
#include <SLES/OpenSLES_AndroidConfiguration.h>
#include <cstdlib>
namespace ring {
namespace opensl {
/*
* Called by OpenSL SimpleBufferQueue for every audio buffer played
* directly pass thru to our handler.
* The regularity of this callback from openSL/Android System affects
* playback continuity. If it does not callback in the regular time
* slot, you are under big pressure for audio processing[here we do
* not do any filtering/mixing]. Callback from fast audio path are
* much more regular than other audio paths by my observation. If it
* very regular, you could buffer much less audio samples between
* recorder and player, hence lower latency.
*/
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *ctx) {
(static_cast<AudioPlayer *>(ctx))->processSLCallback(bq);
}
void AudioPlayer::processSLCallback(SLAndroidSimpleBufferQueueItf bq) {
std::lock_guard<std::mutex> lk(m_);
// retrieve the finished device buf and put onto the free queue
// so recorder could re-use it
sample_buf *buf;
if(!devShadowQueue_.front(&buf)) {
RING_ERR("AudioPlayer buffer lost");
/*
* This should not happen: we got a callback,
* but we have no buffer in deviceShadowedQueue
* we lost buffers this way...(ERROR)
*/
callback_(true);
return;
}
devShadowQueue_.pop();
buf->size_ = 0;
if (!freeQueue_->push(buf)) {
RING_ERR("buffer lost");
}
callback_(false);
while(playQueue_->front(&buf) && devShadowQueue_.push(buf)) {
if ((*bq)->Enqueue(bq, buf->buf_, buf->size_) != SL_RESULT_SUCCESS) {
devShadowQueue_.pop();
RING_ERR("enqueue failed %zu %zu %zu %zu", buf->size_, freeQueue_->size(), playQueue_->size(), devShadowQueue_.size());
break;
} else
playQueue_->pop();
}
if (devShadowQueue_.size() == 0) {
RING_ERR("AudioPlayer: nothing to play %zu %zu %zu", freeQueue_->size(), playQueue_->size(), devShadowQueue_.size());
waiting_ = true;
callback_(true);
}
}
AudioPlayer::AudioPlayer(ring::AudioFormat sampleFormat, SLEngineItf slEngine, SLint32 streamType) :
sampleInfo_(sampleFormat)
{
SLresult result;
result = (*slEngine)->CreateOutputMix(slEngine, &outputMixObjectItf_, 0, nullptr, nullptr);
SLASSERT(result);
// realize the output mix
result = (*outputMixObjectItf_)->Realize(outputMixObjectItf_, SL_BOOLEAN_FALSE);
SLASSERT(result);
// configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
DEVICE_SHADOW_BUFFER_QUEUE_LEN };
auto format_pcm = convertToSLSampleFormat(sampleInfo_);
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObjectItf_};
SLDataSink audioSnk = {&loc_outmix, nullptr};
/*
* create fast path audio player: SL_IID_BUFFERQUEUE and SL_IID_VOLUME interfaces ok,
* NO others!
*/
SLInterfaceID ids[3] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*slEngine)->CreateAudioPlayer(slEngine, &playerObjectItf_, &audioSrc, &audioSnk,
sizeof(ids)/sizeof(ids[0]), ids, req);
SLASSERT(result);
SLAndroidConfigurationItf playerConfig;
result = (*playerObjectItf_)->GetInterface(playerObjectItf_, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
// realize the player
result = (*playerObjectItf_)->Realize(playerObjectItf_, SL_BOOLEAN_FALSE);
SLASSERT(result);
// get the play interface
result = (*playerObjectItf_)->GetInterface(playerObjectItf_, SL_IID_PLAY, &playItf_);
SLASSERT(result);
// get the buffer queue interface
result = (*playerObjectItf_)->GetInterface(playerObjectItf_, SL_IID_BUFFERQUEUE, &playBufferQueueItf_);
SLASSERT(result);
// register callback on the buffer queue
result = (*playBufferQueueItf_)->RegisterCallback(playBufferQueueItf_, bqPlayerCallback, this);
SLASSERT(result);
result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);
SLASSERT(result);
}
AudioPlayer::~AudioPlayer() {
// destroy buffer queue audio player object, and invalidate all associated interfaces
if (playerObjectItf_) {
(*playerObjectItf_)->Destroy(playerObjectItf_);
}
// destroy output mix object, and invalidate all associated interfaces
if (outputMixObjectItf_) {
(*outputMixObjectItf_)->Destroy(outputMixObjectItf_);
}
}
void AudioPlayer::setBufQueue(AudioQueue *playQ, AudioQueue *freeQ) {
playQueue_ = playQ;
freeQueue_ = freeQ;
}
bool AudioPlayer::start() {
SLuint32 state;
SLresult result = (*playItf_)->GetPlayState(playItf_, &state);
if (result != SL_RESULT_SUCCESS)
return false;
if(state == SL_PLAYSTATE_PLAYING)
return true;
result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);
SLASSERT(result);
result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_PLAYING);
SLASSERT(result);
// send pre-defined audio buffers to device
int i = PLAY_KICKSTART_BUFFER_COUNT;
while(i--) {
sample_buf *buf;
if(!playQueue_->front(&buf)) //we have buffers for sure
break;
if(SL_RESULT_SUCCESS !=
(*playBufferQueueItf_)->Enqueue(playBufferQueueItf_, buf, buf->size_))
{
RING_ERR("====failed to enqueue (%d) in %s", i, __FUNCTION__);
return false;
} else {
playQueue_->pop();
devShadowQueue_.push(buf);
}
}
waiting_ = devShadowQueue_.size() == 0;
return true;
}
bool AudioPlayer::started() const
{
if (!playItf_)
return false;
SLuint32 state;
SLresult result = (*playItf_)->GetPlayState(playItf_, &state);
return result == SL_RESULT_SUCCESS && state == SL_PLAYSTATE_PLAYING;
}
void AudioPlayer::stop() {
SLuint32 state;
SLresult result = (*playItf_)->GetPlayState(playItf_, &state);
SLASSERT(result);
if(state == SL_PLAYSTATE_STOPPED)
return;
result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);
SLASSERT(result);
waiting_ = false;
// Consume all non-completed audio buffers
sample_buf *buf = nullptr;
while(devShadowQueue_.front(&buf)) {
buf->size_ = 0;
devShadowQueue_.pop();
freeQueue_->push(buf);
}
while(playQueue_->front(&buf)) {
buf->size_ = 0;
playQueue_->pop();
freeQueue_->push(buf);
}
}
void AudioPlayer::playAudioBuffers(unsigned count) {
while (count--) {
sample_buf *buf = nullptr;
if(!playQueue_->front(&buf)) {
RING_ERR("====Run out of buffers in %s @(count = %d)", __FUNCTION__, count);
break;
}
if(!devShadowQueue_.push(buf)) {
break; // PlayerBufferQueue is full!!!
}
SLresult result = (*playBufferQueueItf_)->Enqueue(playBufferQueueItf_,
buf->buf_, buf->size_);
if(result != SL_RESULT_SUCCESS) {
RING_ERR("%s Error @( %p, %d ), result = %d", __FUNCTION__, (void*)buf->buf_, buf->size_, result);
/*
* when this happens, a buffer is lost. Need to remove the buffer
* from top of the devShadowQueue. Since I do not have it now,
* just pop out the one that is being played right now. Afer a
* cycle it will be normal.
*/
devShadowQueue_.front(&buf), devShadowQueue_.pop();
freeQueue_->push(buf);
break;
}
playQueue_->pop(); // really pop out the buffer
}
waiting_ = devShadowQueue_.size() == 0;
}
size_t AudioPlayer::dbgGetDevBufCount(void) {
return devShadowQueue_.size();
}
}}
/*
* Copyright 2015 The Android Open Source Project
* Copyright 2015 Savoir-faire Linux Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "audio_common.h"
#include "buf_manager.h"
#include <sys/types.h>
#include <SLES/OpenSLES_Android.h>
#include <mutex>
namespace ring {
namespace opensl {
class AudioPlayer {
// buffer queue player interfaces
SLObjectItf outputMixObjectItf_;
SLObjectItf playerObjectItf_;
SLPlayItf playItf_;
SLAndroidSimpleBufferQueueItf playBufferQueueItf_;
ring::AudioFormat sampleInfo_;
AudioQueue *freeQueue_ {nullptr}; // user
AudioQueue *playQueue_ {nullptr}; // user
AudioQueue devShadowQueue_ {DEVICE_SHADOW_BUFFER_QUEUE_LEN}; // owner
EngineCallback callback_ {};
public:
explicit AudioPlayer(ring::AudioFormat sampleFormat, SLEngineItf engine, SLint32 streamType);
~AudioPlayer();
bool start();
void stop();
bool started() const;
void setBufQueue(AudioQueue *playQ, AudioQueue *freeQ);
void processSLCallback(SLAndroidSimpleBufferQueueItf bq);
void playAudioBuffers(unsigned count);
void registerCallback(EngineCallback cb) {callback_ = cb;}
size_t dbgGetDevBufCount();
std::mutex m_;
std::atomic_bool waiting_ {false};
};
}}
/*
* Copyright 2015 The Android Open Source Project
* Copyright 2015 Savoir-faire Linux Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstring>
#include <cstdlib>
#include "audio_recorder.h"
namespace ring {
namespace opensl {
/*
* bqRecorderCallback(): called for every buffer is full;
* pass directly to handler
*/
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *rec) {
(static_cast<AudioRecorder *>(rec))->processSLCallback(bq);
}
void AudioRecorder::processSLCallback(SLAndroidSimpleBufferQueueItf bq) {
assert(bq == recBufQueueItf_);
sample_buf *dataBuf {nullptr};
devShadowQueue_.front(&dataBuf);
devShadowQueue_.pop();
dataBuf->size_ = dataBuf->cap_; //device only calls us when it is really full
recQueue_->push(dataBuf);
sample_buf* freeBuf;
while (freeQueue_->front(&freeBuf) && devShadowQueue_.push(freeBuf)) {
freeQueue_->pop();
SLresult result = (*bq)->Enqueue(bq, freeBuf->buf_, freeBuf->cap_);
SLASSERT(result);
}
// should leave the device to sleep to save power if no buffers
if (devShadowQueue_.size() == 0) {
(*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);
}
callback_(false);
}
AudioRecorder::AudioRecorder(ring::AudioFormat sampleFormat, SLEngineItf slEngine) :
sampleInfo_(sampleFormat)
{
// configure audio source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,
SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT,
nullptr };
SLDataSource audioSrc = {&loc_dev, nullptr };
// configure audio sink
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
DEVICE_SHADOW_BUFFER_QUEUE_LEN };
auto format_pcm = convertToSLSampleFormat(sampleInfo_);
SLDataSink audioSnk = {&loc_bq, &format_pcm};
// create audio recorder
// (requires the RECORD_AUDIO permission)
const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
SLresult result;
result = (*slEngine)->CreateAudioRecorder(slEngine,
&recObjectItf_,
&audioSrc,
&audioSnk,
1, id, req);
SLASSERT(result);
result = (*recObjectItf_)->Realize(recObjectItf_, SL_BOOLEAN_FALSE);
SLASSERT(result);
result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_RECORD, &recItf_);
SLASSERT(result);
result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recBufQueueItf_);
SLASSERT(result);
result = (*recBufQueueItf_)->RegisterCallback(recBufQueueItf_, bqRecorderCallback, this);
SLASSERT(result);
}
bool
AudioRecorder::start()
{
if(!freeQueue_ || !recQueue_) {
RING_ERR("====NULL pointer to Start(%p, %p)", freeQueue_, recQueue_);
return false;
}
audioBufCount = 0;
SLresult result;
// in case already recording, stop recording and clear buffer queue
result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);
SLASSERT(result);
result = (*recBufQueueItf_)->Clear(recBufQueueItf_);
SLASSERT(result);
for(int i =0; i < RECORD_DEVICE_KICKSTART_BUF_COUNT; i++ ) {
sample_buf *buf = NULL;
if(!freeQueue_->front(&buf)) {
RING_ERR("=====OutOfFreeBuffers @ startingRecording @ (%d)", i);
break;
}
freeQueue_->pop();
assert(buf->buf_ && buf->cap_ && !buf->size_);
result = (*recBufQueueItf_)->Enqueue(recBufQueueItf_, buf->buf_,
buf->cap_);
SLASSERT(result);
devShadowQueue_.push(buf);
}
result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_RECORDING);
SLASSERT(result);
return result == SL_RESULT_SUCCESS;
}
bool
AudioRecorder::stop()
{
// in case already recording, stop recording and clear buffer queue
SLuint32 curState;
SLresult result = (*recItf_)->GetRecordState(recItf_, &curState);
SLASSERT(result);
if (curState == SL_RECORDSTATE_STOPPED)
return true;
result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);
SLASSERT(result);
result = (*recBufQueueItf_)->Clear(recBufQueueItf_);
SLASSERT(result);
sample_buf *buf {nullptr};
while(devShadowQueue_.front(&buf)) {
devShadowQueue_.pop();
freeQueue_->push(buf);
}
return true;
}
AudioRecorder::~AudioRecorder() {
// destroy audio recorder object, and invalidate all associated interfaces
if (recObjectItf_) {
(*recObjectItf_)->Destroy(recObjectItf_);
}
}
void AudioRecorder::setBufQueues(AudioQueue *freeQ, AudioQueue *recQ) {
assert(freeQ && recQ);
freeQueue_ = freeQ;
recQueue_ = recQ;
}
size_t AudioRecorder::dbgGetDevBufCount() {
return devShadowQueue_.size();
}
}}
/*
* Copyright 2015 The Android Open Source Project
* Copyright 2015 Savoir-faire Linux Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <sys/types.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include "audio_common.h"
#include "buf_manager.h"
namespace ring {
namespace opensl {
class AudioRecorder {
SLObjectItf recObjectItf_;
SLRecordItf recItf_;
SLAndroidSimpleBufferQueueItf recBufQueueItf_;
ring::AudioFormat sampleInfo_;
AudioQueue *freeQueue_ {nullptr}; // user
AudioQueue *recQueue_ {nullptr}; // user
AudioQueue devShadowQueue_ {DEVICE_SHADOW_BUFFER_QUEUE_LEN}; // owner
uint32_t audioBufCount;
EngineCallback callback_ {};
public:
explicit AudioRecorder(ring::AudioFormat, SLEngineItf engineEngine);
~AudioRecorder();
bool start();
bool stop();
void setBufQueues(AudioQueue *freeQ, AudioQueue *recQ);
void processSLCallback(SLAndroidSimpleBufferQueueItf bq);
void registerCallback(EngineCallback cb) {callback_ = cb;}
size_t dbgGetDevBufCount();
};
}
}
/*
* Copyright 2015 The Android Open Source Project
* Copyright 2015 Savoir-faire Linux Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "audio/audiobuffer.h"
#include "logger.h"
#include <SLES/OpenSLES.h>
#include <sys/types.h>
#include <atomic>
#include <cassert>
#include <memory>
#include <limits>
#include <vector>
#ifndef CACHE_ALIGN
#define CACHE_ALIGN 64
#endif
/*
* ProducerConsumerQueue, borrowed from Ian NiLewis
*/
template <typename T>
class ProducerConsumerQueue {
public:
explicit ProducerConsumerQueue(size_t size) : buffer_(size) {
// This is necessary because we depend on twos-complement wraparound
// to take care of overflow conditions.
assert(size < std::numeric_limits<int>::max());
}
void clear() {
read_.store(0);
write_.store(0);
}
bool push(const T& item) {
return push([&](T* ptr) -> bool {*ptr = item; return true; });
}
// get() is idempotent between calls to commit().
T* getWriteablePtr() {
T* result = nullptr;
bool check __attribute__((unused));//= false;
check = push([&](T* head)-> bool {
result = head;
return false; // don't increment
});
// if there's no space, result should not have been set, and vice versa
assert(check == (result != nullptr));
return result;
}
bool commitWriteablePtr(T *ptr) {
bool result = push([&](T* head)-> bool {
// this writer func does nothing, because we assume that the caller
// has already written to *ptr after acquiring it from a call to get().
// So just double-check that ptr is actually at the write head, and
// return true to indicate that it's safe to advance.
// if this isn't the same pointer we got from a call to get(), then
// something has gone terribly wrong. Either there was an intervening
// call to push() or commit(), or the pointer is spurious.
assert(ptr == head);
return true;
});
return result;
}
// writer() can return false, which indicates that the caller
// of push() changed its mind while writing (e.g. ran out of bytes)
template<typename F>
bool push(const F& writer) {
bool result = false;
int readptr = read_.load(std::memory_order_acquire);
int writeptr = write_.load(std::memory_order_relaxed);
// note that while readptr and writeptr will eventually
// wrap around, taking their difference is still valid as
// long as size_ < MAXINT.
int space = buffer_.size() - (int)(writeptr - readptr);
if (space >= 1) {
result = true;
// writer
if (writer(buffer_.data() + (writeptr % buffer_.size()))) {
++writeptr;
write_.store(writeptr, std::memory_order_release);
}
}
return result;
}
// front out the queue, but not pop-out
bool front(T* out_item) {
return front([&](T* ptr)-> bool {*out_item = *ptr; return true;});
}
void pop(void) {
int readptr = read_.load(std::memory_order_relaxed);
++readptr;
read_.store(readptr, std::memory_order_release);
}
template<typename F>
bool front(const F& reader) {
bool result = false;
int writeptr = write_.load(std::memory_order_acquire);
int readptr = read_.load(std::memory_order_relaxed);
// As above, wraparound is ok
int available = (int)(writeptr - readptr);
if (available >= 1) {
result = true;
reader(buffer_.data() + (readptr % buffer_.size()));
}
return result;
}
uint32_t size(void) {
int writeptr = write_.load(std::memory_order_acquire);
int readptr = read_.load(std::memory_order_relaxed);
return (uint32_t)(writeptr - readptr);
}
private:
std::vector<T> buffer_;
// forcing cache line alignment to eliminate false sharing of the
// frequently-updated read and write pointers. The object is to never
// let these get into the "shared" state where they'd cause a cache miss
// for every write.
alignas(CACHE_ALIGN) std::atomic<int> read_ { 0 };
alignas(CACHE_ALIGN) std::atomic<int> write_ { 0 };
};
struct sample_buf {
uint8_t* buf_ {nullptr}; // audio sample container
size_t cap_ {0}; // buffer capacity in byte
size_t size_ {0}; // audio sample size (n buf) in byte
sample_buf() {}
sample_buf(size_t alloc, size_t size) : buf_(new uint8_t[alloc]), cap_(size) {}
~sample_buf() {
if (buf_) delete[] buf_;
}
};
using AudioQueue = ProducerConsumerQueue<sample_buf*>;
__inline__ std::vector<sample_buf>
allocateSampleBufs(unsigned count, size_t sizeInByte)
{
std::vector<sample_buf> bufs;
if (!count || !sizeInByte)
return bufs;
bufs.reserve(count);
size_t allocSize = (sizeInByte + 3) & ~3; // padding to 4 bytes aligned
for(unsigned i =0; i < count; i++)
bufs.emplace_back(allocSize, sizeInByte);
return bufs;
}
This diff is collapsed.
...@@ -24,8 +24,11 @@ ...@@ -24,8 +24,11 @@
#include <SLES/OpenSLES.h> #include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h> #include <SLES/OpenSLES_Android.h>
#include <vector> #include <vector>
#include <thread>
#include "audio/audiolayer.h" #include "audio/audiolayer.h"
#include "audio_player.h"
#include "audio_recorder.h"
class AudioPreference; class AudioPreference;
...@@ -35,7 +38,6 @@ class AudioPreference; ...@@ -35,7 +38,6 @@ class AudioPreference;
namespace ring { namespace ring {
class OpenSLThread;
class RingBuffer; class RingBuffer;
#define ANDROID_BUFFER_QUEUE_LENGTH 2U #define ANDROID_BUFFER_QUEUE_LENGTH 2U
...@@ -87,13 +89,13 @@ class OpenSLLayer : public AudioLayer { ...@@ -87,13 +89,13 @@ class OpenSLLayer : public AudioLayer {
void init(); void init();
void initAudioEngine() const; void initAudioEngine();
void shutdownAudioEngine(); void shutdownAudioEngine();
void initAudioPlayback() const; void initAudioPlayback();
void initAudioCapture() const; void initAudioCapture();
void startAudioPlayback(); void startAudioPlayback();
...@@ -111,41 +113,20 @@ class OpenSLLayer : public AudioLayer { ...@@ -111,41 +113,20 @@ class OpenSLLayer : public AudioLayer {
return ""; return "";
} }
private: bool engineServicePlay(bool waiting);
typedef std::vector<AudioBuffer> AudioBufferStack; bool engineServiceRing(bool waiting);
bool engineServiceRec(bool waiting);
bool audioBufferFillWithZeros(AudioBuffer &buffer);
/**
* Here fill the input buffer with tone or ringtone samples
*/
bool audioPlaybackFillWithToneOrRingtone(AudioBuffer &buffer);
bool audioPlaybackFillWithUrgent(AudioBuffer &buffer, size_t bytesAvail);
size_t audioPlaybackFillWithVoice(AudioBuffer &buffer);
private:
void audioCaptureFillBuffer(AudioBuffer &buffer); void audioCaptureFillBuffer(AudioBuffer &buffer);
/**
* This is the main audio playabck callback called by the OpenSL layer
*/
static void audioPlaybackCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
/**
* This is the main audio capture callback called by the OpenSL layer
*/
static void audioCaptureCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
/** /**
* Get the index of the audio card for capture * Get the index of the audio card for capture
* @return int The index of the card used for capture * @return int The index of the card used for capture
* 0 for the first available card on the system, 1 ... * 0 for the first available card on the system, 1 ...
*/ */
virtual int getIndexCapture() const { virtual int getIndexCapture() const {
return indexIn_; return 0;
} }
/** /**
...@@ -154,7 +135,7 @@ class OpenSLLayer : public AudioLayer { ...@@ -154,7 +135,7 @@ class OpenSLLayer : public AudioLayer {
* 0 for the first available card on the system, 1 ... * 0 for the first available card on the system, 1 ...
*/ */
virtual int getIndexPlayback() const { virtual int getIndexPlayback() const {
return indexOut_; return 0;
} }
/** /**
...@@ -163,99 +144,56 @@ class OpenSLLayer : public AudioLayer { ...@@ -163,99 +144,56 @@ class OpenSLLayer : public AudioLayer {
* 0 for the first available card on the system, 1 ... * 0 for the first available card on the system, 1 ...
*/ */
virtual int getIndexRingtone() const { virtual int getIndexRingtone() const {
return indexRing_; return 0;
}
AudioBuffer &getNextPlaybackBuffer(void) {
return playbackBufferStack_[playbackBufferIndex_];
}
AudioBuffer &getNextRecordBuffer(void) {
return recordBufferStack_[recordBufferIndex_];
}
void incrementPlaybackIndex(void) {
playbackBufferIndex_ = (playbackBufferIndex_ + 1) % NB_BUFFER_PLAYBACK_QUEUE;
}
void incrementRecordIndex(void) {
recordBufferIndex_ = (recordBufferIndex_ + 1) % NB_BUFFER_CAPTURE_QUEUE;
} }
void CheckErr( SLresult res ) const; uint32_t dbgEngineGetBufCount();
void playback(SLAndroidSimpleBufferQueueItf queue);
void capture(SLAndroidSimpleBufferQueueItf queue);
void dumpAvailableEngineInterfaces(); void dumpAvailableEngineInterfaces();
friend class OpenSLThread;
static const int NB_BUFFER_PLAYBACK_QUEUE;
static const int NB_BUFFER_CAPTURE_QUEUE;
/**
* Number of audio cards on which capture stream has been opened
*/
int indexIn_;
/**
* Number of audio cards on which playback stream has been opened
*/
int indexOut_;
/**
* Number of audio cards on which ringtone stream has been opened
*/
int indexRing_;
NON_COPYABLE(OpenSLLayer); NON_COPYABLE(OpenSLLayer);
virtual void updatePreference(AudioPreference &pref, int index, DeviceType type); virtual void updatePreference(AudioPreference &pref, int index, DeviceType type);
OpenSLThread *audioThread_;
/** /**
* OpenSL standard object interface * OpenSL standard object interface
*/ */
SLObjectItf engineObject_; SLObjectItf engineObject_ {nullptr};
/** /**
* OpenSL sound engine interface * OpenSL sound engine interface
*/ */
SLEngineItf engineInterface_; SLEngineItf engineInterface_ {nullptr};
/** std::unique_ptr<opensl::AudioPlayer> player_ {};
* Output mix interface std::unique_ptr<opensl::AudioPlayer> ringtone_ {};
*/ std::unique_ptr<opensl::AudioRecorder> recorder_ {};
SLObjectItf outputMixer_;
SLObjectItf playerObject_;
SLObjectItf recorderObject_;
AudioQueue freePlayBufQueue_ {BUF_COUNT};
AudioQueue playBufQueue_ {BUF_COUNT};
SLOutputMixItf outputMixInterface_; AudioQueue freeRingBufQueue_ {BUF_COUNT};
SLPlayItf playerInterface_; AudioQueue ringBufQueue_ {BUF_COUNT};
SLRecordItf recorderInterface_; std::thread playThread;
std::mutex playMtx;
std::condition_variable playCv;
SLAudioIODeviceCapabilitiesItf AudioIODeviceCapabilitiesItf; AudioQueue freeRecBufQueue_ {BUF_COUNT}; //Owner of the queue
SLAudioInputDescriptor AudioInputDescriptor; AudioQueue recBufQueue_ {BUF_COUNT}; //Owner of the queue
/** std::thread recThread;
* OpenSL playback buffer std::mutex recMtx;
*/ std::condition_variable recCv;
SLAndroidSimpleBufferQueueItf playbackBufferQueue_;
SLAndroidSimpleBufferQueueItf recorderBufferQueue_; std::vector<sample_buf> bufs_;
int playbackBufferIndex_; SLAudioIODeviceCapabilitiesItf AudioIODeviceCapabilitiesItf {nullptr};
int recordBufferIndex_; SLAudioInputDescriptor audioInputDescriptor_;
bool bufferIsFilled_; AudioFormat hardwareFormat_ {AudioFormat::MONO()};
AudioFormat hardwareFormat_; size_t hardwareBuffSize_ {BUFFER_SIZE};
size_t hardwareBuffSize_;
AudioBufferStack playbackBufferStack_;
AudioBufferStack recordBufferStack_;
std::shared_ptr<RingBuffer> mainRingBuffer_; std::shared_ptr<RingBuffer> mainRingBuffer_;
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment