diff --git a/src/media/audio/audiolayer.cpp b/src/media/audio/audiolayer.cpp index 3e3c4c0b73525a570abe543f7feaf332479a335e..fbb2f185fd0c3f5b0bbd8e1c7080f2e151c4d738 100644 --- a/src/media/audio/audiolayer.cpp +++ b/src/media/audio/audiolayer.cpp @@ -109,4 +109,101 @@ void AudioLayer::notifyIncomingCall() 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 diff --git a/src/media/audio/audiolayer.h b/src/media/audio/audiolayer.h index 92d767bba86655ef0f19378ce3a296a01902773f..b1a79bd936cb730061c06a409cb684a0f8efa851 100644 --- a/src/media/audio/audiolayer.h +++ b/src/media/audio/audiolayer.h @@ -219,6 +219,11 @@ class AudioLayer { */ 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 */ @@ -239,6 +244,14 @@ class AudioLayer { */ double playbackGain_; + /** + * Buffers for audio processing + */ + AudioBuffer playbackBuffer_; + AudioBuffer playbackResampleBuffer_; + AudioBuffer ringtoneBuffer_; + AudioBuffer ringtoneResampleBuffer_; + /** * Whether or not the audio layer stream is started */ diff --git a/src/media/audio/opensl/Makefile.am b/src/media/audio/opensl/Makefile.am index 4e3c0d9e2a8989b42f40883ae87dcea574e1c492..782af065481759059cde0f080351cb4763f62411 100644 --- a/src/media/audio/opensl/Makefile.am +++ b/src/media/audio/opensl/Makefile.am @@ -4,8 +4,8 @@ if BUILD_OPENSL 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 diff --git a/src/media/audio/opensl/audio_common.h b/src/media/audio/opensl/audio_common.h new file mode 100644 index 0000000000000000000000000000000000000000..b1a8fd8cbd04b59784f886735e695ca0b11c276c --- /dev/null +++ b/src/media/audio/opensl/audio_common.h @@ -0,0 +1,62 @@ +/* + * 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)>; + +}} diff --git a/src/media/audio/opensl/audio_player.cpp b/src/media/audio/opensl/audio_player.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9673ddac5baa6201c016a07f702ef68cc6e1204f --- /dev/null +++ b/src/media/audio/opensl/audio_player.cpp @@ -0,0 +1,260 @@ +/* + * 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(); +} + +}} diff --git a/src/media/audio/opensl/audio_player.h b/src/media/audio/opensl/audio_player.h new file mode 100644 index 0000000000000000000000000000000000000000..025a2aa34fd3a6353581843f0237c9158348d051 --- /dev/null +++ b/src/media/audio/opensl/audio_player.h @@ -0,0 +1,62 @@ +/* + * 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}; +}; + +}} diff --git a/src/media/audio/opensl/audio_recorder.cpp b/src/media/audio/opensl/audio_recorder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34f3c73127c01e71802d361882b5d2d9802901dd --- /dev/null +++ b/src/media/audio/opensl/audio_recorder.cpp @@ -0,0 +1,175 @@ +/* + * 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(); +} + +}} diff --git a/src/media/audio/opensl/audio_recorder.h b/src/media/audio/opensl/audio_recorder.h new file mode 100644 index 0000000000000000000000000000000000000000..e26d34acd251bc92c68a4001d80f9f5fa4dda1fa --- /dev/null +++ b/src/media/audio/opensl/audio_recorder.h @@ -0,0 +1,55 @@ +/* + * 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(); +}; + +} +} diff --git a/src/media/audio/opensl/buf_manager.h b/src/media/audio/opensl/buf_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..0cea34b7de412ce4ce470ed43586dadfe359af0e --- /dev/null +++ b/src/media/audio/opensl/buf_manager.h @@ -0,0 +1,183 @@ +/* + * 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; +} diff --git a/src/media/audio/opensl/opensllayer.cpp b/src/media/audio/opensl/opensllayer.cpp index 3eafa8a5bf036a5f25f232e49d04b85209da1fb4..1155f5839cd7ac45917640cc0f6a0fb93bb62b8a 100644 --- a/src/media/audio/opensl/opensllayer.cpp +++ b/src/media/audio/opensl/opensllayer.cpp @@ -22,11 +22,11 @@ #include "client/ring_signal.h" -#include "manager.h" #include "audio/resampler.h" #include "audio/ringbufferpool.h" #include "audio/ringbuffer.h" #include "audio/dcblocker.h" +#include "manager.h" #include "logger.h" #include "array_size.h" @@ -45,40 +45,17 @@ namespace ring { -const int OpenSLLayer::NB_BUFFER_PLAYBACK_QUEUE = ANDROID_BUFFER_QUEUE_LENGTH; -const int OpenSLLayer::NB_BUFFER_CAPTURE_QUEUE = ANDROID_BUFFER_QUEUE_LENGTH; - // Constructor OpenSLLayer::OpenSLLayer(const AudioPreference &pref) - : AudioLayer(pref) - , indexIn_(0) - , indexOut_(0) - , indexRing_(0) - , audioThread_(nullptr) - , engineObject_(nullptr) - , engineInterface_(nullptr) - , outputMixer_(nullptr) - , playerObject_(nullptr) - , recorderObject_(nullptr) - , playerInterface_(nullptr) - , recorderInterface_(nullptr) - , playbackBufferQueue_(nullptr) - , recorderBufferQueue_(nullptr) - , playbackBufferIndex_(0) - , recordBufferIndex_(0) - , hardwareFormat_(AudioFormat::MONO()) - , hardwareBuffSize_(BUFFER_SIZE) - , playbackBufferStack_(ANDROID_BUFFER_QUEUE_LENGTH, AudioBuffer(hardwareBuffSize_, AudioFormat::MONO())) - , recordBufferStack_(ANDROID_BUFFER_QUEUE_LENGTH, AudioBuffer(hardwareBuffSize_, AudioFormat::MONO())) - , mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID)) -{ -} + : AudioLayer(pref), + audioInputDescriptor_(), + mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID)) +{} // Destructor OpenSLLayer::~OpenSLLayer() { - stopAudioPlayback(); - stopAudioCapture(); + shutdownAudioEngine(); } void @@ -111,12 +88,6 @@ OpenSLLayer::startStream() emitSignal<DRing::ConfigurationSignal::GetHardwareAudioFormat>(&hw_infos); hardwareFormat_ = AudioFormat(hw_infos[0], 1); // Mono on Android hardwareBuffSize_ = hw_infos[1]; - - for(auto& buf : playbackBufferStack_) - buf.resize(hardwareBuffSize_); - for(auto& buf : recordBufferStack_) - buf.resize(hardwareBuffSize_); - hardwareFormatAvailable(hardwareFormat_); std::thread launcher([this](){ @@ -150,388 +121,256 @@ OpenSLLayer::stopStream() flushMain(); flushUrgent(); -} - -void -OpenSLLayer::initAudioEngine() const -{ - SLresult result; - - RING_DBG("Create Audio Engine\n"); - result = slCreateEngine((const SLObjectItf_ * const **)&engineObject_, 0, nullptr, 0, nullptr, nullptr); - assert(SL_RESULT_SUCCESS == result); - RING_DBG("Realize Audio Engine\n"); - result = (*engineObject_)->Realize(engineObject_, SL_BOOLEAN_FALSE); - assert(SL_RESULT_SUCCESS == result); - - RING_DBG("Create Audio Engine Interface\n"); - result = (*engineObject_)->GetInterface(engineObject_, SL_IID_ENGINE, - (void *)&engineInterface_); - assert(SL_RESULT_SUCCESS == result); - - RING_DBG("Create Output Mixer\n"); - result = (*engineInterface_)->CreateOutputMix(engineInterface_, - (const SLObjectItf_ * const **)&outputMixer_, - 0, nullptr, nullptr); - assert(SL_RESULT_SUCCESS == result); - - RING_DBG("Realize Output Mixer\n"); - result = (*outputMixer_)->Realize(outputMixer_, SL_BOOLEAN_FALSE); - assert(SL_RESULT_SUCCESS == result); - - RING_DBG("Audio Engine Initialization Done\n"); -} - -void -OpenSLLayer::shutdownAudioEngine() -{ - - // destroy buffer queue audio player object, and invalidate all associated interfaces - RING_DBG("Shutdown audio player\n"); - - if (playerObject_ != nullptr) { - (*playerObject_)->Destroy(playerObject_); - playerObject_ = nullptr; - playerInterface_ = nullptr; - playbackBufferQueue_ = nullptr; - } - - // destroy output mix object, and invalidate all associated interfaces - RING_DBG("Shutdown audio mixer\n"); - - if (outputMixer_ != nullptr) { - (*outputMixer_)->Destroy(outputMixer_); - outputMixer_ = nullptr; - } - - if (recorderObject_ != nullptr) { - (*recorderObject_)->Destroy(recorderObject_); - recorderObject_ = nullptr; - recorderInterface_ = nullptr; - recorderBufferQueue_ = nullptr; - } - - // destroy engine object, and invalidate all associated interfaces - RING_DBG("Shutdown audio engine\n"); if (engineObject_ != nullptr) { (*engineObject_)->Destroy(engineObject_); engineObject_ = nullptr; engineInterface_ = nullptr; } + + freePlayBufQueue_.clear(); + freeRingBufQueue_.clear(); + playBufQueue_.clear(); + ringBufQueue_.clear(); + freeRecBufQueue_.clear(); + recBufQueue_.clear(); + bufs_.clear(); } void -OpenSLLayer::initAudioPlayback() const +OpenSLLayer::initAudioEngine() { - assert(nullptr != engineObject_); - assert(nullptr != engineInterface_); - assert(nullptr != outputMixer_); - SLresult result; - // Initialize the location of the buffer queue - RING_DBG("Create playback queue\n"); - SLDataLocator_AndroidSimpleBufferQueue bufferLocation = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, - NB_BUFFER_PLAYBACK_QUEUE - }; - - // Initnialize the audio format for this queue - RING_DBG("Setting audio format\n"); - SLDataFormat_PCM audioFormat = {SL_DATAFORMAT_PCM, - 1, - audioFormat_.sample_rate * 1000, - SL_PCMSAMPLEFORMAT_FIXED_16, - SL_PCMSAMPLEFORMAT_FIXED_16, - SL_SPEAKER_FRONT_CENTER, - SL_BYTEORDER_LITTLEENDIAN - }; - - // Create the audio source - RING_DBG("Set Audio Sources\n"); - SLDataSource audioSource = {&bufferLocation, &audioFormat}; - - RING_DBG("Get Output Mixer interface\n"); - result = (*outputMixer_)->GetInterface(outputMixer_, SL_IID_OUTPUTMIX, - (void *)&outputMixInterface_); - CheckErr(result); - - // Cofiguration fo the audio sink as an output mixer - RING_DBG("Set output mixer location\n"); - SLDataLocator_OutputMix mixerLocation = {SL_DATALOCATOR_OUTPUTMIX, outputMixer_}; - SLDataSink audioSink = {&mixerLocation, nullptr}; - - const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - SL_IID_VOLUME, - SL_IID_ANDROIDCONFIGURATION, - SL_IID_PLAY}; - const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; - - const unsigned nbInterface = arraySize(ids); - - // create audio player - RING_DBG("Create audio player\n"); - result = (*engineInterface_)->CreateAudioPlayer(engineInterface_, - (const SLObjectItf_ * const **)&playerObject_, - &audioSource, - &audioSink, - nbInterface, - ids, req); - assert(SL_RESULT_SUCCESS == result); - - SLAndroidConfigurationItf playerConfig; - SLint32 streamType = SL_ANDROID_STREAM_VOICE; - + result = slCreateEngine(&engineObject_, 0, nullptr, 0, nullptr, nullptr); + SLASSERT(result); - result = (*playerObject_)->GetInterface(playerObject_, - SL_IID_ANDROIDCONFIGURATION, - (void *)&playerConfig); - - if (result == SL_RESULT_SUCCESS && playerConfig) { - result = (*playerConfig)->SetConfiguration( - playerConfig, SL_ANDROID_KEY_STREAM_TYPE, - &streamType, sizeof(SLint32)); - } - - RING_DBG("Realize audio player\n"); - result = (*playerObject_)->Realize(playerObject_, SL_BOOLEAN_FALSE); - assert(SL_RESULT_SUCCESS == result); - - if (result != SL_RESULT_SUCCESS) { - RING_ERR("Unable to set android player configuration"); - } - - // create audio interface - RING_DBG("Create audio player interface\n"); - result = (*playerObject_)->GetInterface(playerObject_, SL_IID_PLAY, - (void *)&playerInterface_); - assert(SL_RESULT_SUCCESS == result); - - // create the buffer queue interface - RING_DBG("Create buffer queue interface\n"); - result = (*playerObject_)->GetInterface(playerObject_, SL_IID_BUFFERQUEUE, - (void *)&playbackBufferQueue_); - assert(SL_RESULT_SUCCESS == result); - - // register the buffer queue on the buffer object - RING_DBG("Register audio callback\n"); - result = (*playbackBufferQueue_)->RegisterCallback(playbackBufferQueue_, - audioPlaybackCallback, - (void *)this); - assert(SL_RESULT_SUCCESS == result); - - RING_DBG("Audio Playback Initialization Done\n"); + result = (*engineObject_)->Realize(engineObject_, SL_BOOLEAN_FALSE); + SLASSERT(result); + + result = (*engineObject_)->GetInterface(engineObject_, SL_IID_ENGINE, &engineInterface_); + SLASSERT(result); + + uint32_t bufSize = hardwareBuffSize_ * hardwareFormat_.getBytesPerFrame(); + bufs_ = allocateSampleBufs(BUF_COUNT*3, bufSize); + for(int i=0; i<BUF_COUNT; i++) + freePlayBufQueue_.push(&bufs_[i]); + for(int i=BUF_COUNT; i<2*BUF_COUNT; i++) + freeRingBufQueue_.push(&bufs_[i]); + for(int i=2*BUF_COUNT; i<3*BUF_COUNT; i++) + freeRecBufQueue_.push(&bufs_[i]); } void -OpenSLLayer::initAudioCapture() const +OpenSLLayer::shutdownAudioEngine() { - SLresult result; + // destroy engine object, and invalidate all associated interfaces + RING_DBG("Shutdown audio engine"); + stopStream(); +} - // configure audio source - RING_DBG("Configure audio source\n"); - SLDataLocator_IODevice deviceLocator = {SL_DATALOCATOR_IODEVICE, - SL_IODEVICE_AUDIOINPUT, - SL_DEFAULTDEVICEID_AUDIOINPUT, - nullptr - }; - - SLDataSource audioSource = {&deviceLocator, - nullptr - }; - - // configure audio sink - RING_DBG("Configure audio sink\n"); - - SLDataLocator_AndroidSimpleBufferQueue bufferLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, - NB_BUFFER_CAPTURE_QUEUE - }; - - RING_DBG("Capture-> Sampling Rate: %d", audioFormat_.sample_rate); - RING_DBG("Capture-> getInternalSamplingRate: %d", Manager::instance().getRingBufferPool().getInternalSamplingRate()); - SLDataFormat_PCM audioFormat = {SL_DATAFORMAT_PCM, 1, - audioFormat_.sample_rate * 1000, - SL_PCMSAMPLEFORMAT_FIXED_16, - SL_PCMSAMPLEFORMAT_FIXED_16, - SL_SPEAKER_FRONT_CENTER, - SL_BYTEORDER_LITTLEENDIAN - }; - - SLDataSink audioSink = {&bufferLocator, - &audioFormat - }; - - // create audio recorder - // (requires the RECORD_AUDIO permission) - RING_DBG("Create audio recorder\n"); - const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - SL_IID_ANDROIDCONFIGURATION}; - const SLboolean req[2] ={SL_BOOLEAN_TRUE, - SL_BOOLEAN_TRUE}; - SLAndroidConfigurationItf recorderConfig; - - if (engineInterface_ != nullptr) { - result = (*engineInterface_)->CreateAudioRecorder(engineInterface_, - (const SLObjectItf_ * const **)&recorderObject_, - &audioSource, - &audioSink, - 2, id, req); +uint32_t +OpenSLLayer::dbgEngineGetBufCount() { + uint32_t count_player = player_->dbgGetDevBufCount(); + count_player += freePlayBufQueue_.size(); + count_player += playBufQueue_.size(); + + uint32_t count_ringtone = ringtone_->dbgGetDevBufCount(); + count_ringtone += freeRingBufQueue_.size(); + count_ringtone += ringBufQueue_.size(); + + RING_ERR("Buf Disrtibutions: PlayerDev=%d, PlayQ=%d, FreePlayQ=%d", + player_->dbgGetDevBufCount(), + playBufQueue_.size(), + freePlayBufQueue_.size()); + RING_ERR("Buf Disrtibutions: RingDev=%d, RingQ=%d, FreeRingQ=%d", + ringtone_->dbgGetDevBufCount(), + ringBufQueue_.size(), + freeRingBufQueue_.size()); + + if(count_player != BUF_COUNT) { + RING_ERR("====Lost Bufs among the queue(supposed = %d, found = %d)", + BUF_COUNT, count_player); } + return count_player; +} - if (SL_RESULT_SUCCESS != result) { - RING_DBG("Error: could not create audio recorder"); - return; +bool +OpenSLLayer::engineServicePlay(bool waiting) { + if (waiting) { + playCv.notify_one(); + return false; } - - /* Set Android configuration */ - result = (*recorderObject_)->GetInterface(recorderObject_, - SL_IID_ANDROIDCONFIGURATION, - (void *)&recorderConfig); - if (result == SL_RESULT_SUCCESS) { - SLint32 streamType = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - result = (*recorderConfig)->SetConfiguration( - recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, - &streamType, sizeof(SLint32)); + 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 (!playBufQueue_.push(buf)) { + RING_WARN("playThread player_ PLAY_KICKSTART_BUFFER_COUNT 1"); + break; + } else + freePlayBufQueue_.pop(); + } else + break; } + return true; +} - if (result != SL_RESULT_SUCCESS) { - RING_DBG("Warning: Unable to set android recorder configuration"); - return; +bool +OpenSLLayer::engineServiceRing(bool waiting) { + if (waiting) { + playCv.notify_one(); + return false; } - - // realize the audio recorder - RING_DBG("Realize the audio recorder\n"); - result = (*recorderObject_)->Realize(recorderObject_, SL_BOOLEAN_FALSE); - - if (SL_RESULT_SUCCESS != result) { - RING_DBG("Error: could not realize audio recorder"); - return; + 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 (!ringBufQueue_.push(buf)) { + RING_WARN("playThread ringtone_ PLAY_KICKSTART_BUFFER_COUNT 1"); + freeRingBufQueue_.push(buf); + break; + } + } else { + freeRingBufQueue_.push(buf); + break; + } } + return true; +} - // get the record interface - RING_DBG("Create the record interface\n"); - result = (*recorderObject_)->GetInterface(recorderObject_, - SL_IID_RECORD, - (void *)&recorderInterface_); - assert(SL_RESULT_SUCCESS == result); - - // get the buffer queue interface - RING_DBG("Create the buffer queue interface\n"); - result = (*recorderObject_)->GetInterface(recorderObject_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - (void *)&recorderBufferQueue_); - assert(SL_RESULT_SUCCESS == result); - - // register callback on the buffer queue - RING_DBG("Register the audio capture callback\n"); - result = (*recorderBufferQueue_)->RegisterCallback(recorderBufferQueue_, - audioCaptureCallback, - (void *)this); - assert(SL_RESULT_SUCCESS == result); +bool +OpenSLLayer::engineServiceRec(bool waiting) { + playCv.notify_one(); + recCv.notify_one(); + return true; +} - RING_DBG("Audio capture initialized\n"); +void +OpenSLLayer::initAudioPlayback() +{ + using namespace std::placeholders; + std::lock_guard<std::mutex> lck(playMtx); + player_.reset(new opensl::AudioPlayer(hardwareFormat_, engineInterface_, SL_ANDROID_STREAM_VOICE)); + player_->setBufQueue(&playBufQueue_, &freePlayBufQueue_); + player_->registerCallback(std::bind(&OpenSLLayer::engineServicePlay, this, _1)); + + ringtone_.reset(new opensl::AudioPlayer(hardwareFormat_, engineInterface_, SL_ANDROID_STREAM_VOICE)); + ringtone_->setBufQueue(&ringBufQueue_, &freeRingBufQueue_); + ringtone_->registerCallback(std::bind(&OpenSLLayer::engineServiceRing, this, _1)); } +void +OpenSLLayer::initAudioCapture() +{ + using namespace std::placeholders; + std::lock_guard<std::mutex> lck(recMtx); + recorder_.reset(new opensl::AudioRecorder(hardwareFormat_, engineInterface_)); + recorder_->setBufQueues(&freeRecBufQueue_, &recBufQueue_); + recorder_->registerCallback(std::bind(&OpenSLLayer::engineServiceRec, this, _1)); +} void OpenSLLayer::startAudioPlayback() { - assert(nullptr != playbackBufferQueue_); - - RING_DBG("Start audio playback\n"); - - SLresult result; - - for (int i = 0; i < NB_BUFFER_PLAYBACK_QUEUE; i++) { - AudioBuffer &buffer = getNextPlaybackBuffer(); - incrementPlaybackIndex(); - - buffer.reset(); - - result = (*playbackBufferQueue_)->Enqueue(playbackBufferQueue_, buffer.getChannel(0)->data(), buffer.frames()); - - if (SL_RESULT_SUCCESS != result) { - RING_DBG("Error could not enqueue initial buffers\n"); + RING_WARN("Start audio playback"); + + player_->start(); + ringtone_->start(); + playThread = std::thread([&]() { + std::unique_lock<std::mutex> lck(playMtx); + while (player_ || ringtone_) { + playCv.wait(lck); + if (player_ && player_->waiting_) { + std::lock_guard<std::mutex> lk(player_->m_); + engineServicePlay(false); + auto n = playBufQueue_.size(); + if (n >= PLAY_KICKSTART_BUFFER_COUNT) + player_->playAudioBuffers(n); + } + if (ringtone_ && ringtone_->waiting_) { + std::lock_guard<std::mutex> lk(ringtone_->m_); + engineServiceRing(false); + auto n = ringBufQueue_.size(); + if (n >= PLAY_KICKSTART_BUFFER_COUNT) + ringtone_->playAudioBuffers(n); + } } - } - - result = (*playerInterface_)->SetPlayState(playerInterface_, SL_PLAYSTATE_PLAYING); - assert(SL_RESULT_SUCCESS == result); - - RING_DBG("Audio playback started\n"); + }); + RING_WARN("Audio playback started"); } void OpenSLLayer::startAudioCapture() { - assert(nullptr != playbackBufferQueue_); - - RING_DBG("Start audio capture\n"); - - SLresult result; - - - // in case already recording, stop recording and clear buffer queue - if (recorderInterface_ != nullptr) { - result = (*recorderInterface_)->SetRecordState(recorderInterface_, SL_RECORDSTATE_STOPPED); - assert(SL_RESULT_SUCCESS == result); - } - - RING_DBG("Clearing recorderBufferQueue\n"); - result = (*recorderBufferQueue_)->Clear(recorderBufferQueue_); - assert(SL_RESULT_SUCCESS == result); - - RING_DBG("getting next record buffer\n"); - // enqueue an empty buffer to be filled by the recorder - // (for streaming recording, we enqueue at least 2 empty buffers to start things off) - AudioBuffer &buffer = getNextRecordBuffer(); - incrementRecordIndex(); - - buffer.reset(); - - RING_DBG("Enqueue record buffer\n"); - result = (*recorderBufferQueue_)->Enqueue(recorderBufferQueue_, buffer.getChannel(0)->data(), buffer.frames()); - - // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT, - // which for this code example would indicate a programming error - if (SL_RESULT_SUCCESS != result) { - RING_DBG("Error could not enqueue buffers in audio capture\n"); - return; - } - - // start recording - result = (*recorderInterface_)->SetRecordState(recorderInterface_, SL_RECORDSTATE_RECORDING); - assert(SL_RESULT_SUCCESS == result); + RING_DBG("Start audio capture"); + + recorder_->start(); + recThread = std::thread([&]() { + std::unique_lock<std::mutex> lck(recMtx); + while (recorder_) { + recCv.wait(lck); + while (true) { + sample_buf *buf; + if(!recBufQueue_.front(&buf)) + break; + recBufQueue_.pop(); + if (buf->size_ > 0) { + AudioBuffer dat {(const AudioSample*)buf->buf_, buf->size_ / hardwareFormat_.getBytesPerFrame(), hardwareFormat_}; + audioCaptureFillBuffer(dat); + } + buf->size_ = 0; + freeRecBufQueue_.push(buf); + } + } + }); - RING_DBG("Audio capture started\n"); + RING_DBG("Audio capture started"); } void OpenSLLayer::stopAudioPlayback() { - RING_DBG("Stop audio playback\n"); + RING_DBG("Stop audio playback"); - if (playerInterface_ != nullptr) { - SLresult result; - result = (*playerInterface_)->SetPlayState(playerInterface_, SL_PLAYSTATE_STOPPED); - assert(SL_RESULT_SUCCESS == result); + { + std::lock_guard<std::mutex> lck(playMtx); + if (player_) { + player_->stop(); + player_.reset(); + } + if (ringtone_) { + ringtone_->stop(); + ringtone_.reset(); + } + } + if (playThread.joinable()) { + playCv.notify_all(); + playThread.join(); } - RING_DBG("Audio playback stopped\n"); + RING_DBG("Audio playback stopped"); } void OpenSLLayer::stopAudioCapture() { - RING_DBG("Stop audio capture\n"); + RING_DBG("Stop audio capture"); - if (recorderInterface_ != nullptr) { - SLresult result; - result = (*recorderInterface_)->SetRecordState(recorderInterface_, SL_RECORDSTATE_STOPPED); - assert(SL_RESULT_SUCCESS == result); + { + std::lock_guard<std::mutex> lck(recMtx); + recorder_->stop(); + recorder_.reset(); + } + if (recThread.joinable()) { + recCv.notify_all(); + recThread.join(); } - RING_DBG("Audio capture stopped\n"); - + RING_DBG("Audio capture stopped"); } std::vector<std::string> @@ -539,12 +378,10 @@ OpenSLLayer::getCaptureDeviceList() const { std::vector<std::string> captureDeviceList; - - -// Although OpenSL ES specification allows enumerating -// available output (and also input) devices, NDK implementation is not mature enough to -// obtain or select proper one (SLAudioIODeviceCapabilitiesItf, the official interface -// to obtain such an information)-> SL_FEATURE_UNSUPPORTED + // Although OpenSL ES specification allows enumerating + // available output (and also input) devices, NDK implementation is not mature enough to + // obtain or select proper one (SLAudioIODeviceCapabilitiesItf, the official interface + // to obtain such an information)-> SL_FEATURE_UNSUPPORTED SLuint32 InputDeviceIDs[MAX_NUMBER_INPUT_DEVICES]; SLint32 numInputs = 0; @@ -553,41 +390,39 @@ OpenSLLayer::getCaptureDeviceList() const SLresult res; - initAudioEngine(); - initAudioPlayback(); - initAudioCapture(); - - // Get the Audio IO DEVICE CAPABILITIES interface, implicit RING_DBG("Get the Audio IO DEVICE CAPABILITIES interface, implicit"); res = (*engineObject_)->GetInterface(engineObject_, SL_IID_AUDIOIODEVICECAPABILITIES, (void*)&AudioIODeviceCapabilitiesItf); - CheckErr(res); + if (res != SL_RESULT_SUCCESS) + return captureDeviceList; RING_DBG("Get the Audio IO DEVICE CAPABILITIES interface, implicit"); numInputs = MAX_NUMBER_INPUT_DEVICES; res = (*AudioIODeviceCapabilitiesItf)->GetAvailableAudioInputs(AudioIODeviceCapabilitiesItf, &numInputs, InputDeviceIDs); - CheckErr(res); + if (res != SL_RESULT_SUCCESS) + return captureDeviceList; // Search for either earpiece microphone or headset microphone input // device - with a preference for the latter for (int i = 0; i < numInputs; i++) { res = (*AudioIODeviceCapabilitiesItf)->QueryAudioInputCapabilities(AudioIODeviceCapabilitiesItf, InputDeviceIDs[i], - (SLAudioInputDescriptor *)&AudioInputDescriptor); - CheckErr(res); + (SLAudioInputDescriptor_ *)&audioInputDescriptor_); + if (res != SL_RESULT_SUCCESS) + return captureDeviceList; - if (AudioInputDescriptor.deviceConnection == SL_DEVCONNECTION_ATTACHED_WIRED and - AudioInputDescriptor.deviceScope == SL_DEVSCOPE_USER and - AudioInputDescriptor.deviceLocation == SL_DEVLOCATION_HEADSET) { + if (audioInputDescriptor_.deviceConnection == SL_DEVCONNECTION_ATTACHED_WIRED and + audioInputDescriptor_.deviceScope == SL_DEVSCOPE_USER and + audioInputDescriptor_.deviceLocation == SL_DEVLOCATION_HEADSET) { RING_DBG("SL_DEVCONNECTION_ATTACHED_WIRED : mic_deviceID: %d", InputDeviceIDs[i] ); mic_deviceID = InputDeviceIDs[i]; mic_available = SL_BOOLEAN_TRUE; break; - } else if (AudioInputDescriptor.deviceConnection == SL_DEVCONNECTION_INTEGRATED and - AudioInputDescriptor.deviceScope == SL_DEVSCOPE_USER and - AudioInputDescriptor.deviceLocation == SL_DEVLOCATION_HANDSET) { + } else if (audioInputDescriptor_.deviceConnection == SL_DEVCONNECTION_INTEGRATED and + audioInputDescriptor_.deviceScope == SL_DEVSCOPE_USER and + audioInputDescriptor_.deviceLocation == SL_DEVLOCATION_HANDSET) { RING_DBG("SL_DEVCONNECTION_INTEGRATED : mic_deviceID: %d", InputDeviceIDs[i] ); mic_deviceID = InputDeviceIDs[i]; mic_available = SL_BOOLEAN_TRUE; @@ -595,133 +430,26 @@ OpenSLLayer::getCaptureDeviceList() const } } - if (!mic_available) { - // Appropriate error message here - RING_ERR("No mic available quitting"); - exit(1); - } - + if (!mic_available) + RING_ERR("No mic available"); return captureDeviceList; } -/* Checks for error. If any errors exit the application! */ -void -OpenSLLayer::CheckErr( SLresult res ) const -{ - if (res != SL_RESULT_SUCCESS) { - // Debug printing to be placed here - exit(1); - } -} - std::vector<std::string> OpenSLLayer::getPlaybackDeviceList() const { std::vector<std::string> playbackDeviceList; - return playbackDeviceList; } void -OpenSLLayer::audioPlaybackCallback(SLAndroidSimpleBufferQueueItf queue, void *context) -{ - assert(nullptr != context); - //auto start = std::chrono::high_resolution_clock::now(); - static_cast<OpenSLLayer*>(context)->playback(queue); - //auto end = std::chrono::high_resolution_clock::now(); - //auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start); - //RING_DBG("Took %d us\n", elapsed/1000); -} - -void -OpenSLLayer::playback(SLAndroidSimpleBufferQueueItf queue) -{ - assert(nullptr != queue); - notifyIncomingCall(); - - AudioBuffer &buffer = getNextPlaybackBuffer(); - size_t urgentSamplesToGet = urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID); - - bufferIsFilled_ = false; - if (urgentSamplesToGet > 0) { - bufferIsFilled_ = audioPlaybackFillWithUrgent(buffer, std::min(urgentSamplesToGet, hardwareBuffSize_)); - } else { - auto& main_buffer = Manager::instance().getRingBufferPool(); - buffer.resize(hardwareBuffSize_); - size_t samplesToGet = audioPlaybackFillWithVoice(buffer); - if (samplesToGet == 0) { - bufferIsFilled_ = audioPlaybackFillWithToneOrRingtone(buffer); - } else { - bufferIsFilled_ = true; - } - } - - if (bufferIsFilled_) { - SLresult result = (*queue)->Enqueue(queue, buffer.getChannel(0)->data(), buffer.frames()*sizeof(AudioSample)); - if (SL_RESULT_SUCCESS != result) { - RING_DBG("Error could not enqueue buffers in playback callback\n"); - } - incrementPlaybackIndex(); - } else { - RING_DBG("Error buffer not filled in audio playback\n"); - } -} - -void -OpenSLLayer::audioCaptureCallback(SLAndroidSimpleBufferQueueItf queue, void *context) -{ - assert(nullptr != context); - static_cast<OpenSLLayer*>(context)->capture(queue); -} - -void -OpenSLLayer::capture(SLAndroidSimpleBufferQueueItf queue) -{ - assert(nullptr != queue); - - AudioBuffer &old_buffer = getNextRecordBuffer(); - incrementRecordIndex(); - AudioBuffer &buffer = getNextRecordBuffer(); - - SLresult result; - // enqueue an empty buffer to be filled by the recorder - // (for streaming recording, we enqueue at least 2 empty buffers to start things off) - result = (*recorderBufferQueue_)->Enqueue(recorderBufferQueue_, buffer.getChannel(0)->data(), buffer.frames()*sizeof(AudioSample)); - - audioCaptureFillBuffer(old_buffer); - - // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT, - // which for this code example would indicate a programming error - assert(SL_RESULT_SUCCESS == result); -} - - - -void -OpenSLLayer::updatePreference(AudioPreference &preference, int index, DeviceType type) -{ -#ifdef OUTSIDE_TESTING - - switch (type) { - case Device::PLAYBACK: - break; - - case Device::CAPTURE: - break; - - case Device::RINGTONE: - break; - } - -#endif -} +OpenSLLayer::updatePreference(AudioPreference& /*preference*/, int /*index*/, DeviceType /*type*/) +{} void OpenSLLayer::audioCaptureFillBuffer(AudioBuffer &buffer) { RingBufferPool &mbuffer = Manager::instance().getRingBufferPool(); - - //const unsigned mainBufferSampleRate = mbuffer.getInternalSamplingRate(); const AudioFormat mainBufferFormat = mbuffer.getInternalAudioFormat(); const bool resample = mainBufferFormat.sample_rate != audioFormat_.sample_rate; @@ -739,66 +467,17 @@ void OpenSLLayer::audioCaptureFillBuffer(AudioBuffer &buffer) } } -bool OpenSLLayer::audioPlaybackFillWithToneOrRingtone(AudioBuffer &buffer) -{ - buffer.resize(hardwareBuffSize_); - AudioLoop *tone = Manager::instance().getTelephoneTone(); - AudioLoop *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(); - } - - return true; -} - -bool OpenSLLayer::audioPlaybackFillWithUrgent(AudioBuffer &buffer, size_t samplesToGet) -{ - // Urgent data (dtmf, incoming call signal) come first. - samplesToGet = std::min(samplesToGet, hardwareBuffSize_); - 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); - - return true; -} - -size_t OpenSLLayer::audioPlaybackFillWithVoice(AudioBuffer &buffer) -{ - RingBufferPool &mainBuffer = Manager::instance().getRingBufferPool(); - size_t got = mainBuffer.getAvailableData(buffer, RingBufferPool::DEFAULT_ID); - buffer.resize(got); - buffer.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_); - if (audioFormat_.sample_rate != mainBuffer.getInternalSamplingRate()) { - RING_DBG("OpenSLLayer::audioPlaybackFillWithVoice sample_rate != mainBuffer.getInternalSamplingRate() \n"); - AudioBuffer out(buffer, false); - out.setSampleRate(audioFormat_.sample_rate); - resampler_->resample(buffer, out); - buffer = out; - } - return buffer.size(); -} - void dumpAvailableEngineInterfaces() { SLresult result; - RING_DBG("Engine Interfaces\n"); + RING_DBG("Engine Interfaces"); SLuint32 numSupportedInterfaces; result = slQueryNumSupportedEngineInterfaces(&numSupportedInterfaces); assert(SL_RESULT_SUCCESS == result); result = slQueryNumSupportedEngineInterfaces(NULL); assert(SL_RESULT_PARAMETER_INVALID == result); - RING_DBG("Engine number of supported interfaces %lu\n", numSupportedInterfaces); + RING_DBG("Engine number of supported interfaces %u", numSupportedInterfaces); for(SLuint32 i=0; i< numSupportedInterfaces; i++){ SLInterfaceID pInterfaceId; slQuerySupportedEngineInterfaces(i, &pInterfaceId); diff --git a/src/media/audio/opensl/opensllayer.h b/src/media/audio/opensl/opensllayer.h index defba741bad3b07339b6492f1f1b17e04a55c400..df14ca583471c592560751acdb62e15090626b73 100644 --- a/src/media/audio/opensl/opensllayer.h +++ b/src/media/audio/opensl/opensllayer.h @@ -24,8 +24,11 @@ #include <SLES/OpenSLES.h> #include <SLES/OpenSLES_Android.h> #include <vector> +#include <thread> #include "audio/audiolayer.h" +#include "audio_player.h" +#include "audio_recorder.h" class AudioPreference; @@ -35,7 +38,6 @@ class AudioPreference; namespace ring { -class OpenSLThread; class RingBuffer; #define ANDROID_BUFFER_QUEUE_LENGTH 2U @@ -87,13 +89,13 @@ class OpenSLLayer : public AudioLayer { void init(); - void initAudioEngine() const; + void initAudioEngine(); void shutdownAudioEngine(); - void initAudioPlayback() const; + void initAudioPlayback(); - void initAudioCapture() const; + void initAudioCapture(); void startAudioPlayback(); @@ -111,41 +113,20 @@ class OpenSLLayer : public AudioLayer { return ""; } - private: - typedef std::vector<AudioBuffer> AudioBufferStack; - - - 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); + bool engineServicePlay(bool waiting); + bool engineServiceRing(bool waiting); + bool engineServiceRec(bool waiting); + private: 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 * @return int The index of the card used for capture * 0 for the first available card on the system, 1 ... */ virtual int getIndexCapture() const { - return indexIn_; + return 0; } /** @@ -154,7 +135,7 @@ class OpenSLLayer : public AudioLayer { * 0 for the first available card on the system, 1 ... */ virtual int getIndexPlayback() const { - return indexOut_; + return 0; } /** @@ -163,99 +144,56 @@ class OpenSLLayer : public AudioLayer { * 0 for the first available card on the system, 1 ... */ virtual int getIndexRingtone() const { - return indexRing_; - } - - 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; + return 0; } - void CheckErr( SLresult res ) const; - - void playback(SLAndroidSimpleBufferQueueItf queue); - void capture(SLAndroidSimpleBufferQueueItf queue); + uint32_t dbgEngineGetBufCount(); 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); virtual void updatePreference(AudioPreference &pref, int index, DeviceType type); - OpenSLThread *audioThread_; - /** * OpenSL standard object interface */ - SLObjectItf engineObject_; + SLObjectItf engineObject_ {nullptr}; /** * OpenSL sound engine interface */ - SLEngineItf engineInterface_; + SLEngineItf engineInterface_ {nullptr}; - /** - * Output mix interface - */ - SLObjectItf outputMixer_; - SLObjectItf playerObject_; - SLObjectItf recorderObject_; + std::unique_ptr<opensl::AudioPlayer> player_ {}; + std::unique_ptr<opensl::AudioPlayer> ringtone_ {}; + std::unique_ptr<opensl::AudioRecorder> recorder_ {}; + AudioQueue freePlayBufQueue_ {BUF_COUNT}; + AudioQueue playBufQueue_ {BUF_COUNT}; - SLOutputMixItf outputMixInterface_; - SLPlayItf playerInterface_; + AudioQueue freeRingBufQueue_ {BUF_COUNT}; + AudioQueue ringBufQueue_ {BUF_COUNT}; - SLRecordItf recorderInterface_; + std::thread playThread; + std::mutex playMtx; + std::condition_variable playCv; - SLAudioIODeviceCapabilitiesItf AudioIODeviceCapabilitiesItf; - SLAudioInputDescriptor AudioInputDescriptor; + AudioQueue freeRecBufQueue_ {BUF_COUNT}; //Owner of the queue + AudioQueue recBufQueue_ {BUF_COUNT}; //Owner of the queue - /** - * OpenSL playback buffer - */ - SLAndroidSimpleBufferQueueItf playbackBufferQueue_; - SLAndroidSimpleBufferQueueItf recorderBufferQueue_; + std::thread recThread; + std::mutex recMtx; + std::condition_variable recCv; + + std::vector<sample_buf> bufs_; - int playbackBufferIndex_; - int recordBufferIndex_; + SLAudioIODeviceCapabilitiesItf AudioIODeviceCapabilitiesItf {nullptr}; + SLAudioInputDescriptor audioInputDescriptor_; - bool bufferIsFilled_; - AudioFormat hardwareFormat_; - size_t hardwareBuffSize_; + AudioFormat hardwareFormat_ {AudioFormat::MONO()}; + size_t hardwareBuffSize_ {BUFFER_SIZE}; - AudioBufferStack playbackBufferStack_; - AudioBufferStack recordBufferStack_; std::shared_ptr<RingBuffer> mainRingBuffer_; };