From b68e1cf9ac17badeea169c41194b431bbfd11342 Mon Sep 17 00:00:00 2001 From: Julian Santander Date: Sat, 17 Apr 2021 18:12:19 +0200 Subject: [PATCH 1/5] Android: Use common CBuffer instead of custom RingBuffer and use int16_t samples --- android/ring_buffer.h | 172 ------------------------------------------ android/sound.cpp | 56 ++++---------- android/sound.h | 4 +- src/buffer.h | 3 + 4 files changed, 21 insertions(+), 214 deletions(-) delete mode 100644 android/ring_buffer.h diff --git a/android/ring_buffer.h b/android/ring_buffer.h deleted file mode 100644 index 792e6812e3..0000000000 --- a/android/ring_buffer.h +++ /dev/null @@ -1,172 +0,0 @@ -/******************************************************************************\ - * Copyright (c) 2004-2020 - * - * Author(s): - * Julian Santander - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - * -\******************************************************************************/ - -#pragma once -#include - -/** - * Implementantion of a ring buffer. - * Data is contained in a vector dynamically allocated. - */ -template -class RingBuffer { -public: - /** - * @brief RingBuffer - * @param max maximum number of elements that can be contained in the ring buffer - */ - RingBuffer(std::size_t max = 0):mData(max),mRead(0),mWrite(0),mFull(false) { } - - /** - * @brief Resets the ring_buffer - * @param max maximum number of elements that can be contained in the ring buffer. - */ - void reset(std::size_t max = 0) { - mData = std::vector(max); - mRead = 0; - mWrite = 0; - mFull = false; - } - - /** - * @brief Current number of elements contained in the ring buffer - * @return - */ - std::size_t size() const { - std::size_t size = capacity(); - if(!mFull) { - if(mWrite >= mRead) { - size = mWrite - mRead; - } else { - size = capacity() + mWrite - mRead; - } - } - return size; - } - - /** - * @brief whether the ring buffer is full - * @return - */ - bool isFull() const { return mFull; } - - /** - * @brief whether the ring buffer is empty. - * @return - */ - bool isEmpty() const { return !isFull() && (mRead == mWrite); } - - /** - * @brief Maximum number of elements in the ring buffer - * @return - */ - std::size_t capacity() const { return mData.size(); } - - /** - * @brief Adds a single value - * @param v the value to add - */ - void put(const T &v) { - mData[mWrite] = v; - forward(); - } - - /** - * @brief Reads a single value - * @param v the value read - * @return true if the value was read - */ - bool get(T&v) { - if(!isEmpty()) { - v = mData[mRead]; - backward(); - return true; - } else { - return false; - } - } - - /** - * @brief Adds a multiple consecutive values - * @param v pointer to the consecutive values - * @param count number of consecutive values. - */ - void put(const T *v, std::size_t count) { - std::size_t avail = mWrite - capacity(); - std::size_t to_copy = std::min(count,avail); - memcpy(mData.data() + mWrite,v, to_copy*sizeof(T)); - forward(to_copy); - if(to_copy < count) { - put(v+to_copy,count - to_copy); - } - } - - /** - * @brief Reads multiple values - * @param v pointer to the memory wher ethe values will be written - * @param count Maximum available size in the memory area - * @return actual number of elements read. - */ - std::size_t get(T *v, std::size_t count) { - std::size_t avail = 0; - if(mRead < mWrite) { - avail = mWrite - mRead; - } else { - avail = mRead - capacity(); - } - std::size_t to_copy = std::min(count, avail); - memcpy(v, mData.data() + mRead, to_copy * sizeof(T)); - backward(to_copy); - if((size()>0)&&(count > to_copy)) { - return to_copy + get(v + to_copy, count - to_copy); - } else { - return to_copy; - } - } -private: - void forward() { - if(isFull()) { - mRead = (mRead + 1) % capacity(); - } - mWrite = (mWrite + 1) % capacity(); - mFull = (mRead == mWrite); - } - - void forward(std::size_t count) { - for(std::size_t i=0; i mData; - std::size_t mRead; /** offset to reading point */ - std::size_t mWrite; /** offset to writing point */ - bool mFull; -}; - diff --git a/android/sound.cpp b/android/sound.cpp index ffab760999..afd24c2fec 100644 --- a/android/sound.cpp +++ b/android/sound.cpp @@ -46,7 +46,7 @@ void CSound::setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ) // We request EXCLUSIVE mode since this will give us the lowest possible // latency. If EXCLUSIVE mode isn't available the builder will fall back to SHARED mode builder - ->setFormat(oboe::AudioFormat::Float) + ->setFormat(oboe::AudioFormat::I16) ->setSharingMode(oboe::SharingMode::Exclusive) ->setChannelCount(oboe::ChannelCount::Stereo) ->setSampleRate(SYSTEM_SAMPLE_RATE_HZ) @@ -64,7 +64,7 @@ void CSound::closeStream ( oboe::ManagedStream& stream ) oboe::Result requestStopRes = stream->requestStop(); oboe::Result result = stream->close(); - if ( result != oboe::Result::OK ) + if ( result != oboe::Result::OK || requestStopRes != oboe::Result::OK ) { throw CGenErr ( tr ( "Error closing stream: $s", oboe::convertToText ( result ) ) ); @@ -195,7 +195,7 @@ int CSound::Init ( const int iNewPrefMonoBufferSize ) // create memory for intermediate audio buffer vecsTmpInputAudioSndCrdStereo.Init ( iOboeBufferSizeStereo ); - mOutBuffer.reset ( iOboeBufferSizeStereo * RING_FACTOR ); + mOutBuffer.Init ( iOboeBufferSizeStereo * RING_FACTOR ); return iOboeBufferSizeMono; } @@ -249,17 +249,10 @@ oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, v // We're good to start recording now // Take the data from the recording device output buffer and move // it to the vector ready to send up to the server - float* floatData = static_cast ( audioData ); + int16_t* floatData = static_cast ( audioData ); // Copy recording data to internal vector - for ( int frmNum = 0; frmNum < numFrames; ++frmNum ) - { - for ( int channelNum = 0; channelNum < oboeStream->getChannelCount(); channelNum++ ) - { - vecsTmpInputAudioSndCrdStereo[frmNum * oboeStream->getChannelCount() + channelNum] = - static_cast(floatData[frmNum * oboeStream->getChannelCount() + channelNum] * _MAXSHORT); - } - } + memcpy(vecsTmpInputAudioSndCrdStereo.data(),floatData,sizeof(int16_t)*numFrames * oboeStream->getChannelCount()); if ( numFrames != iOboeBufferSizeMono ) { @@ -282,34 +275,14 @@ void CSound::addOutputData(int channel_count) QMutexLocker locker ( &MutexAudioProcessCallback ); // Only copy data if we have data to copy, otherwise fill with silence - if ( !vecsTmpInputAudioSndCrdStereo.empty() ) - { - for ( int frmNum = 0; frmNum < iOboeBufferSizeMono; ++frmNum ) - { - for ( int channelNum = 0; channelNum < channel_count; channelNum++ ) - { - // copy sample received from server into output buffer - - // convert to 32 bit - const int32_t iCurSam = static_cast ( - vecsTmpInputAudioSndCrdStereo[frmNum * channel_count+ channelNum] ); - - mOutBuffer.put ( ( static_cast ( iCurSam ) ) / _MAXSHORT ); - } - } - } - else + if ( vecsTmpInputAudioSndCrdStereo.empty() ) { // prime output stream buffer with silence - for ( int frmNum = 0; frmNum < iOboeBufferSizeMono; ++frmNum ) - { - for ( int channelNum = 0; channelNum < channel_count; channelNum++ ) - { - mOutBuffer.put ( 0 ); - } - } + vecsTmpInputAudioSndCrdStereo.resize(iOboeBufferSizeMono* channel_count, 0); } + mOutBuffer.Put(vecsTmpInputAudioSndCrdStereo, iOboeBufferSizeMono * channel_count); + if ( mOutBuffer.isFull() ) { mStats.ring_overrun++; @@ -326,9 +299,12 @@ oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, QMutexLocker locker ( &MutexAudioProcessCallback ); std::size_t to_write = numFrames*oboeStream->getChannelCount(); - std::size_t count = std::min ( mOutBuffer.size(), to_write ); + std::size_t count = std::min ( (std::size_t)mOutBuffer.GetAvailData(), to_write ); + CVector outBuffer(count); + + mOutBuffer.Get ( outBuffer, count ); - mOutBuffer.get ( (float*) audioData, count ); + memcpy(audioData,outBuffer.data(),count*sizeof(int16_t)); if ( to_write > count ) { @@ -343,14 +319,14 @@ oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, void CSound::onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ) { - qDebug() << "CSound::onErrorAfterClose"; + qDebug() << "CSound::onErrorAfterClose: " << oboe::convertToText(result); } // TODO better handling of stream closing errors void CSound::onErrorBeforeClose ( oboe::AudioStream* oboeStream, oboe::Result result ) { - qDebug() << "CSound::onErrorBeforeClose"; + qDebug() << "CSound::onErrorBeforeClose: " << oboe::convertToText(result); } void CSound::Stats::reset() diff --git a/android/sound.h b/android/sound.h index e16eceb0eb..472c38e073 100644 --- a/android/sound.h +++ b/android/sound.h @@ -29,7 +29,7 @@ #include "global.h" #include #include -#include "ring_buffer.h" +#include "buffer.h" #include /* Classes ********************************************************************/ @@ -70,7 +70,7 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback protected: CVector vecsTmpInputAudioSndCrdStereo; - RingBuffer mOutBuffer; + CBuffer mOutBuffer; int iOboeBufferSizeMono; int iOboeBufferSizeStereo; diff --git a/src/buffer.h b/src/buffer.h index a54d4365a7..9765d0fb84 100755 --- a/src/buffer.h +++ b/src/buffer.h @@ -218,6 +218,9 @@ template class CBuffer return iAvData; } + bool isFull() const { return eBufState == BS_FULL; } + bool isEmpty() const { return eBufState == BS_EMPTY; } + protected: enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; From 75f0692a19d8bac4f4d05e1cf7c3a3052e525d27 Mon Sep 17 00:00:00 2001 From: Julian Santander Date: Sun, 25 Apr 2021 10:50:47 +0200 Subject: [PATCH 2/5] Applied clang-format --- android/sound.cpp | 110 ++++++++++++++++++---------------------------- android/sound.h | 36 +++++++-------- 2 files changed, 58 insertions(+), 88 deletions(-) diff --git a/android/sound.cpp b/android/sound.cpp index afd24c2fec..af10063bd4 100644 --- a/android/sound.cpp +++ b/android/sound.cpp @@ -29,15 +29,11 @@ const uint8_t CSound::RING_FACTOR = 20; -CSound::CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool , - const QString& ) : +CSound::CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* arg ), void* arg, const QString& strMIDISetup, const bool, const QString& ) : CSoundBase ( "Oboe", fpNewProcessCallback, arg, strMIDISetup ) { #ifdef ANDROIDDEBUG - qInstallMessageHandler(myMessageHandler); + qInstallMessageHandler ( myMessageHandler ); #endif } @@ -45,14 +41,13 @@ void CSound::setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ) { // We request EXCLUSIVE mode since this will give us the lowest possible // latency. If EXCLUSIVE mode isn't available the builder will fall back to SHARED mode - builder - ->setFormat(oboe::AudioFormat::I16) - ->setSharingMode(oboe::SharingMode::Exclusive) - ->setChannelCount(oboe::ChannelCount::Stereo) - ->setSampleRate(SYSTEM_SAMPLE_RATE_HZ) - ->setFramesPerCallback(iOboeBufferSizeMono) - ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium) - ->setPerformanceMode(oboe::PerformanceMode::LowLatency); + builder->setFormat ( oboe::AudioFormat::I16 ) + ->setSharingMode ( oboe::SharingMode::Exclusive ) + ->setChannelCount ( oboe::ChannelCount::Stereo ) + ->setSampleRate ( SYSTEM_SAMPLE_RATE_HZ ) + ->setFramesPerCallback ( iOboeBufferSizeMono ) + ->setSampleRateConversionQuality ( oboe::SampleRateConversionQuality::Medium ) + ->setPerformanceMode ( oboe::PerformanceMode::LowLatency ); return; } @@ -66,22 +61,20 @@ void CSound::closeStream ( oboe::ManagedStream& stream ) if ( result != oboe::Result::OK || requestStopRes != oboe::Result::OK ) { - throw CGenErr ( tr ( "Error closing stream: $s", - oboe::convertToText ( result ) ) ); + throw CGenErr ( tr ( "Error closing stream: $s", oboe::convertToText ( result ) ) ); } stream.reset(); } } - void CSound::openStreams() { // Setup output stream oboe::AudioStreamBuilder inBuilder, outBuilder; outBuilder.setDirection ( oboe::Direction::Output ); - outBuilder.setCallback(this); + outBuilder.setCallback ( this ); setupCommonStreamParams ( &outBuilder ); oboe::Result result = outBuilder.openManagedStream ( mPlayStream ); @@ -100,7 +93,7 @@ void CSound::openStreams() // Only set callback for the input direction // the output will be handled writing directly on the stream - inBuilder.setCallback(this); + inBuilder.setCallback ( this ); setupCommonStreamParams ( &inBuilder ); result = inBuilder.openManagedStream ( mRecordingStream ); @@ -120,7 +113,7 @@ void CSound::openStreams() void CSound::printStreamDetails ( oboe::ManagedStream& stream ) { - QString sDirection = ( stream->getDirection()==oboe::Direction::Input ? "Input" : "Output" ); + QString sDirection = ( stream->getDirection() == oboe::Direction::Input ? "Input" : "Output" ); QString sFramesPerBurst = QString::number ( stream->getFramesPerBurst() ); QString sBufferSizeInFrames = QString::number ( stream->getBufferSizeInFrames() ); QString sBytesPerFrame = QString::number ( stream->getBytesPerFrame() ); @@ -130,24 +123,15 @@ void CSound::printStreamDetails ( oboe::ManagedStream& stream ) QString sSharingMode = ( stream->getSharingMode() == oboe::SharingMode::Exclusive ? "Exclusive" : "Shared" ); QString sDeviceID = QString::number ( stream->getDeviceId() ); QString sSampleRate = QString::number ( stream->getSampleRate() ); - QString sAudioFormat = ( stream->getFormat()==oboe::AudioFormat::I16 ? "I16" : "Float" ); + QString sAudioFormat = ( stream->getFormat() == oboe::AudioFormat::I16 ? "I16" : "Float" ); QString sFramesPerCallback = QString::number ( stream->getFramesPerCallback() ); - qInfo() << "Stream details: [sDirection: " << sDirection << - ", FramesPerBurst: " << sFramesPerBurst << - ", BufferSizeInFrames: " << sBufferSizeInFrames << - ", BytesPerFrame: " << sBytesPerFrame << - ", BytesPerSample: " << sBytesPerSample << - ", BufferCapacityInFrames: " << sBufferCapacityInFrames << - ", PerformanceMode: " << sPerformanceMode << - ", SharingMode: " << sSharingMode << - ", DeviceID: " << sDeviceID << - ", SampleRate: " << sSampleRate << - ", AudioFormat: " << sAudioFormat << - ", FramesPerCallback: " << sFramesPerCallback << "]"; + qInfo() << "Stream details: [sDirection: " << sDirection << ", FramesPerBurst: " << sFramesPerBurst << ", BufferSizeInFrames: " << sBufferSizeInFrames + << ", BytesPerFrame: " << sBytesPerFrame << ", BytesPerSample: " << sBytesPerSample << ", BufferCapacityInFrames: " << sBufferCapacityInFrames + << ", PerformanceMode: " << sPerformanceMode << ", SharingMode: " << sSharingMode << ", DeviceID: " << sDeviceID << ", SampleRate: " << sSampleRate + << ", AudioFormat: " << sAudioFormat << ", FramesPerCallback: " << sFramesPerCallback << "]"; } -void CSound::warnIfNotLowLatency ( oboe::ManagedStream& stream, - QString streamName ) +void CSound::warnIfNotLowLatency ( oboe::ManagedStream& stream, QString streamName ) { if ( stream->getPerformanceMode() != oboe::PerformanceMode::LowLatency ) { @@ -203,9 +187,7 @@ int CSound::Init ( const int iNewPrefMonoBufferSize ) // This is the main callback method for when an audio stream is ready to publish data to an output stream // or has received data on an input stream. As per manual much be very careful not to do anything in this back that // can cause delays such as sleeping, file processing, allocate memory, etc. -oboe::DataCallbackResult CSound::onAudioReady ( oboe::AudioStream* oboeStream, - void* audioData, - int32_t numFrames ) +oboe::DataCallbackResult CSound::onAudioReady ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ) { // only process if we are running if ( !bRun ) @@ -220,12 +202,12 @@ oboe::DataCallbackResult CSound::onAudioReady ( oboe::AudioStream* oboeStream, if ( oboeStream == mRecordingStream.get() && audioData ) { - return onAudioInput(oboeStream, audioData, numFrames); + return onAudioInput ( oboeStream, audioData, numFrames ); } - if ( oboeStream == mPlayStream.get() && audioData) + if ( oboeStream == mPlayStream.get() && audioData ) { - return onAudioOutput(oboeStream, audioData, numFrames); + return onAudioOutput ( oboeStream, audioData, numFrames ); } return oboe::DataCallbackResult::Continue; @@ -252,7 +234,7 @@ oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, v int16_t* floatData = static_cast ( audioData ); // Copy recording data to internal vector - memcpy(vecsTmpInputAudioSndCrdStereo.data(),floatData,sizeof(int16_t)*numFrames * oboeStream->getChannelCount()); + memcpy ( vecsTmpInputAudioSndCrdStereo.data(), floatData, sizeof ( int16_t ) * numFrames * oboeStream->getChannelCount() ); if ( numFrames != iOboeBufferSizeMono ) { @@ -262,15 +244,15 @@ oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, v mStats.frames_in += numFrames; // Tell parent class that we've put some data ready to send to the server - ProcessCallback ( vecsTmpInputAudioSndCrdStereo ); + ProcessCallback ( vecsTmpInputAudioSndCrdStereo ); // The callback has placed in the vector the samples to play - addOutputData(oboeStream->getChannelCount()); + addOutputData ( oboeStream->getChannelCount() ); return oboe::DataCallbackResult::Continue; } -void CSound::addOutputData(int channel_count) +void CSound::addOutputData ( int channel_count ) { QMutexLocker locker ( &MutexAudioProcessCallback ); @@ -278,10 +260,10 @@ void CSound::addOutputData(int channel_count) if ( vecsTmpInputAudioSndCrdStereo.empty() ) { // prime output stream buffer with silence - vecsTmpInputAudioSndCrdStereo.resize(iOboeBufferSizeMono* channel_count, 0); + vecsTmpInputAudioSndCrdStereo.resize ( iOboeBufferSizeMono * channel_count, 0 ); } - mOutBuffer.Put(vecsTmpInputAudioSndCrdStereo, iOboeBufferSizeMono * channel_count); + mOutBuffer.Put ( vecsTmpInputAudioSndCrdStereo, iOboeBufferSizeMono * channel_count ); if ( mOutBuffer.isFull() ) { @@ -289,22 +271,20 @@ void CSound::addOutputData(int channel_count) } } -oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, - void* audioData, - int32_t numFrames ) +oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ) { mStats.frames_out += numFrames; mStats.out_callback_calls++; QMutexLocker locker ( &MutexAudioProcessCallback ); - std::size_t to_write = numFrames*oboeStream->getChannelCount(); - std::size_t count = std::min ( (std::size_t)mOutBuffer.GetAvailData(), to_write ); - CVector outBuffer(count); + std::size_t to_write = numFrames * oboeStream->getChannelCount(); + std::size_t count = std::min ( (std::size_t) mOutBuffer.GetAvailData(), to_write ); + CVector outBuffer ( count ); mOutBuffer.Get ( outBuffer, count ); - memcpy(audioData,outBuffer.data(),count*sizeof(int16_t)); + memcpy ( audioData, outBuffer.data(), count * sizeof ( int16_t ) ); if ( to_write > count ) { @@ -316,20 +296,18 @@ oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, } // TODO better handling of stream closing errors -void CSound::onErrorAfterClose ( oboe::AudioStream* oboeStream, - oboe::Result result ) +void CSound::onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ) { - qDebug() << "CSound::onErrorAfterClose: " << oboe::convertToText(result); + qDebug() << "CSound::onErrorAfterClose: " << oboe::convertToText ( result ); } // TODO better handling of stream closing errors -void CSound::onErrorBeforeClose ( oboe::AudioStream* oboeStream, - oboe::Result result ) +void CSound::onErrorBeforeClose ( oboe::AudioStream* oboeStream, oboe::Result result ) { - qDebug() << "CSound::onErrorBeforeClose: " << oboe::convertToText(result); + qDebug() << "CSound::onErrorBeforeClose: " << oboe::convertToText ( result ); } -void CSound::Stats::reset() +void CSound::Stats::reset() { frames_in = 0; frames_out = 0; @@ -339,13 +317,9 @@ void CSound::Stats::reset() ring_overrun = 0; } -void CSound::Stats::log() const +void CSound::Stats::log() const { qDebug() << "Stats: " - << "frames_in: " << frames_in - << ",frames_out: " << frames_out - << ",frames_filled_out: " << frames_filled_out - << ",in_callback_calls: " << in_callback_calls - << ",out_callback_calls: " << out_callback_calls - << ",ring_overrun: " << ring_overrun; + << "frames_in: " << frames_in << ",frames_out: " << frames_out << ",frames_filled_out: " << frames_filled_out + << ",in_callback_calls: " << in_callback_calls << ",out_callback_calls: " << out_callback_calls << ",ring_overrun: " << ring_overrun; } diff --git a/android/sound.h b/android/sound.h index 472c38e073..ae57cad75f 100644 --- a/android/sound.h +++ b/android/sound.h @@ -39,11 +39,7 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback public: static const uint8_t RING_FACTOR; - CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool , - const QString& ); + CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* arg ), void* arg, const QString& strMIDISetup, const bool, const QString& ); virtual ~CSound() {} virtual int Init ( const int iNewPrefMonoBufferSize ); @@ -52,14 +48,14 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback // Call backs for Oboe virtual oboe::DataCallbackResult onAudioReady ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); - virtual void onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ); - virtual void onErrorBeforeClose ( oboe::AudioStream* oboeStream, oboe::Result result ); + virtual void onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ); + virtual void onErrorBeforeClose ( oboe::AudioStream* oboeStream, oboe::Result result ); - struct Stats + struct Stats { Stats() { reset(); } - void reset(); - void log() const; + void reset(); + void log() const; std::size_t frames_in; std::size_t frames_out; std::size_t frames_filled_out; @@ -69,10 +65,10 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback }; protected: - CVector vecsTmpInputAudioSndCrdStereo; - CBuffer mOutBuffer; - int iOboeBufferSizeMono; - int iOboeBufferSizeStereo; + CVector vecsTmpInputAudioSndCrdStereo; + CBuffer mOutBuffer; + int iOboeBufferSizeMono; + int iOboeBufferSizeStereo; private: void setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ); @@ -85,14 +81,14 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback oboe::DataCallbackResult onAudioInput ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); oboe::DataCallbackResult onAudioOutput ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); - void addOutputData(int channel_count); + void addOutputData ( int channel_count ); - oboe::ManagedStream mRecordingStream; - oboe::ManagedStream mPlayStream; + oboe::ManagedStream mRecordingStream; + oboe::ManagedStream mPlayStream; // used to reach a state where the input buffer is // empty and the garbage in the first 500ms or so is discarded - static constexpr int32_t kNumCallbacksToDrain = 10; - int32_t mCountCallbacksToDrain = kNumCallbacksToDrain; - Stats mStats; + static constexpr int32_t kNumCallbacksToDrain = 10; + int32_t mCountCallbacksToDrain = kNumCallbacksToDrain; + Stats mStats; }; From 39943ad2c2b9a3a3fba165ddb69f128154023de6 Mon Sep 17 00:00:00 2001 From: Julian Santander Date: Thu, 24 Jun 2021 17:23:53 +0200 Subject: [PATCH 3/5] fix clang-format --- android/sound.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/android/sound.cpp b/android/sound.cpp index ed1b942512..422e34e9d5 100644 --- a/android/sound.cpp +++ b/android/sound.cpp @@ -46,12 +46,12 @@ void CSound::setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ) // We request EXCLUSIVE mode since this will give us the lowest possible // latency. If EXCLUSIVE mode isn't available the builder will fall back to SHARED mode builder->setFormat ( oboe::AudioFormat::Float ) - ->setSharingMode(oboe::SharingMode::Exclusive) - ->setChannelCount(oboe::ChannelCount::Stereo) - ->setSampleRate(SYSTEM_SAMPLE_RATE_HZ) - ->setFramesPerCallback(iOboeBufferSizeMono) - ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium) - ->setPerformanceMode(oboe::PerformanceMode::LowLatency); + ->setSharingMode ( oboe::SharingMode::Exclusive ) + ->setChannelCount ( oboe::ChannelCount::Stereo ) + ->setSampleRate ( SYSTEM_SAMPLE_RATE_HZ ) + ->setFramesPerCallback ( iOboeBufferSizeMono ) + ->setSampleRateConversionQuality ( oboe::SampleRateConversionQuality::Medium ) + ->setPerformanceMode ( oboe::PerformanceMode::LowLatency ); return; } @@ -283,7 +283,7 @@ oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, QMutexLocker locker ( &MutexAudioProcessCallback ); - std::size_t to_write = numFrames*oboeStream->getChannelCount(); + std::size_t to_write = numFrames * oboeStream->getChannelCount(); std::size_t count = std::min ( (std::size_t) mOutBuffer.GetAvailData(), to_write ); CVector outBuffer ( count ); From c94b2fc72252007700a11ed2503fd3a1d6e4be68 Mon Sep 17 00:00:00 2001 From: Julian Santander Date: Thu, 24 Jun 2021 22:59:41 +0200 Subject: [PATCH 4/5] address review comments --- android/sound.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/android/sound.cpp b/android/sound.cpp index 422e34e9d5..b86404bacd 100644 --- a/android/sound.cpp +++ b/android/sound.cpp @@ -45,7 +45,9 @@ void CSound::setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ) { // We request EXCLUSIVE mode since this will give us the lowest possible // latency. If EXCLUSIVE mode isn't available the builder will fall back to SHARED mode - builder->setFormat ( oboe::AudioFormat::Float ) + + // Setting format to be PCM 16 bits (int16_t) + builder->setFormat ( oboe::AudioFormat::I16 ) ->setSharingMode ( oboe::SharingMode::Exclusive ) ->setChannelCount ( oboe::ChannelCount::Stereo ) ->setSampleRate ( SYSTEM_SAMPLE_RATE_HZ ) @@ -60,10 +62,15 @@ void CSound::closeStream ( oboe::ManagedStream& stream ) { if ( stream ) { - oboe::Result requestStopRes = stream->requestStop(); - oboe::Result result = stream->close(); + oboe::Result result; + result = stream->requestStop(); + if ( oboe::Result::OK != result ) + { + throw CGenErr ( tr ( "Error requesting stream stop: $s", oboe::convertToText ( result ) ) ); + } - if ( result != oboe::Result::OK || requestStopRes != oboe::Result::OK ) + result = stream->close(); + if ( oboe::Result::OK != result ) { throw CGenErr ( tr ( "Error closing stream: $s", oboe::convertToText ( result ) ) ); } @@ -236,6 +243,10 @@ oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, v // We're good to start recording now // Take the data from the recording device output buffer and move // it to the vector ready to send up to the server + // + // According to the format that we've set on initialization, audioData + // is an array of int16_t + // int16_t* floatData = static_cast ( audioData ); // Copy recording data to internal vector @@ -289,6 +300,11 @@ oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, mOutBuffer.Get ( outBuffer, count ); + // + // According to the format that we've set on initialization, audioData + // is an array of int16_t + // + memcpy ( audioData, outBuffer.data(), count * sizeof ( int16_t ) ); if ( to_write > count ) From 75f40cd810898fb6c5e5b4654ab4a17f22d633c5 Mon Sep 17 00:00:00 2001 From: Julian Santander Date: Fri, 25 Jun 2021 18:54:09 +0200 Subject: [PATCH 5/5] rename floatData to intData --- android/sound.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/sound.cpp b/android/sound.cpp index b86404bacd..6d5bde7626 100644 --- a/android/sound.cpp +++ b/android/sound.cpp @@ -247,10 +247,10 @@ oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, v // According to the format that we've set on initialization, audioData // is an array of int16_t // - int16_t* floatData = static_cast ( audioData ); + int16_t* intData = static_cast ( audioData ); // Copy recording data to internal vector - memcpy ( vecsTmpInputAudioSndCrdStereo.data(), floatData, sizeof ( int16_t ) * numFrames * oboeStream->getChannelCount() ); + memcpy ( vecsTmpInputAudioSndCrdStereo.data(), intData, sizeof ( int16_t ) * numFrames * oboeStream->getChannelCount() ); if ( numFrames != iOboeBufferSizeMono ) {