From 9b6ceeb1afc9475e9b013bb5988dd256465532f3 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:10:49 +0200 Subject: [PATCH 001/125] Preparation to start patching - TODOS and directory src/hostapi/Oboe --- .idea/.gitignore | 3 +++ .idea/misc.xml | 6 ++++++ .idea/modules.xml | 8 ++++++++ .idea/portaudio.iml | 9 +++++++++ .idea/vcs.xml | 6 ++++++ README.md | 2 +- include/portaudio.h | 2 ++ src/common/pa_hostapi.h | 2 ++ src/hostapi/skeleton/README.txt | 3 ++- 9 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/portaudio.iml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..26d33521a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..639900d13 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..f2d867481 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/portaudio.iml b/.idea/portaudio.iml new file mode 100644 index 000000000..d6ebd4805 --- /dev/null +++ b/.idea/portaudio.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index cb33144cc..5bbdfbd8b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Please feel free to join. See http://www.portaudio.com for details. src/os = os specific (but host api neutral) code src/hostapi = implementations for different host apis - +[//]: # (TODO: Add Oboe) ### Host API Implementations: src/hostapi/alsa = Advanced Linux Sound Architecture (ALSA) diff --git a/include/portaudio.h b/include/portaudio.h index 9eaeeaedb..daa467c9d 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -273,6 +273,8 @@ PaHostApiIndex Pa_GetDefaultHostApi( void ); @see PaHostApiInfo */ + +//TODO: PaOboe - add paOboe = something typedef enum PaHostApiTypeId { paInDevelopment=0, /* use while developing support for a new host API */ diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h index 4ac3ab60e..696b2879f 100644 --- a/src/common/pa_hostapi.h +++ b/src/common/pa_hostapi.h @@ -153,6 +153,8 @@ are defaulted to 1. #define PA_USE_ASIHPI 1 #endif +//TODO: add PA_USE_OBOE + #ifdef __cplusplus extern "C" { diff --git a/src/hostapi/skeleton/README.txt b/src/hostapi/skeleton/README.txt index 39d4c8d2b..29390cb4f 100644 --- a/src/hostapi/skeleton/README.txt +++ b/src/hostapi/skeleton/README.txt @@ -1 +1,2 @@ -pa_hostapi_skeleton.c provides a starting point for implementing support for a new host API with PortAudio. The idea is that you copy it to a new directory inside /hostapi and start editing. \ No newline at end of file +pa_hostapi_skeleton.c provides a starting point for implementing support for a new host API with PortAudio. +The idea is that you copy it to a new directory inside /hostapi and start editing. \ No newline at end of file From aff3b5a4941b2190efdfe81c49c79665b3fb214b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:40:23 +0200 Subject: [PATCH 002/125] Added src/hostapi/oboe/README.md --- src/hostapi/oboe/README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/hostapi/oboe/README.md diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md new file mode 100644 index 000000000..1cd1f57d4 --- /dev/null +++ b/src/hostapi/oboe/README.md @@ -0,0 +1,38 @@ + +# Portaudio implementation for android using Oboe. + +In order to use this implementation correctly, be sure to include the "portaudio.h" and "pa_oboe.h" +headers in your project. + +Building: +---- +To build portaudio with Oboe, there are some necessary steps: +1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. +2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. + Make sure to correctly link the NDK path in the Oboe build. +3) Set the CMake variable OBOE_DIR to the path of the cloned Oboe repository. + +TODOs: +---- +- Testing. This implementation was non-extensively tested for VoIP calls and blocking streams - for + everything else, it should have a decent structure. + +- Implementing onErrorAfterClose in a way that works, and checking the callback methods. + +Misc +---- +### Latency and Sharing Mode: +Using LowLatency and SharingMode Exclusive is possible, but a function in pa_oboe.h that sets said flags *is yet to be implemented*, so you'll have to manually set those properties in the OboeEngine::OpenStream function. + + +### Audio Format: +If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream by modifying the format selection marked with a *FIXME*. +I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat will not get you anywhere. + + +### Buffer sizes: +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. + + +### Device selection and/or switching mid-stream: +Device selection can be handled by a java/kotlin method that uses getDevices() in order to identify which device to select. Switching mid-stream gives an oboe::ErrorDisconnected result, and you'll have to stop, close and reopen the involved streams with an unspecified device (or a specific device if you know its ID). From 7d2c0c9324198fe6b59e8f227f0a44f2df429fc4 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:52:14 +0200 Subject: [PATCH 003/125] Added include/pa_oboe.h and src/hostapi/oboe/pa_oboe.cpp --- include/pa_oboe.h | 104 ++ src/hostapi/oboe/pa_oboe.cpp | 1899 ++++++++++++++++++++++++++++++++++ 2 files changed, 2003 insertions(+) create mode 100644 include/pa_oboe.h create mode 100644 src/hostapi/oboe/pa_oboe.cpp diff --git a/include/pa_oboe.h b/include/pa_oboe.h new file mode 100644 index 000000000..19b2d0447 --- /dev/null +++ b/include/pa_oboe.h @@ -0,0 +1,104 @@ +/* + * $Id: + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * + **************************************************************************************** + * Author: * + * Carlo Benfatti * + **************************************************************************************** + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#ifndef PA_OBOE_H +#define PA_OBOE_H + +/** + * @file + * @ingroup public_header + * @brief Android Oboe-specific PortAudio API extension header file. + */ + +#include "oboe/Oboe.h" + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +#define TIMEOUT_NS 1000000000 + +/** + * The android stream type and recording preset as defined in Definitions.h + */ +typedef struct PaOboeStreamInfo { + oboe::Usage androidOutputUsage; + oboe::InputPreset androidInputPreset; +} PaOboeStreamInfo; + + +/** + * Provide PA Oboe with the ID of the device the user chose - oboe cannot build a device list, + * but can select the device if provided with its ID. + * @param direction - the direction of the stream for which we want to set the device. + * @param deviceID - the ID of the device chose by the user. + */ +void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); + + +/** + * Provide PA Oboe with native buffer information. If you call this function, you must do so before + * calling Pa_Initialize. To have optimal latency, this function should be called - otherwise, + * PA Oboe will use potentially non-optimal values as default. + * @param bufferSize the native buffersize as returned by AudioManager's + * PROPERTY_OUTPUT_FRAMES_PER_BUFFER. It is recommended you set the number of buffers to 1 if API>17 + * as well, and use the sample rate defined in AudioManager's android.media.property.OUTPUT_SAMPLE_RATE. + * All three together will enable the AUDIO_OUTPUT_FLAG_FAST flag. + */ +void PaOboe_SetNativeBufferSize(unsigned long bufferSize); + +/** + * Provide PA Oboe with native buffer information. If you call this function, you must do so before + * calling Pa_Initialize. To have optimal latency and enable the AUDIO_OUTPUT_FLAG_FAST flag, this + * function should be called - otherwise, PA Oboe will use potentially non-optimal values (2) as default. + * @param buffers The number of buffers can be reduced to 1 on API >17. Make sure you set the native + * buffer size when doing this, and use the sample rate defined in AudioManager's + * android.media.property.OUTPUT_SAMPLE_RATE. + */ +void PaOboe_SetNumberOfBuffers(unsigned buffers); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif //PA_OBOE_H \ No newline at end of file diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp new file mode 100644 index 000000000..9ffc76620 --- /dev/null +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -0,0 +1,1899 @@ +/* + * $Id$ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * + **************************************************************************************** + * Author: * + * Carlo Benfatti * + **************************************************************************************** + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ +#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions" + +/** @file + @ingroup hostapi_src + @brief Oboe implementation of support for a host API. +*/ +#include "pa_allocation.h" +#include "pa_cpuload.h" +#include "pa_debugprint.h" +#include "pa_hostapi.h" +#include "pa_process.h" +#include "pa_stream.h" +#include "pa_unix_util.h" +#include "pa_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "oboe/Oboe.h" + +#include +#include + +#include "pa_oboe.h" + +#define MODULE_NAME "PaOboe" + +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,MODULE_NAME, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,MODULE_NAME, __VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,MODULE_NAME, __VA_ARGS__) + +//TODO: PaOboe_Patch: PaUtil_AllocateZeroInitializedMemory replaces PaUtil_AllocateMemory (same with GoupAllocations) + +// Copied from @{pa_opensles.c}. +#define ENSURE(expr, errorText) \ + do \ + { \ + PaError m_err; \ + if (UNLIKELY((m_err = (expr)) < paNoError)) \ + { \ + PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( \ + __LINE__ ) "\n")); \ + PaUtil_SetLastHostErrorInfo(paInDevelopment, m_err, errorText); \ + m_error = m_err; \ + goto error; \ + } \ + } while (0); + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +static void Terminate(struct PaUtilHostApiRepresentation *hostApi); + +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate); + +static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, + PaStream **s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData); + +static PaError CloseStream(PaStream *stream); + +static PaError StartStream(PaStream *stream); + +static PaError StopStream(PaStream *stream); + +static PaError AbortStream(PaStream *stream); + +static PaError IsStreamStopped(PaStream *s); + +static PaError IsStreamActive(PaStream *stream); + +static PaTime GetStreamTime(PaStream *stream); + +static double GetStreamCpuLoad(PaStream *stream); + +static PaError ReadStream(PaStream *stream, void *buffer, unsigned long frames); + +static PaError WriteStream(PaStream *stream, const void *buffer, unsigned long frames); + +static void StreamProcessingCallback(void *userData); + +static signed long GetStreamReadAvailable(PaStream *stream); + +static signed long GetStreamWriteAvailable(PaStream *stream); + +static unsigned long GetApproximateLowBufferSize(); + +// Commonly used parameters initialized. +static unsigned long nativeBufferSize = 0; +static unsigned numberOfBuffers = 2; + +using namespace oboe; + +int32_t inputDeviceId = kUnspecified; +int32_t outputDeviceId = kUnspecified; + +/** + * Stream structure, useful to store relevant information. It's needed by Portaudio. + */ +typedef struct OboeStream { + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + bool isBlocking; + bool isStopped; + bool isActive; + bool doStop; + bool doAbort; + bool hasOutput; + bool hasInput; + + int callbackResult; + DataCallbackResult oboeCallbackResult; + PaStreamCallbackFlags cbFlags; + + PaSampleFormat inputFormat; + PaSampleFormat outputFormat; + + // Buffers are managed by the callback function in Oboe. + void **outputBuffers; + int currentOutputBuffer; + void **inputBuffers; + int currentInputBuffer; + + long engineAddress; + unsigned long framesPerHostCallback; + unsigned bytesPerFrame; +} OboeStream; + + +/** + * Stream engine of the host API - Oboe. We allocate only one instance of the engine, and + * we call its functions when we want to operate directly on Oboe. More infos on each functions are + * provided right before their implementations. + */ +class OboeEngine : public AudioStreamCallback { +public: + OboeEngine(); + + //Stream-managing functions + bool tryStream(Direction direction, int32_t sampleRate, int32_t channelCount); + PaError openStream(Direction direction, int32_t sampleRate, + Usage outputUsage, InputPreset inputPreset); + bool startStream(); + bool stopStream(); + bool restartStream(int direction); + bool closeStream(); + bool abortStream(); + + //Callback function for non-blocking streams and some callback utils + DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, + int32_t numFrames) override; + void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + void resetCallbackCounters(); + + //Blocking read/write functions + bool writeStream(const void *buffer, int32_t framesToWrite); + bool readStream(void *buffer, int32_t framesToRead); + + //Engine utils + OboeStream* initializeOboeStream(); + void setEngineAddress(long address); + +private: + //The only instance of OboeStream that will be used + OboeStream *oboeStream; + + //The only instances of output and input streams that will be used, and their builders + std::shared_ptr outputStream; + AudioStreamBuilder outputBuilder; + std::shared_ptr inputStream; + AudioStreamBuilder inputBuilder; + + //callback utils + unsigned long framesProcessed{}; + PaStreamCallbackTimeInfo timeInfo{}; + struct timespec timeSpec{}; + + //Conversion utils + static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); + + //device selection implementation + int32_t getSelectedDevice(oboe::Direction direction); +}; + + +/** + * Structure used by Portaudio to interface with the HostApi - in this case, Oboe. + */ +typedef struct PaOboeHostApiRepresentation { + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + OboeEngine *oboeEngine; +} PaOboeHostApiRepresentation; + + +/*----------------------------- OboeEngine functions implementation -----------------------------*/ + + +/** + * \brief Initializes an instance of the engine. + */ +OboeEngine::OboeEngine() { + oboeStream = nullptr; +} + + +/** + * \brief Tries to open a stream with the direction @direction, sample rate @sampleRate and/or + * channel count @channelCount. It then checks if the stream was in fact opened with the + * desired settings, and then closes the stream. It's used to see if the requested + * parameters are supported by the devices that are going to be used. + * @param direction the Direction of the stream; + * @param sampleRate the sample rate we want to try; + * @param channelCount the channel count we want to try; + * @return true if the requested sample rate / channel count is supported by the device, false if + * they aren't, or if tryStream couldn't open a stream. + */ +bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { + AudioStreamBuilder m_builder; + Result m_result; + bool m_outcome = false; + + m_builder.setDeviceId(getSelectedDevice(direction)) + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + ->setFormat(AudioFormat::I16) + ->setDirection(direction) + ->setSampleRate(sampleRate) + ->setChannelCount(channelCount); + if (direction == Direction::Input) { + m_result = m_builder.openStream(inputStream); + } else { + m_result = m_builder.openStream(outputStream); + } + + if (m_result != Result::OK) { + LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", + convertToText(m_result)); + return m_outcome; + } + + if (sampleRate != kUnspecified) { + m_outcome = (sampleRate == m_builder.getSampleRate()); + if(!m_outcome) { + LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", + sampleRate, m_builder.getSampleRate()); + } + } else if (channelCount != kUnspecified) { + m_outcome = (channelCount == m_builder.getChannelCount()); + if(!m_outcome) { + LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", + channelCount, m_builder.getChannelCount()); + } + } else { + LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); + m_outcome = false; + } + + if (direction == Direction::Input) + inputStream->close(); + else + outputStream->close(); + + return m_outcome; +} + + +/** + * \brief Opens an audio stream of oboeStream with a specific direction, sample rate and, + * depending on the direction of the stream, sets its usage (if + * direction == Ditrction::Output) or its preset (if direction == Direction::Input). + * Moreover, this function checks if the stream is blocking, and sets its callback + * function if not. + * @param direction The Oboe::Direction of the stream we want to open; + * @param sampleRate The sample rate of the stream we want to open; + * @param androidOutputUsage The Oboe::Usage of the output stream we want to open + * (only matters with Android Api level >= 28); + * @param androidInputPreset The Preset of the input stream we want to open + * (only matters with Android Api level >= 28). + * @return paNoError if everything goes as expected, paUnanticipatedHostError if Oboe fails to open + * a stream, and paInsufficientMemory if the memory allocation of the buffers fails. + */ +PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, + Usage androidOutputUsage, InputPreset androidInputPreset) { + PaError m_error = paNoError; + Result m_result; + + if (direction == Direction::Input) { + inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) + ->setSampleRate(sampleRate) + ->setDirection(Direction::Input) + ->setDeviceId(getSelectedDevice(Direction::Input)) + ->setInputPreset(androidInputPreset) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); + + if (!(oboeStream->isBlocking)) { + resetCallbackCounters(); + inputBuilder.setDataCallback(this) + ->setPerformanceMode(PerformanceMode::LowLatency); + } + + m_result = inputBuilder.openStream(inputStream); + + if (m_result != Result::OK) { + LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", + convertToText(m_result)); + m_error = paUnanticipatedHostError; + return m_error; + } + + inputStream->setBufferSizeInFrames(inputStream->getFramesPerBurst() * numberOfBuffers); + oboeStream->inputBuffers = + (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + + for (int i = 0; i < numberOfBuffers; ++i) { + oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.inputChannelCount); + + if (!oboeStream->inputBuffers[i]) { + for (int j = 0; j < i; ++j) + PaUtil_FreeMemory(oboeStream->inputBuffers[j]); + PaUtil_FreeMemory(oboeStream->inputBuffers); + inputStream->close(); + m_error = paInsufficientMemory; + break; + } + } + oboeStream->currentInputBuffer = 0; + } else { + outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) + ->setSampleRate(sampleRate) + ->setDirection(Direction::Output) + ->setDeviceId(getSelectedDevice(Direction::Output)) + ->setUsage(androidOutputUsage) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); + + if (!(oboeStream->isBlocking)) { + resetCallbackCounters(); + outputBuilder.setDataCallback(this) + ->setPerformanceMode(PerformanceMode::LowLatency); + } + + m_result = outputBuilder.openStream(outputStream); + if (m_result != Result::OK) { + LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", + convertToText(m_result)); + m_error = paUnanticipatedHostError; + return m_error; + } + + outputStream->setBufferSizeInFrames(outputStream->getFramesPerBurst() * numberOfBuffers); + oboeStream->outputBuffers = + (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + + for (int i = 0; i < numberOfBuffers; ++i) { + oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.outputChannelCount); + + if (!oboeStream->outputBuffers[i]) { + for (int j = 0; j < i; ++j) + PaUtil_FreeMemory(oboeStream->outputBuffers[j]); + PaUtil_FreeMemory(oboeStream->outputBuffers); + outputStream->close(); + m_error = paInsufficientMemory; + break; + } + } + oboeStream->currentOutputBuffer = 0; + } + + return m_error; +} + + +/** + * \brief Starts oboeStream - both input and output audiostreams are checked + * and requested to be started. + * @return true if the streams we wanted to start are started successfully, false otherwise. + */ +bool OboeEngine::startStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if (oboeStream->hasInput) { + m_inputResult = inputStream->requestStart(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", + convertToText(m_inputResult)); + } + if (oboeStream->hasOutput) { + m_outputResult = outputStream->requestStart(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", + convertToText(m_outputResult)); + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Stops oboeStream - both input and output audiostreams are checked + * and requested to be stopped. + * @return true if the streams we wanted to stop are stopped successfully, false otherwise. + */ +bool OboeEngine::stopStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if (oboeStream->hasInput) { + m_inputResult = inputStream->requestStop(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", + convertToText(m_inputResult)); + } + if (oboeStream->hasOutput) { + m_outputResult = outputStream->requestStop(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", + convertToText(m_outputResult)); + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Called when it's needed to restart the oboeStream's audio stream(s), mainly when the + * audio devices change while a stream is started. + * @return true if the stream is restarted successfully, false otherwise. + */ +bool OboeEngine::restartStream(int direction) { + bool m_outcome = true; + Result m_result; + + //TODO: Test if KCTI crashes when ErrorDisconnected occurs + switch (direction) { + case 1: //output-only + //stopping and closing + m_result = outputStream->stop(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", + convertToText(m_result)); + m_result = outputStream->close(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", + convertToText(m_result)); + + //reopening and restarting + m_result = outputBuilder.openStream(outputStream); + if (m_result != Result::OK) + LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", + convertToText(m_result)); + m_result = outputStream->start(); + if (m_result != Result::OK) { + LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", + convertToText(m_result)); + m_outcome = false; + } + break; + + case 2: //input-only + //stopping and closing + m_result = inputStream->stop(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", + convertToText(m_result)); + m_result = inputStream->close(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", + convertToText(m_result)); + + //reopening and restarting + m_result = inputBuilder.openStream(inputStream); + if (m_result != Result::OK) + LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", + convertToText(m_result)); + m_result = inputStream->start(); + if (m_result != Result::OK) { + LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", + convertToText(m_result)); + m_outcome = false; + } + break; + + default: + // unspecified direction or both directions, abort streams + LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); + m_outcome = (restartStream(1) && restartStream(2)); + break; + } + + return m_outcome; +} + + +/** + * \brief Closes oboeStream - both input and output audiostreams are checked + * and closed if active. + * @return true if the stream is closed successfully, otherwise returns false. + */ +bool OboeEngine::closeStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if(oboeStream == nullptr){ + LOGE("[OboeEngine::closeStream]\t Tried to close a NULL stream. Exiting closeStream."); + return false; + } + + if (oboeStream->hasOutput) { + m_outputResult = outputStream->close(); + if (m_outputResult == Result::ErrorClosed) { + m_outputResult = Result::OK; + LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); + } + } + if (oboeStream->hasInput) { + m_inputResult = inputStream->close(); + if (m_inputResult == Result::ErrorClosed) { + m_inputResult = Result::OK; + LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); + } + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Stops oboeStream - both input and output audiostreams are checked and forcefully stopped. + * @return true if the output stream and the input stream are stopped successfully, false otherwise. + */ +bool OboeEngine::abortStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if(oboeStream == nullptr){ + LOGE("[OboeEngine::abortStream]\t Tried to abort a NULL stream. Exiting abortStream."); + return false; + } + + if (oboeStream->hasInput) { + m_inputResult = inputStream->stop(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", + convertToText(m_inputResult)); + m_inputResult = inputStream->close(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", + convertToText(m_inputResult)); + } + if (oboeStream->hasOutput) { + m_outputResult = outputStream->stop(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", + convertToText(m_outputResult)); + m_outputResult = outputStream->close(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", + convertToText(m_outputResult)); + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Oboe's callback routine. FIXME: implement onErrorAfterClose correctly + */ +DataCallbackResult +OboeEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { + + clock_gettime(CLOCK_REALTIME, &timeSpec); + timeInfo.currentTime = (PaTime) (timeSpec.tv_sec + (timeSpec.tv_nsec / 1000000000.0)); + timeInfo.outputBufferDacTime = (PaTime) (oboeStream->framesPerHostCallback + / + oboeStream->streamRepresentation.streamInfo.sampleRate + + timeInfo.currentTime); + timeInfo.inputBufferAdcTime = (PaTime) (oboeStream->framesPerHostCallback + / + oboeStream->streamRepresentation.streamInfo.sampleRate + + timeInfo.currentTime); + + /* check if StopStream or AbortStream was called */ + if (oboeStream->doStop) { + oboeStream->callbackResult = paComplete; + } else if (oboeStream->doAbort) { + oboeStream->callbackResult = paAbort; + } + + PaUtil_BeginCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&oboeStream->bufferProcessor, + &timeInfo, oboeStream->cbFlags); + + if (oboeStream->hasOutput) { + oboeStream->outputBuffers[oboeStream->currentOutputBuffer] = audioData; + PaUtil_SetOutputFrameCount(&oboeStream->bufferProcessor, numFrames); + PaUtil_SetInterleavedOutputChannels(&oboeStream->bufferProcessor, 0, + (void *) ((PaInt16 **) oboeStream->outputBuffers)[oboeStream->currentOutputBuffer], + 0); + } + if (oboeStream->hasInput) { + audioData = oboeStream->inputBuffers[oboeStream->currentInputBuffer]; + PaUtil_SetInputFrameCount(&oboeStream->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&oboeStream->bufferProcessor, 0, + (void *) ((PaInt16 **) oboeStream->inputBuffers)[oboeStream->currentInputBuffer], + 0); + } + + /* continue processing user buffers if cbresult is pacontinue or if cbresult is pacomplete and userbuffers aren't empty yet */ + if (oboeStream->callbackResult == paContinue + || (oboeStream->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&oboeStream->bufferProcessor))) { + framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, + &oboeStream->callbackResult); + } + + /* enqueue a buffer only when there are frames to be processed, + * this will be 0 when paComplete + empty buffers or paAbort + */ + if (framesProcessed > 0) { + if (oboeStream->hasOutput) { + oboeStream->currentOutputBuffer = + (oboeStream->currentOutputBuffer + 1) % numberOfBuffers; + } + if (oboeStream->hasInput) { + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % numberOfBuffers; + } + } + + PaUtil_EndCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer, framesProcessed); + + /* StopStream was called */ + if (framesProcessed == 0 && oboeStream->doStop) { + oboeStream->oboeCallbackResult = DataCallbackResult::Stop; + } + + /* if AbortStream or StopStream weren't called, stop from the cb */ + else if (framesProcessed == 0 && !(oboeStream->doAbort || oboeStream->doStop)) { + oboeStream->isActive = false; + oboeStream->isStopped = true; + if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) + oboeStream->streamRepresentation.streamFinishedCallback( + oboeStream->streamRepresentation.userData); + //oboeStream->oboeCallbackResult = DataCallbackResult::Stop; TODO: Resume this test (onAudioReady) + } + + return oboeStream->oboeCallbackResult; +} + + +/** + * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells + * what error occurred. + */ +void OboeEngine::onErrorAfterClose(AudioStream *audioStream, Result error) { + if (error == oboe::Result::ErrorDisconnected) { + LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + if (!restartStream(0)) + LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); + } + else + LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); +} + + +/** + * \brief Resets callback counters (called at the start of each iteration of onAudioReady + */ +void OboeEngine::resetCallbackCounters() { + framesProcessed = 0; + timeInfo = {0, 0, 0}; +} + + +/** + * \brief Writes frames on the output stream of oboeStream. Used by blocking streams. + * @param buffer The buffer that we want to write on the output stream; + * @param framesToWrite The number of frames that we want to write. + * @return true if the buffer is written correctly, false if the write function returns an error + * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns + * true if the stream is successfully restarted, and false otherwise. + */ +bool OboeEngine::writeStream(const void *buffer, int32_t framesToWrite) { + bool m_outcome = true; + + ResultWithValue m_result = outputStream->write(buffer, framesToWrite, TIMEOUT_NS); + + // If the stream is interrupted because the device suddenly changes, restart the stream. + if (m_result.error() == Result::ErrorDisconnected) { + if (OboeEngine::restartStream(1)) + return true; + } + + if (!m_result) { + LOGE("[OboeEngine::writeStream]\t Error writing stream: %s", convertToText(m_result.error())); + m_outcome = false; + } + return m_outcome; +} + + +/** + * \brief Reads frames from the input stream of oboeStream. Used by blocking streams. + * @param buffer The buffer that we want to read from the input stream; + * @param framesToWrite The number of frames that we want to read. + * @return true if the buffer is read correctly, false if the read function returns an error + * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns + * true if the stream is successfully restarted, and false otherwise. + */ +bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { + bool m_outcome = true; + + ResultWithValue m_result = inputStream->read(buffer, framesToRead, TIMEOUT_NS); + + // If the stream is interrupted because the device suddenly changes, restart the stream. + if (m_result.error() == Result::ErrorDisconnected) { + if (OboeEngine::restartStream(2)) + return true; + } + + if (!m_result) { + LOGE("[OboeEngine::readStream]\t Error reading stream: %s", convertToText(m_result.error())); + m_outcome = false; + } + return m_outcome; +} + + +/** + * \brief Allocates the memory of oboeStream. + * @return the address of the oboeStream. + */ +OboeStream* OboeEngine::initializeOboeStream() { + oboeStream = (OboeStream *) PaUtil_AllocateMemory(sizeof(OboeStream)); + return oboeStream; +} + + +/** + * \brief Sets the engineAddress parameter of oboeStream, useful for recalling the engine whenever + * it's needed. + * @param address the address of the only instance of OboeEngine. + */ +void OboeEngine::setEngineAddress(long address) { + oboeStream->engineAddress = address; +} + +/** + * \brief Converts PaSampleFormat values into Oboe::AudioFormat values. + * @param paFormat the PaSampleFormat we want to convert. + * @return the converted AudioFormat. + */ +AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { + AudioFormat m_oboeFormat; + switch (paFormat) { + case paFloat32: + m_oboeFormat = AudioFormat::Float; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); + break; + case paInt16: + m_oboeFormat = AudioFormat::I16; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); + break; + case paInt32: + m_oboeFormat = AudioFormat::I32; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); + break; + case paInt24: + m_oboeFormat = AudioFormat::I24; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); + break; + default: + m_oboeFormat = AudioFormat::Unspecified; + LOGW("[OboeEngine::PaToOboeFormat]\t Setting AudioFormat to Unspecified, because Oboe does not support the requested format."); + break; + } + return m_oboeFormat; +} + + +/** + * \brief Function used to implement device selection. Device Ids are kUnspecified by default, but + * can be set to something different via JNI using the function PaOboe_SetSelectedDevice. + * @param direction the Oboe::Direction for which we want to know the device Id. + * @return the device Id of the appropriate direction. + */ +int32_t OboeEngine::getSelectedDevice(Direction direction) { + if (direction == Direction::Input) + return inputDeviceId; + else + return outputDeviceId; +} + + + +/*----------------------------- PaSkeleton functions implementations -----------------------------*/ + +/** + * \brief Checks if the requested sample rate is supported by the output device using + * OboeEngine::tryStream. + * This function is used by PaOboe_Initialize, IsFormatSupported, and OpenStream. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param sampleRate is the sample rate we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample + * rate was changed by Oboe. + */ +PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, + sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); + + /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the + sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Checks if the requested sample rate is supported by the input device using + * OboeEngine::tryStream. + * This function is used by PaOboe_Initialize, IsFormatSupported, and OpenStream. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param sampleRate is the sample rate we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample + * rate was changed by Oboe. + */ +PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, + sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); + + /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the + sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Checks if the requested channel count is supported by the output device using + * OboeEngine::tryStream. Used by PaOboe_Initialize. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param numOfChannels the number of channels we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel + * count was changed by Oboe. + */ +static PaError IsOutputChannelCountSupported( + PaOboeHostApiRepresentation *oboeHostApi, + int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { + LOGE("[PaOboe - IsOutputChannelCountSupported]\t Invalid channel count."); + return paInvalidChannelCount; + } + + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, + kUnspecified, + numOfChannels))) + LOGW("[PaOboe - IsOutputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); + + /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if + the sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Checks if the requested channel count is supported by the input device using + * OboeEngine::tryStream. Used by PaOboe_Initialize. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param numOfChannels the number of channels we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel + * count was changed by Oboe. + */ +static PaError IsInputChannelCountSupported( + PaOboeHostApiRepresentation *oboeHostApi, + int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { + LOGE("[PaOboe - IsInputChannelCountSupported]\t Invalid channel count."); + return paInvalidChannelCount; + } + + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, + kUnspecified, + numOfChannels))) + LOGW("[PaOboe - IsInputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); + + /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if + the sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Initializes common parameters and the OboeEngine, and allocates the memory necessary to + * start the audio streams. + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"); + * @param hostApiIndex is a PaHostApiIndex, the type used to enumerate the host APIs at runtime. + * @return paNoError if no errors occur, or paInsufficientMemory if memory allocation fails; + */ +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) { + PaError m_result = paNoError; + int m_deviceCount; + PaOboeHostApiRepresentation *m_oboeHostApi; + PaDeviceInfo *m_deviceInfoArray; + char *m_deviceName; + + m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateMemory( + sizeof(PaOboeHostApiRepresentation)); + if (!m_oboeHostApi) { + m_result = paInsufficientMemory; + goto error; + } + + m_oboeHostApi->oboeEngine = new OboeEngine(); + + m_oboeHostApi->allocations = PaUtil_CreateAllocationGroup(); + if (!m_oboeHostApi->allocations) { + m_result = paInsufficientMemory; + goto error; + } + + *hostApi = &m_oboeHostApi->inheritedHostApiRep; + // Initialization of infos. + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paInDevelopment; + (*hostApi)->info.name = "android Oboe"; + (*hostApi)->info.defaultOutputDevice = 0; + (*hostApi)->info.defaultInputDevice = 0; + (*hostApi)->info.deviceCount = 0; + + + m_deviceCount = 1; + (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateMemory( + m_oboeHostApi->allocations, sizeof(PaDeviceInfo *) * m_deviceCount); + + if (!(*hostApi)->deviceInfos) { + m_result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateMemory( + m_oboeHostApi->allocations, sizeof(PaDeviceInfo) * m_deviceCount); + if (!m_deviceInfoArray) { + m_result = paInsufficientMemory; + goto error; + } + + for (int i = 0; i < m_deviceCount; ++i) { + PaDeviceInfo *m_deviceInfo = &m_deviceInfoArray[i]; + m_deviceInfo->structVersion = 2; + m_deviceInfo->hostApi = hostApiIndex; + + /* OboeEngine will handle manual device selection through the use of + PaOboe_SetSelectedDevice via a JNI interface that can be implemented. + Portaudio doesn't need to know about this, so we just use a default device. */ + + m_deviceInfo->name = "default"; + + /* Try channels in order of preference - Stereo > Mono. */ + const int32_t m_channelsToTry[] = {2, 1}; + const int32_t m_channelsToTryLength = 2; + + m_deviceInfo->maxOutputChannels = 0; + m_deviceInfo->maxInputChannels = 0; + + for (i = 0; i < m_channelsToTryLength; ++i) { + if (IsOutputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { + m_deviceInfo->maxOutputChannels = m_channelsToTry[i]; + break; + } + } + for (i = 0; i < m_channelsToTryLength; ++i) { + if (IsInputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { + m_deviceInfo->maxInputChannels = m_channelsToTry[i]; + break; + } + } + + /* check sample rates in order of preference */ + const int32_t m_sampleRates[] = {48000, 44100, 32000, 24000, 16000}; + const int32_t m_numberOfSampleRates = 5; + + m_deviceInfo->defaultSampleRate = m_sampleRates[0]; + + for (i = 0; i < m_numberOfSampleRates; ++i) { + if (IsOutputSampleRateSupported( + m_oboeHostApi, m_sampleRates[i]) == paNoError && + IsInputSampleRateSupported( + m_oboeHostApi, m_sampleRates[i]) == paNoError) { + m_deviceInfo->defaultSampleRate = m_sampleRates[i]; + break; + } + } + if (m_deviceInfo->defaultSampleRate == 0) + goto error; + + /* If the user has set nativeBufferSize by querying the optimal buffer size via java, + use the user-defined value since that will offer the lowest possible latency. */ + + if (nativeBufferSize != 0) { + m_deviceInfo->defaultLowInputLatency = + (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultLowOutputLatency = + (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighInputLatency = + (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighOutputLatency = + (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; + } else { + m_deviceInfo->defaultLowInputLatency = + (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultLowOutputLatency = + (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighInputLatency = + (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighOutputLatency = + (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; + } + + (*hostApi)->deviceInfos[i] = m_deviceInfo; + ++(*hostApi)->info.deviceCount; + } + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface(&m_oboeHostApi->callbackStreamInterface, + CloseStream, StartStream, StopStream, + AbortStream, IsStreamStopped, + IsStreamActive, GetStreamTime, + GetStreamCpuLoad, PaUtil_DummyRead, + PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, + PaUtil_DummyGetWriteAvailable); + + PaUtil_InitializeStreamInterface(&m_oboeHostApi->blockingStreamInterface, + CloseStream, StartStream, StopStream, + AbortStream, IsStreamStopped, + IsStreamActive, GetStreamTime, + PaUtil_DummyGetCpuLoad, ReadStream, + WriteStream, GetStreamReadAvailable, + GetStreamWriteAvailable); + + if (m_result == paNoError) + LOGV("[PaOboe - Initialize]\t Oboe host API successfully initialized"); + else + LOGE("[PaOboe - Initialize]\t An unusual error occurred. Error code: %d", m_result); + return m_result; + + error: + if (m_oboeHostApi) { + if (m_oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + } + + PaUtil_FreeMemory(m_oboeHostApi); + } + LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", m_result); + return m_result; +} + + +/** + * \brief Interrupts the stream and frees the memory that was allocated to sustain the stream. + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"). + */ +static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { + auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; + + if (!(m_oboeHostApi->oboeEngine->closeStream())) + LOGW("[PaOboe - Terminate]\t Couldn't close the streams correctly - see OboeEngine::CloseStream logs."); + + if(m_oboeHostApi->oboeEngine != nullptr) + delete m_oboeHostApi->oboeEngine; + + if (m_oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + } + + PaUtil_FreeMemory(m_oboeHostApi); +} + + +/** + * \brief Checks if the initialized values are supported by the selected device(s). + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"); + * @param inputParameters points towards the parameters given to the input stream; + * @param outputParameters points towards the parameters given to the output stream; + * @param sampleRate is the value of the sample rate we want to check if it's supported. + * @return paNoError (== paFormatIsSupported) if no errors occur, otherwise returns an appropriate + * PaError message. + */ +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate) { + PaError m_outcome; + int m_inputChannelCount, m_outputChannelCount; + PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; + auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; + + if (inputParameters) { + m_inputChannelCount = inputParameters->channelCount; + m_inputSampleFormat = inputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if (m_inputSampleFormat & paCustomFormat) { + m_outcome = paSampleFormatNotSupported; + return m_outcome; + } + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) { + m_outcome = paInvalidDevice; + return m_outcome; + } + + /* check that input device can support inputChannelCount */ + if (m_inputChannelCount > + hostApi->deviceInfos[inputParameters->device]->maxInputChannels) { + m_outcome = paInvalidChannelCount; + return m_outcome; + } + + /* validate inputStreamInfo */ + if (inputParameters->hostApiSpecificStreamInfo) { + // Only has an effect on ANDROID_API>=28. + InputPreset m_androidRecordingPreset = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (m_androidRecordingPreset != InputPreset::Generic && + m_androidRecordingPreset != InputPreset::Camcorder && + m_androidRecordingPreset != InputPreset::VoiceRecognition && + m_androidRecordingPreset != InputPreset::VoiceCommunication + // Should I add compatibility with VoicePerformance? + ) { + m_outcome = paIncompatibleHostApiSpecificStreamInfo; + return m_outcome; + } + } + } else { + m_inputChannelCount = 0; + } + + if (outputParameters) { + m_outputChannelCount = outputParameters->channelCount; + m_outputSampleFormat = outputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if (m_outputSampleFormat & paCustomFormat) { + m_outcome = paSampleFormatNotSupported; + return m_outcome; + } + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) { + m_outcome = paInvalidDevice; + return m_outcome; + } + + /* check that output device can support outputChannelCount */ + if (m_outputChannelCount > + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) { + m_outcome = paInvalidChannelCount; + return m_outcome; + } + + /* validate outputStreamInfo */ + if (outputParameters->hostApiSpecificStreamInfo) { + // Only has an effect on ANDROID_API>=28. + Usage m_androidOutputUsage = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (m_androidOutputUsage != Usage::Media && + m_androidOutputUsage != Usage::Notification && + m_androidOutputUsage != Usage::NotificationEvent && + m_androidOutputUsage != Usage::NotificationRingtone && + m_androidOutputUsage != Usage::VoiceCommunication && + m_androidOutputUsage != Usage::VoiceCommunicationSignalling && + m_androidOutputUsage != Usage::Alarm + // See if more are needed. + ) { + m_outcome = paIncompatibleHostApiSpecificStreamInfo; + return m_outcome; + } + } + } else { + m_outputChannelCount = 0; + } + + if (m_outputChannelCount > 0) { + if (IsOutputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { + m_outcome = paInvalidSampleRate; + return m_outcome; + } + } + if (m_inputChannelCount > 0) { + if (IsInputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { + m_outcome = paInvalidSampleRate; + return m_outcome; + } + } + + return paFormatIsSupported; +} + + +/** + * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param androidOutputUsage is an attribute that expresses why we are opening the output stream. + * This information can be used by certain platforms to make more refined volume or + * routing decisions. It only has an effect on Android API 28+. + * @param sampleRate is the sample rate we want for the audio stream we want to initialize. This is used to allocate + * the correct amount of memory. + * @return the value returned by OboeEngine::openStream. + */ +static PaError InitializeOutputStream(PaOboeHostApiRepresentation *oboeHostApi, + Usage androidOutputUsage, double sampleRate) { + + return oboeHostApi->oboeEngine->openStream(Direction::Output, + sampleRate, + androidOutputUsage, + Generic //Won't be used, so we put the default value. + ); +} + + +/** + * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param androidInputPreset is an attribute that defines the audio source. This information + * defines both a default physical source of audio signal, and a recording configuration. + * It only has an effect on Android API 28+. + * @param sampleRate is the sample rate we want for the audio stream we want to initialize. This is used to allocate + * the correct amount of memory. + * @return the value returned by OboeEngine::openStream. + */ +static PaError InitializeInputStream(PaOboeHostApiRepresentation *oboeHostApi, + InputPreset androidInputPreset, double sampleRate) { + + return oboeHostApi->oboeEngine->openStream(Direction::Input, + sampleRate, + Usage::Media, //Won't be used, so we put the default value. + androidInputPreset + ); +} + + +/** + * \brief Opens the portaudio audio stream - while initializing our OboeStream. + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"); + * @param s points to a pointer to a PaStream, which is an audio stream structure used and built + * by portaudio, which will hold the information of our OboeStream; + * @param inputParameters points towards the parameters given to the input stream; + * @param outputParameters points towards the parameters given to the output stream; + * @param sampleRate the sample rate we want for our stream; + * @param framesPerBuffer the number of frames per buffer we want for our stream; + * @param streamFlags the flags used to control the behavior of a stream; + * @param streamCallback points to a callback function that allows a non-blocking stream to + * receive or transmit data; + * @param userData stores the user data, and is passed to some PaUtil functions without further + * manipulation or checks. + * @return paNoError if no errors occur, or other error codes accordingly with what goes wrong. +*/ +static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, + PaStream **s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData) { + PaError m_error = paNoError; + auto m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; + unsigned long m_framesPerHostBuffer; /* these may not be equivalent for all implementations */ + int m_inputChannelCount, m_outputChannelCount; + PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; + PaSampleFormat m_hostInputSampleFormat, m_hostOutputSampleFormat; + + Usage m_androidOutputUsage = Usage::VoiceCommunication; + InputPreset m_androidInputPreset = InputPreset::Generic; + + OboeStream *m_oboeStream = m_oboeHostApi->oboeEngine->initializeOboeStream(); + + if (!m_oboeStream) { + m_error = paInsufficientMemory; + goto error; + } + + LOGI("[PaOboe - OpenStream]\t OpenStream called."); + + if (inputParameters) { + m_inputChannelCount = inputParameters->channelCount; + m_inputSampleFormat = inputParameters->sampleFormat; + + /* Oboe supports alternate device specification with API>=28, but for now we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default.*/ + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if (m_inputChannelCount > hostApi->deviceInfos[inputParameters->device]->maxInputChannels) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if (inputParameters->hostApiSpecificStreamInfo) { + // Only has an effect on ANDROID_API>=28. + m_androidInputPreset = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (m_androidInputPreset != InputPreset::Generic && + m_androidInputPreset != InputPreset::Camcorder && + m_androidInputPreset != InputPreset::VoiceRecognition && + m_androidInputPreset != InputPreset::VoiceCommunication + // Should I add compatibility with VoicePerformance? + ) + return paIncompatibleHostApiSpecificStreamInfo; + } + /* FIXME: Replace "paInt16" with whatever format you prefer - + * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( + paInt16, m_inputSampleFormat); + m_oboeStream->inputFormat = m_hostInputSampleFormat; + } else { + m_inputChannelCount = 0; + m_inputSampleFormat = m_hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ + m_oboeStream->inputFormat = m_hostInputSampleFormat; + } + + if (outputParameters) { + m_outputChannelCount = outputParameters->channelCount; + m_outputSampleFormat = outputParameters->sampleFormat; + + /* Oboe supports alternate device specification with API>=28, but for now we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default.*/ + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) + return paInvalidDevice; + + /* check that output device can support outputChannelCount */ + if (m_outputChannelCount > + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if (outputParameters->hostApiSpecificStreamInfo) { + m_androidOutputUsage = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (m_androidOutputUsage != Usage::Media && + m_androidOutputUsage != Usage::Notification && + m_androidOutputUsage != Usage::NotificationEvent && + m_androidOutputUsage != Usage::NotificationRingtone && + m_androidOutputUsage != Usage::VoiceCommunication && + m_androidOutputUsage != Usage::VoiceCommunicationSignalling && + m_androidOutputUsage != Usage::Alarm + // See if more are needed. + ) + return paIncompatibleHostApiSpecificStreamInfo; + } + /* FIXME: Replace "paInt16" with whatever format you prefer - + * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( + paInt16, m_outputSampleFormat); + m_oboeStream->outputFormat = m_hostOutputSampleFormat; + } else { + m_outputChannelCount = 0; + m_outputSampleFormat = m_hostOutputSampleFormat = paInt16; + m_oboeStream->outputFormat = m_hostOutputSampleFormat; + } + + /* validate platform specific flags */ + if ((streamFlags & paPlatformSpecificFlags) != 0) + return paInvalidFlag; /* unexpected platform specific flag */ + + if (framesPerBuffer == paFramesPerBufferUnspecified) { + if (outputParameters) { + m_framesPerHostBuffer = + (unsigned long) (outputParameters->suggestedLatency * sampleRate); + } else { + m_framesPerHostBuffer = + (unsigned long) (inputParameters->suggestedLatency * sampleRate); + } + } else { + m_framesPerHostBuffer = framesPerBuffer; + } + + m_oboeHostApi->oboeEngine->setEngineAddress( + reinterpret_cast(m_oboeHostApi->oboeEngine)); + + if (streamCallback) { + PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), + &m_oboeHostApi->callbackStreamInterface, + streamCallback, userData); + } else { + PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), + &m_oboeHostApi->blockingStreamInterface, + streamCallback, userData); + } + + PaUtil_InitializeCpuLoadMeasurer(&(m_oboeStream->cpuLoadMeasurer), sampleRate); + + m_error = PaUtil_InitializeBufferProcessor(&(m_oboeStream->bufferProcessor), + m_inputChannelCount, + m_inputSampleFormat, + m_hostInputSampleFormat, + m_outputChannelCount, + m_outputSampleFormat, + m_hostOutputSampleFormat, + sampleRate, streamFlags, + framesPerBuffer, + m_framesPerHostBuffer, + paUtilFixedHostBufferSize, + streamCallback, userData); + if (m_error != paNoError) + goto error; + + m_oboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; + m_oboeStream->isBlocking = (streamCallback == nullptr); + m_oboeStream->framesPerHostCallback = m_framesPerHostBuffer; + m_oboeStream->bytesPerFrame = sizeof(int16_t); + m_oboeStream->cbFlags = 0; + m_oboeStream->isStopped = true; + m_oboeStream->isActive = false; + + if (!(m_oboeStream->isBlocking)) {} +// PaUnixThreading_Initialize(); + + if (m_inputChannelCount > 0) { + m_oboeStream->hasInput = true; + m_oboeStream->streamRepresentation.streamInfo.inputLatency = + ((PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( + &(m_oboeStream->bufferProcessor)) + + m_oboeStream->framesPerHostCallback) / sampleRate; + ENSURE(InitializeInputStream(m_oboeHostApi, + m_androidInputPreset, sampleRate), + "Initializing inputstream failed") + } else { m_oboeStream->hasInput = false; } + + if (m_outputChannelCount > 0) { + m_oboeStream->hasOutput = true; + m_oboeStream->streamRepresentation.streamInfo.outputLatency = + ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( + &m_oboeStream->bufferProcessor) + + m_oboeStream->framesPerHostCallback) / sampleRate; + ENSURE(InitializeOutputStream(m_oboeHostApi, + m_androidOutputUsage, sampleRate), + "Initializing outputstream failed"); + } else { m_oboeStream->hasOutput = false; } + + *s = (PaStream *) m_oboeStream; + return m_error; + + error: + if (m_oboeStream) + PaUtil_FreeMemory(m_oboeStream); + + LOGE("[PaOboe - OpenStream]\t Error opening stream(s). Error code: %d", m_error); + + return m_error; +} + + +/** + * \brief Calls OboeEngine::closeStream, and then frees the memory that was allocated to sustain + * the stream(s). When CloseStream() is called, the multi-api layer ensures that the stream + * has already been stopped or aborted. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. + */ +static PaError CloseStream(PaStream *s) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + + if (!(m_oboeEngine->closeStream())) + LOGW("[PaOboe - CloseStream]\t Couldn't close the stream(s) correctly - see OboeEngine::CloseStream logs."); + + PaUtil_TerminateBufferProcessor(&m_stream->bufferProcessor); + PaUtil_TerminateStreamRepresentation(&m_stream->streamRepresentation); + + for (int i = 0; i < numberOfBuffers; ++i) { + if (m_stream->hasOutput) + PaUtil_FreeMemory(m_stream->outputBuffers[i]); + if (m_stream->hasInput) + PaUtil_FreeMemory(m_stream->inputBuffers[i]); + } + + if (m_stream->hasOutput) + PaUtil_FreeMemory(m_stream->outputBuffers); + if (m_stream->hasInput) + PaUtil_FreeMemory(m_stream->inputBuffers); + + PaUtil_FreeMemory(m_stream); + return paNoError; +} + + +/** + * \brief Allocates the memory of the buffers necessary to start a stream, both for output and + * input, then calls OboeEngine::startStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::startStream fails. + */ +static PaError StartStream(PaStream *s) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + + PaUtil_ResetBufferProcessor(&m_stream->bufferProcessor); + + //Checking if the stream(s) are already active. TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) + if (m_stream->isActive) { + LOGW("[PaOboe - StartStream]\t Stream was already active, stopping..."); + StopStream(s); + LOGW("[PaOboe - StartStream]\t Restarting..."); + StartStream(s); + } + + m_stream->currentOutputBuffer = 0; + m_stream->currentInputBuffer = 0; + + /* Initialize buffers */ + for (int i = 0; i < numberOfBuffers; ++i) { + if (m_stream->hasOutput) { + memset(m_stream->outputBuffers[m_stream->currentOutputBuffer], 0, + m_stream->framesPerHostCallback * m_stream->bytesPerFrame * + m_stream->bufferProcessor.outputChannelCount + ); + m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; + } + if (m_stream->hasInput) { + memset(m_stream->inputBuffers[m_stream->currentInputBuffer], 0, + m_stream->framesPerHostCallback * m_stream->bytesPerFrame * + m_stream->bufferProcessor.inputChannelCount + ); + m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; + } + } + + /* Start the processing thread.*/ + if (!m_stream->isBlocking) { + m_stream->callbackResult = paContinue; + m_stream->oboeCallbackResult = DataCallbackResult::Continue; + } + + m_stream->isStopped = false; + m_stream->isActive = true; + m_stream->doStop = false; + m_stream->doAbort = false; + + if (!(m_oboeEngine->startStream())) + return paUnanticipatedHostError; + else + return paNoError; +} + + +/** + * \brief Ends the stream callback, if the stream is not blocking, and calls + * OboeEngine::stopStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::stopStream fails. + */ +static PaError StopStream(PaStream *s) { + PaError m_error = paNoError; + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + + if (m_stream->isStopped) { + LOGW("[PaOboe - StopStream]\t Stream was already stopped."); + } else { + if (!(m_stream->isBlocking)) { + m_stream->doStop = true; + } + if (!(m_oboeEngine->stopStream())) { + LOGE("[PaOboe - StopStream]\t Couldn't stop the stream(s) correctly - see OboeEngine::StopStream logs."); + m_error = paUnanticipatedHostError; + } + + m_stream->isActive = false; + m_stream->isStopped = true; + if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) + m_stream->streamRepresentation.streamFinishedCallback( + m_stream->streamRepresentation.userData); + } + + return m_error; +} + + +/** + * \brief Aborts the stream callback, if the stream is not blocking, and calls + * OboeEngine::abortStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::abortStream fails. + */ +static PaError AbortStream(PaStream *s) { + PaError m_error = paNoError; + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + LOGI("[PaOboe - AbortStream]\t Aborting stream."); + + if (!m_stream->isBlocking) { + m_stream->doAbort = true; + } + + /* stop immediately so enqueue has no effect */ + if (!(m_oboeEngine->abortStream())) { + LOGE("[PaOboe - AbortStream]\t Couldn't abort the stream - see OboeEngine::abortStream logs."); + m_error = paUnanticipatedHostError; + } + + m_stream->isActive = false; + m_stream->isStopped = true; + if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) + m_stream->streamRepresentation.streamFinishedCallback( + m_stream->streamRepresentation.userData); + + return m_error; +} + + +/** + * \brief Copies an input stream buffer by buffer, and calls OboeEngine::readStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream; + * @param buffer is the address of the first sample of the buffer; + * @param frames is the total number of frames to read. + * @return paInternalError if OboeEngine::readStream fails, paNoError otherwise. + */ +static PaError ReadStream(PaStream *s, void *buffer, unsigned long frames) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + void *m_userBuffer = buffer; + unsigned m_framesToRead; + PaError m_error = paNoError; + + while (frames > 0) { + m_framesToRead = PA_MIN(m_stream->framesPerHostCallback, frames); + + if (!(m_oboeEngine->readStream(m_userBuffer, + m_framesToRead * + m_stream->bufferProcessor.inputChannelCount))) + m_error = paInternalError; + + m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; + frames -= m_framesToRead; + } + + return m_error; +} + + +/** + * \brief Copies an output stream buffer by buffer, and calls OboeEngine::writeStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream; + * @param buffer is the address of the first sample of the buffer; + * @param frames is the total number of frames to write. + * @return paInternalError if OboeEngine::writeStream fails, paNoError otherwise. + */ +static PaError WriteStream(PaStream *s, const void *buffer, unsigned long frames) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + const void *m_userBuffer = buffer; + unsigned m_framesToWrite; + PaError m_error = paNoError; + + while (frames > 0) { + m_framesToWrite = PA_MIN(m_stream->framesPerHostCallback, frames); + + if (!(m_oboeEngine->writeStream(m_userBuffer, + m_framesToWrite * + m_stream->bufferProcessor.outputChannelCount))) + m_error = paInternalError; + + m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; + frames -= m_framesToWrite; + } + + return m_error; +} + + +/*-------------------------------- PaSkeleton Secondary Functions --------------------------------*/ + +/** + * \brief Function needed by portaudio to understand how many frames can be read without waiting. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return the minimum number of frames that can be read without waiting. + */ +static signed long GetStreamReadAvailable(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentInputBuffer); +} + + +/** + * \brief Function needed by portaudio to understand how many frames can be written without waiting. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return the minimum number of frames that can be written without waiting. + */ +static signed long GetStreamWriteAvailable(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentOutputBuffer); +} + + +/** + * \brief Function needed by portaudio to understand if the stream is stopped. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return one (1) when the stream is stopped, or zero (0) when the stream is running. + */ +static PaError IsStreamStopped(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->isStopped; +} + + +/** + * \brief Function needed by portaudio to understand if the stream is active. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return one (1) when the stream is active (ie playing or recording audio), or zero (0) otherwise. + */ +static PaError IsStreamActive(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->isActive; +} + + +/** + * \brief Function needed by portaudio to get the stream time in seconds. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return The stream's current time in seconds, or 0 if an error occurred. + */ +static PaTime GetStreamTime(PaStream *s) { + return PaUtil_GetTime(); +} + + +/** + * \brief Function needed by portaudio to retrieve CPU usage information for the specified stream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return A floating point value, typically between 0.0 and 1.0, where 1.0 indicates that the + * stream callback is consuming the maximum number of CPU cycles possible to maintain + * real-time operation. A value of 0.5 would imply that PortAudio and the stream callback + * was consuming roughly 50% of the available CPU time. The return value may exceed 1.0. + * A value of 0.0 will always be returned for a blocking read/write stream, or if an error + * occurs. + */ +static double GetStreamCpuLoad(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return PaUtil_GetCpuLoad(&m_stream->cpuLoadMeasurer); +} + + +/*----------------------------------- PaOboe Utility Functions -----------------------------------*/ + +/** + * \brief In case that no buffer size was specifically set via PaOboe_setNativeBufferSize, this + * function is called to get a sensible value for the buffer size. + * @return 256 for Android API Level <= 23, 192 otherwise. + */ +static unsigned long GetApproximateLowBufferSize() { +/* FIXME: This function should return the following commented values, but was changed in order to improve + compatibility with KCTI for android. Please use the commented values in normal conditions. */ + +// if (__ANDROID_API__ <= 23) +// return 256; +// else +// return 192; + + return 1024; +} + + +/*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ + +void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { + LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); + if (direction == Direction::Input) + inputDeviceId = deviceID; + else + outputDeviceId = deviceID; +} + + +void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { + nativeBufferSize = bufferSize; +} + + +void PaOboe_SetNumberOfBuffers(unsigned buffers) { + numberOfBuffers = buffers; +} \ No newline at end of file From 8ab4dd58d7b5283c56058bba284ca4bdbe04504d Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:58:20 +0200 Subject: [PATCH 004/125] Added PA_USE_OBOE section to CMakeLists.txt --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 194a92a36..7f4036c97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -239,6 +239,16 @@ if(WIN32) endif() endif() + option(PA_USE_OBOE "Enable support for Oboe" ON) + if(PA_USE_OBOE) + target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + target_link_libraries(PortAudio PRIVATE oboe) + endif() + + option(PA_USE_WMME "Enable support for WMME" ON) if(PA_USE_WMME) target_sources(PortAudio PRIVATE src/hostapi/wmme/pa_win_wmme.c) From 3aed6feb6081fc7022fd922d4b13b478990b7397 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 13:00:51 +0200 Subject: [PATCH 005/125] Added PA_USE_OBOE section to CMakeLists.txt --- CMakeLists.txt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f4036c97..4959bf12a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -239,16 +239,6 @@ if(WIN32) endif() endif() - option(PA_USE_OBOE "Enable support for Oboe" ON) - if(PA_USE_OBOE) - target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) - set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) - target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") - target_link_libraries(PortAudio PRIVATE oboe) - endif() - - option(PA_USE_WMME "Enable support for WMME" ON) if(PA_USE_WMME) target_sources(PortAudio PRIVATE src/hostapi/wmme/pa_win_wmme.c) @@ -357,6 +347,15 @@ elseif(UNIX) endif() endif() + option(PA_USE_OBOE "Enable support for Oboe" ON) + if(PA_USE_OBOE) + target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + target_link_libraries(PortAudio PRIVATE oboe) + endif() + # OSS is intentionally off by default to avoid confusing users of PortAudio # applications. OSS builds but there are no devices available on modern # Linux systems. From f5a89e1f549d32ec61ae64920c6c1402de030a5f Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 9 Jun 2023 13:21:02 +0200 Subject: [PATCH 006/125] Heavily reworked CMake dependencies, added FindOboe.cmake, updated pa_oboe.cpp, more work needed --- .github/workflows/MSBuild.yml | 96 ------------------ .github/workflows/autotools.yml | 20 ---- .github/workflows/cmake.yml | 128 ------------------------ .github/workflows/compare_def_files.yml | 17 ---- .github/workflows/whitelint.yml | 17 ---- .gitignore | 3 + .idea/misc.xml | 1 - CMakeLists.txt | 20 ++-- README.md | 1 + cmake/modules/FindOboe.cmake | 65 ++++++++++++ src/hostapi/oboe/pa_oboe.cpp | 22 ++-- 11 files changed, 91 insertions(+), 299 deletions(-) delete mode 100644 .github/workflows/MSBuild.yml delete mode 100644 .github/workflows/autotools.yml delete mode 100644 .github/workflows/cmake.yml delete mode 100644 .github/workflows/compare_def_files.yml delete mode 100644 .github/workflows/whitelint.yml create mode 100644 cmake/modules/FindOboe.cmake diff --git a/.github/workflows/MSBuild.yml b/.github/workflows/MSBuild.yml deleted file mode 100644 index ea90dba46..000000000 --- a/.github/workflows/MSBuild.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: MSBuild MSVC Project File CI - -on: [push, pull_request] - -env: - # Path to the solution file relative to the root of the project. - SOLUTION_FILE_PATH: ./msvc/portaudio.sln - VCPROJ_FILE_PATH: ./msvc/portaudio.vcproj - VCXPROJ_FILE_PATH: ./msvc/portaudio.vcxproj - VCXPROJ_FILTERS_FILE_PATH: ./msvc/portaudio.vcxproj.filters - VCXPROJ_USER_FILE_PATH: ./msvc/portaudio.vcxproj.user - DEF_FILE_PATH: ./msvc/portaudio.def - -jobs: - build: - runs-on: windows-latest - strategy: - matrix: - BUILD_CONFIGURATION: [Release] - BUILD_PLATFORM: [Win32, x64] - - steps: - - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1 - - - uses: actions/checkout@v2 - - - name: Upgrade VC Project File - # We maintain our vcproj file in an old format to maintain backwards compatibility - # This step upgrades the project to the latest version of MSVC - # see https://docs.microsoft.com/en-us/visualstudio/ide/reference/upgrade-devenv-exe?view=vs-2019 - # pipe to file to ensure that it terminates https://stackoverflow.com/questions/48896010/occasionally-occurring-msbuild-error-msb3428/48918105#48918105 - # discussion of using vswhere.exe here: https://stackoverflow.com/questions/65287456/how-to-upgrade-a-visual-studio-project-from-within-a-github-action/65311868#65311868 - run: | - $devenv = & vswhere.exe '-property' productPath - Write-Output "$devenv" - & $devenv "${{env.VCPROJ_FILE_PATH}}" /Upgrade /NoSplash | Out-Null - Write-Output "devenv launched" - while (!(Test-Path "${{env.VCXPROJ_FILE_PATH}}")) { Start-Sleep -Seconds 10 } - Write-Output "vcxproj found" - while (!(Test-Path "${{env.VCXPROJ_FILTERS_FILE_PATH}}")) { Start-Sleep -Seconds 10 } - Write-Output "vcxproj.filters found" - Start-Sleep -Seconds 10 - Write-Output "done." - - - name: Remove ASIO Files and Enable PA_USE_DS=1 - # Process the project files to remove ASIO-related sources and includes (since we can not access the ASIO SDK in a public build) - run: | - # Process .vcxproj file: remove source files - $xdoc = new-object System.Xml.XmlDocument - $vcxprojFile = resolve-path("${{env.VCXPROJ_FILE_PATH}}") - $xdoc.load($vcxprojFile) - $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable - $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) - $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) - Write-Output "deleting ASIO related compilation nodes from .vcxproj:" - Write-Output $nodes - ForEach($node in $nodes) { - $parent = $node.ParentNode - $parent.RemoveChild($node) - } - # Enable DirectSound host API - $nodes = $xdoc.SelectNodes("//vs:PreprocessorDefinitions[contains(., 'PA_USE_DS=0')]", $namespace) - ForEach($node in $nodes) { - $text = $node.InnerText - $node.InnerText = $text -replace 'PA_USE_DS=0', 'PA_USE_DS=1' - } - $xdoc.save($vcxprojFile) - # Process .vcxproj.filters file: remove source files and includes - $vcxprojFiltersFile = resolve-path("${{env.VCXPROJ_FILTERS_FILE_PATH}}") - $xdoc.load($vcxprojFiltersFile) - $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable - $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) - $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) - Write-Output "deleting ASIO related compilation nodes from .vcxproj.filters:" - Write-Output $nodes - ForEach($node in $nodes) { - $parent = $node.ParentNode - $parent.RemoveChild($node) - } - $nodes = $xdoc.SelectNodes("//vs:ClInclude[contains(@Include, 'pa_asio.h')]", $namespace) - Write-Output "deleting ASIO related include nodes from .vcxproj.filters:" - Write-Output $nodes - ForEach($node in $nodes) { - $parent = $node.ParentNode - $parent.RemoveChild($node) - } - $xdoc.save($vcxprojFiltersFile) - # Process .def file: remove PaAsio_ symbols - Set-Content -Path "${{env.DEF_FILE_PATH}}" -Value (Get-Content -Path "${{env.DEF_FILE_PATH}}" | Select-String -Pattern 'PaAsio_' -NotMatch) - - - name: Build - working-directory: ${{env.GITHUB_WORKSPACE}} - # Add additional options to the MSBuild command line here (like platform or verbosity level). - # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference - run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.BUILD_PLATFORM}} ${{env.VCXPROJ_FILE_PATH}} diff --git a/.github/workflows/autotools.yml b/.github/workflows/autotools.yml deleted file mode 100644 index 4d6fb6cb9..000000000 --- a/.github/workflows/autotools.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: autotools build - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build-autotools: - - runs-on: ubuntu-latest - name: Ubuntu - - steps: - - uses: actions/checkout@v2 - - name: configure - run: ./configure - - name: make - run: make diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml deleted file mode 100644 index a25eaabf7..000000000 --- a/.github/workflows/cmake.yml +++ /dev/null @@ -1,128 +0,0 @@ -name: CMake build - -on: [push, pull_request] - -jobs: - build: - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu GCC - os: ubuntu-latest - install_dir: ~/portaudio - vcpkg_triplet: x64-linux - cmake_generator: "Unix Makefiles" - cmake_options: - -DPA_USE_OSS=ON - -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - - name: Ubuntu MinGW - os: ubuntu-latest - install_dir: ~/portaudio - asio_sdk_cache_path: asiosdk.zip - dependencies_extras: mingw-w64 - # FIXME: linking JACK fails with vcpkg. Switch the CMake toolchain file to use vcpkg. The - # toolchain file in this repository is not needed when using a MinGW triplet with vcpkg. - vcpkg_triplet: x64-mingw-static - cmake_generator: "Unix Makefiles" - cmake_options: - -DPA_USE_ASIO=ON - -DASIO_SDK_ZIP_PATH=asiosdk.zip - -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/i686-w64-mingw32.cmake - - name: Windows MSVC - os: windows-latest - install_dir: C:\portaudio - vcpkg_triplet: x64-windows - cmake_generator: "Visual Studio 17 2022" - # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to - # asiosdk instead of asiosdk.zip. - asio_sdk_cache_path: "asiosdk.zip" - # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. - cmake_options: - -DPA_USE_ASIO=ON - -DASIO_SDK_ZIP_PATH="asiosdk.zip" - -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake - - name: Windows MinGW - os: windows-latest - install_dir: C:\portaudio - vcpkg_triplet: x64-mingw-static - cmake_generator: "MinGW Makefiles" - # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to - # asiosdk instead of asiosdk.zip. - asio_sdk_cache_path: "asiosdk.zip" - # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. - cmake_options: - -DPA_USE_ASIO=ON - -DASIO_SDK_ZIP_PATH="asiosdk.zip" - -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake - - name: macOS Clang - os: macOS-latest - install_dir: ~/portaudio - vcpkg_triplet: x64-osx - cmake_generator: "Unix Makefiles" - cmake_options: - -DCMAKE_FRAMEWORK=OFF - -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - - name: macOS Clang framework - os: macOS-latest - install_dir: ~/portaudio - vcpkg_triplet: x64-osx - cmake_generator: "Unix Makefiles" - cmake_options: - -DCMAKE_FRAMEWORK=ON - -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - - runs-on: ${{ matrix.os }} - name: ${{ matrix.name }} - env: - cmake_build_type: RelWithDebInfo - steps: - - name: checkout Git repository - uses: actions/checkout@v2 - - name: "[Ubuntu] install dependencies" - run: | - sudo apt-get update - sudo apt-get install libasound2-dev ${{ matrix.dependencies_extras }} - if: matrix.os == 'ubuntu-latest' - - name: "[macOS] install dependencies" - # https://github.com/PortAudio/portaudio/issues/767 - run: brew install pkg-config - if: matrix.os == 'macOS-latest' - - name: "[Windows/MinGW] set up ASIO SDK cache" - uses: actions/cache@v2 - if: matrix.asio_sdk_cache_path != null - with: - path: ${{ matrix.asio_sdk_cache_path }} - key: ${{ hashFiles('.github/asiosdk-version.txt') }} - - name: Setup vcpkg - uses: lukka/run-vcpkg@v7 - if: ${{ matrix.vcpkg_triplet }} != null - with: - vcpkgTriplet: ${{ matrix.vcpkg_triplet }} - appendedCacheKey: ${{ hashFiles( '**/vcpkg.json' ) }} - additionalCachedPaths: build/vcpkg_installed - vcpkgGitCommitId: f30786c9c4c901f21a13e2d524349e39cc359a90 - # Required when using vcpkg.json manifest in repository - setupOnly: true - - name: configure - run: cmake - -G "${{ matrix.cmake_generator }}" - ${{ matrix.cmake_options }} - -DCMAKE_INSTALL_PREFIX=${{ matrix.install_dir }} - -DCMAKE_BUILD_TYPE=${{ env.cmake_build_type }} - -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} - -DPA_USE_SKELETON=ON - -DPA_BUILD_TESTS=ON - -DPA_BUILD_EXAMPLES=ON - -DPA_WARNINGS_ARE_ERRORS=ON - -S . - -B build - - name: build - run: cmake --build build --config ${{ env.cmake_build_type }} --parallel 2 - - name: install - run: cmake --install build --config ${{ env.cmake_build_type }} - - name: "Upload GitHub Actions artifacts" - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.name }} PortAudio build - path: ${{ matrix.install_dir }} diff --git a/.github/workflows/compare_def_files.yml b/.github/workflows/compare_def_files.yml deleted file mode 100644 index b2b342411..000000000 --- a/.github/workflows/compare_def_files.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Check that PortAudio .def files are in sync - -on: [push, pull_request] - -jobs: - run-pa-compare-def-files-py: - - runs-on: ubuntu-latest - name: Ubuntu - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Run the pa_compare_def_files.py script - run: python ./pa_compare_def_files.py diff --git a/.github/workflows/whitelint.yml b/.github/workflows/whitelint.yml deleted file mode 100644 index 41cf8ff8b..000000000 --- a/.github/workflows/whitelint.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Check for valid whitespace usage in PortAudio source files - -on: [push, pull_request] - -jobs: - run-pa-whitelint-dot-py: - - runs-on: ubuntu-latest - name: Ubuntu - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Run the pa_whitelint.py script - run: python ./pa_whitelint.py diff --git a/.gitignore b/.gitignore index 345708350..72837f411 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ libtool portaudio-2.0.pc autom4te.cache/* +# build_all_PaOboe.sh CMake output folder +build + # Precompiled Headers *.gch *.pch diff --git a/.idea/misc.xml b/.idea/misc.xml index 639900d13..6e8667213 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/CMakeLists.txt b/CMakeLists.txt index 4959bf12a..442da731f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,17 @@ if(PA_USE_JACK) install(FILES cmake/modules/FindJACK.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") endif() +# Oboe only works on Android, which has a Linux kernel. +find_package(Oboe) +option(PA_USE_OBOE "Enable support for Oboe" ON) +if(PA_USE_OBOE) + target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + target_link_libraries(PortAudio PRIVATE Oboe) +endif() + if(WIN32) target_sources(PortAudio PRIVATE src/os/win/pa_win_coinitialize.c @@ -347,15 +358,6 @@ elseif(UNIX) endif() endif() - option(PA_USE_OBOE "Enable support for Oboe" ON) - if(PA_USE_OBOE) - target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) - set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) - target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") - target_link_libraries(PortAudio PRIVATE oboe) - endif() - # OSS is intentionally off by default to avoid confusing users of PortAudio # applications. OSS builds but there are no devices available on modern # Linux systems. diff --git a/README.md b/README.md index 5bbdfbd8b..de0b74507 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Please feel free to join. See http://www.portaudio.com for details. src/hostapi/coreaudio = Macintosh Core Audio for OS X src/hostapi/dsound = Windows Direct Sound src/hostapi/jack = JACK Audio Connection Kit + src/hostapi/oboe = Oboe Library for Android (see src/hostapi/oboe/README.md) src/hostapi/oss = Unix Open Sound System (OSS) src/hostapi/wasapi = Windows Vista WASAPI src/hostapi/wdmks = Windows WDM Kernel Streaming diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake new file mode 100644 index 000000000..79bc590b4 --- /dev/null +++ b/cmake/modules/FindOboe.cmake @@ -0,0 +1,65 @@ +#[=======================================================================[.rst: +FindOboe +-------- + +Finds the Oboe Directory by searching for it in the PA_DIRECTORY, which is the CMAKE_SOURCE_DIR if +not set. You may manually specify the path of the Oboe Directory with the OBOE_DIRECTORY variable. + +This module provides the following imported target, if found: + ``Oboe`` + +#]=======================================================================] + +if (NOT DEFINED PA_DIRECTORY) + set(PA_DIRECTORY ${CMAKE_SOURCE_DIR}) +endif () + +set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe-main) + +set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) +set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) + +set(OBOE_LIBRARY_DIRS ${OBOE_BUILD_DIR}/${ANDROID_ABI}) +set(OBOE_LIBRARY ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) + +if(OBOE_INCLUDE_DIR) + # Already in cache, be silent + set(OBOE_FIND_QUIETLY TRUE) +else() + find_package(PkgConfig) + pkg_check_modules(PC_OBOE QUIET Oboe) +endif(OBOE_INCLUDE_DIR) + +find_path(OBOE_INCLUDE_DIR + NAMES oboe/Oboe.h + DOC "Oboe include directory") + +find_library(OBOE_LIBRARY + NAMES liboboe.so + HINTS ${OBOE_LIBRARY_DIRS} + DOC "Oboe Shared Library") + +find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool + +# Handle the QUIETLY and REQUIRED arguments and set OPENSL_FOUND to TRUE if +# all listed variables are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + Oboe + DEFAULT_MSG + OBOE_INCLUDE_DIR + OBOE_LIBRARY +) + +if(OBOE_INCLUDE_DIR AND OBOE_LIBRARY) + set(OBOE_FOUND TRUE) + if(NOT TARGET Oboe) + add_library(Oboe INTERFACE IMPORTED) + target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + target_link_libraries(Oboe INTERFACE ${LOG_LIBRARY}) + endif() +else() + if (Oboe_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find OBOE") + endif() +endif() \ No newline at end of file diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 9ffc76620..b110fd938 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -91,7 +91,7 @@ PaError m_err; \ if (UNLIKELY((m_err = (expr)) < paNoError)) \ { \ - PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( \ + PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( \ __LINE__ ) "\n")); \ PaUtil_SetLastHostErrorInfo(paInDevelopment, m_err, errorText); \ m_error = m_err; \ @@ -386,10 +386,10 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, inputStream->setBufferSizeInFrames(inputStream->getFramesPerBurst() * numberOfBuffers); oboeStream->inputBuffers = - (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.inputChannelCount); @@ -429,10 +429,10 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, outputStream->setBufferSizeInFrames(outputStream->getFramesPerBurst() * numberOfBuffers); oboeStream->outputBuffers = - (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.outputChannelCount); @@ -810,7 +810,7 @@ bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { * @return the address of the oboeStream. */ OboeStream* OboeEngine::initializeOboeStream() { - oboeStream = (OboeStream *) PaUtil_AllocateMemory(sizeof(OboeStream)); + oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); return oboeStream; } @@ -989,7 +989,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaDeviceInfo *m_deviceInfoArray; char *m_deviceName; - m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateMemory( + m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateZeroInitializedMemory( sizeof(PaOboeHostApiRepresentation)); if (!m_oboeHostApi) { m_result = paInsufficientMemory; @@ -1015,7 +1015,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex m_deviceCount = 1; - (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateMemory( + (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( m_oboeHostApi->allocations, sizeof(PaDeviceInfo *) * m_deviceCount); if (!(*hostApi)->deviceInfos) { @@ -1024,7 +1024,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex } /* allocate all device info structs in a contiguous block */ - m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateMemory( + m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateZeroInitializedMemory( m_oboeHostApi->allocations, sizeof(PaDeviceInfo) * m_deviceCount); if (!m_deviceInfoArray) { m_result = paInsufficientMemory; @@ -1519,7 +1519,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, m_oboeStream->isActive = false; if (!(m_oboeStream->isBlocking)) {} -// PaUnixThreading_Initialize(); +// PaUnixThreading_Initialize(); TODO: see if threading works with this version of PortAudio if (m_inputChannelCount > 0) { m_oboeStream->hasInput = true; @@ -1896,4 +1896,4 @@ void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { void PaOboe_SetNumberOfBuffers(unsigned buffers) { numberOfBuffers = buffers; -} \ No newline at end of file +} From 51b3757e9b900bf418b407f6a5400c30a1c4f69b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 13 Jun 2023 11:35:15 +0200 Subject: [PATCH 007/125] Included build_all_PaOboe.sh, more work needed on CMake --- .gitignore | 1 - build_all_PaOboe.sh | 49 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100755 build_all_PaOboe.sh diff --git a/.gitignore b/.gitignore index 72837f411..bd63e09b0 100644 --- a/.gitignore +++ b/.gitignore @@ -58,4 +58,3 @@ CMakeSettings.json .vscode* # Common build directories of users and VSCode -build* diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh new file mode 100755 index 000000000..9e0295d2f --- /dev/null +++ b/build_all_PaOboe.sh @@ -0,0 +1,49 @@ +# Script to build Portaudio_Oboe for multiple Android ABIs +# +# Ensure that ANDROID_NDK environment variable is set to your Android NDK location +# e.g. /Library/Android/sdk/ndk-bundle + +#!/bin/bash + +ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 + +if [ -z "$ANDROID_NDK" ]; then + echo "Please set ANDROID_NDK to the Android NDK folder" + exit 1 +fi + +# Build directory +BUILD_DIR=build + +CMAKE_ARGS="-H. \ + -DBUILD_SHARED_LIBS=true \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DANDROID_TOOLCHAIN=clang \ + -DANDROID_STL=c++_shared \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX=." + +function build_PaOboe { + + ABI=$1 + MINIMUM_API_LEVEL=$2 + ABI_BUILD_DIR=${BUILD_DIR}/${ABI} + + echo "Building Pa_Oboe for ${ABI}" + + mkdir -p ${ABI_BUILD_DIR} ${ABI_BUILD_DIR}/${STAGING_DIR} + + cmake -B${ABI_BUILD_DIR} \ + -DANDROID_ABI=${ABI} \ + -DANDROID_PLATFORM=android-${MINIMUM_API_LEVEL}\ + ${CMAKE_ARGS} + + pushd ${ABI_BUILD_DIR} + make -j5 + popd +} + +build_PaOboe armeabi-v7a 16 +build_PaOboe arm64-v8a 21 +build_PaOboe x86 16 +build_PaOboe x86_64 21 From 0e05b668e20c23786a9122be2bbda3bac1aceb0e Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 13 Jun 2023 11:35:28 +0200 Subject: [PATCH 008/125] Included build_all_PaOboe.sh, more work needed on CMake --- build_all_PaOboe.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index 9e0295d2f..21f95bd5f 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -5,8 +5,6 @@ #!/bin/bash -ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 - if [ -z "$ANDROID_NDK" ]; then echo "Please set ANDROID_NDK to the Android NDK folder" exit 1 From ed974aa5b77e25114821054991eca3cc0d94d304 Mon Sep 17 00:00:00 2001 From: hopefulGiupplo <116260612+hopefulGiupplo@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:46:19 +0200 Subject: [PATCH 009/125] Update src/hostapi/oboe/README.md --- src/hostapi/oboe/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 1cd1f57d4..55c452612 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -10,7 +10,9 @@ To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. Make sure to correctly link the NDK path in the Oboe build. -3) Set the CMake variable OBOE_DIR to the path of the cloned Oboe repository. +3) Set the CMake variable OBOE_DIR (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository. +4) Build the Oboe Library (you can use "build_all_android.sh"). +5) Build PaOboe (you can use "build_all_PaOboe.sh"). TODOs: ---- From af4673558fb0a9f794bb6ceca94c164d4e607a69 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 26 Jul 2023 13:18:40 +0200 Subject: [PATCH 010/125] Built shared library --- CMakeLists.txt | 5 ++++ build_all_PaOboe.sh | 3 ++- cmake/modules/FindOboe.cmake | 50 ++++++++++++++++++------------------ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 442da731f..e2e94f338 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,9 @@ add_library(PortAudio include(GNUInstallDirs) +MESSAGE("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}") +MESSAGE("CMAKE_INSTALL_INCLUDEDIR: ${CMAKE_INSTALL_INCLUDEDIR}") + target_include_directories(PortAudio PUBLIC $ $ @@ -160,6 +163,8 @@ if(PA_USE_OBOE) target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") target_link_libraries(PortAudio PRIVATE Oboe) + + install(FILES cmake/modules/FindOboe.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") endif() if(WIN32) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index 21f95bd5f..c5985ef8d 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -1,9 +1,10 @@ +#!/bin/bash # Script to build Portaudio_Oboe for multiple Android ABIs # # Ensure that ANDROID_NDK environment variable is set to your Android NDK location # e.g. /Library/Android/sdk/ndk-bundle -#!/bin/bash +ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 if [ -z "$ANDROID_NDK" ]; then echo "Please set ANDROID_NDK to the Android NDK folder" diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 79bc590b4..94ecce8d1 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -11,55 +11,55 @@ This module provides the following imported target, if found: #]=======================================================================] if (NOT DEFINED PA_DIRECTORY) - set(PA_DIRECTORY ${CMAKE_SOURCE_DIR}) + set(PA_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif () -set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe-main) +MESSAGE("Searching for Oboe...") + +set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) set(OBOE_LIBRARY_DIRS ${OBOE_BUILD_DIR}/${ANDROID_ABI}) -set(OBOE_LIBRARY ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) +set(OBOE_LINK_LIBRARIES ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) -if(OBOE_INCLUDE_DIR) - # Already in cache, be silent - set(OBOE_FIND_QUIETLY TRUE) +find_package(PkgConfig QUIET) +if(PkgConfig_FOUND) + pkg_check_modules(OBOE Oboe) else() - find_package(PkgConfig) - pkg_check_modules(PC_OBOE QUIET Oboe) -endif(OBOE_INCLUDE_DIR) - -find_path(OBOE_INCLUDE_DIR - NAMES oboe/Oboe.h - DOC "Oboe include directory") - -find_library(OBOE_LIBRARY + find_library(OBOE_LINK_LIBRARIES NAMES liboboe.so HINTS ${OBOE_LIBRARY_DIRS} - DOC "Oboe Shared Library") + DOC "Oboe Library" + ) + find_path(OBOE_INCLUDE_DIR + NAMES oboe/Oboe.h + DOC "Oboe header" + ) +endif() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool +list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) + -# Handle the QUIETLY and REQUIRED arguments and set OPENSL_FOUND to TRUE if -# all listed variables are TRUE. include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Oboe DEFAULT_MSG + OBOE_LINK_LIBRARIES OBOE_INCLUDE_DIR - OBOE_LIBRARY ) -if(OBOE_INCLUDE_DIR AND OBOE_LIBRARY) +if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) set(OBOE_FOUND TRUE) if(NOT TARGET Oboe) add_library(Oboe INTERFACE IMPORTED) + target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") - target_link_libraries(Oboe INTERFACE ${LOG_LIBRARY}) - endif() -else() - if (Oboe_FIND_REQUIRED) - message(FATAL_ERROR "Could NOT find OBOE") endif() +#else() +# if (Oboe_FIND_REQUIRED) +# message(FATAL_ERROR "Could NOT find OBOE") +# endif() endif() \ No newline at end of file From cdbb27e3682dc246f2d19b5ac45a8d9ae45000e5 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 27 Jul 2023 09:40:41 +0200 Subject: [PATCH 011/125] Polished bits --- README.md | 1 - cmake/modules/FindOboe.cmake | 4 ---- include/pa_oboe.h | 4 ++-- src/hostapi/oboe/pa_oboe.cpp | 5 +++-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index de0b74507..a908dcc7f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ Please feel free to join. See http://www.portaudio.com for details. src/os = os specific (but host api neutral) code src/hostapi = implementations for different host apis -[//]: # (TODO: Add Oboe) ### Host API Implementations: src/hostapi/alsa = Advanced Linux Sound Architecture (ALSA) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 94ecce8d1..a8e110969 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -58,8 +58,4 @@ if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") endif() -#else() -# if (Oboe_FIND_REQUIRED) -# message(FATAL_ERROR "Could NOT find OBOE") -# endif() endif() \ No newline at end of file diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 19b2d0447..a1c25d129 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -3,7 +3,7 @@ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * - * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * Android Oboe implementation of PortAudio. * **************************************************************************************** * Author: * @@ -101,4 +101,4 @@ void PaOboe_SetNumberOfBuffers(unsigned buffers); } #endif //__cplusplus -#endif //PA_OBOE_H \ No newline at end of file +#endif //PA_OBOE_H diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index b110fd938..02c7e1c92 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -3,7 +3,7 @@ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * - * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * Android Oboe implementation of PortAudio. * **************************************************************************************** * Author: * @@ -1454,7 +1454,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paInt16" with whatever format you prefer - - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options + */ m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16, m_outputSampleFormat); m_oboeStream->outputFormat = m_hostOutputSampleFormat; From 816e7f59ae9aa3e1ff9de6e1023dce24f399b540 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 24 Aug 2023 10:54:39 +0200 Subject: [PATCH 012/125] Fixing old problems --- build_all_PaOboe.sh | 6 +++--- cmake/modules/FindOboe.cmake | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index c5985ef8d..a77ec406a 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -42,7 +42,7 @@ function build_PaOboe { popd } -build_PaOboe armeabi-v7a 16 +#build_PaOboe armeabi-v7a 16 build_PaOboe arm64-v8a 21 -build_PaOboe x86 16 -build_PaOboe x86_64 21 +#build_PaOboe x86 16 +#build_PaOboe x86_64 21 diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index a8e110969..1dbd1a132 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -21,8 +21,8 @@ set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) -set(OBOE_LIBRARY_DIRS ${OBOE_BUILD_DIR}/${ANDROID_ABI}) -set(OBOE_LINK_LIBRARIES ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) +set(OBOE_LIBRARY_DIR ${OBOE_DIRECTORY}/lib) +set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIR}/liboboe.so) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) From 948cb3d1ce1ed416446bf2e38bd55bba7cd8bedf Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 24 Aug 2023 12:23:18 +0200 Subject: [PATCH 013/125] Added some debug messages --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2e94f338..a05399cb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ endif() find_package(Oboe) option(PA_USE_OBOE "Enable support for Oboe" ON) if(PA_USE_OBOE) + message("Oboe found!") target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) From 71e0fcbbbcd0f222b57f29d2d1876bc0cc4b9756 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 24 Aug 2023 12:33:33 +0200 Subject: [PATCH 014/125] added new build script --- Build_PaOboe.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Build_PaOboe.sh diff --git a/Build_PaOboe.sh b/Build_PaOboe.sh new file mode 100644 index 000000000..f6e97ae78 --- /dev/null +++ b/Build_PaOboe.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Script to build Portaudio_Oboe for multiple Android ABIs +# +# Ensure that ANDROID_NDK environment variable is set to your Android NDK location +# e.g. /Library/Android/sdk/ndk-bundle + +ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 + +if [ -z "$ANDROID_NDK" ]; then + echo "Please set ANDROID_NDK to the Android NDK folder" + exit 1 +fi + +# Build directory +BUILD_DIR=lib + +CMAKE_ARGS="-H. \ + -DBUILD_SHARED_LIBS=true \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DANDROID_TOOLCHAIN=clang \ + -DANDROID_STL=c++_shared \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX=." + +function build_PaOboe { + + echo "Building Pa_Oboe" + + mkdir -p ${BUILD_DIR} ${BUILD_DIR}/${STAGING_DIR} + + cmake -B${BUILD_DIR} \ + -DANDROID_PLATFORM=android-21\ + ${CMAKE_ARGS} + + pushd ${BUILD_DIR} + make -j5 + make install + popd +} + + +build_PaOboe \ No newline at end of file From e96d7243820f48c9f0599acfb78272137118755b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 25 Aug 2023 13:12:22 +0200 Subject: [PATCH 015/125] Added paOboe in the paHostApi array --- Build_PaOboe.sh | 1 - build_all_PaOboe.sh | 2 +- cmake/modules/FindOboe.cmake | 4 ++-- include/portaudio.h | 4 ++-- src/common/pa_hostapi.h | 7 ++++++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Build_PaOboe.sh b/Build_PaOboe.sh index f6e97ae78..9d6c2e572 100644 --- a/Build_PaOboe.sh +++ b/Build_PaOboe.sh @@ -34,7 +34,6 @@ function build_PaOboe { pushd ${BUILD_DIR} make -j5 - make install popd } diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index a77ec406a..acbc8ad12 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -45,4 +45,4 @@ function build_PaOboe { #build_PaOboe armeabi-v7a 16 build_PaOboe arm64-v8a 21 #build_PaOboe x86 16 -#build_PaOboe x86_64 21 +build_PaOboe x86_64 21 diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 1dbd1a132..bb04d79ab 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -21,8 +21,8 @@ set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) -set(OBOE_LIBRARY_DIR ${OBOE_DIRECTORY}/lib) -set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIR}/liboboe.so) +set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) +set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) diff --git a/include/portaudio.h b/include/portaudio.h index daa467c9d..8b6c3b875 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -274,7 +274,6 @@ PaHostApiIndex Pa_GetDefaultHostApi( void ); @see PaHostApiInfo */ -//TODO: PaOboe - add paOboe = something typedef enum PaHostApiTypeId { paInDevelopment=0, /* use while developing support for a new host API */ @@ -291,7 +290,8 @@ typedef enum PaHostApiTypeId paJACK=12, paWASAPI=13, paAudioScienceHPI=14, - paAudioIO=15 + paAudioIO=15; + paOboe=16 } PaHostApiTypeId; diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h index 696b2879f..752272f18 100644 --- a/src/common/pa_hostapi.h +++ b/src/common/pa_hostapi.h @@ -153,7 +153,12 @@ are defaulted to 1. #define PA_USE_ASIHPI 1 #endif -//TODO: add PA_USE_OBOE +#ifndef PA_USE_OBOE +#define PA_USE_OBOE 0 +#elif (PA_USE_OBOE != 0) && (PA_USE_OBOE != 1) +#undef PA_USE_OBOE +#define PA_USE_OBOE 1 +#endif #ifdef __cplusplus extern "C" From 194c5970312781427cb89b4656ee392f0c270b0b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 25 Aug 2023 13:16:55 +0200 Subject: [PATCH 016/125] Added paOboe in pa_unix_hostapis.c --- include/portaudio.h | 2 +- src/os/unix/pa_unix_hostapis.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/portaudio.h b/include/portaudio.h index 8b6c3b875..55beb9552 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -290,7 +290,7 @@ typedef enum PaHostApiTypeId paJACK=12, paWASAPI=13, paAudioScienceHPI=14, - paAudioIO=15; + paAudioIO=15, paOboe=16 } PaHostApiTypeId; diff --git a/src/os/unix/pa_unix_hostapis.c b/src/os/unix/pa_unix_hostapis.c index 85fefb267..466f6f846 100644 --- a/src/os/unix/pa_unix_hostapis.c +++ b/src/os/unix/pa_unix_hostapis.c @@ -52,6 +52,8 @@ PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +/* Android HostApi */ +PaError PaOboe_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); /** Note that on Linux, ALSA is placed before OSS so that the former is preferred over the latter. */ @@ -100,6 +102,10 @@ PaUtilHostApiInitializer *paHostApiInitializers[] = PaMacCore_Initialize, #endif +#if PA_USE_OBOE + PaOboe_Initialize, +#endif + #if PA_USE_SKELETON PaSkeleton_Initialize, #endif From 313d75958ad62182e80d6317b3b07be3550bd55c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 25 Aug 2023 13:27:39 +0200 Subject: [PATCH 017/125] Working patch --- src/hostapi/oboe/pa_oboe.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 02c7e1c92..ab0117c03 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -82,8 +82,6 @@ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,MODULE_NAME, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,MODULE_NAME, __VA_ARGS__) -//TODO: PaOboe_Patch: PaUtil_AllocateZeroInitializedMemory replaces PaUtil_AllocateMemory (same with GoupAllocations) - // Copied from @{pa_opensles.c}. #define ENSURE(expr, errorText) \ do \ From dd40f7177550ab91363faa7d6472e5ddfd12c5a3 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 12:09:00 +0200 Subject: [PATCH 018/125] Ready for other users to compile/build --- CMakeLists.txt | 5 +- build_all_PaOboe.sh | 2 +- cmake/modules/FindOboe.cmake | 89 +++++++++++++++++++----------------- src/hostapi/oboe/README.md | 18 ++++++-- 4 files changed, 64 insertions(+), 50 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a05399cb1..4cecc71be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,9 +73,6 @@ add_library(PortAudio include(GNUInstallDirs) -MESSAGE("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}") -MESSAGE("CMAKE_INSTALL_INCLUDEDIR: ${CMAKE_INSTALL_INCLUDEDIR}") - target_include_directories(PortAudio PUBLIC $ $ @@ -156,7 +153,7 @@ endif() # Oboe only works on Android, which has a Linux kernel. find_package(Oboe) -option(PA_USE_OBOE "Enable support for Oboe" ON) +cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) if(PA_USE_OBOE) message("Oboe found!") target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index acbc8ad12..7b8c7e8ad 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Script to build Portaudio_Oboe for multiple Android ABIs +# Script used to build Portaudio for multiple Android ABIs, useful for its Oboe host API. # # Ensure that ANDROID_NDK environment variable is set to your Android NDK location # e.g. /Library/Android/sdk/ndk-bundle diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index bb04d79ab..39e88d32f 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -3,59 +3,66 @@ FindOboe -------- Finds the Oboe Directory by searching for it in the PA_DIRECTORY, which is the CMAKE_SOURCE_DIR if -not set. You may manually specify the path of the Oboe Directory with the OBOE_DIRECTORY variable. +not set. This module provides the following imported target, if found: ``Oboe`` #]=======================================================================] -if (NOT DEFINED PA_DIRECTORY) - set(PA_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -endif () - -MESSAGE("Searching for Oboe...") - -set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) +if(NOT DEFINED OBOE_DIRECTORY) + #Insert the path of the directory where you cloned Oboe + set(OBOE_DIRECTORY FALSE) +endif() -set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) -set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) +if(NOT OBOE_DIRECTORY) + message(AUTHOR_WARNING + "If you're trying to use Oboe as a Host API, please specify the directory where you cloned its repository. For further information, please read src/hostapi/oboe/README.md" + ) + set(OBOE_FOUND FALSE) +else() + if(NOT DEFINED OBOE_INCLUDE_DIR) + set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) + endif() -set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) -set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) + if(NOT DEFINED OBOE_LINK_LIBRARIES) + set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) + set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) + endif() -find_package(PkgConfig QUIET) -if(PkgConfig_FOUND) - pkg_check_modules(OBOE Oboe) -else() - find_library(OBOE_LINK_LIBRARIES - NAMES liboboe.so - HINTS ${OBOE_LIBRARY_DIRS} - DOC "Oboe Library" - ) - find_path(OBOE_INCLUDE_DIR - NAMES oboe/Oboe.h - DOC "Oboe header" - ) -endif() + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(OBOE Oboe) + else() + find_library(OBOE_LINK_LIBRARIES + NAMES liboboe.so + HINTS ${OBOE_LIBRARY_DIRS} + DOC "Oboe Library" + ) + find_path(OBOE_INCLUDE_DIR + NAMES oboe/Oboe.h + DOC "Oboe header" + ) + endif() -find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool -list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) + find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool + list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - Oboe - DEFAULT_MSG - OBOE_LINK_LIBRARIES - OBOE_INCLUDE_DIR -) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args( + Oboe + DEFAULT_MSG + OBOE_LINK_LIBRARIES + OBOE_INCLUDE_DIR + ) -if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) - set(OBOE_FOUND TRUE) - if(NOT TARGET Oboe) - add_library(Oboe INTERFACE IMPORTED) - target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") - target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) + set(OBOE_FOUND TRUE) + if(NOT TARGET Oboe) + add_library(Oboe INTERFACE IMPORTED) + target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") + target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + endif() endif() endif() \ No newline at end of file diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 1cd1f57d4..5b29986ff 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -9,15 +9,25 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. -3) Set the CMake variable OBOE_DIR to the path of the cloned Oboe repository. + Make sure to correctly link the NDK path in the Oboe build. Alternatively, you can use the prebuilt + libraries and clone the include directory of said repository. +3) If you cloned the Oboe repository, set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) + to the path of the cloned Oboe repository, and don't forget to build the Oboe libraries (you can use "build_all_android.sh"). + + If you instead used the prebuilt libraries, set the following cmake variables: + - OBOE_DIRECTORY set to TRUE; + - OBOE_INCLUDE_DIR set to the path of the Oboe include directory; + - OBOE_LINK_LIBRARIES set to the path of the prebuilt libraries. +4) Build Portaudio (you can use "build_all_PaOboe.sh" to build it for all supported Android ABIs). TODOs: ---- -- Testing. This implementation was non-extensively tested for VoIP calls and blocking streams - for +- Tests. This implementation was non-extensively tested for VoIP calls and blocking streams - for everything else, it should have a decent structure. -- Implementing onErrorAfterClose in a way that works, and checking the callback methods. +- Add support for armeabi-v7a and x86 architectures. + +- Implement onErrorAfterClose in a way that works, and checking the callback methods. Misc ---- From 5cca69c89503ec12ae485957b543094b71df8d7a Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 12:18:34 +0200 Subject: [PATCH 019/125] Updated oboe/Readme.md --- cmake/modules/FindOboe.cmake | 2 +- src/hostapi/oboe/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 39e88d32f..e3036514e 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -11,7 +11,7 @@ This module provides the following imported target, if found: #]=======================================================================] if(NOT DEFINED OBOE_DIRECTORY) - #Insert the path of the directory where you cloned Oboe + #Insert the path of the directory where you cloned Oboe, i.e. ${CMAKE_CURRENT_SOURCE_DIR}/../oboe set(OBOE_DIRECTORY FALSE) endif() diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 5b29986ff..d67354655 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -23,13 +23,13 @@ To build portaudio with Oboe, there are some necessary steps: TODOs: ---- - Tests. This implementation was non-extensively tested for VoIP calls and blocking streams - for - everything else, it should have a decent structure. + everything else, it should have a decent structure, but needs to be tested. - Add support for armeabi-v7a and x86 architectures. - Implement onErrorAfterClose in a way that works, and checking the callback methods. -Misc +Miscellaneous ---- ### Latency and Sharing Mode: Using LowLatency and SharingMode Exclusive is possible, but a function in pa_oboe.h that sets said flags *is yet to be implemented*, so you'll have to manually set those properties in the OboeEngine::OpenStream function. From 402fc7719ab26e8df63de2e279b554c0c4778f6c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 13:01:44 +0200 Subject: [PATCH 020/125] Added sharing mode selection --- cmake/modules/FindOboe.cmake | 2 +- include/pa_oboe.h | 11 ++++++++++- src/hostapi/oboe/pa_oboe.cpp | 38 +++++++++++++++++++++++++----------- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index e3036514e..825fd0083 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -12,7 +12,7 @@ This module provides the following imported target, if found: if(NOT DEFINED OBOE_DIRECTORY) #Insert the path of the directory where you cloned Oboe, i.e. ${CMAKE_CURRENT_SOURCE_DIR}/../oboe - set(OBOE_DIRECTORY FALSE) + set(OBOE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../oboe) endif() if(NOT OBOE_DIRECTORY) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index a1c25d129..d15704061 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -71,11 +71,20 @@ typedef struct PaOboeStreamInfo { * Provide PA Oboe with the ID of the device the user chose - oboe cannot build a device list, * but can select the device if provided with its ID. * @param direction - the direction of the stream for which we want to set the device. - * @param deviceID - the ID of the device chose by the user. + * @param deviceID - the ID of the device chosen by the user. */ void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); +/** + * Provide PA Oboe with the performance mode chosen by the user. + * @param direction - the direction of the stream for which we want to set the performance mode. If set to kUnspecified, + * both input and output performance modes will be set to performanceMode. + * @param performanceMode - the performance mode chosen by the user. + */ + void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); + + /** * Provide PA Oboe with native buffer information. If you call this function, you must do so before * calling Pa_Initialize. To have optimal latency, this function should be called - otherwise, diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index ab0117c03..aa1649057 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -159,8 +159,10 @@ static unsigned numberOfBuffers = 2; using namespace oboe; -int32_t inputDeviceId = kUnspecified; -int32_t outputDeviceId = kUnspecified; +int32_t g_inputDeviceId = kUnspecified; +int32_t g_outputDeviceId = kUnspecified; +PerformanceMode g_inputPerfMode = PerformanceMode::None; +PerformanceMode g_outputPerfMode = PerformanceMode::None; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -364,13 +366,13 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, ->setSampleRate(sampleRate) ->setDirection(Direction::Input) ->setDeviceId(getSelectedDevice(Direction::Input)) + ->setPerformanceMode(g_inputPerfMode) ->setInputPreset(androidInputPreset) ->setFramesPerCallback(oboeStream->framesPerHostCallback); if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - inputBuilder.setDataCallback(this) - ->setPerformanceMode(PerformanceMode::LowLatency); + inputBuilder.setDataCallback(this); } m_result = inputBuilder.openStream(inputStream); @@ -408,13 +410,13 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, ->setSampleRate(sampleRate) ->setDirection(Direction::Output) ->setDeviceId(getSelectedDevice(Direction::Output)) + ->setPerformanceMode(g_outputPerfMode) ->setUsage(androidOutputUsage) ->setFramesPerCallback(oboeStream->framesPerHostCallback); if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - outputBuilder.setDataCallback(this) - ->setPerformanceMode(PerformanceMode::LowLatency); + outputBuilder.setDataCallback(this); } m_result = outputBuilder.openStream(outputStream); @@ -510,7 +512,6 @@ bool OboeEngine::restartStream(int direction) { bool m_outcome = true; Result m_result; - //TODO: Test if KCTI crashes when ErrorDisconnected occurs switch (direction) { case 1: //output-only //stopping and closing @@ -863,9 +864,9 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { */ int32_t OboeEngine::getSelectedDevice(Direction direction) { if (direction == Direction::Input) - return inputDeviceId; + return g_inputDeviceId; else - return outputDeviceId; + return g_outputDeviceId; } @@ -1882,9 +1883,24 @@ static unsigned long GetApproximateLowBufferSize() { void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); if (direction == Direction::Input) - inputDeviceId = deviceID; + g_inputDeviceId = deviceID; else - outputDeviceId = deviceID; + g_outputDeviceId = deviceID; +} + + +void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ + switch (direction) { + case Direction::Input: + g_inputPerfMode = performanceMode; + break; + case Direction::Output: + g_outputPerfMode = performanceMode; + break; + default: + g_outputPerfMode = g_inputPerfMode = performanceMode; + break; + } } From 5c24f22ea64bb59a12ee5b7844b0b529978f131b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 13:02:35 +0200 Subject: [PATCH 021/125] Fixed minor issue with sharing mode selection --- include/pa_oboe.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index d15704061..229fde0bf 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -78,8 +78,7 @@ void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); /** * Provide PA Oboe with the performance mode chosen by the user. - * @param direction - the direction of the stream for which we want to set the performance mode. If set to kUnspecified, - * both input and output performance modes will be set to performanceMode. + * @param direction - the direction of the stream for which we want to set the performance mode. * @param performanceMode - the performance mode chosen by the user. */ void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); From 75605db1985823ff97843448c0d94b77fd782822 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 13:09:24 +0200 Subject: [PATCH 022/125] Removed references to KCTI --- src/hostapi/oboe/pa_oboe.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index aa1649057..84431fa92 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -297,7 +297,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan bool m_outcome = false; m_builder.setDeviceId(getSelectedDevice(direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::I16) ->setDirection(direction) ->setSampleRate(sampleRate) @@ -1412,8 +1412,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + /* FIXME: Replace "paInt16" with whatever format you prefer - + * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16, m_inputSampleFormat); m_oboeStream->inputFormat = m_hostInputSampleFormat; @@ -1452,9 +1452,9 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - - PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options - */ + /* FIXME: Replace "paInt16" with whatever format you prefer - + PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options + */ m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16, m_outputSampleFormat); m_oboeStream->outputFormat = m_hostOutputSampleFormat; @@ -1866,15 +1866,10 @@ static double GetStreamCpuLoad(PaStream *s) { * @return 256 for Android API Level <= 23, 192 otherwise. */ static unsigned long GetApproximateLowBufferSize() { -/* FIXME: This function should return the following commented values, but was changed in order to improve - compatibility with KCTI for android. Please use the commented values in normal conditions. */ - -// if (__ANDROID_API__ <= 23) -// return 256; -// else -// return 192; - - return 1024; + if (__ANDROID_API__ <= 23) + return 256; + else + return 192; } From a6e4301a70cb821bf321b10318c353ccdbefaf0b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 10:56:44 +0200 Subject: [PATCH 023/125] Fixed error callback, added performance mode autoselection --- src/hostapi/oboe/pa_oboe.cpp | 92 ++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 84431fa92..c96fdbc5f 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -43,7 +43,6 @@ * requested that these non-binding requests be included along with the * license above. */ -#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions" /** @file @ingroup hostapi_src @@ -159,10 +158,13 @@ static unsigned numberOfBuffers = 2; using namespace oboe; +//Useful global variables int32_t g_inputDeviceId = kUnspecified; int32_t g_outputDeviceId = kUnspecified; PerformanceMode g_inputPerfMode = PerformanceMode::None; +bool g_inputPerfModeUser = false; PerformanceMode g_outputPerfMode = PerformanceMode::None; +bool g_outputPerfModeUser = false; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -252,6 +254,10 @@ class OboeEngine : public AudioStreamCallback { //device selection implementation int32_t getSelectedDevice(oboe::Direction direction); + + //auto performance mode selection + void performanceModeAutoSelection(Direction direction); + double assertLatency(Direction direction); }; @@ -297,7 +303,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan bool m_outcome = false; m_builder.setDeviceId(getSelectedDevice(direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::I16) ->setDirection(direction) ->setSampleRate(sampleRate) @@ -314,6 +320,8 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan return m_outcome; } + performanceModeAutoSelection(direction); + if (sampleRate != kUnspecified) { m_outcome = (sampleRate == m_builder.getSampleRate()); if(!m_outcome) { @@ -372,7 +380,8 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - inputBuilder.setDataCallback(this); + inputBuilder.setDataCallback(this) + ->setErrorCallback(this); } m_result = inputBuilder.openStream(inputStream); @@ -416,7 +425,8 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - outputBuilder.setDataCallback(this); + outputBuilder.setDataCallback(this) + ->setErrorCallback(this); } m_result = outputBuilder.openStream(outputStream); @@ -870,6 +880,64 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { } +/** + * \brief Function used to automatically select the performance mode, based on the latency value LOW_LATENCY_MS, if no + * choice was made via PaOboe_SetPerformanceMode. It uses OboeEngine::assertLatency to make this decision. + * @param direction the Oboe::Direction of the stream we want to select the mode of. + */ +void OboeEngine::performanceModeAutoSelection(Direction direction){ + double m_result = assertLatency(direction); + + if(direction == Direction::Input) { + if (m_result >= 0 && !g_inputPerfModeUser) { + if (m_result <= LOW_LATENCY_MS) + g_inputPerfMode = PerformanceMode::LowLatency; + else + g_inputPerfMode = PerformanceMode::PowerSaving; + } + } else { + if (m_result >= 0 && !g_outputPerfModeUser) { + if (m_result <= LOW_LATENCY_MS) + g_outputPerfMode = PerformanceMode::LowLatency; + else + g_outputPerfMode = PerformanceMode::PowerSaving; + } + } +} + + +/** + * \brief Asserts if the device supports latency tutning, then calculates the latency of an AudioStream. + * @param direction the direction of the AudioStream we want to check. + * @return the measured latency, or -1.0 if any error occurs. + */ +double OboeEngine::assertLatency(Direction direction){ + if(__ANDROID_API__<31) { + LOGI("[OboeEngine::assertLatency]\t Latency Tuning is not supported for Android API level < 31"); + return -1.0; + } + if(direction == Direction::Input){ + ResultWithValue m_result = inputStream->calculateLatencyMillis(); + if (m_result) { + return m_result.value(); + } else { + LOGE("[OboeEngine::assertLatency]\t Error calculating input latency: %s", + oboe::convertToText(m_result.error())); + return -1.0; + } + } else { + ResultWithValue m_result = outputStream->calculateLatencyMillis(); + if (m_result) { + return m_result.value(); + } else { + LOGE("[OboeEngine::assertLatency]\t Error calculating output latency: %s", + oboe::convertToText(m_result.error())); + return -1.0; + } + } + +} + /*----------------------------- PaSkeleton functions implementations -----------------------------*/ @@ -1885,16 +1953,12 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ - switch (direction) { - case Direction::Input: - g_inputPerfMode = performanceMode; - break; - case Direction::Output: - g_outputPerfMode = performanceMode; - break; - default: - g_outputPerfMode = g_inputPerfMode = performanceMode; - break; + if(direction == Direction::Input) { + g_inputPerfMode = performanceMode; + g_inputPerfModeUser = true; + } else { + g_outputPerfMode = performanceMode; + g_outputPerfModeUser = true; } } From 3618f117431b45a2e679ca0eb45d1033eb799148 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 11:04:59 +0200 Subject: [PATCH 024/125] Minor change to CMakeLists, added low latency costant in pa_oboe.h --- CMakeLists.txt | 2 +- include/pa_oboe.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cecc71be..fe7f829de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,7 @@ endif() find_package(Oboe) cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) if(PA_USE_OBOE) - message("Oboe found!") + message("-- Oboe structure found") target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 229fde0bf..761a7b937 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -56,7 +56,8 @@ extern "C" { #endif //__cplusplus -#define TIMEOUT_NS 1000000000 +#define TIMEOUT_NS 1000000000 //Arbitrary timeout of the read/write functions +#define LOW_LATENCY_MS 300.0 //Arbitrary value used to automatically determine if low latency performance mode is doable /** * The android stream type and recording preset as defined in Definitions.h @@ -81,7 +82,7 @@ void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); * @param direction - the direction of the stream for which we want to set the performance mode. * @param performanceMode - the performance mode chosen by the user. */ - void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); +void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); /** From 746b3a10b94a3dc2b6cab3084ef7014100b17658 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 11:39:24 +0200 Subject: [PATCH 025/125] Deleted Build_PaOboe.sh --- Build_PaOboe.sh | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 Build_PaOboe.sh diff --git a/Build_PaOboe.sh b/Build_PaOboe.sh deleted file mode 100644 index 9d6c2e572..000000000 --- a/Build_PaOboe.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# Script to build Portaudio_Oboe for multiple Android ABIs -# -# Ensure that ANDROID_NDK environment variable is set to your Android NDK location -# e.g. /Library/Android/sdk/ndk-bundle - -ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 - -if [ -z "$ANDROID_NDK" ]; then - echo "Please set ANDROID_NDK to the Android NDK folder" - exit 1 -fi - -# Build directory -BUILD_DIR=lib - -CMAKE_ARGS="-H. \ - -DBUILD_SHARED_LIBS=true \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DANDROID_TOOLCHAIN=clang \ - -DANDROID_STL=c++_shared \ - -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ - -DCMAKE_INSTALL_PREFIX=." - -function build_PaOboe { - - echo "Building Pa_Oboe" - - mkdir -p ${BUILD_DIR} ${BUILD_DIR}/${STAGING_DIR} - - cmake -B${BUILD_DIR} \ - -DANDROID_PLATFORM=android-21\ - ${CMAKE_ARGS} - - pushd ${BUILD_DIR} - make -j5 - popd -} - - -build_PaOboe \ No newline at end of file From 4a2bf7914d0816f380f12c62cdbfc6c8d3402fa0 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 11:52:29 +0200 Subject: [PATCH 026/125] Ready to push (removed my paths to Oboe directory and Android NDK) --- build_all_PaOboe.sh | 2 -- cmake/modules/FindOboe.cmake | 13 +++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index 7b8c7e8ad..9e589ed0b 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -4,8 +4,6 @@ # Ensure that ANDROID_NDK environment variable is set to your Android NDK location # e.g. /Library/Android/sdk/ndk-bundle -ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 - if [ -z "$ANDROID_NDK" ]; then echo "Please set ANDROID_NDK to the Android NDK folder" exit 1 diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 825fd0083..9a7ddbd8a 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -2,17 +2,22 @@ FindOboe -------- -Finds the Oboe Directory by searching for it in the PA_DIRECTORY, which is the CMAKE_SOURCE_DIR if -not set. +Finds the Oboe library. OBOE_DIRECTORY has to be set to the path of the directory where +the Oboe repository was cloned (see src/hostapi/oboe/README.md for more information). + +Imported Targets +^^^^^^^^^^^^^^^^ This module provides the following imported target, if found: - ``Oboe`` + +``Oboe`` + The OBOE library #]=======================================================================] if(NOT DEFINED OBOE_DIRECTORY) #Insert the path of the directory where you cloned Oboe, i.e. ${CMAKE_CURRENT_SOURCE_DIR}/../oboe - set(OBOE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../oboe) + set(OBOE_DIRECTORY FALSE) endif() if(NOT OBOE_DIRECTORY) From b5ca5f9f02ecdb55686a8ee2ffe9e9fe4026e380 Mon Sep 17 00:00:00 2001 From: hopefulGiupplo <116260612+hopefulGiupplo@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:21:58 +0200 Subject: [PATCH 027/125] Update README.md --- src/hostapi/oboe/README.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 55c452612..d67354655 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -9,19 +9,27 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. -3) Set the CMake variable OBOE_DIR (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository. -4) Build the Oboe Library (you can use "build_all_android.sh"). -5) Build PaOboe (you can use "build_all_PaOboe.sh"). + Make sure to correctly link the NDK path in the Oboe build. Alternatively, you can use the prebuilt + libraries and clone the include directory of said repository. +3) If you cloned the Oboe repository, set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) + to the path of the cloned Oboe repository, and don't forget to build the Oboe libraries (you can use "build_all_android.sh"). + + If you instead used the prebuilt libraries, set the following cmake variables: + - OBOE_DIRECTORY set to TRUE; + - OBOE_INCLUDE_DIR set to the path of the Oboe include directory; + - OBOE_LINK_LIBRARIES set to the path of the prebuilt libraries. +4) Build Portaudio (you can use "build_all_PaOboe.sh" to build it for all supported Android ABIs). TODOs: ---- -- Testing. This implementation was non-extensively tested for VoIP calls and blocking streams - for - everything else, it should have a decent structure. +- Tests. This implementation was non-extensively tested for VoIP calls and blocking streams - for + everything else, it should have a decent structure, but needs to be tested. + +- Add support for armeabi-v7a and x86 architectures. -- Implementing onErrorAfterClose in a way that works, and checking the callback methods. +- Implement onErrorAfterClose in a way that works, and checking the callback methods. -Misc +Miscellaneous ---- ### Latency and Sharing Mode: Using LowLatency and SharingMode Exclusive is possible, but a function in pa_oboe.h that sets said flags *is yet to be implemented*, so you'll have to manually set those properties in the OboeEngine::OpenStream function. From 07b8347f57ed254e93c7dbe5b9dac16fe05921a3 Mon Sep 17 00:00:00 2001 From: hopefulGiupplo <116260612+hopefulGiupplo@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:24:34 +0200 Subject: [PATCH 028/125] Update README.md From 43704595d6a5d600956ec052d5b79997961cb6ee Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 12:37:12 +0200 Subject: [PATCH 029/125] updated readme --- src/hostapi/oboe/README.md | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index d67354655..7e027ec00 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -1,4 +1,3 @@ - # Portaudio implementation for android using Oboe. In order to use this implementation correctly, be sure to include the "portaudio.h" and "pa_oboe.h" @@ -9,40 +8,22 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. Alternatively, you can use the prebuilt - libraries and clone the include directory of said repository. -3) If you cloned the Oboe repository, set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) - to the path of the cloned Oboe repository, and don't forget to build the Oboe libraries (you can use "build_all_android.sh"). - - If you instead used the prebuilt libraries, set the following cmake variables: - - OBOE_DIRECTORY set to TRUE; - - OBOE_INCLUDE_DIR set to the path of the Oboe include directory; - - OBOE_LINK_LIBRARIES set to the path of the prebuilt libraries. -4) Build Portaudio (you can use "build_all_PaOboe.sh" to build it for all supported Android ABIs). + Make sure to correctly link the NDK path in the Oboe build. +3) Set the CMake variable OBOE_DIR (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository. +4) Build the Oboe Library (you can use "build_all_android.sh"). +5) Build PaOboe (you can use "build_all_PaOboe.sh"). +6) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. TODOs: ---- -- Tests. This implementation was non-extensively tested for VoIP calls and blocking streams - for - everything else, it should have a decent structure, but needs to be tested. - -- Add support for armeabi-v7a and x86 architectures. - -- Implement onErrorAfterClose in a way that works, and checking the callback methods. +- Testing. This implementation was tested for VoIP calls that use blocking streams - for everything else, lots of tests are needed. -Miscellaneous +Misc ---- -### Latency and Sharing Mode: -Using LowLatency and SharingMode Exclusive is possible, but a function in pa_oboe.h that sets said flags *is yet to be implemented*, so you'll have to manually set those properties in the OboeEngine::OpenStream function. - - ### Audio Format: If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream by modifying the format selection marked with a *FIXME*. -I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat will not get you anywhere. +I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat got me nowhere. ### Buffer sizes: -Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. - - -### Device selection and/or switching mid-stream: -Device selection can be handled by a java/kotlin method that uses getDevices() in order to identify which device to select. Switching mid-stream gives an oboe::ErrorDisconnected result, and you'll have to stop, close and reopen the involved streams with an unspecified device (or a specific device if you know its ID). +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file From 4100c0667c0bd3a2615055999343af31f883bce6 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 12:38:26 +0200 Subject: [PATCH 030/125] fixed CMakeLists.txt --- CMakeLists.txt | 338 +++++++++++++++++++++++++++---------------------- 1 file changed, 190 insertions(+), 148 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe7f829de..5cefda195 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,8 @@ endif() option(PA_WARNINGS_ARE_ERRORS "Turn compiler warnings into errors" OFF) if(PA_WARNINGS_ARE_ERRORS) - if(MSVC) - add_compile_options(/WX + if(MSVC) + add_compile_options(/WX # "Grandfathered" warnings that existed before we started enforcement. # Do *NOT* add warnings to this list. Instead, fix your code so that it doesn't produce the warning. # TODO: fix the offending code so that we don't have to exclude specific warnings anymore. @@ -26,58 +26,85 @@ if(PA_WARNINGS_ARE_ERRORS) /wd4244 # W2 conversion possible loss of data /wd4267 # W3 conversion possible loss of data /wd4996 # W3 unsafe/deprecated - ) - else() - add_compile_options(-Werror + ) + else() + add_compile_options(-Werror # "Grandfathered" warnings that existed before we started enforcement. # Do *NOT* add warnings to this list. Instead, fix your code so that it doesn't produce the warning. # TODO: fix the offending code so that we don't have to exclude specific warnings anymore. -Wno-error=deprecated-declarations # https://github.com/PortAudio/portaudio/issues/213 https://github.com/PortAudio/portaudio/issues/641 -Wno-error=stringop-overflow - ) - if (CMAKE_C_COMPILER_ID MATCHES "Clang") - # Don't fail on older clang versions that don't recognize the latest warnings in the list above. - # Note that unrecognized warning options are not a fatal error on GCC, and in fact, GCC will choke on this option. Hence the conditional. - add_compile_options(-Wno-error=unknown-warning-option) - endif() + ) + if (CMAKE_C_COMPILER_ID MATCHES "Clang") + # Don't fail on older clang versions that don't recognize the latest warnings in the list above. + # Note that unrecognized warning options are not a fatal error on GCC, and in fact, GCC will choke on this option. Hence the conditional. + add_compile_options(-Wno-error=unknown-warning-option) endif() + endif() endif() +set(PA_COMMON_SOURCES + src/common/pa_allocation.c + src/common/pa_allocation.h + src/common/pa_converters.c + src/common/pa_converters.h + src/common/pa_cpuload.c + src/common/pa_cpuload.h + src/common/pa_debugprint.c + src/common/pa_debugprint.h + src/common/pa_dither.c + src/common/pa_dither.h + src/common/pa_endianness.h + src/common/pa_front.c + src/common/pa_hostapi.h + src/common/pa_memorybarrier.h + src/common/pa_process.c + src/common/pa_process.h + src/common/pa_ringbuffer.c + src/common/pa_ringbuffer.h + src/common/pa_stream.c + src/common/pa_stream.h + src/common/pa_trace.c + src/common/pa_trace.h + src/common/pa_types.h + src/common/pa_util.h + ) + add_library(PortAudio - ${LIBRARY_BUILD_TYPE} - src/common/pa_allocation.c - src/common/pa_allocation.h - src/common/pa_converters.c - src/common/pa_converters.h - src/common/pa_cpuload.c - src/common/pa_cpuload.h - src/common/pa_debugprint.c - src/common/pa_debugprint.h - src/common/pa_dither.c - src/common/pa_dither.h - src/common/pa_endianness.h - src/common/pa_front.c - src/common/pa_hostapi.h - src/common/pa_memorybarrier.h - src/common/pa_process.c - src/common/pa_process.h - src/common/pa_ringbuffer.c - src/common/pa_ringbuffer.h - src/common/pa_stream.c - src/common/pa_stream.h - src/common/pa_trace.c - src/common/pa_trace.h - src/common/pa_types.h - src/common/pa_util.h -) + ${LIBRARY_BUILD_TYPE} + src/common/pa_allocation.c + src/common/pa_allocation.h + src/common/pa_converters.c + src/common/pa_converters.h + src/common/pa_cpuload.c + src/common/pa_cpuload.h + src/common/pa_debugprint.c + src/common/pa_debugprint.h + src/common/pa_dither.c + src/common/pa_dither.h + src/common/pa_endianness.h + src/common/pa_front.c + src/common/pa_hostapi.h + src/common/pa_memorybarrier.h + src/common/pa_process.c + src/common/pa_process.h + src/common/pa_ringbuffer.c + src/common/pa_ringbuffer.h + src/common/pa_stream.c + src/common/pa_stream.h + src/common/pa_trace.c + src/common/pa_trace.h + src/common/pa_types.h + src/common/pa_util.h + ) include(GNUInstallDirs) target_include_directories(PortAudio PUBLIC - $ - $ - $ -) + $ + $ + $ + ) if(UNIX) target_compile_options(PortAudio PRIVATE -fPIC) endif() @@ -101,19 +128,19 @@ else() endif() if(WIN32 AND MSVC AND PA_BUILD_SHARED_LIBS - # Check if the user is building PortAudio stand-alone or as part of a larger - # project. If this is part of a larger project (i.e. the CMakeLists.txt has - # been imported by some other CMakeLists.txt), we don't want to override - # that project's global settings. - AND "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}") + # Check if the user is building PortAudio stand-alone or as part of a larger + # project. If this is part of a larger project (i.e. the CMakeLists.txt has + # been imported by some other CMakeLists.txt), we don't want to override + # that project's global settings. + AND "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}") option(PA_DLL_LINK_WITH_STATIC_RUNTIME - "Link with static runtime libraries (minimizes runtime dependencies)" ON) + "Link with static runtime libraries (minimizes runtime dependencies)" ON) if(PA_DLL_LINK_WITH_STATIC_RUNTIME) foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if(${flag_var} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") endif() @@ -151,31 +178,18 @@ if(PA_USE_JACK) install(FILES cmake/modules/FindJACK.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") endif() -# Oboe only works on Android, which has a Linux kernel. -find_package(Oboe) -cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) -if(PA_USE_OBOE) - message("-- Oboe structure found") - target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) - set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) - target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") - target_link_libraries(PortAudio PRIVATE Oboe) - - install(FILES cmake/modules/FindOboe.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") -endif() if(WIN32) target_sources(PortAudio PRIVATE - src/os/win/pa_win_coinitialize.c - src/os/win/pa_win_coinitialize.h - src/os/win/pa_win_hostapis.c - src/os/win/pa_win_util.c - src/os/win/pa_win_util.h - src/os/win/pa_win_waveformat.c - src/os/win/pa_win_wdmks_utils.h - src/os/win/pa_x86_plain_converters.h - ) + src/os/win/pa_win_coinitialize.c + src/os/win/pa_win_coinitialize.h + src/os/win/pa_win_hostapis.c + src/os/win/pa_win_util.c + src/os/win/pa_win_util.h + src/os/win/pa_win_waveformat.c + src/os/win/pa_win_wdmks_utils.h + src/os/win/pa_x86_plain_converters.h + ) target_include_directories(PortAudio PRIVATE src/os/win) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_win_waveformat.h) target_link_libraries(PortAudio PRIVATE winmm) @@ -206,19 +220,19 @@ if(WIN32) endif() message(STATUS "Downloading ASIO SDK... ${ASIO_SDK_ZIP_PATH}") file(DOWNLOAD "https://www.steinberg.net/asiosdk" - "${ASIO_SDK_ZIP_PATH}" - STATUS ASIO_DOWNLOAD_STATUS - SHOW_PROGRESS - ) + "${ASIO_SDK_ZIP_PATH}" + STATUS ASIO_DOWNLOAD_STATUS + SHOW_PROGRESS + ) if("${ASIO_DOWNLOAD_STATUS}" EQUAL 0) find_package(ASIO) else() list(GET "${ASIO_DOWNLOAD_STATUS}" 1 DOWNLOAD_ERROR) message(FATAL_ERROR "Error downloading ASIO SDK: ${DOWNLOAD_ERROR} " - "Reconfigure CMake with -DPA_USE_ASIO=OFF to build without ASIO. " - "Alternatively, download the ZIP from https://www.steinberg.net/asiosdk " - "and put it in ${CMAKE_PREFIX_PATH} or ${CMAKE_CURRENT_BINARY_DIR}" - ) + "Reconfigure CMake with -DPA_USE_ASIO=OFF to build without ASIO. " + "Alternatively, download the ZIP from https://www.steinberg.net/asiosdk " + "and put it in ${CMAKE_PREFIX_PATH} or ${CMAKE_CURRENT_BINARY_DIR}" + ) endif() endif() endif() @@ -228,10 +242,10 @@ if(WIN32) target_compile_definitions(PortAudio PUBLIC PA_USE_ASIO=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_ASIO=1") target_sources(PortAudio PRIVATE - src/hostapi/asio/pa_asio.cpp - src/hostapi/asio/iasiothiscallresolver.cpp - src/hostapi/asio/iasiothiscallresolver.h - ) + src/hostapi/asio/pa_asio.cpp + src/hostapi/asio/iasiothiscallresolver.cpp + src/hostapi/asio/iasiothiscallresolver.h + ) else() set(DEF_EXCLUDE_ASIO_SYMBOLS ";") endif() @@ -239,10 +253,10 @@ if(WIN32) option(PA_USE_DS "Enable support for DirectSound" ON) if(PA_USE_DS) target_sources(PortAudio PRIVATE - src/hostapi/dsound/pa_win_ds.c - src/hostapi/dsound/pa_win_ds_dynlink.c - src/hostapi/dsound/pa_win_ds_dynlink.h - ) + src/hostapi/dsound/pa_win_ds.c + src/hostapi/dsound/pa_win_ds_dynlink.c + src/hostapi/dsound/pa_win_ds_dynlink.h + ) target_include_directories(PortAudio PRIVATE src/hostapi/dsound) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_win_ds.h) target_compile_definitions(PortAudio PUBLIC PA_USE_DS=1) @@ -278,9 +292,9 @@ if(WIN32) option(PA_USE_WDMKS "Enable support for WDMKS" ON) if(PA_USE_WDMKS) target_sources(PortAudio PRIVATE - src/os/win/pa_win_wdmks_utils.c - src/hostapi/wdmks/pa_win_wdmks.c - ) + src/os/win/pa_win_wdmks_utils.c + src/hostapi/wdmks/pa_win_wdmks.c + ) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_win_wdmks.h) target_compile_definitions(PortAudio PUBLIC PA_USE_WDMKS=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_WDMKS=1") @@ -297,26 +311,54 @@ if(WIN32) target_sources(PortAudio PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/portaudio.def") endif() elseif(UNIX) - target_sources(PortAudio PRIVATE - src/os/unix/pa_unix_hostapis.c - src/os/unix/pa_unix_util.c - src/os/unix/pa_unix_util.h - ) - target_include_directories(PortAudio PRIVATE src/os/unix) + target_sources(PortAudio PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/os/unix/pa_unix_hostapis.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/os/unix/pa_unix_util.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/os/unix/pa_unix_util.h + ) + target_include_directories(PortAudio PUBLIC src/os/unix) target_link_libraries(PortAudio PRIVATE m) - set(PKGCONFIG_LDFLAGS_PRIVATE "${PKGCONFIG_LDFLAGS_PUBLIC} -lm -lpthread") - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -pthread") + + IF(CMAKE_SYSTEM_NAME STREQUAL "Android") + set(PKGCONFIG_LDFLAGS_PRIVATE "${PA_PKGCONFIG_LDFLAGS} -lm") + ELSE() + set(PKGCONFIG_LDFLAGS_PRIVATE "${PKGCONFIG_LDFLAGS_PUBLIC} -lm -lpthread") + target_link_libraries(PortAudio PRIVATE pthread) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -pthread") + ENDIF() + + + # Oboe only works on Android, which has a Unix kernel. + find_package(Oboe) + cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) + if(PA_USE_OBOE) + message("-- Oboe structure found") + target_include_directories(PortAudio PRIVATE ${OBOE_INCLUDE_DIR}) + target_link_libraries(PortAudio PUBLIC Oboe::oboe) + target_sources(PortAudio PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + endif() + + IF(CMAKE_SYSTEM_NAME STREQUAL "Android") + SET(PA_PKGCONFIG_LDFLAGS "${PA_PKGCONFIG_LDFLAGS} -lm") + SET(PA_LIBRARY_DEPENDENCIES ${PA_LIBRARY_DEPENDENCIES} m) + ELSE() + SET(PA_PKGCONFIG_LDFLAGS "${PA_PKGCONFIG_LDFLAGS} -lm -lpthread") + SET(PA_LIBRARY_DEPENDENCIES ${PA_LIBRARY_DEPENDENCIES} m pthread) + ENDIF() if(APPLE) set(CMAKE_MACOSX_RPATH 1) target_sources(PortAudio PRIVATE - src/hostapi/coreaudio/pa_mac_core.c - src/hostapi/coreaudio/pa_mac_core_blocking.c - src/hostapi/coreaudio/pa_mac_core_blocking.h - src/hostapi/coreaudio/pa_mac_core_internal.h - src/hostapi/coreaudio/pa_mac_core_utilities.c - src/hostapi/coreaudio/pa_mac_core_utilities.h - ) + src/hostapi/coreaudio/pa_mac_core.c + src/hostapi/coreaudio/pa_mac_core_blocking.c + src/hostapi/coreaudio/pa_mac_core_blocking.h + src/hostapi/coreaudio/pa_mac_core_internal.h + src/hostapi/coreaudio/pa_mac_core_utilities.c + src/hostapi/coreaudio/pa_mac_core_utilities.h + ) target_include_directories(PortAudio PRIVATE src/hostapi/coreaudio) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_mac_core.h) @@ -326,12 +368,12 @@ elseif(UNIX) find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED) find_library(CORESERVICES_LIBRARY CoreServices REQUIRED) target_link_libraries(PortAudio PRIVATE - "${COREAUDIO_LIBRARY}" - "${AUDIOTOOLBOX_LIBRARY}" - "${AUDIOUNIT_LIBRARY}" - "${COREFOUNDATION_LIBRARY}" - "${CORESERVICES_LIBRARY}" - ) + "${COREAUDIO_LIBRARY}" + "${AUDIOTOOLBOX_LIBRARY}" + "${AUDIOUNIT_LIBRARY}" + "${COREFOUNDATION_LIBRARY}" + "${CORESERVICES_LIBRARY}" + ) target_compile_definitions(PortAudio PUBLIC PA_USE_COREAUDIO=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_COREAUDIO=1") @@ -339,7 +381,7 @@ elseif(UNIX) set_property(TARGET PortAudio PROPERTY C_STANDARD 11) set(PKGCONFIG_LDFLAGS_PRIVATE - "${PKGCONFIG_LDFLAGS_PRIVATE} -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework CoreFoundation -framework CoreServices") + "${PKGCONFIG_LDFLAGS_PRIVATE} -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework CoreFoundation -framework CoreServices") else() # Some BSDs have a reimplementation of alsalib, so do not explicitly check for Linux. find_package(ALSA) @@ -401,53 +443,53 @@ if(NOT CMAKE_FRAMEWORK) configure_file(cmake/portaudio-2.0.pc.in "${CMAKE_CURRENT_BINARY_DIR}/portaudio-2.0.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/portaudio-2.0.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") configure_package_config_file(cmake/PortAudioConfig.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio" - NO_CHECK_REQUIRED_COMPONENTS_MACRO - ) + "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio" + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" - VERSION "${PORTAUDIO_VERSION}" - COMPATIBILITY SameMajorVersion + "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" + VERSION "${PORTAUDIO_VERSION}" + COMPATIBILITY SameMajorVersion ) install(EXPORT PortAudio-targets NAMESPACE "PortAudio::" FILE "PortAudioTargets.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") export(TARGETS PortAudio - FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioTargets.cmake") + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioTargets.cmake") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") + "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") if(NOT TARGET uninstall) configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) add_custom_target(uninstall - COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") endif() endif() set_target_properties(PortAudio PROPERTIES - OUTPUT_NAME portaudio - PUBLIC_HEADER "${PORTAUDIO_PUBLIC_HEADERS}" - MACOSX_FRAMEWORK_IDENTIFIER com.portaudio - FRAMEWORK_VERSION A - WINDOWS_EXPORT_ALL_SYMBOLS TRUE - VERSION ${PROJECT_VERSION} - SOVERSION 2 -) + OUTPUT_NAME portaudio + PUBLIC_HEADER "${PORTAUDIO_PUBLIC_HEADERS}" + MACOSX_FRAMEWORK_IDENTIFIER com.portaudio + FRAMEWORK_VERSION A + WINDOWS_EXPORT_ALL_SYMBOLS TRUE + VERSION ${PROJECT_VERSION} + SOVERSION 2 + ) install(TARGETS PortAudio - EXPORT PortAudio-targets - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" -) + EXPORT PortAudio-targets + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) # # Subdirectories @@ -469,9 +511,9 @@ if(PA_BUILD_TESTS) target_link_libraries(${appl_name} m) endif() set_target_properties(${appl_name} - PROPERTIES - FOLDER "Test" - ) + PROPERTIES + FOLDER "Test" + ) endmacro() subdirs(test) From ff72947fe07aed8735c16b684b0af5fa75179ba8 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 12:40:23 +0200 Subject: [PATCH 031/125] fixed FindOboe.cmake --- cmake/modules/FindOboe.cmake | 49 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 9a7ddbd8a..23fd02308 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -1,16 +1,16 @@ #[=======================================================================[.rst: -FindOboe +Findoboe -------- -Finds the Oboe library. OBOE_DIRECTORY has to be set to the path of the directory where -the Oboe repository was cloned (see src/hostapi/oboe/README.md for more information). +Finds the oboe library. OBOE_DIRECTORY has to be set to the path of the directory where +the oboe repository was cloned (see src/hostapi/oboe/README.md for more information). Imported Targets ^^^^^^^^^^^^^^^^ This module provides the following imported target, if found: -``Oboe`` +``Oboe::oboe`` The OBOE library #]=======================================================================] @@ -23,51 +23,52 @@ endif() if(NOT OBOE_DIRECTORY) message(AUTHOR_WARNING "If you're trying to use Oboe as a Host API, please specify the directory where you cloned its repository. For further information, please read src/hostapi/oboe/README.md" - ) + ) set(OBOE_FOUND FALSE) else() if(NOT DEFINED OBOE_INCLUDE_DIR) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) endif() - if(NOT DEFINED OBOE_LINK_LIBRARIES) + if(NOT DEFINED OBOE_LIBRARIES) set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) - set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) + set(OBOE_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) endif() find_package(PkgConfig QUIET) if(PkgConfig_FOUND) pkg_check_modules(OBOE Oboe) else() - find_library(OBOE_LINK_LIBRARIES - NAMES liboboe.so - HINTS ${OBOE_LIBRARY_DIRS} - DOC "Oboe Library" - ) + find_library(OBOE_LIBRARIES + NAMES liboboe.so + HINTS ${OBOE_LIBRARY_DIRS} + DOC "Oboe Library" + ) find_path(OBOE_INCLUDE_DIR - NAMES oboe/Oboe.h - DOC "Oboe header" - ) + NAMES oboe/Oboe.h + DOC "Oboe header" + ) endif() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool - list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) + + set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARIES} ${LOG_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( - Oboe - DEFAULT_MSG - OBOE_LINK_LIBRARIES - OBOE_INCLUDE_DIR + Oboe + DEFAULT_MSG + OBOE_LINK_LIBRARIES + OBOE_INCLUDE_DIR ) if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) set(OBOE_FOUND TRUE) - if(NOT TARGET Oboe) - add_library(Oboe INTERFACE IMPORTED) - target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") - target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + if(NOT TARGET Oboe::oboe) + add_library(Oboe::oboe INTERFACE IMPORTED GLOBAL) + target_link_libraries(Oboe::oboe INTERFACE "${OBOE_LINK_LIBRARIES}") + target_include_directories(Oboe::oboe INTERFACE "${OBOE_INCLUDE_DIR}") endif() endif() endif() \ No newline at end of file From c6fb36425bc243b0c5b81417bc77057ace1c6792 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:10:49 +0200 Subject: [PATCH 032/125] Preparation to start patching - TODOS and directory src/hostapi/Oboe --- .idea/.gitignore | 3 +++ .idea/misc.xml | 6 ++++++ .idea/modules.xml | 8 ++++++++ .idea/portaudio.iml | 9 +++++++++ .idea/vcs.xml | 6 ++++++ README.md | 2 +- include/portaudio.h | 2 ++ src/common/pa_hostapi.h | 2 ++ src/hostapi/skeleton/README.txt | 3 ++- 9 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/portaudio.iml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..26d33521a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..639900d13 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..f2d867481 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/portaudio.iml b/.idea/portaudio.iml new file mode 100644 index 000000000..d6ebd4805 --- /dev/null +++ b/.idea/portaudio.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index cb33144cc..5bbdfbd8b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Please feel free to join. See http://www.portaudio.com for details. src/os = os specific (but host api neutral) code src/hostapi = implementations for different host apis - +[//]: # (TODO: Add Oboe) ### Host API Implementations: src/hostapi/alsa = Advanced Linux Sound Architecture (ALSA) diff --git a/include/portaudio.h b/include/portaudio.h index 9eaeeaedb..daa467c9d 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -273,6 +273,8 @@ PaHostApiIndex Pa_GetDefaultHostApi( void ); @see PaHostApiInfo */ + +//TODO: PaOboe - add paOboe = something typedef enum PaHostApiTypeId { paInDevelopment=0, /* use while developing support for a new host API */ diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h index 4ac3ab60e..696b2879f 100644 --- a/src/common/pa_hostapi.h +++ b/src/common/pa_hostapi.h @@ -153,6 +153,8 @@ are defaulted to 1. #define PA_USE_ASIHPI 1 #endif +//TODO: add PA_USE_OBOE + #ifdef __cplusplus extern "C" { diff --git a/src/hostapi/skeleton/README.txt b/src/hostapi/skeleton/README.txt index 39d4c8d2b..29390cb4f 100644 --- a/src/hostapi/skeleton/README.txt +++ b/src/hostapi/skeleton/README.txt @@ -1 +1,2 @@ -pa_hostapi_skeleton.c provides a starting point for implementing support for a new host API with PortAudio. The idea is that you copy it to a new directory inside /hostapi and start editing. \ No newline at end of file +pa_hostapi_skeleton.c provides a starting point for implementing support for a new host API with PortAudio. +The idea is that you copy it to a new directory inside /hostapi and start editing. \ No newline at end of file From 9a7349f1e00bd0d940db8acbc0ddfd69a2c94195 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:40:23 +0200 Subject: [PATCH 033/125] Added src/hostapi/oboe/README.md --- src/hostapi/oboe/README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/hostapi/oboe/README.md diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md new file mode 100644 index 000000000..1cd1f57d4 --- /dev/null +++ b/src/hostapi/oboe/README.md @@ -0,0 +1,38 @@ + +# Portaudio implementation for android using Oboe. + +In order to use this implementation correctly, be sure to include the "portaudio.h" and "pa_oboe.h" +headers in your project. + +Building: +---- +To build portaudio with Oboe, there are some necessary steps: +1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. +2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. + Make sure to correctly link the NDK path in the Oboe build. +3) Set the CMake variable OBOE_DIR to the path of the cloned Oboe repository. + +TODOs: +---- +- Testing. This implementation was non-extensively tested for VoIP calls and blocking streams - for + everything else, it should have a decent structure. + +- Implementing onErrorAfterClose in a way that works, and checking the callback methods. + +Misc +---- +### Latency and Sharing Mode: +Using LowLatency and SharingMode Exclusive is possible, but a function in pa_oboe.h that sets said flags *is yet to be implemented*, so you'll have to manually set those properties in the OboeEngine::OpenStream function. + + +### Audio Format: +If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream by modifying the format selection marked with a *FIXME*. +I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat will not get you anywhere. + + +### Buffer sizes: +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. + + +### Device selection and/or switching mid-stream: +Device selection can be handled by a java/kotlin method that uses getDevices() in order to identify which device to select. Switching mid-stream gives an oboe::ErrorDisconnected result, and you'll have to stop, close and reopen the involved streams with an unspecified device (or a specific device if you know its ID). From 78c202b7b1553ebbd907faaf96ef29d001155b14 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:52:14 +0200 Subject: [PATCH 034/125] Added include/pa_oboe.h and src/hostapi/oboe/pa_oboe.cpp --- include/pa_oboe.h | 104 ++ src/hostapi/oboe/pa_oboe.cpp | 1899 ++++++++++++++++++++++++++++++++++ 2 files changed, 2003 insertions(+) create mode 100644 include/pa_oboe.h create mode 100644 src/hostapi/oboe/pa_oboe.cpp diff --git a/include/pa_oboe.h b/include/pa_oboe.h new file mode 100644 index 000000000..19b2d0447 --- /dev/null +++ b/include/pa_oboe.h @@ -0,0 +1,104 @@ +/* + * $Id: + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * + **************************************************************************************** + * Author: * + * Carlo Benfatti * + **************************************************************************************** + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#ifndef PA_OBOE_H +#define PA_OBOE_H + +/** + * @file + * @ingroup public_header + * @brief Android Oboe-specific PortAudio API extension header file. + */ + +#include "oboe/Oboe.h" + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +#define TIMEOUT_NS 1000000000 + +/** + * The android stream type and recording preset as defined in Definitions.h + */ +typedef struct PaOboeStreamInfo { + oboe::Usage androidOutputUsage; + oboe::InputPreset androidInputPreset; +} PaOboeStreamInfo; + + +/** + * Provide PA Oboe with the ID of the device the user chose - oboe cannot build a device list, + * but can select the device if provided with its ID. + * @param direction - the direction of the stream for which we want to set the device. + * @param deviceID - the ID of the device chose by the user. + */ +void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); + + +/** + * Provide PA Oboe with native buffer information. If you call this function, you must do so before + * calling Pa_Initialize. To have optimal latency, this function should be called - otherwise, + * PA Oboe will use potentially non-optimal values as default. + * @param bufferSize the native buffersize as returned by AudioManager's + * PROPERTY_OUTPUT_FRAMES_PER_BUFFER. It is recommended you set the number of buffers to 1 if API>17 + * as well, and use the sample rate defined in AudioManager's android.media.property.OUTPUT_SAMPLE_RATE. + * All three together will enable the AUDIO_OUTPUT_FLAG_FAST flag. + */ +void PaOboe_SetNativeBufferSize(unsigned long bufferSize); + +/** + * Provide PA Oboe with native buffer information. If you call this function, you must do so before + * calling Pa_Initialize. To have optimal latency and enable the AUDIO_OUTPUT_FLAG_FAST flag, this + * function should be called - otherwise, PA Oboe will use potentially non-optimal values (2) as default. + * @param buffers The number of buffers can be reduced to 1 on API >17. Make sure you set the native + * buffer size when doing this, and use the sample rate defined in AudioManager's + * android.media.property.OUTPUT_SAMPLE_RATE. + */ +void PaOboe_SetNumberOfBuffers(unsigned buffers); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif //PA_OBOE_H \ No newline at end of file diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp new file mode 100644 index 000000000..9ffc76620 --- /dev/null +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -0,0 +1,1899 @@ +/* + * $Id$ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * + **************************************************************************************** + * Author: * + * Carlo Benfatti * + **************************************************************************************** + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ +#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions" + +/** @file + @ingroup hostapi_src + @brief Oboe implementation of support for a host API. +*/ +#include "pa_allocation.h" +#include "pa_cpuload.h" +#include "pa_debugprint.h" +#include "pa_hostapi.h" +#include "pa_process.h" +#include "pa_stream.h" +#include "pa_unix_util.h" +#include "pa_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "oboe/Oboe.h" + +#include +#include + +#include "pa_oboe.h" + +#define MODULE_NAME "PaOboe" + +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,MODULE_NAME, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,MODULE_NAME, __VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,MODULE_NAME, __VA_ARGS__) + +//TODO: PaOboe_Patch: PaUtil_AllocateZeroInitializedMemory replaces PaUtil_AllocateMemory (same with GoupAllocations) + +// Copied from @{pa_opensles.c}. +#define ENSURE(expr, errorText) \ + do \ + { \ + PaError m_err; \ + if (UNLIKELY((m_err = (expr)) < paNoError)) \ + { \ + PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( \ + __LINE__ ) "\n")); \ + PaUtil_SetLastHostErrorInfo(paInDevelopment, m_err, errorText); \ + m_error = m_err; \ + goto error; \ + } \ + } while (0); + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +static void Terminate(struct PaUtilHostApiRepresentation *hostApi); + +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate); + +static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, + PaStream **s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData); + +static PaError CloseStream(PaStream *stream); + +static PaError StartStream(PaStream *stream); + +static PaError StopStream(PaStream *stream); + +static PaError AbortStream(PaStream *stream); + +static PaError IsStreamStopped(PaStream *s); + +static PaError IsStreamActive(PaStream *stream); + +static PaTime GetStreamTime(PaStream *stream); + +static double GetStreamCpuLoad(PaStream *stream); + +static PaError ReadStream(PaStream *stream, void *buffer, unsigned long frames); + +static PaError WriteStream(PaStream *stream, const void *buffer, unsigned long frames); + +static void StreamProcessingCallback(void *userData); + +static signed long GetStreamReadAvailable(PaStream *stream); + +static signed long GetStreamWriteAvailable(PaStream *stream); + +static unsigned long GetApproximateLowBufferSize(); + +// Commonly used parameters initialized. +static unsigned long nativeBufferSize = 0; +static unsigned numberOfBuffers = 2; + +using namespace oboe; + +int32_t inputDeviceId = kUnspecified; +int32_t outputDeviceId = kUnspecified; + +/** + * Stream structure, useful to store relevant information. It's needed by Portaudio. + */ +typedef struct OboeStream { + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + bool isBlocking; + bool isStopped; + bool isActive; + bool doStop; + bool doAbort; + bool hasOutput; + bool hasInput; + + int callbackResult; + DataCallbackResult oboeCallbackResult; + PaStreamCallbackFlags cbFlags; + + PaSampleFormat inputFormat; + PaSampleFormat outputFormat; + + // Buffers are managed by the callback function in Oboe. + void **outputBuffers; + int currentOutputBuffer; + void **inputBuffers; + int currentInputBuffer; + + long engineAddress; + unsigned long framesPerHostCallback; + unsigned bytesPerFrame; +} OboeStream; + + +/** + * Stream engine of the host API - Oboe. We allocate only one instance of the engine, and + * we call its functions when we want to operate directly on Oboe. More infos on each functions are + * provided right before their implementations. + */ +class OboeEngine : public AudioStreamCallback { +public: + OboeEngine(); + + //Stream-managing functions + bool tryStream(Direction direction, int32_t sampleRate, int32_t channelCount); + PaError openStream(Direction direction, int32_t sampleRate, + Usage outputUsage, InputPreset inputPreset); + bool startStream(); + bool stopStream(); + bool restartStream(int direction); + bool closeStream(); + bool abortStream(); + + //Callback function for non-blocking streams and some callback utils + DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, + int32_t numFrames) override; + void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + void resetCallbackCounters(); + + //Blocking read/write functions + bool writeStream(const void *buffer, int32_t framesToWrite); + bool readStream(void *buffer, int32_t framesToRead); + + //Engine utils + OboeStream* initializeOboeStream(); + void setEngineAddress(long address); + +private: + //The only instance of OboeStream that will be used + OboeStream *oboeStream; + + //The only instances of output and input streams that will be used, and their builders + std::shared_ptr outputStream; + AudioStreamBuilder outputBuilder; + std::shared_ptr inputStream; + AudioStreamBuilder inputBuilder; + + //callback utils + unsigned long framesProcessed{}; + PaStreamCallbackTimeInfo timeInfo{}; + struct timespec timeSpec{}; + + //Conversion utils + static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); + + //device selection implementation + int32_t getSelectedDevice(oboe::Direction direction); +}; + + +/** + * Structure used by Portaudio to interface with the HostApi - in this case, Oboe. + */ +typedef struct PaOboeHostApiRepresentation { + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + OboeEngine *oboeEngine; +} PaOboeHostApiRepresentation; + + +/*----------------------------- OboeEngine functions implementation -----------------------------*/ + + +/** + * \brief Initializes an instance of the engine. + */ +OboeEngine::OboeEngine() { + oboeStream = nullptr; +} + + +/** + * \brief Tries to open a stream with the direction @direction, sample rate @sampleRate and/or + * channel count @channelCount. It then checks if the stream was in fact opened with the + * desired settings, and then closes the stream. It's used to see if the requested + * parameters are supported by the devices that are going to be used. + * @param direction the Direction of the stream; + * @param sampleRate the sample rate we want to try; + * @param channelCount the channel count we want to try; + * @return true if the requested sample rate / channel count is supported by the device, false if + * they aren't, or if tryStream couldn't open a stream. + */ +bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { + AudioStreamBuilder m_builder; + Result m_result; + bool m_outcome = false; + + m_builder.setDeviceId(getSelectedDevice(direction)) + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + ->setFormat(AudioFormat::I16) + ->setDirection(direction) + ->setSampleRate(sampleRate) + ->setChannelCount(channelCount); + if (direction == Direction::Input) { + m_result = m_builder.openStream(inputStream); + } else { + m_result = m_builder.openStream(outputStream); + } + + if (m_result != Result::OK) { + LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", + convertToText(m_result)); + return m_outcome; + } + + if (sampleRate != kUnspecified) { + m_outcome = (sampleRate == m_builder.getSampleRate()); + if(!m_outcome) { + LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", + sampleRate, m_builder.getSampleRate()); + } + } else if (channelCount != kUnspecified) { + m_outcome = (channelCount == m_builder.getChannelCount()); + if(!m_outcome) { + LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", + channelCount, m_builder.getChannelCount()); + } + } else { + LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); + m_outcome = false; + } + + if (direction == Direction::Input) + inputStream->close(); + else + outputStream->close(); + + return m_outcome; +} + + +/** + * \brief Opens an audio stream of oboeStream with a specific direction, sample rate and, + * depending on the direction of the stream, sets its usage (if + * direction == Ditrction::Output) or its preset (if direction == Direction::Input). + * Moreover, this function checks if the stream is blocking, and sets its callback + * function if not. + * @param direction The Oboe::Direction of the stream we want to open; + * @param sampleRate The sample rate of the stream we want to open; + * @param androidOutputUsage The Oboe::Usage of the output stream we want to open + * (only matters with Android Api level >= 28); + * @param androidInputPreset The Preset of the input stream we want to open + * (only matters with Android Api level >= 28). + * @return paNoError if everything goes as expected, paUnanticipatedHostError if Oboe fails to open + * a stream, and paInsufficientMemory if the memory allocation of the buffers fails. + */ +PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, + Usage androidOutputUsage, InputPreset androidInputPreset) { + PaError m_error = paNoError; + Result m_result; + + if (direction == Direction::Input) { + inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) + ->setSampleRate(sampleRate) + ->setDirection(Direction::Input) + ->setDeviceId(getSelectedDevice(Direction::Input)) + ->setInputPreset(androidInputPreset) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); + + if (!(oboeStream->isBlocking)) { + resetCallbackCounters(); + inputBuilder.setDataCallback(this) + ->setPerformanceMode(PerformanceMode::LowLatency); + } + + m_result = inputBuilder.openStream(inputStream); + + if (m_result != Result::OK) { + LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", + convertToText(m_result)); + m_error = paUnanticipatedHostError; + return m_error; + } + + inputStream->setBufferSizeInFrames(inputStream->getFramesPerBurst() * numberOfBuffers); + oboeStream->inputBuffers = + (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + + for (int i = 0; i < numberOfBuffers; ++i) { + oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.inputChannelCount); + + if (!oboeStream->inputBuffers[i]) { + for (int j = 0; j < i; ++j) + PaUtil_FreeMemory(oboeStream->inputBuffers[j]); + PaUtil_FreeMemory(oboeStream->inputBuffers); + inputStream->close(); + m_error = paInsufficientMemory; + break; + } + } + oboeStream->currentInputBuffer = 0; + } else { + outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) + ->setSampleRate(sampleRate) + ->setDirection(Direction::Output) + ->setDeviceId(getSelectedDevice(Direction::Output)) + ->setUsage(androidOutputUsage) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); + + if (!(oboeStream->isBlocking)) { + resetCallbackCounters(); + outputBuilder.setDataCallback(this) + ->setPerformanceMode(PerformanceMode::LowLatency); + } + + m_result = outputBuilder.openStream(outputStream); + if (m_result != Result::OK) { + LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", + convertToText(m_result)); + m_error = paUnanticipatedHostError; + return m_error; + } + + outputStream->setBufferSizeInFrames(outputStream->getFramesPerBurst() * numberOfBuffers); + oboeStream->outputBuffers = + (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + + for (int i = 0; i < numberOfBuffers; ++i) { + oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.outputChannelCount); + + if (!oboeStream->outputBuffers[i]) { + for (int j = 0; j < i; ++j) + PaUtil_FreeMemory(oboeStream->outputBuffers[j]); + PaUtil_FreeMemory(oboeStream->outputBuffers); + outputStream->close(); + m_error = paInsufficientMemory; + break; + } + } + oboeStream->currentOutputBuffer = 0; + } + + return m_error; +} + + +/** + * \brief Starts oboeStream - both input and output audiostreams are checked + * and requested to be started. + * @return true if the streams we wanted to start are started successfully, false otherwise. + */ +bool OboeEngine::startStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if (oboeStream->hasInput) { + m_inputResult = inputStream->requestStart(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", + convertToText(m_inputResult)); + } + if (oboeStream->hasOutput) { + m_outputResult = outputStream->requestStart(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", + convertToText(m_outputResult)); + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Stops oboeStream - both input and output audiostreams are checked + * and requested to be stopped. + * @return true if the streams we wanted to stop are stopped successfully, false otherwise. + */ +bool OboeEngine::stopStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if (oboeStream->hasInput) { + m_inputResult = inputStream->requestStop(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", + convertToText(m_inputResult)); + } + if (oboeStream->hasOutput) { + m_outputResult = outputStream->requestStop(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", + convertToText(m_outputResult)); + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Called when it's needed to restart the oboeStream's audio stream(s), mainly when the + * audio devices change while a stream is started. + * @return true if the stream is restarted successfully, false otherwise. + */ +bool OboeEngine::restartStream(int direction) { + bool m_outcome = true; + Result m_result; + + //TODO: Test if KCTI crashes when ErrorDisconnected occurs + switch (direction) { + case 1: //output-only + //stopping and closing + m_result = outputStream->stop(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", + convertToText(m_result)); + m_result = outputStream->close(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", + convertToText(m_result)); + + //reopening and restarting + m_result = outputBuilder.openStream(outputStream); + if (m_result != Result::OK) + LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", + convertToText(m_result)); + m_result = outputStream->start(); + if (m_result != Result::OK) { + LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", + convertToText(m_result)); + m_outcome = false; + } + break; + + case 2: //input-only + //stopping and closing + m_result = inputStream->stop(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", + convertToText(m_result)); + m_result = inputStream->close(); + if (m_result != Result::OK) + LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", + convertToText(m_result)); + + //reopening and restarting + m_result = inputBuilder.openStream(inputStream); + if (m_result != Result::OK) + LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", + convertToText(m_result)); + m_result = inputStream->start(); + if (m_result != Result::OK) { + LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", + convertToText(m_result)); + m_outcome = false; + } + break; + + default: + // unspecified direction or both directions, abort streams + LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); + m_outcome = (restartStream(1) && restartStream(2)); + break; + } + + return m_outcome; +} + + +/** + * \brief Closes oboeStream - both input and output audiostreams are checked + * and closed if active. + * @return true if the stream is closed successfully, otherwise returns false. + */ +bool OboeEngine::closeStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if(oboeStream == nullptr){ + LOGE("[OboeEngine::closeStream]\t Tried to close a NULL stream. Exiting closeStream."); + return false; + } + + if (oboeStream->hasOutput) { + m_outputResult = outputStream->close(); + if (m_outputResult == Result::ErrorClosed) { + m_outputResult = Result::OK; + LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); + } + } + if (oboeStream->hasInput) { + m_inputResult = inputStream->close(); + if (m_inputResult == Result::ErrorClosed) { + m_inputResult = Result::OK; + LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); + } + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Stops oboeStream - both input and output audiostreams are checked and forcefully stopped. + * @return true if the output stream and the input stream are stopped successfully, false otherwise. + */ +bool OboeEngine::abortStream() { + Result m_outputResult = Result::OK, m_inputResult = Result::OK; + + if(oboeStream == nullptr){ + LOGE("[OboeEngine::abortStream]\t Tried to abort a NULL stream. Exiting abortStream."); + return false; + } + + if (oboeStream->hasInput) { + m_inputResult = inputStream->stop(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", + convertToText(m_inputResult)); + m_inputResult = inputStream->close(); + if (m_inputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", + convertToText(m_inputResult)); + } + if (oboeStream->hasOutput) { + m_outputResult = outputStream->stop(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", + convertToText(m_outputResult)); + m_outputResult = outputStream->close(); + if (m_outputResult != Result::OK) + LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", + convertToText(m_outputResult)); + } + + return (m_outputResult == Result::OK && m_inputResult == Result::OK); +} + + +/** + * \brief Oboe's callback routine. FIXME: implement onErrorAfterClose correctly + */ +DataCallbackResult +OboeEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { + + clock_gettime(CLOCK_REALTIME, &timeSpec); + timeInfo.currentTime = (PaTime) (timeSpec.tv_sec + (timeSpec.tv_nsec / 1000000000.0)); + timeInfo.outputBufferDacTime = (PaTime) (oboeStream->framesPerHostCallback + / + oboeStream->streamRepresentation.streamInfo.sampleRate + + timeInfo.currentTime); + timeInfo.inputBufferAdcTime = (PaTime) (oboeStream->framesPerHostCallback + / + oboeStream->streamRepresentation.streamInfo.sampleRate + + timeInfo.currentTime); + + /* check if StopStream or AbortStream was called */ + if (oboeStream->doStop) { + oboeStream->callbackResult = paComplete; + } else if (oboeStream->doAbort) { + oboeStream->callbackResult = paAbort; + } + + PaUtil_BeginCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&oboeStream->bufferProcessor, + &timeInfo, oboeStream->cbFlags); + + if (oboeStream->hasOutput) { + oboeStream->outputBuffers[oboeStream->currentOutputBuffer] = audioData; + PaUtil_SetOutputFrameCount(&oboeStream->bufferProcessor, numFrames); + PaUtil_SetInterleavedOutputChannels(&oboeStream->bufferProcessor, 0, + (void *) ((PaInt16 **) oboeStream->outputBuffers)[oboeStream->currentOutputBuffer], + 0); + } + if (oboeStream->hasInput) { + audioData = oboeStream->inputBuffers[oboeStream->currentInputBuffer]; + PaUtil_SetInputFrameCount(&oboeStream->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&oboeStream->bufferProcessor, 0, + (void *) ((PaInt16 **) oboeStream->inputBuffers)[oboeStream->currentInputBuffer], + 0); + } + + /* continue processing user buffers if cbresult is pacontinue or if cbresult is pacomplete and userbuffers aren't empty yet */ + if (oboeStream->callbackResult == paContinue + || (oboeStream->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&oboeStream->bufferProcessor))) { + framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, + &oboeStream->callbackResult); + } + + /* enqueue a buffer only when there are frames to be processed, + * this will be 0 when paComplete + empty buffers or paAbort + */ + if (framesProcessed > 0) { + if (oboeStream->hasOutput) { + oboeStream->currentOutputBuffer = + (oboeStream->currentOutputBuffer + 1) % numberOfBuffers; + } + if (oboeStream->hasInput) { + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % numberOfBuffers; + } + } + + PaUtil_EndCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer, framesProcessed); + + /* StopStream was called */ + if (framesProcessed == 0 && oboeStream->doStop) { + oboeStream->oboeCallbackResult = DataCallbackResult::Stop; + } + + /* if AbortStream or StopStream weren't called, stop from the cb */ + else if (framesProcessed == 0 && !(oboeStream->doAbort || oboeStream->doStop)) { + oboeStream->isActive = false; + oboeStream->isStopped = true; + if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) + oboeStream->streamRepresentation.streamFinishedCallback( + oboeStream->streamRepresentation.userData); + //oboeStream->oboeCallbackResult = DataCallbackResult::Stop; TODO: Resume this test (onAudioReady) + } + + return oboeStream->oboeCallbackResult; +} + + +/** + * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells + * what error occurred. + */ +void OboeEngine::onErrorAfterClose(AudioStream *audioStream, Result error) { + if (error == oboe::Result::ErrorDisconnected) { + LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + if (!restartStream(0)) + LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); + } + else + LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); +} + + +/** + * \brief Resets callback counters (called at the start of each iteration of onAudioReady + */ +void OboeEngine::resetCallbackCounters() { + framesProcessed = 0; + timeInfo = {0, 0, 0}; +} + + +/** + * \brief Writes frames on the output stream of oboeStream. Used by blocking streams. + * @param buffer The buffer that we want to write on the output stream; + * @param framesToWrite The number of frames that we want to write. + * @return true if the buffer is written correctly, false if the write function returns an error + * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns + * true if the stream is successfully restarted, and false otherwise. + */ +bool OboeEngine::writeStream(const void *buffer, int32_t framesToWrite) { + bool m_outcome = true; + + ResultWithValue m_result = outputStream->write(buffer, framesToWrite, TIMEOUT_NS); + + // If the stream is interrupted because the device suddenly changes, restart the stream. + if (m_result.error() == Result::ErrorDisconnected) { + if (OboeEngine::restartStream(1)) + return true; + } + + if (!m_result) { + LOGE("[OboeEngine::writeStream]\t Error writing stream: %s", convertToText(m_result.error())); + m_outcome = false; + } + return m_outcome; +} + + +/** + * \brief Reads frames from the input stream of oboeStream. Used by blocking streams. + * @param buffer The buffer that we want to read from the input stream; + * @param framesToWrite The number of frames that we want to read. + * @return true if the buffer is read correctly, false if the read function returns an error + * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns + * true if the stream is successfully restarted, and false otherwise. + */ +bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { + bool m_outcome = true; + + ResultWithValue m_result = inputStream->read(buffer, framesToRead, TIMEOUT_NS); + + // If the stream is interrupted because the device suddenly changes, restart the stream. + if (m_result.error() == Result::ErrorDisconnected) { + if (OboeEngine::restartStream(2)) + return true; + } + + if (!m_result) { + LOGE("[OboeEngine::readStream]\t Error reading stream: %s", convertToText(m_result.error())); + m_outcome = false; + } + return m_outcome; +} + + +/** + * \brief Allocates the memory of oboeStream. + * @return the address of the oboeStream. + */ +OboeStream* OboeEngine::initializeOboeStream() { + oboeStream = (OboeStream *) PaUtil_AllocateMemory(sizeof(OboeStream)); + return oboeStream; +} + + +/** + * \brief Sets the engineAddress parameter of oboeStream, useful for recalling the engine whenever + * it's needed. + * @param address the address of the only instance of OboeEngine. + */ +void OboeEngine::setEngineAddress(long address) { + oboeStream->engineAddress = address; +} + +/** + * \brief Converts PaSampleFormat values into Oboe::AudioFormat values. + * @param paFormat the PaSampleFormat we want to convert. + * @return the converted AudioFormat. + */ +AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { + AudioFormat m_oboeFormat; + switch (paFormat) { + case paFloat32: + m_oboeFormat = AudioFormat::Float; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); + break; + case paInt16: + m_oboeFormat = AudioFormat::I16; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); + break; + case paInt32: + m_oboeFormat = AudioFormat::I32; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); + break; + case paInt24: + m_oboeFormat = AudioFormat::I24; + LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); + break; + default: + m_oboeFormat = AudioFormat::Unspecified; + LOGW("[OboeEngine::PaToOboeFormat]\t Setting AudioFormat to Unspecified, because Oboe does not support the requested format."); + break; + } + return m_oboeFormat; +} + + +/** + * \brief Function used to implement device selection. Device Ids are kUnspecified by default, but + * can be set to something different via JNI using the function PaOboe_SetSelectedDevice. + * @param direction the Oboe::Direction for which we want to know the device Id. + * @return the device Id of the appropriate direction. + */ +int32_t OboeEngine::getSelectedDevice(Direction direction) { + if (direction == Direction::Input) + return inputDeviceId; + else + return outputDeviceId; +} + + + +/*----------------------------- PaSkeleton functions implementations -----------------------------*/ + +/** + * \brief Checks if the requested sample rate is supported by the output device using + * OboeEngine::tryStream. + * This function is used by PaOboe_Initialize, IsFormatSupported, and OpenStream. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param sampleRate is the sample rate we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample + * rate was changed by Oboe. + */ +PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, + sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); + + /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the + sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Checks if the requested sample rate is supported by the input device using + * OboeEngine::tryStream. + * This function is used by PaOboe_Initialize, IsFormatSupported, and OpenStream. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param sampleRate is the sample rate we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample + * rate was changed by Oboe. + */ +PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, + sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); + + /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the + sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Checks if the requested channel count is supported by the output device using + * OboeEngine::tryStream. Used by PaOboe_Initialize. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param numOfChannels the number of channels we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel + * count was changed by Oboe. + */ +static PaError IsOutputChannelCountSupported( + PaOboeHostApiRepresentation *oboeHostApi, + int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { + LOGE("[PaOboe - IsOutputChannelCountSupported]\t Invalid channel count."); + return paInvalidChannelCount; + } + + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, + kUnspecified, + numOfChannels))) + LOGW("[PaOboe - IsOutputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); + + /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if + the sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Checks if the requested channel count is supported by the input device using + * OboeEngine::tryStream. Used by PaOboe_Initialize. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param numOfChannels the number of channels we want to check. + * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel + * count was changed by Oboe. + */ +static PaError IsInputChannelCountSupported( + PaOboeHostApiRepresentation *oboeHostApi, + int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { + LOGE("[PaOboe - IsInputChannelCountSupported]\t Invalid channel count."); + return paInvalidChannelCount; + } + + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, + kUnspecified, + numOfChannels))) + LOGW("[PaOboe - IsInputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); + + /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if + the sample rate we requested wasn't supported. */ + return paNoError; +} + + +/** + * \brief Initializes common parameters and the OboeEngine, and allocates the memory necessary to + * start the audio streams. + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"); + * @param hostApiIndex is a PaHostApiIndex, the type used to enumerate the host APIs at runtime. + * @return paNoError if no errors occur, or paInsufficientMemory if memory allocation fails; + */ +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) { + PaError m_result = paNoError; + int m_deviceCount; + PaOboeHostApiRepresentation *m_oboeHostApi; + PaDeviceInfo *m_deviceInfoArray; + char *m_deviceName; + + m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateMemory( + sizeof(PaOboeHostApiRepresentation)); + if (!m_oboeHostApi) { + m_result = paInsufficientMemory; + goto error; + } + + m_oboeHostApi->oboeEngine = new OboeEngine(); + + m_oboeHostApi->allocations = PaUtil_CreateAllocationGroup(); + if (!m_oboeHostApi->allocations) { + m_result = paInsufficientMemory; + goto error; + } + + *hostApi = &m_oboeHostApi->inheritedHostApiRep; + // Initialization of infos. + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paInDevelopment; + (*hostApi)->info.name = "android Oboe"; + (*hostApi)->info.defaultOutputDevice = 0; + (*hostApi)->info.defaultInputDevice = 0; + (*hostApi)->info.deviceCount = 0; + + + m_deviceCount = 1; + (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateMemory( + m_oboeHostApi->allocations, sizeof(PaDeviceInfo *) * m_deviceCount); + + if (!(*hostApi)->deviceInfos) { + m_result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateMemory( + m_oboeHostApi->allocations, sizeof(PaDeviceInfo) * m_deviceCount); + if (!m_deviceInfoArray) { + m_result = paInsufficientMemory; + goto error; + } + + for (int i = 0; i < m_deviceCount; ++i) { + PaDeviceInfo *m_deviceInfo = &m_deviceInfoArray[i]; + m_deviceInfo->structVersion = 2; + m_deviceInfo->hostApi = hostApiIndex; + + /* OboeEngine will handle manual device selection through the use of + PaOboe_SetSelectedDevice via a JNI interface that can be implemented. + Portaudio doesn't need to know about this, so we just use a default device. */ + + m_deviceInfo->name = "default"; + + /* Try channels in order of preference - Stereo > Mono. */ + const int32_t m_channelsToTry[] = {2, 1}; + const int32_t m_channelsToTryLength = 2; + + m_deviceInfo->maxOutputChannels = 0; + m_deviceInfo->maxInputChannels = 0; + + for (i = 0; i < m_channelsToTryLength; ++i) { + if (IsOutputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { + m_deviceInfo->maxOutputChannels = m_channelsToTry[i]; + break; + } + } + for (i = 0; i < m_channelsToTryLength; ++i) { + if (IsInputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { + m_deviceInfo->maxInputChannels = m_channelsToTry[i]; + break; + } + } + + /* check sample rates in order of preference */ + const int32_t m_sampleRates[] = {48000, 44100, 32000, 24000, 16000}; + const int32_t m_numberOfSampleRates = 5; + + m_deviceInfo->defaultSampleRate = m_sampleRates[0]; + + for (i = 0; i < m_numberOfSampleRates; ++i) { + if (IsOutputSampleRateSupported( + m_oboeHostApi, m_sampleRates[i]) == paNoError && + IsInputSampleRateSupported( + m_oboeHostApi, m_sampleRates[i]) == paNoError) { + m_deviceInfo->defaultSampleRate = m_sampleRates[i]; + break; + } + } + if (m_deviceInfo->defaultSampleRate == 0) + goto error; + + /* If the user has set nativeBufferSize by querying the optimal buffer size via java, + use the user-defined value since that will offer the lowest possible latency. */ + + if (nativeBufferSize != 0) { + m_deviceInfo->defaultLowInputLatency = + (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultLowOutputLatency = + (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighInputLatency = + (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighOutputLatency = + (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; + } else { + m_deviceInfo->defaultLowInputLatency = + (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultLowOutputLatency = + (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighInputLatency = + (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; + m_deviceInfo->defaultHighOutputLatency = + (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; + } + + (*hostApi)->deviceInfos[i] = m_deviceInfo; + ++(*hostApi)->info.deviceCount; + } + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface(&m_oboeHostApi->callbackStreamInterface, + CloseStream, StartStream, StopStream, + AbortStream, IsStreamStopped, + IsStreamActive, GetStreamTime, + GetStreamCpuLoad, PaUtil_DummyRead, + PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, + PaUtil_DummyGetWriteAvailable); + + PaUtil_InitializeStreamInterface(&m_oboeHostApi->blockingStreamInterface, + CloseStream, StartStream, StopStream, + AbortStream, IsStreamStopped, + IsStreamActive, GetStreamTime, + PaUtil_DummyGetCpuLoad, ReadStream, + WriteStream, GetStreamReadAvailable, + GetStreamWriteAvailable); + + if (m_result == paNoError) + LOGV("[PaOboe - Initialize]\t Oboe host API successfully initialized"); + else + LOGE("[PaOboe - Initialize]\t An unusual error occurred. Error code: %d", m_result); + return m_result; + + error: + if (m_oboeHostApi) { + if (m_oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + } + + PaUtil_FreeMemory(m_oboeHostApi); + } + LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", m_result); + return m_result; +} + + +/** + * \brief Interrupts the stream and frees the memory that was allocated to sustain the stream. + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"). + */ +static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { + auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; + + if (!(m_oboeHostApi->oboeEngine->closeStream())) + LOGW("[PaOboe - Terminate]\t Couldn't close the streams correctly - see OboeEngine::CloseStream logs."); + + if(m_oboeHostApi->oboeEngine != nullptr) + delete m_oboeHostApi->oboeEngine; + + if (m_oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + } + + PaUtil_FreeMemory(m_oboeHostApi); +} + + +/** + * \brief Checks if the initialized values are supported by the selected device(s). + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"); + * @param inputParameters points towards the parameters given to the input stream; + * @param outputParameters points towards the parameters given to the output stream; + * @param sampleRate is the value of the sample rate we want to check if it's supported. + * @return paNoError (== paFormatIsSupported) if no errors occur, otherwise returns an appropriate + * PaError message. + */ +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate) { + PaError m_outcome; + int m_inputChannelCount, m_outputChannelCount; + PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; + auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; + + if (inputParameters) { + m_inputChannelCount = inputParameters->channelCount; + m_inputSampleFormat = inputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if (m_inputSampleFormat & paCustomFormat) { + m_outcome = paSampleFormatNotSupported; + return m_outcome; + } + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) { + m_outcome = paInvalidDevice; + return m_outcome; + } + + /* check that input device can support inputChannelCount */ + if (m_inputChannelCount > + hostApi->deviceInfos[inputParameters->device]->maxInputChannels) { + m_outcome = paInvalidChannelCount; + return m_outcome; + } + + /* validate inputStreamInfo */ + if (inputParameters->hostApiSpecificStreamInfo) { + // Only has an effect on ANDROID_API>=28. + InputPreset m_androidRecordingPreset = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (m_androidRecordingPreset != InputPreset::Generic && + m_androidRecordingPreset != InputPreset::Camcorder && + m_androidRecordingPreset != InputPreset::VoiceRecognition && + m_androidRecordingPreset != InputPreset::VoiceCommunication + // Should I add compatibility with VoicePerformance? + ) { + m_outcome = paIncompatibleHostApiSpecificStreamInfo; + return m_outcome; + } + } + } else { + m_inputChannelCount = 0; + } + + if (outputParameters) { + m_outputChannelCount = outputParameters->channelCount; + m_outputSampleFormat = outputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if (m_outputSampleFormat & paCustomFormat) { + m_outcome = paSampleFormatNotSupported; + return m_outcome; + } + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) { + m_outcome = paInvalidDevice; + return m_outcome; + } + + /* check that output device can support outputChannelCount */ + if (m_outputChannelCount > + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) { + m_outcome = paInvalidChannelCount; + return m_outcome; + } + + /* validate outputStreamInfo */ + if (outputParameters->hostApiSpecificStreamInfo) { + // Only has an effect on ANDROID_API>=28. + Usage m_androidOutputUsage = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (m_androidOutputUsage != Usage::Media && + m_androidOutputUsage != Usage::Notification && + m_androidOutputUsage != Usage::NotificationEvent && + m_androidOutputUsage != Usage::NotificationRingtone && + m_androidOutputUsage != Usage::VoiceCommunication && + m_androidOutputUsage != Usage::VoiceCommunicationSignalling && + m_androidOutputUsage != Usage::Alarm + // See if more are needed. + ) { + m_outcome = paIncompatibleHostApiSpecificStreamInfo; + return m_outcome; + } + } + } else { + m_outputChannelCount = 0; + } + + if (m_outputChannelCount > 0) { + if (IsOutputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { + m_outcome = paInvalidSampleRate; + return m_outcome; + } + } + if (m_inputChannelCount > 0) { + if (IsInputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { + m_outcome = paInvalidSampleRate; + return m_outcome; + } + } + + return paFormatIsSupported; +} + + +/** + * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param androidOutputUsage is an attribute that expresses why we are opening the output stream. + * This information can be used by certain platforms to make more refined volume or + * routing decisions. It only has an effect on Android API 28+. + * @param sampleRate is the sample rate we want for the audio stream we want to initialize. This is used to allocate + * the correct amount of memory. + * @return the value returned by OboeEngine::openStream. + */ +static PaError InitializeOutputStream(PaOboeHostApiRepresentation *oboeHostApi, + Usage androidOutputUsage, double sampleRate) { + + return oboeHostApi->oboeEngine->openStream(Direction::Output, + sampleRate, + androidOutputUsage, + Generic //Won't be used, so we put the default value. + ); +} + + +/** + * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of + * this file); + * @param androidInputPreset is an attribute that defines the audio source. This information + * defines both a default physical source of audio signal, and a recording configuration. + * It only has an effect on Android API 28+. + * @param sampleRate is the sample rate we want for the audio stream we want to initialize. This is used to allocate + * the correct amount of memory. + * @return the value returned by OboeEngine::openStream. + */ +static PaError InitializeInputStream(PaOboeHostApiRepresentation *oboeHostApi, + InputPreset androidInputPreset, double sampleRate) { + + return oboeHostApi->oboeEngine->openStream(Direction::Input, + sampleRate, + Usage::Media, //Won't be used, so we put the default value. + androidInputPreset + ); +} + + +/** + * \brief Opens the portaudio audio stream - while initializing our OboeStream. + * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the + * interface to a host API (see struct in "pa_hostapi.h"); + * @param s points to a pointer to a PaStream, which is an audio stream structure used and built + * by portaudio, which will hold the information of our OboeStream; + * @param inputParameters points towards the parameters given to the input stream; + * @param outputParameters points towards the parameters given to the output stream; + * @param sampleRate the sample rate we want for our stream; + * @param framesPerBuffer the number of frames per buffer we want for our stream; + * @param streamFlags the flags used to control the behavior of a stream; + * @param streamCallback points to a callback function that allows a non-blocking stream to + * receive or transmit data; + * @param userData stores the user data, and is passed to some PaUtil functions without further + * manipulation or checks. + * @return paNoError if no errors occur, or other error codes accordingly with what goes wrong. +*/ +static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, + PaStream **s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData) { + PaError m_error = paNoError; + auto m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; + unsigned long m_framesPerHostBuffer; /* these may not be equivalent for all implementations */ + int m_inputChannelCount, m_outputChannelCount; + PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; + PaSampleFormat m_hostInputSampleFormat, m_hostOutputSampleFormat; + + Usage m_androidOutputUsage = Usage::VoiceCommunication; + InputPreset m_androidInputPreset = InputPreset::Generic; + + OboeStream *m_oboeStream = m_oboeHostApi->oboeEngine->initializeOboeStream(); + + if (!m_oboeStream) { + m_error = paInsufficientMemory; + goto error; + } + + LOGI("[PaOboe - OpenStream]\t OpenStream called."); + + if (inputParameters) { + m_inputChannelCount = inputParameters->channelCount; + m_inputSampleFormat = inputParameters->sampleFormat; + + /* Oboe supports alternate device specification with API>=28, but for now we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default.*/ + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if (m_inputChannelCount > hostApi->deviceInfos[inputParameters->device]->maxInputChannels) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if (inputParameters->hostApiSpecificStreamInfo) { + // Only has an effect on ANDROID_API>=28. + m_androidInputPreset = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (m_androidInputPreset != InputPreset::Generic && + m_androidInputPreset != InputPreset::Camcorder && + m_androidInputPreset != InputPreset::VoiceRecognition && + m_androidInputPreset != InputPreset::VoiceCommunication + // Should I add compatibility with VoicePerformance? + ) + return paIncompatibleHostApiSpecificStreamInfo; + } + /* FIXME: Replace "paInt16" with whatever format you prefer - + * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( + paInt16, m_inputSampleFormat); + m_oboeStream->inputFormat = m_hostInputSampleFormat; + } else { + m_inputChannelCount = 0; + m_inputSampleFormat = m_hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ + m_oboeStream->inputFormat = m_hostInputSampleFormat; + } + + if (outputParameters) { + m_outputChannelCount = outputParameters->channelCount; + m_outputSampleFormat = outputParameters->sampleFormat; + + /* Oboe supports alternate device specification with API>=28, but for now we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default.*/ + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) + return paInvalidDevice; + + /* check that output device can support outputChannelCount */ + if (m_outputChannelCount > + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if (outputParameters->hostApiSpecificStreamInfo) { + m_androidOutputUsage = + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (m_androidOutputUsage != Usage::Media && + m_androidOutputUsage != Usage::Notification && + m_androidOutputUsage != Usage::NotificationEvent && + m_androidOutputUsage != Usage::NotificationRingtone && + m_androidOutputUsage != Usage::VoiceCommunication && + m_androidOutputUsage != Usage::VoiceCommunicationSignalling && + m_androidOutputUsage != Usage::Alarm + // See if more are needed. + ) + return paIncompatibleHostApiSpecificStreamInfo; + } + /* FIXME: Replace "paInt16" with whatever format you prefer - + * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( + paInt16, m_outputSampleFormat); + m_oboeStream->outputFormat = m_hostOutputSampleFormat; + } else { + m_outputChannelCount = 0; + m_outputSampleFormat = m_hostOutputSampleFormat = paInt16; + m_oboeStream->outputFormat = m_hostOutputSampleFormat; + } + + /* validate platform specific flags */ + if ((streamFlags & paPlatformSpecificFlags) != 0) + return paInvalidFlag; /* unexpected platform specific flag */ + + if (framesPerBuffer == paFramesPerBufferUnspecified) { + if (outputParameters) { + m_framesPerHostBuffer = + (unsigned long) (outputParameters->suggestedLatency * sampleRate); + } else { + m_framesPerHostBuffer = + (unsigned long) (inputParameters->suggestedLatency * sampleRate); + } + } else { + m_framesPerHostBuffer = framesPerBuffer; + } + + m_oboeHostApi->oboeEngine->setEngineAddress( + reinterpret_cast(m_oboeHostApi->oboeEngine)); + + if (streamCallback) { + PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), + &m_oboeHostApi->callbackStreamInterface, + streamCallback, userData); + } else { + PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), + &m_oboeHostApi->blockingStreamInterface, + streamCallback, userData); + } + + PaUtil_InitializeCpuLoadMeasurer(&(m_oboeStream->cpuLoadMeasurer), sampleRate); + + m_error = PaUtil_InitializeBufferProcessor(&(m_oboeStream->bufferProcessor), + m_inputChannelCount, + m_inputSampleFormat, + m_hostInputSampleFormat, + m_outputChannelCount, + m_outputSampleFormat, + m_hostOutputSampleFormat, + sampleRate, streamFlags, + framesPerBuffer, + m_framesPerHostBuffer, + paUtilFixedHostBufferSize, + streamCallback, userData); + if (m_error != paNoError) + goto error; + + m_oboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; + m_oboeStream->isBlocking = (streamCallback == nullptr); + m_oboeStream->framesPerHostCallback = m_framesPerHostBuffer; + m_oboeStream->bytesPerFrame = sizeof(int16_t); + m_oboeStream->cbFlags = 0; + m_oboeStream->isStopped = true; + m_oboeStream->isActive = false; + + if (!(m_oboeStream->isBlocking)) {} +// PaUnixThreading_Initialize(); + + if (m_inputChannelCount > 0) { + m_oboeStream->hasInput = true; + m_oboeStream->streamRepresentation.streamInfo.inputLatency = + ((PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( + &(m_oboeStream->bufferProcessor)) + + m_oboeStream->framesPerHostCallback) / sampleRate; + ENSURE(InitializeInputStream(m_oboeHostApi, + m_androidInputPreset, sampleRate), + "Initializing inputstream failed") + } else { m_oboeStream->hasInput = false; } + + if (m_outputChannelCount > 0) { + m_oboeStream->hasOutput = true; + m_oboeStream->streamRepresentation.streamInfo.outputLatency = + ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( + &m_oboeStream->bufferProcessor) + + m_oboeStream->framesPerHostCallback) / sampleRate; + ENSURE(InitializeOutputStream(m_oboeHostApi, + m_androidOutputUsage, sampleRate), + "Initializing outputstream failed"); + } else { m_oboeStream->hasOutput = false; } + + *s = (PaStream *) m_oboeStream; + return m_error; + + error: + if (m_oboeStream) + PaUtil_FreeMemory(m_oboeStream); + + LOGE("[PaOboe - OpenStream]\t Error opening stream(s). Error code: %d", m_error); + + return m_error; +} + + +/** + * \brief Calls OboeEngine::closeStream, and then frees the memory that was allocated to sustain + * the stream(s). When CloseStream() is called, the multi-api layer ensures that the stream + * has already been stopped or aborted. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. + */ +static PaError CloseStream(PaStream *s) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + + if (!(m_oboeEngine->closeStream())) + LOGW("[PaOboe - CloseStream]\t Couldn't close the stream(s) correctly - see OboeEngine::CloseStream logs."); + + PaUtil_TerminateBufferProcessor(&m_stream->bufferProcessor); + PaUtil_TerminateStreamRepresentation(&m_stream->streamRepresentation); + + for (int i = 0; i < numberOfBuffers; ++i) { + if (m_stream->hasOutput) + PaUtil_FreeMemory(m_stream->outputBuffers[i]); + if (m_stream->hasInput) + PaUtil_FreeMemory(m_stream->inputBuffers[i]); + } + + if (m_stream->hasOutput) + PaUtil_FreeMemory(m_stream->outputBuffers); + if (m_stream->hasInput) + PaUtil_FreeMemory(m_stream->inputBuffers); + + PaUtil_FreeMemory(m_stream); + return paNoError; +} + + +/** + * \brief Allocates the memory of the buffers necessary to start a stream, both for output and + * input, then calls OboeEngine::startStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::startStream fails. + */ +static PaError StartStream(PaStream *s) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + + PaUtil_ResetBufferProcessor(&m_stream->bufferProcessor); + + //Checking if the stream(s) are already active. TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) + if (m_stream->isActive) { + LOGW("[PaOboe - StartStream]\t Stream was already active, stopping..."); + StopStream(s); + LOGW("[PaOboe - StartStream]\t Restarting..."); + StartStream(s); + } + + m_stream->currentOutputBuffer = 0; + m_stream->currentInputBuffer = 0; + + /* Initialize buffers */ + for (int i = 0; i < numberOfBuffers; ++i) { + if (m_stream->hasOutput) { + memset(m_stream->outputBuffers[m_stream->currentOutputBuffer], 0, + m_stream->framesPerHostCallback * m_stream->bytesPerFrame * + m_stream->bufferProcessor.outputChannelCount + ); + m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; + } + if (m_stream->hasInput) { + memset(m_stream->inputBuffers[m_stream->currentInputBuffer], 0, + m_stream->framesPerHostCallback * m_stream->bytesPerFrame * + m_stream->bufferProcessor.inputChannelCount + ); + m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; + } + } + + /* Start the processing thread.*/ + if (!m_stream->isBlocking) { + m_stream->callbackResult = paContinue; + m_stream->oboeCallbackResult = DataCallbackResult::Continue; + } + + m_stream->isStopped = false; + m_stream->isActive = true; + m_stream->doStop = false; + m_stream->doAbort = false; + + if (!(m_oboeEngine->startStream())) + return paUnanticipatedHostError; + else + return paNoError; +} + + +/** + * \brief Ends the stream callback, if the stream is not blocking, and calls + * OboeEngine::stopStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::stopStream fails. + */ +static PaError StopStream(PaStream *s) { + PaError m_error = paNoError; + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + + if (m_stream->isStopped) { + LOGW("[PaOboe - StopStream]\t Stream was already stopped."); + } else { + if (!(m_stream->isBlocking)) { + m_stream->doStop = true; + } + if (!(m_oboeEngine->stopStream())) { + LOGE("[PaOboe - StopStream]\t Couldn't stop the stream(s) correctly - see OboeEngine::StopStream logs."); + m_error = paUnanticipatedHostError; + } + + m_stream->isActive = false; + m_stream->isStopped = true; + if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) + m_stream->streamRepresentation.streamFinishedCallback( + m_stream->streamRepresentation.userData); + } + + return m_error; +} + + +/** + * \brief Aborts the stream callback, if the stream is not blocking, and calls + * OboeEngine::abortStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::abortStream fails. + */ +static PaError AbortStream(PaStream *s) { + PaError m_error = paNoError; + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + LOGI("[PaOboe - AbortStream]\t Aborting stream."); + + if (!m_stream->isBlocking) { + m_stream->doAbort = true; + } + + /* stop immediately so enqueue has no effect */ + if (!(m_oboeEngine->abortStream())) { + LOGE("[PaOboe - AbortStream]\t Couldn't abort the stream - see OboeEngine::abortStream logs."); + m_error = paUnanticipatedHostError; + } + + m_stream->isActive = false; + m_stream->isStopped = true; + if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) + m_stream->streamRepresentation.streamFinishedCallback( + m_stream->streamRepresentation.userData); + + return m_error; +} + + +/** + * \brief Copies an input stream buffer by buffer, and calls OboeEngine::readStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream; + * @param buffer is the address of the first sample of the buffer; + * @param frames is the total number of frames to read. + * @return paInternalError if OboeEngine::readStream fails, paNoError otherwise. + */ +static PaError ReadStream(PaStream *s, void *buffer, unsigned long frames) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + void *m_userBuffer = buffer; + unsigned m_framesToRead; + PaError m_error = paNoError; + + while (frames > 0) { + m_framesToRead = PA_MIN(m_stream->framesPerHostCallback, frames); + + if (!(m_oboeEngine->readStream(m_userBuffer, + m_framesToRead * + m_stream->bufferProcessor.inputChannelCount))) + m_error = paInternalError; + + m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; + frames -= m_framesToRead; + } + + return m_error; +} + + +/** + * \brief Copies an output stream buffer by buffer, and calls OboeEngine::writeStream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream; + * @param buffer is the address of the first sample of the buffer; + * @param frames is the total number of frames to write. + * @return paInternalError if OboeEngine::writeStream fails, paNoError otherwise. + */ +static PaError WriteStream(PaStream *s, const void *buffer, unsigned long frames) { + auto *m_stream = (OboeStream *) s; + auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); + const void *m_userBuffer = buffer; + unsigned m_framesToWrite; + PaError m_error = paNoError; + + while (frames > 0) { + m_framesToWrite = PA_MIN(m_stream->framesPerHostCallback, frames); + + if (!(m_oboeEngine->writeStream(m_userBuffer, + m_framesToWrite * + m_stream->bufferProcessor.outputChannelCount))) + m_error = paInternalError; + + m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; + frames -= m_framesToWrite; + } + + return m_error; +} + + +/*-------------------------------- PaSkeleton Secondary Functions --------------------------------*/ + +/** + * \brief Function needed by portaudio to understand how many frames can be read without waiting. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return the minimum number of frames that can be read without waiting. + */ +static signed long GetStreamReadAvailable(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentInputBuffer); +} + + +/** + * \brief Function needed by portaudio to understand how many frames can be written without waiting. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return the minimum number of frames that can be written without waiting. + */ +static signed long GetStreamWriteAvailable(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentOutputBuffer); +} + + +/** + * \brief Function needed by portaudio to understand if the stream is stopped. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return one (1) when the stream is stopped, or zero (0) when the stream is running. + */ +static PaError IsStreamStopped(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->isStopped; +} + + +/** + * \brief Function needed by portaudio to understand if the stream is active. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return one (1) when the stream is active (ie playing or recording audio), or zero (0) otherwise. + */ +static PaError IsStreamActive(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return m_stream->isActive; +} + + +/** + * \brief Function needed by portaudio to get the stream time in seconds. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return The stream's current time in seconds, or 0 if an error occurred. + */ +static PaTime GetStreamTime(PaStream *s) { + return PaUtil_GetTime(); +} + + +/** + * \brief Function needed by portaudio to retrieve CPU usage information for the specified stream. + * @param s points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our OboeStream. + * @return A floating point value, typically between 0.0 and 1.0, where 1.0 indicates that the + * stream callback is consuming the maximum number of CPU cycles possible to maintain + * real-time operation. A value of 0.5 would imply that PortAudio and the stream callback + * was consuming roughly 50% of the available CPU time. The return value may exceed 1.0. + * A value of 0.0 will always be returned for a blocking read/write stream, or if an error + * occurs. + */ +static double GetStreamCpuLoad(PaStream *s) { + auto *m_stream = (OboeStream *) s; + return PaUtil_GetCpuLoad(&m_stream->cpuLoadMeasurer); +} + + +/*----------------------------------- PaOboe Utility Functions -----------------------------------*/ + +/** + * \brief In case that no buffer size was specifically set via PaOboe_setNativeBufferSize, this + * function is called to get a sensible value for the buffer size. + * @return 256 for Android API Level <= 23, 192 otherwise. + */ +static unsigned long GetApproximateLowBufferSize() { +/* FIXME: This function should return the following commented values, but was changed in order to improve + compatibility with KCTI for android. Please use the commented values in normal conditions. */ + +// if (__ANDROID_API__ <= 23) +// return 256; +// else +// return 192; + + return 1024; +} + + +/*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ + +void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { + LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); + if (direction == Direction::Input) + inputDeviceId = deviceID; + else + outputDeviceId = deviceID; +} + + +void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { + nativeBufferSize = bufferSize; +} + + +void PaOboe_SetNumberOfBuffers(unsigned buffers) { + numberOfBuffers = buffers; +} \ No newline at end of file From a879301eb53917e131010351a235536a6731e10c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 12:58:20 +0200 Subject: [PATCH 035/125] Added PA_USE_OBOE section to CMakeLists.txt --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad8e6abb..8c72d7e6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,6 +240,16 @@ if(WIN32) endif() endif() + option(PA_USE_OBOE "Enable support for Oboe" ON) + if(PA_USE_OBOE) + target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + target_link_libraries(PortAudio PRIVATE oboe) + endif() + + option(PA_USE_WMME "Enable support for WMME" ON) if(PA_USE_WMME) target_sources(PortAudio PRIVATE src/hostapi/wmme/pa_win_wmme.c) From b416121e17c4104d8f0f0eca36a789a2e743d02b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 7 Jun 2023 13:00:51 +0200 Subject: [PATCH 036/125] Added PA_USE_OBOE section to CMakeLists.txt --- CMakeLists.txt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c72d7e6c..c3c32ec66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,16 +240,6 @@ if(WIN32) endif() endif() - option(PA_USE_OBOE "Enable support for Oboe" ON) - if(PA_USE_OBOE) - target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) - set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) - target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") - target_link_libraries(PortAudio PRIVATE oboe) - endif() - - option(PA_USE_WMME "Enable support for WMME" ON) if(PA_USE_WMME) target_sources(PortAudio PRIVATE src/hostapi/wmme/pa_win_wmme.c) @@ -354,6 +344,15 @@ elseif(UNIX) endif() endif() + option(PA_USE_OBOE "Enable support for Oboe" ON) + if(PA_USE_OBOE) + target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + target_link_libraries(PortAudio PRIVATE oboe) + endif() + # OSS is intentionally off by default to avoid confusing users of PortAudio # applications. OSS builds but there are no devices available on modern # Linux systems. From 9ae5170bdb931bb658de81fb806b083f844a2c07 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 9 Jun 2023 13:21:02 +0200 Subject: [PATCH 037/125] Heavily reworked CMake dependencies, added FindOboe.cmake, updated pa_oboe.cpp, more work needed --- .github/workflows/MSBuild.yml | 96 ------------------ .github/workflows/autotools.yml | 20 ---- .github/workflows/cmake.yml | 128 ------------------------ .github/workflows/compare_def_files.yml | 17 ---- .github/workflows/whitelint.yml | 17 ---- .gitignore | 3 + .idea/misc.xml | 1 - CMakeLists.txt | 20 ++-- README.md | 1 + cmake/modules/FindOboe.cmake | 65 ++++++++++++ src/hostapi/oboe/pa_oboe.cpp | 22 ++-- 11 files changed, 91 insertions(+), 299 deletions(-) delete mode 100644 .github/workflows/MSBuild.yml delete mode 100644 .github/workflows/autotools.yml delete mode 100644 .github/workflows/cmake.yml delete mode 100644 .github/workflows/compare_def_files.yml delete mode 100644 .github/workflows/whitelint.yml create mode 100644 cmake/modules/FindOboe.cmake diff --git a/.github/workflows/MSBuild.yml b/.github/workflows/MSBuild.yml deleted file mode 100644 index ea90dba46..000000000 --- a/.github/workflows/MSBuild.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: MSBuild MSVC Project File CI - -on: [push, pull_request] - -env: - # Path to the solution file relative to the root of the project. - SOLUTION_FILE_PATH: ./msvc/portaudio.sln - VCPROJ_FILE_PATH: ./msvc/portaudio.vcproj - VCXPROJ_FILE_PATH: ./msvc/portaudio.vcxproj - VCXPROJ_FILTERS_FILE_PATH: ./msvc/portaudio.vcxproj.filters - VCXPROJ_USER_FILE_PATH: ./msvc/portaudio.vcxproj.user - DEF_FILE_PATH: ./msvc/portaudio.def - -jobs: - build: - runs-on: windows-latest - strategy: - matrix: - BUILD_CONFIGURATION: [Release] - BUILD_PLATFORM: [Win32, x64] - - steps: - - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1 - - - uses: actions/checkout@v2 - - - name: Upgrade VC Project File - # We maintain our vcproj file in an old format to maintain backwards compatibility - # This step upgrades the project to the latest version of MSVC - # see https://docs.microsoft.com/en-us/visualstudio/ide/reference/upgrade-devenv-exe?view=vs-2019 - # pipe to file to ensure that it terminates https://stackoverflow.com/questions/48896010/occasionally-occurring-msbuild-error-msb3428/48918105#48918105 - # discussion of using vswhere.exe here: https://stackoverflow.com/questions/65287456/how-to-upgrade-a-visual-studio-project-from-within-a-github-action/65311868#65311868 - run: | - $devenv = & vswhere.exe '-property' productPath - Write-Output "$devenv" - & $devenv "${{env.VCPROJ_FILE_PATH}}" /Upgrade /NoSplash | Out-Null - Write-Output "devenv launched" - while (!(Test-Path "${{env.VCXPROJ_FILE_PATH}}")) { Start-Sleep -Seconds 10 } - Write-Output "vcxproj found" - while (!(Test-Path "${{env.VCXPROJ_FILTERS_FILE_PATH}}")) { Start-Sleep -Seconds 10 } - Write-Output "vcxproj.filters found" - Start-Sleep -Seconds 10 - Write-Output "done." - - - name: Remove ASIO Files and Enable PA_USE_DS=1 - # Process the project files to remove ASIO-related sources and includes (since we can not access the ASIO SDK in a public build) - run: | - # Process .vcxproj file: remove source files - $xdoc = new-object System.Xml.XmlDocument - $vcxprojFile = resolve-path("${{env.VCXPROJ_FILE_PATH}}") - $xdoc.load($vcxprojFile) - $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable - $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) - $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) - Write-Output "deleting ASIO related compilation nodes from .vcxproj:" - Write-Output $nodes - ForEach($node in $nodes) { - $parent = $node.ParentNode - $parent.RemoveChild($node) - } - # Enable DirectSound host API - $nodes = $xdoc.SelectNodes("//vs:PreprocessorDefinitions[contains(., 'PA_USE_DS=0')]", $namespace) - ForEach($node in $nodes) { - $text = $node.InnerText - $node.InnerText = $text -replace 'PA_USE_DS=0', 'PA_USE_DS=1' - } - $xdoc.save($vcxprojFile) - # Process .vcxproj.filters file: remove source files and includes - $vcxprojFiltersFile = resolve-path("${{env.VCXPROJ_FILTERS_FILE_PATH}}") - $xdoc.load($vcxprojFiltersFile) - $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable - $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) - $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) - Write-Output "deleting ASIO related compilation nodes from .vcxproj.filters:" - Write-Output $nodes - ForEach($node in $nodes) { - $parent = $node.ParentNode - $parent.RemoveChild($node) - } - $nodes = $xdoc.SelectNodes("//vs:ClInclude[contains(@Include, 'pa_asio.h')]", $namespace) - Write-Output "deleting ASIO related include nodes from .vcxproj.filters:" - Write-Output $nodes - ForEach($node in $nodes) { - $parent = $node.ParentNode - $parent.RemoveChild($node) - } - $xdoc.save($vcxprojFiltersFile) - # Process .def file: remove PaAsio_ symbols - Set-Content -Path "${{env.DEF_FILE_PATH}}" -Value (Get-Content -Path "${{env.DEF_FILE_PATH}}" | Select-String -Pattern 'PaAsio_' -NotMatch) - - - name: Build - working-directory: ${{env.GITHUB_WORKSPACE}} - # Add additional options to the MSBuild command line here (like platform or verbosity level). - # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference - run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.BUILD_PLATFORM}} ${{env.VCXPROJ_FILE_PATH}} diff --git a/.github/workflows/autotools.yml b/.github/workflows/autotools.yml deleted file mode 100644 index 4d6fb6cb9..000000000 --- a/.github/workflows/autotools.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: autotools build - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build-autotools: - - runs-on: ubuntu-latest - name: Ubuntu - - steps: - - uses: actions/checkout@v2 - - name: configure - run: ./configure - - name: make - run: make diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml deleted file mode 100644 index a25eaabf7..000000000 --- a/.github/workflows/cmake.yml +++ /dev/null @@ -1,128 +0,0 @@ -name: CMake build - -on: [push, pull_request] - -jobs: - build: - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu GCC - os: ubuntu-latest - install_dir: ~/portaudio - vcpkg_triplet: x64-linux - cmake_generator: "Unix Makefiles" - cmake_options: - -DPA_USE_OSS=ON - -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - - name: Ubuntu MinGW - os: ubuntu-latest - install_dir: ~/portaudio - asio_sdk_cache_path: asiosdk.zip - dependencies_extras: mingw-w64 - # FIXME: linking JACK fails with vcpkg. Switch the CMake toolchain file to use vcpkg. The - # toolchain file in this repository is not needed when using a MinGW triplet with vcpkg. - vcpkg_triplet: x64-mingw-static - cmake_generator: "Unix Makefiles" - cmake_options: - -DPA_USE_ASIO=ON - -DASIO_SDK_ZIP_PATH=asiosdk.zip - -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/i686-w64-mingw32.cmake - - name: Windows MSVC - os: windows-latest - install_dir: C:\portaudio - vcpkg_triplet: x64-windows - cmake_generator: "Visual Studio 17 2022" - # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to - # asiosdk instead of asiosdk.zip. - asio_sdk_cache_path: "asiosdk.zip" - # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. - cmake_options: - -DPA_USE_ASIO=ON - -DASIO_SDK_ZIP_PATH="asiosdk.zip" - -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake - - name: Windows MinGW - os: windows-latest - install_dir: C:\portaudio - vcpkg_triplet: x64-mingw-static - cmake_generator: "MinGW Makefiles" - # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to - # asiosdk instead of asiosdk.zip. - asio_sdk_cache_path: "asiosdk.zip" - # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. - cmake_options: - -DPA_USE_ASIO=ON - -DASIO_SDK_ZIP_PATH="asiosdk.zip" - -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake - - name: macOS Clang - os: macOS-latest - install_dir: ~/portaudio - vcpkg_triplet: x64-osx - cmake_generator: "Unix Makefiles" - cmake_options: - -DCMAKE_FRAMEWORK=OFF - -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - - name: macOS Clang framework - os: macOS-latest - install_dir: ~/portaudio - vcpkg_triplet: x64-osx - cmake_generator: "Unix Makefiles" - cmake_options: - -DCMAKE_FRAMEWORK=ON - -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - - runs-on: ${{ matrix.os }} - name: ${{ matrix.name }} - env: - cmake_build_type: RelWithDebInfo - steps: - - name: checkout Git repository - uses: actions/checkout@v2 - - name: "[Ubuntu] install dependencies" - run: | - sudo apt-get update - sudo apt-get install libasound2-dev ${{ matrix.dependencies_extras }} - if: matrix.os == 'ubuntu-latest' - - name: "[macOS] install dependencies" - # https://github.com/PortAudio/portaudio/issues/767 - run: brew install pkg-config - if: matrix.os == 'macOS-latest' - - name: "[Windows/MinGW] set up ASIO SDK cache" - uses: actions/cache@v2 - if: matrix.asio_sdk_cache_path != null - with: - path: ${{ matrix.asio_sdk_cache_path }} - key: ${{ hashFiles('.github/asiosdk-version.txt') }} - - name: Setup vcpkg - uses: lukka/run-vcpkg@v7 - if: ${{ matrix.vcpkg_triplet }} != null - with: - vcpkgTriplet: ${{ matrix.vcpkg_triplet }} - appendedCacheKey: ${{ hashFiles( '**/vcpkg.json' ) }} - additionalCachedPaths: build/vcpkg_installed - vcpkgGitCommitId: f30786c9c4c901f21a13e2d524349e39cc359a90 - # Required when using vcpkg.json manifest in repository - setupOnly: true - - name: configure - run: cmake - -G "${{ matrix.cmake_generator }}" - ${{ matrix.cmake_options }} - -DCMAKE_INSTALL_PREFIX=${{ matrix.install_dir }} - -DCMAKE_BUILD_TYPE=${{ env.cmake_build_type }} - -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} - -DPA_USE_SKELETON=ON - -DPA_BUILD_TESTS=ON - -DPA_BUILD_EXAMPLES=ON - -DPA_WARNINGS_ARE_ERRORS=ON - -S . - -B build - - name: build - run: cmake --build build --config ${{ env.cmake_build_type }} --parallel 2 - - name: install - run: cmake --install build --config ${{ env.cmake_build_type }} - - name: "Upload GitHub Actions artifacts" - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.name }} PortAudio build - path: ${{ matrix.install_dir }} diff --git a/.github/workflows/compare_def_files.yml b/.github/workflows/compare_def_files.yml deleted file mode 100644 index b2b342411..000000000 --- a/.github/workflows/compare_def_files.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Check that PortAudio .def files are in sync - -on: [push, pull_request] - -jobs: - run-pa-compare-def-files-py: - - runs-on: ubuntu-latest - name: Ubuntu - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Run the pa_compare_def_files.py script - run: python ./pa_compare_def_files.py diff --git a/.github/workflows/whitelint.yml b/.github/workflows/whitelint.yml deleted file mode 100644 index 41cf8ff8b..000000000 --- a/.github/workflows/whitelint.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Check for valid whitespace usage in PortAudio source files - -on: [push, pull_request] - -jobs: - run-pa-whitelint-dot-py: - - runs-on: ubuntu-latest - name: Ubuntu - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Run the pa_whitelint.py script - run: python ./pa_whitelint.py diff --git a/.gitignore b/.gitignore index 345708350..72837f411 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ libtool portaudio-2.0.pc autom4te.cache/* +# build_all_PaOboe.sh CMake output folder +build + # Precompiled Headers *.gch *.pch diff --git a/.idea/misc.xml b/.idea/misc.xml index 639900d13..6e8667213 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/CMakeLists.txt b/CMakeLists.txt index c3c32ec66..638aff88c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,17 @@ if(PA_USE_JACK) install(FILES cmake/modules/FindJACK.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") endif() +# Oboe only works on Android, which has a Linux kernel. +find_package(Oboe) +option(PA_USE_OBOE "Enable support for Oboe" ON) +if(PA_USE_OBOE) + target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + target_link_libraries(PortAudio PRIVATE Oboe) +endif() + if(WIN32) target_sources(PortAudio PRIVATE src/os/win/pa_win_coinitialize.c @@ -344,15 +355,6 @@ elseif(UNIX) endif() endif() - option(PA_USE_OBOE "Enable support for Oboe" ON) - if(PA_USE_OBOE) - target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) - set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) - target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") - target_link_libraries(PortAudio PRIVATE oboe) - endif() - # OSS is intentionally off by default to avoid confusing users of PortAudio # applications. OSS builds but there are no devices available on modern # Linux systems. diff --git a/README.md b/README.md index 5bbdfbd8b..de0b74507 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Please feel free to join. See http://www.portaudio.com for details. src/hostapi/coreaudio = Macintosh Core Audio for OS X src/hostapi/dsound = Windows Direct Sound src/hostapi/jack = JACK Audio Connection Kit + src/hostapi/oboe = Oboe Library for Android (see src/hostapi/oboe/README.md) src/hostapi/oss = Unix Open Sound System (OSS) src/hostapi/wasapi = Windows Vista WASAPI src/hostapi/wdmks = Windows WDM Kernel Streaming diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake new file mode 100644 index 000000000..79bc590b4 --- /dev/null +++ b/cmake/modules/FindOboe.cmake @@ -0,0 +1,65 @@ +#[=======================================================================[.rst: +FindOboe +-------- + +Finds the Oboe Directory by searching for it in the PA_DIRECTORY, which is the CMAKE_SOURCE_DIR if +not set. You may manually specify the path of the Oboe Directory with the OBOE_DIRECTORY variable. + +This module provides the following imported target, if found: + ``Oboe`` + +#]=======================================================================] + +if (NOT DEFINED PA_DIRECTORY) + set(PA_DIRECTORY ${CMAKE_SOURCE_DIR}) +endif () + +set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe-main) + +set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) +set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) + +set(OBOE_LIBRARY_DIRS ${OBOE_BUILD_DIR}/${ANDROID_ABI}) +set(OBOE_LIBRARY ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) + +if(OBOE_INCLUDE_DIR) + # Already in cache, be silent + set(OBOE_FIND_QUIETLY TRUE) +else() + find_package(PkgConfig) + pkg_check_modules(PC_OBOE QUIET Oboe) +endif(OBOE_INCLUDE_DIR) + +find_path(OBOE_INCLUDE_DIR + NAMES oboe/Oboe.h + DOC "Oboe include directory") + +find_library(OBOE_LIBRARY + NAMES liboboe.so + HINTS ${OBOE_LIBRARY_DIRS} + DOC "Oboe Shared Library") + +find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool + +# Handle the QUIETLY and REQUIRED arguments and set OPENSL_FOUND to TRUE if +# all listed variables are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + Oboe + DEFAULT_MSG + OBOE_INCLUDE_DIR + OBOE_LIBRARY +) + +if(OBOE_INCLUDE_DIR AND OBOE_LIBRARY) + set(OBOE_FOUND TRUE) + if(NOT TARGET Oboe) + add_library(Oboe INTERFACE IMPORTED) + target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + target_link_libraries(Oboe INTERFACE ${LOG_LIBRARY}) + endif() +else() + if (Oboe_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find OBOE") + endif() +endif() \ No newline at end of file diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 9ffc76620..b110fd938 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -91,7 +91,7 @@ PaError m_err; \ if (UNLIKELY((m_err = (expr)) < paNoError)) \ { \ - PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( \ + PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( \ __LINE__ ) "\n")); \ PaUtil_SetLastHostErrorInfo(paInDevelopment, m_err, errorText); \ m_error = m_err; \ @@ -386,10 +386,10 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, inputStream->setBufferSizeInFrames(inputStream->getFramesPerBurst() * numberOfBuffers); oboeStream->inputBuffers = - (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.inputChannelCount); @@ -429,10 +429,10 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, outputStream->setBufferSizeInFrames(outputStream->getFramesPerBurst() * numberOfBuffers); oboeStream->outputBuffers = - (void **) PaUtil_AllocateMemory(numberOfBuffers * sizeof(int32_t *)); + (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateMemory( + oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.outputChannelCount); @@ -810,7 +810,7 @@ bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { * @return the address of the oboeStream. */ OboeStream* OboeEngine::initializeOboeStream() { - oboeStream = (OboeStream *) PaUtil_AllocateMemory(sizeof(OboeStream)); + oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); return oboeStream; } @@ -989,7 +989,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaDeviceInfo *m_deviceInfoArray; char *m_deviceName; - m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateMemory( + m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateZeroInitializedMemory( sizeof(PaOboeHostApiRepresentation)); if (!m_oboeHostApi) { m_result = paInsufficientMemory; @@ -1015,7 +1015,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex m_deviceCount = 1; - (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateMemory( + (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( m_oboeHostApi->allocations, sizeof(PaDeviceInfo *) * m_deviceCount); if (!(*hostApi)->deviceInfos) { @@ -1024,7 +1024,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex } /* allocate all device info structs in a contiguous block */ - m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateMemory( + m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateZeroInitializedMemory( m_oboeHostApi->allocations, sizeof(PaDeviceInfo) * m_deviceCount); if (!m_deviceInfoArray) { m_result = paInsufficientMemory; @@ -1519,7 +1519,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, m_oboeStream->isActive = false; if (!(m_oboeStream->isBlocking)) {} -// PaUnixThreading_Initialize(); +// PaUnixThreading_Initialize(); TODO: see if threading works with this version of PortAudio if (m_inputChannelCount > 0) { m_oboeStream->hasInput = true; @@ -1896,4 +1896,4 @@ void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { void PaOboe_SetNumberOfBuffers(unsigned buffers) { numberOfBuffers = buffers; -} \ No newline at end of file +} From 7288c90aa06be328c65683d8185c67c463bc1690 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 13 Jun 2023 11:35:15 +0200 Subject: [PATCH 038/125] Included build_all_PaOboe.sh, more work needed on CMake --- .gitignore | 1 - build_all_PaOboe.sh | 49 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100755 build_all_PaOboe.sh diff --git a/.gitignore b/.gitignore index 72837f411..bd63e09b0 100644 --- a/.gitignore +++ b/.gitignore @@ -58,4 +58,3 @@ CMakeSettings.json .vscode* # Common build directories of users and VSCode -build* diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh new file mode 100755 index 000000000..9e0295d2f --- /dev/null +++ b/build_all_PaOboe.sh @@ -0,0 +1,49 @@ +# Script to build Portaudio_Oboe for multiple Android ABIs +# +# Ensure that ANDROID_NDK environment variable is set to your Android NDK location +# e.g. /Library/Android/sdk/ndk-bundle + +#!/bin/bash + +ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 + +if [ -z "$ANDROID_NDK" ]; then + echo "Please set ANDROID_NDK to the Android NDK folder" + exit 1 +fi + +# Build directory +BUILD_DIR=build + +CMAKE_ARGS="-H. \ + -DBUILD_SHARED_LIBS=true \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DANDROID_TOOLCHAIN=clang \ + -DANDROID_STL=c++_shared \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX=." + +function build_PaOboe { + + ABI=$1 + MINIMUM_API_LEVEL=$2 + ABI_BUILD_DIR=${BUILD_DIR}/${ABI} + + echo "Building Pa_Oboe for ${ABI}" + + mkdir -p ${ABI_BUILD_DIR} ${ABI_BUILD_DIR}/${STAGING_DIR} + + cmake -B${ABI_BUILD_DIR} \ + -DANDROID_ABI=${ABI} \ + -DANDROID_PLATFORM=android-${MINIMUM_API_LEVEL}\ + ${CMAKE_ARGS} + + pushd ${ABI_BUILD_DIR} + make -j5 + popd +} + +build_PaOboe armeabi-v7a 16 +build_PaOboe arm64-v8a 21 +build_PaOboe x86 16 +build_PaOboe x86_64 21 From 24465ed472d0547f3ea59de81e7687a195c1f3e2 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 13 Jun 2023 11:35:28 +0200 Subject: [PATCH 039/125] Included build_all_PaOboe.sh, more work needed on CMake --- build_all_PaOboe.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index 9e0295d2f..21f95bd5f 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -5,8 +5,6 @@ #!/bin/bash -ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 - if [ -z "$ANDROID_NDK" ]; then echo "Please set ANDROID_NDK to the Android NDK folder" exit 1 From 63c48b2935bab5717b2c79ad5ef872cb1fe2a605 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 26 Jul 2023 13:18:40 +0200 Subject: [PATCH 040/125] Built shared library --- CMakeLists.txt | 5 ++++ build_all_PaOboe.sh | 3 ++- cmake/modules/FindOboe.cmake | 50 ++++++++++++++++++------------------ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 638aff88c..c2053dc7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,9 @@ add_library(PortAudio include(GNUInstallDirs) +MESSAGE("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}") +MESSAGE("CMAKE_INSTALL_INCLUDEDIR: ${CMAKE_INSTALL_INCLUDEDIR}") + target_include_directories(PortAudio PUBLIC $ $ @@ -159,6 +162,8 @@ if(PA_USE_OBOE) target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") target_link_libraries(PortAudio PRIVATE Oboe) + + install(FILES cmake/modules/FindOboe.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") endif() if(WIN32) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index 21f95bd5f..c5985ef8d 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -1,9 +1,10 @@ +#!/bin/bash # Script to build Portaudio_Oboe for multiple Android ABIs # # Ensure that ANDROID_NDK environment variable is set to your Android NDK location # e.g. /Library/Android/sdk/ndk-bundle -#!/bin/bash +ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 if [ -z "$ANDROID_NDK" ]; then echo "Please set ANDROID_NDK to the Android NDK folder" diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 79bc590b4..94ecce8d1 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -11,55 +11,55 @@ This module provides the following imported target, if found: #]=======================================================================] if (NOT DEFINED PA_DIRECTORY) - set(PA_DIRECTORY ${CMAKE_SOURCE_DIR}) + set(PA_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif () -set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe-main) +MESSAGE("Searching for Oboe...") + +set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) set(OBOE_LIBRARY_DIRS ${OBOE_BUILD_DIR}/${ANDROID_ABI}) -set(OBOE_LIBRARY ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) +set(OBOE_LINK_LIBRARIES ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) -if(OBOE_INCLUDE_DIR) - # Already in cache, be silent - set(OBOE_FIND_QUIETLY TRUE) +find_package(PkgConfig QUIET) +if(PkgConfig_FOUND) + pkg_check_modules(OBOE Oboe) else() - find_package(PkgConfig) - pkg_check_modules(PC_OBOE QUIET Oboe) -endif(OBOE_INCLUDE_DIR) - -find_path(OBOE_INCLUDE_DIR - NAMES oboe/Oboe.h - DOC "Oboe include directory") - -find_library(OBOE_LIBRARY + find_library(OBOE_LINK_LIBRARIES NAMES liboboe.so HINTS ${OBOE_LIBRARY_DIRS} - DOC "Oboe Shared Library") + DOC "Oboe Library" + ) + find_path(OBOE_INCLUDE_DIR + NAMES oboe/Oboe.h + DOC "Oboe header" + ) +endif() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool +list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) + -# Handle the QUIETLY and REQUIRED arguments and set OPENSL_FOUND to TRUE if -# all listed variables are TRUE. include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Oboe DEFAULT_MSG + OBOE_LINK_LIBRARIES OBOE_INCLUDE_DIR - OBOE_LIBRARY ) -if(OBOE_INCLUDE_DIR AND OBOE_LIBRARY) +if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) set(OBOE_FOUND TRUE) if(NOT TARGET Oboe) add_library(Oboe INTERFACE IMPORTED) + target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") - target_link_libraries(Oboe INTERFACE ${LOG_LIBRARY}) - endif() -else() - if (Oboe_FIND_REQUIRED) - message(FATAL_ERROR "Could NOT find OBOE") endif() +#else() +# if (Oboe_FIND_REQUIRED) +# message(FATAL_ERROR "Could NOT find OBOE") +# endif() endif() \ No newline at end of file From 5dab26ba1051343dba53e1ca39ca0c77cb0f5ae4 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 27 Jul 2023 09:40:41 +0200 Subject: [PATCH 041/125] Polished bits --- README.md | 1 - cmake/modules/FindOboe.cmake | 4 ---- include/pa_oboe.h | 4 ++-- src/hostapi/oboe/pa_oboe.cpp | 5 +++-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index de0b74507..a908dcc7f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ Please feel free to join. See http://www.portaudio.com for details. src/os = os specific (but host api neutral) code src/hostapi = implementations for different host apis -[//]: # (TODO: Add Oboe) ### Host API Implementations: src/hostapi/alsa = Advanced Linux Sound Architecture (ALSA) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 94ecce8d1..a8e110969 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -58,8 +58,4 @@ if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") endif() -#else() -# if (Oboe_FIND_REQUIRED) -# message(FATAL_ERROR "Could NOT find OBOE") -# endif() endif() \ No newline at end of file diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 19b2d0447..a1c25d129 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -3,7 +3,7 @@ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * - * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * Android Oboe implementation of PortAudio. * **************************************************************************************** * Author: * @@ -101,4 +101,4 @@ void PaOboe_SetNumberOfBuffers(unsigned buffers); } #endif //__cplusplus -#endif //PA_OBOE_H \ No newline at end of file +#endif //PA_OBOE_H diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index b110fd938..02c7e1c92 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -3,7 +3,7 @@ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * - * Android Oboe implementation of PortAudio based on Sanne Raymaekers' work with OpenSLES. + * Android Oboe implementation of PortAudio. * **************************************************************************************** * Author: * @@ -1454,7 +1454,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paInt16" with whatever format you prefer - - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options + */ m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16, m_outputSampleFormat); m_oboeStream->outputFormat = m_hostOutputSampleFormat; From 35589eef766830f3b0b262ae627898abcef4b956 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 24 Aug 2023 10:54:39 +0200 Subject: [PATCH 042/125] Fixing old problems --- build_all_PaOboe.sh | 6 +++--- cmake/modules/FindOboe.cmake | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index c5985ef8d..a77ec406a 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -42,7 +42,7 @@ function build_PaOboe { popd } -build_PaOboe armeabi-v7a 16 +#build_PaOboe armeabi-v7a 16 build_PaOboe arm64-v8a 21 -build_PaOboe x86 16 -build_PaOboe x86_64 21 +#build_PaOboe x86 16 +#build_PaOboe x86_64 21 diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index a8e110969..1dbd1a132 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -21,8 +21,8 @@ set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) -set(OBOE_LIBRARY_DIRS ${OBOE_BUILD_DIR}/${ANDROID_ABI}) -set(OBOE_LINK_LIBRARIES ${OBOE_BUILD_DIR}/${ANDROID_ABI}/liboboe.so) +set(OBOE_LIBRARY_DIR ${OBOE_DIRECTORY}/lib) +set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIR}/liboboe.so) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) From f6b7814fb852b1d449bdb90e3f9b3764e0abed20 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 24 Aug 2023 12:23:18 +0200 Subject: [PATCH 043/125] Added some debug messages --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c2053dc7d..5f8c82573 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,7 @@ endif() find_package(Oboe) option(PA_USE_OBOE "Enable support for Oboe" ON) if(PA_USE_OBOE) + message("Oboe found!") target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) From 09aa1b877c6b9207adbfd0339cba1b1659e44cae Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 24 Aug 2023 12:33:33 +0200 Subject: [PATCH 044/125] added new build script --- Build_PaOboe.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Build_PaOboe.sh diff --git a/Build_PaOboe.sh b/Build_PaOboe.sh new file mode 100644 index 000000000..f6e97ae78 --- /dev/null +++ b/Build_PaOboe.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Script to build Portaudio_Oboe for multiple Android ABIs +# +# Ensure that ANDROID_NDK environment variable is set to your Android NDK location +# e.g. /Library/Android/sdk/ndk-bundle + +ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 + +if [ -z "$ANDROID_NDK" ]; then + echo "Please set ANDROID_NDK to the Android NDK folder" + exit 1 +fi + +# Build directory +BUILD_DIR=lib + +CMAKE_ARGS="-H. \ + -DBUILD_SHARED_LIBS=true \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DANDROID_TOOLCHAIN=clang \ + -DANDROID_STL=c++_shared \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX=." + +function build_PaOboe { + + echo "Building Pa_Oboe" + + mkdir -p ${BUILD_DIR} ${BUILD_DIR}/${STAGING_DIR} + + cmake -B${BUILD_DIR} \ + -DANDROID_PLATFORM=android-21\ + ${CMAKE_ARGS} + + pushd ${BUILD_DIR} + make -j5 + make install + popd +} + + +build_PaOboe \ No newline at end of file From efb6f1aa4f032a26299f1648b94af07476fa93a4 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 25 Aug 2023 13:12:22 +0200 Subject: [PATCH 045/125] Added paOboe in the paHostApi array --- Build_PaOboe.sh | 1 - build_all_PaOboe.sh | 2 +- cmake/modules/FindOboe.cmake | 4 ++-- include/portaudio.h | 4 ++-- src/common/pa_hostapi.h | 7 ++++++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Build_PaOboe.sh b/Build_PaOboe.sh index f6e97ae78..9d6c2e572 100644 --- a/Build_PaOboe.sh +++ b/Build_PaOboe.sh @@ -34,7 +34,6 @@ function build_PaOboe { pushd ${BUILD_DIR} make -j5 - make install popd } diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index a77ec406a..acbc8ad12 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -45,4 +45,4 @@ function build_PaOboe { #build_PaOboe armeabi-v7a 16 build_PaOboe arm64-v8a 21 #build_PaOboe x86 16 -#build_PaOboe x86_64 21 +build_PaOboe x86_64 21 diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 1dbd1a132..bb04d79ab 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -21,8 +21,8 @@ set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) -set(OBOE_LIBRARY_DIR ${OBOE_DIRECTORY}/lib) -set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIR}/liboboe.so) +set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) +set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) diff --git a/include/portaudio.h b/include/portaudio.h index daa467c9d..8b6c3b875 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -274,7 +274,6 @@ PaHostApiIndex Pa_GetDefaultHostApi( void ); @see PaHostApiInfo */ -//TODO: PaOboe - add paOboe = something typedef enum PaHostApiTypeId { paInDevelopment=0, /* use while developing support for a new host API */ @@ -291,7 +290,8 @@ typedef enum PaHostApiTypeId paJACK=12, paWASAPI=13, paAudioScienceHPI=14, - paAudioIO=15 + paAudioIO=15; + paOboe=16 } PaHostApiTypeId; diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h index 696b2879f..752272f18 100644 --- a/src/common/pa_hostapi.h +++ b/src/common/pa_hostapi.h @@ -153,7 +153,12 @@ are defaulted to 1. #define PA_USE_ASIHPI 1 #endif -//TODO: add PA_USE_OBOE +#ifndef PA_USE_OBOE +#define PA_USE_OBOE 0 +#elif (PA_USE_OBOE != 0) && (PA_USE_OBOE != 1) +#undef PA_USE_OBOE +#define PA_USE_OBOE 1 +#endif #ifdef __cplusplus extern "C" From 3bf8046922def6ded1325101110a8376b2a47641 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 25 Aug 2023 13:16:55 +0200 Subject: [PATCH 046/125] Added paOboe in pa_unix_hostapis.c --- include/portaudio.h | 2 +- src/os/unix/pa_unix_hostapis.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/portaudio.h b/include/portaudio.h index 8b6c3b875..55beb9552 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -290,7 +290,7 @@ typedef enum PaHostApiTypeId paJACK=12, paWASAPI=13, paAudioScienceHPI=14, - paAudioIO=15; + paAudioIO=15, paOboe=16 } PaHostApiTypeId; diff --git a/src/os/unix/pa_unix_hostapis.c b/src/os/unix/pa_unix_hostapis.c index 85fefb267..466f6f846 100644 --- a/src/os/unix/pa_unix_hostapis.c +++ b/src/os/unix/pa_unix_hostapis.c @@ -52,6 +52,8 @@ PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +/* Android HostApi */ +PaError PaOboe_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); /** Note that on Linux, ALSA is placed before OSS so that the former is preferred over the latter. */ @@ -100,6 +102,10 @@ PaUtilHostApiInitializer *paHostApiInitializers[] = PaMacCore_Initialize, #endif +#if PA_USE_OBOE + PaOboe_Initialize, +#endif + #if PA_USE_SKELETON PaSkeleton_Initialize, #endif From b22fee5a31ae65cf9638c24f8624b9e22a10a667 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 25 Aug 2023 13:27:39 +0200 Subject: [PATCH 047/125] Working patch --- src/hostapi/oboe/pa_oboe.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 02c7e1c92..ab0117c03 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -82,8 +82,6 @@ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,MODULE_NAME, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,MODULE_NAME, __VA_ARGS__) -//TODO: PaOboe_Patch: PaUtil_AllocateZeroInitializedMemory replaces PaUtil_AllocateMemory (same with GoupAllocations) - // Copied from @{pa_opensles.c}. #define ENSURE(expr, errorText) \ do \ From 7acde75914ae4ed7bdf93d25c9558b7b7695d206 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 12:09:00 +0200 Subject: [PATCH 048/125] Ready for other users to compile/build --- CMakeLists.txt | 5 +- build_all_PaOboe.sh | 2 +- cmake/modules/FindOboe.cmake | 89 +++++++++++++++++++----------------- src/hostapi/oboe/README.md | 18 ++++++-- 4 files changed, 64 insertions(+), 50 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f8c82573..431680648 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,9 +72,6 @@ add_library(PortAudio include(GNUInstallDirs) -MESSAGE("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}") -MESSAGE("CMAKE_INSTALL_INCLUDEDIR: ${CMAKE_INSTALL_INCLUDEDIR}") - target_include_directories(PortAudio PUBLIC $ $ @@ -155,7 +152,7 @@ endif() # Oboe only works on Android, which has a Linux kernel. find_package(Oboe) -option(PA_USE_OBOE "Enable support for Oboe" ON) +cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) if(PA_USE_OBOE) message("Oboe found!") target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index acbc8ad12..7b8c7e8ad 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Script to build Portaudio_Oboe for multiple Android ABIs +# Script used to build Portaudio for multiple Android ABIs, useful for its Oboe host API. # # Ensure that ANDROID_NDK environment variable is set to your Android NDK location # e.g. /Library/Android/sdk/ndk-bundle diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index bb04d79ab..39e88d32f 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -3,59 +3,66 @@ FindOboe -------- Finds the Oboe Directory by searching for it in the PA_DIRECTORY, which is the CMAKE_SOURCE_DIR if -not set. You may manually specify the path of the Oboe Directory with the OBOE_DIRECTORY variable. +not set. This module provides the following imported target, if found: ``Oboe`` #]=======================================================================] -if (NOT DEFINED PA_DIRECTORY) - set(PA_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -endif () - -MESSAGE("Searching for Oboe...") - -set(OBOE_DIRECTORY ${PA_DIRECTORY}/../../oboe) +if(NOT DEFINED OBOE_DIRECTORY) + #Insert the path of the directory where you cloned Oboe + set(OBOE_DIRECTORY FALSE) +endif() -set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) -set(OBOE_BUILD_DIR ${OBOE_DIRECTORY}/build) +if(NOT OBOE_DIRECTORY) + message(AUTHOR_WARNING + "If you're trying to use Oboe as a Host API, please specify the directory where you cloned its repository. For further information, please read src/hostapi/oboe/README.md" + ) + set(OBOE_FOUND FALSE) +else() + if(NOT DEFINED OBOE_INCLUDE_DIR) + set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) + endif() -set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) -set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) + if(NOT DEFINED OBOE_LINK_LIBRARIES) + set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) + set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) + endif() -find_package(PkgConfig QUIET) -if(PkgConfig_FOUND) - pkg_check_modules(OBOE Oboe) -else() - find_library(OBOE_LINK_LIBRARIES - NAMES liboboe.so - HINTS ${OBOE_LIBRARY_DIRS} - DOC "Oboe Library" - ) - find_path(OBOE_INCLUDE_DIR - NAMES oboe/Oboe.h - DOC "Oboe header" - ) -endif() + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(OBOE Oboe) + else() + find_library(OBOE_LINK_LIBRARIES + NAMES liboboe.so + HINTS ${OBOE_LIBRARY_DIRS} + DOC "Oboe Library" + ) + find_path(OBOE_INCLUDE_DIR + NAMES oboe/Oboe.h + DOC "Oboe header" + ) + endif() -find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool -list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) + find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool + list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - Oboe - DEFAULT_MSG - OBOE_LINK_LIBRARIES - OBOE_INCLUDE_DIR -) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args( + Oboe + DEFAULT_MSG + OBOE_LINK_LIBRARIES + OBOE_INCLUDE_DIR + ) -if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) - set(OBOE_FOUND TRUE) - if(NOT TARGET Oboe) - add_library(Oboe INTERFACE IMPORTED) - target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") - target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) + set(OBOE_FOUND TRUE) + if(NOT TARGET Oboe) + add_library(Oboe INTERFACE IMPORTED) + target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") + target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + endif() endif() endif() \ No newline at end of file diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 1cd1f57d4..5b29986ff 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -9,15 +9,25 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. -3) Set the CMake variable OBOE_DIR to the path of the cloned Oboe repository. + Make sure to correctly link the NDK path in the Oboe build. Alternatively, you can use the prebuilt + libraries and clone the include directory of said repository. +3) If you cloned the Oboe repository, set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) + to the path of the cloned Oboe repository, and don't forget to build the Oboe libraries (you can use "build_all_android.sh"). + + If you instead used the prebuilt libraries, set the following cmake variables: + - OBOE_DIRECTORY set to TRUE; + - OBOE_INCLUDE_DIR set to the path of the Oboe include directory; + - OBOE_LINK_LIBRARIES set to the path of the prebuilt libraries. +4) Build Portaudio (you can use "build_all_PaOboe.sh" to build it for all supported Android ABIs). TODOs: ---- -- Testing. This implementation was non-extensively tested for VoIP calls and blocking streams - for +- Tests. This implementation was non-extensively tested for VoIP calls and blocking streams - for everything else, it should have a decent structure. -- Implementing onErrorAfterClose in a way that works, and checking the callback methods. +- Add support for armeabi-v7a and x86 architectures. + +- Implement onErrorAfterClose in a way that works, and checking the callback methods. Misc ---- From 3ae7ac3290d54b63754b9668b73da82d12f412c4 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 12:18:34 +0200 Subject: [PATCH 049/125] Updated oboe/Readme.md --- cmake/modules/FindOboe.cmake | 2 +- src/hostapi/oboe/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 39e88d32f..e3036514e 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -11,7 +11,7 @@ This module provides the following imported target, if found: #]=======================================================================] if(NOT DEFINED OBOE_DIRECTORY) - #Insert the path of the directory where you cloned Oboe + #Insert the path of the directory where you cloned Oboe, i.e. ${CMAKE_CURRENT_SOURCE_DIR}/../oboe set(OBOE_DIRECTORY FALSE) endif() diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 5b29986ff..d67354655 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -23,13 +23,13 @@ To build portaudio with Oboe, there are some necessary steps: TODOs: ---- - Tests. This implementation was non-extensively tested for VoIP calls and blocking streams - for - everything else, it should have a decent structure. + everything else, it should have a decent structure, but needs to be tested. - Add support for armeabi-v7a and x86 architectures. - Implement onErrorAfterClose in a way that works, and checking the callback methods. -Misc +Miscellaneous ---- ### Latency and Sharing Mode: Using LowLatency and SharingMode Exclusive is possible, but a function in pa_oboe.h that sets said flags *is yet to be implemented*, so you'll have to manually set those properties in the OboeEngine::OpenStream function. From a018452195a15e5a3f6cb479a6e2bbc481b6f925 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 13:01:44 +0200 Subject: [PATCH 050/125] Added sharing mode selection --- cmake/modules/FindOboe.cmake | 2 +- include/pa_oboe.h | 11 ++++++++++- src/hostapi/oboe/pa_oboe.cpp | 38 +++++++++++++++++++++++++----------- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index e3036514e..825fd0083 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -12,7 +12,7 @@ This module provides the following imported target, if found: if(NOT DEFINED OBOE_DIRECTORY) #Insert the path of the directory where you cloned Oboe, i.e. ${CMAKE_CURRENT_SOURCE_DIR}/../oboe - set(OBOE_DIRECTORY FALSE) + set(OBOE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../oboe) endif() if(NOT OBOE_DIRECTORY) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index a1c25d129..d15704061 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -71,11 +71,20 @@ typedef struct PaOboeStreamInfo { * Provide PA Oboe with the ID of the device the user chose - oboe cannot build a device list, * but can select the device if provided with its ID. * @param direction - the direction of the stream for which we want to set the device. - * @param deviceID - the ID of the device chose by the user. + * @param deviceID - the ID of the device chosen by the user. */ void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); +/** + * Provide PA Oboe with the performance mode chosen by the user. + * @param direction - the direction of the stream for which we want to set the performance mode. If set to kUnspecified, + * both input and output performance modes will be set to performanceMode. + * @param performanceMode - the performance mode chosen by the user. + */ + void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); + + /** * Provide PA Oboe with native buffer information. If you call this function, you must do so before * calling Pa_Initialize. To have optimal latency, this function should be called - otherwise, diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index ab0117c03..aa1649057 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -159,8 +159,10 @@ static unsigned numberOfBuffers = 2; using namespace oboe; -int32_t inputDeviceId = kUnspecified; -int32_t outputDeviceId = kUnspecified; +int32_t g_inputDeviceId = kUnspecified; +int32_t g_outputDeviceId = kUnspecified; +PerformanceMode g_inputPerfMode = PerformanceMode::None; +PerformanceMode g_outputPerfMode = PerformanceMode::None; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -364,13 +366,13 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, ->setSampleRate(sampleRate) ->setDirection(Direction::Input) ->setDeviceId(getSelectedDevice(Direction::Input)) + ->setPerformanceMode(g_inputPerfMode) ->setInputPreset(androidInputPreset) ->setFramesPerCallback(oboeStream->framesPerHostCallback); if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - inputBuilder.setDataCallback(this) - ->setPerformanceMode(PerformanceMode::LowLatency); + inputBuilder.setDataCallback(this); } m_result = inputBuilder.openStream(inputStream); @@ -408,13 +410,13 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, ->setSampleRate(sampleRate) ->setDirection(Direction::Output) ->setDeviceId(getSelectedDevice(Direction::Output)) + ->setPerformanceMode(g_outputPerfMode) ->setUsage(androidOutputUsage) ->setFramesPerCallback(oboeStream->framesPerHostCallback); if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - outputBuilder.setDataCallback(this) - ->setPerformanceMode(PerformanceMode::LowLatency); + outputBuilder.setDataCallback(this); } m_result = outputBuilder.openStream(outputStream); @@ -510,7 +512,6 @@ bool OboeEngine::restartStream(int direction) { bool m_outcome = true; Result m_result; - //TODO: Test if KCTI crashes when ErrorDisconnected occurs switch (direction) { case 1: //output-only //stopping and closing @@ -863,9 +864,9 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { */ int32_t OboeEngine::getSelectedDevice(Direction direction) { if (direction == Direction::Input) - return inputDeviceId; + return g_inputDeviceId; else - return outputDeviceId; + return g_outputDeviceId; } @@ -1882,9 +1883,24 @@ static unsigned long GetApproximateLowBufferSize() { void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); if (direction == Direction::Input) - inputDeviceId = deviceID; + g_inputDeviceId = deviceID; else - outputDeviceId = deviceID; + g_outputDeviceId = deviceID; +} + + +void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ + switch (direction) { + case Direction::Input: + g_inputPerfMode = performanceMode; + break; + case Direction::Output: + g_outputPerfMode = performanceMode; + break; + default: + g_outputPerfMode = g_inputPerfMode = performanceMode; + break; + } } From 73d5b56787cf056c6daff460b3da26c0d67606ed Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 13:02:35 +0200 Subject: [PATCH 051/125] Fixed minor issue with sharing mode selection --- include/pa_oboe.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index d15704061..229fde0bf 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -78,8 +78,7 @@ void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); /** * Provide PA Oboe with the performance mode chosen by the user. - * @param direction - the direction of the stream for which we want to set the performance mode. If set to kUnspecified, - * both input and output performance modes will be set to performanceMode. + * @param direction - the direction of the stream for which we want to set the performance mode. * @param performanceMode - the performance mode chosen by the user. */ void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); From 9bd1c317e56f8ae1357ab7b36ffcd3d73e958a8d Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 28 Aug 2023 13:09:24 +0200 Subject: [PATCH 052/125] Removed references to KCTI --- src/hostapi/oboe/pa_oboe.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index aa1649057..84431fa92 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -297,7 +297,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan bool m_outcome = false; m_builder.setDeviceId(getSelectedDevice(direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::I16) ->setDirection(direction) ->setSampleRate(sampleRate) @@ -1412,8 +1412,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ + /* FIXME: Replace "paInt16" with whatever format you prefer - + * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16, m_inputSampleFormat); m_oboeStream->inputFormat = m_hostInputSampleFormat; @@ -1452,9 +1452,9 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - - PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options - */ + /* FIXME: Replace "paInt16" with whatever format you prefer - + PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options + */ m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16, m_outputSampleFormat); m_oboeStream->outputFormat = m_hostOutputSampleFormat; @@ -1866,15 +1866,10 @@ static double GetStreamCpuLoad(PaStream *s) { * @return 256 for Android API Level <= 23, 192 otherwise. */ static unsigned long GetApproximateLowBufferSize() { -/* FIXME: This function should return the following commented values, but was changed in order to improve - compatibility with KCTI for android. Please use the commented values in normal conditions. */ - -// if (__ANDROID_API__ <= 23) -// return 256; -// else -// return 192; - - return 1024; + if (__ANDROID_API__ <= 23) + return 256; + else + return 192; } From ac7c1c913c900c93d7db52907bc628efd6e6306c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 10:56:44 +0200 Subject: [PATCH 053/125] Fixed error callback, added performance mode autoselection --- src/hostapi/oboe/pa_oboe.cpp | 92 ++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 84431fa92..c96fdbc5f 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -43,7 +43,6 @@ * requested that these non-binding requests be included along with the * license above. */ -#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions" /** @file @ingroup hostapi_src @@ -159,10 +158,13 @@ static unsigned numberOfBuffers = 2; using namespace oboe; +//Useful global variables int32_t g_inputDeviceId = kUnspecified; int32_t g_outputDeviceId = kUnspecified; PerformanceMode g_inputPerfMode = PerformanceMode::None; +bool g_inputPerfModeUser = false; PerformanceMode g_outputPerfMode = PerformanceMode::None; +bool g_outputPerfModeUser = false; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -252,6 +254,10 @@ class OboeEngine : public AudioStreamCallback { //device selection implementation int32_t getSelectedDevice(oboe::Direction direction); + + //auto performance mode selection + void performanceModeAutoSelection(Direction direction); + double assertLatency(Direction direction); }; @@ -297,7 +303,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan bool m_outcome = false; m_builder.setDeviceId(getSelectedDevice(direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::I16) ->setDirection(direction) ->setSampleRate(sampleRate) @@ -314,6 +320,8 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan return m_outcome; } + performanceModeAutoSelection(direction); + if (sampleRate != kUnspecified) { m_outcome = (sampleRate == m_builder.getSampleRate()); if(!m_outcome) { @@ -372,7 +380,8 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - inputBuilder.setDataCallback(this); + inputBuilder.setDataCallback(this) + ->setErrorCallback(this); } m_result = inputBuilder.openStream(inputStream); @@ -416,7 +425,8 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, if (!(oboeStream->isBlocking)) { resetCallbackCounters(); - outputBuilder.setDataCallback(this); + outputBuilder.setDataCallback(this) + ->setErrorCallback(this); } m_result = outputBuilder.openStream(outputStream); @@ -870,6 +880,64 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { } +/** + * \brief Function used to automatically select the performance mode, based on the latency value LOW_LATENCY_MS, if no + * choice was made via PaOboe_SetPerformanceMode. It uses OboeEngine::assertLatency to make this decision. + * @param direction the Oboe::Direction of the stream we want to select the mode of. + */ +void OboeEngine::performanceModeAutoSelection(Direction direction){ + double m_result = assertLatency(direction); + + if(direction == Direction::Input) { + if (m_result >= 0 && !g_inputPerfModeUser) { + if (m_result <= LOW_LATENCY_MS) + g_inputPerfMode = PerformanceMode::LowLatency; + else + g_inputPerfMode = PerformanceMode::PowerSaving; + } + } else { + if (m_result >= 0 && !g_outputPerfModeUser) { + if (m_result <= LOW_LATENCY_MS) + g_outputPerfMode = PerformanceMode::LowLatency; + else + g_outputPerfMode = PerformanceMode::PowerSaving; + } + } +} + + +/** + * \brief Asserts if the device supports latency tutning, then calculates the latency of an AudioStream. + * @param direction the direction of the AudioStream we want to check. + * @return the measured latency, or -1.0 if any error occurs. + */ +double OboeEngine::assertLatency(Direction direction){ + if(__ANDROID_API__<31) { + LOGI("[OboeEngine::assertLatency]\t Latency Tuning is not supported for Android API level < 31"); + return -1.0; + } + if(direction == Direction::Input){ + ResultWithValue m_result = inputStream->calculateLatencyMillis(); + if (m_result) { + return m_result.value(); + } else { + LOGE("[OboeEngine::assertLatency]\t Error calculating input latency: %s", + oboe::convertToText(m_result.error())); + return -1.0; + } + } else { + ResultWithValue m_result = outputStream->calculateLatencyMillis(); + if (m_result) { + return m_result.value(); + } else { + LOGE("[OboeEngine::assertLatency]\t Error calculating output latency: %s", + oboe::convertToText(m_result.error())); + return -1.0; + } + } + +} + /*----------------------------- PaSkeleton functions implementations -----------------------------*/ @@ -1885,16 +1953,12 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ - switch (direction) { - case Direction::Input: - g_inputPerfMode = performanceMode; - break; - case Direction::Output: - g_outputPerfMode = performanceMode; - break; - default: - g_outputPerfMode = g_inputPerfMode = performanceMode; - break; + if(direction == Direction::Input) { + g_inputPerfMode = performanceMode; + g_inputPerfModeUser = true; + } else { + g_outputPerfMode = performanceMode; + g_outputPerfModeUser = true; } } From a7f285987e0d21c899cc961b08735423a49a69df Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 11:04:59 +0200 Subject: [PATCH 054/125] Minor change to CMakeLists, added low latency costant in pa_oboe.h --- CMakeLists.txt | 2 +- include/pa_oboe.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 431680648..5892869db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,7 +154,7 @@ endif() find_package(Oboe) cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) if(PA_USE_OBOE) - message("Oboe found!") + message("-- Oboe structure found") target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 229fde0bf..761a7b937 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -56,7 +56,8 @@ extern "C" { #endif //__cplusplus -#define TIMEOUT_NS 1000000000 +#define TIMEOUT_NS 1000000000 //Arbitrary timeout of the read/write functions +#define LOW_LATENCY_MS 300.0 //Arbitrary value used to automatically determine if low latency performance mode is doable /** * The android stream type and recording preset as defined in Definitions.h @@ -81,7 +82,7 @@ void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); * @param direction - the direction of the stream for which we want to set the performance mode. * @param performanceMode - the performance mode chosen by the user. */ - void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); +void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); /** From fd67311243c362a465aa98912af821d3924cd702 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 11:39:24 +0200 Subject: [PATCH 055/125] Deleted Build_PaOboe.sh --- Build_PaOboe.sh | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 Build_PaOboe.sh diff --git a/Build_PaOboe.sh b/Build_PaOboe.sh deleted file mode 100644 index 9d6c2e572..000000000 --- a/Build_PaOboe.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# Script to build Portaudio_Oboe for multiple Android ABIs -# -# Ensure that ANDROID_NDK environment variable is set to your Android NDK location -# e.g. /Library/Android/sdk/ndk-bundle - -ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 - -if [ -z "$ANDROID_NDK" ]; then - echo "Please set ANDROID_NDK to the Android NDK folder" - exit 1 -fi - -# Build directory -BUILD_DIR=lib - -CMAKE_ARGS="-H. \ - -DBUILD_SHARED_LIBS=true \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DANDROID_TOOLCHAIN=clang \ - -DANDROID_STL=c++_shared \ - -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ - -DCMAKE_INSTALL_PREFIX=." - -function build_PaOboe { - - echo "Building Pa_Oboe" - - mkdir -p ${BUILD_DIR} ${BUILD_DIR}/${STAGING_DIR} - - cmake -B${BUILD_DIR} \ - -DANDROID_PLATFORM=android-21\ - ${CMAKE_ARGS} - - pushd ${BUILD_DIR} - make -j5 - popd -} - - -build_PaOboe \ No newline at end of file From bbf847af0297bfd52e5cfb7e3d7d1449be882052 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 30 Aug 2023 11:52:29 +0200 Subject: [PATCH 056/125] Ready to push (removed my paths to Oboe directory and Android NDK) --- build_all_PaOboe.sh | 2 -- cmake/modules/FindOboe.cmake | 13 +++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/build_all_PaOboe.sh b/build_all_PaOboe.sh index 7b8c7e8ad..9e589ed0b 100755 --- a/build_all_PaOboe.sh +++ b/build_all_PaOboe.sh @@ -4,8 +4,6 @@ # Ensure that ANDROID_NDK environment variable is set to your Android NDK location # e.g. /Library/Android/sdk/ndk-bundle -ANDROID_NDK=/home/netresults.wintranet/benfatti/Android/Sdk/ndk/23.1.7779620 - if [ -z "$ANDROID_NDK" ]; then echo "Please set ANDROID_NDK to the Android NDK folder" exit 1 diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 825fd0083..9a7ddbd8a 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -2,17 +2,22 @@ FindOboe -------- -Finds the Oboe Directory by searching for it in the PA_DIRECTORY, which is the CMAKE_SOURCE_DIR if -not set. +Finds the Oboe library. OBOE_DIRECTORY has to be set to the path of the directory where +the Oboe repository was cloned (see src/hostapi/oboe/README.md for more information). + +Imported Targets +^^^^^^^^^^^^^^^^ This module provides the following imported target, if found: - ``Oboe`` + +``Oboe`` + The OBOE library #]=======================================================================] if(NOT DEFINED OBOE_DIRECTORY) #Insert the path of the directory where you cloned Oboe, i.e. ${CMAKE_CURRENT_SOURCE_DIR}/../oboe - set(OBOE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../oboe) + set(OBOE_DIRECTORY FALSE) endif() if(NOT OBOE_DIRECTORY) From 6a12b65174a28e5e145e53e91002bed9225cbcdc Mon Sep 17 00:00:00 2001 From: hopefulGiupplo <116260612+hopefulGiupplo@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:46:19 +0200 Subject: [PATCH 057/125] Update src/hostapi/oboe/README.md --- src/hostapi/oboe/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index d67354655..24468abe4 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -19,6 +19,7 @@ To build portaudio with Oboe, there are some necessary steps: - OBOE_INCLUDE_DIR set to the path of the Oboe include directory; - OBOE_LINK_LIBRARIES set to the path of the prebuilt libraries. 4) Build Portaudio (you can use "build_all_PaOboe.sh" to build it for all supported Android ABIs). +5) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. TODOs: ---- From fd5967c28bd991a3f36b6cbb8307c42640a8ffe5 Mon Sep 17 00:00:00 2001 From: hopefulGiupplo <116260612+hopefulGiupplo@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:21:58 +0200 Subject: [PATCH 058/125] Update README.md --- src/hostapi/oboe/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 24468abe4..d67354655 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -19,7 +19,6 @@ To build portaudio with Oboe, there are some necessary steps: - OBOE_INCLUDE_DIR set to the path of the Oboe include directory; - OBOE_LINK_LIBRARIES set to the path of the prebuilt libraries. 4) Build Portaudio (you can use "build_all_PaOboe.sh" to build it for all supported Android ABIs). -5) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. TODOs: ---- From aa6bcbbb16b7d33cd5f36a24e73477d97bc6c4b6 Mon Sep 17 00:00:00 2001 From: hopefulGiupplo <116260612+hopefulGiupplo@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:24:34 +0200 Subject: [PATCH 059/125] Update README.md From 4d4d0713d67193cde0f00cc5e8d29b4b17bda453 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 12:37:12 +0200 Subject: [PATCH 060/125] updated readme --- src/hostapi/oboe/README.md | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index d67354655..7e027ec00 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -1,4 +1,3 @@ - # Portaudio implementation for android using Oboe. In order to use this implementation correctly, be sure to include the "portaudio.h" and "pa_oboe.h" @@ -9,40 +8,22 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. Alternatively, you can use the prebuilt - libraries and clone the include directory of said repository. -3) If you cloned the Oboe repository, set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) - to the path of the cloned Oboe repository, and don't forget to build the Oboe libraries (you can use "build_all_android.sh"). - - If you instead used the prebuilt libraries, set the following cmake variables: - - OBOE_DIRECTORY set to TRUE; - - OBOE_INCLUDE_DIR set to the path of the Oboe include directory; - - OBOE_LINK_LIBRARIES set to the path of the prebuilt libraries. -4) Build Portaudio (you can use "build_all_PaOboe.sh" to build it for all supported Android ABIs). + Make sure to correctly link the NDK path in the Oboe build. +3) Set the CMake variable OBOE_DIR (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository. +4) Build the Oboe Library (you can use "build_all_android.sh"). +5) Build PaOboe (you can use "build_all_PaOboe.sh"). +6) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. TODOs: ---- -- Tests. This implementation was non-extensively tested for VoIP calls and blocking streams - for - everything else, it should have a decent structure, but needs to be tested. - -- Add support for armeabi-v7a and x86 architectures. - -- Implement onErrorAfterClose in a way that works, and checking the callback methods. +- Testing. This implementation was tested for VoIP calls that use blocking streams - for everything else, lots of tests are needed. -Miscellaneous +Misc ---- -### Latency and Sharing Mode: -Using LowLatency and SharingMode Exclusive is possible, but a function in pa_oboe.h that sets said flags *is yet to be implemented*, so you'll have to manually set those properties in the OboeEngine::OpenStream function. - - ### Audio Format: If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream by modifying the format selection marked with a *FIXME*. -I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat will not get you anywhere. +I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat got me nowhere. ### Buffer sizes: -Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. - - -### Device selection and/or switching mid-stream: -Device selection can be handled by a java/kotlin method that uses getDevices() in order to identify which device to select. Switching mid-stream gives an oboe::ErrorDisconnected result, and you'll have to stop, close and reopen the involved streams with an unspecified device (or a specific device if you know its ID). +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file From bde12cef1f0268a472ddb96eae2299a31ca528f2 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 12:38:26 +0200 Subject: [PATCH 061/125] fixed CMakeLists.txt --- CMakeLists.txt | 349 ++++++++++++++++++++++++++++--------------------- 1 file changed, 197 insertions(+), 152 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5892869db..5cefda195 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,66 +17,94 @@ endif() option(PA_WARNINGS_ARE_ERRORS "Turn compiler warnings into errors" OFF) if(PA_WARNINGS_ARE_ERRORS) - if(MSVC) - add_compile_options(/WX + if(MSVC) + add_compile_options(/WX # "Grandfathered" warnings that existed before we started enforcement. # Do *NOT* add warnings to this list. Instead, fix your code so that it doesn't produce the warning. # TODO: fix the offending code so that we don't have to exclude specific warnings anymore. + /wd4018 # W3 signed/unsigned mismatch /wd4244 # W2 conversion possible loss of data /wd4267 # W3 conversion possible loss of data /wd4996 # W3 unsafe/deprecated - ) - else() - add_compile_options(-Werror + ) + else() + add_compile_options(-Werror # "Grandfathered" warnings that existed before we started enforcement. # Do *NOT* add warnings to this list. Instead, fix your code so that it doesn't produce the warning. # TODO: fix the offending code so that we don't have to exclude specific warnings anymore. -Wno-error=deprecated-declarations # https://github.com/PortAudio/portaudio/issues/213 https://github.com/PortAudio/portaudio/issues/641 -Wno-error=stringop-overflow - ) - if (CMAKE_C_COMPILER_ID MATCHES "Clang") - # Don't fail on older clang versions that don't recognize the latest warnings in the list above. - # Note that unrecognized warning options are not a fatal error on GCC, and in fact, GCC will choke on this option. Hence the conditional. - add_compile_options(-Wno-error=unknown-warning-option) - endif() + ) + if (CMAKE_C_COMPILER_ID MATCHES "Clang") + # Don't fail on older clang versions that don't recognize the latest warnings in the list above. + # Note that unrecognized warning options are not a fatal error on GCC, and in fact, GCC will choke on this option. Hence the conditional. + add_compile_options(-Wno-error=unknown-warning-option) endif() + endif() endif() +set(PA_COMMON_SOURCES + src/common/pa_allocation.c + src/common/pa_allocation.h + src/common/pa_converters.c + src/common/pa_converters.h + src/common/pa_cpuload.c + src/common/pa_cpuload.h + src/common/pa_debugprint.c + src/common/pa_debugprint.h + src/common/pa_dither.c + src/common/pa_dither.h + src/common/pa_endianness.h + src/common/pa_front.c + src/common/pa_hostapi.h + src/common/pa_memorybarrier.h + src/common/pa_process.c + src/common/pa_process.h + src/common/pa_ringbuffer.c + src/common/pa_ringbuffer.h + src/common/pa_stream.c + src/common/pa_stream.h + src/common/pa_trace.c + src/common/pa_trace.h + src/common/pa_types.h + src/common/pa_util.h + ) + add_library(PortAudio - ${LIBRARY_BUILD_TYPE} - src/common/pa_allocation.c - src/common/pa_allocation.h - src/common/pa_converters.c - src/common/pa_converters.h - src/common/pa_cpuload.c - src/common/pa_cpuload.h - src/common/pa_debugprint.c - src/common/pa_debugprint.h - src/common/pa_dither.c - src/common/pa_dither.h - src/common/pa_endianness.h - src/common/pa_front.c - src/common/pa_hostapi.h - src/common/pa_memorybarrier.h - src/common/pa_process.c - src/common/pa_process.h - src/common/pa_ringbuffer.c - src/common/pa_ringbuffer.h - src/common/pa_stream.c - src/common/pa_stream.h - src/common/pa_trace.c - src/common/pa_trace.h - src/common/pa_types.h - src/common/pa_util.h -) + ${LIBRARY_BUILD_TYPE} + src/common/pa_allocation.c + src/common/pa_allocation.h + src/common/pa_converters.c + src/common/pa_converters.h + src/common/pa_cpuload.c + src/common/pa_cpuload.h + src/common/pa_debugprint.c + src/common/pa_debugprint.h + src/common/pa_dither.c + src/common/pa_dither.h + src/common/pa_endianness.h + src/common/pa_front.c + src/common/pa_hostapi.h + src/common/pa_memorybarrier.h + src/common/pa_process.c + src/common/pa_process.h + src/common/pa_ringbuffer.c + src/common/pa_ringbuffer.h + src/common/pa_stream.c + src/common/pa_stream.h + src/common/pa_trace.c + src/common/pa_trace.h + src/common/pa_types.h + src/common/pa_util.h + ) include(GNUInstallDirs) target_include_directories(PortAudio PUBLIC - $ - $ - $ -) + $ + $ + $ + ) if(UNIX) target_compile_options(PortAudio PRIVATE -fPIC) endif() @@ -100,19 +128,19 @@ else() endif() if(WIN32 AND MSVC AND PA_BUILD_SHARED_LIBS - # Check if the user is building PortAudio stand-alone or as part of a larger - # project. If this is part of a larger project (i.e. the CMakeLists.txt has - # been imported by some other CMakeLists.txt), we don't want to override - # that project's global settings. - AND "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}") + # Check if the user is building PortAudio stand-alone or as part of a larger + # project. If this is part of a larger project (i.e. the CMakeLists.txt has + # been imported by some other CMakeLists.txt), we don't want to override + # that project's global settings. + AND "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}") option(PA_DLL_LINK_WITH_STATIC_RUNTIME - "Link with static runtime libraries (minimizes runtime dependencies)" ON) + "Link with static runtime libraries (minimizes runtime dependencies)" ON) if(PA_DLL_LINK_WITH_STATIC_RUNTIME) foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if(${flag_var} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") endif() @@ -150,33 +178,18 @@ if(PA_USE_JACK) install(FILES cmake/modules/FindJACK.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") endif() -# Oboe only works on Android, which has a Linux kernel. -find_package(Oboe) -cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) -if(PA_USE_OBOE) - message("-- Oboe structure found") - target_sources(PortAudio PRIVATE src/hostapi/oboe/pa_oboe.cpp) - set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) - target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") - target_link_libraries(PortAudio PRIVATE Oboe) - - install(FILES cmake/modules/FindOboe.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") -endif() if(WIN32) target_sources(PortAudio PRIVATE - src/os/win/pa_win_coinitialize.c - src/os/win/pa_win_coinitialize.h - src/os/win/pa_win_hostapis.c - src/os/win/pa_win_util.c - src/os/win/pa_win_util.h - src/os/win/pa_win_version.c - src/os/win/pa_win_version.h - src/os/win/pa_win_waveformat.c - src/os/win/pa_win_wdmks_utils.h - src/os/win/pa_x86_plain_converters.h - ) + src/os/win/pa_win_coinitialize.c + src/os/win/pa_win_coinitialize.h + src/os/win/pa_win_hostapis.c + src/os/win/pa_win_util.c + src/os/win/pa_win_util.h + src/os/win/pa_win_waveformat.c + src/os/win/pa_win_wdmks_utils.h + src/os/win/pa_x86_plain_converters.h + ) target_include_directories(PortAudio PRIVATE src/os/win) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_win_waveformat.h) target_link_libraries(PortAudio PRIVATE winmm) @@ -207,19 +220,19 @@ if(WIN32) endif() message(STATUS "Downloading ASIO SDK... ${ASIO_SDK_ZIP_PATH}") file(DOWNLOAD "https://www.steinberg.net/asiosdk" - "${ASIO_SDK_ZIP_PATH}" - STATUS ASIO_DOWNLOAD_STATUS - SHOW_PROGRESS - ) + "${ASIO_SDK_ZIP_PATH}" + STATUS ASIO_DOWNLOAD_STATUS + SHOW_PROGRESS + ) if("${ASIO_DOWNLOAD_STATUS}" EQUAL 0) find_package(ASIO) else() list(GET "${ASIO_DOWNLOAD_STATUS}" 1 DOWNLOAD_ERROR) message(FATAL_ERROR "Error downloading ASIO SDK: ${DOWNLOAD_ERROR} " - "Reconfigure CMake with -DPA_USE_ASIO=OFF to build without ASIO. " - "Alternatively, download the ZIP from https://www.steinberg.net/asiosdk " - "and put it in ${CMAKE_PREFIX_PATH} or ${CMAKE_CURRENT_BINARY_DIR}" - ) + "Reconfigure CMake with -DPA_USE_ASIO=OFF to build without ASIO. " + "Alternatively, download the ZIP from https://www.steinberg.net/asiosdk " + "and put it in ${CMAKE_PREFIX_PATH} or ${CMAKE_CURRENT_BINARY_DIR}" + ) endif() endif() endif() @@ -229,10 +242,10 @@ if(WIN32) target_compile_definitions(PortAudio PUBLIC PA_USE_ASIO=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_ASIO=1") target_sources(PortAudio PRIVATE - src/hostapi/asio/pa_asio.cpp - src/hostapi/asio/iasiothiscallresolver.cpp - src/hostapi/asio/iasiothiscallresolver.h - ) + src/hostapi/asio/pa_asio.cpp + src/hostapi/asio/iasiothiscallresolver.cpp + src/hostapi/asio/iasiothiscallresolver.h + ) else() set(DEF_EXCLUDE_ASIO_SYMBOLS ";") endif() @@ -240,10 +253,10 @@ if(WIN32) option(PA_USE_DS "Enable support for DirectSound" ON) if(PA_USE_DS) target_sources(PortAudio PRIVATE - src/hostapi/dsound/pa_win_ds.c - src/hostapi/dsound/pa_win_ds_dynlink.c - src/hostapi/dsound/pa_win_ds_dynlink.h - ) + src/hostapi/dsound/pa_win_ds.c + src/hostapi/dsound/pa_win_ds_dynlink.c + src/hostapi/dsound/pa_win_ds_dynlink.h + ) target_include_directories(PortAudio PRIVATE src/hostapi/dsound) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_win_ds.h) target_compile_definitions(PortAudio PUBLIC PA_USE_DS=1) @@ -279,9 +292,9 @@ if(WIN32) option(PA_USE_WDMKS "Enable support for WDMKS" ON) if(PA_USE_WDMKS) target_sources(PortAudio PRIVATE - src/os/win/pa_win_wdmks_utils.c - src/hostapi/wdmks/pa_win_wdmks.c - ) + src/os/win/pa_win_wdmks_utils.c + src/hostapi/wdmks/pa_win_wdmks.c + ) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_win_wdmks.h) target_compile_definitions(PortAudio PUBLIC PA_USE_WDMKS=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_WDMKS=1") @@ -298,37 +311,69 @@ if(WIN32) target_sources(PortAudio PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/portaudio.def") endif() elseif(UNIX) - target_sources(PortAudio PRIVATE - src/os/unix/pa_unix_hostapis.c - src/os/unix/pa_unix_util.c - src/os/unix/pa_unix_util.h - ) - target_include_directories(PortAudio PRIVATE src/os/unix) + target_sources(PortAudio PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/os/unix/pa_unix_hostapis.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/os/unix/pa_unix_util.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/os/unix/pa_unix_util.h + ) + target_include_directories(PortAudio PUBLIC src/os/unix) target_link_libraries(PortAudio PRIVATE m) - set(PKGCONFIG_LDFLAGS_PRIVATE "${PKGCONFIG_LDFLAGS_PUBLIC} -lm -lpthread") - set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -pthread") + + IF(CMAKE_SYSTEM_NAME STREQUAL "Android") + set(PKGCONFIG_LDFLAGS_PRIVATE "${PA_PKGCONFIG_LDFLAGS} -lm") + ELSE() + set(PKGCONFIG_LDFLAGS_PRIVATE "${PKGCONFIG_LDFLAGS_PUBLIC} -lm -lpthread") + target_link_libraries(PortAudio PRIVATE pthread) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -pthread") + ENDIF() + + + # Oboe only works on Android, which has a Unix kernel. + find_package(Oboe) + cmake_dependent_option(PA_USE_OBOE "Enable support for Oboe" ON OBOE_FOUND OFF) + if(PA_USE_OBOE) + message("-- Oboe structure found") + target_include_directories(PortAudio PRIVATE ${OBOE_INCLUDE_DIR}) + target_link_libraries(PortAudio PUBLIC Oboe::oboe) + target_sources(PortAudio PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/hostapi/oboe/pa_oboe.cpp) + set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_oboe.h) + target_compile_definitions(PortAudio PUBLIC PA_USE_OBOE=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_OBOE=1") + endif() + + IF(CMAKE_SYSTEM_NAME STREQUAL "Android") + SET(PA_PKGCONFIG_LDFLAGS "${PA_PKGCONFIG_LDFLAGS} -lm") + SET(PA_LIBRARY_DEPENDENCIES ${PA_LIBRARY_DEPENDENCIES} m) + ELSE() + SET(PA_PKGCONFIG_LDFLAGS "${PA_PKGCONFIG_LDFLAGS} -lm -lpthread") + SET(PA_LIBRARY_DEPENDENCIES ${PA_LIBRARY_DEPENDENCIES} m pthread) + ENDIF() if(APPLE) set(CMAKE_MACOSX_RPATH 1) target_sources(PortAudio PRIVATE - src/hostapi/coreaudio/pa_mac_core.c - src/hostapi/coreaudio/pa_mac_core_blocking.c - src/hostapi/coreaudio/pa_mac_core_blocking.h - src/hostapi/coreaudio/pa_mac_core_internal.h - src/hostapi/coreaudio/pa_mac_core_utilities.c - src/hostapi/coreaudio/pa_mac_core_utilities.h - ) + src/hostapi/coreaudio/pa_mac_core.c + src/hostapi/coreaudio/pa_mac_core_blocking.c + src/hostapi/coreaudio/pa_mac_core_blocking.h + src/hostapi/coreaudio/pa_mac_core_internal.h + src/hostapi/coreaudio/pa_mac_core_utilities.c + src/hostapi/coreaudio/pa_mac_core_utilities.h + ) target_include_directories(PortAudio PRIVATE src/hostapi/coreaudio) set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_mac_core.h) - target_link_libraries(PortAudio - PRIVATE - -Wl,-framework,CoreAudio - -Wl,-framework,AudioToolbox - -Wl,-framework,AudioUnit - -Wl,-framework,CoreFoundation - -Wl,-framework,CoreServices - ) + find_library(COREAUDIO_LIBRARY CoreAudio REQUIRED) + find_library(AUDIOTOOLBOX_LIBRARY AudioToolbox REQUIRED) + find_library(AUDIOUNIT_LIBRARY AudioUnit REQUIRED) + find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED) + find_library(CORESERVICES_LIBRARY CoreServices REQUIRED) + target_link_libraries(PortAudio PRIVATE + "${COREAUDIO_LIBRARY}" + "${AUDIOTOOLBOX_LIBRARY}" + "${AUDIOUNIT_LIBRARY}" + "${COREFOUNDATION_LIBRARY}" + "${CORESERVICES_LIBRARY}" + ) target_compile_definitions(PortAudio PUBLIC PA_USE_COREAUDIO=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_COREAUDIO=1") @@ -336,7 +381,7 @@ elseif(UNIX) set_property(TARGET PortAudio PROPERTY C_STANDARD 11) set(PKGCONFIG_LDFLAGS_PRIVATE - "${PKGCONFIG_LDFLAGS_PRIVATE} -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework CoreFoundation -framework CoreServices") + "${PKGCONFIG_LDFLAGS_PRIVATE} -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework CoreFoundation -framework CoreServices") else() # Some BSDs have a reimplementation of alsalib, so do not explicitly check for Linux. find_package(ALSA) @@ -398,53 +443,53 @@ if(NOT CMAKE_FRAMEWORK) configure_file(cmake/portaudio-2.0.pc.in "${CMAKE_CURRENT_BINARY_DIR}/portaudio-2.0.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/portaudio-2.0.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") configure_package_config_file(cmake/PortAudioConfig.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio" - NO_CHECK_REQUIRED_COMPONENTS_MACRO - ) + "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio" + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" - VERSION "${PORTAUDIO_VERSION}" - COMPATIBILITY SameMajorVersion + "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" + VERSION "${PORTAUDIO_VERSION}" + COMPATIBILITY SameMajorVersion ) install(EXPORT PortAudio-targets NAMESPACE "PortAudio::" FILE "PortAudioTargets.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") export(TARGETS PortAudio - FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioTargets.cmake") + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioTargets.cmake") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") + "${CMAKE_CURRENT_BINARY_DIR}/cmake/portaudio/PortAudioConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio") if(NOT TARGET uninstall) configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) add_custom_target(uninstall - COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") endif() endif() set_target_properties(PortAudio PROPERTIES - OUTPUT_NAME portaudio - PUBLIC_HEADER "${PORTAUDIO_PUBLIC_HEADERS}" - MACOSX_FRAMEWORK_IDENTIFIER com.portaudio - FRAMEWORK_VERSION A - WINDOWS_EXPORT_ALL_SYMBOLS TRUE - VERSION ${PROJECT_VERSION} - SOVERSION 2 -) + OUTPUT_NAME portaudio + PUBLIC_HEADER "${PORTAUDIO_PUBLIC_HEADERS}" + MACOSX_FRAMEWORK_IDENTIFIER com.portaudio + FRAMEWORK_VERSION A + WINDOWS_EXPORT_ALL_SYMBOLS TRUE + VERSION ${PROJECT_VERSION} + SOVERSION 2 + ) install(TARGETS PortAudio - EXPORT PortAudio-targets - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" -) + EXPORT PortAudio-targets + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) # # Subdirectories @@ -466,9 +511,9 @@ if(PA_BUILD_TESTS) target_link_libraries(${appl_name} m) endif() set_target_properties(${appl_name} - PROPERTIES - FOLDER "Test" - ) + PROPERTIES + FOLDER "Test" + ) endmacro() subdirs(test) From 8b0730f72e9d11b74d8452ebb8958f7559165095 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 12:40:23 +0200 Subject: [PATCH 062/125] fixed FindOboe.cmake --- cmake/modules/FindOboe.cmake | 49 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 9a7ddbd8a..23fd02308 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -1,16 +1,16 @@ #[=======================================================================[.rst: -FindOboe +Findoboe -------- -Finds the Oboe library. OBOE_DIRECTORY has to be set to the path of the directory where -the Oboe repository was cloned (see src/hostapi/oboe/README.md for more information). +Finds the oboe library. OBOE_DIRECTORY has to be set to the path of the directory where +the oboe repository was cloned (see src/hostapi/oboe/README.md for more information). Imported Targets ^^^^^^^^^^^^^^^^ This module provides the following imported target, if found: -``Oboe`` +``Oboe::oboe`` The OBOE library #]=======================================================================] @@ -23,51 +23,52 @@ endif() if(NOT OBOE_DIRECTORY) message(AUTHOR_WARNING "If you're trying to use Oboe as a Host API, please specify the directory where you cloned its repository. For further information, please read src/hostapi/oboe/README.md" - ) + ) set(OBOE_FOUND FALSE) else() if(NOT DEFINED OBOE_INCLUDE_DIR) set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) endif() - if(NOT DEFINED OBOE_LINK_LIBRARIES) + if(NOT DEFINED OBOE_LIBRARIES) set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) - set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) + set(OBOE_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) endif() find_package(PkgConfig QUIET) if(PkgConfig_FOUND) pkg_check_modules(OBOE Oboe) else() - find_library(OBOE_LINK_LIBRARIES - NAMES liboboe.so - HINTS ${OBOE_LIBRARY_DIRS} - DOC "Oboe Library" - ) + find_library(OBOE_LIBRARIES + NAMES liboboe.so + HINTS ${OBOE_LIBRARY_DIRS} + DOC "Oboe Library" + ) find_path(OBOE_INCLUDE_DIR - NAMES oboe/Oboe.h - DOC "Oboe header" - ) + NAMES oboe/Oboe.h + DOC "Oboe header" + ) endif() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool - list(APPEND OBOE_LINK_LIBRARIES ${LOG_LIBRARY}) + + set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARIES} ${LOG_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( - Oboe - DEFAULT_MSG - OBOE_LINK_LIBRARIES - OBOE_INCLUDE_DIR + Oboe + DEFAULT_MSG + OBOE_LINK_LIBRARIES + OBOE_INCLUDE_DIR ) if(OBOE_INCLUDE_DIR AND OBOE_LINK_LIBRARIES) set(OBOE_FOUND TRUE) - if(NOT TARGET Oboe) - add_library(Oboe INTERFACE IMPORTED) - target_link_libraries(Oboe INTERFACE "${OBOE_LINK_LIBRARIES}") - target_include_directories(Oboe INTERFACE "${OBOE_INCLUDE_DIR}") + if(NOT TARGET Oboe::oboe) + add_library(Oboe::oboe INTERFACE IMPORTED GLOBAL) + target_link_libraries(Oboe::oboe INTERFACE "${OBOE_LINK_LIBRARIES}") + target_include_directories(Oboe::oboe INTERFACE "${OBOE_INCLUDE_DIR}") endif() endif() endif() \ No newline at end of file From 6e8701fe748b62d1f8f493c2f64d328be5e469a0 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 15:52:41 +0200 Subject: [PATCH 063/125] corrected oboe/Readme.md --- src/hostapi/oboe/README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 7e027ec00..50c7b9a83 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -8,11 +8,16 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. -3) Set the CMake variable OBOE_DIR (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository. -4) Build the Oboe Library (you can use "build_all_android.sh"). -5) Build PaOboe (you can use "build_all_PaOboe.sh"). -6) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. + Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the prebuilt libraries, you can just clone the include directory. +3) Set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository, and build the Oboe libraries (you can use "build_all_android.sh"). + + If you instead used the prebuilt libraries, do the following: + - set OBOE_DIRECTORY to TRUE; + - set OBOE_INCLUDE_DIR to the include directory you cloned in step 2; + - set OBOE_LIBRARIES to path_to_Oboe_libraries_folder/${ANDROID_ABI}/your_path_to_prebuilt_libraries_of_the_chosen_ABI; + +4) Build PaOboe (you can use "build_all_PaOboe.sh"). +5) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. TODOs: ---- From b2adaa5fc532f53eee77be8dabf65916b9245cd9 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 19 Sep 2023 15:54:23 +0200 Subject: [PATCH 064/125] Updated oboe/Readme.md --- src/hostapi/oboe/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index 50c7b9a83..db64c90bc 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -21,7 +21,7 @@ To build portaudio with Oboe, there are some necessary steps: TODOs: ---- -- Testing. This implementation was tested for VoIP calls that use blocking streams - for everything else, lots of tests are needed. +- Testing. This implementation is being tested for VoIP calls that use blocking streams - for everything else, lots of tests are needed. Misc ---- From c253251d9309b6a5eb4ea0f0bf28226db206276c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 10:23:24 +0200 Subject: [PATCH 065/125] added .idea to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index bd63e09b0..1adfe452d 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,7 @@ CMakeSettings.json # VSCode .vscode* +# idea +.idea* + # Common build directories of users and VSCode From b6cc2e110bd16aed555b42ba71734a1ad977536f Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 10:23:24 +0200 Subject: [PATCH 066/125] added .idea to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index bd63e09b0..1adfe452d 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,7 @@ CMakeSettings.json # VSCode .vscode* +# idea +.idea* + # Common build directories of users and VSCode From 2217003caa9556591d96e5a79879da51711e62b7 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 10:45:24 +0200 Subject: [PATCH 067/125] restored workflows directory --- .github/workflows/MSBuild.yml | 96 ++++++++++++++++++ .github/workflows/autotools.yml | 20 ++++ .github/workflows/cmake.yml | 128 ++++++++++++++++++++++++ .github/workflows/compare_def_files.yml | 17 ++++ .github/workflows/whitelint.yml | 17 ++++ 5 files changed, 278 insertions(+) create mode 100644 .github/workflows/MSBuild.yml create mode 100644 .github/workflows/autotools.yml create mode 100644 .github/workflows/cmake.yml create mode 100644 .github/workflows/compare_def_files.yml create mode 100644 .github/workflows/whitelint.yml diff --git a/.github/workflows/MSBuild.yml b/.github/workflows/MSBuild.yml new file mode 100644 index 000000000..ea90dba46 --- /dev/null +++ b/.github/workflows/MSBuild.yml @@ -0,0 +1,96 @@ +name: MSBuild MSVC Project File CI + +on: [push, pull_request] + +env: + # Path to the solution file relative to the root of the project. + SOLUTION_FILE_PATH: ./msvc/portaudio.sln + VCPROJ_FILE_PATH: ./msvc/portaudio.vcproj + VCXPROJ_FILE_PATH: ./msvc/portaudio.vcxproj + VCXPROJ_FILTERS_FILE_PATH: ./msvc/portaudio.vcxproj.filters + VCXPROJ_USER_FILE_PATH: ./msvc/portaudio.vcxproj.user + DEF_FILE_PATH: ./msvc/portaudio.def + +jobs: + build: + runs-on: windows-latest + strategy: + matrix: + BUILD_CONFIGURATION: [Release] + BUILD_PLATFORM: [Win32, x64] + + steps: + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v1 + + - uses: actions/checkout@v2 + + - name: Upgrade VC Project File + # We maintain our vcproj file in an old format to maintain backwards compatibility + # This step upgrades the project to the latest version of MSVC + # see https://docs.microsoft.com/en-us/visualstudio/ide/reference/upgrade-devenv-exe?view=vs-2019 + # pipe to file to ensure that it terminates https://stackoverflow.com/questions/48896010/occasionally-occurring-msbuild-error-msb3428/48918105#48918105 + # discussion of using vswhere.exe here: https://stackoverflow.com/questions/65287456/how-to-upgrade-a-visual-studio-project-from-within-a-github-action/65311868#65311868 + run: | + $devenv = & vswhere.exe '-property' productPath + Write-Output "$devenv" + & $devenv "${{env.VCPROJ_FILE_PATH}}" /Upgrade /NoSplash | Out-Null + Write-Output "devenv launched" + while (!(Test-Path "${{env.VCXPROJ_FILE_PATH}}")) { Start-Sleep -Seconds 10 } + Write-Output "vcxproj found" + while (!(Test-Path "${{env.VCXPROJ_FILTERS_FILE_PATH}}")) { Start-Sleep -Seconds 10 } + Write-Output "vcxproj.filters found" + Start-Sleep -Seconds 10 + Write-Output "done." + + - name: Remove ASIO Files and Enable PA_USE_DS=1 + # Process the project files to remove ASIO-related sources and includes (since we can not access the ASIO SDK in a public build) + run: | + # Process .vcxproj file: remove source files + $xdoc = new-object System.Xml.XmlDocument + $vcxprojFile = resolve-path("${{env.VCXPROJ_FILE_PATH}}") + $xdoc.load($vcxprojFile) + $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable + $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) + $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) + Write-Output "deleting ASIO related compilation nodes from .vcxproj:" + Write-Output $nodes + ForEach($node in $nodes) { + $parent = $node.ParentNode + $parent.RemoveChild($node) + } + # Enable DirectSound host API + $nodes = $xdoc.SelectNodes("//vs:PreprocessorDefinitions[contains(., 'PA_USE_DS=0')]", $namespace) + ForEach($node in $nodes) { + $text = $node.InnerText + $node.InnerText = $text -replace 'PA_USE_DS=0', 'PA_USE_DS=1' + } + $xdoc.save($vcxprojFile) + # Process .vcxproj.filters file: remove source files and includes + $vcxprojFiltersFile = resolve-path("${{env.VCXPROJ_FILTERS_FILE_PATH}}") + $xdoc.load($vcxprojFiltersFile) + $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable + $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) + $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) + Write-Output "deleting ASIO related compilation nodes from .vcxproj.filters:" + Write-Output $nodes + ForEach($node in $nodes) { + $parent = $node.ParentNode + $parent.RemoveChild($node) + } + $nodes = $xdoc.SelectNodes("//vs:ClInclude[contains(@Include, 'pa_asio.h')]", $namespace) + Write-Output "deleting ASIO related include nodes from .vcxproj.filters:" + Write-Output $nodes + ForEach($node in $nodes) { + $parent = $node.ParentNode + $parent.RemoveChild($node) + } + $xdoc.save($vcxprojFiltersFile) + # Process .def file: remove PaAsio_ symbols + Set-Content -Path "${{env.DEF_FILE_PATH}}" -Value (Get-Content -Path "${{env.DEF_FILE_PATH}}" | Select-String -Pattern 'PaAsio_' -NotMatch) + + - name: Build + working-directory: ${{env.GITHUB_WORKSPACE}} + # Add additional options to the MSBuild command line here (like platform or verbosity level). + # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference + run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.BUILD_PLATFORM}} ${{env.VCXPROJ_FILE_PATH}} diff --git a/.github/workflows/autotools.yml b/.github/workflows/autotools.yml new file mode 100644 index 000000000..4d6fb6cb9 --- /dev/null +++ b/.github/workflows/autotools.yml @@ -0,0 +1,20 @@ +name: autotools build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-autotools: + + runs-on: ubuntu-latest + name: Ubuntu + + steps: + - uses: actions/checkout@v2 + - name: configure + run: ./configure + - name: make + run: make diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 000000000..a25eaabf7 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,128 @@ +name: CMake build + +on: [push, pull_request] + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - name: Ubuntu GCC + os: ubuntu-latest + install_dir: ~/portaudio + vcpkg_triplet: x64-linux + cmake_generator: "Unix Makefiles" + cmake_options: + -DPA_USE_OSS=ON + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + - name: Ubuntu MinGW + os: ubuntu-latest + install_dir: ~/portaudio + asio_sdk_cache_path: asiosdk.zip + dependencies_extras: mingw-w64 + # FIXME: linking JACK fails with vcpkg. Switch the CMake toolchain file to use vcpkg. The + # toolchain file in this repository is not needed when using a MinGW triplet with vcpkg. + vcpkg_triplet: x64-mingw-static + cmake_generator: "Unix Makefiles" + cmake_options: + -DPA_USE_ASIO=ON + -DASIO_SDK_ZIP_PATH=asiosdk.zip + -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/i686-w64-mingw32.cmake + - name: Windows MSVC + os: windows-latest + install_dir: C:\portaudio + vcpkg_triplet: x64-windows + cmake_generator: "Visual Studio 17 2022" + # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to + # asiosdk instead of asiosdk.zip. + asio_sdk_cache_path: "asiosdk.zip" + # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. + cmake_options: + -DPA_USE_ASIO=ON + -DASIO_SDK_ZIP_PATH="asiosdk.zip" + -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake + - name: Windows MinGW + os: windows-latest + install_dir: C:\portaudio + vcpkg_triplet: x64-mingw-static + cmake_generator: "MinGW Makefiles" + # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to + # asiosdk instead of asiosdk.zip. + asio_sdk_cache_path: "asiosdk.zip" + # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. + cmake_options: + -DPA_USE_ASIO=ON + -DASIO_SDK_ZIP_PATH="asiosdk.zip" + -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake + - name: macOS Clang + os: macOS-latest + install_dir: ~/portaudio + vcpkg_triplet: x64-osx + cmake_generator: "Unix Makefiles" + cmake_options: + -DCMAKE_FRAMEWORK=OFF + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + - name: macOS Clang framework + os: macOS-latest + install_dir: ~/portaudio + vcpkg_triplet: x64-osx + cmake_generator: "Unix Makefiles" + cmake_options: + -DCMAKE_FRAMEWORK=ON + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + + runs-on: ${{ matrix.os }} + name: ${{ matrix.name }} + env: + cmake_build_type: RelWithDebInfo + steps: + - name: checkout Git repository + uses: actions/checkout@v2 + - name: "[Ubuntu] install dependencies" + run: | + sudo apt-get update + sudo apt-get install libasound2-dev ${{ matrix.dependencies_extras }} + if: matrix.os == 'ubuntu-latest' + - name: "[macOS] install dependencies" + # https://github.com/PortAudio/portaudio/issues/767 + run: brew install pkg-config + if: matrix.os == 'macOS-latest' + - name: "[Windows/MinGW] set up ASIO SDK cache" + uses: actions/cache@v2 + if: matrix.asio_sdk_cache_path != null + with: + path: ${{ matrix.asio_sdk_cache_path }} + key: ${{ hashFiles('.github/asiosdk-version.txt') }} + - name: Setup vcpkg + uses: lukka/run-vcpkg@v7 + if: ${{ matrix.vcpkg_triplet }} != null + with: + vcpkgTriplet: ${{ matrix.vcpkg_triplet }} + appendedCacheKey: ${{ hashFiles( '**/vcpkg.json' ) }} + additionalCachedPaths: build/vcpkg_installed + vcpkgGitCommitId: f30786c9c4c901f21a13e2d524349e39cc359a90 + # Required when using vcpkg.json manifest in repository + setupOnly: true + - name: configure + run: cmake + -G "${{ matrix.cmake_generator }}" + ${{ matrix.cmake_options }} + -DCMAKE_INSTALL_PREFIX=${{ matrix.install_dir }} + -DCMAKE_BUILD_TYPE=${{ env.cmake_build_type }} + -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} + -DPA_USE_SKELETON=ON + -DPA_BUILD_TESTS=ON + -DPA_BUILD_EXAMPLES=ON + -DPA_WARNINGS_ARE_ERRORS=ON + -S . + -B build + - name: build + run: cmake --build build --config ${{ env.cmake_build_type }} --parallel 2 + - name: install + run: cmake --install build --config ${{ env.cmake_build_type }} + - name: "Upload GitHub Actions artifacts" + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.name }} PortAudio build + path: ${{ matrix.install_dir }} diff --git a/.github/workflows/compare_def_files.yml b/.github/workflows/compare_def_files.yml new file mode 100644 index 000000000..b2b342411 --- /dev/null +++ b/.github/workflows/compare_def_files.yml @@ -0,0 +1,17 @@ +name: Check that PortAudio .def files are in sync + +on: [push, pull_request] + +jobs: + run-pa-compare-def-files-py: + + runs-on: ubuntu-latest + name: Ubuntu + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Run the pa_compare_def_files.py script + run: python ./pa_compare_def_files.py diff --git a/.github/workflows/whitelint.yml b/.github/workflows/whitelint.yml new file mode 100644 index 000000000..41cf8ff8b --- /dev/null +++ b/.github/workflows/whitelint.yml @@ -0,0 +1,17 @@ +name: Check for valid whitespace usage in PortAudio source files + +on: [push, pull_request] + +jobs: + run-pa-whitelint-dot-py: + + runs-on: ubuntu-latest + name: Ubuntu + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Run the pa_whitelint.py script + run: python ./pa_whitelint.py From b9e7f419a90f6d39c3743e62bcbb07d138481e0f Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 11:41:36 +0200 Subject: [PATCH 068/125] Minor fixes to FindOboe.cmake --- cmake/modules/FindOboe.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 23fd02308..9e27ee01e 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -22,7 +22,8 @@ endif() if(NOT OBOE_DIRECTORY) message(AUTHOR_WARNING - "If you're trying to use Oboe as a Host API, please specify the directory where you cloned its repository. For further information, please read src/hostapi/oboe/README.md" + "If you're trying to use Oboe as a Host API, please specify the directory where you + cloned its repository. For further information, please read src/hostapi/oboe/README.md" ) set(OBOE_FOUND FALSE) else() @@ -53,8 +54,7 @@ else() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARIES} ${LOG_LIBRARY}) - - + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Oboe @@ -71,4 +71,4 @@ else() target_include_directories(Oboe::oboe INTERFACE "${OBOE_INCLUDE_DIR}") endif() endif() -endif() \ No newline at end of file +endif() From 42a02b4f42b43bb76c1bffb3d3f81af44e12b8ef Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 11:52:36 +0200 Subject: [PATCH 069/125] Enhanced prebuilt libraries compatibility in FindOboe.cmake --- cmake/modules/FindOboe.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 9e27ee01e..ea66ec394 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -31,10 +31,10 @@ else() set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) endif() - if(NOT DEFINED OBOE_LIBRARIES) + if(NOT DEFINED OBOE_LIBRARY_DIRS) set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) - set(OBOE_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) endif() + set(OBOE_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) @@ -54,7 +54,7 @@ else() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARIES} ${LOG_LIBRARY}) - + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Oboe From 26b11aa8f7dd885067c67977e1beead245a8d2a6 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 11:53:50 +0200 Subject: [PATCH 070/125] Minor changes to Pa_Oboe/Readme and pa_oboe.cpp --- src/hostapi/oboe/README.md | 7 +++---- src/hostapi/oboe/pa_oboe.cpp | 12 +++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index db64c90bc..c4de74088 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -8,13 +8,12 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the prebuilt libraries, you can just clone the include directory. + Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the prebuilt libraries, you can skip this step. 3) Set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository, and build the Oboe libraries (you can use "build_all_android.sh"). If you instead used the prebuilt libraries, do the following: - set OBOE_DIRECTORY to TRUE; - - set OBOE_INCLUDE_DIR to the include directory you cloned in step 2; - - set OBOE_LIBRARIES to path_to_Oboe_libraries_folder/${ANDROID_ABI}/your_path_to_prebuilt_libraries_of_the_chosen_ABI; + - set OBOE_LIBRARY_DIRS path_to_Oboe_libraries_folder/${ANDROID_ABI}), the code will search the prebuilt library of the chosen ABI in that folder. 4) Build PaOboe (you can use "build_all_PaOboe.sh"). 5) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. @@ -31,4 +30,4 @@ I'm positive that automatic format selection is possible, but simply using PaUt ### Buffer sizes: -Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index c96fdbc5f..5878ca25e 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -1337,9 +1337,9 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, m_androidOutputUsage != Usage::NotificationRingtone && m_androidOutputUsage != Usage::VoiceCommunication && m_androidOutputUsage != Usage::VoiceCommunicationSignalling && - m_androidOutputUsage != Usage::Alarm - // See if more are needed. - ) { + m_androidOutputUsage != Usage::Alarm && + m_androidOutputUsage != Usage::Game + ) { m_outcome = paIncompatibleHostApiSpecificStreamInfo; return m_outcome; } @@ -1442,6 +1442,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; PaSampleFormat m_hostInputSampleFormat, m_hostOutputSampleFormat; + + //FIXME: add a function that lets the user choose usage and preset Usage m_androidOutputUsage = Usage::VoiceCommunication; InputPreset m_androidInputPreset = InputPreset::Generic; @@ -1476,8 +1478,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, m_androidInputPreset != InputPreset::Camcorder && m_androidInputPreset != InputPreset::VoiceRecognition && m_androidInputPreset != InputPreset::VoiceCommunication - // Should I add compatibility with VoicePerformance? - ) + m_androidInputPreset != InputPreset::VoicePerformance + ) return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paInt16" with whatever format you prefer - From 8265a71f1a77350c33506429666eaee3047261ab Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 12:09:14 +0200 Subject: [PATCH 071/125] Removed auto latency tuning in favor of simpler impleentation in pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 74 ++---------------------------------- 1 file changed, 3 insertions(+), 71 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 5878ca25e..fd763bd40 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -161,10 +161,9 @@ using namespace oboe; //Useful global variables int32_t g_inputDeviceId = kUnspecified; int32_t g_outputDeviceId = kUnspecified; -PerformanceMode g_inputPerfMode = PerformanceMode::None; -bool g_inputPerfModeUser = false; -PerformanceMode g_outputPerfMode = PerformanceMode::None; -bool g_outputPerfModeUser = false; + +PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; +PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -254,10 +253,6 @@ class OboeEngine : public AudioStreamCallback { //device selection implementation int32_t getSelectedDevice(oboe::Direction direction); - - //auto performance mode selection - void performanceModeAutoSelection(Direction direction); - double assertLatency(Direction direction); }; @@ -320,8 +315,6 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan return m_outcome; } - performanceModeAutoSelection(direction); - if (sampleRate != kUnspecified) { m_outcome = (sampleRate == m_builder.getSampleRate()); if(!m_outcome) { @@ -880,65 +873,6 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { } -/** - * \brief Function used to automatically select the performance mode, based on the latency value LOW_LATENCY_MS, if no - * choice was made via PaOboe_SetPerformanceMode. It uses OboeEngine::assertLatency to make this decision. - * @param direction the Oboe::Direction of the stream we want to select the mode of. - */ -void OboeEngine::performanceModeAutoSelection(Direction direction){ - double m_result = assertLatency(direction); - - if(direction == Direction::Input) { - if (m_result >= 0 && !g_inputPerfModeUser) { - if (m_result <= LOW_LATENCY_MS) - g_inputPerfMode = PerformanceMode::LowLatency; - else - g_inputPerfMode = PerformanceMode::PowerSaving; - } - } else { - if (m_result >= 0 && !g_outputPerfModeUser) { - if (m_result <= LOW_LATENCY_MS) - g_outputPerfMode = PerformanceMode::LowLatency; - else - g_outputPerfMode = PerformanceMode::PowerSaving; - } - } -} - - -/** - * \brief Asserts if the device supports latency tutning, then calculates the latency of an AudioStream. - * @param direction the direction of the AudioStream we want to check. - * @return the measured latency, or -1.0 if any error occurs. - */ -double OboeEngine::assertLatency(Direction direction){ - if(__ANDROID_API__<31) { - LOGI("[OboeEngine::assertLatency]\t Latency Tuning is not supported for Android API level < 31"); - return -1.0; - } - if(direction == Direction::Input){ - ResultWithValue m_result = inputStream->calculateLatencyMillis(); - if (m_result) { - return m_result.value(); - } else { - LOGE("[OboeEngine::assertLatency]\t Error calculating input latency: %s", - oboe::convertToText(m_result.error())); - return -1.0; - } - } else { - ResultWithValue m_result = outputStream->calculateLatencyMillis(); - if (m_result) { - return m_result.value(); - } else { - LOGE("[OboeEngine::assertLatency]\t Error calculating output latency: %s", - oboe::convertToText(m_result.error())); - return -1.0; - } - } - -} - - /*----------------------------- PaSkeleton functions implementations -----------------------------*/ /** @@ -1957,10 +1891,8 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ if(direction == Direction::Input) { g_inputPerfMode = performanceMode; - g_inputPerfModeUser = true; } else { g_outputPerfMode = performanceMode; - g_outputPerfModeUser = true; } } From e19c30d6d5382d5dff2cffd802d4e8433dbc6840 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 12:35:46 +0200 Subject: [PATCH 072/125] Set paFloat32 as default format in pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index fd763bd40..1f9b63e99 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -299,14 +299,14 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan m_builder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. - ->setFormat(AudioFormat::I16) + ->setFormat(AudioFormat::Float) ->setDirection(direction) ->setSampleRate(sampleRate) ->setChannelCount(channelCount); if (direction == Direction::Input) { - m_result = m_builder.openStream(inputStream); + m_result = m_builder->openStream(inputStream); } else { - m_result = m_builder.openStream(outputStream); + m_result = m_builder->openStream(outputStream); } if (m_result != Result::OK) { @@ -1225,8 +1225,8 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, m_androidRecordingPreset != InputPreset::Camcorder && m_androidRecordingPreset != InputPreset::VoiceRecognition && m_androidRecordingPreset != InputPreset::VoiceCommunication - // Should I add compatibility with VoicePerformance? - ) { + m_androidRecordingPreset != InputPreset::VoicePerformance + ) { m_outcome = paIncompatibleHostApiSpecificStreamInfo; return m_outcome; } @@ -1416,14 +1416,14 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - + /* FIXME: Replace "paFloat32" with whatever format you prefer - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paInt16, m_inputSampleFormat); + paFloat32, m_inputSampleFormat); m_oboeStream->inputFormat = m_hostInputSampleFormat; } else { m_inputChannelCount = 0; - m_inputSampleFormat = m_hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ + m_inputSampleFormat = m_hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ m_oboeStream->inputFormat = m_hostInputSampleFormat; } @@ -1456,15 +1456,15 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - + /* FIXME: Replace "paFloat32" with whatever format you prefer - PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paInt16, m_outputSampleFormat); + paFloat32, m_outputSampleFormat); m_oboeStream->outputFormat = m_hostOutputSampleFormat; } else { m_outputChannelCount = 0; - m_outputSampleFormat = m_hostOutputSampleFormat = paInt16; + m_outputSampleFormat = m_hostOutputSampleFormat = paFloat32; m_oboeStream->outputFormat = m_hostOutputSampleFormat; } From 4112fcbfbcf890ab033ffded5d59a9a126b01b94 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 10:06:19 +0200 Subject: [PATCH 073/125] Renamed most of the variables according to best coding practices. --- include/pa_oboe.h | 2 +- src/hostapi/oboe/pa_oboe.cpp | 1547 +++++++++++++++++----------------- 2 files changed, 767 insertions(+), 782 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 761a7b937..d345617d7 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -104,7 +104,7 @@ void PaOboe_SetNativeBufferSize(unsigned long bufferSize); * buffer size when doing this, and use the sample rate defined in AudioManager's * android.media.property.OUTPUT_SAMPLE_RATE. */ -void PaOboe_SetNumberOfBuffers(unsigned buffers); +void PaOboe_SetNumberOfBuffers(unsigned numberOfBuffers); #ifdef __cplusplus } diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 1f9b63e99..fd70931a3 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -153,8 +153,8 @@ static signed long GetStreamWriteAvailable(PaStream *stream); static unsigned long GetApproximateLowBufferSize(); // Commonly used parameters initialized. -static unsigned long nativeBufferSize = 0; -static unsigned numberOfBuffers = 2; +static unsigned long g_nativeBufferSize = 0; +static unsigned g_numberOfBuffers = 2; using namespace oboe; @@ -165,6 +165,8 @@ int32_t g_outputDeviceId = kUnspecified; PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; +class OboeEngine; + /** * Stream structure, useful to store relevant information. It's needed by Portaudio. */ @@ -194,9 +196,15 @@ typedef struct OboeStream { void **inputBuffers; int currentInputBuffer; - long engineAddress; unsigned long framesPerHostCallback; unsigned bytesPerFrame; + + OboeEngine *getEngineAddress() { return oboeEngineAddress; } + + void setEngineAddress(OboeEngine *i_oboeEngine) { oboeEngineAddress = i_oboeEngine; } + +private: + OboeEngine *oboeEngineAddress; } OboeStream; @@ -211,42 +219,49 @@ class OboeEngine : public AudioStreamCallback { //Stream-managing functions bool tryStream(Direction direction, int32_t sampleRate, int32_t channelCount); - PaError openStream(Direction direction, int32_t sampleRate, + + PaError openStream(OboeStream *oboeStream, Direction direction, int32_t sampleRate, Usage outputUsage, InputPreset inputPreset); - bool startStream(); - bool stopStream(); + + bool startStream(OboeStream *oboeStream); + + bool stopStream(OboeStream *oboeStream); + bool restartStream(int direction); - bool closeStream(); - bool abortStream(); + + bool closeStream(OboeStream *oboeStream); + + bool abortStream(OboeStream *oboeStream); //Callback function for non-blocking streams and some callback utils DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) override; + void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + void resetCallbackCounters(); //Blocking read/write functions bool writeStream(const void *buffer, int32_t framesToWrite); + bool readStream(void *buffer, int32_t framesToRead); //Engine utils - OboeStream* initializeOboeStream(); - void setEngineAddress(long address); + OboeStream *allocateOboeStream(); private: - //The only instance of OboeStream that will be used - OboeStream *oboeStream; + OboeStream *m_oboeStreamHolder; //The only instances of output and input streams that will be used, and their builders - std::shared_ptr outputStream; - AudioStreamBuilder outputBuilder; - std::shared_ptr inputStream; - AudioStreamBuilder inputBuilder; + std::shared_ptr m_outputStream; + AudioStreamBuilder m_outputBuilder; + std::shared_ptr m_inputStream; + AudioStreamBuilder m_inputBuilder; //callback utils - unsigned long framesProcessed{}; - PaStreamCallbackTimeInfo timeInfo{}; - struct timespec timeSpec{}; + unsigned long m_framesProcessed{}; + PaStreamCallbackTimeInfo m_timeInfo{}; + struct timespec m_timeSpec{}; //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -277,7 +292,7 @@ typedef struct PaOboeHostApiRepresentation { * \brief Initializes an instance of the engine. */ OboeEngine::OboeEngine() { - oboeStream = nullptr; + m_oboeStreamHolder = allocateOboeStream(); } @@ -292,52 +307,52 @@ OboeEngine::OboeEngine() { * @return true if the requested sample rate / channel count is supported by the device, false if * they aren't, or if tryStream couldn't open a stream. */ -bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { - AudioStreamBuilder m_builder; - Result m_result; - bool m_outcome = false; +bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t i_channelCount) { + AudioStreamBuilder builder; + Result result; + bool outcome = false; - m_builder.setDeviceId(getSelectedDevice(direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + builder.setDeviceId(getSelectedDevice(i_direction)) + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) - ->setDirection(direction) - ->setSampleRate(sampleRate) - ->setChannelCount(channelCount); - if (direction == Direction::Input) { - m_result = m_builder->openStream(inputStream); + ->setDirection(i_direction) + ->setSampleRate(i_sampleRate) + ->setChannelCount(i_channelCount); + if (i_direction == Direction::Input) { + result = builder->openStream(m_inputStream); } else { - m_result = m_builder->openStream(outputStream); + result = builder->openStream(m_outputStream); } - if (m_result != Result::OK) { + if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", - convertToText(m_result)); - return m_outcome; + convertToText(result)); + return outcome; } - if (sampleRate != kUnspecified) { - m_outcome = (sampleRate == m_builder.getSampleRate()); - if(!m_outcome) { + if (i_sampleRate != kUnspecified) { + outcome = (i_sampleRate == builder.getSampleRate()); + if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - sampleRate, m_builder.getSampleRate()); + i_sampleRate, builder.getSampleRate()); } - } else if (channelCount != kUnspecified) { - m_outcome = (channelCount == m_builder.getChannelCount()); - if(!m_outcome) { + } else if (i_channelCount != kUnspecified) { + outcome = (i_channelCount == builder.getChannelCount()); + if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, m_builder.getChannelCount()); + channelCount, builder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); - m_outcome = false; + outcome = false; } - if (direction == Direction::Input) - inputStream->close(); + if (i_direction == Direction::Input) + m_inputStream->close(); else - outputStream->close(); + m_outputStream->close(); - return m_outcome; + return outcome; } @@ -356,103 +371,101 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan * @return paNoError if everything goes as expected, paUnanticipatedHostError if Oboe fails to open * a stream, and paInsufficientMemory if the memory allocation of the buffers fails. */ -PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, - Usage androidOutputUsage, InputPreset androidInputPreset) { - PaError m_error = paNoError; - Result m_result; - - if (direction == Direction::Input) { - inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) - ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) - ->setSampleRate(sampleRate) +PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, int32_t i_sampleRate, + Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { + PaError error = paNoError; + Result result; + + if (i_direction == Direction::Input) { + m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) + ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) + ->setSampleRate(i_sampleRate) ->setDirection(Direction::Input) ->setDeviceId(getSelectedDevice(Direction::Input)) ->setPerformanceMode(g_inputPerfMode) - ->setInputPreset(androidInputPreset) - ->setFramesPerCallback(oboeStream->framesPerHostCallback); + ->setInputPreset(i_androidInputPreset) + ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); - if (!(oboeStream->isBlocking)) { + if (!(i_oboeStream->isBlocking)) { resetCallbackCounters(); - inputBuilder.setDataCallback(this) + m_inputBuilder.setDataCallback(this) ->setErrorCallback(this); } - m_result = inputBuilder.openStream(inputStream); + result = m_inputBuilder.openStream(m_inputStream); - if (m_result != Result::OK) { + if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", - convertToText(m_result)); - m_error = paUnanticipatedHostError; - return m_error; + convertToText(result)); + return paUnanticipatedHostError; } - inputStream->setBufferSizeInFrames(inputStream->getFramesPerBurst() * numberOfBuffers); - oboeStream->inputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); + m_inputStream->setBufferSizeInFrames(m_inputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->inputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - oboeStream->framesPerHostCallback * - oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.inputChannelCount); + for (int i = 0; i < g_numberOfBuffers; ++i) { + i_oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + i_oboeStream->framesPerHostCallback * + i_oboeStream->bytesPerFrame * + i_oboeStream->bufferProcessor.inputChannelCount); - if (!oboeStream->inputBuffers[i]) { + if (!i_oboeStream->inputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(oboeStream->inputBuffers[j]); - PaUtil_FreeMemory(oboeStream->inputBuffers); - inputStream->close(); - m_error = paInsufficientMemory; + PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); + PaUtil_FreeMemory(i_oboeStream->inputBuffers); + m_inputStream->close(); + error = paInsufficientMemory; break; } } - oboeStream->currentInputBuffer = 0; + i_oboeStream->currentInputBuffer = 0; } else { - outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) - ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) - ->setSampleRate(sampleRate) + m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) + ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) + ->setSampleRate(i_sampleRate) ->setDirection(Direction::Output) ->setDeviceId(getSelectedDevice(Direction::Output)) ->setPerformanceMode(g_outputPerfMode) - ->setUsage(androidOutputUsage) - ->setFramesPerCallback(oboeStream->framesPerHostCallback); + ->setUsage(i_androidOutputUsage) + ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); - if (!(oboeStream->isBlocking)) { + if (!(i_oboeStream->isBlocking)) { resetCallbackCounters(); - outputBuilder.setDataCallback(this) + m_outputBuilder.setDataCallback(this) ->setErrorCallback(this); } - m_result = outputBuilder.openStream(outputStream); - if (m_result != Result::OK) { + result = m_outputBuilder.openStream(m_outputStream); + if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", - convertToText(m_result)); - m_error = paUnanticipatedHostError; - return m_error; + convertToText(result)); + return paUnanticipatedHostError; } - outputStream->setBufferSizeInFrames(outputStream->getFramesPerBurst() * numberOfBuffers); - oboeStream->outputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); + m_outputStream->setBufferSizeInFrames(m_outputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->outputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - oboeStream->framesPerHostCallback * - oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.outputChannelCount); + for (int i = 0; i < g_numberOfBuffers; ++i) { + i_oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + i_oboeStream->framesPerHostCallback * + i_oboeStream->bytesPerFrame * + i_oboeStream->bufferProcessor.outputChannelCount); - if (!oboeStream->outputBuffers[i]) { + if (!i_oboeStream->outputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(oboeStream->outputBuffers[j]); - PaUtil_FreeMemory(oboeStream->outputBuffers); - outputStream->close(); - m_error = paInsufficientMemory; + PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); + PaUtil_FreeMemory(i_oboeStream->outputBuffers); + m_outputStream->close(); + error = paInsufficientMemory; break; } } - oboeStream->currentOutputBuffer = 0; + i_oboeStream->currentOutputBuffer = 0; } - return m_error; + return error; } @@ -461,23 +474,23 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, * and requested to be started. * @return true if the streams we wanted to start are started successfully, false otherwise. */ -bool OboeEngine::startStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::startStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; - if (oboeStream->hasInput) { - m_inputResult = inputStream->requestStart(); - if (m_inputResult != Result::OK) + if (i_oboeStream->hasInput) { + inputResult = m_inputStream->requestStart(); + if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", - convertToText(m_inputResult)); + convertToText(inputResult)); } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->requestStart(); - if (m_outputResult != Result::OK) + if (i_oboeStream->hasOutput) { + outputResult = m_outputStream->requestStart(); + if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", - convertToText(m_outputResult)); + convertToText(outputResult)); } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -486,23 +499,23 @@ bool OboeEngine::startStream() { * and requested to be stopped. * @return true if the streams we wanted to stop are stopped successfully, false otherwise. */ -bool OboeEngine::stopStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::stopStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; - if (oboeStream->hasInput) { - m_inputResult = inputStream->requestStop(); - if (m_inputResult != Result::OK) + if (i_oboeStream->hasInput) { + inputResult = m_inputStream->requestStop(); + if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", - convertToText(m_inputResult)); + convertToText(inputResult)); } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->requestStop(); - if (m_outputResult != Result::OK) + if (i_oboeStream->hasOutput) { + outputResult = m_outputStream->requestStop(); + if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", - convertToText(m_outputResult)); + convertToText(outputResult)); } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -511,67 +524,67 @@ bool OboeEngine::stopStream() { * audio devices change while a stream is started. * @return true if the stream is restarted successfully, false otherwise. */ -bool OboeEngine::restartStream(int direction) { - bool m_outcome = true; - Result m_result; +bool OboeEngine::restartStream(int i_direction) { + bool outcome = true; + Result result; - switch (direction) { + switch (i_direction) { case 1: //output-only //stopping and closing - m_result = outputStream->stop(); - if (m_result != Result::OK) + result = m_outputStream->stop(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", - convertToText(m_result)); - m_result = outputStream->close(); - if (m_result != Result::OK) + convertToText(result)); + result = m_outputStream->close(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", - convertToText(m_result)); + convertToText(result)); //reopening and restarting - m_result = outputBuilder.openStream(outputStream); - if (m_result != Result::OK) + result = outputBuilder.openStream(m_outputStream); + if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", - convertToText(m_result)); - m_result = outputStream->start(); - if (m_result != Result::OK) { + convertToText(result)); + result = m_outputStream->start(); + if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", - convertToText(m_result)); - m_outcome = false; + convertToText(result)); + outcome = false; } break; case 2: //input-only //stopping and closing - m_result = inputStream->stop(); - if (m_result != Result::OK) + result = m_inputStream->stop(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", - convertToText(m_result)); - m_result = inputStream->close(); - if (m_result != Result::OK) + convertToText(result)); + result = m_inputStream->close(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", - convertToText(m_result)); + convertToText(result)); //reopening and restarting - m_result = inputBuilder.openStream(inputStream); - if (m_result != Result::OK) + result = inputBuilder.openStream(m_inputStream); + if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", - convertToText(m_result)); - m_result = inputStream->start(); - if (m_result != Result::OK) { + convertToText(result)); + result = m_inputStream->start(); + if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", - convertToText(m_result)); - m_outcome = false; + convertToText(result)); + outcome = false; } break; default: - // unspecified direction or both directions, abort streams + // unspecified direction or both directions: restart both streams LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); - m_outcome = (restartStream(1) && restartStream(2)); + outcome = (restartStream(1) && restartStream(2)); break; } - return m_outcome; + return outcome; } @@ -580,30 +593,33 @@ bool OboeEngine::restartStream(int direction) { * and closed if active. * @return true if the stream is closed successfully, otherwise returns false. */ -bool OboeEngine::closeStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::closeStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; + bool hasOutput = true, hasInput = true; - if(oboeStream == nullptr){ - LOGE("[OboeEngine::closeStream]\t Tried to close a NULL stream. Exiting closeStream."); - return false; + if (i_oboeStream == nullptr) { + LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Terminating both oboe streams."); + } else { + hasInput = i_oboeStream->hasInput; + hasOutput = i_oboeStream->hasOutput; } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->close(); - if (m_outputResult == Result::ErrorClosed) { - m_outputResult = Result::OK; + if (hasOutput) { + outputResult = m_outputStream->close(); + if (outputResult == Result::ErrorClosed) { + outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } - if (oboeStream->hasInput) { - m_inputResult = inputStream->close(); - if (m_inputResult == Result::ErrorClosed) { - m_inputResult = Result::OK; + if (hasInput) { + inputResult = m_inputStream->close(); + if (inputResult == Result::ErrorClosed) { + inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); } } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -611,36 +627,39 @@ bool OboeEngine::closeStream() { * \brief Stops oboeStream - both input and output audiostreams are checked and forcefully stopped. * @return true if the output stream and the input stream are stopped successfully, false otherwise. */ -bool OboeEngine::abortStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::abortStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; + bool hasOutput = true, hasInput = true; - if(oboeStream == nullptr){ - LOGE("[OboeEngine::abortStream]\t Tried to abort a NULL stream. Exiting abortStream."); - return false; + if (i_oboeStream == nullptr) { + LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams."); + } else { + hasInput = i_oboeStream->hasInput; + hasOutput = i_oboeStream->hasOutput; } - if (oboeStream->hasInput) { - m_inputResult = inputStream->stop(); - if (m_inputResult != Result::OK) + if (hasInput) { + inputResult = m_inputStream->stop(); + if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", - convertToText(m_inputResult)); - m_inputResult = inputStream->close(); + convertToText(inputResult)); + inputResult = m_inputStream->close(); if (m_inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", - convertToText(m_inputResult)); + convertToText(inputResult)); } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->stop(); + if (hasOutput) { + outputResult = m_outputStream->stop(); if (m_outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(m_outputResult)); - m_outputResult = outputStream->close(); - if (m_outputResult != Result::OK) + outputResult = m_outputStream->close(); + if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", - convertToText(m_outputResult)); + convertToText(outputResult)); } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -648,84 +667,86 @@ bool OboeEngine::abortStream() { * \brief Oboe's callback routine. FIXME: implement onErrorAfterClose correctly */ DataCallbackResult -OboeEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { - - clock_gettime(CLOCK_REALTIME, &timeSpec); - timeInfo.currentTime = (PaTime) (timeSpec.tv_sec + (timeSpec.tv_nsec / 1000000000.0)); - timeInfo.outputBufferDacTime = (PaTime) (oboeStream->framesPerHostCallback +OboeEngine::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { + + clock_gettime(CLOCK_REALTIME, &m_timeSpec); + m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); + m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + / + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); + m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback / - oboeStream->streamRepresentation.streamInfo.sampleRate - + timeInfo.currentTime); - timeInfo.inputBufferAdcTime = (PaTime) (oboeStream->framesPerHostCallback - / - oboeStream->streamRepresentation.streamInfo.sampleRate - + timeInfo.currentTime); + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); /* check if StopStream or AbortStream was called */ - if (oboeStream->doStop) { - oboeStream->callbackResult = paComplete; - } else if (oboeStream->doAbort) { - oboeStream->callbackResult = paAbort; + if (m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->callbackResult = paComplete; + } else if (m_oboeStreamHolder->doAbort) { + m_oboeStreamHolder->callbackResult = paAbort; } - PaUtil_BeginCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&oboeStream->bufferProcessor, - &timeInfo, oboeStream->cbFlags); + PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, + &m_timeInfo, m_oboeStreamHolder->cbFlags); - if (oboeStream->hasOutput) { - oboeStream->outputBuffers[oboeStream->currentOutputBuffer] = audioData; - PaUtil_SetOutputFrameCount(&oboeStream->bufferProcessor, numFrames); - PaUtil_SetInterleavedOutputChannels(&oboeStream->bufferProcessor, 0, - (void *) ((PaInt16 **) oboeStream->outputBuffers)[oboeStream->currentOutputBuffer], + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; + PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); + PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ + m_oboeStreamHolder->currentOutputBuffer], 0); } - if (oboeStream->hasInput) { - audioData = oboeStream->inputBuffers[oboeStream->currentInputBuffer]; - PaUtil_SetInputFrameCount(&oboeStream->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&oboeStream->bufferProcessor, 0, - (void *) ((PaInt16 **) oboeStream->inputBuffers)[oboeStream->currentInputBuffer], + if (m_oboeStreamHolder->hasInput) { + i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; + PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ + m_oboeStreamHolder->currentInputBuffer], 0); } - /* continue processing user buffers if cbresult is pacontinue or if cbresult is pacomplete and userbuffers aren't empty yet */ - if (oboeStream->callbackResult == paContinue - || (oboeStream->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&oboeStream->bufferProcessor))) { - framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, - &oboeStream->callbackResult); + /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ + if (m_oboeStreamHolder->callbackResult == paContinue + || (m_oboeStreamHolder->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { + m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, + &oboeStream->callbackResult); } /* enqueue a buffer only when there are frames to be processed, * this will be 0 when paComplete + empty buffers or paAbort */ - if (framesProcessed > 0) { - if (oboeStream->hasOutput) { - oboeStream->currentOutputBuffer = - (oboeStream->currentOutputBuffer + 1) % numberOfBuffers; + if (m_framesProcessed > 0) { + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->currentOutputBuffer = + (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; } - if (oboeStream->hasInput) { - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % numberOfBuffers; + if (m_oboeStreamHolder->hasInput) { + m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; } } - PaUtil_EndCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer, framesProcessed); + PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); /* StopStream was called */ - if (framesProcessed == 0 && oboeStream->doStop) { - oboeStream->oboeCallbackResult = DataCallbackResult::Stop; + if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; } /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (framesProcessed == 0 && !(oboeStream->doAbort || oboeStream->doStop)) { - oboeStream->isActive = false; - oboeStream->isStopped = true; - if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) - oboeStream->streamRepresentation.streamFinishedCallback( - oboeStream->streamRepresentation.userData); - //oboeStream->oboeCallbackResult = DataCallbackResult::Stop; TODO: Resume this test (onAudioReady) + else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { + m_oboeStreamHolder->isActive = false; + m_oboeStreamHolder->isStopped = true; + if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) + m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( + m_oboeStreamHolder->streamRepresentation.userData); + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) } - return oboeStream->oboeCallbackResult; + return m_oboeStreamHolder->oboeCallbackResult; } @@ -733,14 +754,13 @@ OboeEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numF * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells * what error occurred. */ -void OboeEngine::onErrorAfterClose(AudioStream *audioStream, Result error) { - if (error == oboe::Result::ErrorDisconnected) { +void OboeEngine::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { + if (i_error == oboe::Result::ErrorDisconnected) { LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); if (!restartStream(0)) LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); - } - else - LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); + } else + LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); } @@ -748,8 +768,8 @@ void OboeEngine::onErrorAfterClose(AudioStream *audioStream, Result error) { * \brief Resets callback counters (called at the start of each iteration of onAudioReady */ void OboeEngine::resetCallbackCounters() { - framesProcessed = 0; - timeInfo = {0, 0, 0}; + m_framesProcessed = 0; + m_timeInfo = {0, 0, 0}; } @@ -761,22 +781,22 @@ void OboeEngine::resetCallbackCounters() { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(const void *buffer, int32_t framesToWrite) { - bool m_outcome = true; +bool OboeEngine::writeStream(const void *i_buffer, int32_t i_framesToWrite) { + bool outcome = true; - ResultWithValue m_result = outputStream->write(buffer, framesToWrite, TIMEOUT_NS); + ResultWithValue result = m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. - if (m_result.error() == Result::ErrorDisconnected) { + if (result.error() == Result::ErrorDisconnected) { if (OboeEngine::restartStream(1)) return true; } - if (!m_result) { - LOGE("[OboeEngine::writeStream]\t Error writing stream: %s", convertToText(m_result.error())); - m_outcome = false; + if (!result) { + LOGE("[OboeEngine::writeStream]\t Error writing stream: %s", convertToText(result.error())); + outcome = false; } - return m_outcome; + return outcome; } @@ -788,22 +808,22 @@ bool OboeEngine::writeStream(const void *buffer, int32_t framesToWrite) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { - bool m_outcome = true; +bool OboeEngine::readStream(void *i_buffer, int32_t i_framesToRead) { + bool outcome = true; - ResultWithValue m_result = inputStream->read(buffer, framesToRead, TIMEOUT_NS); + ResultWithValue result = m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. - if (m_result.error() == Result::ErrorDisconnected) { + if (result.error() == Result::ErrorDisconnected) { if (OboeEngine::restartStream(2)) return true; } - if (!m_result) { - LOGE("[OboeEngine::readStream]\t Error reading stream: %s", convertToText(m_result.error())); - m_outcome = false; + if (!result) { + LOGE("[OboeEngine::readStream]\t Error reading stream: %s", convertToText(result.error())); + outcome = false; } - return m_outcome; + return outcome; } @@ -811,51 +831,43 @@ bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { * \brief Allocates the memory of oboeStream. * @return the address of the oboeStream. */ -OboeStream* OboeEngine::initializeOboeStream() { - oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); +OboeStream *OboeEngine::allocateOboeStream() { + OboeStream *oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); + oboeStream->setEngineAddress(this); return oboeStream; } -/** - * \brief Sets the engineAddress parameter of oboeStream, useful for recalling the engine whenever - * it's needed. - * @param address the address of the only instance of OboeEngine. - */ -void OboeEngine::setEngineAddress(long address) { - oboeStream->engineAddress = address; -} - /** * \brief Converts PaSampleFormat values into Oboe::AudioFormat values. * @param paFormat the PaSampleFormat we want to convert. * @return the converted AudioFormat. */ -AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { - AudioFormat m_oboeFormat; - switch (paFormat) { +AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat i_paFormat) { + AudioFormat oboeFormat; + switch (i_paFormat) { case paFloat32: - m_oboeFormat = AudioFormat::Float; + oboeFormat = AudioFormat::Float; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); break; case paInt16: - m_oboeFormat = AudioFormat::I16; + oboeFormat = AudioFormat::I16; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); break; case paInt32: - m_oboeFormat = AudioFormat::I32; + oboeFormat = AudioFormat::I32; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); break; case paInt24: - m_oboeFormat = AudioFormat::I24; + oboeFormat = AudioFormat::I24; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); break; default: - m_oboeFormat = AudioFormat::Unspecified; - LOGW("[OboeEngine::PaToOboeFormat]\t Setting AudioFormat to Unspecified, because Oboe does not support the requested format."); + oboeFormat = AudioFormat::Unspecified; + LOGW("[OboeEngine::PaToOboeFormat]\t Setting AudioFormat to Unspecified, Oboe does not support the requested format."); break; } - return m_oboeFormat; + return oboeFormat; } @@ -865,8 +877,8 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { * @param direction the Oboe::Direction for which we want to know the device Id. * @return the device Id of the appropriate direction. */ -int32_t OboeEngine::getSelectedDevice(Direction direction) { - if (direction == Direction::Input) +int32_t OboeEngine::getSelectedDevice(Direction i_direction) { + if (i_direction == Direction::Input) return g_inputDeviceId; else return g_outputDeviceId; @@ -879,17 +891,16 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { * \brief Checks if the requested sample rate is supported by the output device using * OboeEngine::tryStream. * This function is used by PaOboe_Initialize, IsFormatSupported, and OpenStream. - * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of - * this file); + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of this file); * @param sampleRate is the sample rate we want to check. * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, - sampleRate, - kUnspecified))) - LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); +PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, + i_sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe."); /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the sample rate we requested wasn't supported. */ @@ -907,11 +918,11 @@ PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, do * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, - sampleRate, - kUnspecified))) - LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); +PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, + i_sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe."); /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the sample rate we requested wasn't supported. */ @@ -928,17 +939,15 @@ PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, dou * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsOutputChannelCountSupported( - PaOboeHostApiRepresentation *oboeHostApi, - int32_t numOfChannels) { - if (numOfChannels > 2 || numOfChannels == 0) { +static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { + if (i_numOfChannels > 2 || i_numOfChannels == 0) { LOGE("[PaOboe - IsOutputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, - kUnspecified, - numOfChannels))) + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, + kUnspecified, + i_numOfChannels))) LOGW("[PaOboe - IsOutputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -956,17 +965,15 @@ static PaError IsOutputChannelCountSupported( * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsInputChannelCountSupported( - PaOboeHostApiRepresentation *oboeHostApi, - int32_t numOfChannels) { - if (numOfChannels > 2 || numOfChannels == 0) { +static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { + if (i_numOfChannels > 2 || i_numOfChannels == 0) { LOGE("[PaOboe - IsInputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, - kUnspecified, - numOfChannels))) + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, + kUnspecified, + i_numOfChannels))) LOGW("[PaOboe - IsInputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -983,136 +990,133 @@ static PaError IsInputChannelCountSupported( * @param hostApiIndex is a PaHostApiIndex, the type used to enumerate the host APIs at runtime. * @return paNoError if no errors occur, or paInsufficientMemory if memory allocation fails; */ -PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) { - PaError m_result = paNoError; - int m_deviceCount; - PaOboeHostApiRepresentation *m_oboeHostApi; - PaDeviceInfo *m_deviceInfoArray; - char *m_deviceName; - - m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateZeroInitializedMemory( +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiIndex i_hostApiIndex) { + PaError result = paNoError; + int deviceCount; + PaOboeHostApiRepresentation *oboeHostApi; + PaDeviceInfo *deviceInfoArray; + char *deviceName; + + oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateZeroInitializedMemory( sizeof(PaOboeHostApiRepresentation)); - if (!m_oboeHostApi) { - m_result = paInsufficientMemory; + if (!oboeHostApi) { + result = paInsufficientMemory; goto error; } - m_oboeHostApi->oboeEngine = new OboeEngine(); + oboeHostApi->oboeEngine = new OboeEngine(); - m_oboeHostApi->allocations = PaUtil_CreateAllocationGroup(); - if (!m_oboeHostApi->allocations) { - m_result = paInsufficientMemory; + oboeHostApi->allocations = PaUtil_CreateAllocationGroup(); + if (!oboeHostApi->allocations) { + result = paInsufficientMemory; goto error; } - *hostApi = &m_oboeHostApi->inheritedHostApiRep; + *i_hostApi = &oboeHostApi->inheritedHostApiRep; // Initialization of infos. - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paInDevelopment; - (*hostApi)->info.name = "android Oboe"; - (*hostApi)->info.defaultOutputDevice = 0; - (*hostApi)->info.defaultInputDevice = 0; - (*hostApi)->info.deviceCount = 0; - - - m_deviceCount = 1; - (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( - m_oboeHostApi->allocations, sizeof(PaDeviceInfo *) * m_deviceCount); - - if (!(*hostApi)->deviceInfos) { - m_result = paInsufficientMemory; + (*i_hostApi)->info.structVersion = 1; + (*i_hostApi)->info.type = paInDevelopment; + (*i_hostApi)->info.name = "android Oboe"; + (*i_hostApi)->info.defaultOutputDevice = 0; + (*i_hostApi)->info.defaultInputDevice = 0; + (*i_hostApi)->info.deviceCount = 0; + + deviceCount = 1; + (*i_hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( + oboeHostApi->allocations, sizeof(PaDeviceInfo * ) * deviceCount); + + if (!(*i_hostApi)->deviceInfos) { + result = paInsufficientMemory; goto error; } /* allocate all device info structs in a contiguous block */ - m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateZeroInitializedMemory( - m_oboeHostApi->allocations, sizeof(PaDeviceInfo) * m_deviceCount); - if (!m_deviceInfoArray) { - m_result = paInsufficientMemory; + deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateZeroInitializedMemory( + oboeHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount); + if (!deviceInfoArray) { + result = paInsufficientMemory; goto error; } - for (int i = 0; i < m_deviceCount; ++i) { - PaDeviceInfo *m_deviceInfo = &m_deviceInfoArray[i]; - m_deviceInfo->structVersion = 2; - m_deviceInfo->hostApi = hostApiIndex; + for (int i = 0; i < deviceCount; ++i) { + PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; - /* OboeEngine will handle manual device selection through the use of - PaOboe_SetSelectedDevice via a JNI interface that can be implemented. + /* OboeEngine will handle manual device selection through the use of PaOboe_SetSelectedDevice. Portaudio doesn't need to know about this, so we just use a default device. */ - - m_deviceInfo->name = "default"; + deviceInfo->name = "default"; /* Try channels in order of preference - Stereo > Mono. */ - const int32_t m_channelsToTry[] = {2, 1}; - const int32_t m_channelsToTryLength = 2; + const int32_t channelsToTry[] = {2, 1}; + const int32_t channelsToTryLength = 2; - m_deviceInfo->maxOutputChannels = 0; - m_deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + deviceInfo->maxInputChannels = 0; - for (i = 0; i < m_channelsToTryLength; ++i) { - if (IsOutputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { - m_deviceInfo->maxOutputChannels = m_channelsToTry[i]; + for (i = 0; i < channelsToTryLength; ++i) { + if (IsOutputChannelCountSupported(oboeHostApi, channelsToTry[i]) == paNoError) { + deviceInfo->maxOutputChannels = channelsToTry[i]; break; } } - for (i = 0; i < m_channelsToTryLength; ++i) { - if (IsInputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { - m_deviceInfo->maxInputChannels = m_channelsToTry[i]; + for (i = 0; i < channelsToTryLength; ++i) { + if (IsInputChannelCountSupported(oboeHostApi, channelsToTry[i]) == paNoError) { + deviceInfo->maxInputChannels = channelsToTry[i]; break; } } /* check sample rates in order of preference */ - const int32_t m_sampleRates[] = {48000, 44100, 32000, 24000, 16000}; - const int32_t m_numberOfSampleRates = 5; + const int32_t sampleRates[] = {48000, 44100, 32000, 24000, 16000}; + const int32_t numberOfSampleRates = 5; - m_deviceInfo->defaultSampleRate = m_sampleRates[0]; + deviceInfo->defaultSampleRate = sampleRates[0]; - for (i = 0; i < m_numberOfSampleRates; ++i) { + for (i = 0; i < numberOfSampleRates; ++i) { if (IsOutputSampleRateSupported( - m_oboeHostApi, m_sampleRates[i]) == paNoError && + oboeHostApi, sampleRates[i]) == paNoError && IsInputSampleRateSupported( - m_oboeHostApi, m_sampleRates[i]) == paNoError) { - m_deviceInfo->defaultSampleRate = m_sampleRates[i]; + oboeHostApi, sampleRates[i]) == paNoError) { + deviceInfo->defaultSampleRate = sampleRates[i]; break; } } - if (m_deviceInfo->defaultSampleRate == 0) + if (deviceInfo->defaultSampleRate == 0) goto error; - /* If the user has set nativeBufferSize by querying the optimal buffer size via java, + /* If the user has set g_nativeBufferSize by querying the optimal buffer size via java, use the user-defined value since that will offer the lowest possible latency. */ - if (nativeBufferSize != 0) { - m_deviceInfo->defaultLowInputLatency = - (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultLowOutputLatency = - (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighInputLatency = - (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighOutputLatency = - (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; + if (g_nativeBufferSize != 0) { + deviceInfo->defaultLowInputLatency = + (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + deviceInfo->defaultLowOutputLatency = + (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighInputLatency = + (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighOutputLatency = + (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; } else { - m_deviceInfo->defaultLowInputLatency = - (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultLowOutputLatency = - (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighInputLatency = - (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighOutputLatency = - (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; + deviceInfo->defaultLowInputLatency = + (double) GetApproximateLowBufferSize() / deviceInfo->defaultSampleRate; + deviceInfo->defaultLowOutputLatency = + (double) GetApproximateLowBufferSize() / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighInputLatency = + (double) GetApproximateLowBufferSize() * 4 / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighOutputLatency = + (double) GetApproximateLowBufferSize() * 4 / deviceInfo->defaultSampleRate; } - (*hostApi)->deviceInfos[i] = m_deviceInfo; - ++(*hostApi)->info.deviceCount; + (*i_hostApi)->deviceInfos[i] = deviceInfo; + ++(*i_hostApi)->info.deviceCount; } - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; + (*i_hostApi)->Terminate = Terminate; + (*i_hostApi)->OpenStream = OpenStream; + (*i_hostApi)->IsFormatSupported = IsFormatSupported; - PaUtil_InitializeStreamInterface(&m_oboeHostApi->callbackStreamInterface, + PaUtil_InitializeStreamInterface(&oboeHostApi->callbackStreamInterface, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, @@ -1121,7 +1125,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable); - PaUtil_InitializeStreamInterface(&m_oboeHostApi->blockingStreamInterface, + PaUtil_InitializeStreamInterface(&oboeHostApi->blockingStreamInterface, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, @@ -1129,23 +1133,23 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable); - if (m_result == paNoError) + if (result == paNoError) LOGV("[PaOboe - Initialize]\t Oboe host API successfully initialized"); else - LOGE("[PaOboe - Initialize]\t An unusual error occurred. Error code: %d", m_result); - return m_result; + LOGE("[PaOboe - Initialize]\t An unusual error occurred. Error code: %d", result); + return result; error: - if (m_oboeHostApi) { - if (m_oboeHostApi->allocations) { - PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); - PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + if (oboeHostApi) { + if (oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(m_oboeHostApi); + PaUtil_FreeMemory(boeHostApi); } - LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", m_result); - return m_result; + LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", result); + return result; } @@ -1154,21 +1158,21 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the * interface to a host API (see struct in "pa_hostapi.h"). */ -static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { - auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; +static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { + auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; - if (!(m_oboeHostApi->oboeEngine->closeStream())) + if (!(oboeHostApi->oboeEngine->closeStream(nullptr))) LOGW("[PaOboe - Terminate]\t Couldn't close the streams correctly - see OboeEngine::CloseStream logs."); - if(m_oboeHostApi->oboeEngine != nullptr) - delete m_oboeHostApi->oboeEngine; + if (oboeHostApi->oboeEngine != nullptr) + delete oboeHostApi->oboeEngine; - if (m_oboeHostApi->allocations) { - PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); - PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + if (oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(m_oboeHostApi); + PaUtil_FreeMemory(oboeHostApi); } @@ -1182,116 +1186,103 @@ static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { * @return paNoError (== paFormatIsSupported) if no errors occur, otherwise returns an appropriate * PaError message. */ -static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate) { - PaError m_outcome; - int m_inputChannelCount, m_outputChannelCount; - PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; - auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; - - if (inputParameters) { - m_inputChannelCount = inputParameters->channelCount; - m_inputSampleFormat = inputParameters->sampleFormat; +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, + const PaStreamParameters *i_inputParameters, + const PaStreamParameters *i_outputParameters, + double i_sampleRate) { + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + + if (i_inputParameters) { + inputChannelCount = i_inputParameters->channelCount; + inputSampleFormat = i_inputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ - if (m_inputSampleFormat & paCustomFormat) { - m_outcome = paSampleFormatNotSupported; - return m_outcome; + if (inputSampleFormat & paCustomFormat) { + return paSampleFormatNotSupported; } /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) { - m_outcome = paInvalidDevice; - return m_outcome; + if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) { + return paInvalidDevice; } /* check that input device can support inputChannelCount */ - if (m_inputChannelCount > - hostApi->deviceInfos[inputParameters->device]->maxInputChannels) { - m_outcome = paInvalidChannelCount; - return m_outcome; + if (inputChannelCount > + i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { + return paInvalidChannelCount; } /* validate inputStreamInfo */ - if (inputParameters->hostApiSpecificStreamInfo) { + if (i_inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. - InputPreset m_androidRecordingPreset = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; - if (m_androidRecordingPreset != InputPreset::Generic && - m_androidRecordingPreset != InputPreset::Camcorder && - m_androidRecordingPreset != InputPreset::VoiceRecognition && - m_androidRecordingPreset != InputPreset::VoiceCommunication - m_androidRecordingPreset != InputPreset::VoicePerformance - ) { - m_outcome = paIncompatibleHostApiSpecificStreamInfo; - return m_outcome; + InputPreset androidRecordingPreset = + ((PaOboeStreamInfo *) i_inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (androidRecordingPreset != InputPreset::Generic && + androidRecordingPreset != InputPreset::Camcorder && + androidRecordingPreset != InputPreset::VoiceRecognition && + androidRecordingPreset != InputPreset::VoiceCommunication + androidRecordingPreset != InputPreset::VoicePerformance) { + return paIncompatibleHostApiSpecificStreamInfo; } } } else { - m_inputChannelCount = 0; + inputChannelCount = 0; } - if (outputParameters) { - m_outputChannelCount = outputParameters->channelCount; - m_outputSampleFormat = outputParameters->sampleFormat; + if (i_outputParameters) { + outputChannelCount = i_outputParameters->channelCount; + outputSampleFormat = i_outputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ - if (m_outputSampleFormat & paCustomFormat) { - m_outcome = paSampleFormatNotSupported; - return m_outcome; + if (outputSampleFormat & paCustomFormat) { + return paSampleFormatNotSupported; } /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) { - m_outcome = paInvalidDevice; - return m_outcome; + if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) { + return paInvalidDevice; } /* check that output device can support outputChannelCount */ - if (m_outputChannelCount > - hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) { - m_outcome = paInvalidChannelCount; - return m_outcome; + if (outputChannelCount > + i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { + return paInvalidChannelCount; } /* validate outputStreamInfo */ - if (outputParameters->hostApiSpecificStreamInfo) { + if (i_outputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. - Usage m_androidOutputUsage = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; - if (m_androidOutputUsage != Usage::Media && - m_androidOutputUsage != Usage::Notification && - m_androidOutputUsage != Usage::NotificationEvent && - m_androidOutputUsage != Usage::NotificationRingtone && - m_androidOutputUsage != Usage::VoiceCommunication && - m_androidOutputUsage != Usage::VoiceCommunicationSignalling && - m_androidOutputUsage != Usage::Alarm && - m_androidOutputUsage != Usage::Game - ) { - m_outcome = paIncompatibleHostApiSpecificStreamInfo; - return m_outcome; + Usage androidOutputUsage = + ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (androidOutputUsage != Usage::Media && + androidOutputUsage != Usage::Notification && + androidOutputUsage != Usage::NotificationEvent && + androidOutputUsage != Usage::NotificationRingtone && + androidOutputUsage != Usage::VoiceCommunication && + androidOutputUsage != Usage::VoiceCommunicationSignalling && + androidOutputUsage != Usage::Alarm && + androidOutputUsage != Usage::Game) { + return paIncompatibleHostApiSpecificStreamInfo; } } } else { - m_outputChannelCount = 0; + outputChannelCount = 0; } - if (m_outputChannelCount > 0) { - if (IsOutputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { - m_outcome = paInvalidSampleRate; - return m_outcome; + if (outputChannelCount > 0) { + if (IsOutputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + return paInvalidSampleRate; } } - if (m_inputChannelCount > 0) { - if (IsInputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { - m_outcome = paInvalidSampleRate; - return m_outcome; + if (inputChannelCount > 0) { + if (IsInputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + return paInvalidSampleRate; } } @@ -1310,14 +1301,14 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(PaOboeHostApiRepresentation *oboeHostApi, - Usage androidOutputUsage, double sampleRate) { - - return oboeHostApi->oboeEngine->openStream(Direction::Output, - sampleRate, - androidOutputUsage, - Generic //Won't be used, so we put the default value. - ); +static PaError InitializeOutputStream(PaOboeHostApiRepresentation *i_oboeHostApi, + Usage i_androidOutputUsage, double i_sampleRate) { + + return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + Direction::Output, + sampleRate, + androidOutputUsage, + Generic); //Input preset won't be used, so we put the default value. } @@ -1332,14 +1323,14 @@ static PaError InitializeOutputStream(PaOboeHostApiRepresentation *oboeHostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeInputStream(PaOboeHostApiRepresentation *oboeHostApi, - InputPreset androidInputPreset, double sampleRate) { - - return oboeHostApi->oboeEngine->openStream(Direction::Input, - sampleRate, - Usage::Media, //Won't be used, so we put the default value. - androidInputPreset - ); +static PaError InitializeInputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, + InputPreset i_androidInputPreset, double i_sampleRate) { + + return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + Direction::Input, + i_sampleRate, + Usage::Media, //Usage won't be used, so we put the default value. + i_androidInputPreset); } @@ -1360,203 +1351,200 @@ static PaError InitializeInputStream(PaOboeHostApiRepresentation *oboeHostApi, * manipulation or checks. * @return paNoError if no errors occur, or other error codes accordingly with what goes wrong. */ -static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, - PaStream **s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData) { - PaError m_error = paNoError; - auto m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; - unsigned long m_framesPerHostBuffer; /* these may not be equivalent for all implementations */ - int m_inputChannelCount, m_outputChannelCount; - PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; - PaSampleFormat m_hostInputSampleFormat, m_hostOutputSampleFormat; - - - //FIXME: add a function that lets the user choose usage and preset - Usage m_androidOutputUsage = Usage::VoiceCommunication; - InputPreset m_androidInputPreset = InputPreset::Generic; - - OboeStream *m_oboeStream = m_oboeHostApi->oboeEngine->initializeOboeStream(); - - if (!m_oboeStream) { - m_error = paInsufficientMemory; +static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, + PaStream **i_paStream, + const PaStreamParameters *i_inputParameters, + const PaStreamParameters *i_outputParameters, + double i_sampleRate, + unsigned long i_framesPerBuffer, + PaStreamFlags i_streamFlags, + PaStreamCallback *i_streamCallback, + void *i_userData) { + PaError error = paNoError; + auto oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + unsigned long framesPerHostBuffer; /* these may not be equivalent for all implementations */ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + + + //TODO: add a function that lets the user choose usage and preset + Usage androidOutputUsage = Usage::VoiceCommunication; + InputPreset androidInputPreset = InputPreset::Generic; + + OboeStream *oboeStream = oboeHostApi->oboeEngine->allocateOboeStream(); + + if (!oboeStream) { + error = paInsufficientMemory; goto error; } LOGI("[PaOboe - OpenStream]\t OpenStream called."); - if (inputParameters) { - m_inputChannelCount = inputParameters->channelCount; - m_inputSampleFormat = inputParameters->sampleFormat; + if (i_inputParameters) { + inputChannelCount = i_inputParameters->channelCount; + inputSampleFormat = i_inputParameters->sampleFormat; - /* Oboe supports alternate device specification with API>=28, but for now we reject the use of - paUseHostApiSpecificDeviceSpecification and stick with the default.*/ - if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) + /* Oboe supports alternate device specification with API>=28, but here we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via + PaOboe_SetSelectedDevice. */ + if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that input device can support inputChannelCount */ - if (m_inputChannelCount > hostApi->deviceInfos[inputParameters->device]->maxInputChannels) + if (inputChannelCount > i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) return paInvalidChannelCount; /* validate inputStreamInfo */ - if (inputParameters->hostApiSpecificStreamInfo) { + if (i_inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. - m_androidInputPreset = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; - if (m_androidInputPreset != InputPreset::Generic && - m_androidInputPreset != InputPreset::Camcorder && - m_androidInputPreset != InputPreset::VoiceRecognition && - m_androidInputPreset != InputPreset::VoiceCommunication - m_androidInputPreset != InputPreset::VoicePerformance - ) + androidInputPreset = + ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (androidInputPreset != InputPreset::Generic && + androidInputPreset != InputPreset::Camcorder && + androidInputPreset != InputPreset::VoiceRecognition && + androidInputPreset != InputPreset::VoiceCommunication + androidInputPreset != InputPreset::VoicePerformance) return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paFloat32" with whatever format you prefer - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ - m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paFloat32, m_inputSampleFormat); - m_oboeStream->inputFormat = m_hostInputSampleFormat; + hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( + paFloat32, inputSampleFormat); + oboeStream->inputFormat = hostInputSampleFormat; } else { - m_inputChannelCount = 0; - m_inputSampleFormat = m_hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ - m_oboeStream->inputFormat = m_hostInputSampleFormat; + inputChannelCount = 0; + inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ + oboeStream->inputFormat = hostInputSampleFormat; } - if (outputParameters) { - m_outputChannelCount = outputParameters->channelCount; - m_outputSampleFormat = outputParameters->sampleFormat; + if (i_outputParameters) { + outputChannelCount = i_outputParameters->channelCount; + outputSampleFormat = i_outputParameters->sampleFormat; - /* Oboe supports alternate device specification with API>=28, but for now we reject the use of - paUseHostApiSpecificDeviceSpecification and stick with the default.*/ - if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) + /* Oboe supports alternate device specification with API>=28, but here we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via + PaOboe_SetSelectedDevice. */ + if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that output device can support outputChannelCount */ - if (m_outputChannelCount > - hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) + if (outputChannelCount > + i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) return paInvalidChannelCount; /* validate outputStreamInfo */ - if (outputParameters->hostApiSpecificStreamInfo) { - m_androidOutputUsage = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; - if (m_androidOutputUsage != Usage::Media && - m_androidOutputUsage != Usage::Notification && - m_androidOutputUsage != Usage::NotificationEvent && - m_androidOutputUsage != Usage::NotificationRingtone && - m_androidOutputUsage != Usage::VoiceCommunication && - m_androidOutputUsage != Usage::VoiceCommunicationSignalling && - m_androidOutputUsage != Usage::Alarm - // See if more are needed. - ) + if (i_outputParameters->hostApiSpecificStreamInfo) { + androidOutputUsage = + ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (androidOutputUsage != Usage::Media && + androidOutputUsage != Usage::Notification && + androidOutputUsage != Usage::NotificationEvent && + androidOutputUsage != Usage::NotificationRingtone && + androidOutputUsage != Usage::VoiceCommunication && + androidOutputUsage != Usage::VoiceCommunicationSignalling && + androidOutputUsage != Usage::Alarm && + androidOutputUsage != Usage::Game) return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paFloat32" with whatever format you prefer - PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ - m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( + hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paFloat32, m_outputSampleFormat); - m_oboeStream->outputFormat = m_hostOutputSampleFormat; + oboeStream->outputFormat = hostOutputSampleFormat; } else { - m_outputChannelCount = 0; - m_outputSampleFormat = m_hostOutputSampleFormat = paFloat32; - m_oboeStream->outputFormat = m_hostOutputSampleFormat; + outputChannelCount = 0; + outputSampleFormat = hostOutputSampleFormat = paFloat32; + oboeStream->outputFormat = hostOutputSampleFormat; } /* validate platform specific flags */ - if ((streamFlags & paPlatformSpecificFlags) != 0) + if ((i_streamFlags & paPlatformSpecificFlags) != 0) return paInvalidFlag; /* unexpected platform specific flag */ - if (framesPerBuffer == paFramesPerBufferUnspecified) { - if (outputParameters) { - m_framesPerHostBuffer = - (unsigned long) (outputParameters->suggestedLatency * sampleRate); + if (i_framesPerBuffer == paFramesPerBufferUnspecified) { + if (i_outputParameters) { + framesPerHostBuffer = + (unsigned long) (i_outputParameters->suggestedLatency * i_sampleRate); } else { - m_framesPerHostBuffer = - (unsigned long) (inputParameters->suggestedLatency * sampleRate); + framesPerHostBuffer = + (unsigned long) (i_inputParameters->suggestedLatency * i_sampleRate); } } else { - m_framesPerHostBuffer = framesPerBuffer; + framesPerHostBuffer = i_framesPerBuffer; } - m_oboeHostApi->oboeEngine->setEngineAddress( - reinterpret_cast(m_oboeHostApi->oboeEngine)); - - if (streamCallback) { - PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), - &m_oboeHostApi->callbackStreamInterface, - streamCallback, userData); + if (i_streamCallback) { + PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), + &oboeHostApi->callbackStreamInterface, + i_streamCallback, i_userData); } else { - PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), - &m_oboeHostApi->blockingStreamInterface, - streamCallback, userData); + PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), + &oboeHostApi->blockingStreamInterface, + i_streamCallback, i_userData); } - PaUtil_InitializeCpuLoadMeasurer(&(m_oboeStream->cpuLoadMeasurer), sampleRate); - - m_error = PaUtil_InitializeBufferProcessor(&(m_oboeStream->bufferProcessor), - m_inputChannelCount, - m_inputSampleFormat, - m_hostInputSampleFormat, - m_outputChannelCount, - m_outputSampleFormat, - m_hostOutputSampleFormat, - sampleRate, streamFlags, - framesPerBuffer, - m_framesPerHostBuffer, - paUtilFixedHostBufferSize, - streamCallback, userData); - if (m_error != paNoError) + PaUtil_InitializeCpuLoadMeasurer(&(oboeStream->cpuLoadMeasurer), i_sampleRate); + + error = PaUtil_InitializeBufferProcessor(&(oboeStream->bufferProcessor), + inputChannelCount, + inputSampleFormat, + hostInputSampleFormat, + outputChannelCount, + outputSampleFormat, + hostOutputSampleFormat, + i_sampleRate, i_streamFlags, + i_framesPerBuffer, + framesPerHostBuffer, + paUtilFixedHostBufferSize, + i_streamCallback, i_userData); + if (error != paNoError) goto error; - m_oboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; - m_oboeStream->isBlocking = (streamCallback == nullptr); - m_oboeStream->framesPerHostCallback = m_framesPerHostBuffer; - m_oboeStream->bytesPerFrame = sizeof(int16_t); - m_oboeStream->cbFlags = 0; - m_oboeStream->isStopped = true; - m_oboeStream->isActive = false; + oboeStream->streamRepresentation.streamInfo.sampleRate = i_sampleRate; + oboeStream->isBlocking = (i_streamCallback == nullptr); + oboeStream->framesPerHostCallback = framesPerHostBuffer; + oboeStream->bytesPerFrame = sizeof(int16_t); + oboeStream->cbFlags = 0; + oboeStream->isStopped = true; + oboeStream->isActive = false; - if (!(m_oboeStream->isBlocking)) {} + if (!(oboeStream->isBlocking)) {} // PaUnixThreading_Initialize(); TODO: see if threading works with this version of PortAudio - if (m_inputChannelCount > 0) { - m_oboeStream->hasInput = true; - m_oboeStream->streamRepresentation.streamInfo.inputLatency = + if (inputChannelCount > 0) { + oboeStream->hasInput = true; + oboeStream->streamRepresentation.streamInfo.inputLatency = ((PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( - &(m_oboeStream->bufferProcessor)) + - m_oboeStream->framesPerHostCallback) / sampleRate; - ENSURE(InitializeInputStream(m_oboeHostApi, - m_androidInputPreset, sampleRate), + &(oboeStream->bufferProcessor)) + + oboeStream->framesPerHostCallback) / i_sampleRate; + ENSURE(InitializeInputStream(oboeStream, oboeHostApi, + androidInputPreset, i_sampleRate), "Initializing inputstream failed") - } else { m_oboeStream->hasInput = false; } + } else { oboeStream->hasInput = false; } - if (m_outputChannelCount > 0) { - m_oboeStream->hasOutput = true; - m_oboeStream->streamRepresentation.streamInfo.outputLatency = + if (outputChannelCount > 0) { + oboeStream->hasOutput = true; + oboeStream->streamRepresentation.streamInfo.outputLatency = ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( - &m_oboeStream->bufferProcessor) - + m_oboeStream->framesPerHostCallback) / sampleRate; - ENSURE(InitializeOutputStream(m_oboeHostApi, - m_androidOutputUsage, sampleRate), + &oboeStream->bufferProcessor) + + oboeStream->framesPerHostCallback) / i_sampleRate; + ENSURE(InitializeOutputStream(oboeHostApi, + androidOutputUsage, i_sampleRate), "Initializing outputstream failed"); - } else { m_oboeStream->hasOutput = false; } + } else { oboeStream->hasOutput = false; } - *s = (PaStream *) m_oboeStream; - return m_error; + *i_paStream = (PaStream *) oboeStream; + return error; error: - if (m_oboeStream) - PaUtil_FreeMemory(m_oboeStream); + if (oboeStream) + PaUtil_FreeMemory(oboeStream); - LOGE("[PaOboe - OpenStream]\t Error opening stream(s). Error code: %d", m_error); + LOGE("[PaOboe - OpenStream]\t Error opening stream(s). Error code: %d", error); - return m_error; + return error; } @@ -1568,29 +1556,29 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, * portaudio, which holds the information of our OboeStream. * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. */ -static PaError CloseStream(PaStream *s) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError CloseStream(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); - if (!(m_oboeEngine->closeStream())) - LOGW("[PaOboe - CloseStream]\t Couldn't close the stream(s) correctly - see OboeEngine::CloseStream logs."); + if (!(oboeEngine->closeStream(oboeStream))) + LOGW("[PaOboe - CloseStream]\t Some errors have occurred in closing oboe streams - see OboeEngine::CloseStream logs."); - PaUtil_TerminateBufferProcessor(&m_stream->bufferProcessor); - PaUtil_TerminateStreamRepresentation(&m_stream->streamRepresentation); + PaUtil_TerminateBufferProcessor(&oboeStream->bufferProcessor); + PaUtil_TerminateStreamRepresentation(&oboeStream->streamRepresentation); - for (int i = 0; i < numberOfBuffers; ++i) { - if (m_stream->hasOutput) - PaUtil_FreeMemory(m_stream->outputBuffers[i]); - if (m_stream->hasInput) - PaUtil_FreeMemory(m_stream->inputBuffers[i]); + for (int i = 0; i < g_numberOfBuffers; ++i) { + if (oboeStream->hasOutput) + PaUtil_FreeMemory(oboeStream->outputBuffers[i]); + if (oboeStream->hasInput) + PaUtil_FreeMemory(oboeStream->inputBuffers[i]); } - if (m_stream->hasOutput) - PaUtil_FreeMemory(m_stream->outputBuffers); - if (m_stream->hasInput) - PaUtil_FreeMemory(m_stream->inputBuffers); + if (oboeStream->hasOutput) + PaUtil_FreeMemory(oboeStream->outputBuffers); + if (oboeStream->hasInput) + PaUtil_FreeMemory(oboeStream->inputBuffers); - PaUtil_FreeMemory(m_stream); + PaUtil_FreeMemory(oboeStream); return paNoError; } @@ -1602,53 +1590,52 @@ static PaError CloseStream(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::startStream fails. */ -static PaError StartStream(PaStream *s) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError StartStream(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); - PaUtil_ResetBufferProcessor(&m_stream->bufferProcessor); + PaUtil_ResetBufferProcessor(&oboeStream->bufferProcessor); - //Checking if the stream(s) are already active. TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) - if (m_stream->isActive) { + //Checking if the stream(s) are already active. + //TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) + if (oboeStream->isActive) { LOGW("[PaOboe - StartStream]\t Stream was already active, stopping..."); - StopStream(s); + StopStream(i_paStream); LOGW("[PaOboe - StartStream]\t Restarting..."); - StartStream(s); + StartStream(i_paStream); } - m_stream->currentOutputBuffer = 0; - m_stream->currentInputBuffer = 0; + oboeStream->currentOutputBuffer = 0; + oboeStream->currentInputBuffer = 0; /* Initialize buffers */ - for (int i = 0; i < numberOfBuffers; ++i) { - if (m_stream->hasOutput) { - memset(m_stream->outputBuffers[m_stream->currentOutputBuffer], 0, - m_stream->framesPerHostCallback * m_stream->bytesPerFrame * - m_stream->bufferProcessor.outputChannelCount - ); - m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; + for (int i = 0; i < g_numberOfBuffers; ++i) { + if (oboeStream->hasOutput) { + memset(oboeStream->outputBuffers[oboeStream->currentOutputBuffer], 0, + oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.outputChannelCount); + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; } - if (m_stream->hasInput) { - memset(m_stream->inputBuffers[m_stream->currentInputBuffer], 0, - m_stream->framesPerHostCallback * m_stream->bytesPerFrame * - m_stream->bufferProcessor.inputChannelCount - ); - m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; + if (oboeStream->hasInput) { + memset(oboeStream->inputBuffers[oboeStream->currentInputBuffer], 0, + oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.inputChannelCount); + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; } } /* Start the processing thread.*/ - if (!m_stream->isBlocking) { - m_stream->callbackResult = paContinue; - m_stream->oboeCallbackResult = DataCallbackResult::Continue; + if (!oboeStream->isBlocking) { + oboeStream->callbackResult = paContinue; + oboeStream->oboeCallbackResult = DataCallbackResult::Continue; } - m_stream->isStopped = false; - m_stream->isActive = true; - m_stream->doStop = false; - m_stream->doAbort = false; + oboeStream->isStopped = false; + oboeStream->isActive = true; + oboeStream->doStop = false; + oboeStream->doAbort = false; - if (!(m_oboeEngine->startStream())) + if (!(oboeEngine->startStream(oboeStream))) return paUnanticipatedHostError; else return paNoError; @@ -1662,30 +1649,30 @@ static PaError StartStream(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::stopStream fails. */ -static PaError StopStream(PaStream *s) { - PaError m_error = paNoError; - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError StopStream(PaStream *i_paStream) { + PaError error = paNoError; + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); - if (m_stream->isStopped) { + if (oboeStream->isStopped) { LOGW("[PaOboe - StopStream]\t Stream was already stopped."); } else { - if (!(m_stream->isBlocking)) { - m_stream->doStop = true; + if (!(oboeStream->isBlocking)) { + oboeStream->doStop = true; } - if (!(m_oboeEngine->stopStream())) { + if (!(oboeEngine->stopStream(oboeStream))) { LOGE("[PaOboe - StopStream]\t Couldn't stop the stream(s) correctly - see OboeEngine::StopStream logs."); - m_error = paUnanticipatedHostError; + error = paUnanticipatedHostError; } - m_stream->isActive = false; - m_stream->isStopped = true; - if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) - m_stream->streamRepresentation.streamFinishedCallback( - m_stream->streamRepresentation.userData); + oboeStream->isActive = false; + oboeStream->isStopped = true; + if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) + oboeStream->streamRepresentation.streamFinishedCallback( + oboeStream->streamRepresentation.userData); } - return m_error; + return error; } @@ -1696,29 +1683,29 @@ static PaError StopStream(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::abortStream fails. */ -static PaError AbortStream(PaStream *s) { - PaError m_error = paNoError; - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError AbortStream(PaStream *i_paStream) { + PaError error = paNoError; + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); LOGI("[PaOboe - AbortStream]\t Aborting stream."); - if (!m_stream->isBlocking) { - m_stream->doAbort = true; + if (!oboeStream->isBlocking) { + oboeStream->doAbort = true; } /* stop immediately so enqueue has no effect */ - if (!(m_oboeEngine->abortStream())) { + if (!(oboeEngine->abortStream(oboeStream))) { LOGE("[PaOboe - AbortStream]\t Couldn't abort the stream - see OboeEngine::abortStream logs."); - m_error = paUnanticipatedHostError; + error = paUnanticipatedHostError; } - m_stream->isActive = false; - m_stream->isStopped = true; - if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) - m_stream->streamRepresentation.streamFinishedCallback( - m_stream->streamRepresentation.userData); + oboeStream->isActive = false; + oboeStream->isStopped = true; + if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) + oboeStream->streamRepresentation.streamFinishedCallback( + oboeStream->streamRepresentation.userData); - return m_error; + return error; } @@ -1730,26 +1717,25 @@ static PaError AbortStream(PaStream *s) { * @param frames is the total number of frames to read. * @return paInternalError if OboeEngine::readStream fails, paNoError otherwise. */ -static PaError ReadStream(PaStream *s, void *buffer, unsigned long frames) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); - void *m_userBuffer = buffer; - unsigned m_framesToRead; - PaError m_error = paNoError; - - while (frames > 0) { - m_framesToRead = PA_MIN(m_stream->framesPerHostCallback, frames); - - if (!(m_oboeEngine->readStream(m_userBuffer, - m_framesToRead * - m_stream->bufferProcessor.inputChannelCount))) - m_error = paInternalError; - - m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; - frames -= m_framesToRead; +static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_frames) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); + void *userBuffer = i_buffer; + unsigned framesToRead; + PaError error = paNoError; + + while (i_frames > 0) { + framesToRead = PA_MIN(oboeStream->framesPerHostCallback, i_frames); + + if (!(oboeEngine->readStream(userBuffer, framesToRead * + oboeStream->bufferProcessor.inputChannelCount))) + error = paInternalError; + + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; + i_frames -= framesToRead; } - return m_error; + return error; } @@ -1761,26 +1747,25 @@ static PaError ReadStream(PaStream *s, void *buffer, unsigned long frames) { * @param frames is the total number of frames to write. * @return paInternalError if OboeEngine::writeStream fails, paNoError otherwise. */ -static PaError WriteStream(PaStream *s, const void *buffer, unsigned long frames) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); - const void *m_userBuffer = buffer; - unsigned m_framesToWrite; - PaError m_error = paNoError; - - while (frames > 0) { - m_framesToWrite = PA_MIN(m_stream->framesPerHostCallback, frames); - - if (!(m_oboeEngine->writeStream(m_userBuffer, - m_framesToWrite * - m_stream->bufferProcessor.outputChannelCount))) - m_error = paInternalError; - - m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; - frames -= m_framesToWrite; +static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned long i_frames) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); + const void *userBuffer = i_buffer; + unsigned framesToWrite; + PaError error = paNoError; + + while (i_frames > 0) { + framesToWrite = PA_MIN(stream->framesPerHostCallback, i_frames); + + if (!(oboeEngine->writeStream(userBuffer, framesToWrite * + oboeStream->bufferProcessor.outputChannelCount))) + error = paInternalError; + + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; + i_frames -= framesToWrite; } - return m_error; + return error; } @@ -1792,9 +1777,9 @@ static PaError WriteStream(PaStream *s, const void *buffer, unsigned long frames * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be read without waiting. */ -static signed long GetStreamReadAvailable(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentInputBuffer); +static signed long GetStreamReadAvailable(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentInputBuffer); } @@ -1804,9 +1789,9 @@ static signed long GetStreamReadAvailable(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be written without waiting. */ -static signed long GetStreamWriteAvailable(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentOutputBuffer); +static signed long GetStreamWriteAvailable(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentOutputBuffer); } @@ -1816,9 +1801,9 @@ static signed long GetStreamWriteAvailable(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is stopped, or zero (0) when the stream is running. */ -static PaError IsStreamStopped(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->isStopped; +static PaError IsStreamStopped(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->isStopped; } @@ -1828,9 +1813,9 @@ static PaError IsStreamStopped(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is active (ie playing or recording audio), or zero (0) otherwise. */ -static PaError IsStreamActive(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->isActive; +static PaError IsStreamActive(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->isActive; } @@ -1840,7 +1825,7 @@ static PaError IsStreamActive(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return The stream's current time in seconds, or 0 if an error occurred. */ -static PaTime GetStreamTime(PaStream *s) { +static PaTime GetStreamTime(PaStream *i_paStream) { return PaUtil_GetTime(); } @@ -1856,9 +1841,9 @@ static PaTime GetStreamTime(PaStream *s) { * A value of 0.0 will always be returned for a blocking read/write stream, or if an error * occurs. */ -static double GetStreamCpuLoad(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return PaUtil_GetCpuLoad(&m_stream->cpuLoadMeasurer); +static double GetStreamCpuLoad(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return PaUtil_GetCpuLoad(&oboeStream->cpuLoadMeasurer); } @@ -1879,29 +1864,29 @@ static unsigned long GetApproximateLowBufferSize() { /*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ -void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { +void PaOboe_SetSelectedDevice(Direction i_direction, int32_t i_deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); - if (direction == Direction::Input) - g_inputDeviceId = deviceID; + if (i_direction == Direction::Input) + g_inputDeviceId = i_deviceID; else - g_outputDeviceId = deviceID; + g_outputDeviceId = i_deviceID; } -void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ - if(direction == Direction::Input) { - g_inputPerfMode = performanceMode; +void PaOboe_SetPerformanceMode(oboe::Direction i_direction, oboe::PerformanceMode i_performanceMode) { + if (i_direction == Direction::Input) { + g_inputPerfMode = i_performanceMode; } else { - g_outputPerfMode = performanceMode; + g_outputPerfMode = i_performanceMode; } } -void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { - nativeBufferSize = bufferSize; +void PaOboe_SetNativeBufferSize(unsigned long i_bufferSize) { + g_nativeBufferSize = i_bufferSize; } -void PaOboe_SetNumberOfBuffers(unsigned buffers) { - numberOfBuffers = buffers; +void PaOboe_SetNumberOfBuffers(unsigned i_numberOfBuffers) { + g_numberOfBuffers = i_numberOfBuffers; } From 49d8122494a2a01ab1a24d58b920ffaa4feb33dc Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 12:04:52 +0200 Subject: [PATCH 074/125] Added separate callback class to fix the single-stream issue. --- include/pa_oboe.h | 1 + src/hostapi/oboe/pa_oboe.cpp | 293 ++++++++++++++++++----------------- 2 files changed, 153 insertions(+), 141 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index d345617d7..900a1f19f 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -50,6 +50,7 @@ * @brief Android Oboe-specific PortAudio API extension header file. */ +#include "portaudio.h" #include "oboe/Oboe.h" #ifdef __cplusplus diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index fd70931a3..3f0de18d1 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -166,6 +166,7 @@ PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; class OboeEngine; +class OboeCallback; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -190,6 +191,7 @@ typedef struct OboeStream { PaSampleFormat inputFormat; PaSampleFormat outputFormat; + OboeCallback *oboeCallback; // Buffers are managed by the callback function in Oboe. void **outputBuffers; int currentOutputBuffer; @@ -207,9 +209,33 @@ typedef struct OboeStream { OboeEngine *oboeEngineAddress; } OboeStream; +/** + * Callback class for OboeStream. Will be used for non-blocking streams. + */ +class OboeCallback: public AudioStreamCallback { +public: + OboeCallback(){ m_oboeStreamHolder = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); } + //Callback function for non-blocking streams + DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, + int32_t numFrames) override; + + void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + + void setStreamHolder(OboeStream* oboeStream){ m_oboeStreamHolder = oboeStream; } + + void resetCallbackCounters(); + +private: + //callback utils + OboeStream *m_oboeStreamHolder; + unsigned long m_framesProcessed{}; + PaStreamCallbackTimeInfo m_timeInfo{}; + struct timespec m_timeSpec{}; +}; + /** - * Stream engine of the host API - Oboe. We allocate only one instance of the engine, and + * Stream engine of the host API - Oboe. We allocate only one instance of the engine per PaOboe_Initialize call, and * we call its functions when we want to operate directly on Oboe. More infos on each functions are * provided right before their implementations. */ @@ -233,14 +259,6 @@ class OboeEngine : public AudioStreamCallback { bool abortStream(OboeStream *oboeStream); - //Callback function for non-blocking streams and some callback utils - DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, - int32_t numFrames) override; - - void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; - - void resetCallbackCounters(); - //Blocking read/write functions bool writeStream(const void *buffer, int32_t framesToWrite); @@ -250,19 +268,12 @@ class OboeEngine : public AudioStreamCallback { OboeStream *allocateOboeStream(); private: - OboeStream *m_oboeStreamHolder; - //The only instances of output and input streams that will be used, and their builders std::shared_ptr m_outputStream; AudioStreamBuilder m_outputBuilder; std::shared_ptr m_inputStream; AudioStreamBuilder m_inputBuilder; - //callback utils - unsigned long m_framesProcessed{}; - PaStreamCallbackTimeInfo m_timeInfo{}; - struct timespec m_timeSpec{}; - //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -291,14 +302,12 @@ typedef struct PaOboeHostApiRepresentation { /** * \brief Initializes an instance of the engine. */ -OboeEngine::OboeEngine() { - m_oboeStreamHolder = allocateOboeStream(); -} +OboeEngine::OboeEngine() {} /** - * \brief Tries to open a stream with the direction @direction, sample rate @sampleRate and/or - * channel count @channelCount. It then checks if the stream was in fact opened with the + * \brief Tries to open a stream with the direction i_direction, sample rate i_sampleRate and/or + * channel count i_channelCount. It then checks if the stream was in fact opened with the * desired settings, and then closes the stream. It's used to see if the requested * parameters are supported by the devices that are going to be used. * @param direction the Direction of the stream; @@ -357,7 +366,7 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t /** - * \brief Opens an audio stream of oboeStream with a specific direction, sample rate and, + * \brief Opens an audio stream with a specific direction, sample rate and, * depending on the direction of the stream, sets its usage (if * direction == Ditrction::Output) or its preset (if direction == Direction::Input). * Moreover, this function checks if the stream is blocking, and sets its callback @@ -376,6 +385,12 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, PaError error = paNoError; Result result; + if(!(i_oboeStream->isBlocking)){ + i_oboeStream->oboeCallback = new OboeCallback(); + i_oboeStream->oboeCallback->setStreamHolder(i_oboeStream); + i_oboeStream->oboeCallback->resetCallbackCounters(); + } + if (i_direction == Direction::Input) { m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) @@ -387,9 +402,8 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - resetCallbackCounters(); - m_inputBuilder.setDataCallback(this) - ->setErrorCallback(this); + m_inputBuilder.setDataCallback(i_oboeStream->oboeCallback) + ->setErrorCallback(i_oboeStream->oboeCallback); } result = m_inputBuilder.openStream(m_inputStream); @@ -431,9 +445,8 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - resetCallbackCounters(); - m_outputBuilder.setDataCallback(this) - ->setErrorCallback(this); + m_outputBuilder.setDataCallback(i_oboeStream->oboeCallback) + ->setErrorCallback(i_oboeStream->oboeCallback); } result = m_outputBuilder.openStream(m_outputStream); @@ -663,116 +676,6 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { } -/** - * \brief Oboe's callback routine. FIXME: implement onErrorAfterClose correctly - */ -DataCallbackResult -OboeEngine::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { - - clock_gettime(CLOCK_REALTIME, &m_timeSpec); - m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); - m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback - / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); - m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback - / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); - - /* check if StopStream or AbortStream was called */ - if (m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->callbackResult = paComplete; - } else if (m_oboeStreamHolder->doAbort) { - m_oboeStreamHolder->callbackResult = paAbort; - } - - PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, - &m_timeInfo, m_oboeStreamHolder->cbFlags); - - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; - PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); - PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ - m_oboeStreamHolder->currentOutputBuffer], - 0); - } - if (m_oboeStreamHolder->hasInput) { - i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; - PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ - m_oboeStreamHolder->currentInputBuffer], - 0); - } - - /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ - if (m_oboeStreamHolder->callbackResult == paContinue - || (m_oboeStreamHolder->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, - &oboeStream->callbackResult); - } - - /* enqueue a buffer only when there are frames to be processed, - * this will be 0 when paComplete + empty buffers or paAbort - */ - if (m_framesProcessed > 0) { - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->currentOutputBuffer = - (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; - } - if (m_oboeStreamHolder->hasInput) { - m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; - } - } - - PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); - - /* StopStream was called */ - if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; - } - - /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { - m_oboeStreamHolder->isActive = false; - m_oboeStreamHolder->isStopped = true; - if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) - m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( - m_oboeStreamHolder->streamRepresentation.userData); - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) - } - - return m_oboeStreamHolder->oboeCallbackResult; -} - - -/** - * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells - * what error occurred. - */ -void OboeEngine::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { - if (i_error == oboe::Result::ErrorDisconnected) { - LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); - if (!restartStream(0)) - LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); - } else - LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); -} - - -/** - * \brief Resets callback counters (called at the start of each iteration of onAudioReady - */ -void OboeEngine::resetCallbackCounters() { - m_framesProcessed = 0; - m_timeInfo = {0, 0, 0}; -} - - /** * \brief Writes frames on the output stream of oboeStream. Used by blocking streams. * @param buffer The buffer that we want to write on the output stream; @@ -884,6 +787,117 @@ int32_t OboeEngine::getSelectedDevice(Direction i_direction) { return g_outputDeviceId; } +/*----------------------------- OboeCallback functions implementations -----------------------------*/ +/** + * \brief Oboe's callback routine. + */ +DataCallbackResult +OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { + + clock_gettime(CLOCK_REALTIME, &m_timeSpec); + m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); + m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + / + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); + m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + / + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); + + /* check if StopStream or AbortStream was called */ + if (m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->callbackResult = paComplete; + } else if (m_oboeStreamHolder->doAbort) { + m_oboeStreamHolder->callbackResult = paAbort; + } + + PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, + &m_timeInfo, m_oboeStreamHolder->cbFlags); + + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; + PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); + PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ + m_oboeStreamHolder->currentOutputBuffer], + 0); + } + if (m_oboeStreamHolder->hasInput) { + i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; + PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ + m_oboeStreamHolder->currentInputBuffer], + 0); + } + + /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ + if (m_oboeStreamHolder->callbackResult == paContinue + || (m_oboeStreamHolder->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { + m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, + &oboeStream->callbackResult); + } + + /* enqueue a buffer only when there are frames to be processed, + * this will be 0 when paComplete + empty buffers or paAbort + */ + if (m_framesProcessed > 0) { + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->currentOutputBuffer = + (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; + } + if (m_oboeStreamHolder->hasInput) { + m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; + } + } + + PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); + + /* StopStream was called */ + if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; + } + + /* if AbortStream or StopStream weren't called, stop from the cb */ + else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { + m_oboeStreamHolder->isActive = false; + m_oboeStreamHolder->isStopped = true; + if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) + m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( + m_oboeStreamHolder->streamRepresentation.userData); + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) + } + + return m_oboeStreamHolder->oboeCallbackResult; +} + + +/** + * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells + * what error occurred. + */ +void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { + if (i_error == oboe::Result::ErrorDisconnected) { + OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); + LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + if (!oboeEngine->restartStream(0)) + LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); + } else + LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); +} + + +/** + * \brief Resets callback counters (called at the start of each iteration of onAudioReady + */ +void OboeCallback::resetCallbackCounters() { + m_framesProcessed = 0; + m_timeInfo = {0, 0, 0}; +} + /*----------------------------- PaSkeleton functions implementations -----------------------------*/ @@ -1367,8 +1381,6 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, PaSampleFormat inputSampleFormat, outputSampleFormat; PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - - //TODO: add a function that lets the user choose usage and preset Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; @@ -1624,7 +1636,6 @@ static PaError StartStream(PaStream *i_paStream) { } } - /* Start the processing thread.*/ if (!oboeStream->isBlocking) { oboeStream->callbackResult = paContinue; oboeStream->oboeCallbackResult = DataCallbackResult::Continue; From 8035e7ea79317d0c2eaec68bdf3c3e88f370d124 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 12:51:09 +0200 Subject: [PATCH 075/125] Modified OboeEngine accordingly --- src/hostapi/oboe/pa_oboe.cpp | 140 +++++++++++++++++------------------ 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 3f0de18d1..853b7c8e5 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -205,6 +205,12 @@ typedef struct OboeStream { void setEngineAddress(OboeEngine *i_oboeEngine) { oboeEngineAddress = i_oboeEngine; } + //The only instances of output and input streams that will be used, and their builders + std::shared_ptr outputStream; + AudioStreamBuilder outputBuilder; + std::shared_ptr inputStream; + AudioStreamBuilder inputBuilder; + private: OboeEngine *oboeEngineAddress; } OboeStream; @@ -235,11 +241,11 @@ class OboeCallback: public AudioStreamCallback { /** - * Stream engine of the host API - Oboe. We allocate only one instance of the engine per PaOboe_Initialize call, and - * we call its functions when we want to operate directly on Oboe. More infos on each functions are - * provided right before their implementations. + * Stream engine of the host API - Oboe. We allocate only one instance of the engine per OboeStream, and + * we call its functions when we want to operate directly on Oboe. More infos on each function are + * provided right before its implementation. */ -class OboeEngine : public AudioStreamCallback { +class OboeEngine { public: OboeEngine(); @@ -253,26 +259,23 @@ class OboeEngine : public AudioStreamCallback { bool stopStream(OboeStream *oboeStream); - bool restartStream(int direction); + bool restartStream(OboeStream *oboeStream, int direction); bool closeStream(OboeStream *oboeStream); bool abortStream(OboeStream *oboeStream); //Blocking read/write functions - bool writeStream(const void *buffer, int32_t framesToWrite); + bool writeStream(OboeStream *oboeStream, const void *buffer, int32_t framesToWrite); - bool readStream(void *buffer, int32_t framesToRead); + bool readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead); //Engine utils OboeStream *allocateOboeStream(); private: - //The only instances of output and input streams that will be used, and their builders - std::shared_ptr m_outputStream; - AudioStreamBuilder m_outputBuilder; - std::shared_ptr m_inputStream; - AudioStreamBuilder m_inputBuilder; + std::shared_ptr m_testStream; + AudioStreamBuilder m_testBuilder; //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -300,7 +303,7 @@ typedef struct PaOboeHostApiRepresentation { /** - * \brief Initializes an instance of the engine. + * \brief Constructor. */ OboeEngine::OboeEngine() {} @@ -317,21 +320,16 @@ OboeEngine::OboeEngine() {} * they aren't, or if tryStream couldn't open a stream. */ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t i_channelCount) { - AudioStreamBuilder builder; Result result; bool outcome = false; - builder.setDeviceId(getSelectedDevice(i_direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + m_testBuilder.setDeviceId(getSelectedDevice(i_direction)) + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) ->setDirection(i_direction) ->setSampleRate(i_sampleRate) - ->setChannelCount(i_channelCount); - if (i_direction == Direction::Input) { - result = builder->openStream(m_inputStream); - } else { - result = builder->openStream(m_outputStream); - } + ->setChannelCount(i_channelCount) + ->openStream(m_testStream); if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", @@ -340,26 +338,23 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t } if (i_sampleRate != kUnspecified) { - outcome = (i_sampleRate == builder.getSampleRate()); + outcome = (i_sampleRate == m_testBuilder.getSampleRate()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - i_sampleRate, builder.getSampleRate()); + i_sampleRate, m_testBuilder.getSampleRate()); } } else if (i_channelCount != kUnspecified) { - outcome = (i_channelCount == builder.getChannelCount()); + outcome = (i_channelCount == m_testBuilder.getChannelCount()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, builder.getChannelCount()); + i_channelCount, m_testBuilder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); outcome = false; } - if (i_direction == Direction::Input) - m_inputStream->close(); - else - m_outputStream->close(); + m_testStream->close(); return outcome; } @@ -371,6 +366,7 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t * direction == Ditrction::Output) or its preset (if direction == Direction::Input). * Moreover, this function checks if the stream is blocking, and sets its callback * function if not. + * @param oboeStream The stream we want to open * @param direction The Oboe::Direction of the stream we want to open; * @param sampleRate The sample rate of the stream we want to open; * @param androidOutputUsage The Oboe::Usage of the output stream we want to open @@ -392,7 +388,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, } if (i_direction == Direction::Input) { - m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) + i_oboeStream->inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Input) @@ -406,7 +402,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setErrorCallback(i_oboeStream->oboeCallback); } - result = m_inputBuilder.openStream(m_inputStream); + result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", @@ -414,7 +410,8 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, return paUnanticipatedHostError; } - m_inputStream->setBufferSizeInFrames(m_inputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->inputStream->setBufferSizeInFrames(i_oboeStream->inputStream->getFramesPerBurst() * + g_numberOfBuffers); i_oboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -428,14 +425,14 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->inputBuffers); - m_inputStream->close(); + i_oboeStream->inputStream->close(); error = paInsufficientMemory; break; } } i_oboeStream->currentInputBuffer = 0; } else { - m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) + i_oboeStream->outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Output) @@ -445,18 +442,19 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - m_outputBuilder.setDataCallback(i_oboeStream->oboeCallback) + i_oboeStream->outputBuilder.setDataCallback(i_oboeStream->oboeCallback) ->setErrorCallback(i_oboeStream->oboeCallback); } - result = m_outputBuilder.openStream(m_outputStream); + result = i_oboeStream->outputBuilder.openStream(m_outputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); return paUnanticipatedHostError; } - m_outputStream->setBufferSizeInFrames(m_outputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->outputStream->setBufferSizeInFrames(i_oboeStream->outputStream->getFramesPerBurst() * + g_numberOfBuffers); i_oboeStream->outputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -470,7 +468,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->outputBuffers); - m_outputStream->close(); + i_oboeStream->outputStream->close(); error = paInsufficientMemory; break; } @@ -491,13 +489,13 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; if (i_oboeStream->hasInput) { - inputResult = m_inputStream->requestStart(); + inputResult = i_oboeStream->inputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = m_outputStream->requestStart(); + outputResult = i_oboeStream->outputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", convertToText(outputResult)); @@ -516,13 +514,13 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; if (i_oboeStream->hasInput) { - inputResult = m_inputStream->requestStop(); + inputResult = i_oboeStream->inputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = m_outputStream->requestStop(); + outputResult = i_oboeStream->outputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", convertToText(outputResult)); @@ -537,28 +535,28 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { * audio devices change while a stream is started. * @return true if the stream is restarted successfully, false otherwise. */ -bool OboeEngine::restartStream(int i_direction) { +bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { bool outcome = true; Result result; switch (i_direction) { case 1: //output-only //stopping and closing - result = m_outputStream->stop(); + result = i_oboeStream->outputStream->stop(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", convertToText(result)); - result = m_outputStream->close(); + result = i_oboeStream->outputStream->close(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", convertToText(result)); //reopening and restarting - result = outputBuilder.openStream(m_outputStream); + result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", convertToText(result)); - result = m_outputStream->start(); + result = i_oboeStream->outputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", convertToText(result)); @@ -568,21 +566,21 @@ bool OboeEngine::restartStream(int i_direction) { case 2: //input-only //stopping and closing - result = m_inputStream->stop(); + result = i_oboeStream->inputStream->stop(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", convertToText(result)); - result = m_inputStream->close(); + result = i_oboeStream->inputStream->close(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", convertToText(result)); //reopening and restarting - result = inputBuilder.openStream(m_inputStream); + result = inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); - result = m_inputStream->start(); + result = i_oboeStream->inputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", convertToText(result)); @@ -593,7 +591,7 @@ bool OboeEngine::restartStream(int i_direction) { default: // unspecified direction or both directions: restart both streams LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); - outcome = (restartStream(1) && restartStream(2)); + outcome = (restartStream(i_oboeStream, 1) && restartStream(i_oboeStream, 2)); break; } @@ -618,14 +616,14 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { } if (hasOutput) { - outputResult = m_outputStream->close(); + outputResult = i_oboeStream->outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } if (hasInput) { - inputResult = m_inputStream->close(); + inputResult = i_oboeStream->inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); @@ -652,21 +650,21 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { } if (hasInput) { - inputResult = m_inputStream->stop(); + inputResult = i_oboeStream->inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); - inputResult = m_inputStream->close(); - if (m_inputResult != Result::OK) + inputResult = i_oboeStream->inputStream->close(); + if (i_oboeStream->inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (hasOutput) { - outputResult = m_outputStream->stop(); - if (m_outputResult != Result::OK) + outputResult = i_oboeStream->outputStream->stop(); + if (i_oboeStream->outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", - convertToText(m_outputResult)); - outputResult = m_outputStream->close(); + convertToText(outputResult)); + outputResult = i_oboeStream->outputStream->close(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", convertToText(outputResult)); @@ -684,14 +682,14 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(const void *i_buffer, int32_t i_framesToWrite) { +bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int32_t i_framesToWrite) { bool outcome = true; - ResultWithValue result = m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); + ResultWithValue result = i_oboeStream->outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (OboeEngine::restartStream(1)) + if (restartStream(i_oboeStream, 1)) return true; } @@ -711,14 +709,14 @@ bool OboeEngine::writeStream(const void *i_buffer, int32_t i_framesToWrite) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::readStream(void *i_buffer, int32_t i_framesToRead) { +bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_framesToRead) { bool outcome = true; - ResultWithValue result = m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); + ResultWithValue result = i_oboeStream->inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (OboeEngine::restartStream(2)) + if (restartStream(i_oboeStream, 2)) return true; } @@ -837,8 +835,8 @@ OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ if (m_oboeStreamHolder->callbackResult == paContinue || (m_oboeStreamHolder->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, - &oboeStream->callbackResult); + m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeStreamHolder->bufferProcessor, + &m_oboeStreamHolder->callbackResult); } /* enqueue a buffer only when there are frames to be processed, @@ -883,7 +881,7 @@ void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) if (i_error == oboe::Result::ErrorDisconnected) { OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); - if (!oboeEngine->restartStream(0)) + if (!oboeEngine->restartStream(m_oboeStreamHolder, 0)) LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); } else LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); From 494600d34e32a3a30372ee8c0233b150c7232f14 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 16:04:03 +0200 Subject: [PATCH 076/125] Adjusted the code in the rest of pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 853b7c8e5..0929033fa 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -880,11 +880,11 @@ OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { if (i_error == oboe::Result::ErrorDisconnected) { OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); - LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + LOGW("[OboeCallback::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); if (!oboeEngine->restartStream(m_oboeStreamHolder, 0)) - LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); + LOGE("[OboeCallback::onErrorAfterClose]\t Couldn't restart stream(s)"); } else - LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); + LOGE("[OboeCallback::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); } @@ -1086,10 +1086,8 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde deviceInfo->defaultSampleRate = sampleRates[0]; for (i = 0; i < numberOfSampleRates; ++i) { - if (IsOutputSampleRateSupported( - oboeHostApi, sampleRates[i]) == paNoError && - IsInputSampleRateSupported( - oboeHostApi, sampleRates[i]) == paNoError) { + if (IsOutputSampleRateSupported(oboeHostApi, sampleRates[i]) == paNoError && + IsInputSampleRateSupported(oboeHostApi, sampleRates[i]) == paNoError) { deviceInfo->defaultSampleRate = sampleRates[i]; break; } @@ -1224,7 +1222,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /* check that input device can support inputChannelCount */ if (inputChannelCount > - i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { + i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { return paInvalidChannelCount; } @@ -1262,8 +1260,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, } /* check that output device can support outputChannelCount */ - if (outputChannelCount > - i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { + if (outputChannelCount > i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { return paInvalidChannelCount; } @@ -1304,6 +1301,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /** * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeStream is the OboeStream we want to initialize in the output direction. * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of * this file); * @param androidOutputUsage is an attribute that expresses why we are opening the output stream. @@ -1313,7 +1311,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(PaOboeHostApiRepresentation *i_oboeHostApi, +static PaError InitializeOutputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, Usage i_androidOutputUsage, double i_sampleRate) { return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, @@ -1326,6 +1324,7 @@ static PaError InitializeOutputStream(PaOboeHostApiRepresentation *i_oboeHostApi /** * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeStream is the OboeStream we want to initialize in the input direction. * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of * this file); * @param androidInputPreset is an attribute that defines the audio source. This information @@ -1350,7 +1349,7 @@ static PaError InitializeInputStream(OboeStream i_oboeStream, PaOboeHostApiRepre * \brief Opens the portaudio audio stream - while initializing our OboeStream. * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the * interface to a host API (see struct in "pa_hostapi.h"); - * @param s points to a pointer to a PaStream, which is an audio stream structure used and built + * @param paStream points to a pointer to a PaStream, which is an audio stream structure used and built * by portaudio, which will hold the information of our OboeStream; * @param inputParameters points towards the parameters given to the input stream; * @param outputParameters points towards the parameters given to the output stream; @@ -1540,7 +1539,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( &oboeStream->bufferProcessor) + oboeStream->framesPerHostCallback) / i_sampleRate; - ENSURE(InitializeOutputStream(oboeHostApi, + ENSURE(InitializeOutputStream(oboeStream, oboeHostApi, androidOutputUsage, i_sampleRate), "Initializing outputstream failed"); } else { oboeStream->hasOutput = false; } @@ -1562,7 +1561,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, * \brief Calls OboeEngine::closeStream, and then frees the memory that was allocated to sustain * the stream(s). When CloseStream() is called, the multi-api layer ensures that the stream * has already been stopped or aborted. - * @param s points to to a PaStream, which is an audio stream structure used and built by + * @param paStream points to to a PaStream, which is an audio stream structure used and built by * portaudio, which holds the information of our OboeStream. * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. */ @@ -1736,7 +1735,7 @@ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_ while (i_frames > 0) { framesToRead = PA_MIN(oboeStream->framesPerHostCallback, i_frames); - if (!(oboeEngine->readStream(userBuffer, framesToRead * + if (!(oboeEngine->readStream(oboeStream, userBuffer, framesToRead * oboeStream->bufferProcessor.inputChannelCount))) error = paInternalError; @@ -1766,7 +1765,7 @@ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned while (i_frames > 0) { framesToWrite = PA_MIN(stream->framesPerHostCallback, i_frames); - if (!(oboeEngine->writeStream(userBuffer, framesToWrite * + if (!(oboeEngine->writeStream(oboeStream, userBuffer, framesToWrite * oboeStream->bufferProcessor.outputChannelCount))) error = paInternalError; From f389437633bf5b092ff4c218cea702ccff92f162 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 16:17:09 +0200 Subject: [PATCH 077/125] Removed stop and close phases of OboeEngine::restartStream --- src/hostapi/oboe/pa_oboe.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 0929033fa..d0d2c3d78 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -541,17 +541,6 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { switch (i_direction) { case 1: //output-only - //stopping and closing - result = i_oboeStream->outputStream->stop(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", - convertToText(result)); - result = i_oboeStream->outputStream->close(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", - convertToText(result)); - - //reopening and restarting result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", @@ -565,17 +554,6 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { break; case 2: //input-only - //stopping and closing - result = i_oboeStream->inputStream->stop(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", - convertToText(result)); - result = i_oboeStream->inputStream->close(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", - convertToText(result)); - - //reopening and restarting result = inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", From e62621461de76f7946af457f6528388ce94e6858 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 09:42:01 +0200 Subject: [PATCH 078/125] Updated functions' description --- src/hostapi/oboe/pa_oboe.cpp | 43 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index d0d2c3d78..f500f5995 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -81,7 +81,6 @@ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,MODULE_NAME, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,MODULE_NAME, __VA_ARGS__) -// Copied from @{pa_opensles.c}. #define ENSURE(expr, errorText) \ do \ { \ @@ -212,7 +211,7 @@ typedef struct OboeStream { AudioStreamBuilder inputBuilder; private: - OboeEngine *oboeEngineAddress; + OboeEngine *oboeEngineAddress = nullptr; } OboeStream; /** @@ -242,7 +241,7 @@ class OboeCallback: public AudioStreamCallback { /** * Stream engine of the host API - Oboe. We allocate only one instance of the engine per OboeStream, and - * we call its functions when we want to operate directly on Oboe. More infos on each function are + * we call its functions when we want to operate directly on Oboe. More information on each function is * provided right before its implementation. */ class OboeEngine { @@ -363,7 +362,7 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t /** * \brief Opens an audio stream with a specific direction, sample rate and, * depending on the direction of the stream, sets its usage (if - * direction == Ditrction::Output) or its preset (if direction == Direction::Input). + * direction == Direction::Output) or its preset (if direction == Direction::Input). * Moreover, this function checks if the stream is blocking, and sets its callback * function if not. * @param oboeStream The stream we want to open @@ -481,8 +480,9 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, /** - * \brief Starts oboeStream - both input and output audiostreams are checked + * \brief Starts oboeStream - both input and output AudioStreams of the OboeStream are checked * and requested to be started. + * @param oboeStream The stream we want to start. * @return true if the streams we wanted to start are started successfully, false otherwise. */ bool OboeEngine::startStream(OboeStream *i_oboeStream) { @@ -506,8 +506,9 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { /** - * \brief Stops oboeStream - both input and output audiostreams are checked + * \brief Stops oboeStream - both input and output AudioStreams of the OboeStream are checked * and requested to be stopped. + * @param oboeStream The stream we want to stop. * @return true if the streams we wanted to stop are stopped successfully, false otherwise. */ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { @@ -531,8 +532,11 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { /** - * \brief Called when it's needed to restart the oboeStream's audio stream(s), mainly when the - * audio devices change while a stream is started. + * \brief Called when it's needed to restart the OboeStream's audio stream(s) when the audio device(s) change + * while a stream is started. Oboe will stop and close said streams in that case, + * so this function just reopens and restarts them. + * @param oboeStream The stream we want to restart. + * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 0 for both). * @return true if the stream is restarted successfully, false otherwise. */ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { @@ -578,8 +582,9 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { /** - * \brief Closes oboeStream - both input and output audiostreams are checked + * \brief Closes oboeStream - both input and output AudioStreams of the OboeStream are checked * and closed if active. + * @param oboeStream The stream we want to close. * @return true if the stream is closed successfully, otherwise returns false. */ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { @@ -613,7 +618,8 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { /** - * \brief Stops oboeStream - both input and output audiostreams are checked and forcefully stopped. + * \brief Stops oboeStream - both input and output AudioStreams of the OboeStream are checked and forcefully stopped. + * @param oboeStream The stream we want to abort. * @return true if the output stream and the input stream are stopped successfully, false otherwise. */ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { @@ -654,6 +660,7 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { /** * \brief Writes frames on the output stream of oboeStream. Used by blocking streams. + * @param oboeStream The stream we want to write onto. * @param buffer The buffer that we want to write on the output stream; * @param framesToWrite The number of frames that we want to write. * @return true if the buffer is written correctly, false if the write function returns an error @@ -681,6 +688,7 @@ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int /** * \brief Reads frames from the input stream of oboeStream. Used by blocking streams. + * @param oboeStream The stream we want to read from. * @param buffer The buffer that we want to read from the input stream; * @param framesToWrite The number of frames that we want to read. * @return true if the buffer is read correctly, false if the read function returns an error @@ -707,7 +715,7 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ /** - * \brief Allocates the memory of oboeStream. + * \brief Allocates the memory of an OboeStream, and sets its address in it. * @return the address of the oboeStream. */ OboeStream *OboeEngine::allocateOboeStream() { @@ -809,7 +817,8 @@ OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ 0); } - /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ + /* continue processing user buffers if callback result is paContinue or + * if it is paComplete and userBuffers aren't empty yet */ if (m_oboeStreamHolder->callbackResult == paContinue || (m_oboeStreamHolder->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { @@ -1003,7 +1012,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde } *i_hostApi = &oboeHostApi->inheritedHostApiRep; - // Initialization of infos. + // Info initialization. (*i_hostApi)->info.structVersion = 1; (*i_hostApi)->info.type = paInDevelopment; (*i_hostApi)->info.name = "android Oboe"; @@ -1134,7 +1143,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(boeHostApi); + PaUtil_FreeMemory(oboeeHostApi); } LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", result); return result; @@ -1401,7 +1410,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->inputFormat = hostInputSampleFormat; } else { inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ + inputSampleFormat = hostInputSampleFormat = paFloat32; /* Suppress 'uninitialised var' warnings. */ oboeStream->inputFormat = hostInputSampleFormat; } @@ -1508,7 +1517,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->framesPerHostCallback) / i_sampleRate; ENSURE(InitializeInputStream(oboeStream, oboeHostApi, androidInputPreset, i_sampleRate), - "Initializing inputstream failed") + "Initializing input stream failed") } else { oboeStream->hasInput = false; } if (outputChannelCount > 0) { @@ -1519,7 +1528,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, + oboeStream->framesPerHostCallback) / i_sampleRate; ENSURE(InitializeOutputStream(oboeStream, oboeHostApi, androidOutputUsage, i_sampleRate), - "Initializing outputstream failed"); + "Initializing output stream failed"); } else { oboeStream->hasOutput = false; } *i_paStream = (PaStream *) oboeStream; From 62cfeb6c3c275185aa76639f4029a6b8d9820aa3 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 09:56:52 +0200 Subject: [PATCH 079/125] minor description corrections --- src/hostapi/oboe/pa_oboe.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index f500f5995..d417238ad 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -715,7 +715,7 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ /** - * \brief Allocates the memory of an OboeStream, and sets its address in it. + * \brief Allocates the memory of an OboeStream, and sets its EngineAddress to this. * @return the address of the oboeStream. */ OboeStream *OboeEngine::allocateOboeStream() { @@ -1159,7 +1159,7 @@ static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; if (!(oboeHostApi->oboeEngine->closeStream(nullptr))) - LOGW("[PaOboe - Terminate]\t Couldn't close the streams correctly - see OboeEngine::CloseStream logs."); + LOGI("[PaOboe - Terminate]\t The streams were probably already closed - see OboeEngine::CloseStream logs."); if (oboeHostApi->oboeEngine != nullptr) delete oboeHostApi->oboeEngine; From ae5c61c20ee18656df030d2b446078d129b2b01c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 10:14:56 +0200 Subject: [PATCH 080/125] fixed all compiling errors generated by typos --- include/portaudio.h | 2 +- src/hostapi/oboe/pa_oboe.cpp | 38 ++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/portaudio.h b/include/portaudio.h index bfee195f7..2b4e3ba75 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -291,7 +291,7 @@ typedef enum PaHostApiTypeId paWASAPI=13, paAudioScienceHPI=14, paAudioIO=15, - paPulseAudio=16 + paPulseAudio=16, paOboe=17 } PaHostApiTypeId; diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index d417238ad..c69edafd3 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -84,13 +84,13 @@ #define ENSURE(expr, errorText) \ do \ { \ - PaError m_err; \ - if (UNLIKELY((m_err = (expr)) < paNoError)) \ + PaError err; \ + if (UNLIKELY((err = (expr)) < paNoError)) \ { \ PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( \ __LINE__ ) "\n")); \ - PaUtil_SetLastHostErrorInfo(paInDevelopment, m_err, errorText); \ - m_error = m_err; \ + PaUtil_SetLastHostErrorInfo(paInDevelopment, err, errorText); \ + error = err; \ goto error; \ } \ } while (0); @@ -397,7 +397,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - m_inputBuilder.setDataCallback(i_oboeStream->oboeCallback) + i_oboeStream->inputBuilder.setDataCallback(i_oboeStream->oboeCallback) ->setErrorCallback(i_oboeStream->oboeCallback); } @@ -445,7 +445,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setErrorCallback(i_oboeStream->oboeCallback); } - result = i_oboeStream->outputBuilder.openStream(m_outputStream); + result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); @@ -558,7 +558,7 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { break; case 2: //input-only - result = inputBuilder.openStream(i_oboeStream->inputStream); + result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); @@ -639,13 +639,13 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); inputResult = i_oboeStream->inputStream->close(); - if (i_oboeStream->inputResult != Result::OK) + if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (hasOutput) { outputResult = i_oboeStream->outputStream->stop(); - if (i_oboeStream->outputResult != Result::OK) + if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(outputResult)); outputResult = i_oboeStream->outputStream->close(); @@ -1040,7 +1040,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde for (int i = 0; i < deviceCount; ++i) { PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; + deviceInfo->hostApi = i_hostApiIndex; /* OboeEngine will handle manual device selection through the use of PaOboe_SetSelectedDevice. Portaudio doesn't need to know about this, so we just use a default device. */ @@ -1143,7 +1143,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(oboeeHostApi); + PaUtil_FreeMemory(oboeHostApi); } LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", result); return result; @@ -1221,7 +1221,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, if (androidRecordingPreset != InputPreset::Generic && androidRecordingPreset != InputPreset::Camcorder && androidRecordingPreset != InputPreset::VoiceRecognition && - androidRecordingPreset != InputPreset::VoiceCommunication + androidRecordingPreset != InputPreset::VoiceCommunication && androidRecordingPreset != InputPreset::VoicePerformance) { return paIncompatibleHostApiSpecificStreamInfo; } @@ -1298,13 +1298,13 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, +static PaError InitializeOutputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, Usage i_androidOutputUsage, double i_sampleRate) { return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, Direction::Output, - sampleRate, - androidOutputUsage, + i_sampleRate, + i_androidOutputUsage, Generic); //Input preset won't be used, so we put the default value. } @@ -1321,7 +1321,7 @@ static PaError InitializeOutputStream(OboeStream i_oboeStream, PaOboeHostApiRepr * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeInputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, +static PaError InitializeInputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, InputPreset i_androidInputPreset, double i_sampleRate) { return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, @@ -1399,7 +1399,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, if (androidInputPreset != InputPreset::Generic && androidInputPreset != InputPreset::Camcorder && androidInputPreset != InputPreset::VoiceRecognition && - androidInputPreset != InputPreset::VoiceCommunication + androidInputPreset != InputPreset::VoiceCommunication && androidInputPreset != InputPreset::VoicePerformance) return paIncompatibleHostApiSpecificStreamInfo; } @@ -1447,7 +1447,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paFloat32, m_outputSampleFormat); + paFloat32, outputSampleFormat); oboeStream->outputFormat = hostOutputSampleFormat; } else { outputChannelCount = 0; @@ -1750,7 +1750,7 @@ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned PaError error = paNoError; while (i_frames > 0) { - framesToWrite = PA_MIN(stream->framesPerHostCallback, i_frames); + framesToWrite = PA_MIN(oboeStream->framesPerHostCallback, i_frames); if (!(oboeEngine->writeStream(oboeStream, userBuffer, framesToWrite * oboeStream->bufferProcessor.outputChannelCount))) From 9d03a735517e0d77f6560e7e5077b9c0b358953c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 16:09:29 +0200 Subject: [PATCH 081/125] Added OboeMediator class in place of OboeCallback, that mediates PortAudio C stream struct and the C++ OboeEngine class --- src/hostapi/oboe/pa_oboe.cpp | 267 +++++++++++++++++++---------------- 1 file changed, 143 insertions(+), 124 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index c69edafd3..a716c4340 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -165,7 +165,7 @@ PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; class OboeEngine; -class OboeCallback; +class OboeMediator; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -190,7 +190,6 @@ typedef struct OboeStream { PaSampleFormat inputFormat; PaSampleFormat outputFormat; - OboeCallback *oboeCallback; // Buffers are managed by the callback function in Oboe. void **outputBuffers; int currentOutputBuffer; @@ -200,39 +199,43 @@ typedef struct OboeStream { unsigned long framesPerHostCallback; unsigned bytesPerFrame; - OboeEngine *getEngineAddress() { return oboeEngineAddress; } - - void setEngineAddress(OboeEngine *i_oboeEngine) { oboeEngineAddress = i_oboeEngine; } - - //The only instances of output and input streams that will be used, and their builders - std::shared_ptr outputStream; - AudioStreamBuilder outputBuilder; - std::shared_ptr inputStream; - AudioStreamBuilder inputBuilder; - -private: - OboeEngine *oboeEngineAddress = nullptr; + OboeMediator* oboeMediator; } OboeStream; -/** - * Callback class for OboeStream. Will be used for non-blocking streams. - */ -class OboeCallback: public AudioStreamCallback { + +class OboeMediator: public AudioStreamCallback{ public: - OboeCallback(){ m_oboeStreamHolder = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); } + OboeMediator(OboeStream* i_oboeStream) { + m_oboeCallbackStream = i_oboeStream; + } + //Callback function for non-blocking streams DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) override; - + //Callback utils void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + void resetCallbackCounters(); + void setOutputCallback() { m_outputBuilder.setDataCallback(this)->setErrorCallback(this); } + void setInputCallback() { m_inputBuilder.setDataCallback(this)->setErrorCallback(this); } - void setStreamHolder(OboeStream* oboeStream){ m_oboeStreamHolder = oboeStream; } + //getter and setter of m_oboeEngine and m_oboeCallbackStream + OboeEngine *getEngine() { return m_oboeEngine; } + void setEngine(OboeEngine *i_oboeEngine) { m_oboeEngine = i_oboeEngine; } - void resetCallbackCounters(); + OboeStream *getStreamAddress() { return m_oboeCallbackStream; } + void setCallbackStream(OboeStream *i_oboeStream) { m_oboeCallbackStream = i_oboeStream; } + + //The only instances of output and input streams that will be used, and their builders + std::shared_ptr m_outputStream; + AudioStreamBuilder m_outputBuilder; + std::shared_ptr m_inputStream; + AudioStreamBuilder m_inputBuilder; private: + OboeEngine *m_oboeEngine; + //callback utils - OboeStream *m_oboeStreamHolder; + OboeStream *m_oboeCallbackStream; unsigned long m_framesProcessed{}; PaStreamCallbackTimeInfo m_timeInfo{}; struct timespec m_timeSpec{}; @@ -270,12 +273,14 @@ class OboeEngine { bool readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead); //Engine utils - OboeStream *allocateOboeStream(); + void constructOboeStream(OboeStream* oboeStream); private: std::shared_ptr m_testStream; AudioStreamBuilder m_testBuilder; + OboeMediator* m_terminableMediator; + //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -379,15 +384,14 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { PaError error = paNoError; Result result; + OboeMediator* mediator = i_oboeStream->oboeMediator; if(!(i_oboeStream->isBlocking)){ - i_oboeStream->oboeCallback = new OboeCallback(); - i_oboeStream->oboeCallback->setStreamHolder(i_oboeStream); - i_oboeStream->oboeCallback->resetCallbackCounters(); + mediator->resetCallbackCounters(); } if (i_direction == Direction::Input) { - i_oboeStream->inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) + mediator->m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Input) @@ -397,11 +401,10 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - i_oboeStream->inputBuilder.setDataCallback(i_oboeStream->oboeCallback) - ->setErrorCallback(i_oboeStream->oboeCallback); + mediator->setInputCallback(); } - result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); + result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", @@ -409,7 +412,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, return paUnanticipatedHostError; } - i_oboeStream->inputStream->setBufferSizeInFrames(i_oboeStream->inputStream->getFramesPerBurst() * + mediator->m_inputStream->setBufferSizeInFrames(mediator->m_inputStream->getFramesPerBurst() * g_numberOfBuffers); i_oboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -424,14 +427,14 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->inputBuffers); - i_oboeStream->inputStream->close(); + mediator->m_inputStream->close(); error = paInsufficientMemory; break; } } i_oboeStream->currentInputBuffer = 0; } else { - i_oboeStream->outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) + mediator->m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Output) @@ -441,18 +444,17 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - i_oboeStream->outputBuilder.setDataCallback(i_oboeStream->oboeCallback) - ->setErrorCallback(i_oboeStream->oboeCallback); + mediator->setOutputCallback(); } - result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); + result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); return paUnanticipatedHostError; } - i_oboeStream->outputStream->setBufferSizeInFrames(i_oboeStream->outputStream->getFramesPerBurst() * + mediator->m_outputStream->setBufferSizeInFrames(mediator->m_outputStream->getFramesPerBurst() * g_numberOfBuffers); i_oboeStream->outputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -467,7 +469,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->outputBuffers); - i_oboeStream->outputStream->close(); + mediator->m_outputStream->close(); error = paInsufficientMemory; break; } @@ -487,15 +489,16 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, */ bool OboeEngine::startStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; + OboeMediator* mediator = i_oboeStream->oboeMediator; if (i_oboeStream->hasInput) { - inputResult = i_oboeStream->inputStream->requestStart(); + inputResult = mediator->m_inputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = i_oboeStream->outputStream->requestStart(); + outputResult = mediator->m_outputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", convertToText(outputResult)); @@ -513,15 +516,16 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { */ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; + OboeMediator* mediator = i_oboeStream->oboeMediator; if (i_oboeStream->hasInput) { - inputResult = i_oboeStream->inputStream->requestStop(); + inputResult = mediator->m_inputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = i_oboeStream->outputStream->requestStop(); + outputResult = mediator->m_outputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", convertToText(outputResult)); @@ -536,20 +540,21 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { * while a stream is started. Oboe will stop and close said streams in that case, * so this function just reopens and restarts them. * @param oboeStream The stream we want to restart. - * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 0 for both). + * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 3 for both). * @return true if the stream is restarted successfully, false otherwise. */ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { bool outcome = true; Result result; + OboeMediator* mediator = i_oboeStream->oboeMediator; switch (i_direction) { case 1: //output-only - result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); + result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", convertToText(result)); - result = i_oboeStream->outputStream->start(); + result = mediator->m_outputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", convertToText(result)); @@ -558,11 +563,11 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { break; case 2: //input-only - result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); + result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); - result = i_oboeStream->inputStream->start(); + result = mediator->m_inputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", convertToText(result)); @@ -590,23 +595,26 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { bool OboeEngine::closeStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; bool hasOutput = true, hasInput = true; + OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Terminating both oboe streams."); + LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); + mediator = m_terminableMediator; } else { + mediator = i_oboeStream->oboeMediator; hasInput = i_oboeStream->hasInput; hasOutput = i_oboeStream->hasOutput; } if (hasOutput) { - outputResult = i_oboeStream->outputStream->close(); + outputResult = mediator->m_outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } if (hasInput) { - inputResult = i_oboeStream->inputStream->close(); + inputResult = mediator->m_inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); @@ -625,30 +633,33 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { bool OboeEngine::abortStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; bool hasOutput = true, hasInput = true; + OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams."); + LOGW("[OboeEngine::abortStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); + mediator = m_terminableMediator; } else { + mediator = i_oboeStream->oboeMediator; hasInput = i_oboeStream->hasInput; hasOutput = i_oboeStream->hasOutput; } if (hasInput) { - inputResult = i_oboeStream->inputStream->stop(); + inputResult = mediator->m_inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); - inputResult = i_oboeStream->inputStream->close(); + inputResult = mediator->m_inputStream->close(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (hasOutput) { - outputResult = i_oboeStream->outputStream->stop(); + outputResult = mediator->m_outputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(outputResult)); - outputResult = i_oboeStream->outputStream->close(); + outputResult = mediator->m_outputStream->close(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", convertToText(outputResult)); @@ -669,8 +680,9 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { */ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int32_t i_framesToWrite) { bool outcome = true; + OboeMediator* mediator = i_oboeStream->oboeMediator; - ResultWithValue result = i_oboeStream->outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); + ResultWithValue result = mediator->m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -697,8 +709,9 @@ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int */ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_framesToRead) { bool outcome = true; + OboeMediator* mediator = i_oboeStream->oboeMediator; - ResultWithValue result = i_oboeStream->inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); + ResultWithValue result = mediator->m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -718,10 +731,10 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ * \brief Allocates the memory of an OboeStream, and sets its EngineAddress to this. * @return the address of the oboeStream. */ -OboeStream *OboeEngine::allocateOboeStream() { - OboeStream *oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); - oboeStream->setEngineAddress(this); - return oboeStream; +void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { + i_oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); + m_terminableMediator = i_oboeStream->oboeMediator = new OboeMediator(i_oboeStream); + i_oboeStream->oboeMediator->setEngine(this); } @@ -771,114 +784,119 @@ int32_t OboeEngine::getSelectedDevice(Direction i_direction) { return g_outputDeviceId; } -/*----------------------------- OboeCallback functions implementations -----------------------------*/ +/*----------------------------- OboeMediator functions implementations -----------------------------*/ /** * \brief Oboe's callback routine. */ DataCallbackResult -OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { +OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { clock_gettime(CLOCK_REALTIME, &m_timeSpec); m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); - m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate + m_timeInfo.currentTime); - m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate + m_timeInfo.currentTime); /* check if StopStream or AbortStream was called */ - if (m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->callbackResult = paComplete; - } else if (m_oboeStreamHolder->doAbort) { - m_oboeStreamHolder->callbackResult = paAbort; + if (m_oboeCallbackStream->doStop) { + m_oboeCallbackStream->callbackResult = paComplete; + } else if (m_oboeCallbackStream->doAbort) { + m_oboeCallbackStream->callbackResult = paAbort; } - PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, - &m_timeInfo, m_oboeStreamHolder->cbFlags); + PaUtil_BeginCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&m_oboeCallbackStream->bufferProcessor, + &m_timeInfo, m_oboeCallbackStream->cbFlags); - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; - PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); - PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ - m_oboeStreamHolder->currentOutputBuffer], + if (m_oboeCallbackStream->hasOutput) { + m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = i_audioData; + PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, i_numFrames); + PaUtil_SetInterleavedOutputChannels(&m_oboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeCallbackStream->outputBuffers)[ + m_oboeCallbackStream->currentOutputBuffer], 0); } - if (m_oboeStreamHolder->hasInput) { - i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; - PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ - m_oboeStreamHolder->currentInputBuffer], + if (m_oboeCallbackStream->hasInput) { + i_audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; + PaUtil_SetInputFrameCount(&m_oboeCallbackStream->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&m_oboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeCallbackStream->inputBuffers)[ + m_oboeCallbackStream->currentInputBuffer], 0); } /* continue processing user buffers if callback result is paContinue or * if it is paComplete and userBuffers aren't empty yet */ - if (m_oboeStreamHolder->callbackResult == paContinue - || (m_oboeStreamHolder->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeStreamHolder->bufferProcessor, - &m_oboeStreamHolder->callbackResult); + if (m_oboeCallbackStream->callbackResult == paContinue + || (m_oboeCallbackStream->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeCallbackStream->bufferProcessor))) { + m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeCallbackStream->bufferProcessor, + &m_oboeCallbackStream->callbackResult); } /* enqueue a buffer only when there are frames to be processed, * this will be 0 when paComplete + empty buffers or paAbort */ if (m_framesProcessed > 0) { - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->currentOutputBuffer = - (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; + if (m_oboeCallbackStream->hasOutput) { + m_oboeCallbackStream->currentOutputBuffer = + (m_oboeCallbackStream->currentOutputBuffer + 1) % g_numberOfBuffers; } - if (m_oboeStreamHolder->hasInput) { - m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; + if (m_oboeCallbackStream->hasInput) { + m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % g_numberOfBuffers; } } - PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); + PaUtil_EndCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer, m_framesProcessed); /* StopStream was called */ - if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; + if (m_framesProcessed == 0 && m_oboeCallbackStream->doStop) { + m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; } /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { - m_oboeStreamHolder->isActive = false; - m_oboeStreamHolder->isStopped = true; - if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) - m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( - m_oboeStreamHolder->streamRepresentation.userData); - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) + else if (m_framesProcessed == 0 && !(m_oboeCallbackStream->doAbort || m_oboeCallbackStream->doStop)) { + m_oboeCallbackStream->isActive = false; + m_oboeCallbackStream->isStopped = true; + if (m_oboeCallbackStream->streamRepresentation.streamFinishedCallback != nullptr) + m_oboeCallbackStream->streamRepresentation.streamFinishedCallback( + m_oboeCallbackStream->streamRepresentation.userData); + m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) } - return m_oboeStreamHolder->oboeCallbackResult; + return m_oboeCallbackStream->oboeCallbackResult; } /** * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells - * what error occurred. + * what error occurred, and tries to restart the stream if the error was ErrorDisconnected. */ -void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { +void OboeMediator::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { if (i_error == oboe::Result::ErrorDisconnected) { - OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); - LOGW("[OboeCallback::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); - if (!oboeEngine->restartStream(m_oboeStreamHolder, 0)) - LOGE("[OboeCallback::onErrorAfterClose]\t Couldn't restart stream(s)"); + OboeEngine* oboeEngine = getEngine(); + LOGW("[OboeMediator::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + int i = 0; + if(m_oboeCallbackStream->hasOutput) + i++; + if(m_oboeCallbackStream->hasInput) + i+=2; + if (!oboeEngine->restartStream(m_oboeCallbackStream, i)) + LOGE("[OboeMediator::onErrorAfterClose]\t Couldn't restart stream(s)"); } else - LOGE("[OboeCallback::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); + LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); } /** - * \brief Resets callback counters (called at the start of each iteration of onAudioReady + * \brief Resets callback counters (called at the start of each iteration of onAudioReady). */ -void OboeCallback::resetCallbackCounters() { +void OboeMediator::resetCallbackCounters() { m_framesProcessed = 0; m_timeInfo = {0, 0, 0}; } @@ -1368,7 +1386,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; - OboeStream *oboeStream = oboeHostApi->oboeEngine->allocateOboeStream(); + OboeStream* oboeStream; + oboeHostApi->oboeEngine->constructOboeStream(oboeStream); if (!oboeStream) { error = paInsufficientMemory; @@ -1554,7 +1573,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, */ static PaError CloseStream(PaStream *i_paStream) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (!(oboeEngine->closeStream(oboeStream))) LOGW("[PaOboe - CloseStream]\t Some errors have occurred in closing oboe streams - see OboeEngine::CloseStream logs."); @@ -1588,7 +1607,7 @@ static PaError CloseStream(PaStream *i_paStream) { */ static PaError StartStream(PaStream *i_paStream) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); PaUtil_ResetBufferProcessor(&oboeStream->bufferProcessor); @@ -1647,7 +1666,7 @@ static PaError StartStream(PaStream *i_paStream) { static PaError StopStream(PaStream *i_paStream) { PaError error = paNoError; auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (oboeStream->isStopped) { LOGW("[PaOboe - StopStream]\t Stream was already stopped."); @@ -1681,7 +1700,7 @@ static PaError StopStream(PaStream *i_paStream) { static PaError AbortStream(PaStream *i_paStream) { PaError error = paNoError; auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); LOGI("[PaOboe - AbortStream]\t Aborting stream."); if (!oboeStream->isBlocking) { @@ -1714,7 +1733,7 @@ static PaError AbortStream(PaStream *i_paStream) { */ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_frames) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); void *userBuffer = i_buffer; unsigned framesToRead; PaError error = paNoError; @@ -1744,7 +1763,7 @@ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_ */ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned long i_frames) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); const void *userBuffer = i_buffer; unsigned framesToWrite; PaError error = paNoError; From 5098235f37a3bd4eb3b4809fcae460ff27c34ae4 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 6 Oct 2023 10:07:35 +0200 Subject: [PATCH 082/125] Fixed allocation problem, working PaOboe implementation --- src/hostapi/oboe/pa_oboe.cpp | 44 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index a716c4340..2fd0dc564 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -384,6 +384,12 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { PaError error = paNoError; Result result; + + if (i_oboeStream == nullptr) { + LOGE("[OboeEngine::openStream]\t i_oboeStream is a nullptr."); + return paInternalError; + } + OboeMediator* mediator = i_oboeStream->oboeMediator; if(!(i_oboeStream->isBlocking)){ @@ -594,26 +600,22 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { */ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - bool hasOutput = true, hasInput = true; - OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); - mediator = m_terminableMediator; - } else { - mediator = i_oboeStream->oboeMediator; - hasInput = i_oboeStream->hasInput; - hasOutput = i_oboeStream->hasOutput; + LOGE("[OboeEngine::closeStream]\t i_oboeStream is a nullptr."); + return false; } - if (hasOutput) { + OboeMediator* mediator = i_oboeStream->oboeMediator; + + if (i_oboeStream->hasOutput) { outputResult = mediator->m_outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } - if (hasInput) { + if (i_oboeStream->hasInput) { inputResult = mediator->m_inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; @@ -632,19 +634,15 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { */ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - bool hasOutput = true, hasInput = true; - OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::abortStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); - mediator = m_terminableMediator; - } else { - mediator = i_oboeStream->oboeMediator; - hasInput = i_oboeStream->hasInput; - hasOutput = i_oboeStream->hasOutput; + LOGE("[OboeEngine::abortStream]\t i_oboeStream is a nullptr."); + return false; } - if (hasInput) { + OboeMediator* mediator = i_oboeStream->oboeMediator; + + if (i_oboeStream->hasInput) { inputResult = mediator->m_inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", @@ -654,7 +652,7 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } - if (hasOutput) { + if (i_oboeStream->hasOutput) { outputResult = mediator->m_outputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", @@ -732,7 +730,6 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ * @return the address of the oboeStream. */ void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { - i_oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); m_terminableMediator = i_oboeStream->oboeMediator = new OboeMediator(i_oboeStream); i_oboeStream->oboeMediator->setEngine(this); } @@ -1176,9 +1173,6 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; - if (!(oboeHostApi->oboeEngine->closeStream(nullptr))) - LOGI("[PaOboe - Terminate]\t The streams were probably already closed - see OboeEngine::CloseStream logs."); - if (oboeHostApi->oboeEngine != nullptr) delete oboeHostApi->oboeEngine; @@ -1386,7 +1380,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; - OboeStream* oboeStream; + OboeStream* oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream));; oboeHostApi->oboeEngine->constructOboeStream(oboeStream); if (!oboeStream) { From d8b1b8e3a8582f136ca50b0b09a514749a18b753 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 10:29:19 +0200 Subject: [PATCH 083/125] fixed README.md indenting --- src/hostapi/oboe/README.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index c4de74088..14d6b6b73 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -6,28 +6,37 @@ headers in your project. Building: ---- To build portaudio with Oboe, there are some necessary steps: -1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. -2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the prebuilt libraries, you can skip this step. -3) Set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository, and build the Oboe libraries (you can use "build_all_android.sh"). +1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at + https://developer.android.com/ndk/downloads. +2) Clone the Oboe repository - just follow the steps detailed here: + https://github.com/google/oboe/blob/main/docs/GettingStarted.md. + Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the + prebuilt libraries, you can skip this step. +3) Set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) to the path of the + cloned Oboe repository, and build the Oboe libraries (you can use "build_all_android.sh"). If you instead used the prebuilt libraries, do the following: - set OBOE_DIRECTORY to TRUE; - - set OBOE_LIBRARY_DIRS path_to_Oboe_libraries_folder/${ANDROID_ABI}), the code will search the prebuilt library of the chosen ABI in that folder. + - set OBOE_LIBRARY_DIRS path_to_Oboe_libraries_folder/${ANDROID_ABI}), the code will search the + prebuilt library of the chosen ABI in that folder. 4) Build PaOboe (you can use "build_all_PaOboe.sh"). 5) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. TODOs: ---- -- Testing. This implementation is being tested for VoIP calls that use blocking streams - for everything else, lots of tests are needed. +- Testing. This implementation is being tested for VoIP calls that use blocking streams - for + everything else, lots of tests are needed. Misc ---- ### Audio Format: -If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream by modifying the format selection marked with a *FIXME*. -I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat got me nowhere. +If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream +by modifying the format selection marked with a *FIXME*. I'm positive that automatic format selection +is possible, but simply using PaUtil_SelectClosestAvailableFormat got me nowhere. ### Buffer sizes: -Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your +buffer you should manually modify it (or make a simple function that can set it). For your convenience, +there is a *FIXME* as a bookmark. \ No newline at end of file From 72abf68c14e21702844bbbe7a296fe2c3ba2bd73 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 10:30:28 +0200 Subject: [PATCH 084/125] Removed .idea folder from repo --- .idea/.gitignore | 3 --- .idea/misc.xml | 5 ----- .idea/modules.xml | 8 -------- .idea/portaudio.iml | 9 --------- .idea/vcs.xml | 6 ------ 5 files changed, 31 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/portaudio.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521a..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 6e8667213..000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index f2d867481..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/portaudio.iml b/.idea/portaudio.iml deleted file mode 100644 index d6ebd4805..000000000 --- a/.idea/portaudio.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfb..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 912e951edf07c8a0bbc1dab38ae261ea55bebab3 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 10:46:33 +0200 Subject: [PATCH 085/125] replaced 'g_' with 'paOboe_' in non-static global variables --- src/hostapi/oboe/pa_oboe.cpp | 568 +++++++++++++++++------------------ 1 file changed, 284 insertions(+), 284 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 2fd0dc564..ca1571a59 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -152,17 +152,17 @@ static signed long GetStreamWriteAvailable(PaStream *stream); static unsigned long GetApproximateLowBufferSize(); // Commonly used parameters initialized. -static unsigned long g_nativeBufferSize = 0; -static unsigned g_numberOfBuffers = 2; +static unsigned long paOboe_nativeBufferSize = 0; +static unsigned paOboe_numberOfBuffers = 2; using namespace oboe; //Useful global variables -int32_t g_inputDeviceId = kUnspecified; -int32_t g_outputDeviceId = kUnspecified; +int32_t paOboe_inputDeviceId = kUnspecified; +int32_t paOboe_outputDeviceId = kUnspecified; -PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; -PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; +PerformanceMode paOboe_inputPerfMode = PerformanceMode::LowLatency; +PerformanceMode paOboe_outputPerfMode = PerformanceMode::LowLatency; class OboeEngine; class OboeMediator; @@ -205,8 +205,8 @@ typedef struct OboeStream { class OboeMediator: public AudioStreamCallback{ public: - OboeMediator(OboeStream* i_oboeStream) { - m_oboeCallbackStream = i_oboeStream; + OboeMediator(OboeStream* oboeStream) { + m_oboeCallbackStream = oboeStream; } //Callback function for non-blocking streams @@ -220,10 +220,10 @@ class OboeMediator: public AudioStreamCallback{ //getter and setter of m_oboeEngine and m_oboeCallbackStream OboeEngine *getEngine() { return m_oboeEngine; } - void setEngine(OboeEngine *i_oboeEngine) { m_oboeEngine = i_oboeEngine; } + void setEngine(OboeEngine *oboeEngine) { m_oboeEngine = oboeEngine; } OboeStream *getStreamAddress() { return m_oboeCallbackStream; } - void setCallbackStream(OboeStream *i_oboeStream) { m_oboeCallbackStream = i_oboeStream; } + void setCallbackStream(OboeStream *oboeStream) { m_oboeCallbackStream = oboeStream; } //The only instances of output and input streams that will be used, and their builders std::shared_ptr m_outputStream; @@ -313,8 +313,8 @@ OboeEngine::OboeEngine() {} /** - * \brief Tries to open a stream with the direction i_direction, sample rate i_sampleRate and/or - * channel count i_channelCount. It then checks if the stream was in fact opened with the + * \brief Tries to open a stream with the direction direction, sample rate sampleRate and/or + * channel count channelCount. It then checks if the stream was in fact opened with the * desired settings, and then closes the stream. It's used to see if the requested * parameters are supported by the devices that are going to be used. * @param direction the Direction of the stream; @@ -323,16 +323,16 @@ OboeEngine::OboeEngine() {} * @return true if the requested sample rate / channel count is supported by the device, false if * they aren't, or if tryStream couldn't open a stream. */ -bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t i_channelCount) { +bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { Result result; bool outcome = false; - m_testBuilder.setDeviceId(getSelectedDevice(i_direction)) + m_testBuilder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) - ->setDirection(i_direction) - ->setSampleRate(i_sampleRate) - ->setChannelCount(i_channelCount) + ->setDirection(direction) + ->setSampleRate(sampleRate) + ->setChannelCount(channelCount) ->openStream(m_testStream); if (result != Result::OK) { @@ -341,17 +341,17 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t return outcome; } - if (i_sampleRate != kUnspecified) { - outcome = (i_sampleRate == m_testBuilder.getSampleRate()); + if (sampleRate != kUnspecified) { + outcome = (sampleRate == m_testBuilder.getSampleRate()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - i_sampleRate, m_testBuilder.getSampleRate()); + sampleRate, m_testBuilder.getSampleRate()); } - } else if (i_channelCount != kUnspecified) { - outcome = (i_channelCount == m_testBuilder.getChannelCount()); + } else if (channelCount != kUnspecified) { + outcome = (channelCount == m_testBuilder.getChannelCount()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - i_channelCount, m_testBuilder.getChannelCount()); + channelCount, m_testBuilder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); @@ -380,33 +380,33 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t * @return paNoError if everything goes as expected, paUnanticipatedHostError if Oboe fails to open * a stream, and paInsufficientMemory if the memory allocation of the buffers fails. */ -PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, int32_t i_sampleRate, - Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { +PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int32_t sampleRate, + Usage androidOutputUsage, InputPreset androidInputPreset) { PaError error = paNoError; Result result; - if (i_oboeStream == nullptr) { - LOGE("[OboeEngine::openStream]\t i_oboeStream is a nullptr."); + if (oboeStream == nullptr) { + LOGE("[OboeEngine::openStream]\t oboeStream is a nullptr."); return paInternalError; } - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if(!(i_oboeStream->isBlocking)){ + if(!(oboeStream->isBlocking)){ mediator->resetCallbackCounters(); } - if (i_direction == Direction::Input) { - mediator->m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) - ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) - ->setSampleRate(i_sampleRate) + if (direction == Direction::Input) { + mediator->m_inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) + ->setSampleRate(sampleRate) ->setDirection(Direction::Input) ->setDeviceId(getSelectedDevice(Direction::Input)) - ->setPerformanceMode(g_inputPerfMode) - ->setInputPreset(i_androidInputPreset) - ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); + ->setPerformanceMode(paOboe_inputPerfMode) + ->setInputPreset(androidInputPreset) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); - if (!(i_oboeStream->isBlocking)) { + if (!(oboeStream->isBlocking)) { mediator->setInputCallback(); } @@ -419,37 +419,37 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, } mediator->m_inputStream->setBufferSizeInFrames(mediator->m_inputStream->getFramesPerBurst() * - g_numberOfBuffers); - i_oboeStream->inputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); + paOboe_numberOfBuffers); + oboeStream->inputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < g_numberOfBuffers; ++i) { - i_oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - i_oboeStream->framesPerHostCallback * - i_oboeStream->bytesPerFrame * - i_oboeStream->bufferProcessor.inputChannelCount); + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { + oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.inputChannelCount); - if (!i_oboeStream->inputBuffers[i]) { + if (!oboeStream->inputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); - PaUtil_FreeMemory(i_oboeStream->inputBuffers); + PaUtil_FreeMemory(oboeStream->inputBuffers[j]); + PaUtil_FreeMemory(oboeStream->inputBuffers); mediator->m_inputStream->close(); error = paInsufficientMemory; break; } } - i_oboeStream->currentInputBuffer = 0; + oboeStream->currentInputBuffer = 0; } else { - mediator->m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) - ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) - ->setSampleRate(i_sampleRate) + mediator->m_outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) + ->setSampleRate(sampleRate) ->setDirection(Direction::Output) ->setDeviceId(getSelectedDevice(Direction::Output)) - ->setPerformanceMode(g_outputPerfMode) - ->setUsage(i_androidOutputUsage) - ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); + ->setPerformanceMode(paOboe_outputPerfMode) + ->setUsage(androidOutputUsage) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); - if (!(i_oboeStream->isBlocking)) { + if (!(oboeStream->isBlocking)) { mediator->setOutputCallback(); } @@ -461,26 +461,26 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, } mediator->m_outputStream->setBufferSizeInFrames(mediator->m_outputStream->getFramesPerBurst() * - g_numberOfBuffers); - i_oboeStream->outputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); + paOboe_numberOfBuffers); + oboeStream->outputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < g_numberOfBuffers; ++i) { - i_oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - i_oboeStream->framesPerHostCallback * - i_oboeStream->bytesPerFrame * - i_oboeStream->bufferProcessor.outputChannelCount); + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { + oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.outputChannelCount); - if (!i_oboeStream->outputBuffers[i]) { + if (!oboeStream->outputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); - PaUtil_FreeMemory(i_oboeStream->outputBuffers); + PaUtil_FreeMemory(oboeStream->outputBuffers[j]); + PaUtil_FreeMemory(oboeStream->outputBuffers); mediator->m_outputStream->close(); error = paInsufficientMemory; break; } } - i_oboeStream->currentOutputBuffer = 0; + oboeStream->currentOutputBuffer = 0; } return error; @@ -493,17 +493,17 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, * @param oboeStream The stream we want to start. * @return true if the streams we wanted to start are started successfully, false otherwise. */ -bool OboeEngine::startStream(OboeStream *i_oboeStream) { +bool OboeEngine::startStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", @@ -520,17 +520,17 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { * @param oboeStream The stream we want to stop. * @return true if the streams we wanted to stop are stopped successfully, false otherwise. */ -bool OboeEngine::stopStream(OboeStream *i_oboeStream) { +bool OboeEngine::stopStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", @@ -549,12 +549,12 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 3 for both). * @return true if the stream is restarted successfully, false otherwise. */ -bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { +bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { bool outcome = true; Result result; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - switch (i_direction) { + switch (direction) { case 1: //output-only result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); if (result != Result::OK) @@ -584,7 +584,7 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { default: // unspecified direction or both directions: restart both streams LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); - outcome = (restartStream(i_oboeStream, 1) && restartStream(i_oboeStream, 2)); + outcome = (restartStream(oboeStream, 1) && restartStream(oboeStream, 2)); break; } @@ -598,24 +598,24 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { * @param oboeStream The stream we want to close. * @return true if the stream is closed successfully, otherwise returns false. */ -bool OboeEngine::closeStream(OboeStream *i_oboeStream) { +bool OboeEngine::closeStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - if (i_oboeStream == nullptr) { - LOGE("[OboeEngine::closeStream]\t i_oboeStream is a nullptr."); + if (oboeStream == nullptr) { + LOGE("[OboeEngine::closeStream]\t oboeStream is a nullptr."); return false; } - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; @@ -632,17 +632,17 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { * @param oboeStream The stream we want to abort. * @return true if the output stream and the input stream are stopped successfully, false otherwise. */ -bool OboeEngine::abortStream(OboeStream *i_oboeStream) { +bool OboeEngine::abortStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - if (i_oboeStream == nullptr) { - LOGE("[OboeEngine::abortStream]\t i_oboeStream is a nullptr."); + if (oboeStream == nullptr) { + LOGE("[OboeEngine::abortStream]\t oboeStream is a nullptr."); return false; } - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", @@ -652,7 +652,7 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", @@ -676,15 +676,15 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int32_t i_framesToWrite) { +bool OboeEngine::writeStream(OboeStream *oboeStream, const void buffer, int32_t framesToWrite) { bool outcome = true; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); + ResultWithValue result = mediator->m_outputStream->write(buffer, framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (restartStream(i_oboeStream, 1)) + if (restartStream(oboeStream, 1)) return true; } @@ -705,15 +705,15 @@ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_framesToRead) { +bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead) { bool outcome = true; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); + ResultWithValue result = mediator->m_inputStream->read(buffer, framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (restartStream(i_oboeStream, 2)) + if (restartStream(oboeStream, 2)) return true; } @@ -729,9 +729,9 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ * \brief Allocates the memory of an OboeStream, and sets its EngineAddress to this. * @return the address of the oboeStream. */ -void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { - m_terminableMediator = i_oboeStream->oboeMediator = new OboeMediator(i_oboeStream); - i_oboeStream->oboeMediator->setEngine(this); +void OboeEngine::constructOboeStream(OboeStream* oboeStream) { + m_terminableMediator = oboeStream->oboeMediator = new OboeMediator(oboeStream); + oboeStream->oboeMediator->setEngine(this); } @@ -740,9 +740,9 @@ void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { * @param paFormat the PaSampleFormat we want to convert. * @return the converted AudioFormat. */ -AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat i_paFormat) { +AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { AudioFormat oboeFormat; - switch (i_paFormat) { + switch (paFormat) { case paFloat32: oboeFormat = AudioFormat::Float; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); @@ -774,11 +774,11 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat i_paFormat) { * @param direction the Oboe::Direction for which we want to know the device Id. * @return the device Id of the appropriate direction. */ -int32_t OboeEngine::getSelectedDevice(Direction i_direction) { - if (i_direction == Direction::Input) - return g_inputDeviceId; +int32_t OboeEngine::getSelectedDevice(Direction direction) { + if (direction == Direction::Input) + return paOboe_inputDeviceId; else - return g_outputDeviceId; + return paOboe_outputDeviceId; } /*----------------------------- OboeMediator functions implementations -----------------------------*/ @@ -786,7 +786,7 @@ int32_t OboeEngine::getSelectedDevice(Direction i_direction) { * \brief Oboe's callback routine. */ DataCallbackResult -OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { +OboeMediator::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { clock_gettime(CLOCK_REALTIME, &m_timeSpec); m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); @@ -811,15 +811,15 @@ OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ &m_timeInfo, m_oboeCallbackStream->cbFlags); if (m_oboeCallbackStream->hasOutput) { - m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = i_audioData; - PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, i_numFrames); + m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = audioData; + PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, numFrames); PaUtil_SetInterleavedOutputChannels(&m_oboeCallbackStream->bufferProcessor, 0, (void *) ((PaInt16 **) m_oboeCallbackStream->outputBuffers)[ m_oboeCallbackStream->currentOutputBuffer], 0); } if (m_oboeCallbackStream->hasInput) { - i_audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; + audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; PaUtil_SetInputFrameCount(&m_oboeCallbackStream->bufferProcessor, 0); PaUtil_SetInterleavedInputChannels(&m_oboeCallbackStream->bufferProcessor, 0, (void *) ((PaInt16 **) m_oboeCallbackStream->inputBuffers)[ @@ -842,10 +842,10 @@ OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ if (m_framesProcessed > 0) { if (m_oboeCallbackStream->hasOutput) { m_oboeCallbackStream->currentOutputBuffer = - (m_oboeCallbackStream->currentOutputBuffer + 1) % g_numberOfBuffers; + (m_oboeCallbackStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; } if (m_oboeCallbackStream->hasInput) { - m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % g_numberOfBuffers; + m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; } } @@ -874,8 +874,8 @@ OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells * what error occurred, and tries to restart the stream if the error was ErrorDisconnected. */ -void OboeMediator::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { - if (i_error == oboe::Result::ErrorDisconnected) { +void OboeMediator::onErrorAfterClose(AudioStream *audioStream, Result error) { + if (error == oboe::Result::ErrorDisconnected) { OboeEngine* oboeEngine = getEngine(); LOGW("[OboeMediator::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); int i = 0; @@ -886,7 +886,7 @@ void OboeMediator::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) if (!oboeEngine->restartStream(m_oboeCallbackStream, i)) LOGE("[OboeMediator::onErrorAfterClose]\t Couldn't restart stream(s)"); } else - LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); + LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); } @@ -910,9 +910,9 @@ void OboeMediator::resetCallbackCounters() { * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, - i_sampleRate, +PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, + sampleRate, kUnspecified))) LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe."); @@ -932,9 +932,9 @@ PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, - i_sampleRate, +PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, + sampleRate, kUnspecified))) LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe."); @@ -953,15 +953,15 @@ PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, d * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { - if (i_numOfChannels > 2 || i_numOfChannels == 0) { +static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *oboeHostApi, int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { LOGE("[PaOboe - IsOutputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, kUnspecified, - i_numOfChannels))) + numOfChannels))) LOGW("[PaOboe - IsOutputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -979,15 +979,15 @@ static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *i_oboe * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { - if (i_numOfChannels > 2 || i_numOfChannels == 0) { +static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *oboeHostApi, int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { LOGE("[PaOboe - IsInputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, kUnspecified, - i_numOfChannels))) + numOfChannels))) LOGW("[PaOboe - IsInputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -1004,7 +1004,7 @@ static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeH * @param hostApiIndex is a PaHostApiIndex, the type used to enumerate the host APIs at runtime. * @return paNoError if no errors occur, or paInsufficientMemory if memory allocation fails; */ -PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiIndex i_hostApiIndex) { +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) { PaError result = paNoError; int deviceCount; PaOboeHostApiRepresentation *oboeHostApi; @@ -1026,20 +1026,20 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde goto error; } - *i_hostApi = &oboeHostApi->inheritedHostApiRep; + *hostApi = &oboeHostApi->inheritedHostApiRep; // Info initialization. - (*i_hostApi)->info.structVersion = 1; - (*i_hostApi)->info.type = paInDevelopment; - (*i_hostApi)->info.name = "android Oboe"; - (*i_hostApi)->info.defaultOutputDevice = 0; - (*i_hostApi)->info.defaultInputDevice = 0; - (*i_hostApi)->info.deviceCount = 0; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paInDevelopment; + (*hostApi)->info.name = "android Oboe"; + (*hostApi)->info.defaultOutputDevice = 0; + (*hostApi)->info.defaultInputDevice = 0; + (*hostApi)->info.deviceCount = 0; deviceCount = 1; - (*i_hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( + (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( oboeHostApi->allocations, sizeof(PaDeviceInfo * ) * deviceCount); - if (!(*i_hostApi)->deviceInfos) { + if (!(*hostApi)->deviceInfos) { result = paInsufficientMemory; goto error; } @@ -1055,7 +1055,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde for (int i = 0; i < deviceCount; ++i) { PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; deviceInfo->structVersion = 2; - deviceInfo->hostApi = i_hostApiIndex; + deviceInfo->hostApi = hostApiIndex; /* OboeEngine will handle manual device selection through the use of PaOboe_SetSelectedDevice. Portaudio doesn't need to know about this, so we just use a default device. */ @@ -1097,18 +1097,18 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde if (deviceInfo->defaultSampleRate == 0) goto error; - /* If the user has set g_nativeBufferSize by querying the optimal buffer size via java, + /* If the user has set paOboe_nativeBufferSize by querying the optimal buffer size via java, use the user-defined value since that will offer the lowest possible latency. */ - if (g_nativeBufferSize != 0) { + if (paOboe_nativeBufferSize != 0) { deviceInfo->defaultLowInputLatency = - (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize / deviceInfo->defaultSampleRate; deviceInfo->defaultLowOutputLatency = - (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize / deviceInfo->defaultSampleRate; deviceInfo->defaultHighInputLatency = - (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; deviceInfo->defaultHighOutputLatency = - (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; } else { deviceInfo->defaultLowInputLatency = (double) GetApproximateLowBufferSize() / deviceInfo->defaultSampleRate; @@ -1120,13 +1120,13 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde (double) GetApproximateLowBufferSize() * 4 / deviceInfo->defaultSampleRate; } - (*i_hostApi)->deviceInfos[i] = deviceInfo; - ++(*i_hostApi)->info.deviceCount; + (*hostApi)->deviceInfos[i] = deviceInfo; + ++(*hostApi)->info.deviceCount; } - (*i_hostApi)->Terminate = Terminate; - (*i_hostApi)->OpenStream = OpenStream; - (*i_hostApi)->IsFormatSupported = IsFormatSupported; + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; PaUtil_InitializeStreamInterface(&oboeHostApi->callbackStreamInterface, CloseStream, StartStream, StopStream, @@ -1170,8 +1170,8 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the * interface to a host API (see struct in "pa_hostapi.h"). */ -static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { - auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; +static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { + auto *oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; if (oboeHostApi->oboeEngine != nullptr) delete oboeHostApi->oboeEngine; @@ -1195,17 +1195,17 @@ static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { * @return paNoError (== paFormatIsSupported) if no errors occur, otherwise returns an appropriate * PaError message. */ -static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, - const PaStreamParameters *i_inputParameters, - const PaStreamParameters *i_outputParameters, - double i_sampleRate) { +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate) { int inputChannelCount, outputChannelCount; PaSampleFormat inputSampleFormat, outputSampleFormat; - auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + auto *oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; - if (i_inputParameters) { - inputChannelCount = i_inputParameters->channelCount; - inputSampleFormat = i_inputParameters->sampleFormat; + if (inputParameters) { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ @@ -1215,21 +1215,21 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) { + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) { return paInvalidDevice; } /* check that input device can support inputChannelCount */ if (inputChannelCount > - i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { + hostApi->deviceInfos[inputParameters->device]->maxInputChannels) { return paInvalidChannelCount; } /* validate inputStreamInfo */ - if (i_inputParameters->hostApiSpecificStreamInfo) { + if (inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. InputPreset androidRecordingPreset = - ((PaOboeStreamInfo *) i_inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + ((PaOboeStreamInfo *) inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; if (androidRecordingPreset != InputPreset::Generic && androidRecordingPreset != InputPreset::Camcorder && androidRecordingPreset != InputPreset::VoiceRecognition && @@ -1242,9 +1242,9 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, inputChannelCount = 0; } - if (i_outputParameters) { - outputChannelCount = i_outputParameters->channelCount; - outputSampleFormat = i_outputParameters->sampleFormat; + if (outputParameters) { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ @@ -1254,20 +1254,20 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) { + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) { return paInvalidDevice; } /* check that output device can support outputChannelCount */ - if (outputChannelCount > i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { + if (outputChannelCount > hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) { return paInvalidChannelCount; } /* validate outputStreamInfo */ - if (i_outputParameters->hostApiSpecificStreamInfo) { + if (outputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. Usage androidOutputUsage = - ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; if (androidOutputUsage != Usage::Media && androidOutputUsage != Usage::Notification && androidOutputUsage != Usage::NotificationEvent && @@ -1284,12 +1284,12 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, } if (outputChannelCount > 0) { - if (IsOutputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + if (IsOutputSampleRateSupported(oboeHostApi, sampleRate) != paNoError) { return paInvalidSampleRate; } } if (inputChannelCount > 0) { - if (IsInputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + if (IsInputSampleRateSupported(oboeHostApi, sampleRate) != paNoError) { return paInvalidSampleRate; } } @@ -1310,13 +1310,13 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, - Usage i_androidOutputUsage, double i_sampleRate) { +static PaError InitializeOutputStream(OboeStream* oboeStream, PaOboeHostApiRepresentation *oboeHostApi, + Usage androidOutputUsage, double sampleRate) { - return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + return oboeHostApi->oboeEngine->openStream(oboeStream, Direction::Output, - i_sampleRate, - i_androidOutputUsage, + sampleRate, + androidOutputUsage, Generic); //Input preset won't be used, so we put the default value. } @@ -1333,14 +1333,14 @@ static PaError InitializeOutputStream(OboeStream* i_oboeStream, PaOboeHostApiRep * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeInputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, - InputPreset i_androidInputPreset, double i_sampleRate) { +static PaError InitializeInputStream(OboeStream* oboeStream, PaOboeHostApiRepresentation *oboeHostApi, + InputPreset androidInputPreset, double sampleRate) { - return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + return oboeHostApi->oboeEngine->openStream(oboeStream, Direction::Input, - i_sampleRate, + sampleRate, Usage::Media, //Usage won't be used, so we put the default value. - i_androidInputPreset); + androidInputPreset); } @@ -1361,17 +1361,17 @@ static PaError InitializeInputStream(OboeStream* i_oboeStream, PaOboeHostApiRepr * manipulation or checks. * @return paNoError if no errors occur, or other error codes accordingly with what goes wrong. */ -static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, - PaStream **i_paStream, - const PaStreamParameters *i_inputParameters, - const PaStreamParameters *i_outputParameters, - double i_sampleRate, - unsigned long i_framesPerBuffer, - PaStreamFlags i_streamFlags, - PaStreamCallback *i_streamCallback, - void *i_userData) { +static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, + PaStream **paStream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData) { PaError error = paNoError; - auto oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + auto oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; unsigned long framesPerHostBuffer; /* these may not be equivalent for all implementations */ int inputChannelCount, outputChannelCount; PaSampleFormat inputSampleFormat, outputSampleFormat; @@ -1390,25 +1390,25 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, LOGI("[PaOboe - OpenStream]\t OpenStream called."); - if (i_inputParameters) { - inputChannelCount = i_inputParameters->channelCount; - inputSampleFormat = i_inputParameters->sampleFormat; + if (inputParameters) { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; /* Oboe supports alternate device specification with API>=28, but here we reject the use of paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via PaOboe_SetSelectedDevice. */ - if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that input device can support inputChannelCount */ - if (inputChannelCount > i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) + if (inputChannelCount > hostApi->deviceInfos[inputParameters->device]->maxInputChannels) return paInvalidChannelCount; /* validate inputStreamInfo */ - if (i_inputParameters->hostApiSpecificStreamInfo) { + if (inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. androidInputPreset = - ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; if (androidInputPreset != InputPreset::Generic && androidInputPreset != InputPreset::Camcorder && androidInputPreset != InputPreset::VoiceRecognition && @@ -1427,25 +1427,25 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->inputFormat = hostInputSampleFormat; } - if (i_outputParameters) { - outputChannelCount = i_outputParameters->channelCount; - outputSampleFormat = i_outputParameters->sampleFormat; + if (outputParameters) { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; /* Oboe supports alternate device specification with API>=28, but here we reject the use of paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via PaOboe_SetSelectedDevice. */ - if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that output device can support outputChannelCount */ if (outputChannelCount > - i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) return paInvalidChannelCount; /* validate outputStreamInfo */ - if (i_outputParameters->hostApiSpecificStreamInfo) { + if (outputParameters->hostApiSpecificStreamInfo) { androidOutputUsage = - ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; if (androidOutputUsage != Usage::Media && androidOutputUsage != Usage::Notification && androidOutputUsage != Usage::NotificationEvent && @@ -1469,32 +1469,32 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, } /* validate platform specific flags */ - if ((i_streamFlags & paPlatformSpecificFlags) != 0) + if ((streamFlags & paPlatformSpecificFlags) != 0) return paInvalidFlag; /* unexpected platform specific flag */ - if (i_framesPerBuffer == paFramesPerBufferUnspecified) { - if (i_outputParameters) { + if (framesPerBuffer == paFramesPerBufferUnspecified) { + if (outputParameters) { framesPerHostBuffer = - (unsigned long) (i_outputParameters->suggestedLatency * i_sampleRate); + (unsigned long) (outputParameters->suggestedLatency * sampleRate); } else { framesPerHostBuffer = - (unsigned long) (i_inputParameters->suggestedLatency * i_sampleRate); + (unsigned long) (inputParameters->suggestedLatency * sampleRate); } } else { - framesPerHostBuffer = i_framesPerBuffer; + framesPerHostBuffer = framesPerBuffer; } - if (i_streamCallback) { + if (streamCallback) { PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), &oboeHostApi->callbackStreamInterface, - i_streamCallback, i_userData); + streamCallback, userData); } else { PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), &oboeHostApi->blockingStreamInterface, - i_streamCallback, i_userData); + streamCallback, userData); } - PaUtil_InitializeCpuLoadMeasurer(&(oboeStream->cpuLoadMeasurer), i_sampleRate); + PaUtil_InitializeCpuLoadMeasurer(&(oboeStream->cpuLoadMeasurer), sampleRate); error = PaUtil_InitializeBufferProcessor(&(oboeStream->bufferProcessor), inputChannelCount, @@ -1503,16 +1503,16 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - i_sampleRate, i_streamFlags, - i_framesPerBuffer, + sampleRate, streamFlags, + framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize, - i_streamCallback, i_userData); + streamCallback, userData); if (error != paNoError) goto error; - oboeStream->streamRepresentation.streamInfo.sampleRate = i_sampleRate; - oboeStream->isBlocking = (i_streamCallback == nullptr); + oboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; + oboeStream->isBlocking = (streamCallback == nullptr); oboeStream->framesPerHostCallback = framesPerHostBuffer; oboeStream->bytesPerFrame = sizeof(int16_t); oboeStream->cbFlags = 0; @@ -1527,9 +1527,9 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->streamRepresentation.streamInfo.inputLatency = ((PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( &(oboeStream->bufferProcessor)) + - oboeStream->framesPerHostCallback) / i_sampleRate; + oboeStream->framesPerHostCallback) / sampleRate; ENSURE(InitializeInputStream(oboeStream, oboeHostApi, - androidInputPreset, i_sampleRate), + androidInputPreset, sampleRate), "Initializing input stream failed") } else { oboeStream->hasInput = false; } @@ -1538,13 +1538,13 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->streamRepresentation.streamInfo.outputLatency = ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( &oboeStream->bufferProcessor) - + oboeStream->framesPerHostCallback) / i_sampleRate; + + oboeStream->framesPerHostCallback) / sampleRate; ENSURE(InitializeOutputStream(oboeStream, oboeHostApi, - androidOutputUsage, i_sampleRate), + androidOutputUsage, sampleRate), "Initializing output stream failed"); } else { oboeStream->hasOutput = false; } - *i_paStream = (PaStream *) oboeStream; + *paStream = (PaStream *) oboeStream; return error; error: @@ -1565,8 +1565,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, * portaudio, which holds the information of our OboeStream. * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. */ -static PaError CloseStream(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError CloseStream(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (!(oboeEngine->closeStream(oboeStream))) @@ -1575,7 +1575,7 @@ static PaError CloseStream(PaStream *i_paStream) { PaUtil_TerminateBufferProcessor(&oboeStream->bufferProcessor); PaUtil_TerminateStreamRepresentation(&oboeStream->streamRepresentation); - for (int i = 0; i < g_numberOfBuffers; ++i) { + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { if (oboeStream->hasOutput) PaUtil_FreeMemory(oboeStream->outputBuffers[i]); if (oboeStream->hasInput) @@ -1599,8 +1599,8 @@ static PaError CloseStream(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::startStream fails. */ -static PaError StartStream(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError StartStream(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); PaUtil_ResetBufferProcessor(&oboeStream->bufferProcessor); @@ -1609,27 +1609,27 @@ static PaError StartStream(PaStream *i_paStream) { //TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) if (oboeStream->isActive) { LOGW("[PaOboe - StartStream]\t Stream was already active, stopping..."); - StopStream(i_paStream); + StopStream(paStream); LOGW("[PaOboe - StartStream]\t Restarting..."); - StartStream(i_paStream); + StartStream(paStream); } oboeStream->currentOutputBuffer = 0; oboeStream->currentInputBuffer = 0; /* Initialize buffers */ - for (int i = 0; i < g_numberOfBuffers; ++i) { + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { if (oboeStream->hasOutput) { memset(oboeStream->outputBuffers[oboeStream->currentOutputBuffer], 0, oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.outputChannelCount); - oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; } if (oboeStream->hasInput) { memset(oboeStream->inputBuffers[oboeStream->currentInputBuffer], 0, oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.inputChannelCount); - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; } } @@ -1657,9 +1657,9 @@ static PaError StartStream(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::stopStream fails. */ -static PaError StopStream(PaStream *i_paStream) { +static PaError StopStream(PaStream *paStream) { PaError error = paNoError; - auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (oboeStream->isStopped) { @@ -1691,9 +1691,9 @@ static PaError StopStream(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::abortStream fails. */ -static PaError AbortStream(PaStream *i_paStream) { +static PaError AbortStream(PaStream *paStream) { PaError error = paNoError; - auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); LOGI("[PaOboe - AbortStream]\t Aborting stream."); @@ -1725,22 +1725,22 @@ static PaError AbortStream(PaStream *i_paStream) { * @param frames is the total number of frames to read. * @return paInternalError if OboeEngine::readStream fails, paNoError otherwise. */ -static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_frames) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError ReadStream(PaStream *paStream, void *buffer, unsigned long frames) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); - void *userBuffer = i_buffer; + void *userBuffer = buffer; unsigned framesToRead; PaError error = paNoError; - while (i_frames > 0) { - framesToRead = PA_MIN(oboeStream->framesPerHostCallback, i_frames); + while (frames > 0) { + framesToRead = PA_MIN(oboeStream->framesPerHostCallback, frames); if (!(oboeEngine->readStream(oboeStream, userBuffer, framesToRead * oboeStream->bufferProcessor.inputChannelCount))) error = paInternalError; - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; - i_frames -= framesToRead; + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; + frames -= framesToRead; } return error; @@ -1755,22 +1755,22 @@ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_ * @param frames is the total number of frames to write. * @return paInternalError if OboeEngine::writeStream fails, paNoError otherwise. */ -static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned long i_frames) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError WriteStream(PaStream *paStream, const void *buffer, unsigned long frames) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); - const void *userBuffer = i_buffer; + const void *userBuffer = buffer; unsigned framesToWrite; PaError error = paNoError; - while (i_frames > 0) { - framesToWrite = PA_MIN(oboeStream->framesPerHostCallback, i_frames); + while (frames > 0) { + framesToWrite = PA_MIN(oboeStream->framesPerHostCallback, frames); if (!(oboeEngine->writeStream(oboeStream, userBuffer, framesToWrite * oboeStream->bufferProcessor.outputChannelCount))) error = paInternalError; - oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; - i_frames -= framesToWrite; + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; + frames -= framesToWrite; } return error; @@ -1785,9 +1785,9 @@ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be read without waiting. */ -static signed long GetStreamReadAvailable(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; - return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentInputBuffer); +static signed long GetStreamReadAvailable(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; + return oboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - oboeStream->currentInputBuffer); } @@ -1797,9 +1797,9 @@ static signed long GetStreamReadAvailable(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be written without waiting. */ -static signed long GetStreamWriteAvailable(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; - return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentOutputBuffer); +static signed long GetStreamWriteAvailable(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; + return oboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - oboeStream->currentOutputBuffer); } @@ -1809,8 +1809,8 @@ static signed long GetStreamWriteAvailable(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is stopped, or zero (0) when the stream is running. */ -static PaError IsStreamStopped(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError IsStreamStopped(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; return oboeStream->isStopped; } @@ -1821,8 +1821,8 @@ static PaError IsStreamStopped(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is active (ie playing or recording audio), or zero (0) otherwise. */ -static PaError IsStreamActive(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError IsStreamActive(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; return oboeStream->isActive; } @@ -1833,7 +1833,7 @@ static PaError IsStreamActive(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return The stream's current time in seconds, or 0 if an error occurred. */ -static PaTime GetStreamTime(PaStream *i_paStream) { +static PaTime GetStreamTime(PaStream *paStream) { return PaUtil_GetTime(); } @@ -1849,8 +1849,8 @@ static PaTime GetStreamTime(PaStream *i_paStream) { * A value of 0.0 will always be returned for a blocking read/write stream, or if an error * occurs. */ -static double GetStreamCpuLoad(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static double GetStreamCpuLoad(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; return PaUtil_GetCpuLoad(&oboeStream->cpuLoadMeasurer); } @@ -1872,29 +1872,29 @@ static unsigned long GetApproximateLowBufferSize() { /*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ -void PaOboe_SetSelectedDevice(Direction i_direction, int32_t i_deviceID) { +void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); - if (i_direction == Direction::Input) - g_inputDeviceId = i_deviceID; + if (direction == Direction::Input) + paOboe_inputDeviceId = deviceID; else - g_outputDeviceId = i_deviceID; + paOboe_outputDeviceId = deviceID; } -void PaOboe_SetPerformanceMode(oboe::Direction i_direction, oboe::PerformanceMode i_performanceMode) { - if (i_direction == Direction::Input) { - g_inputPerfMode = i_performanceMode; +void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode) { + if (direction == Direction::Input) { + paOboe_inputPerfMode = performanceMode; } else { - g_outputPerfMode = i_performanceMode; + paOboe_outputPerfMode = performanceMode; } } -void PaOboe_SetNativeBufferSize(unsigned long i_bufferSize) { - g_nativeBufferSize = i_bufferSize; +void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { + paOboe_nativeBufferSize = bufferSize; } -void PaOboe_SetNumberOfBuffers(unsigned i_numberOfBuffers) { - g_numberOfBuffers = i_numberOfBuffers; +void PaOboe_SetNumberOfBuffers(unsigned numberOfBuffers) { + paOboe_numberOfBuffers = numberOfBuffers; } From 48947c05073a3b85d0795936b2dfaa8c9b0e293f Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 11:02:18 +0200 Subject: [PATCH 086/125] replaced 'm_' prefix with 'm' prefix --- src/hostapi/oboe/pa_oboe.cpp | 215 +++++++++++++++++------------------ 1 file changed, 106 insertions(+), 109 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index ca1571a59..50e43f6dd 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -206,7 +206,7 @@ typedef struct OboeStream { class OboeMediator: public AudioStreamCallback{ public: OboeMediator(OboeStream* oboeStream) { - m_oboeCallbackStream = oboeStream; + mOboeCallbackStream = oboeStream; } //Callback function for non-blocking streams @@ -215,30 +215,30 @@ class OboeMediator: public AudioStreamCallback{ //Callback utils void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; void resetCallbackCounters(); - void setOutputCallback() { m_outputBuilder.setDataCallback(this)->setErrorCallback(this); } - void setInputCallback() { m_inputBuilder.setDataCallback(this)->setErrorCallback(this); } + void setOutputCallback() { mOutputBuilder.setDataCallback(this)->setErrorCallback(this); } + void setInputCallback() { mInputBuilder.setDataCallback(this)->setErrorCallback(this); } - //getter and setter of m_oboeEngine and m_oboeCallbackStream - OboeEngine *getEngine() { return m_oboeEngine; } - void setEngine(OboeEngine *oboeEngine) { m_oboeEngine = oboeEngine; } + //getter and setter of mOboeEngine and mOboeCallbackStream + OboeEngine *getEngine() { return mOboeEngine; } + void setEngine(OboeEngine *oboeEngine) { mOboeEngine = oboeEngine; } - OboeStream *getStreamAddress() { return m_oboeCallbackStream; } - void setCallbackStream(OboeStream *oboeStream) { m_oboeCallbackStream = oboeStream; } + OboeStream *getStreamAddress() { return mOboeCallbackStream; } + void setCallbackStream(OboeStream *oboeStream) { mOboeCallbackStream = oboeStream; } //The only instances of output and input streams that will be used, and their builders - std::shared_ptr m_outputStream; - AudioStreamBuilder m_outputBuilder; - std::shared_ptr m_inputStream; - AudioStreamBuilder m_inputBuilder; + std::shared_ptr mOutputStream; + AudioStreamBuilder mOutputBuilder; + std::shared_ptr mInputStream; + AudioStreamBuilder mInputBuilder; private: - OboeEngine *m_oboeEngine; + OboeEngine *mOboeEngine; //callback utils - OboeStream *m_oboeCallbackStream; - unsigned long m_framesProcessed{}; - PaStreamCallbackTimeInfo m_timeInfo{}; - struct timespec m_timeSpec{}; + OboeStream *mOboeCallbackStream; + unsigned long mFramesProcessed{}; + PaStreamCallbackTimeInfo mTimeInfo{}; + struct timespec mTimeSpec{}; }; @@ -276,10 +276,10 @@ class OboeEngine { void constructOboeStream(OboeStream* oboeStream); private: - std::shared_ptr m_testStream; - AudioStreamBuilder m_testBuilder; + std::shared_ptr mTestStream; + AudioStreamBuilder mTestBuilder; - OboeMediator* m_terminableMediator; + OboeMediator* mTerminableMediator; //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -327,13 +327,13 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan Result result; bool outcome = false; - m_testBuilder.setDeviceId(getSelectedDevice(direction)) + mTestBuilder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) ->setDirection(direction) ->setSampleRate(sampleRate) ->setChannelCount(channelCount) - ->openStream(m_testStream); + ->openStream(mTestStream); if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", @@ -342,23 +342,23 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan } if (sampleRate != kUnspecified) { - outcome = (sampleRate == m_testBuilder.getSampleRate()); + outcome = (sampleRate == mTestBuilder.getSampleRate()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - sampleRate, m_testBuilder.getSampleRate()); + sampleRate, mTestBuilder.getSampleRate()); } } else if (channelCount != kUnspecified) { - outcome = (channelCount == m_testBuilder.getChannelCount()); + outcome = (channelCount == mTestBuilder.getChannelCount()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, m_testBuilder.getChannelCount()); + channelCount, mTestBuilder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); outcome = false; } - m_testStream->close(); + mTestStream->close(); return outcome; } @@ -397,7 +397,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 } if (direction == Direction::Input) { - mediator->m_inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) + mediator->mInputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) ->setSampleRate(sampleRate) ->setDirection(Direction::Input) @@ -410,7 +410,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 mediator->setInputCallback(); } - result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); + result = mediator->mInputBuilder.openStream(mediator->mInputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", @@ -418,7 +418,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 return paUnanticipatedHostError; } - mediator->m_inputStream->setBufferSizeInFrames(mediator->m_inputStream->getFramesPerBurst() * + mediator->minputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * paOboe_numberOfBuffers); oboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); @@ -433,14 +433,14 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 for (int j = 0; j < i; ++j) PaUtil_FreeMemory(oboeStream->inputBuffers[j]); PaUtil_FreeMemory(oboeStream->inputBuffers); - mediator->m_inputStream->close(); + mediator->mInputStream->close(); error = paInsufficientMemory; break; } } oboeStream->currentInputBuffer = 0; } else { - mediator->m_outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) + mediator->mOutputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) ->setSampleRate(sampleRate) ->setDirection(Direction::Output) @@ -453,14 +453,14 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 mediator->setOutputCallback(); } - result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); + result = mediator->mOutputBuilder.openStream(mediator->mOutputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); return paUnanticipatedHostError; } - mediator->m_outputStream->setBufferSizeInFrames(mediator->m_outputStream->getFramesPerBurst() * + mediator->mOutputStream->setBufferSizeInFrames(mediator->mOutputStream->getFramesPerBurst() * paOboe_numberOfBuffers); oboeStream->outputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); @@ -475,7 +475,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 for (int j = 0; j < i; ++j) PaUtil_FreeMemory(oboeStream->outputBuffers[j]); PaUtil_FreeMemory(oboeStream->outputBuffers); - mediator->m_outputStream->close(); + mediator->mOutputStream->close(); error = paInsufficientMemory; break; } @@ -498,13 +498,13 @@ bool OboeEngine::startStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->requestStart(); + inputResult = mediator->mInputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->requestStart(); + outputResult = mediator->mOutputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", convertToText(outputResult)); @@ -525,13 +525,13 @@ bool OboeEngine::stopStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->requestStop(); + inputResult = mediator->mInputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->requestStop(); + outputResult = mediator->mOutputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", convertToText(outputResult)); @@ -556,11 +556,11 @@ bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { switch (direction) { case 1: //output-only - result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); + result = mediator->mOutputBuilder.openStream(mediator->mOutputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", convertToText(result)); - result = mediator->m_outputStream->start(); + result = mediator->mOutputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", convertToText(result)); @@ -569,11 +569,11 @@ bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { break; case 2: //input-only - result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); + result = mediator->mInputBuilder.openStream(mediator->mInputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); - result = mediator->m_inputStream->start(); + result = mediator->mInputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", convertToText(result)); @@ -609,14 +609,14 @@ bool OboeEngine::closeStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->close(); + outputResult = mediator->mOutputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->close(); + inputResult = mediator->mInputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); @@ -643,21 +643,21 @@ bool OboeEngine::abortStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->stop(); + inputResult = mediator->mInputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); - inputResult = mediator->m_inputStream->close(); + inputResult = mediator->mInputStream->close(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->stop(); + outputResult = mediator->mOutputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(outputResult)); - outputResult = mediator->m_outputStream->close(); + outputResult = mediator->mOutputStream->close(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", convertToText(outputResult)); @@ -680,7 +680,7 @@ bool OboeEngine::writeStream(OboeStream *oboeStream, const void buffer, int32_t bool outcome = true; OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_outputStream->write(buffer, framesToWrite, TIMEOUT_NS); + ResultWithValue result = mediator->mOutputStream->write(buffer, framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -709,7 +709,7 @@ bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t frames bool outcome = true; OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_inputStream->read(buffer, framesToRead, TIMEOUT_NS); + ResultWithValue result = mediator->mInputStream->read(buffer, framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -730,7 +730,7 @@ bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t frames * @return the address of the oboeStream. */ void OboeEngine::constructOboeStream(OboeStream* oboeStream) { - m_terminableMediator = oboeStream->oboeMediator = new OboeMediator(oboeStream); + mTerminableMediator = oboeStream->oboeMediator = new OboeMediator(oboeStream); oboeStream->oboeMediator->setEngine(this); } @@ -788,85 +788,85 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { DataCallbackResult OboeMediator::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { - clock_gettime(CLOCK_REALTIME, &m_timeSpec); - m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); - m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback + clock_gettime(CLOCK_REALTIME, &mTimeSpec); + mTimeInfo.currentTime = (PaTime)(mTimeSpec.tv_sec + (mTimeSpec.tv_nsec / 1000000000.0)); + mTimeInfo.outputBufferDacTime = (PaTime)(mOboeCallbackStream->framesPerHostCallback / - m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); - m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback + mOboeCallbackStream->streamRepresentation.streamInfo.sampleRate + + mTimeInfo.currentTime); + mTimeInfo.inputBufferAdcTime = (PaTime)(mOboeCallbackStream->framesPerHostCallback / - m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); + mOboeCallbackStream->streamRepresentation.streamInfo.sampleRate + + mTimeInfo.currentTime); /* check if StopStream or AbortStream was called */ - if (m_oboeCallbackStream->doStop) { - m_oboeCallbackStream->callbackResult = paComplete; - } else if (m_oboeCallbackStream->doAbort) { - m_oboeCallbackStream->callbackResult = paAbort; + if (mOboeCallbackStream->doStop) { + mOboeCallbackStream->callbackResult = paComplete; + } else if (mOboeCallbackStream->doAbort) { + mOboeCallbackStream->callbackResult = paAbort; } - PaUtil_BeginCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&m_oboeCallbackStream->bufferProcessor, - &m_timeInfo, m_oboeCallbackStream->cbFlags); + PaUtil_BeginCpuLoadMeasurement(&mOboeCallbackStream->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&mOboeCallbackStream->bufferProcessor, + &mTimeInfo, mOboeCallbackStream->cbFlags); - if (m_oboeCallbackStream->hasOutput) { - m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = audioData; - PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, numFrames); - PaUtil_SetInterleavedOutputChannels(&m_oboeCallbackStream->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeCallbackStream->outputBuffers)[ - m_oboeCallbackStream->currentOutputBuffer], + if (mOboeCallbackStream->hasOutput) { + mOboeCallbackStream->outputBuffers[mOboeCallbackStream->currentOutputBuffer] = audioData; + PaUtil_SetOutputFrameCount(&mOboeCallbackStream->bufferProcessor, numFrames); + PaUtil_SetInterleavedOutputChannels(&mOboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) mOboeCallbackStream->outputBuffers)[ + mOboeCallbackStream->currentOutputBuffer], 0); } - if (m_oboeCallbackStream->hasInput) { - audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; - PaUtil_SetInputFrameCount(&m_oboeCallbackStream->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&m_oboeCallbackStream->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeCallbackStream->inputBuffers)[ - m_oboeCallbackStream->currentInputBuffer], + if (mOboeCallbackStream->hasInput) { + audioData = mOboeCallbackStream->inputBuffers[mOboeCallbackStream->currentInputBuffer]; + PaUtil_SetInputFrameCount(&mOboeCallbackStream->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&mOboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) mOboeCallbackStream->inputBuffers)[ + mOboeCallbackStream->currentInputBuffer], 0); } /* continue processing user buffers if callback result is paContinue or * if it is paComplete and userBuffers aren't empty yet */ - if (m_oboeCallbackStream->callbackResult == paContinue - || (m_oboeCallbackStream->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeCallbackStream->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeCallbackStream->bufferProcessor, - &m_oboeCallbackStream->callbackResult); + if (mOboeCallbackStream->callbackResult == paContinue + || (mOboeCallbackStream->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&mOboeCallbackStream->bufferProcessor))) { + mFramesProcessed = PaUtil_EndBufferProcessing(&mOboeCallbackStream->bufferProcessor, + &mOboeCallbackStream->callbackResult); } /* enqueue a buffer only when there are frames to be processed, * this will be 0 when paComplete + empty buffers or paAbort */ - if (m_framesProcessed > 0) { - if (m_oboeCallbackStream->hasOutput) { - m_oboeCallbackStream->currentOutputBuffer = - (m_oboeCallbackStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; + if (mFramesProcessed > 0) { + if (mOboeCallbackStream->hasOutput) { + mOboeCallbackStream->currentOutputBuffer = + (mOboeCallbackStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; } - if (m_oboeCallbackStream->hasInput) { - m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; + if (mOboeCallbackStream->hasInput) { + mOboeCallbackStream->currentInputBuffer = (mOboeCallbackStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; } } - PaUtil_EndCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer, m_framesProcessed); + PaUtil_EndCpuLoadMeasurement(&mOboeCallbackStream->cpuLoadMeasurer, mFramesProcessed); /* StopStream was called */ - if (m_framesProcessed == 0 && m_oboeCallbackStream->doStop) { - m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; + if (mFramesProcessed == 0 && mOboeCallbackStream->doStop) { + mOboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; } /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (m_framesProcessed == 0 && !(m_oboeCallbackStream->doAbort || m_oboeCallbackStream->doStop)) { - m_oboeCallbackStream->isActive = false; - m_oboeCallbackStream->isStopped = true; - if (m_oboeCallbackStream->streamRepresentation.streamFinishedCallback != nullptr) - m_oboeCallbackStream->streamRepresentation.streamFinishedCallback( - m_oboeCallbackStream->streamRepresentation.userData); - m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) + else if (mFramesProcessed == 0 && !(mOboeCallbackStream->doAbort || mOboeCallbackStream->doStop)) { + mOboeCallbackStream->isActive = false; + mOboeCallbackStream->isStopped = true; + if (mOboeCallbackStream->streamRepresentation.streamFinishedCallback != nullptr) + mOboeCallbackStream->streamRepresentation.streamFinishedCallback( + mOboeCallbackStream->streamRepresentation.userData); + mOboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) } - return m_oboeCallbackStream->oboeCallbackResult; + return mOboeCallbackStream->oboeCallbackResult; } @@ -879,11 +879,11 @@ void OboeMediator::onErrorAfterClose(AudioStream *audioStream, Result error) { OboeEngine* oboeEngine = getEngine(); LOGW("[OboeMediator::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); int i = 0; - if(m_oboeCallbackStream->hasOutput) + if(mOboeCallbackStream->hasOutput) i++; - if(m_oboeCallbackStream->hasInput) + if(mOboeCallbackStream->hasInput) i+=2; - if (!oboeEngine->restartStream(m_oboeCallbackStream, i)) + if (!oboeEngine->restartStream(mOboeCallbackStream, i)) LOGE("[OboeMediator::onErrorAfterClose]\t Couldn't restart stream(s)"); } else LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); @@ -894,8 +894,8 @@ void OboeMediator::onErrorAfterClose(AudioStream *audioStream, Result error) { * \brief Resets callback counters (called at the start of each iteration of onAudioReady). */ void OboeMediator::resetCallbackCounters() { - m_framesProcessed = 0; - m_timeInfo = {0, 0, 0}; + mFramesProcessed = 0; + mTimeInfo = {0, 0, 0}; } @@ -1880,7 +1880,6 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { paOboe_outputDeviceId = deviceID; } - void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode) { if (direction == Direction::Input) { paOboe_inputPerfMode = performanceMode; @@ -1889,12 +1888,10 @@ void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode } } - void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { paOboe_nativeBufferSize = bufferSize; } - void PaOboe_SetNumberOfBuffers(unsigned numberOfBuffers) { paOboe_numberOfBuffers = numberOfBuffers; } From cc9a83ecedfce04143b2e16e3af4b553f642707f Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 11:25:52 +0200 Subject: [PATCH 087/125] fixed OboeEngine::tryStream as requested --- src/hostapi/oboe/pa_oboe.cpp | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 50e43f6dd..5b0fd683a 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -276,9 +276,6 @@ class OboeEngine { void constructOboeStream(OboeStream* oboeStream); private: - std::shared_ptr mTestStream; - AudioStreamBuilder mTestBuilder; - OboeMediator* mTerminableMediator; //Conversion utils @@ -314,26 +311,28 @@ OboeEngine::OboeEngine() {} /** * \brief Tries to open a stream with the direction direction, sample rate sampleRate and/or - * channel count channelCount. It then checks if the stream was in fact opened with the - * desired settings, and then closes the stream. It's used to see if the requested + * channel count channelCount. It then closes the stream. It's used to see if the requested * parameters are supported by the devices that are going to be used. * @param direction the Direction of the stream; * @param sampleRate the sample rate we want to try; * @param channelCount the channel count we want to try; - * @return true if the requested sample rate / channel count is supported by the device, false if - * they aren't, or if tryStream couldn't open a stream. + * @return true if the stream was opened with Result::OK, false otherwise. */ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { Result result; bool outcome = false; - mTestBuilder.setDeviceId(getSelectedDevice(direction)) + std::shared_ptr stream; + AudioStreamBuilder builder; + + builder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + // FIXME: if needed, modify this format to whatever you require ->setFormat(AudioFormat::Float) ->setDirection(direction) ->setSampleRate(sampleRate) ->setChannelCount(channelCount) - ->openStream(mTestStream); + ->openStream(stream); if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", @@ -341,24 +340,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan return outcome; } - if (sampleRate != kUnspecified) { - outcome = (sampleRate == mTestBuilder.getSampleRate()); - if (!outcome) { - LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - sampleRate, mTestBuilder.getSampleRate()); - } - } else if (channelCount != kUnspecified) { - outcome = (channelCount == mTestBuilder.getChannelCount()); - if (!outcome) { - LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, mTestBuilder.getChannelCount()); - } - } else { - LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); - outcome = false; - } - - mTestStream->close(); + stream->close(); return outcome; } From a56e813ebf697a8f0ef5eb06d2e56bacb7719b1f Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Sat, 30 Sep 2023 01:47:09 +0300 Subject: [PATCH 088/125] PulseAudio Portaudio HostAPI (#336) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for PulseAudio API on Linux. For more information about Pulseaudio visit: https://www.freedesktop.org/wiki/Software/PulseAudio/ --------- Signed-off-by: Mario Kleiner Co-authored-by: sqweek Co-authored-by: Daniel Schürmann Co-authored-by: Mooneer Salem Co-authored-by: Be Co-authored-by: Mario Kleiner --- CMakeLists.txt | 18 + Makefile.in | 7 +- README.md | 1 + cmake/modules/FindPulseAudio.cmake | 147 ++ configure.in | 29 +- include/pa_linux_pulseaudio.h | 79 + include/portaudio.h | 4 +- src/common/pa_hostapi.h | 9 +- src/hostapi/pulseaudio/pa_linux_pulseaudio.c | 1453 +++++++++++++++++ .../pulseaudio/pa_linux_pulseaudio_block.c | 197 +++ .../pa_linux_pulseaudio_block_internal.h | 91 ++ .../pulseaudio/pa_linux_pulseaudio_cb.c | 971 +++++++++++ .../pa_linux_pulseaudio_cb_internal.h | 97 ++ .../pulseaudio/pa_linux_pulseaudio_internal.h | 273 ++++ src/os/unix/pa_unix_hostapis.c | 5 + 15 files changed, 3374 insertions(+), 7 deletions(-) create mode 100644 cmake/modules/FindPulseAudio.cmake create mode 100644 include/pa_linux_pulseaudio.h create mode 100644 src/hostapi/pulseaudio/pa_linux_pulseaudio.c create mode 100644 src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c create mode 100644 src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h create mode 100644 src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c create mode 100644 src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h create mode 100644 src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cefda195..67b30b586 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -424,6 +424,24 @@ elseif(UNIX) target_compile_definitions(PortAudio PUBLIC PA_USE_AUDIOIO=1) set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_AUDIOIO=1") endif() + + find_package(PulseAudio) + cmake_dependent_option(PA_USE_PULSEAUDIO "Enable support for PulseAudio general purpose sound server" ON PulseAudio_FOUND OFF) + if(PA_USE_PULSEAUDIO) + target_link_libraries(PortAudio PRIVATE PulseAudio::PulseAudio) + target_sources(PortAudio PRIVATE + src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c + src/hostapi/pulseaudio/pa_linux_pulseaudio.c + src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c) + + target_compile_definitions(PortAudio PUBLIC PA_USE_PULSEAUDIO=1) + set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_PULSEAUDIO=1") + set(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE} libpulse") + + # needed for PortAudioConfig.cmake so `find_package(PortAudio)` works in downstream projects + install(FILES cmake/modules/FindPulseAudio.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules") + endif() + endif() endif() diff --git a/Makefile.in b/Makefile.in index ef5f2d857..05597cc23 100644 --- a/Makefile.in +++ b/Makefile.in @@ -44,7 +44,7 @@ PALIB = libportaudio.la PAINC = include/portaudio.h PA_LDFLAGS = $(LDFLAGS) $(SHARED_FLAGS) -rpath $(libdir) -no-undefined \ - -export-symbols-regex "(Pa|PaMacCore|PaJack|PaAlsa|PaAsio|PaOSS|PaWasapi|PaWasapiWinrt|PaWinMME)_.*" \ + -export-symbols-regex "(Pa|PaMacCore|PaPulseAudio|PaJack|PaAlsa|PaAsio|PaOSS|PaWasapi|PaWasapiWinrt|PaWinMME)_.*" \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) COMMON_OBJS = \ @@ -66,7 +66,7 @@ LOOPBACK_OBJS = \ qa/loopback/src/test_audio_analyzer.o \ qa/loopback/src/write_wav.o \ qa/loopback/src/paqa.o - + EXAMPLES = \ bin/pa_devs \ bin/pa_fuzz \ @@ -82,7 +82,7 @@ SELFTESTS = \ bin/paqa_devs \ bin/paqa_errs \ bin/paqa_latency - + TESTS = \ bin/patest1 \ bin/patest_buffer \ @@ -146,6 +146,7 @@ SRC_DIRS = \ src/hostapi/coreaudio \ src/hostapi/dsound \ src/hostapi/jack \ + src/hostapi/pulseaudio \ src/hostapi/oss \ src/hostapi/skeleton \ src/hostapi/wasapi \ diff --git a/README.md b/README.md index a908dcc7f..bea6a386b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Please feel free to join. See http://www.portaudio.com for details. src/hostapi/jack = JACK Audio Connection Kit src/hostapi/oboe = Oboe Library for Android (see src/hostapi/oboe/README.md) src/hostapi/oss = Unix Open Sound System (OSS) + src/hostapi/pulseaudio = Sound system for POSIX OSes src/hostapi/wasapi = Windows Vista WASAPI src/hostapi/wdmks = Windows WDM Kernel Streaming src/hostapi/wmme = Windows MultiMedia Extensions (MME) diff --git a/cmake/modules/FindPulseAudio.cmake b/cmake/modules/FindPulseAudio.cmake new file mode 100644 index 000000000..234692c43 --- /dev/null +++ b/cmake/modules/FindPulseAudio.cmake @@ -0,0 +1,147 @@ +# Copyright 2008 Matthias Kretz +# Copyright 2009 Marcus Hufgard +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# SPDX-FileCopyrightText: 2008 Matthias Kretz +# SPDX-FileCopyrightText: 2009 Marcus Hufgard +# +# SPDX-License-Identifier: BSD-3-Clause + +#.rst: +# FindPulseAudio +# -------------- +# +# This is base on +# https://invent.kde.org/frameworks/extra-cmake-modules/-/blob/master/find-modules/FindPulseAudio.cmake +# +# Try to locate the PulseAudio library. +# If found, this will define the following variables: +# +# ``PulseAudio_FOUND`` +# True if the system has the PulseAudio library of at least +# the minimum version specified by either the version parameter +# to find_package() or the variable PulseAudio_MINIMUM_VERSION +# ``PulseAudio_INCLUDE_DIRS`` +# The PulseAudio include directory +# ``PulseAudio_LIBRARIES`` +# The PulseAudio libraries for linking +# ``PulseAudio_MAINLOOP_LIBRARY`` +# The libraries needed to use PulseAudio Mainloop +# ``PulseAudio_VERSION`` +# The version of PulseAudio that was found +# ``PulseAudio_INCLUDE_DIR`` +# Deprecated, use ``PulseAudio_INCLUDE_DIRS`` +# ``PulseAudio_LIBRARY`` +# Deprecated, use ``PulseAudio_LIBRARIES`` +# +# If ``PulseAudio_FOUND`` is TRUE, it will also define the following +# imported target: +# +# ``PulseAudio::PulseAudio`` +# The PulseAudio library +# +# Since 5.41.0. + +# Support PulseAudio_MINIMUM_VERSION for compatibility: +if(NOT PulseAudio_FIND_VERSION) + set(PulseAudio_FIND_VERSION "${PulseAudio_MINIMUM_VERSION}") +endif() + +# the minimum version of PulseAudio we require +if(NOT PulseAudio_FIND_VERSION) + set(PulseAudio_FIND_VERSION "1.0.0") +endif() + +find_package(PkgConfig) +pkg_check_modules(PC_PulseAudio QUIET libpulse>=${PulseAudio_FIND_VERSION}) +pkg_check_modules(PC_PulseAudio_MAINLOOP QUIET libpulse-mainloop-glib) + +find_path(PulseAudio_INCLUDE_DIRS pulse/pulseaudio.h + HINTS + ${PC_PulseAudio_INCLUDEDIR} + ${PC_PulseAudio_INCLUDE_DIRS} + ) + +find_library(PulseAudio_LIBRARIES NAMES pulse libpulse + HINTS + ${PC_PulseAudio_LIBDIR} + ${PC_PulseAudio_LIBRARY_DIRS} + ) + +find_library(PulseAudio_MAINLOOP_LIBRARY + NAMES pulse-mainloop pulse-mainloop-glib libpulse-mainloop-glib + HINTS + ${PC_PulseAudio_LIBDIR} + ${PC_PulseAudio_LIBRARY_DIRS} + ) + +# Store the version number in the cache, +# so we don't have to search every time again: +if(PulseAudio_INCLUDE_DIRS AND NOT PulseAudio_VERSION) + + # get PulseAudio's version from its version.h + file(STRINGS "${PulseAudio_INCLUDE_DIRS}/pulse/version.h" pulse_version_h + REGEX ".*pa_get_headers_version\\(\\).*") + string(REGEX REPLACE ".*pa_get_headers_version\\(\\)\ \\(\"([0-9]+\\.[0-9]+\\.[0-9]+)[^\"]*\"\\).*" "\\1" + _PulseAudio_VERSION "${pulse_version_h}") + + set(PulseAudio_VERSION "${_PulseAudio_VERSION}" + CACHE STRING "Version number of PulseAudio" + FORCE) +endif() + +# Use the new extended syntax of +# find_package_handle_standard_args(), +# which also handles version checking: +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PulseAudio + REQUIRED_VARS PulseAudio_LIBRARIES + PulseAudio_INCLUDE_DIRS + VERSION_VAR PulseAudio_VERSION) + +# Deprecated synonyms +set(PulseAudio_INCLUDE_DIR "${PulseAudio_INCLUDE_DIRS}") +set(PulseAudio_LIBRARY "${PulseAudio_LIBRARIES}") +set(PulseAudio_MAINLOOP_LIBRARY "${PulseAudio_MAINLOOP_LIBRARY}") +set(PulseAudio_FOUND "${PulseAudio_FOUND}") + +if(PulseAudio_FOUND AND NOT TARGET PulseAudio::PulseAudio) + add_library(PulseAudio::PulseAudio UNKNOWN IMPORTED) + set_target_properties(PulseAudio::PulseAudio PROPERTIES + IMPORTED_LOCATION "${PulseAudio_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${PulseAudio_INCLUDE_DIRS}") +endif() + +mark_as_advanced(PulseAudio_INCLUDE_DIRS PulseAudio_INCLUDE_DIR + PulseAudio_LIBRARIES PulseAudio_LIBRARY + PulseAudio_MAINLOOP_LIBRARY PulseAudio_MAINLOOP_LIBRARY) + +include(FeatureSummary) +set_package_properties(PulseAudio PROPERTIES + URL "https://www.freedesktop.org/wiki/Software/PulseAudio" + DESCRIPTION "Sound server, for sound stream routing and mixing") diff --git a/configure.in b/configure.in index d3f4fe198..a8de16aac 100644 --- a/configure.in +++ b/configure.in @@ -41,6 +41,10 @@ AC_ARG_WITH(jack, AS_HELP_STRING([--with-jack], [Enable support for JACK @<:@autodetect@:>@]), [with_jack=$withval]) +AC_ARG_WITH(pulseaudio, + AS_HELP_STRING([--with-pulseaudio], [Enable support for PulseAudio @<:@autodetect@:>@]), + [with_pulseaudio=$withval]) + AC_ARG_WITH(oss, AS_HELP_STRING([--with-oss], [Enable support for OSS @<:@autodetect@:>@]), [with_oss=$withval]) @@ -149,10 +153,17 @@ if test "x$with_oss" != "xno"; then AC_CHECK_LIB(ossaudio, _oss_ioctl, have_libossaudio=yes, have_libossaudio=no) fi fi +if [[ "x$with_jack" != "xno" ] || [ "x$with_pulseaudio" != "xno" ]]; then + PKG_PROG_PKG_CONFIG +fi have_jack=no if test "x$with_jack" != "xno"; then PKG_CHECK_MODULES(JACK, jack, have_jack=yes, have_jack=no) fi +have_pulse=no +if test "x$with_pulseaudio" != "xno"; then + PKG_CHECK_MODULES(PULSE, libpulse, have_pulse=yes, have_pulse=no) +fi dnl sizeof checks: we will need a 16-bit and a 32-bit type @@ -386,11 +397,26 @@ case "${host_os}" in if [[ "$have_jack" = "yes" ] && [ "$with_jack" != "no" ]] ; then DLL_LIBS="$DLL_LIBS $JACK_LIBS" CFLAGS="$CFLAGS $JACK_CFLAGS" - OTHER_OBJS="$OTHER_OBJS src/hostapi/jack/pa_jack.o src/common/pa_ringbuffer.o" + OTHER_OBJS="$OTHER_OBJS src/hostapi/jack/pa_jack.o" INCLUDES="$INCLUDES pa_jack.h" AC_DEFINE(PA_USE_JACK,1) fi + if [[ "$have_pulse" = "yes" ] || [ "$have_jack" = "yes" ]] ; then + OTHER_OBJS="$OTHER_OBJS src/common/pa_ringbuffer.o" + INCLUDES="$INCLUDES pa_linux_pulseaudio.h" + fi + + if [[ "$have_pulse" = "yes" ] && [ "$with_pulse" != "no" ]] ; then + DLL_LIBS="$DLL_LIBS $PULSE_LIBS" + CFLAGS="$CFLAGS $PULSE_CFLAGS" + OTHER_OBJS="$OTHER_OBJS src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.o" + OTHER_OBJS="$OTHER_OBJS src/hostapi/pulseaudio/pa_linux_pulseaudio_block.o" + OTHER_OBJS="$OTHER_OBJS src/hostapi/pulseaudio/pa_linux_pulseaudio.o" + dnl INCLUDES="$INCLUDES pa_pulseaudio.h src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h" + AC_DEFINE(PA_USE_PULSEAUDIO,1) + fi + if [[ "$with_oss" != "no" ]] ; then OTHER_OBJS="$OTHER_OBJS src/hostapi/oss/pa_unix_oss.o" if [[ "$have_libossaudio" = "yes" ]] ; then @@ -489,6 +515,7 @@ case "$target_os" in AudioIO ..................... $have_audioio OSS ......................... $have_oss JACK ........................ $have_jack + PulseAudio .................. $have_pulse ]) ;; esac diff --git a/include/pa_linux_pulseaudio.h b/include/pa_linux_pulseaudio.h new file mode 100644 index 000000000..7c5dfce05 --- /dev/null +++ b/include/pa_linux_pulseaudio.h @@ -0,0 +1,79 @@ +#ifndef PA_LINUX_PULSEAUDIO_H +#define PA_LINUX_PULSEAUDIO_H + +/* + * $Id$ + * PortAudio Portable Real-Time Audio Library + * PulseAudio-specific extensions + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + * @ingroup public_header + * @brief PulseAudio-specific PortAudio API extension header file. + */ + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Renames the PulseAudio description for the source that is opened + * by PortAudio. + * + * @param s The PortAudio stream to operate on. + * @param streamName The new name/description of the source. + * + * @return paNoError on success or the error encountered otherwise. + */ +PaError PaPulseAudio_RenameSource( PaStream *s, const char *streamName ); + +/** + * Renames the PulseAudio description for the sink that is opened + * by PortAudio. + * + * @param s The PortAudio stream to operate on. + * @param streamName The new name/description of the sink. + * + * @return paNoError on success or the error encountered otherwise. + */ +PaError PaPulseAudio_RenameSink( PaStream *s, const char *streamName ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/portaudio.h b/include/portaudio.h index 55beb9552..2f9cb5662 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -273,7 +273,6 @@ PaHostApiIndex Pa_GetDefaultHostApi( void ); @see PaHostApiInfo */ - typedef enum PaHostApiTypeId { paInDevelopment=0, /* use while developing support for a new host API */ @@ -291,7 +290,8 @@ typedef enum PaHostApiTypeId paWASAPI=13, paAudioScienceHPI=14, paAudioIO=15, - paOboe=16 + paPulseAudio=16, + paOboe=17 } PaHostApiTypeId; diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h index 752272f18..1979274b8 100644 --- a/src/common/pa_hostapi.h +++ b/src/common/pa_hostapi.h @@ -67,7 +67,7 @@ are defaulted to 1. #define PA_USE_SKELETON 1 #endif -#if defined(PA_NO_ASIO) || defined(PA_NO_DS) || defined(PA_NO_WMME) || defined(PA_NO_WASAPI) || defined(PA_NO_WDMKS) +#if defined(PA_NO_PULSEAUDIO) || defined(PA_NO_ASIO) || defined(PA_NO_DS) || defined(PA_NO_WMME) || defined(PA_NO_WASAPI) || defined(PA_NO_WDMKS) #error "Portaudio: PA_NO_ is no longer supported, please remove definition and use PA_USE_ instead" #endif @@ -132,6 +132,13 @@ are defaulted to 1. #define PA_USE_JACK 1 #endif +#ifndef PA_USE_PULSEAUDIO +#define PA_USE_PULSEAUDIO 0 +#elif (PA_USE_PULSEAUDIO != 0) && (PA_USE_PULSEAUDIO != 1) +#undef PA_USE_PULSEAUDIO +#define PA_USE_PULSEAUDIO 1 +#endif + #ifndef PA_USE_SGI #define PA_USE_SGI 0 #elif (PA_USE_SGI != 0) && (PA_USE_SGI != 1) diff --git a/src/hostapi/pulseaudio/pa_linux_pulseaudio.c b/src/hostapi/pulseaudio/pa_linux_pulseaudio.c new file mode 100644 index 000000000..27dcdf89e --- /dev/null +++ b/src/hostapi/pulseaudio/pa_linux_pulseaudio.c @@ -0,0 +1,1453 @@ + +/* + * PulseAudio host to play natively in Linux based systems without + * ALSA emulation + * + * Copyright (c) 2014-2023 Tuukka Pasanen + * Copyright (c) 2016 Sqweek + * Copyright (c) 2020 Daniel Schurmann + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup common_src + + @brief PulseAudio implementation of support for a host API. + + This host API implements PulseAudio support for portaudio + it has callbackmode and normal write mode support +*/ + + +#include /* strlen() */ + +#include "pa_linux_pulseaudio_cb_internal.h" +#include "pa_linux_pulseaudio_block_internal.h" + +/* PulseAudio headers */ +#include +#include +#include + +/* This is used to identify process name for PulseAudio. */ +extern char *__progname; + +/* PulseAudio specific functions */ +int PaPulseAudio_CheckConnection( PaPulseAudio_HostApiRepresentation * ptr ) +{ + pa_context_state_t state; + + + /* Sanity check if ptr if NULL don't go anywhere or + * it will SIGSEGV + */ + if( !ptr ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_CheckConnection: Host API is NULL! Can't do anything about it" ); + return -1; + } + + if( !ptr->context || !ptr->mainloop ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_CheckConnection: PulseAudio context or mainloop are NULL" ); + return -1; + } + + state = pa_context_get_state(ptr->context); + + if( !PA_CONTEXT_IS_GOOD(state) ) + { + switch( state ) + { + /* These can be found from + * https://freedesktop.org/software/pulseaudio/doxygen/def_8h.html + */ + + case PA_CONTEXT_UNCONNECTED: + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_CheckConnection: The context hasn't been connected yet (PA_CONTEXT_UNCONNECTED)" ); + break; + + case PA_CONTEXT_FAILED: + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_CheckConnection: The connection failed or was disconnected. (PA_CONTEXT_FAILED)" ); + break; + } + + return -1; + } + return 0; +} + +/* Create HostAPI presensentation */ +PaPulseAudio_HostApiRepresentation *PaPulseAudio_New( void ) +{ + PaPulseAudio_HostApiRepresentation *ptr; + int fd[2] = { -1, -1 }; + char pulseaudioDeviceName[PAPULSEAUDIO_MAX_DEVICENAME]; + + ptr = (PaPulseAudio_HostApiRepresentation *) + PaUtil_AllocateZeroInitializedMemory(sizeof(PaPulseAudio_HostApiRepresentation)); + + /* ptr is NULL if runs out of memory or pointer to allocated memory */ + if( !ptr ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_HostApiRepresentation: Can't allocate memory required for using PulseAudio" ); + return NULL; + } + + /* Make sure we have NULL all struct first */ + memset(ptr, 0x00, sizeof(PaPulseAudio_HostApiRepresentation)); + + ptr->mainloop = pa_threaded_mainloop_new(); + + if( !ptr->mainloop ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_HostApiRepresentation: Can't allocate PulseAudio mainloop" ); + goto fail; + } + + ptr->mainloopApi = pa_threaded_mainloop_get_api(ptr->mainloop); + + /* Use program name as PulseAudio device name */ + snprintf( pulseaudioDeviceName, PAPULSEAUDIO_MAX_DEVICENAME, "%s", __progname ); + + ptr->context = + pa_context_new( pa_threaded_mainloop_get_api(ptr->mainloop), pulseaudioDeviceName ); + + if( !ptr->context ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_HostApiRepresentation: Can't instantiate PulseAudio context" ); + goto fail; + } + + pa_context_set_state_callback( ptr->context, PaPulseAudio_CheckContextStateCb, + ptr ); + + + if( pa_threaded_mainloop_start( ptr->mainloop ) < 0 ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_HostApiRepresentation: PulseAudio can't start mainloop" ); + goto fail; + } + + ptr->deviceCount = 0; + + return ptr; + + fail: + PaPulseAudio_Free( ptr ); + return NULL; +} + +/* Free HostAPI */ +void PaPulseAudio_Free( PaPulseAudio_HostApiRepresentation * ptr ) +{ + + /* Sanity check if ptr if NULL don't go anywhere or + * it will SIGSEGV + */ + if( !ptr ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_Free: Host API is NULL! Can't do anything about it" ); + return; + } + + if( ptr->mainloop ) + { + pa_threaded_mainloop_stop( ptr->mainloop ); + } + + if( ptr->context ) + { + pa_context_disconnect( ptr->context ); + pa_context_unref( ptr->context ); + ptr->context = NULL; + } + + if( ptr->mainloopApi && ptr->timeEvent ) + { + ptr->mainloopApi->time_free( ptr->timeEvent ); + ptr->mainloopApi = NULL; + ptr->timeEvent = NULL; + } + + + if( ptr->mainloop ) + { + pa_threaded_mainloop_free( ptr->mainloop ); + ptr->mainloop = NULL; + } + + PaUtil_FreeMemory( ptr ); +} + +/* If there is drop connection to server this one is called + * in future it should stop the stream also + */ +void PaPulseAudio_CheckContextStateCb( pa_context * c, + void *userdata ) +{ + PaPulseAudio_HostApiRepresentation *ptr = + (PaPulseAudio_HostApiRepresentation *) userdata; + /* If this is null we have big problems and we probably are out of memory */ + if( !c ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_CheckContextStateCb: Invalid context " ); + pa_threaded_mainloop_signal( ptr->mainloop, 0 ); + return; + } + + pa_threaded_mainloop_signal( ptr->mainloop, 0 ); +} + +/* Server info callback */ +void PaPulseAudio_ServerInfoCb( pa_context *c, + const pa_server_info *i, + void *userdata ) +{ + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = + (PaPulseAudio_HostApiRepresentation *) userdata; + + if( !c || !i ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_ServerInfoCb: Invalid context or can't get server info" ); + pa_threaded_mainloop_signal( pulseaudioHostApi->mainloop, 0 ); + return; + } + + pulseaudioHostApi->pulseaudioDefaultSampleSpec = i->sample_spec; + + pa_threaded_mainloop_signal( pulseaudioHostApi->mainloop, 0 ); +} + +/* Function adds device to list. It can be input or output stream + * or in pulseaudio source or sink. + */ +int _PaPulseAudio_AddAudioDevice( PaPulseAudio_HostApiRepresentation *hostapi, + const char *PaPulseAudio_SinkSourceName, + const char *PaPulseAudio_SinkSourceNameDesc, + int inputChannels, + int outputChannels, + double defaultLowInputLatency, + double defaultHighInputLatency, + double defaultLowOutputLatency, + double defaultHighOutputLatency, + const long defaultSampleRate ) +{ + /* These should be at least 1 + * + * Maximun size of string is 1024 (PAPULSEAUDIO_MAX_DEVICENAME) + * which should be mostly suffient even pulseaudio device + * names can be very long + */ + int pulseaudioRealNameSize = strnlen( PaPulseAudio_SinkSourceNameDesc, (PAPULSEAUDIO_MAX_DEVICENAME - 1) ) + 1; + int pulseaudioDeviceNameSize = strnlen( PaPulseAudio_SinkSourceName, (PAPULSEAUDIO_MAX_DEVICENAME - 1) ) + 1; + char *pulseaudioLocalDeviceName = NULL; + + hostapi->deviceInfoArray[hostapi->deviceCount].structVersion = 2; + hostapi->deviceInfoArray[hostapi->deviceCount].hostApi = hostapi->hostApiIndex; + hostapi->pulseaudioDeviceNames[hostapi->deviceCount] = + PaUtil_GroupAllocateZeroInitializedMemory( hostapi->allocations, + pulseaudioRealNameSize ); + pulseaudioLocalDeviceName = PaUtil_GroupAllocateZeroInitializedMemory( hostapi->allocations, + pulseaudioDeviceNameSize ); + if( !hostapi->pulseaudioDeviceNames[hostapi->deviceCount] && + !pulseaudioLocalDeviceName ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "_PaPulseAudio_AddAudioDevice: Can't alloc memory" ); + return paInsufficientMemory; + } + + /* We can maximum have 1024 (PAPULSEAUDIO_MAX_DEVICECOUNT) + * devices where to choose which should be mostly enough + */ + if( hostapi->deviceCount >= PAPULSEAUDIO_MAX_DEVICECOUNT ) + { + return paDeviceUnavailable; + } + + snprintf( hostapi->pulseaudioDeviceNames[hostapi->deviceCount], + pulseaudioRealNameSize, + "%s", + PaPulseAudio_SinkSourceNameDesc ); + snprintf( pulseaudioLocalDeviceName, + pulseaudioDeviceNameSize, + "%s", + PaPulseAudio_SinkSourceName ); + + + hostapi->deviceInfoArray[hostapi->deviceCount].name = pulseaudioLocalDeviceName; + + hostapi->deviceInfoArray[hostapi->deviceCount].maxInputChannels = inputChannels; + hostapi->deviceInfoArray[hostapi->deviceCount].maxOutputChannels = outputChannels; + hostapi->deviceInfoArray[hostapi->deviceCount].defaultLowInputLatency = defaultLowInputLatency; + hostapi->deviceInfoArray[hostapi->deviceCount].defaultLowOutputLatency = defaultLowOutputLatency; + hostapi->deviceInfoArray[hostapi->deviceCount].defaultHighInputLatency = defaultHighInputLatency; + hostapi->deviceInfoArray[hostapi->deviceCount].defaultHighOutputLatency = defaultHighOutputLatency; + hostapi->deviceInfoArray[hostapi->deviceCount].defaultSampleRate = defaultSampleRate; + hostapi->deviceCount++; + + return paNoError; +} + +/* Called when iterating through sinks */ +void PaPulseAudio_SinkListCb( pa_context * c, + const pa_sink_info * l, + int eol, + void *userdata ) +{ + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = + (PaPulseAudio_HostApiRepresentation *) userdata; + const char *pulseaudioDeviceName = NULL; + + + /* If this is null we have big problems and we probably are out of memory */ + if( !c || !l ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_SinkListCb: Invalid context or sink info" ); + goto error; + } + + /* If eol is set to a positive number, you're at the end of the list */ + if( eol > 0 ) + { + goto error; + } + + pulseaudioDeviceName = l->name; + + if( l->description != NULL ) + { + pulseaudioDeviceName = l->description; + } + + if( _PaPulseAudio_AddAudioDevice( pulseaudioHostApi, + pulseaudioDeviceName, + l->name, + 0, + l->sample_spec.channels, + 0, + 0, + PA_PULSEAUDIO_DEFAULT_MIN_LATENCY, + PA_PULSEAUDIO_DEFAULT_MAX_LATENCY, + l->sample_spec.rate ) != paNoError ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_SinkListCb: Can't add device. Maximum amount reached!" ); + } + + error: + pa_threaded_mainloop_signal( pulseaudioHostApi->mainloop, + 0 ); +} + +/* Called when iterating through sources */ +void PaPulseAudio_SourceListCb( pa_context * c, + const pa_source_info * l, + int eol, + void *userdata ) +{ + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = + (PaPulseAudio_HostApiRepresentation *) userdata; + const char *pulseaudioDeviceName = NULL; + + + /* If this is null we have big problems and we probably are out of memory */ + if( !c ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_SourceListCb: Invalid context" ); + goto error; + } + + /* If eol is set to a positive number, you're at the end of the list */ + if( eol > 0 ) + { + goto error; + } + + pulseaudioDeviceName = l->name; + + if( l->description != NULL ) + { + pulseaudioDeviceName = l->description; + } + + if( _PaPulseAudio_AddAudioDevice( pulseaudioHostApi, + pulseaudioDeviceName, + l->name, + l->sample_spec.channels, + 0, + PA_PULSEAUDIO_DEFAULT_MIN_LATENCY, + PA_PULSEAUDIO_DEFAULT_MAX_LATENCY, + 0, + 0, + l->sample_spec.rate ) != paNoError ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_SourceListCb: Can't add device. Maximum amount reached!" ); + } + + error: + pa_threaded_mainloop_signal( pulseaudioHostApi->mainloop, + 0 ); +} + +/* This routine is called whenever the stream state changes */ +void PaPulseAudio_StreamStateCb( pa_stream * s, + void *userdata ) +{ + const pa_buffer_attr *pulseaudioBufferAttr = NULL; + /* If you need debug pring enable these + * char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX]; + */ + + + /* If this is null we have big problems and we probably are out of memory */ + if( !s ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_StreamStateCb: Invalid stream" ); + return; + } + + switch( pa_stream_get_state(s) ) + { + case PA_STREAM_TERMINATED: + break; + + case PA_STREAM_CREATING: + break; + + case PA_STREAM_READY: + if (!(pulseaudioBufferAttr = pa_stream_get_buffer_attr(s))) + { + PA_DEBUG( ("Portaudio %s: Can get buffer attr: '%s'\n", + __FUNCTION__, + pa_strerror(pa_context_errno(pa_stream_get_context(s) ) )) ); + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_StreamStateCb: Can't get Stream pa_buffer_attr" ); + } + else + { + PA_DEBUG( ("%s: %s Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u, fragsize=%u\n", + __FUNCTION__, pa_stream_get_device_name(s), + pulseaudioBufferAttr->maxlength, pulseaudioBufferAttr->tlength, pulseaudioBufferAttr->prebuf, + pulseaudioBufferAttr->minreq, pulseaudioBufferAttr->maxlength, pulseaudioBufferAttr->fragsize) ); + } + break; + + case PA_STREAM_FAILED: + default: + PA_DEBUG( ("Portaudio %s: FAILED '%s'\n", + __FUNCTION__, + pa_strerror( pa_context_errno( pa_stream_get_context( s ) ) )) ); + + break; + } +} + +/* If stream is underflowed then this callback is called + * one needs to enable debug to make use os this + * + * Otherwise it's used to update error message + */ +void PaPulseAudio_StreamUnderflowCb( pa_stream *s, + void *userdata ) +{ + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) userdata; + pa_buffer_attr *pulseaudioOutputSampleSpec = NULL; + + /* If this is null we have big problems and we probably are out of memory */ + if( !s ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_StreamUnderflowCb: Invalid stream" ); + return; + } + + stream->outputUnderflows++; + pulseaudioOutputSampleSpec = (pa_buffer_attr *)pa_stream_get_buffer_attr(s); + PA_DEBUG( ("Portaudio %s: PulseAudio '%s' with delay: %ld stream has underflowed\n", __FUNCTION__, pa_stream_get_device_name(s), pulseaudioOutputSampleSpec->tlength) ); + + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_StreamUnderflowCb: Pulseaudio stream underflow"); + + pa_threaded_mainloop_signal( stream->mainloop, + 0 ); +} + +/* Initialize HostAPI */ +PaError PaPulseAudio_Initialize( PaUtilHostApiRepresentation ** hostApi, + PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i; + int deviceCount; + int ret = 0; + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = NULL; + PaDeviceInfo *deviceInfoArray = NULL; + + pa_operation *pulseaudioOperation = NULL; + + pulseaudioHostApi = PaPulseAudio_New(); + + if( !pulseaudioHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + pulseaudioHostApi->allocations = PaUtil_CreateAllocationGroup(); + + if( !pulseaudioHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + pulseaudioHostApi->hostApiIndex = hostApiIndex; + *hostApi = &pulseaudioHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paPulseAudio; + (*hostApi)->info.name = "PulseAudio"; + + (*hostApi)->info.defaultInputDevice = paNoDevice; + (*hostApi)->info.defaultOutputDevice = paNoDevice; + + /* Connect to server */ + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + ret = pa_context_connect( pulseaudioHostApi->context, + NULL, + 0, + NULL ); + + if( ret < 0 ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PulseAudio_Initialize: Can't connect to server"); + result = paNotInitialized; + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + goto error; + } + + ret = 0; + + /* We should wait that PulseAudio server let us in or fails us */ + while( !ret ) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + + switch( pa_context_get_state( pulseaudioHostApi->context ) ) + { + case PA_CONTEXT_READY: + ret = 1; + break; + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + goto error; + break; + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } + } + + memset( pulseaudioHostApi->deviceInfoArray, + 0x00, + sizeof(PaDeviceInfo) * PAPULSEAUDIO_MAX_DEVICECOUNT ); + for (i = 0; i < PAPULSEAUDIO_MAX_DEVICECOUNT; i++) + { + pulseaudioHostApi->pulseaudioDeviceNames[i] = NULL; + } + + /* Get info about server. This returns Default sink and soure name. */ + pulseaudioOperation = + pa_context_get_server_info( pulseaudioHostApi->context, + PaPulseAudio_ServerInfoCb, + pulseaudioHostApi ); + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING ) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + } + + pa_operation_unref( pulseaudioOperation ); + + /* Add the "Default" sink at index 0 */ + if( _PaPulseAudio_AddAudioDevice( pulseaudioHostApi, + "Default Sink", + "The PulseAudio default sink", + 0, + PA_CHANNELS_MAX, + 0, + 0, + PA_PULSEAUDIO_DEFAULT_MIN_LATENCY, + PA_PULSEAUDIO_DEFAULT_MAX_LATENCY, + pulseaudioHostApi->pulseaudioDefaultSampleSpec.rate ) != paNoError ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_SinkListCb: Can't add device. Maximum amount reached!" ); + } else { + pulseaudioHostApi->inheritedHostApiRep.info.defaultOutputDevice = + pulseaudioHostApi->deviceCount - 1; + } + + /* Add the "Default" source at index 1 */ + if( _PaPulseAudio_AddAudioDevice( pulseaudioHostApi, + "Default Source", + "The PulseAudio default source", + PA_CHANNELS_MAX, + 0, + PA_PULSEAUDIO_DEFAULT_MIN_LATENCY, + PA_PULSEAUDIO_DEFAULT_MAX_LATENCY, + 0, + 0, + pulseaudioHostApi->pulseaudioDefaultSampleSpec.rate ) != paNoError ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_SinkListCb: Can't add device. Maximum amount reached!" ); + } else { + pulseaudioHostApi->inheritedHostApiRep.info.defaultInputDevice = + pulseaudioHostApi->deviceCount - 1; + } + + /* List PulseAudio sinks. If found callback: PaPulseAudio_SinkListCb */ + pulseaudioOperation = + pa_context_get_sink_info_list( pulseaudioHostApi->context, + PaPulseAudio_SinkListCb, + pulseaudioHostApi ); + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING ) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + } + + pa_operation_unref( pulseaudioOperation ); + + /* List PulseAudio sources. If found callback: PaPulseAudio_SourceListCb */ + pulseaudioOperation = + pa_context_get_source_info_list( pulseaudioHostApi->context, + PaPulseAudio_SourceListCb, + pulseaudioHostApi ); + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING ) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + } + + pa_operation_unref( pulseaudioOperation ); + + (*hostApi)->info.deviceCount = pulseaudioHostApi->deviceCount; + + if( pulseaudioHostApi->deviceCount > 0 ) + { + /* If you have over 1024 Audio devices.. shame on you! */ + + (*hostApi)->deviceInfos = + (PaDeviceInfo **) + PaUtil_GroupAllocateZeroInitializedMemory( pulseaudioHostApi->allocations, + sizeof(PaDeviceInfo *) * + pulseaudioHostApi->deviceCount ); + + if( !(*hostApi)->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + for ( i = 0; i < pulseaudioHostApi->deviceCount; i++ ) + { + (*hostApi)->deviceInfos[i] = + &pulseaudioHostApi->deviceInfoArray[i]; + } + } + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &pulseaudioHostApi->callbackStreamInterface, + PaPulseAudio_CloseStreamCb, + PaPulseAudio_StartStreamCb, + PaPulseAudio_StopStreamCb, + PaPulseAudio_AbortStreamCb, + IsStreamStopped, + IsStreamActive, + GetStreamTime, + GetStreamCpuLoad, + PaUtil_DummyRead, + PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, + PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &pulseaudioHostApi->blockingStreamInterface, + PaPulseAudio_CloseStreamCb, + PaPulseAudio_StartStreamCb, + PaPulseAudio_StopStreamCb, + PaPulseAudio_AbortStreamCb, + IsStreamStopped, + IsStreamActive, + GetStreamTime, + PaUtil_DummyGetCpuLoad, + PaPulseAudio_ReadStreamBlock, + PaPulseAudio_WriteStreamBlock, + PaPulseAudio_GetStreamReadAvailableBlock, + PaUtil_DummyGetWriteAvailable ); + + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + return result; + + error: + + if( pulseaudioHostApi ) + { + if( pulseaudioHostApi->allocations ) + { + PaUtil_FreeAllAllocations( pulseaudioHostApi->allocations ); + PaUtil_DestroyAllocationGroup( pulseaudioHostApi->allocations ); + } + + PaPulseAudio_Free( pulseaudioHostApi ); + pulseaudioHostApi = NULL; + } + + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + return result; +} + +/* Drop stream now */ +void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = + (PaPulseAudio_HostApiRepresentation *) hostApi; + + if( pulseaudioHostApi->allocations ) + { + PaUtil_FreeAllAllocations( pulseaudioHostApi->allocations ); + PaUtil_DestroyAllocationGroup( pulseaudioHostApi->allocations ); + } + + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + pa_context_disconnect( pulseaudioHostApi->context ); + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + + PaPulseAudio_Free( pulseaudioHostApi ); +} + +/* Checks from pulseaudio that is format supported */ +PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters * inputParameters, + const PaStreamParameters * outputParameters, + double sampleRate ) +{ + int inputChannelCount, + outputChannelCount; + PaSampleFormat inputSampleFormat, + outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + * this implementation doesn't support any custom sample formats */ + if( inputSampleFormat & paCustomFormat ) + { + return paSampleFormatNotSupported; + } + + /* unless alternate device specification is supported, reject the use of + * paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + return paInvalidDevice; + } + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > + hostApi->deviceInfos[inputParameters->device]->maxInputChannels ) + { + return paInvalidChannelCount; + } + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + { + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + + } + + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + * this implementation doesn't support any custom sample formats + */ + if( outputSampleFormat & paCustomFormat ) + { + return paSampleFormatNotSupported; + } + + /* unless alternate device specification is supported, reject the use of + * paUseHostApiSpecificDeviceSpecification + */ + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + return paInvalidDevice; + } + + /* check that output device can support outputChannelCount */ + if( outputChannelCount > + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels ) + { + return paInvalidChannelCount; + } + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + { + /* this implementation doesn't use custom stream info */ + return paIncompatibleHostApiSpecificStreamInfo; + } + + } + + else + { + outputChannelCount = 0; + } + + return paFormatIsSupported; +} + +/* Makes conversion from portaudio to pulseaudio sample defines + * Little endian formats are used (if there is some mystical big endian + * sound device this should be fixed but until then it's safe to believe + * this works + */ +PaError PaPulseAudio_ConvertPortaudioFormatToPaPulseAudio_( PaSampleFormat portaudiosf, + pa_sample_spec * pulseaudiosf ) +{ + switch( portaudiosf ) + { + case paFloat32: + pulseaudiosf->format = PA_SAMPLE_FLOAT32LE; + break; + + case paInt32: + pulseaudiosf->format = PA_SAMPLE_S32LE; + break; + + case paInt24: + pulseaudiosf->format = PA_SAMPLE_S24LE; + break; + + case paInt16: + pulseaudiosf->format = PA_SAMPLE_S16LE; + break; + + case paInt8: + pulseaudiosf->format = PA_SAMPLE_U8; + break; + + case paUInt8: + pulseaudiosf->format = PA_SAMPLE_U8; + break; + + case paCustomFormat: + case paNonInterleaved: + PA_DEBUG(("PaPulseAudio %s: THIS IS NOT SUPPORTED BY PULSEAUDIO!\n", + __FUNCTION__)); + return paSampleFormatNotSupported; + break; + } + + return paNoError; +} + + +/* Allocate buffer. */ +PaError PaPulseAudio_BlockingInitRingBuffer( PaUtilRingBuffer * rbuf, + int size ) +{ + char *ringbufferBuffer = (char *) malloc( size ); + PaError ret = paNoError; + + if( ringbufferBuffer == NULL ) + { + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_BlockingInitRingBuffer: Not enough memory to handle request" ); + return paInsufficientMemory; + } + + memset( ringbufferBuffer, + 0x00, + size ); + + ret = PaUtil_InitializeRingBuffer( rbuf, + 1, + size, + ringbufferBuffer ); + + if( ret < paNoError ) + { + free( ringbufferBuffer ); + PA_DEBUG( ("Portaudio %s: Can't initialize input ringbuffer with size: %ld!\n", + __FUNCTION__, size) ); + PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0, + "PaPulseAudio_BlockingInitRingBuffer: Can't initialize input ringbuffer" ); + + return paNotInitialized; + } + + return paNoError; +} + +/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ + +PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream ** s, + const PaStreamParameters * inputParameters, + const PaStreamParameters * outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback * streamCallback, + void *userData ) +{ + PaError result = paNoError; + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = + (PaPulseAudio_HostApiRepresentation *) hostApi; + PaPulseAudio_Stream *stream = NULL; + unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */ + int inputChannelCount, + outputChannelCount; + PaSampleFormat inputSampleFormat, + outputSampleFormat; + PaSampleFormat hostInputSampleFormat, + hostOutputSampleFormat; + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + { + return paInvalidFlag; + } + + /* This is something that Pulseaudio can handle + * and it's also bearable small + */ + if( framesPerBuffer == paFramesPerBufferUnspecified ) + { + framesPerBuffer = PAPULSEAUDIO_FRAMESPERBUFFERUNSPEC; + } + + PaPulseAudio_Lock(pulseaudioHostApi->mainloop); + stream = + (PaPulseAudio_Stream *) PaUtil_AllocateZeroInitializedMemory( sizeof( PaPulseAudio_Stream ) ); + + if( !stream ) + { + result = paInsufficientMemory; + goto openstream_error; + } + + /* Allocate memory for source and sink names. */ + const char defaultSourceStreamName[] = "Portaudio source"; + const char defaultSinkStreamName[] = "Portaudio sink"; + + stream->framesPerHostCallback = framesPerBuffer; + stream->inputStreamName = (char*)PaUtil_AllocateZeroInitializedMemory( sizeof( defaultSourceStreamName ) ); + stream->outputStreamName = (char*)PaUtil_AllocateZeroInitializedMemory( sizeof( defaultSinkStreamName ) ); + if ( !stream->inputStreamName || !stream->outputStreamName ) + { + result = paInsufficientMemory; + goto openstream_error; + } + + /* Copy initial stream names to memory. */ + memcpy( stream->inputStreamName, defaultSourceStreamName, sizeof( defaultSourceStreamName ) ); + memcpy( stream->outputStreamName, defaultSinkStreamName, sizeof( defaultSinkStreamName ) ); + + stream->isActive = 0; + stream->isStopped = 1; + stream->pulseaudioIsActive = 0; + stream->pulseaudioIsStopped = 1; + + stream->inputStream = NULL; + stream->outputStream = NULL; + memset( &stream->inputRing, + 0x00, + sizeof( PaUtilRingBuffer ) ); + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + * paUseHostApiSpecificDeviceSpecification + */ + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + result = paInvalidDevice; + goto openstream_error; + } + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > + hostApi->deviceInfos[inputParameters->device]->maxInputChannels ) + { + result = paInvalidChannelCount; + goto openstream_error; + } + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + { + /* this implementation doesn't use custom stream info */ + result = paIncompatibleHostApiSpecificStreamInfo; + goto openstream_error; + } + + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( inputSampleFormat, + inputSampleFormat ); + + stream->inputFrameSize = Pa_GetSampleSize( inputSampleFormat ) * inputChannelCount; + + result = PaPulseAudio_ConvertPortaudioFormatToPaPulseAudio_( + hostInputSampleFormat, + &stream->inputSampleSpec ); + + if( result != paNoError ) + { + goto openstream_error; + } + + stream->inputSampleSpec.rate = sampleRate; + stream->inputSampleSpec.channels = inputChannelCount; + stream->inputChannelCount = inputChannelCount; + + if( !pa_sample_spec_valid( &stream->inputSampleSpec ) ) + { + PA_DEBUG( ("Portaudio %s: Invalid input audio spec!\n", + __FUNCTION__) ); + result = paUnanticipatedHostError; /* should have been caught above */ + goto openstream_error; + } + + stream->inputStream = + pa_stream_new( pulseaudioHostApi->context, + stream->inputStreamName, + &stream->inputSampleSpec, + NULL ); + + if( stream->inputStream != NULL ) + { + pa_stream_set_state_callback( stream->inputStream, + PaPulseAudio_StreamStateCb, + stream); + pa_stream_set_started_callback( stream->inputStream, + PaPulseAudio_StreamStartedCb, + stream ); + } + else + { + PA_DEBUG( ("Portaudio %s: Can't alloc input stream!\n", + __FUNCTION__) ); + } + + stream->inputDevice = inputParameters->device; + + /* + * This is too much as most of the time there is not much + * stuff in buffer but it's enough if we are doing blocked + * and reading is somewhat slower than callback + */ + result = PaPulseAudio_BlockingInitRingBuffer( &stream->inputRing, + (65536 * 4) ); + if( result != paNoError ) + { + goto openstream_error; + } + + } + + else + { + inputChannelCount = 0; + inputSampleFormat = hostInputSampleFormat = paFloat32; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + * paUseHostApiSpecificDeviceSpecification + */ + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + result = paInvalidDevice; + goto openstream_error; + } + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels ) + { + result = paInvalidChannelCount; + goto openstream_error; + } + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + { + result = paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + goto openstream_error; + } + + /* IMPLEMENT ME - establish which host formats are available */ + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( outputSampleFormat + /* native formats */ , + outputSampleFormat ); + + stream->outputFrameSize = + Pa_GetSampleSize( outputSampleFormat ) * outputChannelCount; + + result = PaPulseAudio_ConvertPortaudioFormatToPaPulseAudio_( + hostOutputSampleFormat, + &stream->outputSampleSpec ); + + if( result != paNoError ) + { + goto openstream_error; + } + + stream->outputSampleSpec.rate = sampleRate; + stream->outputSampleSpec.channels = outputChannelCount; + stream->outputChannelCount = outputChannelCount; + + if( !pa_sample_spec_valid( &stream->outputSampleSpec ) ) + { + PA_DEBUG( ("Portaudio %s: Invalid audio spec for output!\n", + __FUNCTION__) ); + result = paUnanticipatedHostError; /* should have been caught above */ + goto openstream_error; + } + + stream->outputStream = + pa_stream_new( pulseaudioHostApi->context, + stream->outputStreamName, + &stream->outputSampleSpec, + NULL ); + + if( stream->outputStream != NULL ) + { + pa_stream_set_state_callback( stream->outputStream, + PaPulseAudio_StreamStateCb, + stream ); + pa_stream_set_started_callback( stream->outputStream, + PaPulseAudio_StreamStartedCb, + stream ); + } + + else + { + PA_DEBUG( ("Portaudio %s: Can't alloc output stream!\n", + __FUNCTION__) ); + } + + if( result != paNoError ) + { + goto openstream_error; + } + + if( result != paNoError ) + { + goto openstream_error; + } + + stream->outputDevice = outputParameters->device; + } + + else + { + outputChannelCount = 0; + outputSampleFormat = hostOutputSampleFormat = paFloat32; + } + + stream->hostapi = pulseaudioHostApi; + stream->context = pulseaudioHostApi->context; + stream->mainloop = pulseaudioHostApi->mainloop; + + if( streamCallback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &pulseaudioHostApi->callbackStreamInterface, + streamCallback, + userData ); + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &pulseaudioHostApi->blockingStreamInterface, + streamCallback, + userData ); + } + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, + sampleRate + ); + + /* we assume a fixed host buffer size in this example, but the buffer processor + * can also support bounded and unknown host buffer sizes by passing + * paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of + * paUtilFixedHostBufferSize below. */ + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, + inputSampleFormat, + hostInputSampleFormat, + outputChannelCount, + outputSampleFormat, + hostOutputSampleFormat, + sampleRate, + streamFlags, + framesPerBuffer, + framesPerHostBuffer, + paUtilUnknownHostBufferSize, + streamCallback, + userData ); + + if( result != paNoError ) + { + goto openstream_error; + } + + /* inputLatency is specified in _seconds_ */ + stream->streamRepresentation.streamInfo.inputLatency = + (PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( + &stream->bufferProcessor ) / sampleRate; + + /* outputLatency is specified in _seconds_ */ + stream->streamRepresentation.streamInfo.outputLatency = + (PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( + &stream->bufferProcessor ) / sampleRate; + + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + stream->maxFramesHostPerBuffer = framesPerBuffer; + stream->maxFramesPerBuffer = framesPerBuffer; + + *s = (PaStream *) stream; + + openstream_end: + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + return result; + + openstream_error: + + if( stream ) + { + PaUtil_FreeMemory( stream->inputStreamName ); + PaUtil_FreeMemory( stream->outputStreamName ); + PaUtil_FreeMemory( stream ); + } + + goto openstream_end; +} + +PaError IsStreamStopped( PaStream * s ) +{ + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + return stream->isStopped; +} + + +PaError IsStreamActive( PaStream * s ) +{ + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + return stream->isActive; +} + + +PaTime GetStreamTime( PaStream * s ) +{ + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = stream->hostapi; + PaStreamCallbackTimeInfo timeInfo = { 0, 0, 0 }; + + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + + if( stream->outputStream ) + { + if( PaPulseAudio_updateTimeInfo( stream->outputStream, + &timeInfo, + 0 ) == -PA_ERR_NODATA ) + { + return 0; + } + } + + if( stream->inputStream ) + { + if( PaPulseAudio_updateTimeInfo( stream->inputStream, + &timeInfo, + 1 ) == -PA_ERR_NODATA ) + { + return 0; + } + } + + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + return timeInfo.currentTime; +} + + +double GetStreamCpuLoad( PaStream * s ) +{ + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + +/** Extensions */ +static void RenameStreamCb(pa_stream *s, int success, void *userdata) +{ + /* Currently does nothing but signal the caller. */ + PaPulseAudio_Stream *l_ptrStream = (PaPulseAudio_Stream *) userdata; + pa_threaded_mainloop_signal( l_ptrStream->mainloop, + 0 ); +} + +PaError PaPulseAudio_RenameSource( PaStream *s, const char *streamName ) +{ + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + PaError result = paNoError; + pa_operation *op = NULL; + + if ( stream->inputStream == NULL ) + { + return paInvalidDevice; + } + + /* Reallocate stream name in memory. */ + PaPulseAudio_Lock( stream->mainloop ); + char *newStreamName = (char*)PaUtil_AllocateZeroInitializedMemory( strnlen( streamName, PAPULSEAUDIO_MAX_DEVICENAME ) + 1 ); + if ( !newStreamName ) + { + PaPulseAudio_UnLock( stream->mainloop ); + return paInsufficientMemory; + } + snprintf( newStreamName, strnlen( streamName, PAPULSEAUDIO_MAX_DEVICENAME ) + 1, "%s", streamName ); + + PaUtil_FreeMemory( stream->inputStreamName ); + stream->inputStreamName = newStreamName; + + op = pa_stream_set_name( stream->inputStream, streamName, RenameStreamCb, stream ); + PaPulseAudio_UnLock( stream->mainloop ); + + /* Wait for completion. */ + while (pa_operation_get_state( op ) == PA_OPERATION_RUNNING) + { + pa_threaded_mainloop_wait( stream->mainloop ); + } + + return result; +} + +PaError PaPulseAudio_RenameSink( PaStream *s, const char *streamName ) +{ + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + PaError result = paNoError; + pa_operation *op = NULL; + + if ( stream->outputStream == NULL ) + { + return paInvalidDevice; + } + + /* Reallocate stream name in memory. */ + PaPulseAudio_Lock( stream->mainloop ); + char *newStreamName = (char*)PaUtil_AllocateZeroInitializedMemory( strnlen( streamName, PAPULSEAUDIO_MAX_DEVICENAME ) + 1 ); + if ( !newStreamName ) + { + PaPulseAudio_UnLock( stream->mainloop ); + return paInsufficientMemory; + } + snprintf( newStreamName, strnlen( streamName, PAPULSEAUDIO_MAX_DEVICENAME ) + 1, "%s", streamName ); + + PaUtil_FreeMemory( stream->outputStreamName ); + stream->outputStreamName = newStreamName; + + op = pa_stream_set_name( stream->outputStream, streamName, RenameStreamCb, stream ); + PaPulseAudio_UnLock( stream->mainloop ); + + /* Wait for completion. */ + while (pa_operation_get_state( op ) == PA_OPERATION_RUNNING) + { + pa_threaded_mainloop_wait( stream->mainloop ); + } + + return result; +} diff --git a/src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c b/src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c new file mode 100644 index 000000000..3ef9b458c --- /dev/null +++ b/src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c @@ -0,0 +1,197 @@ + +/* + * PulseAudio host to play natively in Linux based systems without + * ALSA emulation + * + * Copyright (c) 2014-2023 Tuukka Pasanen + * Copyright (c) 2016 Sqweek + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup common_src + + @brief PulseAudio implementation of support for a host API. + + This host API implements PulseAudio support for portaudio + it has callbackmode and normal write mode support +*/ + +#include "pa_linux_pulseaudio_block_internal.h" +#include + +/* + As separate stream interfaces are used for blocking and callback + streams, the following functions can be guaranteed to only be called + for blocking streams. +*/ + +PaError PaPulseAudio_ReadStreamBlock( PaStream * s, + void *buffer, + unsigned long frames ) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) s; + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = + pulseaudioStream->hostapi; + PaError ret = 0; + uint8_t *readableBuffer = (uint8_t *) buffer; + long bufferLeftToRead = (frames * pulseaudioStream->inputFrameSize); + + while( bufferLeftToRead > 0 ) + { + PA_PULSEAUDIO_IS_ERROR( pulseaudioStream, paStreamIsStopped ) + + PaPulseAudio_Lock( pulseaudioStream->mainloop ); + long l_read = PaUtil_ReadRingBuffer( &pulseaudioStream->inputRing, readableBuffer, + bufferLeftToRead ); + readableBuffer += l_read; + bufferLeftToRead -= l_read; + if( bufferLeftToRead > 0 ) + pa_threaded_mainloop_wait( pulseaudioStream->mainloop ); + + PaPulseAudio_UnLock( pulseaudioStream->mainloop ); + + if( bufferLeftToRead > 0 ) + { + /* Sleep small amount of time not burn CPU + * we block anyway so this is bearable + */ + usleep(100); + } + } + return paNoError; +} + + +PaError PaPulseAudio_WriteStreamBlock( PaStream * s, + const void *buffer, + unsigned long frames ) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) s; + int ret = 0; + size_t pulseaudioWritable = 0; + uint8_t *writableBuffer = (uint8_t *) buffer; + long bufferLeftToWrite = (frames * pulseaudioStream->outputFrameSize); + pa_operation *pulseaudioOperation = NULL; + + PaUtil_BeginCpuLoadMeasurement( &pulseaudioStream->cpuLoadMeasurer ); + + while( bufferLeftToWrite > 0) + { + PA_PULSEAUDIO_IS_ERROR( pulseaudioStream, paStreamIsStopped ) + + PaPulseAudio_Lock( pulseaudioStream->mainloop ); + pulseaudioWritable = pa_stream_writable_size( pulseaudioStream->outputStream ); + PaPulseAudio_UnLock( pulseaudioStream->mainloop ); + + if( pulseaudioWritable > 0 ) + { + if( bufferLeftToWrite < pulseaudioWritable ) + { + pulseaudioWritable = bufferLeftToWrite; + } + PaPulseAudio_Lock( pulseaudioStream->mainloop ); + ret = pa_stream_write( pulseaudioStream->outputStream, + writableBuffer, + pulseaudioWritable, + NULL, + 0, + PA_SEEK_RELATIVE ); + + pulseaudioOperation = pa_stream_update_timing_info( pulseaudioStream->outputStream, + NULL, + NULL ); + PaPulseAudio_UnLock( pulseaudioStream->mainloop ); + + ret = 0; + + if( pulseaudioOperation == NULL ) + { + return paInsufficientMemory; + } + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING ) + { + ret ++; + PA_PULSEAUDIO_IS_ERROR( pulseaudioStream, paStreamIsStopped ) + + /* As this shouldn never happen it's error if it does */ + if( ret >= 10000 ) + { + return paStreamIsStopped; + } + + usleep(100); + } + + PaPulseAudio_Lock( pulseaudioStream->mainloop ); + + pa_operation_unref( pulseaudioOperation ); + pulseaudioOperation = NULL; + + PaPulseAudio_UnLock( pulseaudioStream->mainloop ); + + writableBuffer += pulseaudioWritable; + bufferLeftToWrite -= pulseaudioWritable; + } + + if( bufferLeftToWrite > 0 ) + { + /* Sleep small amount of time not burn CPU + * we block anyway so this is bearable + */ + usleep(100); + } + + } + PaUtil_EndCpuLoadMeasurement( &pulseaudioStream->cpuLoadMeasurer, + frames ); + + return paNoError; +} + + +signed long PaPulseAudio_GetStreamReadAvailableBlock( PaStream * s ) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) s; + + if( pulseaudioStream->inputStream == NULL ) + { + return 0; + } + + return ( PaUtil_GetRingBufferReadAvailable( &pulseaudioStream->inputRing ) / + pulseaudioStream->inputFrameSize ); +} diff --git a/src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h b/src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h new file mode 100644 index 000000000..1c1bb092c --- /dev/null +++ b/src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h @@ -0,0 +1,91 @@ + +/* + * PulseAudio host to play natively in Linux based systems without + * ALSA emulation + * + * Copyright (c) 2014-2023 Tuukka Pasanen + * Copyright (c) 2016 Sqweek + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#ifndef _PA_HOSTAPI_PULSEAUDIO_BLOCK_H_ +#define _PA_HOSTAPI_PULSEAUDIO_BLOCK_H_ + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "pa_unix_util.h" +#include "pa_ringbuffer.h" + +/* PulseAudio headers */ +#include +#include +#include + +#include "pa_linux_pulseaudio_internal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PaError PaPulseAudio_CloseStreamBlock( PaStream * stream ); + +PaError PaPulseAudio_StartStreamBlock( PaStream * stream ); + +PaError PaPulseAudio_StopStreamBlock( PaStream * stream ); + +PaError PaPulseAudio_AbortStreamBlock( PaStream * stream ); + +PaError PaPulseAudio_ReadStreamBlock( PaStream * stream, + void *buffer, + unsigned long frames ); + +PaError PaPulseAudio_WriteStreamBlock( PaStream * stream, + const void *buffer, + unsigned long frames ); + +signed long PaPulseAudio_GetStreamReadAvailableBlock( PaStream * stream ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c b/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c new file mode 100644 index 000000000..18da32289 --- /dev/null +++ b/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c @@ -0,0 +1,971 @@ + +/* + * PulseAudio host to play natively in Linux based systems without + * ALSA emulation + * + * Copyright (c) 2014-2023 Tuukka Pasanen + * Copyright (c) 2016 Sqweek + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup common_src + + @brief PulseAudio implementation of support for a host API. + + This host API implements PulseAudio support for portaudio + it has callback mode and normal write mode support +*/ + + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "pa_unix_util.h" +#include "pa_ringbuffer.h" + +#include "pa_linux_pulseaudio_cb_internal.h" + + +/* PulseAudio headers */ +#include +#include + +int PaPulseAudio_updateTimeInfo( pa_stream * s, + PaStreamCallbackTimeInfo *timeInfo, + int record ) +{ + unsigned int isNegative = 0; + pa_usec_t pulseaudioStreamTime = 0; + pa_usec_t pulseaudioStreamLatency = 0; + + if( pa_stream_get_time( s, + &pulseaudioStreamTime ) == -PA_ERR_NODATA ) + { + return -PA_ERR_NODATA; + } + else + { + timeInfo->currentTime = ((PaTime) pulseaudioStreamTime / (PaTime) 1000000); + } + + if( pa_stream_get_latency( s, + &pulseaudioStreamLatency, + &isNegative ) == -PA_ERR_NODATA ) + { + return -PA_ERR_NODATA; + } + else + { + if( record == 0 ) + { + timeInfo->outputBufferDacTime = timeInfo->currentTime + ((PaTime) pulseaudioStreamLatency / (PaTime) 1000000); + } + else + { + timeInfo->inputBufferAdcTime = timeInfo->currentTime - ((PaTime) pulseaudioStreamLatency / (PaTime) 1000000); + } + } + return 0; +} + + +/* locks the Pulse Main loop when not called from it */ +void PaPulseAudio_Lock( pa_threaded_mainloop *mainloop ) +{ + if( !pa_threaded_mainloop_in_thread( mainloop ) ) { + pa_threaded_mainloop_lock( mainloop ); + } +} + +/* unlocks the Pulse Main loop when not called from it */ +void PaPulseAudio_UnLock( pa_threaded_mainloop *mainloop ) +{ + if( !pa_threaded_mainloop_in_thread( mainloop ) ) { + pa_threaded_mainloop_unlock( mainloop ); + } +} + +void _PaPulseAudio_WriteRingBuffer( PaUtilRingBuffer *ringbuffer, + const void *buffer, + size_t length ) +{ + /* + * If there is not enough room. Read from ringbuffer to make + * sure that if not full and audio will just underrun + * + * If you try to read too much and there is no room then this + * will fail. But I don't know how to get into that? + */ + if( PaUtil_GetRingBufferWriteAvailable( ringbuffer ) < length ) + { + uint8_t tmpBuffer[ PULSEAUDIO_BUFFER_SIZE ]; + PaUtil_ReadRingBuffer( ringbuffer, + tmpBuffer, + length ); + } + + PaUtil_WriteRingBuffer( ringbuffer, + buffer, + length ); + +} + +void _PaPulseAudio_Read( PaPulseAudio_Stream *stream, + size_t length ) +{ + const void *pulseaudioData = NULL; + + /* + * Idea behind this is that we fill ringbuffer with data + * that comes from input device. When it's available + * we'll fill it to callback or blocking reading + */ + if( pa_stream_peek( stream->inputStream, + &pulseaudioData, + &length )) + { + PA_DEBUG( ("Portaudio %s: Can't read audio!\n", + __FUNCTION__) ); + } + else + { + _PaPulseAudio_WriteRingBuffer( &stream->inputRing, pulseaudioData, length ); + } + + pa_stream_drop( stream->inputStream ); + + pulseaudioData = NULL; + +} + +static int _PaPulseAudio_ProcessAudio(PaPulseAudio_Stream *stream, + size_t length) +{ + uint8_t pulseaudioSampleBuffer[PULSEAUDIO_BUFFER_SIZE]; + size_t hostFramesPerBuffer = stream->bufferProcessor.framesPerHostBuffer; + size_t pulseaudioOutputBytes = 0; + size_t pulseaudioInputBytes = 0; + size_t hostFrameCount = 0; + int isOutputCb = 0; + int isInputCb = 0; + PaStreamCallbackTimeInfo timeInfo; + int ret = paContinue; + void *bufferData = NULL; + size_t pulseaudioOutputWritten = 0; + + /* If there is no specified per host buffer then + * just generate one or but correct one in place + */ + if( hostFramesPerBuffer == paFramesPerBufferUnspecified ) + { + if( !stream->framesPerHostCallback ) + { + /* This just good enough and most + * Pulseaudio server and ALSA can handle it + * + * We should never get here but this is ultimate + * backup. + */ + hostFramesPerBuffer = PAPULSEAUDIO_FRAMESPERBUFFERUNSPEC; + + stream->framesPerHostCallback = hostFramesPerBuffer; + } + else + { + hostFramesPerBuffer = stream->framesPerHostCallback; + } + } + + if( stream->outputStream ) + { + /* Calculate how many bytes goes to one frame */ + pulseaudioInputBytes = pulseaudioOutputBytes = (hostFramesPerBuffer * stream->outputFrameSize); + + if( stream->bufferProcessor.streamCallback ) + { + isOutputCb = 1; + } + } + + /* If we just want to have input but not output (Not Duplex) + * Use this calculation + */ + if( stream->inputStream ) + { + pulseaudioInputBytes = pulseaudioOutputBytes = (hostFramesPerBuffer * stream->inputFrameSize); + + if( stream->bufferProcessor.streamCallback ) + { + isInputCb = 1; + } + } + + /* + * When stopped we should stop feeding or recording right away + */ + if( stream->isStopped ) + { + return paStreamIsStopped; + } + + /* + * This can be called before we have reached out + * starting Portaudio stream or Portaudio stream + * is stopped + */ + if( !stream->pulseaudioIsActive ) + { + if(stream->outputStream) + { + bufferData = pulseaudioSampleBuffer; + memset( bufferData, 0x00, length); + + pa_stream_write( stream->outputStream, + bufferData, + length, + NULL, + 0, + PA_SEEK_RELATIVE ); + } + + return paContinue; + } + + /* If we have input which is mono and + * output which is stereo. We have to copy + * mono to monomono which is stereo. + * Then just read half and copy + */ + if( isOutputCb && + stream->outputSampleSpec.channels == 2 && + stream->inputSampleSpec.channels == 1) + { + pulseaudioInputBytes /= 2; + } + + while(1) + { + /* + * If everything fail like stream vanish or mainloop + * is in error state try to handle error + */ + PA_PULSEAUDIO_IS_ERROR( stream, paStreamIsStopped ) + + /* There is only Record stream so + * see if we have enough stuff to feed record stream + * If not then bail out. + */ + if( isInputCb && + PaUtil_GetRingBufferReadAvailable(&stream->inputRing) < pulseaudioInputBytes ) + { + if(isOutputCb && (pulseaudioOutputWritten < length) && !stream->missedBytes) + { + stream->missedBytes = length - pulseaudioOutputWritten; + } + else + { + stream->missedBytes = 0; + } + break; + } + else if( pulseaudioOutputWritten >= length) + { + stream->missedBytes = 0; + break; + } + + if( stream->outputStream ) + { + PaPulseAudio_updateTimeInfo( stream->outputStream, + &timeInfo, + 0 ); + } + + if( stream->inputStream ) + { + PaPulseAudio_updateTimeInfo( stream->inputStream, + &timeInfo, + 1 ); + } + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + /* When doing Portaudio Duplex one has to write and read same amount of data + * if not done that way Portaudio will go boo boo and nothing works. + * This is why this is done as it's done + * + * Pulseaudio does not care and this is something that needs small adjusting + */ + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, + &timeInfo, + 0 ); + + /* Read of ther is something to read */ + if( isInputCb ) + { + PaUtil_ReadRingBuffer( &stream->inputRing, + pulseaudioSampleBuffer, + pulseaudioInputBytes); + + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, + 0, + pulseaudioSampleBuffer, + stream->inputSampleSpec.channels ); + + PaUtil_SetInputFrameCount( &stream->bufferProcessor, + hostFramesPerBuffer ); + } + + if( isOutputCb ) + { + + size_t tmpSize = pulseaudioOutputBytes; + + /* Allocate memory to make it faster to output stuff */ + pa_stream_begin_write( stream->outputStream, &bufferData, &tmpSize ); + + /* If bufferData is NULL then output is not ready + * and we have to wait for it + */ + if(!bufferData) + { + return paNotInitialized; + } + + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, + 0, + bufferData, + stream->outputChannelCount ); + + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, + hostFramesPerBuffer ); + + if( pa_stream_write( stream->outputStream, + bufferData, + pulseaudioOutputBytes, + NULL, + 0, + PA_SEEK_RELATIVE ) ) + { + PA_DEBUG( ("Portaudio %s: Can't write audio!\n", + __FUNCTION__) ); + } + + pulseaudioOutputWritten += pulseaudioOutputBytes; + } + + hostFrameCount = + PaUtil_EndBufferProcessing( &stream->bufferProcessor, + &ret ); + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, + hostFrameCount ); + } + + return ret; +} + +void PaPulseAudio_StreamRecordCb( pa_stream * s, + size_t length, + void *userdata ) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata; + + if( !pulseaudioStream->pulseaudioIsActive ) + { + pulseaudioStream->pulseaudioIsActive = 1; + pulseaudioStream->pulseaudioIsStopped= 0; + } + + _PaPulseAudio_Read( pulseaudioStream, length ); + + /* Let's handle when output happens if Duplex + * + * Also there is no callback there is no meaning to continue + * as we have blocking reading + */ + if( pulseaudioStream->bufferProcessor.streamCallback ) + { + _PaPulseAudio_ProcessAudio( pulseaudioStream, length ); + } + + pa_threaded_mainloop_signal( pulseaudioStream->mainloop, + 0 ); +} + +void PaPulseAudio_StreamPlaybackCb( pa_stream * s, + size_t length, + void *userdata ) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata; + + if( !pulseaudioStream->inputStream && !pulseaudioStream->pulseaudioIsActive ) + { + pulseaudioStream->pulseaudioIsActive = 1; + pulseaudioStream->pulseaudioIsStopped = 0; + } + + if( pulseaudioStream->bufferProcessor.streamCallback ) + { + _PaPulseAudio_ProcessAudio( pulseaudioStream, length ); + } + + pa_threaded_mainloop_signal( pulseaudioStream->mainloop, + 0 ); +} + +/* This is left for future use! */ +static void PaPulseAudio_StreamSuccessCb( pa_stream * s, + int success, + void *userdata ) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata; + PA_DEBUG( ("Portaudio %s: %d\n", __FUNCTION__, + success) ); + pa_threaded_mainloop_signal( pulseaudioStream->mainloop, + 0 ); +} + +/* This is left for future use! */ +static void PaPulseAudio_CorkSuccessCb( + pa_stream * s, + int success, + void *userdata +) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata; + pa_threaded_mainloop_signal( pulseaudioStream->mainloop, + 0 ); +} + + +/* This is left for future use! */ +void PaPulseAudio_StreamStartedCb( pa_stream * stream, + void *userdata ) +{ + PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata; + pa_threaded_mainloop_signal( pulseaudioStream->mainloop, + 0 ); +} + +/* + When CloseStream() is called, the multi-api layer ensures that + the stream has already been stopped or aborted. +*/ +PaError PaPulseAudio_CloseStreamCb( PaStream * s ) +{ + PaError result = paNoError; + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = stream->hostapi; + pa_operation *pulseaudioOperation = NULL; + int waitLoop = 0; + int pulseaudioError = 0; + + /* Wait for stream to be stopped */ + stream->isActive = 0; + stream->isStopped = 1; + stream->pulseaudioIsActive = 0; + stream->pulseaudioIsStopped = 1; + + if( stream->outputStream != NULL + && pa_stream_get_state( stream->outputStream ) == PA_STREAM_READY ) + { + PaPulseAudio_Lock(stream->mainloop); + /** + * Pause stream so it stops faster + */ + pulseaudioOperation = pa_stream_cork( stream->outputStream, + 1, + PaPulseAudio_CorkSuccessCb, + stream ); + + PaPulseAudio_UnLock( stream->mainloop ); + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING ) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + waitLoop ++; + + if(waitLoop > 250) + { + break; + } + } + + waitLoop = 0; + + PaPulseAudio_Lock(stream->mainloop); + pa_operation_unref( pulseaudioOperation ); + pulseaudioOperation = NULL; + + pa_stream_disconnect( stream->outputStream ); + PaPulseAudio_UnLock( stream->mainloop ); + } + + if( stream->inputStream != NULL + && pa_stream_get_state( stream->inputStream ) == PA_STREAM_READY ) + { + PaPulseAudio_Lock( stream->mainloop ); + + /** + * Pause stream so it stops so it stops faster + */ + pulseaudioOperation = pa_stream_cork( stream->inputStream, + 1, + PaPulseAudio_CorkSuccessCb, + stream ); + + PaPulseAudio_UnLock( stream->mainloop ); + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING ) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + waitLoop ++; + + if(waitLoop > 250) + { + break; + } + } + + waitLoop = 0; + + PaPulseAudio_Lock( stream->mainloop ); + pa_operation_unref( pulseaudioOperation ); + pulseaudioOperation = NULL; + + /* Then we disconnect stream and wait for + * Termination + */ + pa_stream_disconnect( stream->inputStream ); + + PaPulseAudio_UnLock( stream->mainloop ); + + } + + /* Wait for termination for both */ + while(!waitLoop) + { + PaPulseAudio_Lock( stream->mainloop ); + if( stream->inputStream != NULL + && pa_stream_get_state( stream->inputStream ) == PA_STREAM_TERMINATED ) + { + pa_stream_unref( stream->inputStream ); + stream->inputStream = NULL; + } + PaPulseAudio_UnLock( stream->mainloop ); + + PaPulseAudio_Lock( stream->mainloop ); + if( stream->outputStream != NULL + && pa_stream_get_state(stream->outputStream) == PA_STREAM_TERMINATED ) + { + pa_stream_unref( stream->outputStream ); + stream->outputStream = NULL; + } + PaPulseAudio_UnLock( stream->mainloop ); + + if((stream->outputStream == NULL + && stream->inputStream == NULL) + || pulseaudioError >= 5000 ) + { + waitLoop = 1; + } + + pulseaudioError ++; + usleep(10000); + } + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + + PaUtil_FreeMemory( stream->inputStreamName ); + PaUtil_FreeMemory( stream->outputStreamName ); + PaUtil_FreeMemory( stream ); + + return result; +} + +PaError PaPulseAudio_StartStreamCb( PaStream * s ) +{ + PaError ret = paNoError; + PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s; + int pulseaudioPlaybackStarted = 0; + int pulseaudioRecordStarted = 0; + pa_stream_state_t pulseaudioState = PA_STREAM_UNCONNECTED; + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = stream->hostapi; + const char *pulseaudioName = NULL; + pa_operation *pulseaudioOperation = NULL; + int waitLoop = 0; + unsigned int pulseaudioReqFrameSize = (1024 * 2); + + stream->isActive = 0; + stream->isStopped = 1; + stream->pulseaudioIsActive = 0; + stream->pulseaudioIsStopped = 1; + stream->missedBytes = 0; + + /* Ready the processor */ + PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); + + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + /* Adjust latencies if that is wanted + * https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/LatencyControl/ + * + * tlength is for Playback + * fragsize if for Record + */ + stream->outputBufferAttr.maxlength = (uint32_t)-1; + stream->inputBufferAttr.maxlength = (uint32_t)-1; + + stream->outputBufferAttr.tlength = (uint32_t)-1; + stream->inputBufferAttr.tlength = (uint32_t)-1; + + stream->outputBufferAttr.fragsize = (uint32_t)-1; + stream->inputBufferAttr.fragsize = (uint32_t)-1; + + stream->outputBufferAttr.prebuf = (uint32_t)-1; + stream->inputBufferAttr.prebuf = (uint32_t)-1; + + stream->outputBufferAttr.minreq = (uint32_t)-1; + stream->inputBufferAttr.minreq = (uint32_t)-1; + + stream->outputUnderflows = 0; + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + + pa_stream_flags_t pulseaudioStreamFlags = PA_STREAM_INTERPOLATE_TIMING | + PA_STREAM_AUTO_TIMING_UPDATE | + PA_STREAM_ADJUST_LATENCY | + PA_STREAM_NO_REMIX_CHANNELS | + PA_STREAM_NO_REMAP_CHANNELS | + PA_STREAM_DONT_MOVE; + + if( stream->inputStream ) + { + /* Default input reads 65,535 bytes setting fragsize + * fragments request to smaller chunks of data so it's + * easier to get nicer looking timestamps with current + * callback system + */ + stream->inputBufferAttr.fragsize = pa_usec_to_bytes( pulseaudioReqFrameSize, + &stream->inputSampleSpec ); + + if( stream->inputDevice != paNoDevice) + { + PA_DEBUG( ("Portaudio %s: %d (%s)\n", __FUNCTION__, stream->inputDevice, + pulseaudioHostApi->pulseaudioDeviceNames[stream-> + inputDevice]) ); + } + + pa_stream_set_read_callback( stream->inputStream, + PaPulseAudio_StreamRecordCb, + stream ); + + PaDeviceIndex defaultInputDevice; + PaError result = PaUtil_DeviceIndexToHostApiDeviceIndex( + &defaultInputDevice, + pulseaudioHostApi->inheritedHostApiRep.info.defaultInputDevice, + &(pulseaudioHostApi->inheritedHostApiRep) ); + + /* NULL means default device */ + pulseaudioName = NULL; + + /* If default device is not requested then change to wanted device */ + if( result == paNoError && stream->inputDevice != defaultInputDevice ) + { + pulseaudioName = pulseaudioHostApi-> + pulseaudioDeviceNames[stream->inputDevice]; + } + + if ( result == paNoError ) + { + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + /* Zero means success */ + if( ! pa_stream_connect_record( stream->inputStream, + pulseaudioName, + &stream->inputBufferAttr, + pulseaudioStreamFlags ) ) + { + pa_stream_set_started_callback( stream->inputStream, + PaPulseAudio_StreamStartedCb, + stream ); + } + else + { + PA_DEBUG( ("Portaudio %s: Can't read audio!\n", + __FUNCTION__) ); + + goto startstreamcb_error; + } + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + + for( waitLoop = 0; waitLoop < 100; waitLoop ++ ) + { + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + pulseaudioState = pa_stream_get_state( stream->inputStream ); + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + + if( pulseaudioState == PA_STREAM_READY ) + { + break; + } + else if( pulseaudioState == PA_STREAM_FAILED || + pulseaudioState == PA_STREAM_TERMINATED ) + { + goto startstreamcb_error; + } + + usleep(10000); + } + } + else + { + goto startstreamcb_error; + } + + } + + if( stream->outputStream ) + { + /* tlength does almost the same as fragsize in record. + * See reasoning up there in comments. + * + * In future this should we tuned when things changed + * this just 'good' default + */ + stream->outputBufferAttr.tlength = pa_usec_to_bytes( pulseaudioReqFrameSize, + &stream->outputSampleSpec ); + + pa_stream_set_write_callback( stream->outputStream, + PaPulseAudio_StreamPlaybackCb, + stream ); + + /* Just keep on trucking if we are just corked */ + if( pa_stream_get_state( stream->outputStream ) == PA_STREAM_READY + && pa_stream_is_corked( stream->outputStream ) ) + { + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + pulseaudioOperation = pa_stream_cork( stream->outputStream, + 0, + PaPulseAudio_CorkSuccessCb, + stream ); + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + } + + pa_operation_unref( pulseaudioOperation ); + pulseaudioOperation = NULL; + } + else + { + if( stream->outputDevice != paNoDevice ) + { + PA_DEBUG( ("Portaudio %s: %d (%s)\n", + __FUNCTION__, + stream->outputDevice, + pulseaudioHostApi->pulseaudioDeviceNames[stream-> + outputDevice]) ); + } + + PaDeviceIndex defaultOutputDevice; + PaError result = PaUtil_DeviceIndexToHostApiDeviceIndex( &defaultOutputDevice, + pulseaudioHostApi->inheritedHostApiRep.info.defaultOutputDevice, + &(pulseaudioHostApi->inheritedHostApiRep) ); + + /* NULL means default device */ + pulseaudioName = NULL; + + /* If default device is not requested then change to wanted device */ + if( result == paNoError && stream->outputDevice != defaultOutputDevice ) + { + pulseaudioName = pulseaudioHostApi-> + pulseaudioDeviceNames[stream->outputDevice]; + } + + if(result == paNoError) + { + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + + if ( ! pa_stream_connect_playback( stream->outputStream, + pulseaudioName, + &stream->outputBufferAttr, + pulseaudioStreamFlags, + NULL, + NULL ) ) + { + pa_stream_set_underflow_callback( stream->outputStream, + PaPulseAudio_StreamUnderflowCb, + stream); + pa_stream_set_started_callback( stream->outputStream, + PaPulseAudio_StreamStartedCb, + stream ); + } + else + { + PA_DEBUG( ("Portaudio %s: Can't write audio!\n", + __FUNCTION__) ); + goto startstreamcb_error; + } + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + + for( waitLoop = 0; waitLoop < 100; waitLoop ++ ) + { + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + pulseaudioState = pa_stream_get_state( stream->outputStream ); + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + + if( pulseaudioState = PA_STREAM_READY ) + { + break; + } + else if( pulseaudioState == PA_STREAM_FAILED || + pulseaudioState == PA_STREAM_TERMINATED ) + { + goto startstreamcb_error; + } + + usleep(10000); + } + + } + else + { + goto startstreamcb_error; + } + } + } + + if( !stream->outputStream && + !stream->inputStream ) + { + PA_DEBUG( ("Portaudio %s: Streams not initialized!\n", + __FUNCTION__) ); + goto startstreamcb_error; + } + + /* Make sure we pass no error on intialize */ + ret = paNoError; + + /* Stream is now active */ + stream->isActive = 1; + stream->isStopped = 0; + + /* Allways unlock.. so we don't get locked */ + startstreamcb_end: + return ret; + + error: + startstreamcb_error: + PA_DEBUG( ("Portaudio %s: Can't start audio!\n", + __FUNCTION__) ); + + if( pulseaudioPlaybackStarted || pulseaudioRecordStarted ) + { + PaPulseAudio_AbortStreamCb( stream ); + } + + stream->isActive = 0; + stream->isStopped = 1; + ret = paNotInitialized; + + goto startstreamcb_end; +} + +static PaError RequestStop( PaPulseAudio_Stream * stream, + int abort ) +{ + PaError ret = paNoError; + PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = stream->hostapi; + pa_operation *pulseaudioOperation = NULL; + + PaPulseAudio_Lock( pulseaudioHostApi->mainloop ); + + /* Wait for stream to be stopped */ + stream->isActive = 0; + stream->isStopped = 1; + stream->pulseaudioIsActive = 0; + stream->pulseaudioIsStopped = 1; + + stream->missedBytes = 0; + + /* Test if there is something that we can play */ + if( stream->outputStream + && pa_stream_get_state( stream->outputStream ) == PA_STREAM_READY + && !pa_stream_is_corked( stream->outputStream ) + && !abort ) + { + pulseaudioOperation = pa_stream_cork( stream->outputStream, + 1, + PaPulseAudio_CorkSuccessCb, + stream ); + + while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING ) + { + pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop ); + } + + pa_operation_unref( pulseaudioOperation ); + + pulseaudioOperation = NULL; + } + + requeststop_error: + PaPulseAudio_UnLock( pulseaudioHostApi->mainloop ); + stream->isActive = 0; + stream->isStopped = 1; + stream->pulseaudioIsActive = 0; + stream->pulseaudioIsStopped = 1; + + return ret; +} + +PaError PaPulseAudio_StopStreamCb( PaStream * s ) +{ + return RequestStop( (PaPulseAudio_Stream *) s, + 0 ); +} + +PaError PaPulseAudio_AbortStreamCb( PaStream * s ) +{ + return RequestStop( (PaPulseAudio_Stream *) s, + 1 ); +} diff --git a/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h b/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h new file mode 100644 index 000000000..74e510de9 --- /dev/null +++ b/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h @@ -0,0 +1,97 @@ + +/* + * PulseAudio host to play natively in Linux based systems without + * ALSA emulation + * + * Copyright (c) 2014-2023 Tuukka Pasanen + * Copyright (c) 2016 Sqweek + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#ifndef _PA_HOSTAPI_PULSEAUDIO_CB_H_ +#define _PA_HOSTAPI_PULSEAUDIO_CB_H_ + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "pa_unix_util.h" +#include "pa_ringbuffer.h" + + +/* PulseAudio headers */ +#include +#include +#include + +#include "pa_linux_pulseaudio_internal.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +int PaPulseAudio_updateTimeInfo( pa_stream * s, + PaStreamCallbackTimeInfo *timeInfo, + int record ); + +void *PaPulseAudio_processThread( void *userdata ); + +PaError PaPulseAudio_CloseStreamCb( PaStream * stream ); + +PaError PaPulseAudio_StartStreamCb( PaStream * stream ); + +PaError PaPulseAudio_StopStreamCb( PaStream * stream ); + +PaError PaPulseAudio_AbortStreamCb( PaStream * stream ); + +void PaPulseAudio_StreamRecordCb( pa_stream * s, + size_t length, + void *userdata ); + +void PaPulseAudio_StreamPlaybackCb( pa_stream * s, + size_t length, + void *userdata ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h b/src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h new file mode 100644 index 000000000..84bff2579 --- /dev/null +++ b/src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h @@ -0,0 +1,273 @@ + +/* + * PulseAudio host to play natively in Linux based systems without + * ALSA emulation + * + * Copyright (c) 2014-2023 Tuukka Pasanen + * Copyright (c) 2016 Sqweek + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#ifndef _PA_HOSTAPI_PULSEAUDIO_H_ +#define _PA_HOSTAPI_PULSEAUDIO_H_ + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "pa_unix_util.h" +#include "pa_ringbuffer.h" +#include "pa_debugprint.h" + +/* PulseAudio headers */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* prototypes for functions declared in this file */ + +#define PA_PULSEAUDIO_SET_LAST_HOST_ERROR(errorCode, errorText) \ + PaUtil_SetLastHostErrorInfo(paInDevelopment, errorCode, errorText) + +#define PAPULSEAUDIO_MAX_DEVICECOUNT 1024 +#define PAPULSEAUDIO_MAX_DEVICENAME 1024 + +/* Default latency values to expose. Chosen by trial and error to be reasonable. */ +#define PA_PULSEAUDIO_DEFAULT_MIN_LATENCY 0.010 +#define PA_PULSEAUDIO_DEFAULT_MAX_LATENCY 0.080 + +/* Just some value that Pulseaudio can handle */ +#define PAPULSEAUDIO_FRAMESPERBUFFERUNSPEC 32 + +/* Assuming of 2 seconds of 44100 Hz sample rate with FLOAT (4 bytes) and stereo channels (2 channels). + You should have pretty good size buffer with this. If output/intput doesn't happens in 2 second we + have more trouble than this buffer. + @todo change this to something more sophisticated */ +#define PULSEAUDIO_BUFFER_SIZE (96100 * 4 * 2) + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + PaHostApiIndex hostApiIndex; + PaDeviceInfo deviceInfoArray[PAPULSEAUDIO_MAX_DEVICECOUNT]; + char *pulseaudioDeviceNames[PAPULSEAUDIO_MAX_DEVICECOUNT]; + pa_sample_spec pulseaudioDefaultSampleSpec; + + /* PulseAudio stuff goes here */ + pa_threaded_mainloop *mainloop; + pa_mainloop_api *mainloopApi; + pa_context *context; + int deviceCount; + pa_time_event *timeEvent; +} +PaPulseAudio_HostApiRepresentation; + +/* PaPulseAudio_Stream - a stream data structure specifically for this implementation */ + +typedef struct PaPulseAudio_Stream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + PaPulseAudio_HostApiRepresentation *hostapi; + + unsigned long framesPerHostCallback; + pa_threaded_mainloop *mainloop; + pa_context *context; + pa_sample_spec outputSampleSpec; + pa_sample_spec inputSampleSpec; + pa_stream *outputStream; + pa_stream *inputStream; + pa_buffer_attr outputBufferAttr; + pa_buffer_attr inputBufferAttr; + int outputUnderflows; + int outputChannelCount; + int inputChannelCount; + + size_t maxFramesPerBuffer; + size_t maxFramesHostPerBuffer; + int outputFrameSize; + int inputFrameSize; + + PaDeviceIndex inputDevice; + PaDeviceIndex outputDevice; + + char *outputStreamName; + char *inputStreamName; + + PaUtilRingBuffer inputRing; + + size_t missedBytes; + + /* Used in communication between threads + * + * State machine works like this: + * When stream is wanted to start with Pa_StartStream + * then isActive is 1 if opening of devices goes well + * and isStopped is then 0. + * + * When requested to stop isStopped is 1 on isActive is 0 + * and nothing should be written to ouput or read from input + * anymore + * + * Pulseaudio does not like this as it creates streams and they + * start when they are ready and it can be after we have + * exited Pa_StartStream or before if get's kicked up very fast + * + * pulseaudioIsActive and pulseaudioIsStopped are used to find if + * there is stream active or stopped in pulseaudio side. They + * live their own life besides isActive and isStopped to make sure + * that portaudio will have input and output available before + * reading or writing to stream. + */ + volatile sig_atomic_t isActive; + volatile sig_atomic_t isStopped; + volatile sig_atomic_t pulseaudioIsActive; + volatile sig_atomic_t pulseaudioIsStopped; + +} +PaPulseAudio_Stream; + +/* PulseAudio Error checking macro */ +#define PA_PULSEAUDIO_IS_ERROR(pastream, errorCode) \ + if( !(pastream) || \ + !(pastream)->context || \ + !PA_CONTEXT_IS_GOOD( pa_context_get_state( (pastream)->context ) ) || \ + ( (pastream)->outputStream && \ + !PA_STREAM_IS_GOOD( pa_stream_get_state( (pastream)->outputStream ) ) ) || \ + ( (pastream)->inputStream && \ + !PA_STREAM_IS_GOOD( pa_stream_get_state( (pastream)->inputStream ) ) ) ) \ + { \ + if( !(pastream) || \ + ( (pastream)->context && \ + pa_context_get_state( (pastream)->context ) == PA_CONTEXT_FAILED ) || \ + ( (pastream)->outputStream && \ + pa_stream_get_state( (pastream)->outputStream ) == PA_STREAM_FAILED ) || \ + ( (pastream)->inputStream && \ + pa_stream_get_state( (pastream)->inputStream ) == PA_STREAM_FAILED ) ) \ + { \ + return errorCode; \ + } \ + } \ + if( !pastream->isActive || pastream->isStopped ) \ + { \ + return paStreamIsStopped; \ + } + +void PaPulseAudio_Lock( pa_threaded_mainloop *mainloop ); + +void PaPulseAudio_UnLock( pa_threaded_mainloop *mainloop ); + +PaError PaPulseAudio_Initialize( PaUtilHostApiRepresentation ** hostApi, + PaHostApiIndex index ); + +void Terminate( struct PaUtilHostApiRepresentation *hostApi ); + + +PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters * inputParameters, + const PaStreamParameters * outputParameters, + double sampleRate ); + +PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream ** s, + const PaStreamParameters * inputParameters, + const PaStreamParameters * outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback * streamCallback, + void *userData ); + + +PaError IsStreamStopped( PaStream * s ); +PaError IsStreamActive( PaStream * stream ); + +PaTime GetStreamTime( PaStream * stream ); +double GetStreamCpuLoad( PaStream * stream ); + +PaPulseAudio_HostApiRepresentation *PaPulseAudio_New( void ); +void PaPulseAudio_Free( PaPulseAudio_HostApiRepresentation * ptr ); + +int PaPulseAudio_CheckConnection( PaPulseAudio_HostApiRepresentation * ptr ); + +void PaPulseAudio_CheckContextStateCb( pa_context * c, + void *userdata ); +void PaPulseAudio_ServerInfoCb( pa_context *c, + const pa_server_info *i, + void *userdata ); + +void PaPulseAudio_SinkListCb( pa_context * c, + const pa_sink_info * l, + int eol, + void *userdata ); + +void PaPulseAudio_SourceListCb( pa_context * c, + const pa_source_info * l, + int eol, + void *userdata ); + +void PaPulseAudio_StreamStateCb( pa_stream * s, + void *userdata ); + +void PaPulseAudio_StreamStartedCb( pa_stream * s, + void *userdata ); + +void PaPulseAudio_StreamUnderflowCb( pa_stream * s, + void *userdata ); + +PaError PaPulseAudio_ConvertPortaudioFormatToPaPulseAudio_( PaSampleFormat portaudiosf, + pa_sample_spec * pulseaudiosf +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/src/os/unix/pa_unix_hostapis.c b/src/os/unix/pa_unix_hostapis.c index 466f6f846..7a81ce83b 100644 --- a/src/os/unix/pa_unix_hostapis.c +++ b/src/os/unix/pa_unix_hostapis.c @@ -43,6 +43,7 @@ #include "pa_hostapi.h" PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaPulseAudio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); PaError PaAudioIO_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); @@ -102,6 +103,10 @@ PaUtilHostApiInitializer *paHostApiInitializers[] = PaMacCore_Initialize, #endif +#if PA_USE_PULSEAUDIO + PaPulseAudio_Initialize, +#endif + #if PA_USE_OBOE PaOboe_Initialize, #endif From fb38b7f874562f304675c65019f1bc0562430446 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Sat, 30 Sep 2023 20:12:08 +0300 Subject: [PATCH 089/125] pulseaudio: Move Pulseaudio include in correct place when using autoconf (#843) As Jack and Pulseaudio both needs Ringbuffer that include can be done in same place. In configure.in also Pulseaudio header file was included before it was sure that it was really needed. Commit makes sure that Pulseaudio include is available only if it's needed as it can cause failing in build if Pulseaudio develoment files are not available. --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index a8de16aac..af0844426 100644 --- a/configure.in +++ b/configure.in @@ -404,10 +404,10 @@ case "${host_os}" in if [[ "$have_pulse" = "yes" ] || [ "$have_jack" = "yes" ]] ; then OTHER_OBJS="$OTHER_OBJS src/common/pa_ringbuffer.o" - INCLUDES="$INCLUDES pa_linux_pulseaudio.h" fi if [[ "$have_pulse" = "yes" ] && [ "$with_pulse" != "no" ]] ; then + INCLUDES="$INCLUDES pa_linux_pulseaudio.h" DLL_LIBS="$DLL_LIBS $PULSE_LIBS" CFLAGS="$CFLAGS $PULSE_CFLAGS" OTHER_OBJS="$OTHER_OBJS src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.o" From 7b71a1296eede8f9b5030ae648ce503ce80650c3 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 10:45:24 +0200 Subject: [PATCH 090/125] restored workflows directory --- .github/workflows/MSBuild.yml | 96 ++++++++++++++++++ .github/workflows/autotools.yml | 20 ++++ .github/workflows/cmake.yml | 128 ++++++++++++++++++++++++ .github/workflows/compare_def_files.yml | 17 ++++ .github/workflows/whitelint.yml | 17 ++++ 5 files changed, 278 insertions(+) create mode 100644 .github/workflows/MSBuild.yml create mode 100644 .github/workflows/autotools.yml create mode 100644 .github/workflows/cmake.yml create mode 100644 .github/workflows/compare_def_files.yml create mode 100644 .github/workflows/whitelint.yml diff --git a/.github/workflows/MSBuild.yml b/.github/workflows/MSBuild.yml new file mode 100644 index 000000000..ea90dba46 --- /dev/null +++ b/.github/workflows/MSBuild.yml @@ -0,0 +1,96 @@ +name: MSBuild MSVC Project File CI + +on: [push, pull_request] + +env: + # Path to the solution file relative to the root of the project. + SOLUTION_FILE_PATH: ./msvc/portaudio.sln + VCPROJ_FILE_PATH: ./msvc/portaudio.vcproj + VCXPROJ_FILE_PATH: ./msvc/portaudio.vcxproj + VCXPROJ_FILTERS_FILE_PATH: ./msvc/portaudio.vcxproj.filters + VCXPROJ_USER_FILE_PATH: ./msvc/portaudio.vcxproj.user + DEF_FILE_PATH: ./msvc/portaudio.def + +jobs: + build: + runs-on: windows-latest + strategy: + matrix: + BUILD_CONFIGURATION: [Release] + BUILD_PLATFORM: [Win32, x64] + + steps: + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v1 + + - uses: actions/checkout@v2 + + - name: Upgrade VC Project File + # We maintain our vcproj file in an old format to maintain backwards compatibility + # This step upgrades the project to the latest version of MSVC + # see https://docs.microsoft.com/en-us/visualstudio/ide/reference/upgrade-devenv-exe?view=vs-2019 + # pipe to file to ensure that it terminates https://stackoverflow.com/questions/48896010/occasionally-occurring-msbuild-error-msb3428/48918105#48918105 + # discussion of using vswhere.exe here: https://stackoverflow.com/questions/65287456/how-to-upgrade-a-visual-studio-project-from-within-a-github-action/65311868#65311868 + run: | + $devenv = & vswhere.exe '-property' productPath + Write-Output "$devenv" + & $devenv "${{env.VCPROJ_FILE_PATH}}" /Upgrade /NoSplash | Out-Null + Write-Output "devenv launched" + while (!(Test-Path "${{env.VCXPROJ_FILE_PATH}}")) { Start-Sleep -Seconds 10 } + Write-Output "vcxproj found" + while (!(Test-Path "${{env.VCXPROJ_FILTERS_FILE_PATH}}")) { Start-Sleep -Seconds 10 } + Write-Output "vcxproj.filters found" + Start-Sleep -Seconds 10 + Write-Output "done." + + - name: Remove ASIO Files and Enable PA_USE_DS=1 + # Process the project files to remove ASIO-related sources and includes (since we can not access the ASIO SDK in a public build) + run: | + # Process .vcxproj file: remove source files + $xdoc = new-object System.Xml.XmlDocument + $vcxprojFile = resolve-path("${{env.VCXPROJ_FILE_PATH}}") + $xdoc.load($vcxprojFile) + $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable + $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) + $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) + Write-Output "deleting ASIO related compilation nodes from .vcxproj:" + Write-Output $nodes + ForEach($node in $nodes) { + $parent = $node.ParentNode + $parent.RemoveChild($node) + } + # Enable DirectSound host API + $nodes = $xdoc.SelectNodes("//vs:PreprocessorDefinitions[contains(., 'PA_USE_DS=0')]", $namespace) + ForEach($node in $nodes) { + $text = $node.InnerText + $node.InnerText = $text -replace 'PA_USE_DS=0', 'PA_USE_DS=1' + } + $xdoc.save($vcxprojFile) + # Process .vcxproj.filters file: remove source files and includes + $vcxprojFiltersFile = resolve-path("${{env.VCXPROJ_FILTERS_FILE_PATH}}") + $xdoc.load($vcxprojFiltersFile) + $namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xdoc.NameTable + $namespace.AddNamespace("vs", $xdoc.DocumentElement.NamespaceURI) + $nodes = $xdoc.SelectNodes("//vs:ClCompile[contains(@Include, '..\src\hostapi\asio')]", $namespace) + Write-Output "deleting ASIO related compilation nodes from .vcxproj.filters:" + Write-Output $nodes + ForEach($node in $nodes) { + $parent = $node.ParentNode + $parent.RemoveChild($node) + } + $nodes = $xdoc.SelectNodes("//vs:ClInclude[contains(@Include, 'pa_asio.h')]", $namespace) + Write-Output "deleting ASIO related include nodes from .vcxproj.filters:" + Write-Output $nodes + ForEach($node in $nodes) { + $parent = $node.ParentNode + $parent.RemoveChild($node) + } + $xdoc.save($vcxprojFiltersFile) + # Process .def file: remove PaAsio_ symbols + Set-Content -Path "${{env.DEF_FILE_PATH}}" -Value (Get-Content -Path "${{env.DEF_FILE_PATH}}" | Select-String -Pattern 'PaAsio_' -NotMatch) + + - name: Build + working-directory: ${{env.GITHUB_WORKSPACE}} + # Add additional options to the MSBuild command line here (like platform or verbosity level). + # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference + run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.BUILD_PLATFORM}} ${{env.VCXPROJ_FILE_PATH}} diff --git a/.github/workflows/autotools.yml b/.github/workflows/autotools.yml new file mode 100644 index 000000000..4d6fb6cb9 --- /dev/null +++ b/.github/workflows/autotools.yml @@ -0,0 +1,20 @@ +name: autotools build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-autotools: + + runs-on: ubuntu-latest + name: Ubuntu + + steps: + - uses: actions/checkout@v2 + - name: configure + run: ./configure + - name: make + run: make diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 000000000..a25eaabf7 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,128 @@ +name: CMake build + +on: [push, pull_request] + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - name: Ubuntu GCC + os: ubuntu-latest + install_dir: ~/portaudio + vcpkg_triplet: x64-linux + cmake_generator: "Unix Makefiles" + cmake_options: + -DPA_USE_OSS=ON + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + - name: Ubuntu MinGW + os: ubuntu-latest + install_dir: ~/portaudio + asio_sdk_cache_path: asiosdk.zip + dependencies_extras: mingw-w64 + # FIXME: linking JACK fails with vcpkg. Switch the CMake toolchain file to use vcpkg. The + # toolchain file in this repository is not needed when using a MinGW triplet with vcpkg. + vcpkg_triplet: x64-mingw-static + cmake_generator: "Unix Makefiles" + cmake_options: + -DPA_USE_ASIO=ON + -DASIO_SDK_ZIP_PATH=asiosdk.zip + -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/i686-w64-mingw32.cmake + - name: Windows MSVC + os: windows-latest + install_dir: C:\portaudio + vcpkg_triplet: x64-windows + cmake_generator: "Visual Studio 17 2022" + # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to + # asiosdk instead of asiosdk.zip. + asio_sdk_cache_path: "asiosdk.zip" + # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. + cmake_options: + -DPA_USE_ASIO=ON + -DASIO_SDK_ZIP_PATH="asiosdk.zip" + -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake + - name: Windows MinGW + os: windows-latest + install_dir: C:\portaudio + vcpkg_triplet: x64-mingw-static + cmake_generator: "MinGW Makefiles" + # ASIO_SDK_ZIP_PATH needs to be quoted or CMake will save the download to + # asiosdk instead of asiosdk.zip. + asio_sdk_cache_path: "asiosdk.zip" + # Somehow CMake fails to find the toolchain file if a relative path is used on Windows. + cmake_options: + -DPA_USE_ASIO=ON + -DASIO_SDK_ZIP_PATH="asiosdk.zip" + -DCMAKE_TOOLCHAIN_FILE=D:\a\portaudio\portaudio\vcpkg\scripts\buildsystems\vcpkg.cmake + - name: macOS Clang + os: macOS-latest + install_dir: ~/portaudio + vcpkg_triplet: x64-osx + cmake_generator: "Unix Makefiles" + cmake_options: + -DCMAKE_FRAMEWORK=OFF + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + - name: macOS Clang framework + os: macOS-latest + install_dir: ~/portaudio + vcpkg_triplet: x64-osx + cmake_generator: "Unix Makefiles" + cmake_options: + -DCMAKE_FRAMEWORK=ON + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + + runs-on: ${{ matrix.os }} + name: ${{ matrix.name }} + env: + cmake_build_type: RelWithDebInfo + steps: + - name: checkout Git repository + uses: actions/checkout@v2 + - name: "[Ubuntu] install dependencies" + run: | + sudo apt-get update + sudo apt-get install libasound2-dev ${{ matrix.dependencies_extras }} + if: matrix.os == 'ubuntu-latest' + - name: "[macOS] install dependencies" + # https://github.com/PortAudio/portaudio/issues/767 + run: brew install pkg-config + if: matrix.os == 'macOS-latest' + - name: "[Windows/MinGW] set up ASIO SDK cache" + uses: actions/cache@v2 + if: matrix.asio_sdk_cache_path != null + with: + path: ${{ matrix.asio_sdk_cache_path }} + key: ${{ hashFiles('.github/asiosdk-version.txt') }} + - name: Setup vcpkg + uses: lukka/run-vcpkg@v7 + if: ${{ matrix.vcpkg_triplet }} != null + with: + vcpkgTriplet: ${{ matrix.vcpkg_triplet }} + appendedCacheKey: ${{ hashFiles( '**/vcpkg.json' ) }} + additionalCachedPaths: build/vcpkg_installed + vcpkgGitCommitId: f30786c9c4c901f21a13e2d524349e39cc359a90 + # Required when using vcpkg.json manifest in repository + setupOnly: true + - name: configure + run: cmake + -G "${{ matrix.cmake_generator }}" + ${{ matrix.cmake_options }} + -DCMAKE_INSTALL_PREFIX=${{ matrix.install_dir }} + -DCMAKE_BUILD_TYPE=${{ env.cmake_build_type }} + -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} + -DPA_USE_SKELETON=ON + -DPA_BUILD_TESTS=ON + -DPA_BUILD_EXAMPLES=ON + -DPA_WARNINGS_ARE_ERRORS=ON + -S . + -B build + - name: build + run: cmake --build build --config ${{ env.cmake_build_type }} --parallel 2 + - name: install + run: cmake --install build --config ${{ env.cmake_build_type }} + - name: "Upload GitHub Actions artifacts" + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.name }} PortAudio build + path: ${{ matrix.install_dir }} diff --git a/.github/workflows/compare_def_files.yml b/.github/workflows/compare_def_files.yml new file mode 100644 index 000000000..b2b342411 --- /dev/null +++ b/.github/workflows/compare_def_files.yml @@ -0,0 +1,17 @@ +name: Check that PortAudio .def files are in sync + +on: [push, pull_request] + +jobs: + run-pa-compare-def-files-py: + + runs-on: ubuntu-latest + name: Ubuntu + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Run the pa_compare_def_files.py script + run: python ./pa_compare_def_files.py diff --git a/.github/workflows/whitelint.yml b/.github/workflows/whitelint.yml new file mode 100644 index 000000000..41cf8ff8b --- /dev/null +++ b/.github/workflows/whitelint.yml @@ -0,0 +1,17 @@ +name: Check for valid whitespace usage in PortAudio source files + +on: [push, pull_request] + +jobs: + run-pa-whitelint-dot-py: + + runs-on: ubuntu-latest + name: Ubuntu + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Run the pa_whitelint.py script + run: python ./pa_whitelint.py From 34bf282ad9757fe819116a9cd8b17ce44e88de56 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 11:41:36 +0200 Subject: [PATCH 091/125] Minor fixes to FindOboe.cmake --- cmake/modules/FindOboe.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 23fd02308..9e27ee01e 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -22,7 +22,8 @@ endif() if(NOT OBOE_DIRECTORY) message(AUTHOR_WARNING - "If you're trying to use Oboe as a Host API, please specify the directory where you cloned its repository. For further information, please read src/hostapi/oboe/README.md" + "If you're trying to use Oboe as a Host API, please specify the directory where you + cloned its repository. For further information, please read src/hostapi/oboe/README.md" ) set(OBOE_FOUND FALSE) else() @@ -53,8 +54,7 @@ else() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARIES} ${LOG_LIBRARY}) - - + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Oboe @@ -71,4 +71,4 @@ else() target_include_directories(Oboe::oboe INTERFACE "${OBOE_INCLUDE_DIR}") endif() endif() -endif() \ No newline at end of file +endif() From 603bdde1eecbd99654805b72ca6416f9d4c08ed0 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 11:52:36 +0200 Subject: [PATCH 092/125] Enhanced prebuilt libraries compatibility in FindOboe.cmake --- cmake/modules/FindOboe.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index 9e27ee01e..ea66ec394 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -31,10 +31,10 @@ else() set(OBOE_INCLUDE_DIR ${OBOE_DIRECTORY}/include) endif() - if(NOT DEFINED OBOE_LIBRARIES) + if(NOT DEFINED OBOE_LIBRARY_DIRS) set(OBOE_LIBRARY_DIRS ${OBOE_DIRECTORY}/build/${ANDROID_ABI}) - set(OBOE_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) endif() + set(OBOE_LIBRARIES ${OBOE_LIBRARY_DIRS}/liboboe.so) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) @@ -54,7 +54,7 @@ else() find_library(LOG_LIBRARY log) #used by pa_oboe.cpp and pa_oboe.h as a logging tool set(OBOE_LINK_LIBRARIES ${OBOE_LIBRARIES} ${LOG_LIBRARY}) - + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Oboe From 1412c1dd5e06a021e35c13f4670dbafcd5638274 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 11:53:50 +0200 Subject: [PATCH 093/125] Minor changes to Pa_Oboe/Readme and pa_oboe.cpp --- src/hostapi/oboe/README.md | 7 +++---- src/hostapi/oboe/pa_oboe.cpp | 12 +++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index db64c90bc..c4de74088 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -8,13 +8,12 @@ Building: To build portaudio with Oboe, there are some necessary steps: 1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. 2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the prebuilt libraries, you can just clone the include directory. + Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the prebuilt libraries, you can skip this step. 3) Set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository, and build the Oboe libraries (you can use "build_all_android.sh"). If you instead used the prebuilt libraries, do the following: - set OBOE_DIRECTORY to TRUE; - - set OBOE_INCLUDE_DIR to the include directory you cloned in step 2; - - set OBOE_LIBRARIES to path_to_Oboe_libraries_folder/${ANDROID_ABI}/your_path_to_prebuilt_libraries_of_the_chosen_ABI; + - set OBOE_LIBRARY_DIRS path_to_Oboe_libraries_folder/${ANDROID_ABI}), the code will search the prebuilt library of the chosen ABI in that folder. 4) Build PaOboe (you can use "build_all_PaOboe.sh"). 5) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. @@ -31,4 +30,4 @@ I'm positive that automatic format selection is possible, but simply using PaUt ### Buffer sizes: -Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index c96fdbc5f..5878ca25e 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -1337,9 +1337,9 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, m_androidOutputUsage != Usage::NotificationRingtone && m_androidOutputUsage != Usage::VoiceCommunication && m_androidOutputUsage != Usage::VoiceCommunicationSignalling && - m_androidOutputUsage != Usage::Alarm - // See if more are needed. - ) { + m_androidOutputUsage != Usage::Alarm && + m_androidOutputUsage != Usage::Game + ) { m_outcome = paIncompatibleHostApiSpecificStreamInfo; return m_outcome; } @@ -1442,6 +1442,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; PaSampleFormat m_hostInputSampleFormat, m_hostOutputSampleFormat; + + //FIXME: add a function that lets the user choose usage and preset Usage m_androidOutputUsage = Usage::VoiceCommunication; InputPreset m_androidInputPreset = InputPreset::Generic; @@ -1476,8 +1478,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, m_androidInputPreset != InputPreset::Camcorder && m_androidInputPreset != InputPreset::VoiceRecognition && m_androidInputPreset != InputPreset::VoiceCommunication - // Should I add compatibility with VoicePerformance? - ) + m_androidInputPreset != InputPreset::VoicePerformance + ) return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paInt16" with whatever format you prefer - From df7f7e97cb58a53cd1dd71d2c29de2fcb5db8ac6 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 12:09:14 +0200 Subject: [PATCH 094/125] Removed auto latency tuning in favor of simpler impleentation in pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 74 ++---------------------------------- 1 file changed, 3 insertions(+), 71 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 5878ca25e..fd763bd40 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -161,10 +161,9 @@ using namespace oboe; //Useful global variables int32_t g_inputDeviceId = kUnspecified; int32_t g_outputDeviceId = kUnspecified; -PerformanceMode g_inputPerfMode = PerformanceMode::None; -bool g_inputPerfModeUser = false; -PerformanceMode g_outputPerfMode = PerformanceMode::None; -bool g_outputPerfModeUser = false; + +PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; +PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -254,10 +253,6 @@ class OboeEngine : public AudioStreamCallback { //device selection implementation int32_t getSelectedDevice(oboe::Direction direction); - - //auto performance mode selection - void performanceModeAutoSelection(Direction direction); - double assertLatency(Direction direction); }; @@ -320,8 +315,6 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan return m_outcome; } - performanceModeAutoSelection(direction); - if (sampleRate != kUnspecified) { m_outcome = (sampleRate == m_builder.getSampleRate()); if(!m_outcome) { @@ -880,65 +873,6 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { } -/** - * \brief Function used to automatically select the performance mode, based on the latency value LOW_LATENCY_MS, if no - * choice was made via PaOboe_SetPerformanceMode. It uses OboeEngine::assertLatency to make this decision. - * @param direction the Oboe::Direction of the stream we want to select the mode of. - */ -void OboeEngine::performanceModeAutoSelection(Direction direction){ - double m_result = assertLatency(direction); - - if(direction == Direction::Input) { - if (m_result >= 0 && !g_inputPerfModeUser) { - if (m_result <= LOW_LATENCY_MS) - g_inputPerfMode = PerformanceMode::LowLatency; - else - g_inputPerfMode = PerformanceMode::PowerSaving; - } - } else { - if (m_result >= 0 && !g_outputPerfModeUser) { - if (m_result <= LOW_LATENCY_MS) - g_outputPerfMode = PerformanceMode::LowLatency; - else - g_outputPerfMode = PerformanceMode::PowerSaving; - } - } -} - - -/** - * \brief Asserts if the device supports latency tutning, then calculates the latency of an AudioStream. - * @param direction the direction of the AudioStream we want to check. - * @return the measured latency, or -1.0 if any error occurs. - */ -double OboeEngine::assertLatency(Direction direction){ - if(__ANDROID_API__<31) { - LOGI("[OboeEngine::assertLatency]\t Latency Tuning is not supported for Android API level < 31"); - return -1.0; - } - if(direction == Direction::Input){ - ResultWithValue m_result = inputStream->calculateLatencyMillis(); - if (m_result) { - return m_result.value(); - } else { - LOGE("[OboeEngine::assertLatency]\t Error calculating input latency: %s", - oboe::convertToText(m_result.error())); - return -1.0; - } - } else { - ResultWithValue m_result = outputStream->calculateLatencyMillis(); - if (m_result) { - return m_result.value(); - } else { - LOGE("[OboeEngine::assertLatency]\t Error calculating output latency: %s", - oboe::convertToText(m_result.error())); - return -1.0; - } - } - -} - - /*----------------------------- PaSkeleton functions implementations -----------------------------*/ /** @@ -1957,10 +1891,8 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ if(direction == Direction::Input) { g_inputPerfMode = performanceMode; - g_inputPerfModeUser = true; } else { g_outputPerfMode = performanceMode; - g_outputPerfModeUser = true; } } From 125bc0000d0a7cf15f4bf613c6192d7ad20c0c6e Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 2 Oct 2023 12:35:46 +0200 Subject: [PATCH 095/125] Set paFloat32 as default format in pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index fd763bd40..1f9b63e99 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -299,14 +299,14 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan m_builder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. - ->setFormat(AudioFormat::I16) + ->setFormat(AudioFormat::Float) ->setDirection(direction) ->setSampleRate(sampleRate) ->setChannelCount(channelCount); if (direction == Direction::Input) { - m_result = m_builder.openStream(inputStream); + m_result = m_builder->openStream(inputStream); } else { - m_result = m_builder.openStream(outputStream); + m_result = m_builder->openStream(outputStream); } if (m_result != Result::OK) { @@ -1225,8 +1225,8 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, m_androidRecordingPreset != InputPreset::Camcorder && m_androidRecordingPreset != InputPreset::VoiceRecognition && m_androidRecordingPreset != InputPreset::VoiceCommunication - // Should I add compatibility with VoicePerformance? - ) { + m_androidRecordingPreset != InputPreset::VoicePerformance + ) { m_outcome = paIncompatibleHostApiSpecificStreamInfo; return m_outcome; } @@ -1416,14 +1416,14 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - + /* FIXME: Replace "paFloat32" with whatever format you prefer - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paInt16, m_inputSampleFormat); + paFloat32, m_inputSampleFormat); m_oboeStream->inputFormat = m_hostInputSampleFormat; } else { m_inputChannelCount = 0; - m_inputSampleFormat = m_hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ + m_inputSampleFormat = m_hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ m_oboeStream->inputFormat = m_hostInputSampleFormat; } @@ -1456,15 +1456,15 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, ) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paInt16" with whatever format you prefer - + /* FIXME: Replace "paFloat32" with whatever format you prefer - PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paInt16, m_outputSampleFormat); + paFloat32, m_outputSampleFormat); m_oboeStream->outputFormat = m_hostOutputSampleFormat; } else { m_outputChannelCount = 0; - m_outputSampleFormat = m_hostOutputSampleFormat = paInt16; + m_outputSampleFormat = m_hostOutputSampleFormat = paFloat32; m_oboeStream->outputFormat = m_hostOutputSampleFormat; } From ba44469f3f62545d07e9ed1e88f646c1b8f5617f Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 10:06:19 +0200 Subject: [PATCH 096/125] Renamed most of the variables according to best coding practices. --- include/pa_oboe.h | 2 +- src/hostapi/oboe/pa_oboe.cpp | 1547 +++++++++++++++++----------------- 2 files changed, 767 insertions(+), 782 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 761a7b937..d345617d7 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -104,7 +104,7 @@ void PaOboe_SetNativeBufferSize(unsigned long bufferSize); * buffer size when doing this, and use the sample rate defined in AudioManager's * android.media.property.OUTPUT_SAMPLE_RATE. */ -void PaOboe_SetNumberOfBuffers(unsigned buffers); +void PaOboe_SetNumberOfBuffers(unsigned numberOfBuffers); #ifdef __cplusplus } diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 1f9b63e99..fd70931a3 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -153,8 +153,8 @@ static signed long GetStreamWriteAvailable(PaStream *stream); static unsigned long GetApproximateLowBufferSize(); // Commonly used parameters initialized. -static unsigned long nativeBufferSize = 0; -static unsigned numberOfBuffers = 2; +static unsigned long g_nativeBufferSize = 0; +static unsigned g_numberOfBuffers = 2; using namespace oboe; @@ -165,6 +165,8 @@ int32_t g_outputDeviceId = kUnspecified; PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; +class OboeEngine; + /** * Stream structure, useful to store relevant information. It's needed by Portaudio. */ @@ -194,9 +196,15 @@ typedef struct OboeStream { void **inputBuffers; int currentInputBuffer; - long engineAddress; unsigned long framesPerHostCallback; unsigned bytesPerFrame; + + OboeEngine *getEngineAddress() { return oboeEngineAddress; } + + void setEngineAddress(OboeEngine *i_oboeEngine) { oboeEngineAddress = i_oboeEngine; } + +private: + OboeEngine *oboeEngineAddress; } OboeStream; @@ -211,42 +219,49 @@ class OboeEngine : public AudioStreamCallback { //Stream-managing functions bool tryStream(Direction direction, int32_t sampleRate, int32_t channelCount); - PaError openStream(Direction direction, int32_t sampleRate, + + PaError openStream(OboeStream *oboeStream, Direction direction, int32_t sampleRate, Usage outputUsage, InputPreset inputPreset); - bool startStream(); - bool stopStream(); + + bool startStream(OboeStream *oboeStream); + + bool stopStream(OboeStream *oboeStream); + bool restartStream(int direction); - bool closeStream(); - bool abortStream(); + + bool closeStream(OboeStream *oboeStream); + + bool abortStream(OboeStream *oboeStream); //Callback function for non-blocking streams and some callback utils DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) override; + void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + void resetCallbackCounters(); //Blocking read/write functions bool writeStream(const void *buffer, int32_t framesToWrite); + bool readStream(void *buffer, int32_t framesToRead); //Engine utils - OboeStream* initializeOboeStream(); - void setEngineAddress(long address); + OboeStream *allocateOboeStream(); private: - //The only instance of OboeStream that will be used - OboeStream *oboeStream; + OboeStream *m_oboeStreamHolder; //The only instances of output and input streams that will be used, and their builders - std::shared_ptr outputStream; - AudioStreamBuilder outputBuilder; - std::shared_ptr inputStream; - AudioStreamBuilder inputBuilder; + std::shared_ptr m_outputStream; + AudioStreamBuilder m_outputBuilder; + std::shared_ptr m_inputStream; + AudioStreamBuilder m_inputBuilder; //callback utils - unsigned long framesProcessed{}; - PaStreamCallbackTimeInfo timeInfo{}; - struct timespec timeSpec{}; + unsigned long m_framesProcessed{}; + PaStreamCallbackTimeInfo m_timeInfo{}; + struct timespec m_timeSpec{}; //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -277,7 +292,7 @@ typedef struct PaOboeHostApiRepresentation { * \brief Initializes an instance of the engine. */ OboeEngine::OboeEngine() { - oboeStream = nullptr; + m_oboeStreamHolder = allocateOboeStream(); } @@ -292,52 +307,52 @@ OboeEngine::OboeEngine() { * @return true if the requested sample rate / channel count is supported by the device, false if * they aren't, or if tryStream couldn't open a stream. */ -bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { - AudioStreamBuilder m_builder; - Result m_result; - bool m_outcome = false; +bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t i_channelCount) { + AudioStreamBuilder builder; + Result result; + bool outcome = false; - m_builder.setDeviceId(getSelectedDevice(direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + builder.setDeviceId(getSelectedDevice(i_direction)) + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) - ->setDirection(direction) - ->setSampleRate(sampleRate) - ->setChannelCount(channelCount); - if (direction == Direction::Input) { - m_result = m_builder->openStream(inputStream); + ->setDirection(i_direction) + ->setSampleRate(i_sampleRate) + ->setChannelCount(i_channelCount); + if (i_direction == Direction::Input) { + result = builder->openStream(m_inputStream); } else { - m_result = m_builder->openStream(outputStream); + result = builder->openStream(m_outputStream); } - if (m_result != Result::OK) { + if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", - convertToText(m_result)); - return m_outcome; + convertToText(result)); + return outcome; } - if (sampleRate != kUnspecified) { - m_outcome = (sampleRate == m_builder.getSampleRate()); - if(!m_outcome) { + if (i_sampleRate != kUnspecified) { + outcome = (i_sampleRate == builder.getSampleRate()); + if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - sampleRate, m_builder.getSampleRate()); + i_sampleRate, builder.getSampleRate()); } - } else if (channelCount != kUnspecified) { - m_outcome = (channelCount == m_builder.getChannelCount()); - if(!m_outcome) { + } else if (i_channelCount != kUnspecified) { + outcome = (i_channelCount == builder.getChannelCount()); + if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, m_builder.getChannelCount()); + channelCount, builder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); - m_outcome = false; + outcome = false; } - if (direction == Direction::Input) - inputStream->close(); + if (i_direction == Direction::Input) + m_inputStream->close(); else - outputStream->close(); + m_outputStream->close(); - return m_outcome; + return outcome; } @@ -356,103 +371,101 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan * @return paNoError if everything goes as expected, paUnanticipatedHostError if Oboe fails to open * a stream, and paInsufficientMemory if the memory allocation of the buffers fails. */ -PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, - Usage androidOutputUsage, InputPreset androidInputPreset) { - PaError m_error = paNoError; - Result m_result; - - if (direction == Direction::Input) { - inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) - ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) - ->setSampleRate(sampleRate) +PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, int32_t i_sampleRate, + Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { + PaError error = paNoError; + Result result; + + if (i_direction == Direction::Input) { + m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) + ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) + ->setSampleRate(i_sampleRate) ->setDirection(Direction::Input) ->setDeviceId(getSelectedDevice(Direction::Input)) ->setPerformanceMode(g_inputPerfMode) - ->setInputPreset(androidInputPreset) - ->setFramesPerCallback(oboeStream->framesPerHostCallback); + ->setInputPreset(i_androidInputPreset) + ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); - if (!(oboeStream->isBlocking)) { + if (!(i_oboeStream->isBlocking)) { resetCallbackCounters(); - inputBuilder.setDataCallback(this) + m_inputBuilder.setDataCallback(this) ->setErrorCallback(this); } - m_result = inputBuilder.openStream(inputStream); + result = m_inputBuilder.openStream(m_inputStream); - if (m_result != Result::OK) { + if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", - convertToText(m_result)); - m_error = paUnanticipatedHostError; - return m_error; + convertToText(result)); + return paUnanticipatedHostError; } - inputStream->setBufferSizeInFrames(inputStream->getFramesPerBurst() * numberOfBuffers); - oboeStream->inputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); + m_inputStream->setBufferSizeInFrames(m_inputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->inputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - oboeStream->framesPerHostCallback * - oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.inputChannelCount); + for (int i = 0; i < g_numberOfBuffers; ++i) { + i_oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + i_oboeStream->framesPerHostCallback * + i_oboeStream->bytesPerFrame * + i_oboeStream->bufferProcessor.inputChannelCount); - if (!oboeStream->inputBuffers[i]) { + if (!i_oboeStream->inputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(oboeStream->inputBuffers[j]); - PaUtil_FreeMemory(oboeStream->inputBuffers); - inputStream->close(); - m_error = paInsufficientMemory; + PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); + PaUtil_FreeMemory(i_oboeStream->inputBuffers); + m_inputStream->close(); + error = paInsufficientMemory; break; } } - oboeStream->currentInputBuffer = 0; + i_oboeStream->currentInputBuffer = 0; } else { - outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) - ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) - ->setSampleRate(sampleRate) + m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) + ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) + ->setSampleRate(i_sampleRate) ->setDirection(Direction::Output) ->setDeviceId(getSelectedDevice(Direction::Output)) ->setPerformanceMode(g_outputPerfMode) - ->setUsage(androidOutputUsage) - ->setFramesPerCallback(oboeStream->framesPerHostCallback); + ->setUsage(i_androidOutputUsage) + ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); - if (!(oboeStream->isBlocking)) { + if (!(i_oboeStream->isBlocking)) { resetCallbackCounters(); - outputBuilder.setDataCallback(this) + m_outputBuilder.setDataCallback(this) ->setErrorCallback(this); } - m_result = outputBuilder.openStream(outputStream); - if (m_result != Result::OK) { + result = m_outputBuilder.openStream(m_outputStream); + if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", - convertToText(m_result)); - m_error = paUnanticipatedHostError; - return m_error; + convertToText(result)); + return paUnanticipatedHostError; } - outputStream->setBufferSizeInFrames(outputStream->getFramesPerBurst() * numberOfBuffers); - oboeStream->outputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(numberOfBuffers * sizeof(int32_t *)); + m_outputStream->setBufferSizeInFrames(m_outputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->outputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < numberOfBuffers; ++i) { - oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - oboeStream->framesPerHostCallback * - oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.outputChannelCount); + for (int i = 0; i < g_numberOfBuffers; ++i) { + i_oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + i_oboeStream->framesPerHostCallback * + i_oboeStream->bytesPerFrame * + i_oboeStream->bufferProcessor.outputChannelCount); - if (!oboeStream->outputBuffers[i]) { + if (!i_oboeStream->outputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(oboeStream->outputBuffers[j]); - PaUtil_FreeMemory(oboeStream->outputBuffers); - outputStream->close(); - m_error = paInsufficientMemory; + PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); + PaUtil_FreeMemory(i_oboeStream->outputBuffers); + m_outputStream->close(); + error = paInsufficientMemory; break; } } - oboeStream->currentOutputBuffer = 0; + i_oboeStream->currentOutputBuffer = 0; } - return m_error; + return error; } @@ -461,23 +474,23 @@ PaError OboeEngine::openStream(Direction direction, int32_t sampleRate, * and requested to be started. * @return true if the streams we wanted to start are started successfully, false otherwise. */ -bool OboeEngine::startStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::startStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; - if (oboeStream->hasInput) { - m_inputResult = inputStream->requestStart(); - if (m_inputResult != Result::OK) + if (i_oboeStream->hasInput) { + inputResult = m_inputStream->requestStart(); + if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", - convertToText(m_inputResult)); + convertToText(inputResult)); } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->requestStart(); - if (m_outputResult != Result::OK) + if (i_oboeStream->hasOutput) { + outputResult = m_outputStream->requestStart(); + if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", - convertToText(m_outputResult)); + convertToText(outputResult)); } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -486,23 +499,23 @@ bool OboeEngine::startStream() { * and requested to be stopped. * @return true if the streams we wanted to stop are stopped successfully, false otherwise. */ -bool OboeEngine::stopStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::stopStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; - if (oboeStream->hasInput) { - m_inputResult = inputStream->requestStop(); - if (m_inputResult != Result::OK) + if (i_oboeStream->hasInput) { + inputResult = m_inputStream->requestStop(); + if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", - convertToText(m_inputResult)); + convertToText(inputResult)); } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->requestStop(); - if (m_outputResult != Result::OK) + if (i_oboeStream->hasOutput) { + outputResult = m_outputStream->requestStop(); + if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", - convertToText(m_outputResult)); + convertToText(outputResult)); } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -511,67 +524,67 @@ bool OboeEngine::stopStream() { * audio devices change while a stream is started. * @return true if the stream is restarted successfully, false otherwise. */ -bool OboeEngine::restartStream(int direction) { - bool m_outcome = true; - Result m_result; +bool OboeEngine::restartStream(int i_direction) { + bool outcome = true; + Result result; - switch (direction) { + switch (i_direction) { case 1: //output-only //stopping and closing - m_result = outputStream->stop(); - if (m_result != Result::OK) + result = m_outputStream->stop(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", - convertToText(m_result)); - m_result = outputStream->close(); - if (m_result != Result::OK) + convertToText(result)); + result = m_outputStream->close(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", - convertToText(m_result)); + convertToText(result)); //reopening and restarting - m_result = outputBuilder.openStream(outputStream); - if (m_result != Result::OK) + result = outputBuilder.openStream(m_outputStream); + if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", - convertToText(m_result)); - m_result = outputStream->start(); - if (m_result != Result::OK) { + convertToText(result)); + result = m_outputStream->start(); + if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", - convertToText(m_result)); - m_outcome = false; + convertToText(result)); + outcome = false; } break; case 2: //input-only //stopping and closing - m_result = inputStream->stop(); - if (m_result != Result::OK) + result = m_inputStream->stop(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", - convertToText(m_result)); - m_result = inputStream->close(); - if (m_result != Result::OK) + convertToText(result)); + result = m_inputStream->close(); + if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", - convertToText(m_result)); + convertToText(result)); //reopening and restarting - m_result = inputBuilder.openStream(inputStream); - if (m_result != Result::OK) + result = inputBuilder.openStream(m_inputStream); + if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", - convertToText(m_result)); - m_result = inputStream->start(); - if (m_result != Result::OK) { + convertToText(result)); + result = m_inputStream->start(); + if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", - convertToText(m_result)); - m_outcome = false; + convertToText(result)); + outcome = false; } break; default: - // unspecified direction or both directions, abort streams + // unspecified direction or both directions: restart both streams LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); - m_outcome = (restartStream(1) && restartStream(2)); + outcome = (restartStream(1) && restartStream(2)); break; } - return m_outcome; + return outcome; } @@ -580,30 +593,33 @@ bool OboeEngine::restartStream(int direction) { * and closed if active. * @return true if the stream is closed successfully, otherwise returns false. */ -bool OboeEngine::closeStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::closeStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; + bool hasOutput = true, hasInput = true; - if(oboeStream == nullptr){ - LOGE("[OboeEngine::closeStream]\t Tried to close a NULL stream. Exiting closeStream."); - return false; + if (i_oboeStream == nullptr) { + LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Terminating both oboe streams."); + } else { + hasInput = i_oboeStream->hasInput; + hasOutput = i_oboeStream->hasOutput; } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->close(); - if (m_outputResult == Result::ErrorClosed) { - m_outputResult = Result::OK; + if (hasOutput) { + outputResult = m_outputStream->close(); + if (outputResult == Result::ErrorClosed) { + outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } - if (oboeStream->hasInput) { - m_inputResult = inputStream->close(); - if (m_inputResult == Result::ErrorClosed) { - m_inputResult = Result::OK; + if (hasInput) { + inputResult = m_inputStream->close(); + if (inputResult == Result::ErrorClosed) { + inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); } } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -611,36 +627,39 @@ bool OboeEngine::closeStream() { * \brief Stops oboeStream - both input and output audiostreams are checked and forcefully stopped. * @return true if the output stream and the input stream are stopped successfully, false otherwise. */ -bool OboeEngine::abortStream() { - Result m_outputResult = Result::OK, m_inputResult = Result::OK; +bool OboeEngine::abortStream(OboeStream *i_oboeStream) { + Result outputResult = Result::OK, inputResult = Result::OK; + bool hasOutput = true, hasInput = true; - if(oboeStream == nullptr){ - LOGE("[OboeEngine::abortStream]\t Tried to abort a NULL stream. Exiting abortStream."); - return false; + if (i_oboeStream == nullptr) { + LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams."); + } else { + hasInput = i_oboeStream->hasInput; + hasOutput = i_oboeStream->hasOutput; } - if (oboeStream->hasInput) { - m_inputResult = inputStream->stop(); - if (m_inputResult != Result::OK) + if (hasInput) { + inputResult = m_inputStream->stop(); + if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", - convertToText(m_inputResult)); - m_inputResult = inputStream->close(); + convertToText(inputResult)); + inputResult = m_inputStream->close(); if (m_inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", - convertToText(m_inputResult)); + convertToText(inputResult)); } - if (oboeStream->hasOutput) { - m_outputResult = outputStream->stop(); + if (hasOutput) { + outputResult = m_outputStream->stop(); if (m_outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(m_outputResult)); - m_outputResult = outputStream->close(); - if (m_outputResult != Result::OK) + outputResult = m_outputStream->close(); + if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", - convertToText(m_outputResult)); + convertToText(outputResult)); } - return (m_outputResult == Result::OK && m_inputResult == Result::OK); + return (outputResult == Result::OK && inputResult == Result::OK); } @@ -648,84 +667,86 @@ bool OboeEngine::abortStream() { * \brief Oboe's callback routine. FIXME: implement onErrorAfterClose correctly */ DataCallbackResult -OboeEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { - - clock_gettime(CLOCK_REALTIME, &timeSpec); - timeInfo.currentTime = (PaTime) (timeSpec.tv_sec + (timeSpec.tv_nsec / 1000000000.0)); - timeInfo.outputBufferDacTime = (PaTime) (oboeStream->framesPerHostCallback +OboeEngine::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { + + clock_gettime(CLOCK_REALTIME, &m_timeSpec); + m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); + m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + / + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); + m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback / - oboeStream->streamRepresentation.streamInfo.sampleRate - + timeInfo.currentTime); - timeInfo.inputBufferAdcTime = (PaTime) (oboeStream->framesPerHostCallback - / - oboeStream->streamRepresentation.streamInfo.sampleRate - + timeInfo.currentTime); + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); /* check if StopStream or AbortStream was called */ - if (oboeStream->doStop) { - oboeStream->callbackResult = paComplete; - } else if (oboeStream->doAbort) { - oboeStream->callbackResult = paAbort; + if (m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->callbackResult = paComplete; + } else if (m_oboeStreamHolder->doAbort) { + m_oboeStreamHolder->callbackResult = paAbort; } - PaUtil_BeginCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&oboeStream->bufferProcessor, - &timeInfo, oboeStream->cbFlags); + PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, + &m_timeInfo, m_oboeStreamHolder->cbFlags); - if (oboeStream->hasOutput) { - oboeStream->outputBuffers[oboeStream->currentOutputBuffer] = audioData; - PaUtil_SetOutputFrameCount(&oboeStream->bufferProcessor, numFrames); - PaUtil_SetInterleavedOutputChannels(&oboeStream->bufferProcessor, 0, - (void *) ((PaInt16 **) oboeStream->outputBuffers)[oboeStream->currentOutputBuffer], + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; + PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); + PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ + m_oboeStreamHolder->currentOutputBuffer], 0); } - if (oboeStream->hasInput) { - audioData = oboeStream->inputBuffers[oboeStream->currentInputBuffer]; - PaUtil_SetInputFrameCount(&oboeStream->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&oboeStream->bufferProcessor, 0, - (void *) ((PaInt16 **) oboeStream->inputBuffers)[oboeStream->currentInputBuffer], + if (m_oboeStreamHolder->hasInput) { + i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; + PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ + m_oboeStreamHolder->currentInputBuffer], 0); } - /* continue processing user buffers if cbresult is pacontinue or if cbresult is pacomplete and userbuffers aren't empty yet */ - if (oboeStream->callbackResult == paContinue - || (oboeStream->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&oboeStream->bufferProcessor))) { - framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, - &oboeStream->callbackResult); + /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ + if (m_oboeStreamHolder->callbackResult == paContinue + || (m_oboeStreamHolder->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { + m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, + &oboeStream->callbackResult); } /* enqueue a buffer only when there are frames to be processed, * this will be 0 when paComplete + empty buffers or paAbort */ - if (framesProcessed > 0) { - if (oboeStream->hasOutput) { - oboeStream->currentOutputBuffer = - (oboeStream->currentOutputBuffer + 1) % numberOfBuffers; + if (m_framesProcessed > 0) { + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->currentOutputBuffer = + (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; } - if (oboeStream->hasInput) { - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % numberOfBuffers; + if (m_oboeStreamHolder->hasInput) { + m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; } } - PaUtil_EndCpuLoadMeasurement(&oboeStream->cpuLoadMeasurer, framesProcessed); + PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); /* StopStream was called */ - if (framesProcessed == 0 && oboeStream->doStop) { - oboeStream->oboeCallbackResult = DataCallbackResult::Stop; + if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; } /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (framesProcessed == 0 && !(oboeStream->doAbort || oboeStream->doStop)) { - oboeStream->isActive = false; - oboeStream->isStopped = true; - if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) - oboeStream->streamRepresentation.streamFinishedCallback( - oboeStream->streamRepresentation.userData); - //oboeStream->oboeCallbackResult = DataCallbackResult::Stop; TODO: Resume this test (onAudioReady) + else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { + m_oboeStreamHolder->isActive = false; + m_oboeStreamHolder->isStopped = true; + if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) + m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( + m_oboeStreamHolder->streamRepresentation.userData); + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) } - return oboeStream->oboeCallbackResult; + return m_oboeStreamHolder->oboeCallbackResult; } @@ -733,14 +754,13 @@ OboeEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numF * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells * what error occurred. */ -void OboeEngine::onErrorAfterClose(AudioStream *audioStream, Result error) { - if (error == oboe::Result::ErrorDisconnected) { +void OboeEngine::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { + if (i_error == oboe::Result::ErrorDisconnected) { LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); if (!restartStream(0)) LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); - } - else - LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); + } else + LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); } @@ -748,8 +768,8 @@ void OboeEngine::onErrorAfterClose(AudioStream *audioStream, Result error) { * \brief Resets callback counters (called at the start of each iteration of onAudioReady */ void OboeEngine::resetCallbackCounters() { - framesProcessed = 0; - timeInfo = {0, 0, 0}; + m_framesProcessed = 0; + m_timeInfo = {0, 0, 0}; } @@ -761,22 +781,22 @@ void OboeEngine::resetCallbackCounters() { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(const void *buffer, int32_t framesToWrite) { - bool m_outcome = true; +bool OboeEngine::writeStream(const void *i_buffer, int32_t i_framesToWrite) { + bool outcome = true; - ResultWithValue m_result = outputStream->write(buffer, framesToWrite, TIMEOUT_NS); + ResultWithValue result = m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. - if (m_result.error() == Result::ErrorDisconnected) { + if (result.error() == Result::ErrorDisconnected) { if (OboeEngine::restartStream(1)) return true; } - if (!m_result) { - LOGE("[OboeEngine::writeStream]\t Error writing stream: %s", convertToText(m_result.error())); - m_outcome = false; + if (!result) { + LOGE("[OboeEngine::writeStream]\t Error writing stream: %s", convertToText(result.error())); + outcome = false; } - return m_outcome; + return outcome; } @@ -788,22 +808,22 @@ bool OboeEngine::writeStream(const void *buffer, int32_t framesToWrite) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { - bool m_outcome = true; +bool OboeEngine::readStream(void *i_buffer, int32_t i_framesToRead) { + bool outcome = true; - ResultWithValue m_result = inputStream->read(buffer, framesToRead, TIMEOUT_NS); + ResultWithValue result = m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. - if (m_result.error() == Result::ErrorDisconnected) { + if (result.error() == Result::ErrorDisconnected) { if (OboeEngine::restartStream(2)) return true; } - if (!m_result) { - LOGE("[OboeEngine::readStream]\t Error reading stream: %s", convertToText(m_result.error())); - m_outcome = false; + if (!result) { + LOGE("[OboeEngine::readStream]\t Error reading stream: %s", convertToText(result.error())); + outcome = false; } - return m_outcome; + return outcome; } @@ -811,51 +831,43 @@ bool OboeEngine::readStream(void *buffer, int32_t framesToRead) { * \brief Allocates the memory of oboeStream. * @return the address of the oboeStream. */ -OboeStream* OboeEngine::initializeOboeStream() { - oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); +OboeStream *OboeEngine::allocateOboeStream() { + OboeStream *oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); + oboeStream->setEngineAddress(this); return oboeStream; } -/** - * \brief Sets the engineAddress parameter of oboeStream, useful for recalling the engine whenever - * it's needed. - * @param address the address of the only instance of OboeEngine. - */ -void OboeEngine::setEngineAddress(long address) { - oboeStream->engineAddress = address; -} - /** * \brief Converts PaSampleFormat values into Oboe::AudioFormat values. * @param paFormat the PaSampleFormat we want to convert. * @return the converted AudioFormat. */ -AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { - AudioFormat m_oboeFormat; - switch (paFormat) { +AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat i_paFormat) { + AudioFormat oboeFormat; + switch (i_paFormat) { case paFloat32: - m_oboeFormat = AudioFormat::Float; + oboeFormat = AudioFormat::Float; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); break; case paInt16: - m_oboeFormat = AudioFormat::I16; + oboeFormat = AudioFormat::I16; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); break; case paInt32: - m_oboeFormat = AudioFormat::I32; + oboeFormat = AudioFormat::I32; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); break; case paInt24: - m_oboeFormat = AudioFormat::I24; + oboeFormat = AudioFormat::I24; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); break; default: - m_oboeFormat = AudioFormat::Unspecified; - LOGW("[OboeEngine::PaToOboeFormat]\t Setting AudioFormat to Unspecified, because Oboe does not support the requested format."); + oboeFormat = AudioFormat::Unspecified; + LOGW("[OboeEngine::PaToOboeFormat]\t Setting AudioFormat to Unspecified, Oboe does not support the requested format."); break; } - return m_oboeFormat; + return oboeFormat; } @@ -865,8 +877,8 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { * @param direction the Oboe::Direction for which we want to know the device Id. * @return the device Id of the appropriate direction. */ -int32_t OboeEngine::getSelectedDevice(Direction direction) { - if (direction == Direction::Input) +int32_t OboeEngine::getSelectedDevice(Direction i_direction) { + if (i_direction == Direction::Input) return g_inputDeviceId; else return g_outputDeviceId; @@ -879,17 +891,16 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { * \brief Checks if the requested sample rate is supported by the output device using * OboeEngine::tryStream. * This function is used by PaOboe_Initialize, IsFormatSupported, and OpenStream. - * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of - * this file); + * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of this file); * @param sampleRate is the sample rate we want to check. * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, - sampleRate, - kUnspecified))) - LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); +PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, + i_sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe."); /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the sample rate we requested wasn't supported. */ @@ -907,11 +918,11 @@ PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, do * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, - sampleRate, - kUnspecified))) - LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe. The device might not support high frequencies."); +PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, + i_sampleRate, + kUnspecified))) + LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe."); /* Since Oboe manages the sample rate in a smart way, we can avoid blocking the process if the sample rate we requested wasn't supported. */ @@ -928,17 +939,15 @@ PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, dou * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsOutputChannelCountSupported( - PaOboeHostApiRepresentation *oboeHostApi, - int32_t numOfChannels) { - if (numOfChannels > 2 || numOfChannels == 0) { +static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { + if (i_numOfChannels > 2 || i_numOfChannels == 0) { LOGE("[PaOboe - IsOutputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, - kUnspecified, - numOfChannels))) + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, + kUnspecified, + i_numOfChannels))) LOGW("[PaOboe - IsOutputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -956,17 +965,15 @@ static PaError IsOutputChannelCountSupported( * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsInputChannelCountSupported( - PaOboeHostApiRepresentation *oboeHostApi, - int32_t numOfChannels) { - if (numOfChannels > 2 || numOfChannels == 0) { +static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { + if (i_numOfChannels > 2 || i_numOfChannels == 0) { LOGE("[PaOboe - IsInputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, - kUnspecified, - numOfChannels))) + if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, + kUnspecified, + i_numOfChannels))) LOGW("[PaOboe - IsInputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -983,136 +990,133 @@ static PaError IsInputChannelCountSupported( * @param hostApiIndex is a PaHostApiIndex, the type used to enumerate the host APIs at runtime. * @return paNoError if no errors occur, or paInsufficientMemory if memory allocation fails; */ -PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) { - PaError m_result = paNoError; - int m_deviceCount; - PaOboeHostApiRepresentation *m_oboeHostApi; - PaDeviceInfo *m_deviceInfoArray; - char *m_deviceName; - - m_oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateZeroInitializedMemory( +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiIndex i_hostApiIndex) { + PaError result = paNoError; + int deviceCount; + PaOboeHostApiRepresentation *oboeHostApi; + PaDeviceInfo *deviceInfoArray; + char *deviceName; + + oboeHostApi = (PaOboeHostApiRepresentation *) PaUtil_AllocateZeroInitializedMemory( sizeof(PaOboeHostApiRepresentation)); - if (!m_oboeHostApi) { - m_result = paInsufficientMemory; + if (!oboeHostApi) { + result = paInsufficientMemory; goto error; } - m_oboeHostApi->oboeEngine = new OboeEngine(); + oboeHostApi->oboeEngine = new OboeEngine(); - m_oboeHostApi->allocations = PaUtil_CreateAllocationGroup(); - if (!m_oboeHostApi->allocations) { - m_result = paInsufficientMemory; + oboeHostApi->allocations = PaUtil_CreateAllocationGroup(); + if (!oboeHostApi->allocations) { + result = paInsufficientMemory; goto error; } - *hostApi = &m_oboeHostApi->inheritedHostApiRep; + *i_hostApi = &oboeHostApi->inheritedHostApiRep; // Initialization of infos. - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paInDevelopment; - (*hostApi)->info.name = "android Oboe"; - (*hostApi)->info.defaultOutputDevice = 0; - (*hostApi)->info.defaultInputDevice = 0; - (*hostApi)->info.deviceCount = 0; - - - m_deviceCount = 1; - (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( - m_oboeHostApi->allocations, sizeof(PaDeviceInfo *) * m_deviceCount); - - if (!(*hostApi)->deviceInfos) { - m_result = paInsufficientMemory; + (*i_hostApi)->info.structVersion = 1; + (*i_hostApi)->info.type = paInDevelopment; + (*i_hostApi)->info.name = "android Oboe"; + (*i_hostApi)->info.defaultOutputDevice = 0; + (*i_hostApi)->info.defaultInputDevice = 0; + (*i_hostApi)->info.deviceCount = 0; + + deviceCount = 1; + (*i_hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( + oboeHostApi->allocations, sizeof(PaDeviceInfo * ) * deviceCount); + + if (!(*i_hostApi)->deviceInfos) { + result = paInsufficientMemory; goto error; } /* allocate all device info structs in a contiguous block */ - m_deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateZeroInitializedMemory( - m_oboeHostApi->allocations, sizeof(PaDeviceInfo) * m_deviceCount); - if (!m_deviceInfoArray) { - m_result = paInsufficientMemory; + deviceInfoArray = (PaDeviceInfo *) PaUtil_GroupAllocateZeroInitializedMemory( + oboeHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount); + if (!deviceInfoArray) { + result = paInsufficientMemory; goto error; } - for (int i = 0; i < m_deviceCount; ++i) { - PaDeviceInfo *m_deviceInfo = &m_deviceInfoArray[i]; - m_deviceInfo->structVersion = 2; - m_deviceInfo->hostApi = hostApiIndex; + for (int i = 0; i < deviceCount; ++i) { + PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; - /* OboeEngine will handle manual device selection through the use of - PaOboe_SetSelectedDevice via a JNI interface that can be implemented. + /* OboeEngine will handle manual device selection through the use of PaOboe_SetSelectedDevice. Portaudio doesn't need to know about this, so we just use a default device. */ - - m_deviceInfo->name = "default"; + deviceInfo->name = "default"; /* Try channels in order of preference - Stereo > Mono. */ - const int32_t m_channelsToTry[] = {2, 1}; - const int32_t m_channelsToTryLength = 2; + const int32_t channelsToTry[] = {2, 1}; + const int32_t channelsToTryLength = 2; - m_deviceInfo->maxOutputChannels = 0; - m_deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + deviceInfo->maxInputChannels = 0; - for (i = 0; i < m_channelsToTryLength; ++i) { - if (IsOutputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { - m_deviceInfo->maxOutputChannels = m_channelsToTry[i]; + for (i = 0; i < channelsToTryLength; ++i) { + if (IsOutputChannelCountSupported(oboeHostApi, channelsToTry[i]) == paNoError) { + deviceInfo->maxOutputChannels = channelsToTry[i]; break; } } - for (i = 0; i < m_channelsToTryLength; ++i) { - if (IsInputChannelCountSupported(m_oboeHostApi, m_channelsToTry[i]) == paNoError) { - m_deviceInfo->maxInputChannels = m_channelsToTry[i]; + for (i = 0; i < channelsToTryLength; ++i) { + if (IsInputChannelCountSupported(oboeHostApi, channelsToTry[i]) == paNoError) { + deviceInfo->maxInputChannels = channelsToTry[i]; break; } } /* check sample rates in order of preference */ - const int32_t m_sampleRates[] = {48000, 44100, 32000, 24000, 16000}; - const int32_t m_numberOfSampleRates = 5; + const int32_t sampleRates[] = {48000, 44100, 32000, 24000, 16000}; + const int32_t numberOfSampleRates = 5; - m_deviceInfo->defaultSampleRate = m_sampleRates[0]; + deviceInfo->defaultSampleRate = sampleRates[0]; - for (i = 0; i < m_numberOfSampleRates; ++i) { + for (i = 0; i < numberOfSampleRates; ++i) { if (IsOutputSampleRateSupported( - m_oboeHostApi, m_sampleRates[i]) == paNoError && + oboeHostApi, sampleRates[i]) == paNoError && IsInputSampleRateSupported( - m_oboeHostApi, m_sampleRates[i]) == paNoError) { - m_deviceInfo->defaultSampleRate = m_sampleRates[i]; + oboeHostApi, sampleRates[i]) == paNoError) { + deviceInfo->defaultSampleRate = sampleRates[i]; break; } } - if (m_deviceInfo->defaultSampleRate == 0) + if (deviceInfo->defaultSampleRate == 0) goto error; - /* If the user has set nativeBufferSize by querying the optimal buffer size via java, + /* If the user has set g_nativeBufferSize by querying the optimal buffer size via java, use the user-defined value since that will offer the lowest possible latency. */ - if (nativeBufferSize != 0) { - m_deviceInfo->defaultLowInputLatency = - (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultLowOutputLatency = - (double) nativeBufferSize / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighInputLatency = - (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighOutputLatency = - (double) nativeBufferSize * 4 / m_deviceInfo->defaultSampleRate; + if (g_nativeBufferSize != 0) { + deviceInfo->defaultLowInputLatency = + (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + deviceInfo->defaultLowOutputLatency = + (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighInputLatency = + (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighOutputLatency = + (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; } else { - m_deviceInfo->defaultLowInputLatency = - (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultLowOutputLatency = - (double) GetApproximateLowBufferSize() / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighInputLatency = - (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; - m_deviceInfo->defaultHighOutputLatency = - (double) GetApproximateLowBufferSize() * 4 / m_deviceInfo->defaultSampleRate; + deviceInfo->defaultLowInputLatency = + (double) GetApproximateLowBufferSize() / deviceInfo->defaultSampleRate; + deviceInfo->defaultLowOutputLatency = + (double) GetApproximateLowBufferSize() / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighInputLatency = + (double) GetApproximateLowBufferSize() * 4 / deviceInfo->defaultSampleRate; + deviceInfo->defaultHighOutputLatency = + (double) GetApproximateLowBufferSize() * 4 / deviceInfo->defaultSampleRate; } - (*hostApi)->deviceInfos[i] = m_deviceInfo; - ++(*hostApi)->info.deviceCount; + (*i_hostApi)->deviceInfos[i] = deviceInfo; + ++(*i_hostApi)->info.deviceCount; } - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; + (*i_hostApi)->Terminate = Terminate; + (*i_hostApi)->OpenStream = OpenStream; + (*i_hostApi)->IsFormatSupported = IsFormatSupported; - PaUtil_InitializeStreamInterface(&m_oboeHostApi->callbackStreamInterface, + PaUtil_InitializeStreamInterface(&oboeHostApi->callbackStreamInterface, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, @@ -1121,7 +1125,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable); - PaUtil_InitializeStreamInterface(&m_oboeHostApi->blockingStreamInterface, + PaUtil_InitializeStreamInterface(&oboeHostApi->blockingStreamInterface, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, @@ -1129,23 +1133,23 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable); - if (m_result == paNoError) + if (result == paNoError) LOGV("[PaOboe - Initialize]\t Oboe host API successfully initialized"); else - LOGE("[PaOboe - Initialize]\t An unusual error occurred. Error code: %d", m_result); - return m_result; + LOGE("[PaOboe - Initialize]\t An unusual error occurred. Error code: %d", result); + return result; error: - if (m_oboeHostApi) { - if (m_oboeHostApi->allocations) { - PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); - PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + if (oboeHostApi) { + if (oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(m_oboeHostApi); + PaUtil_FreeMemory(boeHostApi); } - LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", m_result); - return m_result; + LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", result); + return result; } @@ -1154,21 +1158,21 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the * interface to a host API (see struct in "pa_hostapi.h"). */ -static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { - auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; +static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { + auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; - if (!(m_oboeHostApi->oboeEngine->closeStream())) + if (!(oboeHostApi->oboeEngine->closeStream(nullptr))) LOGW("[PaOboe - Terminate]\t Couldn't close the streams correctly - see OboeEngine::CloseStream logs."); - if(m_oboeHostApi->oboeEngine != nullptr) - delete m_oboeHostApi->oboeEngine; + if (oboeHostApi->oboeEngine != nullptr) + delete oboeHostApi->oboeEngine; - if (m_oboeHostApi->allocations) { - PaUtil_FreeAllAllocations(m_oboeHostApi->allocations); - PaUtil_DestroyAllocationGroup(m_oboeHostApi->allocations); + if (oboeHostApi->allocations) { + PaUtil_FreeAllAllocations(oboeHostApi->allocations); + PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(m_oboeHostApi); + PaUtil_FreeMemory(oboeHostApi); } @@ -1182,116 +1186,103 @@ static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { * @return paNoError (== paFormatIsSupported) if no errors occur, otherwise returns an appropriate * PaError message. */ -static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate) { - PaError m_outcome; - int m_inputChannelCount, m_outputChannelCount; - PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; - auto *m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; - - if (inputParameters) { - m_inputChannelCount = inputParameters->channelCount; - m_inputSampleFormat = inputParameters->sampleFormat; +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, + const PaStreamParameters *i_inputParameters, + const PaStreamParameters *i_outputParameters, + double i_sampleRate) { + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + + if (i_inputParameters) { + inputChannelCount = i_inputParameters->channelCount; + inputSampleFormat = i_inputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ - if (m_inputSampleFormat & paCustomFormat) { - m_outcome = paSampleFormatNotSupported; - return m_outcome; + if (inputSampleFormat & paCustomFormat) { + return paSampleFormatNotSupported; } /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) { - m_outcome = paInvalidDevice; - return m_outcome; + if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) { + return paInvalidDevice; } /* check that input device can support inputChannelCount */ - if (m_inputChannelCount > - hostApi->deviceInfos[inputParameters->device]->maxInputChannels) { - m_outcome = paInvalidChannelCount; - return m_outcome; + if (inputChannelCount > + i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { + return paInvalidChannelCount; } /* validate inputStreamInfo */ - if (inputParameters->hostApiSpecificStreamInfo) { + if (i_inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. - InputPreset m_androidRecordingPreset = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; - if (m_androidRecordingPreset != InputPreset::Generic && - m_androidRecordingPreset != InputPreset::Camcorder && - m_androidRecordingPreset != InputPreset::VoiceRecognition && - m_androidRecordingPreset != InputPreset::VoiceCommunication - m_androidRecordingPreset != InputPreset::VoicePerformance - ) { - m_outcome = paIncompatibleHostApiSpecificStreamInfo; - return m_outcome; + InputPreset androidRecordingPreset = + ((PaOboeStreamInfo *) i_inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (androidRecordingPreset != InputPreset::Generic && + androidRecordingPreset != InputPreset::Camcorder && + androidRecordingPreset != InputPreset::VoiceRecognition && + androidRecordingPreset != InputPreset::VoiceCommunication + androidRecordingPreset != InputPreset::VoicePerformance) { + return paIncompatibleHostApiSpecificStreamInfo; } } } else { - m_inputChannelCount = 0; + inputChannelCount = 0; } - if (outputParameters) { - m_outputChannelCount = outputParameters->channelCount; - m_outputSampleFormat = outputParameters->sampleFormat; + if (i_outputParameters) { + outputChannelCount = i_outputParameters->channelCount; + outputSampleFormat = i_outputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ - if (m_outputSampleFormat & paCustomFormat) { - m_outcome = paSampleFormatNotSupported; - return m_outcome; + if (outputSampleFormat & paCustomFormat) { + return paSampleFormatNotSupported; } /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) { - m_outcome = paInvalidDevice; - return m_outcome; + if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) { + return paInvalidDevice; } /* check that output device can support outputChannelCount */ - if (m_outputChannelCount > - hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) { - m_outcome = paInvalidChannelCount; - return m_outcome; + if (outputChannelCount > + i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { + return paInvalidChannelCount; } /* validate outputStreamInfo */ - if (outputParameters->hostApiSpecificStreamInfo) { + if (i_outputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. - Usage m_androidOutputUsage = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; - if (m_androidOutputUsage != Usage::Media && - m_androidOutputUsage != Usage::Notification && - m_androidOutputUsage != Usage::NotificationEvent && - m_androidOutputUsage != Usage::NotificationRingtone && - m_androidOutputUsage != Usage::VoiceCommunication && - m_androidOutputUsage != Usage::VoiceCommunicationSignalling && - m_androidOutputUsage != Usage::Alarm && - m_androidOutputUsage != Usage::Game - ) { - m_outcome = paIncompatibleHostApiSpecificStreamInfo; - return m_outcome; + Usage androidOutputUsage = + ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (androidOutputUsage != Usage::Media && + androidOutputUsage != Usage::Notification && + androidOutputUsage != Usage::NotificationEvent && + androidOutputUsage != Usage::NotificationRingtone && + androidOutputUsage != Usage::VoiceCommunication && + androidOutputUsage != Usage::VoiceCommunicationSignalling && + androidOutputUsage != Usage::Alarm && + androidOutputUsage != Usage::Game) { + return paIncompatibleHostApiSpecificStreamInfo; } } } else { - m_outputChannelCount = 0; + outputChannelCount = 0; } - if (m_outputChannelCount > 0) { - if (IsOutputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { - m_outcome = paInvalidSampleRate; - return m_outcome; + if (outputChannelCount > 0) { + if (IsOutputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + return paInvalidSampleRate; } } - if (m_inputChannelCount > 0) { - if (IsInputSampleRateSupported(m_oboeHostApi, sampleRate) != paNoError) { - m_outcome = paInvalidSampleRate; - return m_outcome; + if (inputChannelCount > 0) { + if (IsInputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + return paInvalidSampleRate; } } @@ -1310,14 +1301,14 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(PaOboeHostApiRepresentation *oboeHostApi, - Usage androidOutputUsage, double sampleRate) { - - return oboeHostApi->oboeEngine->openStream(Direction::Output, - sampleRate, - androidOutputUsage, - Generic //Won't be used, so we put the default value. - ); +static PaError InitializeOutputStream(PaOboeHostApiRepresentation *i_oboeHostApi, + Usage i_androidOutputUsage, double i_sampleRate) { + + return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + Direction::Output, + sampleRate, + androidOutputUsage, + Generic); //Input preset won't be used, so we put the default value. } @@ -1332,14 +1323,14 @@ static PaError InitializeOutputStream(PaOboeHostApiRepresentation *oboeHostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeInputStream(PaOboeHostApiRepresentation *oboeHostApi, - InputPreset androidInputPreset, double sampleRate) { - - return oboeHostApi->oboeEngine->openStream(Direction::Input, - sampleRate, - Usage::Media, //Won't be used, so we put the default value. - androidInputPreset - ); +static PaError InitializeInputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, + InputPreset i_androidInputPreset, double i_sampleRate) { + + return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + Direction::Input, + i_sampleRate, + Usage::Media, //Usage won't be used, so we put the default value. + i_androidInputPreset); } @@ -1360,203 +1351,200 @@ static PaError InitializeInputStream(PaOboeHostApiRepresentation *oboeHostApi, * manipulation or checks. * @return paNoError if no errors occur, or other error codes accordingly with what goes wrong. */ -static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, - PaStream **s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData) { - PaError m_error = paNoError; - auto m_oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; - unsigned long m_framesPerHostBuffer; /* these may not be equivalent for all implementations */ - int m_inputChannelCount, m_outputChannelCount; - PaSampleFormat m_inputSampleFormat, m_outputSampleFormat; - PaSampleFormat m_hostInputSampleFormat, m_hostOutputSampleFormat; - - - //FIXME: add a function that lets the user choose usage and preset - Usage m_androidOutputUsage = Usage::VoiceCommunication; - InputPreset m_androidInputPreset = InputPreset::Generic; - - OboeStream *m_oboeStream = m_oboeHostApi->oboeEngine->initializeOboeStream(); - - if (!m_oboeStream) { - m_error = paInsufficientMemory; +static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, + PaStream **i_paStream, + const PaStreamParameters *i_inputParameters, + const PaStreamParameters *i_outputParameters, + double i_sampleRate, + unsigned long i_framesPerBuffer, + PaStreamFlags i_streamFlags, + PaStreamCallback *i_streamCallback, + void *i_userData) { + PaError error = paNoError; + auto oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + unsigned long framesPerHostBuffer; /* these may not be equivalent for all implementations */ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + + + //TODO: add a function that lets the user choose usage and preset + Usage androidOutputUsage = Usage::VoiceCommunication; + InputPreset androidInputPreset = InputPreset::Generic; + + OboeStream *oboeStream = oboeHostApi->oboeEngine->allocateOboeStream(); + + if (!oboeStream) { + error = paInsufficientMemory; goto error; } LOGI("[PaOboe - OpenStream]\t OpenStream called."); - if (inputParameters) { - m_inputChannelCount = inputParameters->channelCount; - m_inputSampleFormat = inputParameters->sampleFormat; + if (i_inputParameters) { + inputChannelCount = i_inputParameters->channelCount; + inputSampleFormat = i_inputParameters->sampleFormat; - /* Oboe supports alternate device specification with API>=28, but for now we reject the use of - paUseHostApiSpecificDeviceSpecification and stick with the default.*/ - if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) + /* Oboe supports alternate device specification with API>=28, but here we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via + PaOboe_SetSelectedDevice. */ + if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that input device can support inputChannelCount */ - if (m_inputChannelCount > hostApi->deviceInfos[inputParameters->device]->maxInputChannels) + if (inputChannelCount > i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) return paInvalidChannelCount; /* validate inputStreamInfo */ - if (inputParameters->hostApiSpecificStreamInfo) { + if (i_inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. - m_androidInputPreset = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; - if (m_androidInputPreset != InputPreset::Generic && - m_androidInputPreset != InputPreset::Camcorder && - m_androidInputPreset != InputPreset::VoiceRecognition && - m_androidInputPreset != InputPreset::VoiceCommunication - m_androidInputPreset != InputPreset::VoicePerformance - ) + androidInputPreset = + ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + if (androidInputPreset != InputPreset::Generic && + androidInputPreset != InputPreset::Camcorder && + androidInputPreset != InputPreset::VoiceRecognition && + androidInputPreset != InputPreset::VoiceCommunication + androidInputPreset != InputPreset::VoicePerformance) return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paFloat32" with whatever format you prefer - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ - m_hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paFloat32, m_inputSampleFormat); - m_oboeStream->inputFormat = m_hostInputSampleFormat; + hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( + paFloat32, inputSampleFormat); + oboeStream->inputFormat = hostInputSampleFormat; } else { - m_inputChannelCount = 0; - m_inputSampleFormat = m_hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ - m_oboeStream->inputFormat = m_hostInputSampleFormat; + inputChannelCount = 0; + inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ + oboeStream->inputFormat = hostInputSampleFormat; } - if (outputParameters) { - m_outputChannelCount = outputParameters->channelCount; - m_outputSampleFormat = outputParameters->sampleFormat; + if (i_outputParameters) { + outputChannelCount = i_outputParameters->channelCount; + outputSampleFormat = i_outputParameters->sampleFormat; - /* Oboe supports alternate device specification with API>=28, but for now we reject the use of - paUseHostApiSpecificDeviceSpecification and stick with the default.*/ - if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) + /* Oboe supports alternate device specification with API>=28, but here we reject the use of + paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via + PaOboe_SetSelectedDevice. */ + if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that output device can support outputChannelCount */ - if (m_outputChannelCount > - hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) + if (outputChannelCount > + i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) return paInvalidChannelCount; /* validate outputStreamInfo */ - if (outputParameters->hostApiSpecificStreamInfo) { - m_androidOutputUsage = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; - if (m_androidOutputUsage != Usage::Media && - m_androidOutputUsage != Usage::Notification && - m_androidOutputUsage != Usage::NotificationEvent && - m_androidOutputUsage != Usage::NotificationRingtone && - m_androidOutputUsage != Usage::VoiceCommunication && - m_androidOutputUsage != Usage::VoiceCommunicationSignalling && - m_androidOutputUsage != Usage::Alarm - // See if more are needed. - ) + if (i_outputParameters->hostApiSpecificStreamInfo) { + androidOutputUsage = + ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + if (androidOutputUsage != Usage::Media && + androidOutputUsage != Usage::Notification && + androidOutputUsage != Usage::NotificationEvent && + androidOutputUsage != Usage::NotificationRingtone && + androidOutputUsage != Usage::VoiceCommunication && + androidOutputUsage != Usage::VoiceCommunicationSignalling && + androidOutputUsage != Usage::Alarm && + androidOutputUsage != Usage::Game) return paIncompatibleHostApiSpecificStreamInfo; } /* FIXME: Replace "paFloat32" with whatever format you prefer - PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ - m_hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( + hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paFloat32, m_outputSampleFormat); - m_oboeStream->outputFormat = m_hostOutputSampleFormat; + oboeStream->outputFormat = hostOutputSampleFormat; } else { - m_outputChannelCount = 0; - m_outputSampleFormat = m_hostOutputSampleFormat = paFloat32; - m_oboeStream->outputFormat = m_hostOutputSampleFormat; + outputChannelCount = 0; + outputSampleFormat = hostOutputSampleFormat = paFloat32; + oboeStream->outputFormat = hostOutputSampleFormat; } /* validate platform specific flags */ - if ((streamFlags & paPlatformSpecificFlags) != 0) + if ((i_streamFlags & paPlatformSpecificFlags) != 0) return paInvalidFlag; /* unexpected platform specific flag */ - if (framesPerBuffer == paFramesPerBufferUnspecified) { - if (outputParameters) { - m_framesPerHostBuffer = - (unsigned long) (outputParameters->suggestedLatency * sampleRate); + if (i_framesPerBuffer == paFramesPerBufferUnspecified) { + if (i_outputParameters) { + framesPerHostBuffer = + (unsigned long) (i_outputParameters->suggestedLatency * i_sampleRate); } else { - m_framesPerHostBuffer = - (unsigned long) (inputParameters->suggestedLatency * sampleRate); + framesPerHostBuffer = + (unsigned long) (i_inputParameters->suggestedLatency * i_sampleRate); } } else { - m_framesPerHostBuffer = framesPerBuffer; + framesPerHostBuffer = i_framesPerBuffer; } - m_oboeHostApi->oboeEngine->setEngineAddress( - reinterpret_cast(m_oboeHostApi->oboeEngine)); - - if (streamCallback) { - PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), - &m_oboeHostApi->callbackStreamInterface, - streamCallback, userData); + if (i_streamCallback) { + PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), + &oboeHostApi->callbackStreamInterface, + i_streamCallback, i_userData); } else { - PaUtil_InitializeStreamRepresentation(&(m_oboeStream->streamRepresentation), - &m_oboeHostApi->blockingStreamInterface, - streamCallback, userData); + PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), + &oboeHostApi->blockingStreamInterface, + i_streamCallback, i_userData); } - PaUtil_InitializeCpuLoadMeasurer(&(m_oboeStream->cpuLoadMeasurer), sampleRate); - - m_error = PaUtil_InitializeBufferProcessor(&(m_oboeStream->bufferProcessor), - m_inputChannelCount, - m_inputSampleFormat, - m_hostInputSampleFormat, - m_outputChannelCount, - m_outputSampleFormat, - m_hostOutputSampleFormat, - sampleRate, streamFlags, - framesPerBuffer, - m_framesPerHostBuffer, - paUtilFixedHostBufferSize, - streamCallback, userData); - if (m_error != paNoError) + PaUtil_InitializeCpuLoadMeasurer(&(oboeStream->cpuLoadMeasurer), i_sampleRate); + + error = PaUtil_InitializeBufferProcessor(&(oboeStream->bufferProcessor), + inputChannelCount, + inputSampleFormat, + hostInputSampleFormat, + outputChannelCount, + outputSampleFormat, + hostOutputSampleFormat, + i_sampleRate, i_streamFlags, + i_framesPerBuffer, + framesPerHostBuffer, + paUtilFixedHostBufferSize, + i_streamCallback, i_userData); + if (error != paNoError) goto error; - m_oboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; - m_oboeStream->isBlocking = (streamCallback == nullptr); - m_oboeStream->framesPerHostCallback = m_framesPerHostBuffer; - m_oboeStream->bytesPerFrame = sizeof(int16_t); - m_oboeStream->cbFlags = 0; - m_oboeStream->isStopped = true; - m_oboeStream->isActive = false; + oboeStream->streamRepresentation.streamInfo.sampleRate = i_sampleRate; + oboeStream->isBlocking = (i_streamCallback == nullptr); + oboeStream->framesPerHostCallback = framesPerHostBuffer; + oboeStream->bytesPerFrame = sizeof(int16_t); + oboeStream->cbFlags = 0; + oboeStream->isStopped = true; + oboeStream->isActive = false; - if (!(m_oboeStream->isBlocking)) {} + if (!(oboeStream->isBlocking)) {} // PaUnixThreading_Initialize(); TODO: see if threading works with this version of PortAudio - if (m_inputChannelCount > 0) { - m_oboeStream->hasInput = true; - m_oboeStream->streamRepresentation.streamInfo.inputLatency = + if (inputChannelCount > 0) { + oboeStream->hasInput = true; + oboeStream->streamRepresentation.streamInfo.inputLatency = ((PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( - &(m_oboeStream->bufferProcessor)) + - m_oboeStream->framesPerHostCallback) / sampleRate; - ENSURE(InitializeInputStream(m_oboeHostApi, - m_androidInputPreset, sampleRate), + &(oboeStream->bufferProcessor)) + + oboeStream->framesPerHostCallback) / i_sampleRate; + ENSURE(InitializeInputStream(oboeStream, oboeHostApi, + androidInputPreset, i_sampleRate), "Initializing inputstream failed") - } else { m_oboeStream->hasInput = false; } + } else { oboeStream->hasInput = false; } - if (m_outputChannelCount > 0) { - m_oboeStream->hasOutput = true; - m_oboeStream->streamRepresentation.streamInfo.outputLatency = + if (outputChannelCount > 0) { + oboeStream->hasOutput = true; + oboeStream->streamRepresentation.streamInfo.outputLatency = ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( - &m_oboeStream->bufferProcessor) - + m_oboeStream->framesPerHostCallback) / sampleRate; - ENSURE(InitializeOutputStream(m_oboeHostApi, - m_androidOutputUsage, sampleRate), + &oboeStream->bufferProcessor) + + oboeStream->framesPerHostCallback) / i_sampleRate; + ENSURE(InitializeOutputStream(oboeHostApi, + androidOutputUsage, i_sampleRate), "Initializing outputstream failed"); - } else { m_oboeStream->hasOutput = false; } + } else { oboeStream->hasOutput = false; } - *s = (PaStream *) m_oboeStream; - return m_error; + *i_paStream = (PaStream *) oboeStream; + return error; error: - if (m_oboeStream) - PaUtil_FreeMemory(m_oboeStream); + if (oboeStream) + PaUtil_FreeMemory(oboeStream); - LOGE("[PaOboe - OpenStream]\t Error opening stream(s). Error code: %d", m_error); + LOGE("[PaOboe - OpenStream]\t Error opening stream(s). Error code: %d", error); - return m_error; + return error; } @@ -1568,29 +1556,29 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, * portaudio, which holds the information of our OboeStream. * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. */ -static PaError CloseStream(PaStream *s) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError CloseStream(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); - if (!(m_oboeEngine->closeStream())) - LOGW("[PaOboe - CloseStream]\t Couldn't close the stream(s) correctly - see OboeEngine::CloseStream logs."); + if (!(oboeEngine->closeStream(oboeStream))) + LOGW("[PaOboe - CloseStream]\t Some errors have occurred in closing oboe streams - see OboeEngine::CloseStream logs."); - PaUtil_TerminateBufferProcessor(&m_stream->bufferProcessor); - PaUtil_TerminateStreamRepresentation(&m_stream->streamRepresentation); + PaUtil_TerminateBufferProcessor(&oboeStream->bufferProcessor); + PaUtil_TerminateStreamRepresentation(&oboeStream->streamRepresentation); - for (int i = 0; i < numberOfBuffers; ++i) { - if (m_stream->hasOutput) - PaUtil_FreeMemory(m_stream->outputBuffers[i]); - if (m_stream->hasInput) - PaUtil_FreeMemory(m_stream->inputBuffers[i]); + for (int i = 0; i < g_numberOfBuffers; ++i) { + if (oboeStream->hasOutput) + PaUtil_FreeMemory(oboeStream->outputBuffers[i]); + if (oboeStream->hasInput) + PaUtil_FreeMemory(oboeStream->inputBuffers[i]); } - if (m_stream->hasOutput) - PaUtil_FreeMemory(m_stream->outputBuffers); - if (m_stream->hasInput) - PaUtil_FreeMemory(m_stream->inputBuffers); + if (oboeStream->hasOutput) + PaUtil_FreeMemory(oboeStream->outputBuffers); + if (oboeStream->hasInput) + PaUtil_FreeMemory(oboeStream->inputBuffers); - PaUtil_FreeMemory(m_stream); + PaUtil_FreeMemory(oboeStream); return paNoError; } @@ -1602,53 +1590,52 @@ static PaError CloseStream(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::startStream fails. */ -static PaError StartStream(PaStream *s) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError StartStream(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); - PaUtil_ResetBufferProcessor(&m_stream->bufferProcessor); + PaUtil_ResetBufferProcessor(&oboeStream->bufferProcessor); - //Checking if the stream(s) are already active. TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) - if (m_stream->isActive) { + //Checking if the stream(s) are already active. + //TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) + if (oboeStream->isActive) { LOGW("[PaOboe - StartStream]\t Stream was already active, stopping..."); - StopStream(s); + StopStream(i_paStream); LOGW("[PaOboe - StartStream]\t Restarting..."); - StartStream(s); + StartStream(i_paStream); } - m_stream->currentOutputBuffer = 0; - m_stream->currentInputBuffer = 0; + oboeStream->currentOutputBuffer = 0; + oboeStream->currentInputBuffer = 0; /* Initialize buffers */ - for (int i = 0; i < numberOfBuffers; ++i) { - if (m_stream->hasOutput) { - memset(m_stream->outputBuffers[m_stream->currentOutputBuffer], 0, - m_stream->framesPerHostCallback * m_stream->bytesPerFrame * - m_stream->bufferProcessor.outputChannelCount - ); - m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; + for (int i = 0; i < g_numberOfBuffers; ++i) { + if (oboeStream->hasOutput) { + memset(oboeStream->outputBuffers[oboeStream->currentOutputBuffer], 0, + oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.outputChannelCount); + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; } - if (m_stream->hasInput) { - memset(m_stream->inputBuffers[m_stream->currentInputBuffer], 0, - m_stream->framesPerHostCallback * m_stream->bytesPerFrame * - m_stream->bufferProcessor.inputChannelCount - ); - m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; + if (oboeStream->hasInput) { + memset(oboeStream->inputBuffers[oboeStream->currentInputBuffer], 0, + oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.inputChannelCount); + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; } } /* Start the processing thread.*/ - if (!m_stream->isBlocking) { - m_stream->callbackResult = paContinue; - m_stream->oboeCallbackResult = DataCallbackResult::Continue; + if (!oboeStream->isBlocking) { + oboeStream->callbackResult = paContinue; + oboeStream->oboeCallbackResult = DataCallbackResult::Continue; } - m_stream->isStopped = false; - m_stream->isActive = true; - m_stream->doStop = false; - m_stream->doAbort = false; + oboeStream->isStopped = false; + oboeStream->isActive = true; + oboeStream->doStop = false; + oboeStream->doAbort = false; - if (!(m_oboeEngine->startStream())) + if (!(oboeEngine->startStream(oboeStream))) return paUnanticipatedHostError; else return paNoError; @@ -1662,30 +1649,30 @@ static PaError StartStream(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::stopStream fails. */ -static PaError StopStream(PaStream *s) { - PaError m_error = paNoError; - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError StopStream(PaStream *i_paStream) { + PaError error = paNoError; + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); - if (m_stream->isStopped) { + if (oboeStream->isStopped) { LOGW("[PaOboe - StopStream]\t Stream was already stopped."); } else { - if (!(m_stream->isBlocking)) { - m_stream->doStop = true; + if (!(oboeStream->isBlocking)) { + oboeStream->doStop = true; } - if (!(m_oboeEngine->stopStream())) { + if (!(oboeEngine->stopStream(oboeStream))) { LOGE("[PaOboe - StopStream]\t Couldn't stop the stream(s) correctly - see OboeEngine::StopStream logs."); - m_error = paUnanticipatedHostError; + error = paUnanticipatedHostError; } - m_stream->isActive = false; - m_stream->isStopped = true; - if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) - m_stream->streamRepresentation.streamFinishedCallback( - m_stream->streamRepresentation.userData); + oboeStream->isActive = false; + oboeStream->isStopped = true; + if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) + oboeStream->streamRepresentation.streamFinishedCallback( + oboeStream->streamRepresentation.userData); } - return m_error; + return error; } @@ -1696,29 +1683,29 @@ static PaError StopStream(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::abortStream fails. */ -static PaError AbortStream(PaStream *s) { - PaError m_error = paNoError; - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); +static PaError AbortStream(PaStream *i_paStream) { + PaError error = paNoError; + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); LOGI("[PaOboe - AbortStream]\t Aborting stream."); - if (!m_stream->isBlocking) { - m_stream->doAbort = true; + if (!oboeStream->isBlocking) { + oboeStream->doAbort = true; } /* stop immediately so enqueue has no effect */ - if (!(m_oboeEngine->abortStream())) { + if (!(oboeEngine->abortStream(oboeStream))) { LOGE("[PaOboe - AbortStream]\t Couldn't abort the stream - see OboeEngine::abortStream logs."); - m_error = paUnanticipatedHostError; + error = paUnanticipatedHostError; } - m_stream->isActive = false; - m_stream->isStopped = true; - if (m_stream->streamRepresentation.streamFinishedCallback != nullptr) - m_stream->streamRepresentation.streamFinishedCallback( - m_stream->streamRepresentation.userData); + oboeStream->isActive = false; + oboeStream->isStopped = true; + if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) + oboeStream->streamRepresentation.streamFinishedCallback( + oboeStream->streamRepresentation.userData); - return m_error; + return error; } @@ -1730,26 +1717,25 @@ static PaError AbortStream(PaStream *s) { * @param frames is the total number of frames to read. * @return paInternalError if OboeEngine::readStream fails, paNoError otherwise. */ -static PaError ReadStream(PaStream *s, void *buffer, unsigned long frames) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); - void *m_userBuffer = buffer; - unsigned m_framesToRead; - PaError m_error = paNoError; - - while (frames > 0) { - m_framesToRead = PA_MIN(m_stream->framesPerHostCallback, frames); - - if (!(m_oboeEngine->readStream(m_userBuffer, - m_framesToRead * - m_stream->bufferProcessor.inputChannelCount))) - m_error = paInternalError; - - m_stream->currentInputBuffer = (m_stream->currentInputBuffer + 1) % numberOfBuffers; - frames -= m_framesToRead; +static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_frames) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); + void *userBuffer = i_buffer; + unsigned framesToRead; + PaError error = paNoError; + + while (i_frames > 0) { + framesToRead = PA_MIN(oboeStream->framesPerHostCallback, i_frames); + + if (!(oboeEngine->readStream(userBuffer, framesToRead * + oboeStream->bufferProcessor.inputChannelCount))) + error = paInternalError; + + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; + i_frames -= framesToRead; } - return m_error; + return error; } @@ -1761,26 +1747,25 @@ static PaError ReadStream(PaStream *s, void *buffer, unsigned long frames) { * @param frames is the total number of frames to write. * @return paInternalError if OboeEngine::writeStream fails, paNoError otherwise. */ -static PaError WriteStream(PaStream *s, const void *buffer, unsigned long frames) { - auto *m_stream = (OboeStream *) s; - auto *m_oboeEngine = reinterpret_cast(m_stream->engineAddress); - const void *m_userBuffer = buffer; - unsigned m_framesToWrite; - PaError m_error = paNoError; - - while (frames > 0) { - m_framesToWrite = PA_MIN(m_stream->framesPerHostCallback, frames); - - if (!(m_oboeEngine->writeStream(m_userBuffer, - m_framesToWrite * - m_stream->bufferProcessor.outputChannelCount))) - m_error = paInternalError; - - m_stream->currentOutputBuffer = (m_stream->currentOutputBuffer + 1) % numberOfBuffers; - frames -= m_framesToWrite; +static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned long i_frames) { + auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeEngine = oboeStream->getEngineAddress(); + const void *userBuffer = i_buffer; + unsigned framesToWrite; + PaError error = paNoError; + + while (i_frames > 0) { + framesToWrite = PA_MIN(stream->framesPerHostCallback, i_frames); + + if (!(oboeEngine->writeStream(userBuffer, framesToWrite * + oboeStream->bufferProcessor.outputChannelCount))) + error = paInternalError; + + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; + i_frames -= framesToWrite; } - return m_error; + return error; } @@ -1792,9 +1777,9 @@ static PaError WriteStream(PaStream *s, const void *buffer, unsigned long frames * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be read without waiting. */ -static signed long GetStreamReadAvailable(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentInputBuffer); +static signed long GetStreamReadAvailable(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentInputBuffer); } @@ -1804,9 +1789,9 @@ static signed long GetStreamReadAvailable(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be written without waiting. */ -static signed long GetStreamWriteAvailable(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->framesPerHostCallback * (numberOfBuffers - m_stream->currentOutputBuffer); +static signed long GetStreamWriteAvailable(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentOutputBuffer); } @@ -1816,9 +1801,9 @@ static signed long GetStreamWriteAvailable(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is stopped, or zero (0) when the stream is running. */ -static PaError IsStreamStopped(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->isStopped; +static PaError IsStreamStopped(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->isStopped; } @@ -1828,9 +1813,9 @@ static PaError IsStreamStopped(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is active (ie playing or recording audio), or zero (0) otherwise. */ -static PaError IsStreamActive(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return m_stream->isActive; +static PaError IsStreamActive(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return oboeStream->isActive; } @@ -1840,7 +1825,7 @@ static PaError IsStreamActive(PaStream *s) { * portaudio, which holds the information of our OboeStream. * @return The stream's current time in seconds, or 0 if an error occurred. */ -static PaTime GetStreamTime(PaStream *s) { +static PaTime GetStreamTime(PaStream *i_paStream) { return PaUtil_GetTime(); } @@ -1856,9 +1841,9 @@ static PaTime GetStreamTime(PaStream *s) { * A value of 0.0 will always be returned for a blocking read/write stream, or if an error * occurs. */ -static double GetStreamCpuLoad(PaStream *s) { - auto *m_stream = (OboeStream *) s; - return PaUtil_GetCpuLoad(&m_stream->cpuLoadMeasurer); +static double GetStreamCpuLoad(PaStream *i_paStream) { + auto *oboeStream = (OboeStream *) i_paStream; + return PaUtil_GetCpuLoad(&oboeStream->cpuLoadMeasurer); } @@ -1879,29 +1864,29 @@ static unsigned long GetApproximateLowBufferSize() { /*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ -void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { +void PaOboe_SetSelectedDevice(Direction i_direction, int32_t i_deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); - if (direction == Direction::Input) - g_inputDeviceId = deviceID; + if (i_direction == Direction::Input) + g_inputDeviceId = i_deviceID; else - g_outputDeviceId = deviceID; + g_outputDeviceId = i_deviceID; } -void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode){ - if(direction == Direction::Input) { - g_inputPerfMode = performanceMode; +void PaOboe_SetPerformanceMode(oboe::Direction i_direction, oboe::PerformanceMode i_performanceMode) { + if (i_direction == Direction::Input) { + g_inputPerfMode = i_performanceMode; } else { - g_outputPerfMode = performanceMode; + g_outputPerfMode = i_performanceMode; } } -void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { - nativeBufferSize = bufferSize; +void PaOboe_SetNativeBufferSize(unsigned long i_bufferSize) { + g_nativeBufferSize = i_bufferSize; } -void PaOboe_SetNumberOfBuffers(unsigned buffers) { - numberOfBuffers = buffers; +void PaOboe_SetNumberOfBuffers(unsigned i_numberOfBuffers) { + g_numberOfBuffers = i_numberOfBuffers; } From 8bc554f1b54b7339d0ac3e2b1d48d24bc7416378 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 12:04:52 +0200 Subject: [PATCH 097/125] Added separate callback class to fix the single-stream issue. --- include/pa_oboe.h | 1 + src/hostapi/oboe/pa_oboe.cpp | 293 ++++++++++++++++++----------------- 2 files changed, 153 insertions(+), 141 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index d345617d7..900a1f19f 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -50,6 +50,7 @@ * @brief Android Oboe-specific PortAudio API extension header file. */ +#include "portaudio.h" #include "oboe/Oboe.h" #ifdef __cplusplus diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index fd70931a3..3f0de18d1 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -166,6 +166,7 @@ PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; class OboeEngine; +class OboeCallback; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -190,6 +191,7 @@ typedef struct OboeStream { PaSampleFormat inputFormat; PaSampleFormat outputFormat; + OboeCallback *oboeCallback; // Buffers are managed by the callback function in Oboe. void **outputBuffers; int currentOutputBuffer; @@ -207,9 +209,33 @@ typedef struct OboeStream { OboeEngine *oboeEngineAddress; } OboeStream; +/** + * Callback class for OboeStream. Will be used for non-blocking streams. + */ +class OboeCallback: public AudioStreamCallback { +public: + OboeCallback(){ m_oboeStreamHolder = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); } + //Callback function for non-blocking streams + DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, + int32_t numFrames) override; + + void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + + void setStreamHolder(OboeStream* oboeStream){ m_oboeStreamHolder = oboeStream; } + + void resetCallbackCounters(); + +private: + //callback utils + OboeStream *m_oboeStreamHolder; + unsigned long m_framesProcessed{}; + PaStreamCallbackTimeInfo m_timeInfo{}; + struct timespec m_timeSpec{}; +}; + /** - * Stream engine of the host API - Oboe. We allocate only one instance of the engine, and + * Stream engine of the host API - Oboe. We allocate only one instance of the engine per PaOboe_Initialize call, and * we call its functions when we want to operate directly on Oboe. More infos on each functions are * provided right before their implementations. */ @@ -233,14 +259,6 @@ class OboeEngine : public AudioStreamCallback { bool abortStream(OboeStream *oboeStream); - //Callback function for non-blocking streams and some callback utils - DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, - int32_t numFrames) override; - - void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; - - void resetCallbackCounters(); - //Blocking read/write functions bool writeStream(const void *buffer, int32_t framesToWrite); @@ -250,19 +268,12 @@ class OboeEngine : public AudioStreamCallback { OboeStream *allocateOboeStream(); private: - OboeStream *m_oboeStreamHolder; - //The only instances of output and input streams that will be used, and their builders std::shared_ptr m_outputStream; AudioStreamBuilder m_outputBuilder; std::shared_ptr m_inputStream; AudioStreamBuilder m_inputBuilder; - //callback utils - unsigned long m_framesProcessed{}; - PaStreamCallbackTimeInfo m_timeInfo{}; - struct timespec m_timeSpec{}; - //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -291,14 +302,12 @@ typedef struct PaOboeHostApiRepresentation { /** * \brief Initializes an instance of the engine. */ -OboeEngine::OboeEngine() { - m_oboeStreamHolder = allocateOboeStream(); -} +OboeEngine::OboeEngine() {} /** - * \brief Tries to open a stream with the direction @direction, sample rate @sampleRate and/or - * channel count @channelCount. It then checks if the stream was in fact opened with the + * \brief Tries to open a stream with the direction i_direction, sample rate i_sampleRate and/or + * channel count i_channelCount. It then checks if the stream was in fact opened with the * desired settings, and then closes the stream. It's used to see if the requested * parameters are supported by the devices that are going to be used. * @param direction the Direction of the stream; @@ -357,7 +366,7 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t /** - * \brief Opens an audio stream of oboeStream with a specific direction, sample rate and, + * \brief Opens an audio stream with a specific direction, sample rate and, * depending on the direction of the stream, sets its usage (if * direction == Ditrction::Output) or its preset (if direction == Direction::Input). * Moreover, this function checks if the stream is blocking, and sets its callback @@ -376,6 +385,12 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, PaError error = paNoError; Result result; + if(!(i_oboeStream->isBlocking)){ + i_oboeStream->oboeCallback = new OboeCallback(); + i_oboeStream->oboeCallback->setStreamHolder(i_oboeStream); + i_oboeStream->oboeCallback->resetCallbackCounters(); + } + if (i_direction == Direction::Input) { m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) @@ -387,9 +402,8 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - resetCallbackCounters(); - m_inputBuilder.setDataCallback(this) - ->setErrorCallback(this); + m_inputBuilder.setDataCallback(i_oboeStream->oboeCallback) + ->setErrorCallback(i_oboeStream->oboeCallback); } result = m_inputBuilder.openStream(m_inputStream); @@ -431,9 +445,8 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - resetCallbackCounters(); - m_outputBuilder.setDataCallback(this) - ->setErrorCallback(this); + m_outputBuilder.setDataCallback(i_oboeStream->oboeCallback) + ->setErrorCallback(i_oboeStream->oboeCallback); } result = m_outputBuilder.openStream(m_outputStream); @@ -663,116 +676,6 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { } -/** - * \brief Oboe's callback routine. FIXME: implement onErrorAfterClose correctly - */ -DataCallbackResult -OboeEngine::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { - - clock_gettime(CLOCK_REALTIME, &m_timeSpec); - m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); - m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback - / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); - m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback - / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); - - /* check if StopStream or AbortStream was called */ - if (m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->callbackResult = paComplete; - } else if (m_oboeStreamHolder->doAbort) { - m_oboeStreamHolder->callbackResult = paAbort; - } - - PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, - &m_timeInfo, m_oboeStreamHolder->cbFlags); - - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; - PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); - PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ - m_oboeStreamHolder->currentOutputBuffer], - 0); - } - if (m_oboeStreamHolder->hasInput) { - i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; - PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ - m_oboeStreamHolder->currentInputBuffer], - 0); - } - - /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ - if (m_oboeStreamHolder->callbackResult == paContinue - || (m_oboeStreamHolder->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, - &oboeStream->callbackResult); - } - - /* enqueue a buffer only when there are frames to be processed, - * this will be 0 when paComplete + empty buffers or paAbort - */ - if (m_framesProcessed > 0) { - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->currentOutputBuffer = - (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; - } - if (m_oboeStreamHolder->hasInput) { - m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; - } - } - - PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); - - /* StopStream was called */ - if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; - } - - /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { - m_oboeStreamHolder->isActive = false; - m_oboeStreamHolder->isStopped = true; - if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) - m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( - m_oboeStreamHolder->streamRepresentation.userData); - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) - } - - return m_oboeStreamHolder->oboeCallbackResult; -} - - -/** - * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells - * what error occurred. - */ -void OboeEngine::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { - if (i_error == oboe::Result::ErrorDisconnected) { - LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); - if (!restartStream(0)) - LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); - } else - LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); -} - - -/** - * \brief Resets callback counters (called at the start of each iteration of onAudioReady - */ -void OboeEngine::resetCallbackCounters() { - m_framesProcessed = 0; - m_timeInfo = {0, 0, 0}; -} - - /** * \brief Writes frames on the output stream of oboeStream. Used by blocking streams. * @param buffer The buffer that we want to write on the output stream; @@ -884,6 +787,117 @@ int32_t OboeEngine::getSelectedDevice(Direction i_direction) { return g_outputDeviceId; } +/*----------------------------- OboeCallback functions implementations -----------------------------*/ +/** + * \brief Oboe's callback routine. + */ +DataCallbackResult +OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { + + clock_gettime(CLOCK_REALTIME, &m_timeSpec); + m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); + m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + / + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); + m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + / + m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + + m_timeInfo.currentTime); + + /* check if StopStream or AbortStream was called */ + if (m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->callbackResult = paComplete; + } else if (m_oboeStreamHolder->doAbort) { + m_oboeStreamHolder->callbackResult = paAbort; + } + + PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, + &m_timeInfo, m_oboeStreamHolder->cbFlags); + + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; + PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); + PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ + m_oboeStreamHolder->currentOutputBuffer], + 0); + } + if (m_oboeStreamHolder->hasInput) { + i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; + PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ + m_oboeStreamHolder->currentInputBuffer], + 0); + } + + /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ + if (m_oboeStreamHolder->callbackResult == paContinue + || (m_oboeStreamHolder->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { + m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, + &oboeStream->callbackResult); + } + + /* enqueue a buffer only when there are frames to be processed, + * this will be 0 when paComplete + empty buffers or paAbort + */ + if (m_framesProcessed > 0) { + if (m_oboeStreamHolder->hasOutput) { + m_oboeStreamHolder->currentOutputBuffer = + (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; + } + if (m_oboeStreamHolder->hasInput) { + m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; + } + } + + PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); + + /* StopStream was called */ + if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; + } + + /* if AbortStream or StopStream weren't called, stop from the cb */ + else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { + m_oboeStreamHolder->isActive = false; + m_oboeStreamHolder->isStopped = true; + if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) + m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( + m_oboeStreamHolder->streamRepresentation.userData); + m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) + } + + return m_oboeStreamHolder->oboeCallbackResult; +} + + +/** + * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells + * what error occurred. + */ +void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { + if (i_error == oboe::Result::ErrorDisconnected) { + OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); + LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + if (!oboeEngine->restartStream(0)) + LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); + } else + LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); +} + + +/** + * \brief Resets callback counters (called at the start of each iteration of onAudioReady + */ +void OboeCallback::resetCallbackCounters() { + m_framesProcessed = 0; + m_timeInfo = {0, 0, 0}; +} + /*----------------------------- PaSkeleton functions implementations -----------------------------*/ @@ -1367,8 +1381,6 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, PaSampleFormat inputSampleFormat, outputSampleFormat; PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - - //TODO: add a function that lets the user choose usage and preset Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; @@ -1624,7 +1636,6 @@ static PaError StartStream(PaStream *i_paStream) { } } - /* Start the processing thread.*/ if (!oboeStream->isBlocking) { oboeStream->callbackResult = paContinue; oboeStream->oboeCallbackResult = DataCallbackResult::Continue; From 6723d6df7aa56754663c71301859ef3458709441 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 12:51:09 +0200 Subject: [PATCH 098/125] Modified OboeEngine accordingly --- src/hostapi/oboe/pa_oboe.cpp | 140 +++++++++++++++++------------------ 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 3f0de18d1..853b7c8e5 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -205,6 +205,12 @@ typedef struct OboeStream { void setEngineAddress(OboeEngine *i_oboeEngine) { oboeEngineAddress = i_oboeEngine; } + //The only instances of output and input streams that will be used, and their builders + std::shared_ptr outputStream; + AudioStreamBuilder outputBuilder; + std::shared_ptr inputStream; + AudioStreamBuilder inputBuilder; + private: OboeEngine *oboeEngineAddress; } OboeStream; @@ -235,11 +241,11 @@ class OboeCallback: public AudioStreamCallback { /** - * Stream engine of the host API - Oboe. We allocate only one instance of the engine per PaOboe_Initialize call, and - * we call its functions when we want to operate directly on Oboe. More infos on each functions are - * provided right before their implementations. + * Stream engine of the host API - Oboe. We allocate only one instance of the engine per OboeStream, and + * we call its functions when we want to operate directly on Oboe. More infos on each function are + * provided right before its implementation. */ -class OboeEngine : public AudioStreamCallback { +class OboeEngine { public: OboeEngine(); @@ -253,26 +259,23 @@ class OboeEngine : public AudioStreamCallback { bool stopStream(OboeStream *oboeStream); - bool restartStream(int direction); + bool restartStream(OboeStream *oboeStream, int direction); bool closeStream(OboeStream *oboeStream); bool abortStream(OboeStream *oboeStream); //Blocking read/write functions - bool writeStream(const void *buffer, int32_t framesToWrite); + bool writeStream(OboeStream *oboeStream, const void *buffer, int32_t framesToWrite); - bool readStream(void *buffer, int32_t framesToRead); + bool readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead); //Engine utils OboeStream *allocateOboeStream(); private: - //The only instances of output and input streams that will be used, and their builders - std::shared_ptr m_outputStream; - AudioStreamBuilder m_outputBuilder; - std::shared_ptr m_inputStream; - AudioStreamBuilder m_inputBuilder; + std::shared_ptr m_testStream; + AudioStreamBuilder m_testBuilder; //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -300,7 +303,7 @@ typedef struct PaOboeHostApiRepresentation { /** - * \brief Initializes an instance of the engine. + * \brief Constructor. */ OboeEngine::OboeEngine() {} @@ -317,21 +320,16 @@ OboeEngine::OboeEngine() {} * they aren't, or if tryStream couldn't open a stream. */ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t i_channelCount) { - AudioStreamBuilder builder; Result result; bool outcome = false; - builder.setDeviceId(getSelectedDevice(i_direction)) - // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + m_testBuilder.setDeviceId(getSelectedDevice(i_direction)) + // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) ->setDirection(i_direction) ->setSampleRate(i_sampleRate) - ->setChannelCount(i_channelCount); - if (i_direction == Direction::Input) { - result = builder->openStream(m_inputStream); - } else { - result = builder->openStream(m_outputStream); - } + ->setChannelCount(i_channelCount) + ->openStream(m_testStream); if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", @@ -340,26 +338,23 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t } if (i_sampleRate != kUnspecified) { - outcome = (i_sampleRate == builder.getSampleRate()); + outcome = (i_sampleRate == m_testBuilder.getSampleRate()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - i_sampleRate, builder.getSampleRate()); + i_sampleRate, m_testBuilder.getSampleRate()); } } else if (i_channelCount != kUnspecified) { - outcome = (i_channelCount == builder.getChannelCount()); + outcome = (i_channelCount == m_testBuilder.getChannelCount()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, builder.getChannelCount()); + i_channelCount, m_testBuilder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); outcome = false; } - if (i_direction == Direction::Input) - m_inputStream->close(); - else - m_outputStream->close(); + m_testStream->close(); return outcome; } @@ -371,6 +366,7 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t * direction == Ditrction::Output) or its preset (if direction == Direction::Input). * Moreover, this function checks if the stream is blocking, and sets its callback * function if not. + * @param oboeStream The stream we want to open * @param direction The Oboe::Direction of the stream we want to open; * @param sampleRate The sample rate of the stream we want to open; * @param androidOutputUsage The Oboe::Usage of the output stream we want to open @@ -392,7 +388,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, } if (i_direction == Direction::Input) { - m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) + i_oboeStream->inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Input) @@ -406,7 +402,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setErrorCallback(i_oboeStream->oboeCallback); } - result = m_inputBuilder.openStream(m_inputStream); + result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", @@ -414,7 +410,8 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, return paUnanticipatedHostError; } - m_inputStream->setBufferSizeInFrames(m_inputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->inputStream->setBufferSizeInFrames(i_oboeStream->inputStream->getFramesPerBurst() * + g_numberOfBuffers); i_oboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -428,14 +425,14 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->inputBuffers); - m_inputStream->close(); + i_oboeStream->inputStream->close(); error = paInsufficientMemory; break; } } i_oboeStream->currentInputBuffer = 0; } else { - m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) + i_oboeStream->outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Output) @@ -445,18 +442,19 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - m_outputBuilder.setDataCallback(i_oboeStream->oboeCallback) + i_oboeStream->outputBuilder.setDataCallback(i_oboeStream->oboeCallback) ->setErrorCallback(i_oboeStream->oboeCallback); } - result = m_outputBuilder.openStream(m_outputStream); + result = i_oboeStream->outputBuilder.openStream(m_outputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); return paUnanticipatedHostError; } - m_outputStream->setBufferSizeInFrames(m_outputStream->getFramesPerBurst() * g_numberOfBuffers); + i_oboeStream->outputStream->setBufferSizeInFrames(i_oboeStream->outputStream->getFramesPerBurst() * + g_numberOfBuffers); i_oboeStream->outputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -470,7 +468,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->outputBuffers); - m_outputStream->close(); + i_oboeStream->outputStream->close(); error = paInsufficientMemory; break; } @@ -491,13 +489,13 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; if (i_oboeStream->hasInput) { - inputResult = m_inputStream->requestStart(); + inputResult = i_oboeStream->inputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = m_outputStream->requestStart(); + outputResult = i_oboeStream->outputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", convertToText(outputResult)); @@ -516,13 +514,13 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; if (i_oboeStream->hasInput) { - inputResult = m_inputStream->requestStop(); + inputResult = i_oboeStream->inputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = m_outputStream->requestStop(); + outputResult = i_oboeStream->outputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", convertToText(outputResult)); @@ -537,28 +535,28 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { * audio devices change while a stream is started. * @return true if the stream is restarted successfully, false otherwise. */ -bool OboeEngine::restartStream(int i_direction) { +bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { bool outcome = true; Result result; switch (i_direction) { case 1: //output-only //stopping and closing - result = m_outputStream->stop(); + result = i_oboeStream->outputStream->stop(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", convertToText(result)); - result = m_outputStream->close(); + result = i_oboeStream->outputStream->close(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", convertToText(result)); //reopening and restarting - result = outputBuilder.openStream(m_outputStream); + result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", convertToText(result)); - result = m_outputStream->start(); + result = i_oboeStream->outputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", convertToText(result)); @@ -568,21 +566,21 @@ bool OboeEngine::restartStream(int i_direction) { case 2: //input-only //stopping and closing - result = m_inputStream->stop(); + result = i_oboeStream->inputStream->stop(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", convertToText(result)); - result = m_inputStream->close(); + result = i_oboeStream->inputStream->close(); if (result != Result::OK) LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", convertToText(result)); //reopening and restarting - result = inputBuilder.openStream(m_inputStream); + result = inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); - result = m_inputStream->start(); + result = i_oboeStream->inputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", convertToText(result)); @@ -593,7 +591,7 @@ bool OboeEngine::restartStream(int i_direction) { default: // unspecified direction or both directions: restart both streams LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); - outcome = (restartStream(1) && restartStream(2)); + outcome = (restartStream(i_oboeStream, 1) && restartStream(i_oboeStream, 2)); break; } @@ -618,14 +616,14 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { } if (hasOutput) { - outputResult = m_outputStream->close(); + outputResult = i_oboeStream->outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } if (hasInput) { - inputResult = m_inputStream->close(); + inputResult = i_oboeStream->inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); @@ -652,21 +650,21 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { } if (hasInput) { - inputResult = m_inputStream->stop(); + inputResult = i_oboeStream->inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); - inputResult = m_inputStream->close(); - if (m_inputResult != Result::OK) + inputResult = i_oboeStream->inputStream->close(); + if (i_oboeStream->inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (hasOutput) { - outputResult = m_outputStream->stop(); - if (m_outputResult != Result::OK) + outputResult = i_oboeStream->outputStream->stop(); + if (i_oboeStream->outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", - convertToText(m_outputResult)); - outputResult = m_outputStream->close(); + convertToText(outputResult)); + outputResult = i_oboeStream->outputStream->close(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", convertToText(outputResult)); @@ -684,14 +682,14 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(const void *i_buffer, int32_t i_framesToWrite) { +bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int32_t i_framesToWrite) { bool outcome = true; - ResultWithValue result = m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); + ResultWithValue result = i_oboeStream->outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (OboeEngine::restartStream(1)) + if (restartStream(i_oboeStream, 1)) return true; } @@ -711,14 +709,14 @@ bool OboeEngine::writeStream(const void *i_buffer, int32_t i_framesToWrite) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::readStream(void *i_buffer, int32_t i_framesToRead) { +bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_framesToRead) { bool outcome = true; - ResultWithValue result = m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); + ResultWithValue result = i_oboeStream->inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (OboeEngine::restartStream(2)) + if (restartStream(i_oboeStream, 2)) return true; } @@ -837,8 +835,8 @@ OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ if (m_oboeStreamHolder->callbackResult == paContinue || (m_oboeStreamHolder->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&oboeStream->bufferProcessor, - &oboeStream->callbackResult); + m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeStreamHolder->bufferProcessor, + &m_oboeStreamHolder->callbackResult); } /* enqueue a buffer only when there are frames to be processed, @@ -883,7 +881,7 @@ void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) if (i_error == oboe::Result::ErrorDisconnected) { OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); - if (!oboeEngine->restartStream(0)) + if (!oboeEngine->restartStream(m_oboeStreamHolder, 0)) LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); } else LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); From ae0cef2e160f36d9e461429afee67d30b07e0676 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 16:04:03 +0200 Subject: [PATCH 099/125] Adjusted the code in the rest of pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 853b7c8e5..0929033fa 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -880,11 +880,11 @@ OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { if (i_error == oboe::Result::ErrorDisconnected) { OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); - LOGW("[OboeEngine::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + LOGW("[OboeCallback::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); if (!oboeEngine->restartStream(m_oboeStreamHolder, 0)) - LOGE("[OboeEngine::onErrorAfterClose]\t Couldn't restart stream(s)"); + LOGE("[OboeCallback::onErrorAfterClose]\t Couldn't restart stream(s)"); } else - LOGE("[OboeEngine::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); + LOGE("[OboeCallback::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); } @@ -1086,10 +1086,8 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde deviceInfo->defaultSampleRate = sampleRates[0]; for (i = 0; i < numberOfSampleRates; ++i) { - if (IsOutputSampleRateSupported( - oboeHostApi, sampleRates[i]) == paNoError && - IsInputSampleRateSupported( - oboeHostApi, sampleRates[i]) == paNoError) { + if (IsOutputSampleRateSupported(oboeHostApi, sampleRates[i]) == paNoError && + IsInputSampleRateSupported(oboeHostApi, sampleRates[i]) == paNoError) { deviceInfo->defaultSampleRate = sampleRates[i]; break; } @@ -1224,7 +1222,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /* check that input device can support inputChannelCount */ if (inputChannelCount > - i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { + i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { return paInvalidChannelCount; } @@ -1262,8 +1260,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, } /* check that output device can support outputChannelCount */ - if (outputChannelCount > - i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { + if (outputChannelCount > i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { return paInvalidChannelCount; } @@ -1304,6 +1301,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /** * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeStream is the OboeStream we want to initialize in the output direction. * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of * this file); * @param androidOutputUsage is an attribute that expresses why we are opening the output stream. @@ -1313,7 +1311,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(PaOboeHostApiRepresentation *i_oboeHostApi, +static PaError InitializeOutputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, Usage i_androidOutputUsage, double i_sampleRate) { return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, @@ -1326,6 +1324,7 @@ static PaError InitializeOutputStream(PaOboeHostApiRepresentation *i_oboeHostApi /** * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. + * @param oboeStream is the OboeStream we want to initialize in the input direction. * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of * this file); * @param androidInputPreset is an attribute that defines the audio source. This information @@ -1350,7 +1349,7 @@ static PaError InitializeInputStream(OboeStream i_oboeStream, PaOboeHostApiRepre * \brief Opens the portaudio audio stream - while initializing our OboeStream. * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the * interface to a host API (see struct in "pa_hostapi.h"); - * @param s points to a pointer to a PaStream, which is an audio stream structure used and built + * @param paStream points to a pointer to a PaStream, which is an audio stream structure used and built * by portaudio, which will hold the information of our OboeStream; * @param inputParameters points towards the parameters given to the input stream; * @param outputParameters points towards the parameters given to the output stream; @@ -1540,7 +1539,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( &oboeStream->bufferProcessor) + oboeStream->framesPerHostCallback) / i_sampleRate; - ENSURE(InitializeOutputStream(oboeHostApi, + ENSURE(InitializeOutputStream(oboeStream, oboeHostApi, androidOutputUsage, i_sampleRate), "Initializing outputstream failed"); } else { oboeStream->hasOutput = false; } @@ -1562,7 +1561,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, * \brief Calls OboeEngine::closeStream, and then frees the memory that was allocated to sustain * the stream(s). When CloseStream() is called, the multi-api layer ensures that the stream * has already been stopped or aborted. - * @param s points to to a PaStream, which is an audio stream structure used and built by + * @param paStream points to to a PaStream, which is an audio stream structure used and built by * portaudio, which holds the information of our OboeStream. * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. */ @@ -1736,7 +1735,7 @@ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_ while (i_frames > 0) { framesToRead = PA_MIN(oboeStream->framesPerHostCallback, i_frames); - if (!(oboeEngine->readStream(userBuffer, framesToRead * + if (!(oboeEngine->readStream(oboeStream, userBuffer, framesToRead * oboeStream->bufferProcessor.inputChannelCount))) error = paInternalError; @@ -1766,7 +1765,7 @@ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned while (i_frames > 0) { framesToWrite = PA_MIN(stream->framesPerHostCallback, i_frames); - if (!(oboeEngine->writeStream(userBuffer, framesToWrite * + if (!(oboeEngine->writeStream(oboeStream, userBuffer, framesToWrite * oboeStream->bufferProcessor.outputChannelCount))) error = paInternalError; From 9f8e6fcfc9a6bf92cd2c2999fe2ed20d0ec6c21b Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 4 Oct 2023 16:17:09 +0200 Subject: [PATCH 100/125] Removed stop and close phases of OboeEngine::restartStream --- src/hostapi/oboe/pa_oboe.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 0929033fa..d0d2c3d78 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -541,17 +541,6 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { switch (i_direction) { case 1: //output-only - //stopping and closing - result = i_oboeStream->outputStream->stop(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the output stream: %s", - convertToText(result)); - result = i_oboeStream->outputStream->close(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the output stream: %s", - convertToText(result)); - - //reopening and restarting result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", @@ -565,17 +554,6 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { break; case 2: //input-only - //stopping and closing - result = i_oboeStream->inputStream->stop(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't stop the input stream: %s", - convertToText(result)); - result = i_oboeStream->inputStream->close(); - if (result != Result::OK) - LOGW("[OboeEngine::restartStream]\t Oboe couldn't close the input stream: %s", - convertToText(result)); - - //reopening and restarting result = inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", From 74fa4b5c54df97f424fc44580d458801885a9e45 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 09:42:01 +0200 Subject: [PATCH 101/125] Updated functions' description --- src/hostapi/oboe/pa_oboe.cpp | 43 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index d0d2c3d78..f500f5995 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -81,7 +81,6 @@ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,MODULE_NAME, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,MODULE_NAME, __VA_ARGS__) -// Copied from @{pa_opensles.c}. #define ENSURE(expr, errorText) \ do \ { \ @@ -212,7 +211,7 @@ typedef struct OboeStream { AudioStreamBuilder inputBuilder; private: - OboeEngine *oboeEngineAddress; + OboeEngine *oboeEngineAddress = nullptr; } OboeStream; /** @@ -242,7 +241,7 @@ class OboeCallback: public AudioStreamCallback { /** * Stream engine of the host API - Oboe. We allocate only one instance of the engine per OboeStream, and - * we call its functions when we want to operate directly on Oboe. More infos on each function are + * we call its functions when we want to operate directly on Oboe. More information on each function is * provided right before its implementation. */ class OboeEngine { @@ -363,7 +362,7 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t /** * \brief Opens an audio stream with a specific direction, sample rate and, * depending on the direction of the stream, sets its usage (if - * direction == Ditrction::Output) or its preset (if direction == Direction::Input). + * direction == Direction::Output) or its preset (if direction == Direction::Input). * Moreover, this function checks if the stream is blocking, and sets its callback * function if not. * @param oboeStream The stream we want to open @@ -481,8 +480,9 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, /** - * \brief Starts oboeStream - both input and output audiostreams are checked + * \brief Starts oboeStream - both input and output AudioStreams of the OboeStream are checked * and requested to be started. + * @param oboeStream The stream we want to start. * @return true if the streams we wanted to start are started successfully, false otherwise. */ bool OboeEngine::startStream(OboeStream *i_oboeStream) { @@ -506,8 +506,9 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { /** - * \brief Stops oboeStream - both input and output audiostreams are checked + * \brief Stops oboeStream - both input and output AudioStreams of the OboeStream are checked * and requested to be stopped. + * @param oboeStream The stream we want to stop. * @return true if the streams we wanted to stop are stopped successfully, false otherwise. */ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { @@ -531,8 +532,11 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { /** - * \brief Called when it's needed to restart the oboeStream's audio stream(s), mainly when the - * audio devices change while a stream is started. + * \brief Called when it's needed to restart the OboeStream's audio stream(s) when the audio device(s) change + * while a stream is started. Oboe will stop and close said streams in that case, + * so this function just reopens and restarts them. + * @param oboeStream The stream we want to restart. + * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 0 for both). * @return true if the stream is restarted successfully, false otherwise. */ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { @@ -578,8 +582,9 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { /** - * \brief Closes oboeStream - both input and output audiostreams are checked + * \brief Closes oboeStream - both input and output AudioStreams of the OboeStream are checked * and closed if active. + * @param oboeStream The stream we want to close. * @return true if the stream is closed successfully, otherwise returns false. */ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { @@ -613,7 +618,8 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { /** - * \brief Stops oboeStream - both input and output audiostreams are checked and forcefully stopped. + * \brief Stops oboeStream - both input and output AudioStreams of the OboeStream are checked and forcefully stopped. + * @param oboeStream The stream we want to abort. * @return true if the output stream and the input stream are stopped successfully, false otherwise. */ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { @@ -654,6 +660,7 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { /** * \brief Writes frames on the output stream of oboeStream. Used by blocking streams. + * @param oboeStream The stream we want to write onto. * @param buffer The buffer that we want to write on the output stream; * @param framesToWrite The number of frames that we want to write. * @return true if the buffer is written correctly, false if the write function returns an error @@ -681,6 +688,7 @@ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int /** * \brief Reads frames from the input stream of oboeStream. Used by blocking streams. + * @param oboeStream The stream we want to read from. * @param buffer The buffer that we want to read from the input stream; * @param framesToWrite The number of frames that we want to read. * @return true if the buffer is read correctly, false if the read function returns an error @@ -707,7 +715,7 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ /** - * \brief Allocates the memory of oboeStream. + * \brief Allocates the memory of an OboeStream, and sets its address in it. * @return the address of the oboeStream. */ OboeStream *OboeEngine::allocateOboeStream() { @@ -809,7 +817,8 @@ OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ 0); } - /* continue processing user buffers if cbresult is paContinue or if cbresult is paComplete and userBuffers aren't empty yet */ + /* continue processing user buffers if callback result is paContinue or + * if it is paComplete and userBuffers aren't empty yet */ if (m_oboeStreamHolder->callbackResult == paContinue || (m_oboeStreamHolder->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { @@ -1003,7 +1012,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde } *i_hostApi = &oboeHostApi->inheritedHostApiRep; - // Initialization of infos. + // Info initialization. (*i_hostApi)->info.structVersion = 1; (*i_hostApi)->info.type = paInDevelopment; (*i_hostApi)->info.name = "android Oboe"; @@ -1134,7 +1143,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(boeHostApi); + PaUtil_FreeMemory(oboeeHostApi); } LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", result); return result; @@ -1401,7 +1410,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->inputFormat = hostInputSampleFormat; } else { inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ + inputSampleFormat = hostInputSampleFormat = paFloat32; /* Suppress 'uninitialised var' warnings. */ oboeStream->inputFormat = hostInputSampleFormat; } @@ -1508,7 +1517,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->framesPerHostCallback) / i_sampleRate; ENSURE(InitializeInputStream(oboeStream, oboeHostApi, androidInputPreset, i_sampleRate), - "Initializing inputstream failed") + "Initializing input stream failed") } else { oboeStream->hasInput = false; } if (outputChannelCount > 0) { @@ -1519,7 +1528,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, + oboeStream->framesPerHostCallback) / i_sampleRate; ENSURE(InitializeOutputStream(oboeStream, oboeHostApi, androidOutputUsage, i_sampleRate), - "Initializing outputstream failed"); + "Initializing output stream failed"); } else { oboeStream->hasOutput = false; } *i_paStream = (PaStream *) oboeStream; From 9304b07b562cf8bd03d5b3cd1488bd12553b44b7 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 09:56:52 +0200 Subject: [PATCH 102/125] minor description corrections --- src/hostapi/oboe/pa_oboe.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index f500f5995..d417238ad 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -715,7 +715,7 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ /** - * \brief Allocates the memory of an OboeStream, and sets its address in it. + * \brief Allocates the memory of an OboeStream, and sets its EngineAddress to this. * @return the address of the oboeStream. */ OboeStream *OboeEngine::allocateOboeStream() { @@ -1159,7 +1159,7 @@ static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; if (!(oboeHostApi->oboeEngine->closeStream(nullptr))) - LOGW("[PaOboe - Terminate]\t Couldn't close the streams correctly - see OboeEngine::CloseStream logs."); + LOGI("[PaOboe - Terminate]\t The streams were probably already closed - see OboeEngine::CloseStream logs."); if (oboeHostApi->oboeEngine != nullptr) delete oboeHostApi->oboeEngine; From 26a34972443d75c65ae15503dea02dd3b5603e10 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 10:14:56 +0200 Subject: [PATCH 103/125] fixed all compiling errors generated by typos --- src/hostapi/oboe/pa_oboe.cpp | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index d417238ad..c69edafd3 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -84,13 +84,13 @@ #define ENSURE(expr, errorText) \ do \ { \ - PaError m_err; \ - if (UNLIKELY((m_err = (expr)) < paNoError)) \ + PaError err; \ + if (UNLIKELY((err = (expr)) < paNoError)) \ { \ PaUtil_DebugPrint(("Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( \ __LINE__ ) "\n")); \ - PaUtil_SetLastHostErrorInfo(paInDevelopment, m_err, errorText); \ - m_error = m_err; \ + PaUtil_SetLastHostErrorInfo(paInDevelopment, err, errorText); \ + error = err; \ goto error; \ } \ } while (0); @@ -397,7 +397,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - m_inputBuilder.setDataCallback(i_oboeStream->oboeCallback) + i_oboeStream->inputBuilder.setDataCallback(i_oboeStream->oboeCallback) ->setErrorCallback(i_oboeStream->oboeCallback); } @@ -445,7 +445,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setErrorCallback(i_oboeStream->oboeCallback); } - result = i_oboeStream->outputBuilder.openStream(m_outputStream); + result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); @@ -558,7 +558,7 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { break; case 2: //input-only - result = inputBuilder.openStream(i_oboeStream->inputStream); + result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); @@ -639,13 +639,13 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); inputResult = i_oboeStream->inputStream->close(); - if (i_oboeStream->inputResult != Result::OK) + if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (hasOutput) { outputResult = i_oboeStream->outputStream->stop(); - if (i_oboeStream->outputResult != Result::OK) + if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(outputResult)); outputResult = i_oboeStream->outputStream->close(); @@ -1040,7 +1040,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde for (int i = 0; i < deviceCount; ++i) { PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; + deviceInfo->hostApi = i_hostApiIndex; /* OboeEngine will handle manual device selection through the use of PaOboe_SetSelectedDevice. Portaudio doesn't need to know about this, so we just use a default device. */ @@ -1143,7 +1143,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde PaUtil_DestroyAllocationGroup(oboeHostApi->allocations); } - PaUtil_FreeMemory(oboeeHostApi); + PaUtil_FreeMemory(oboeHostApi); } LOGE("[PaOboe - Initialize]\t Initialization failed. Error code: %d", result); return result; @@ -1221,7 +1221,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, if (androidRecordingPreset != InputPreset::Generic && androidRecordingPreset != InputPreset::Camcorder && androidRecordingPreset != InputPreset::VoiceRecognition && - androidRecordingPreset != InputPreset::VoiceCommunication + androidRecordingPreset != InputPreset::VoiceCommunication && androidRecordingPreset != InputPreset::VoicePerformance) { return paIncompatibleHostApiSpecificStreamInfo; } @@ -1298,13 +1298,13 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, +static PaError InitializeOutputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, Usage i_androidOutputUsage, double i_sampleRate) { return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, Direction::Output, - sampleRate, - androidOutputUsage, + i_sampleRate, + i_androidOutputUsage, Generic); //Input preset won't be used, so we put the default value. } @@ -1321,7 +1321,7 @@ static PaError InitializeOutputStream(OboeStream i_oboeStream, PaOboeHostApiRepr * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeInputStream(OboeStream i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, +static PaError InitializeInputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, InputPreset i_androidInputPreset, double i_sampleRate) { return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, @@ -1399,7 +1399,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, if (androidInputPreset != InputPreset::Generic && androidInputPreset != InputPreset::Camcorder && androidInputPreset != InputPreset::VoiceRecognition && - androidInputPreset != InputPreset::VoiceCommunication + androidInputPreset != InputPreset::VoiceCommunication && androidInputPreset != InputPreset::VoicePerformance) return paIncompatibleHostApiSpecificStreamInfo; } @@ -1447,7 +1447,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paFloat32, m_outputSampleFormat); + paFloat32, outputSampleFormat); oboeStream->outputFormat = hostOutputSampleFormat; } else { outputChannelCount = 0; @@ -1750,7 +1750,7 @@ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned PaError error = paNoError; while (i_frames > 0) { - framesToWrite = PA_MIN(stream->framesPerHostCallback, i_frames); + framesToWrite = PA_MIN(oboeStream->framesPerHostCallback, i_frames); if (!(oboeEngine->writeStream(oboeStream, userBuffer, framesToWrite * oboeStream->bufferProcessor.outputChannelCount))) From 881970a90233c9424b406cc8a0fc5dd98cb491dd Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 16:09:29 +0200 Subject: [PATCH 104/125] Added OboeMediator class in place of OboeCallback, that mediates PortAudio C stream struct and the C++ OboeEngine class --- src/hostapi/oboe/pa_oboe.cpp | 267 +++++++++++++++++++---------------- 1 file changed, 143 insertions(+), 124 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index c69edafd3..a716c4340 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -165,7 +165,7 @@ PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; class OboeEngine; -class OboeCallback; +class OboeMediator; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. @@ -190,7 +190,6 @@ typedef struct OboeStream { PaSampleFormat inputFormat; PaSampleFormat outputFormat; - OboeCallback *oboeCallback; // Buffers are managed by the callback function in Oboe. void **outputBuffers; int currentOutputBuffer; @@ -200,39 +199,43 @@ typedef struct OboeStream { unsigned long framesPerHostCallback; unsigned bytesPerFrame; - OboeEngine *getEngineAddress() { return oboeEngineAddress; } - - void setEngineAddress(OboeEngine *i_oboeEngine) { oboeEngineAddress = i_oboeEngine; } - - //The only instances of output and input streams that will be used, and their builders - std::shared_ptr outputStream; - AudioStreamBuilder outputBuilder; - std::shared_ptr inputStream; - AudioStreamBuilder inputBuilder; - -private: - OboeEngine *oboeEngineAddress = nullptr; + OboeMediator* oboeMediator; } OboeStream; -/** - * Callback class for OboeStream. Will be used for non-blocking streams. - */ -class OboeCallback: public AudioStreamCallback { + +class OboeMediator: public AudioStreamCallback{ public: - OboeCallback(){ m_oboeStreamHolder = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); } + OboeMediator(OboeStream* i_oboeStream) { + m_oboeCallbackStream = i_oboeStream; + } + //Callback function for non-blocking streams DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) override; - + //Callback utils void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + void resetCallbackCounters(); + void setOutputCallback() { m_outputBuilder.setDataCallback(this)->setErrorCallback(this); } + void setInputCallback() { m_inputBuilder.setDataCallback(this)->setErrorCallback(this); } - void setStreamHolder(OboeStream* oboeStream){ m_oboeStreamHolder = oboeStream; } + //getter and setter of m_oboeEngine and m_oboeCallbackStream + OboeEngine *getEngine() { return m_oboeEngine; } + void setEngine(OboeEngine *i_oboeEngine) { m_oboeEngine = i_oboeEngine; } - void resetCallbackCounters(); + OboeStream *getStreamAddress() { return m_oboeCallbackStream; } + void setCallbackStream(OboeStream *i_oboeStream) { m_oboeCallbackStream = i_oboeStream; } + + //The only instances of output and input streams that will be used, and their builders + std::shared_ptr m_outputStream; + AudioStreamBuilder m_outputBuilder; + std::shared_ptr m_inputStream; + AudioStreamBuilder m_inputBuilder; private: + OboeEngine *m_oboeEngine; + //callback utils - OboeStream *m_oboeStreamHolder; + OboeStream *m_oboeCallbackStream; unsigned long m_framesProcessed{}; PaStreamCallbackTimeInfo m_timeInfo{}; struct timespec m_timeSpec{}; @@ -270,12 +273,14 @@ class OboeEngine { bool readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead); //Engine utils - OboeStream *allocateOboeStream(); + void constructOboeStream(OboeStream* oboeStream); private: std::shared_ptr m_testStream; AudioStreamBuilder m_testBuilder; + OboeMediator* m_terminableMediator; + //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -379,15 +384,14 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { PaError error = paNoError; Result result; + OboeMediator* mediator = i_oboeStream->oboeMediator; if(!(i_oboeStream->isBlocking)){ - i_oboeStream->oboeCallback = new OboeCallback(); - i_oboeStream->oboeCallback->setStreamHolder(i_oboeStream); - i_oboeStream->oboeCallback->resetCallbackCounters(); + mediator->resetCallbackCounters(); } if (i_direction == Direction::Input) { - i_oboeStream->inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) + mediator->m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Input) @@ -397,11 +401,10 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - i_oboeStream->inputBuilder.setDataCallback(i_oboeStream->oboeCallback) - ->setErrorCallback(i_oboeStream->oboeCallback); + mediator->setInputCallback(); } - result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); + result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", @@ -409,7 +412,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, return paUnanticipatedHostError; } - i_oboeStream->inputStream->setBufferSizeInFrames(i_oboeStream->inputStream->getFramesPerBurst() * + mediator->m_inputStream->setBufferSizeInFrames(mediator->m_inputStream->getFramesPerBurst() * g_numberOfBuffers); i_oboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -424,14 +427,14 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->inputBuffers); - i_oboeStream->inputStream->close(); + mediator->m_inputStream->close(); error = paInsufficientMemory; break; } } i_oboeStream->currentInputBuffer = 0; } else { - i_oboeStream->outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) + mediator->m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) ->setSampleRate(i_sampleRate) ->setDirection(Direction::Output) @@ -441,18 +444,17 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); if (!(i_oboeStream->isBlocking)) { - i_oboeStream->outputBuilder.setDataCallback(i_oboeStream->oboeCallback) - ->setErrorCallback(i_oboeStream->oboeCallback); + mediator->setOutputCallback(); } - result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); + result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); return paUnanticipatedHostError; } - i_oboeStream->outputStream->setBufferSizeInFrames(i_oboeStream->outputStream->getFramesPerBurst() * + mediator->m_outputStream->setBufferSizeInFrames(mediator->m_outputStream->getFramesPerBurst() * g_numberOfBuffers); i_oboeStream->outputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); @@ -467,7 +469,7 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, for (int j = 0; j < i; ++j) PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); PaUtil_FreeMemory(i_oboeStream->outputBuffers); - i_oboeStream->outputStream->close(); + mediator->m_outputStream->close(); error = paInsufficientMemory; break; } @@ -487,15 +489,16 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, */ bool OboeEngine::startStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; + OboeMediator* mediator = i_oboeStream->oboeMediator; if (i_oboeStream->hasInput) { - inputResult = i_oboeStream->inputStream->requestStart(); + inputResult = mediator->m_inputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = i_oboeStream->outputStream->requestStart(); + outputResult = mediator->m_outputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", convertToText(outputResult)); @@ -513,15 +516,16 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { */ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; + OboeMediator* mediator = i_oboeStream->oboeMediator; if (i_oboeStream->hasInput) { - inputResult = i_oboeStream->inputStream->requestStop(); + inputResult = mediator->m_inputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } if (i_oboeStream->hasOutput) { - outputResult = i_oboeStream->outputStream->requestStop(); + outputResult = mediator->m_outputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", convertToText(outputResult)); @@ -536,20 +540,21 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { * while a stream is started. Oboe will stop and close said streams in that case, * so this function just reopens and restarts them. * @param oboeStream The stream we want to restart. - * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 0 for both). + * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 3 for both). * @return true if the stream is restarted successfully, false otherwise. */ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { bool outcome = true; Result result; + OboeMediator* mediator = i_oboeStream->oboeMediator; switch (i_direction) { case 1: //output-only - result = i_oboeStream->outputBuilder.openStream(i_oboeStream->outputStream); + result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", convertToText(result)); - result = i_oboeStream->outputStream->start(); + result = mediator->m_outputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", convertToText(result)); @@ -558,11 +563,11 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { break; case 2: //input-only - result = i_oboeStream->inputBuilder.openStream(i_oboeStream->inputStream); + result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); - result = i_oboeStream->inputStream->start(); + result = mediator->m_inputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", convertToText(result)); @@ -590,23 +595,26 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { bool OboeEngine::closeStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; bool hasOutput = true, hasInput = true; + OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Terminating both oboe streams."); + LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); + mediator = m_terminableMediator; } else { + mediator = i_oboeStream->oboeMediator; hasInput = i_oboeStream->hasInput; hasOutput = i_oboeStream->hasOutput; } if (hasOutput) { - outputResult = i_oboeStream->outputStream->close(); + outputResult = mediator->m_outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } if (hasInput) { - inputResult = i_oboeStream->inputStream->close(); + inputResult = mediator->m_inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); @@ -625,30 +633,33 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { bool OboeEngine::abortStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; bool hasOutput = true, hasInput = true; + OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams."); + LOGW("[OboeEngine::abortStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); + mediator = m_terminableMediator; } else { + mediator = i_oboeStream->oboeMediator; hasInput = i_oboeStream->hasInput; hasOutput = i_oboeStream->hasOutput; } if (hasInput) { - inputResult = i_oboeStream->inputStream->stop(); + inputResult = mediator->m_inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); - inputResult = i_oboeStream->inputStream->close(); + inputResult = mediator->m_inputStream->close(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (hasOutput) { - outputResult = i_oboeStream->outputStream->stop(); + outputResult = mediator->m_outputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(outputResult)); - outputResult = i_oboeStream->outputStream->close(); + outputResult = mediator->m_outputStream->close(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", convertToText(outputResult)); @@ -669,8 +680,9 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { */ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int32_t i_framesToWrite) { bool outcome = true; + OboeMediator* mediator = i_oboeStream->oboeMediator; - ResultWithValue result = i_oboeStream->outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); + ResultWithValue result = mediator->m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -697,8 +709,9 @@ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int */ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_framesToRead) { bool outcome = true; + OboeMediator* mediator = i_oboeStream->oboeMediator; - ResultWithValue result = i_oboeStream->inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); + ResultWithValue result = mediator->m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -718,10 +731,10 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ * \brief Allocates the memory of an OboeStream, and sets its EngineAddress to this. * @return the address of the oboeStream. */ -OboeStream *OboeEngine::allocateOboeStream() { - OboeStream *oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); - oboeStream->setEngineAddress(this); - return oboeStream; +void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { + i_oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); + m_terminableMediator = i_oboeStream->oboeMediator = new OboeMediator(i_oboeStream); + i_oboeStream->oboeMediator->setEngine(this); } @@ -771,114 +784,119 @@ int32_t OboeEngine::getSelectedDevice(Direction i_direction) { return g_outputDeviceId; } -/*----------------------------- OboeCallback functions implementations -----------------------------*/ +/*----------------------------- OboeMediator functions implementations -----------------------------*/ /** * \brief Oboe's callback routine. */ DataCallbackResult -OboeCallback::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { +OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { clock_gettime(CLOCK_REALTIME, &m_timeSpec); m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); - m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate + m_timeInfo.currentTime); - m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeStreamHolder->framesPerHostCallback + m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback / - m_oboeStreamHolder->streamRepresentation.streamInfo.sampleRate + m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate + m_timeInfo.currentTime); /* check if StopStream or AbortStream was called */ - if (m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->callbackResult = paComplete; - } else if (m_oboeStreamHolder->doAbort) { - m_oboeStreamHolder->callbackResult = paAbort; + if (m_oboeCallbackStream->doStop) { + m_oboeCallbackStream->callbackResult = paComplete; + } else if (m_oboeCallbackStream->doAbort) { + m_oboeCallbackStream->callbackResult = paAbort; } - PaUtil_BeginCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&m_oboeStreamHolder->bufferProcessor, - &m_timeInfo, m_oboeStreamHolder->cbFlags); + PaUtil_BeginCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&m_oboeCallbackStream->bufferProcessor, + &m_timeInfo, m_oboeCallbackStream->cbFlags); - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->outputBuffers[m_oboeStreamHolder->currentOutputBuffer] = i_audioData; - PaUtil_SetOutputFrameCount(&m_oboeStreamHolder->bufferProcessor, i_numFrames); - PaUtil_SetInterleavedOutputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->outputBuffers)[ - m_oboeStreamHolder->currentOutputBuffer], + if (m_oboeCallbackStream->hasOutput) { + m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = i_audioData; + PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, i_numFrames); + PaUtil_SetInterleavedOutputChannels(&m_oboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeCallbackStream->outputBuffers)[ + m_oboeCallbackStream->currentOutputBuffer], 0); } - if (m_oboeStreamHolder->hasInput) { - i_audioData = m_oboeStreamHolder->inputBuffers[m_oboeStreamHolder->currentInputBuffer]; - PaUtil_SetInputFrameCount(&m_oboeStreamHolder->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&m_oboeStreamHolder->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeStreamHolder->inputBuffers)[ - m_oboeStreamHolder->currentInputBuffer], + if (m_oboeCallbackStream->hasInput) { + i_audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; + PaUtil_SetInputFrameCount(&m_oboeCallbackStream->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&m_oboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) m_oboeCallbackStream->inputBuffers)[ + m_oboeCallbackStream->currentInputBuffer], 0); } /* continue processing user buffers if callback result is paContinue or * if it is paComplete and userBuffers aren't empty yet */ - if (m_oboeStreamHolder->callbackResult == paContinue - || (m_oboeStreamHolder->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeStreamHolder->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeStreamHolder->bufferProcessor, - &m_oboeStreamHolder->callbackResult); + if (m_oboeCallbackStream->callbackResult == paContinue + || (m_oboeCallbackStream->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeCallbackStream->bufferProcessor))) { + m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeCallbackStream->bufferProcessor, + &m_oboeCallbackStream->callbackResult); } /* enqueue a buffer only when there are frames to be processed, * this will be 0 when paComplete + empty buffers or paAbort */ if (m_framesProcessed > 0) { - if (m_oboeStreamHolder->hasOutput) { - m_oboeStreamHolder->currentOutputBuffer = - (m_oboeStreamHolder->currentOutputBuffer + 1) % g_numberOfBuffers; + if (m_oboeCallbackStream->hasOutput) { + m_oboeCallbackStream->currentOutputBuffer = + (m_oboeCallbackStream->currentOutputBuffer + 1) % g_numberOfBuffers; } - if (m_oboeStreamHolder->hasInput) { - m_oboeStreamHolder->currentInputBuffer = (m_oboeStreamHolder->currentInputBuffer + 1) % g_numberOfBuffers; + if (m_oboeCallbackStream->hasInput) { + m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % g_numberOfBuffers; } } - PaUtil_EndCpuLoadMeasurement(&m_oboeStreamHolder->cpuLoadMeasurer, m_framesProcessed); + PaUtil_EndCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer, m_framesProcessed); /* StopStream was called */ - if (m_framesProcessed == 0 && m_oboeStreamHolder->doStop) { - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; + if (m_framesProcessed == 0 && m_oboeCallbackStream->doStop) { + m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; } /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (m_framesProcessed == 0 && !(m_oboeStreamHolder->doAbort || m_oboeStreamHolder->doStop)) { - m_oboeStreamHolder->isActive = false; - m_oboeStreamHolder->isStopped = true; - if (m_oboeStreamHolder->streamRepresentation.streamFinishedCallback != nullptr) - m_oboeStreamHolder->streamRepresentation.streamFinishedCallback( - m_oboeStreamHolder->streamRepresentation.userData); - m_oboeStreamHolder->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) + else if (m_framesProcessed == 0 && !(m_oboeCallbackStream->doAbort || m_oboeCallbackStream->doStop)) { + m_oboeCallbackStream->isActive = false; + m_oboeCallbackStream->isStopped = true; + if (m_oboeCallbackStream->streamRepresentation.streamFinishedCallback != nullptr) + m_oboeCallbackStream->streamRepresentation.streamFinishedCallback( + m_oboeCallbackStream->streamRepresentation.userData); + m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) } - return m_oboeStreamHolder->oboeCallbackResult; + return m_oboeCallbackStream->oboeCallbackResult; } /** * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells - * what error occurred. + * what error occurred, and tries to restart the stream if the error was ErrorDisconnected. */ -void OboeCallback::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { +void OboeMediator::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { if (i_error == oboe::Result::ErrorDisconnected) { - OboeEngine* oboeEngine = m_oboeStreamHolder->getEngineAddress(); - LOGW("[OboeCallback::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); - if (!oboeEngine->restartStream(m_oboeStreamHolder, 0)) - LOGE("[OboeCallback::onErrorAfterClose]\t Couldn't restart stream(s)"); + OboeEngine* oboeEngine = getEngine(); + LOGW("[OboeMediator::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); + int i = 0; + if(m_oboeCallbackStream->hasOutput) + i++; + if(m_oboeCallbackStream->hasInput) + i+=2; + if (!oboeEngine->restartStream(m_oboeCallbackStream, i)) + LOGE("[OboeMediator::onErrorAfterClose]\t Couldn't restart stream(s)"); } else - LOGE("[OboeCallback::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); + LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); } /** - * \brief Resets callback counters (called at the start of each iteration of onAudioReady + * \brief Resets callback counters (called at the start of each iteration of onAudioReady). */ -void OboeCallback::resetCallbackCounters() { +void OboeMediator::resetCallbackCounters() { m_framesProcessed = 0; m_timeInfo = {0, 0, 0}; } @@ -1368,7 +1386,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; - OboeStream *oboeStream = oboeHostApi->oboeEngine->allocateOboeStream(); + OboeStream* oboeStream; + oboeHostApi->oboeEngine->constructOboeStream(oboeStream); if (!oboeStream) { error = paInsufficientMemory; @@ -1554,7 +1573,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, */ static PaError CloseStream(PaStream *i_paStream) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (!(oboeEngine->closeStream(oboeStream))) LOGW("[PaOboe - CloseStream]\t Some errors have occurred in closing oboe streams - see OboeEngine::CloseStream logs."); @@ -1588,7 +1607,7 @@ static PaError CloseStream(PaStream *i_paStream) { */ static PaError StartStream(PaStream *i_paStream) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); PaUtil_ResetBufferProcessor(&oboeStream->bufferProcessor); @@ -1647,7 +1666,7 @@ static PaError StartStream(PaStream *i_paStream) { static PaError StopStream(PaStream *i_paStream) { PaError error = paNoError; auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (oboeStream->isStopped) { LOGW("[PaOboe - StopStream]\t Stream was already stopped."); @@ -1681,7 +1700,7 @@ static PaError StopStream(PaStream *i_paStream) { static PaError AbortStream(PaStream *i_paStream) { PaError error = paNoError; auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); LOGI("[PaOboe - AbortStream]\t Aborting stream."); if (!oboeStream->isBlocking) { @@ -1714,7 +1733,7 @@ static PaError AbortStream(PaStream *i_paStream) { */ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_frames) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); void *userBuffer = i_buffer; unsigned framesToRead; PaError error = paNoError; @@ -1744,7 +1763,7 @@ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_ */ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned long i_frames) { auto *oboeStream = (OboeStream *) i_paStream; - auto *oboeEngine = oboeStream->getEngineAddress(); + auto *oboeEngine = oboeStream->oboeMediator->getEngine(); const void *userBuffer = i_buffer; unsigned framesToWrite; PaError error = paNoError; From b3a5422cf8b541e45adf84ad22ba5326d3571e2e Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Fri, 6 Oct 2023 10:07:35 +0200 Subject: [PATCH 105/125] Fixed allocation problem, working PaOboe implementation --- src/hostapi/oboe/pa_oboe.cpp | 44 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index a716c4340..2fd0dc564 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -384,6 +384,12 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { PaError error = paNoError; Result result; + + if (i_oboeStream == nullptr) { + LOGE("[OboeEngine::openStream]\t i_oboeStream is a nullptr."); + return paInternalError; + } + OboeMediator* mediator = i_oboeStream->oboeMediator; if(!(i_oboeStream->isBlocking)){ @@ -594,26 +600,22 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { */ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - bool hasOutput = true, hasInput = true; - OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::closeStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); - mediator = m_terminableMediator; - } else { - mediator = i_oboeStream->oboeMediator; - hasInput = i_oboeStream->hasInput; - hasOutput = i_oboeStream->hasOutput; + LOGE("[OboeEngine::closeStream]\t i_oboeStream is a nullptr."); + return false; } - if (hasOutput) { + OboeMediator* mediator = i_oboeStream->oboeMediator; + + if (i_oboeStream->hasOutput) { outputResult = mediator->m_outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } - if (hasInput) { + if (i_oboeStream->hasInput) { inputResult = mediator->m_inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; @@ -632,19 +634,15 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { */ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - bool hasOutput = true, hasInput = true; - OboeMediator* mediator; if (i_oboeStream == nullptr) { - LOGW("[OboeEngine::abortStream]\t i_oboeStream is a nullptr. Aborting both oboe streams of the terminable mediator"); - mediator = m_terminableMediator; - } else { - mediator = i_oboeStream->oboeMediator; - hasInput = i_oboeStream->hasInput; - hasOutput = i_oboeStream->hasOutput; + LOGE("[OboeEngine::abortStream]\t i_oboeStream is a nullptr."); + return false; } - if (hasInput) { + OboeMediator* mediator = i_oboeStream->oboeMediator; + + if (i_oboeStream->hasInput) { inputResult = mediator->m_inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", @@ -654,7 +652,7 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } - if (hasOutput) { + if (i_oboeStream->hasOutput) { outputResult = mediator->m_outputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", @@ -732,7 +730,6 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ * @return the address of the oboeStream. */ void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { - i_oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream)); m_terminableMediator = i_oboeStream->oboeMediator = new OboeMediator(i_oboeStream); i_oboeStream->oboeMediator->setEngine(this); } @@ -1176,9 +1173,6 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; - if (!(oboeHostApi->oboeEngine->closeStream(nullptr))) - LOGI("[PaOboe - Terminate]\t The streams were probably already closed - see OboeEngine::CloseStream logs."); - if (oboeHostApi->oboeEngine != nullptr) delete oboeHostApi->oboeEngine; @@ -1386,7 +1380,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; - OboeStream* oboeStream; + OboeStream* oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream));; oboeHostApi->oboeEngine->constructOboeStream(oboeStream); if (!oboeStream) { From b78a2414545c53032752d83365bf80b1bba54ee8 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 10:29:19 +0200 Subject: [PATCH 106/125] fixed README.md indenting --- src/hostapi/oboe/README.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/hostapi/oboe/README.md b/src/hostapi/oboe/README.md index c4de74088..14d6b6b73 100644 --- a/src/hostapi/oboe/README.md +++ b/src/hostapi/oboe/README.md @@ -6,28 +6,37 @@ headers in your project. Building: ---- To build portaudio with Oboe, there are some necessary steps: -1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at https://developer.android.com/ndk/downloads. -2) Clone the Oboe repository - just follow the steps detailed here: https://github.com/google/oboe/blob/main/docs/GettingStarted.md. - Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the prebuilt libraries, you can skip this step. -3) Set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) to the path of the cloned Oboe repository, and build the Oboe libraries (you can use "build_all_android.sh"). +1) An android NDK is needed to crosscompile it. I used the version 25.1.8937393, which I found at + https://developer.android.com/ndk/downloads. +2) Clone the Oboe repository - just follow the steps detailed here: + https://github.com/google/oboe/blob/main/docs/GettingStarted.md. + Make sure to correctly link the NDK path in the Oboe build. If you instead prefer to use the + prebuilt libraries, you can skip this step. +3) Set the CMake variable OBOE_DIRECTORY (used in cmake/modules/FindOboe.cmake) to the path of the + cloned Oboe repository, and build the Oboe libraries (you can use "build_all_android.sh"). If you instead used the prebuilt libraries, do the following: - set OBOE_DIRECTORY to TRUE; - - set OBOE_LIBRARY_DIRS path_to_Oboe_libraries_folder/${ANDROID_ABI}), the code will search the prebuilt library of the chosen ABI in that folder. + - set OBOE_LIBRARY_DIRS path_to_Oboe_libraries_folder/${ANDROID_ABI}), the code will search the + prebuilt library of the chosen ABI in that folder. 4) Build PaOboe (you can use "build_all_PaOboe.sh"). 5) Don't forget to add liboboe.so and libportaudio.so in your jniLibs folder. TODOs: ---- -- Testing. This implementation is being tested for VoIP calls that use blocking streams - for everything else, lots of tests are needed. +- Testing. This implementation is being tested for VoIP calls that use blocking streams - for + everything else, lots of tests are needed. Misc ---- ### Audio Format: -If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream by modifying the format selection marked with a *FIXME*. -I'm positive that automatic format selection is possible, but simply using PaUtil_SelectClosestAvailableFormat got me nowhere. +If you need to select a specific audio format, you'll have to manually set it in PaOboe_OpenStream +by modifying the format selection marked with a *FIXME*. I'm positive that automatic format selection +is possible, but simply using PaUtil_SelectClosestAvailableFormat got me nowhere. ### Buffer sizes: -Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your buffer you should manually modify it (or make a simple function that can set it). For your convenience, there is a *FIXME* as a bookmark. \ No newline at end of file +Portaudio often tries to get approximately low buffer sizes, and if you need specific sizes for your +buffer you should manually modify it (or make a simple function that can set it). For your convenience, +there is a *FIXME* as a bookmark. \ No newline at end of file From ea1a4a4cd69c3599048cc1222af83d08e3ba68ba Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 10:30:28 +0200 Subject: [PATCH 107/125] Removed .idea folder from repo --- .idea/.gitignore | 3 --- .idea/misc.xml | 5 ----- .idea/modules.xml | 8 -------- .idea/portaudio.iml | 9 --------- .idea/vcs.xml | 6 ------ 5 files changed, 31 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/portaudio.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521a..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 6e8667213..000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index f2d867481..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/portaudio.iml b/.idea/portaudio.iml deleted file mode 100644 index d6ebd4805..000000000 --- a/.idea/portaudio.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfb..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 25999dedaecab0c917389c0aadb17176e18602a1 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 10:46:33 +0200 Subject: [PATCH 108/125] replaced 'g_' with 'paOboe_' in non-static global variables --- src/hostapi/oboe/pa_oboe.cpp | 568 +++++++++++++++++------------------ 1 file changed, 284 insertions(+), 284 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 2fd0dc564..ca1571a59 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -152,17 +152,17 @@ static signed long GetStreamWriteAvailable(PaStream *stream); static unsigned long GetApproximateLowBufferSize(); // Commonly used parameters initialized. -static unsigned long g_nativeBufferSize = 0; -static unsigned g_numberOfBuffers = 2; +static unsigned long paOboe_nativeBufferSize = 0; +static unsigned paOboe_numberOfBuffers = 2; using namespace oboe; //Useful global variables -int32_t g_inputDeviceId = kUnspecified; -int32_t g_outputDeviceId = kUnspecified; +int32_t paOboe_inputDeviceId = kUnspecified; +int32_t paOboe_outputDeviceId = kUnspecified; -PerformanceMode g_inputPerfMode = PerformanceMode::LowLatency; -PerformanceMode g_outputPerfMode = PerformanceMode::LowLatency; +PerformanceMode paOboe_inputPerfMode = PerformanceMode::LowLatency; +PerformanceMode paOboe_outputPerfMode = PerformanceMode::LowLatency; class OboeEngine; class OboeMediator; @@ -205,8 +205,8 @@ typedef struct OboeStream { class OboeMediator: public AudioStreamCallback{ public: - OboeMediator(OboeStream* i_oboeStream) { - m_oboeCallbackStream = i_oboeStream; + OboeMediator(OboeStream* oboeStream) { + m_oboeCallbackStream = oboeStream; } //Callback function for non-blocking streams @@ -220,10 +220,10 @@ class OboeMediator: public AudioStreamCallback{ //getter and setter of m_oboeEngine and m_oboeCallbackStream OboeEngine *getEngine() { return m_oboeEngine; } - void setEngine(OboeEngine *i_oboeEngine) { m_oboeEngine = i_oboeEngine; } + void setEngine(OboeEngine *oboeEngine) { m_oboeEngine = oboeEngine; } OboeStream *getStreamAddress() { return m_oboeCallbackStream; } - void setCallbackStream(OboeStream *i_oboeStream) { m_oboeCallbackStream = i_oboeStream; } + void setCallbackStream(OboeStream *oboeStream) { m_oboeCallbackStream = oboeStream; } //The only instances of output and input streams that will be used, and their builders std::shared_ptr m_outputStream; @@ -313,8 +313,8 @@ OboeEngine::OboeEngine() {} /** - * \brief Tries to open a stream with the direction i_direction, sample rate i_sampleRate and/or - * channel count i_channelCount. It then checks if the stream was in fact opened with the + * \brief Tries to open a stream with the direction direction, sample rate sampleRate and/or + * channel count channelCount. It then checks if the stream was in fact opened with the * desired settings, and then closes the stream. It's used to see if the requested * parameters are supported by the devices that are going to be used. * @param direction the Direction of the stream; @@ -323,16 +323,16 @@ OboeEngine::OboeEngine() {} * @return true if the requested sample rate / channel count is supported by the device, false if * they aren't, or if tryStream couldn't open a stream. */ -bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t i_channelCount) { +bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { Result result; bool outcome = false; - m_testBuilder.setDeviceId(getSelectedDevice(i_direction)) + m_testBuilder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) - ->setDirection(i_direction) - ->setSampleRate(i_sampleRate) - ->setChannelCount(i_channelCount) + ->setDirection(direction) + ->setSampleRate(sampleRate) + ->setChannelCount(channelCount) ->openStream(m_testStream); if (result != Result::OK) { @@ -341,17 +341,17 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t return outcome; } - if (i_sampleRate != kUnspecified) { - outcome = (i_sampleRate == m_testBuilder.getSampleRate()); + if (sampleRate != kUnspecified) { + outcome = (sampleRate == m_testBuilder.getSampleRate()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - i_sampleRate, m_testBuilder.getSampleRate()); + sampleRate, m_testBuilder.getSampleRate()); } - } else if (i_channelCount != kUnspecified) { - outcome = (i_channelCount == m_testBuilder.getChannelCount()); + } else if (channelCount != kUnspecified) { + outcome = (channelCount == m_testBuilder.getChannelCount()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - i_channelCount, m_testBuilder.getChannelCount()); + channelCount, m_testBuilder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); @@ -380,33 +380,33 @@ bool OboeEngine::tryStream(Direction i_direction, int32_t i_sampleRate, int32_t * @return paNoError if everything goes as expected, paUnanticipatedHostError if Oboe fails to open * a stream, and paInsufficientMemory if the memory allocation of the buffers fails. */ -PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, int32_t i_sampleRate, - Usage i_androidOutputUsage, InputPreset i_androidInputPreset) { +PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int32_t sampleRate, + Usage androidOutputUsage, InputPreset androidInputPreset) { PaError error = paNoError; Result result; - if (i_oboeStream == nullptr) { - LOGE("[OboeEngine::openStream]\t i_oboeStream is a nullptr."); + if (oboeStream == nullptr) { + LOGE("[OboeEngine::openStream]\t oboeStream is a nullptr."); return paInternalError; } - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if(!(i_oboeStream->isBlocking)){ + if(!(oboeStream->isBlocking)){ mediator->resetCallbackCounters(); } - if (i_direction == Direction::Input) { - mediator->m_inputBuilder.setChannelCount(i_oboeStream->bufferProcessor.inputChannelCount) - ->setFormat(PaToOboeFormat(i_oboeStream->inputFormat)) - ->setSampleRate(i_sampleRate) + if (direction == Direction::Input) { + mediator->m_inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) + ->setSampleRate(sampleRate) ->setDirection(Direction::Input) ->setDeviceId(getSelectedDevice(Direction::Input)) - ->setPerformanceMode(g_inputPerfMode) - ->setInputPreset(i_androidInputPreset) - ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); + ->setPerformanceMode(paOboe_inputPerfMode) + ->setInputPreset(androidInputPreset) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); - if (!(i_oboeStream->isBlocking)) { + if (!(oboeStream->isBlocking)) { mediator->setInputCallback(); } @@ -419,37 +419,37 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, } mediator->m_inputStream->setBufferSizeInFrames(mediator->m_inputStream->getFramesPerBurst() * - g_numberOfBuffers); - i_oboeStream->inputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); + paOboe_numberOfBuffers); + oboeStream->inputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < g_numberOfBuffers; ++i) { - i_oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - i_oboeStream->framesPerHostCallback * - i_oboeStream->bytesPerFrame * - i_oboeStream->bufferProcessor.inputChannelCount); + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { + oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.inputChannelCount); - if (!i_oboeStream->inputBuffers[i]) { + if (!oboeStream->inputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(i_oboeStream->inputBuffers[j]); - PaUtil_FreeMemory(i_oboeStream->inputBuffers); + PaUtil_FreeMemory(oboeStream->inputBuffers[j]); + PaUtil_FreeMemory(oboeStream->inputBuffers); mediator->m_inputStream->close(); error = paInsufficientMemory; break; } } - i_oboeStream->currentInputBuffer = 0; + oboeStream->currentInputBuffer = 0; } else { - mediator->m_outputBuilder.setChannelCount(i_oboeStream->bufferProcessor.outputChannelCount) - ->setFormat(PaToOboeFormat(i_oboeStream->outputFormat)) - ->setSampleRate(i_sampleRate) + mediator->m_outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) + ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) + ->setSampleRate(sampleRate) ->setDirection(Direction::Output) ->setDeviceId(getSelectedDevice(Direction::Output)) - ->setPerformanceMode(g_outputPerfMode) - ->setUsage(i_androidOutputUsage) - ->setFramesPerCallback(i_oboeStream->framesPerHostCallback); + ->setPerformanceMode(paOboe_outputPerfMode) + ->setUsage(androidOutputUsage) + ->setFramesPerCallback(oboeStream->framesPerHostCallback); - if (!(i_oboeStream->isBlocking)) { + if (!(oboeStream->isBlocking)) { mediator->setOutputCallback(); } @@ -461,26 +461,26 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, } mediator->m_outputStream->setBufferSizeInFrames(mediator->m_outputStream->getFramesPerBurst() * - g_numberOfBuffers); - i_oboeStream->outputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(g_numberOfBuffers * sizeof(int32_t * )); + paOboe_numberOfBuffers); + oboeStream->outputBuffers = + (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); - for (int i = 0; i < g_numberOfBuffers; ++i) { - i_oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - i_oboeStream->framesPerHostCallback * - i_oboeStream->bytesPerFrame * - i_oboeStream->bufferProcessor.outputChannelCount); + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { + oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + oboeStream->framesPerHostCallback * + oboeStream->bytesPerFrame * + oboeStream->bufferProcessor.outputChannelCount); - if (!i_oboeStream->outputBuffers[i]) { + if (!oboeStream->outputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(i_oboeStream->outputBuffers[j]); - PaUtil_FreeMemory(i_oboeStream->outputBuffers); + PaUtil_FreeMemory(oboeStream->outputBuffers[j]); + PaUtil_FreeMemory(oboeStream->outputBuffers); mediator->m_outputStream->close(); error = paInsufficientMemory; break; } } - i_oboeStream->currentOutputBuffer = 0; + oboeStream->currentOutputBuffer = 0; } return error; @@ -493,17 +493,17 @@ PaError OboeEngine::openStream(OboeStream *i_oboeStream, Direction i_direction, * @param oboeStream The stream we want to start. * @return true if the streams we wanted to start are started successfully, false otherwise. */ -bool OboeEngine::startStream(OboeStream *i_oboeStream) { +bool OboeEngine::startStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", @@ -520,17 +520,17 @@ bool OboeEngine::startStream(OboeStream *i_oboeStream) { * @param oboeStream The stream we want to stop. * @return true if the streams we wanted to stop are stopped successfully, false otherwise. */ -bool OboeEngine::stopStream(OboeStream *i_oboeStream) { +bool OboeEngine::stopStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", @@ -549,12 +549,12 @@ bool OboeEngine::stopStream(OboeStream *i_oboeStream) { * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 3 for both). * @return true if the stream is restarted successfully, false otherwise. */ -bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { +bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { bool outcome = true; Result result; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - switch (i_direction) { + switch (direction) { case 1: //output-only result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); if (result != Result::OK) @@ -584,7 +584,7 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { default: // unspecified direction or both directions: restart both streams LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); - outcome = (restartStream(i_oboeStream, 1) && restartStream(i_oboeStream, 2)); + outcome = (restartStream(oboeStream, 1) && restartStream(oboeStream, 2)); break; } @@ -598,24 +598,24 @@ bool OboeEngine::restartStream(OboeStream* i_oboeStream, int i_direction) { * @param oboeStream The stream we want to close. * @return true if the stream is closed successfully, otherwise returns false. */ -bool OboeEngine::closeStream(OboeStream *i_oboeStream) { +bool OboeEngine::closeStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - if (i_oboeStream == nullptr) { - LOGE("[OboeEngine::closeStream]\t i_oboeStream is a nullptr."); + if (oboeStream == nullptr) { + LOGE("[OboeEngine::closeStream]\t oboeStream is a nullptr."); return false; } - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; @@ -632,17 +632,17 @@ bool OboeEngine::closeStream(OboeStream *i_oboeStream) { * @param oboeStream The stream we want to abort. * @return true if the output stream and the input stream are stopped successfully, false otherwise. */ -bool OboeEngine::abortStream(OboeStream *i_oboeStream) { +bool OboeEngine::abortStream(OboeStream *oboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - if (i_oboeStream == nullptr) { - LOGE("[OboeEngine::abortStream]\t i_oboeStream is a nullptr."); + if (oboeStream == nullptr) { + LOGE("[OboeEngine::abortStream]\t oboeStream is a nullptr."); return false; } - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - if (i_oboeStream->hasInput) { + if (oboeStream->hasInput) { inputResult = mediator->m_inputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", @@ -652,7 +652,7 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } - if (i_oboeStream->hasOutput) { + if (oboeStream->hasOutput) { outputResult = mediator->m_outputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", @@ -676,15 +676,15 @@ bool OboeEngine::abortStream(OboeStream *i_oboeStream) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int32_t i_framesToWrite) { +bool OboeEngine::writeStream(OboeStream *oboeStream, const void buffer, int32_t framesToWrite) { bool outcome = true; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_outputStream->write(i_buffer, i_framesToWrite, TIMEOUT_NS); + ResultWithValue result = mediator->m_outputStream->write(buffer, framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (restartStream(i_oboeStream, 1)) + if (restartStream(oboeStream, 1)) return true; } @@ -705,15 +705,15 @@ bool OboeEngine::writeStream(OboeStream *i_oboeStream, const void *i_buffer, int * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_framesToRead) { +bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead) { bool outcome = true; - OboeMediator* mediator = i_oboeStream->oboeMediator; + OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_inputStream->read(i_buffer, i_framesToRead, TIMEOUT_NS); + ResultWithValue result = mediator->m_inputStream->read(buffer, framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (restartStream(i_oboeStream, 2)) + if (restartStream(oboeStream, 2)) return true; } @@ -729,9 +729,9 @@ bool OboeEngine::readStream(OboeStream *i_oboeStream, void *i_buffer, int32_t i_ * \brief Allocates the memory of an OboeStream, and sets its EngineAddress to this. * @return the address of the oboeStream. */ -void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { - m_terminableMediator = i_oboeStream->oboeMediator = new OboeMediator(i_oboeStream); - i_oboeStream->oboeMediator->setEngine(this); +void OboeEngine::constructOboeStream(OboeStream* oboeStream) { + m_terminableMediator = oboeStream->oboeMediator = new OboeMediator(oboeStream); + oboeStream->oboeMediator->setEngine(this); } @@ -740,9 +740,9 @@ void OboeEngine::constructOboeStream(OboeStream* i_oboeStream) { * @param paFormat the PaSampleFormat we want to convert. * @return the converted AudioFormat. */ -AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat i_paFormat) { +AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { AudioFormat oboeFormat; - switch (i_paFormat) { + switch (paFormat) { case paFloat32: oboeFormat = AudioFormat::Float; LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); @@ -774,11 +774,11 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat i_paFormat) { * @param direction the Oboe::Direction for which we want to know the device Id. * @return the device Id of the appropriate direction. */ -int32_t OboeEngine::getSelectedDevice(Direction i_direction) { - if (i_direction == Direction::Input) - return g_inputDeviceId; +int32_t OboeEngine::getSelectedDevice(Direction direction) { + if (direction == Direction::Input) + return paOboe_inputDeviceId; else - return g_outputDeviceId; + return paOboe_outputDeviceId; } /*----------------------------- OboeMediator functions implementations -----------------------------*/ @@ -786,7 +786,7 @@ int32_t OboeEngine::getSelectedDevice(Direction i_direction) { * \brief Oboe's callback routine. */ DataCallbackResult -OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_t i_numFrames) { +OboeMediator::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { clock_gettime(CLOCK_REALTIME, &m_timeSpec); m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); @@ -811,15 +811,15 @@ OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ &m_timeInfo, m_oboeCallbackStream->cbFlags); if (m_oboeCallbackStream->hasOutput) { - m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = i_audioData; - PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, i_numFrames); + m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = audioData; + PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, numFrames); PaUtil_SetInterleavedOutputChannels(&m_oboeCallbackStream->bufferProcessor, 0, (void *) ((PaInt16 **) m_oboeCallbackStream->outputBuffers)[ m_oboeCallbackStream->currentOutputBuffer], 0); } if (m_oboeCallbackStream->hasInput) { - i_audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; + audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; PaUtil_SetInputFrameCount(&m_oboeCallbackStream->bufferProcessor, 0); PaUtil_SetInterleavedInputChannels(&m_oboeCallbackStream->bufferProcessor, 0, (void *) ((PaInt16 **) m_oboeCallbackStream->inputBuffers)[ @@ -842,10 +842,10 @@ OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ if (m_framesProcessed > 0) { if (m_oboeCallbackStream->hasOutput) { m_oboeCallbackStream->currentOutputBuffer = - (m_oboeCallbackStream->currentOutputBuffer + 1) % g_numberOfBuffers; + (m_oboeCallbackStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; } if (m_oboeCallbackStream->hasInput) { - m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % g_numberOfBuffers; + m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; } } @@ -874,8 +874,8 @@ OboeMediator::onAudioReady(AudioStream *i_audioStream, void *i_audioData, int32_ * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells * what error occurred, and tries to restart the stream if the error was ErrorDisconnected. */ -void OboeMediator::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) { - if (i_error == oboe::Result::ErrorDisconnected) { +void OboeMediator::onErrorAfterClose(AudioStream *audioStream, Result error) { + if (error == oboe::Result::ErrorDisconnected) { OboeEngine* oboeEngine = getEngine(); LOGW("[OboeMediator::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); int i = 0; @@ -886,7 +886,7 @@ void OboeMediator::onErrorAfterClose(AudioStream *i_audioStream, Result i_error) if (!oboeEngine->restartStream(m_oboeCallbackStream, i)) LOGE("[OboeMediator::onErrorAfterClose]\t Couldn't restart stream(s)"); } else - LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(i_error)); + LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); } @@ -910,9 +910,9 @@ void OboeMediator::resetCallbackCounters() { * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, - i_sampleRate, +PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, + sampleRate, kUnspecified))) LOGW("[PaOboe - IsOutputSampleRateSupported]\t Sample Rate was changed by Oboe."); @@ -932,9 +932,9 @@ PaError IsOutputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the sample * rate was changed by Oboe. */ -PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, double i_sampleRate) { - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, - i_sampleRate, +PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *oboeHostApi, double sampleRate) { + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, + sampleRate, kUnspecified))) LOGW("[PaOboe - IsInputSampleRateSupported]\t Sample Rate was changed by Oboe."); @@ -953,15 +953,15 @@ PaError IsInputSampleRateSupported(PaOboeHostApiRepresentation *i_oboeHostApi, d * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { - if (i_numOfChannels > 2 || i_numOfChannels == 0) { +static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *oboeHostApi, int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { LOGE("[PaOboe - IsOutputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Output, + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Output, kUnspecified, - i_numOfChannels))) + numOfChannels))) LOGW("[PaOboe - IsOutputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -979,15 +979,15 @@ static PaError IsOutputChannelCountSupported(PaOboeHostApiRepresentation *i_oboe * @return PaNoError regardless of the outcome of the check, but warns in the Logs if the channel * count was changed by Oboe. */ -static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeHostApi, int32_t i_numOfChannels) { - if (i_numOfChannels > 2 || i_numOfChannels == 0) { +static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *oboeHostApi, int32_t numOfChannels) { + if (numOfChannels > 2 || numOfChannels == 0) { LOGE("[PaOboe - IsInputChannelCountSupported]\t Invalid channel count."); return paInvalidChannelCount; } - if (!(i_oboeHostApi->oboeEngine->tryStream(Direction::Input, + if (!(oboeHostApi->oboeEngine->tryStream(Direction::Input, kUnspecified, - i_numOfChannels))) + numOfChannels))) LOGW("[PaOboe - IsInputChannelCountSupported]\t Channel Count was changed by Oboe. The device might not support stereo audio."); /* Since Oboe manages the channel count in a smart way, we can avoid blocking the process if @@ -1004,7 +1004,7 @@ static PaError IsInputChannelCountSupported(PaOboeHostApiRepresentation *i_oboeH * @param hostApiIndex is a PaHostApiIndex, the type used to enumerate the host APIs at runtime. * @return paNoError if no errors occur, or paInsufficientMemory if memory allocation fails; */ -PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiIndex i_hostApiIndex) { +PaError PaOboe_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) { PaError result = paNoError; int deviceCount; PaOboeHostApiRepresentation *oboeHostApi; @@ -1026,20 +1026,20 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde goto error; } - *i_hostApi = &oboeHostApi->inheritedHostApiRep; + *hostApi = &oboeHostApi->inheritedHostApiRep; // Info initialization. - (*i_hostApi)->info.structVersion = 1; - (*i_hostApi)->info.type = paInDevelopment; - (*i_hostApi)->info.name = "android Oboe"; - (*i_hostApi)->info.defaultOutputDevice = 0; - (*i_hostApi)->info.defaultInputDevice = 0; - (*i_hostApi)->info.deviceCount = 0; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paInDevelopment; + (*hostApi)->info.name = "android Oboe"; + (*hostApi)->info.defaultOutputDevice = 0; + (*hostApi)->info.defaultInputDevice = 0; + (*hostApi)->info.deviceCount = 0; deviceCount = 1; - (*i_hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( + (*hostApi)->deviceInfos = (PaDeviceInfo **) PaUtil_GroupAllocateZeroInitializedMemory( oboeHostApi->allocations, sizeof(PaDeviceInfo * ) * deviceCount); - if (!(*i_hostApi)->deviceInfos) { + if (!(*hostApi)->deviceInfos) { result = paInsufficientMemory; goto error; } @@ -1055,7 +1055,7 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde for (int i = 0; i < deviceCount; ++i) { PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; deviceInfo->structVersion = 2; - deviceInfo->hostApi = i_hostApiIndex; + deviceInfo->hostApi = hostApiIndex; /* OboeEngine will handle manual device selection through the use of PaOboe_SetSelectedDevice. Portaudio doesn't need to know about this, so we just use a default device. */ @@ -1097,18 +1097,18 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde if (deviceInfo->defaultSampleRate == 0) goto error; - /* If the user has set g_nativeBufferSize by querying the optimal buffer size via java, + /* If the user has set paOboe_nativeBufferSize by querying the optimal buffer size via java, use the user-defined value since that will offer the lowest possible latency. */ - if (g_nativeBufferSize != 0) { + if (paOboe_nativeBufferSize != 0) { deviceInfo->defaultLowInputLatency = - (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize / deviceInfo->defaultSampleRate; deviceInfo->defaultLowOutputLatency = - (double) g_nativeBufferSize / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize / deviceInfo->defaultSampleRate; deviceInfo->defaultHighInputLatency = - (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; deviceInfo->defaultHighOutputLatency = - (double) g_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; + (double) paOboe_nativeBufferSize * 4 / deviceInfo->defaultSampleRate; } else { deviceInfo->defaultLowInputLatency = (double) GetApproximateLowBufferSize() / deviceInfo->defaultSampleRate; @@ -1120,13 +1120,13 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde (double) GetApproximateLowBufferSize() * 4 / deviceInfo->defaultSampleRate; } - (*i_hostApi)->deviceInfos[i] = deviceInfo; - ++(*i_hostApi)->info.deviceCount; + (*hostApi)->deviceInfos[i] = deviceInfo; + ++(*hostApi)->info.deviceCount; } - (*i_hostApi)->Terminate = Terminate; - (*i_hostApi)->OpenStream = OpenStream; - (*i_hostApi)->IsFormatSupported = IsFormatSupported; + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; PaUtil_InitializeStreamInterface(&oboeHostApi->callbackStreamInterface, CloseStream, StartStream, StopStream, @@ -1170,8 +1170,8 @@ PaError PaOboe_Initialize(PaUtilHostApiRepresentation **i_hostApi, PaHostApiInde * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the * interface to a host API (see struct in "pa_hostapi.h"). */ -static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { - auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; +static void Terminate(struct PaUtilHostApiRepresentation *hostApi) { + auto *oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; if (oboeHostApi->oboeEngine != nullptr) delete oboeHostApi->oboeEngine; @@ -1195,17 +1195,17 @@ static void Terminate(struct PaUtilHostApiRepresentation *i_hostApi) { * @return paNoError (== paFormatIsSupported) if no errors occur, otherwise returns an appropriate * PaError message. */ -static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, - const PaStreamParameters *i_inputParameters, - const PaStreamParameters *i_outputParameters, - double i_sampleRate) { +static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate) { int inputChannelCount, outputChannelCount; PaSampleFormat inputSampleFormat, outputSampleFormat; - auto *oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + auto *oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; - if (i_inputParameters) { - inputChannelCount = i_inputParameters->channelCount; - inputSampleFormat = i_inputParameters->sampleFormat; + if (inputParameters) { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ @@ -1215,21 +1215,21 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) { + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) { return paInvalidDevice; } /* check that input device can support inputChannelCount */ if (inputChannelCount > - i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) { + hostApi->deviceInfos[inputParameters->device]->maxInputChannels) { return paInvalidChannelCount; } /* validate inputStreamInfo */ - if (i_inputParameters->hostApiSpecificStreamInfo) { + if (inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. InputPreset androidRecordingPreset = - ((PaOboeStreamInfo *) i_inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + ((PaOboeStreamInfo *) inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; if (androidRecordingPreset != InputPreset::Generic && androidRecordingPreset != InputPreset::Camcorder && androidRecordingPreset != InputPreset::VoiceRecognition && @@ -1242,9 +1242,9 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, inputChannelCount = 0; } - if (i_outputParameters) { - outputChannelCount = i_outputParameters->channelCount; - outputSampleFormat = i_outputParameters->sampleFormat; + if (outputParameters) { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ @@ -1254,20 +1254,20 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ - if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) { + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) { return paInvalidDevice; } /* check that output device can support outputChannelCount */ - if (outputChannelCount > i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) { + if (outputChannelCount > hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) { return paInvalidChannelCount; } /* validate outputStreamInfo */ - if (i_outputParameters->hostApiSpecificStreamInfo) { + if (outputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. Usage androidOutputUsage = - ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; if (androidOutputUsage != Usage::Media && androidOutputUsage != Usage::Notification && androidOutputUsage != Usage::NotificationEvent && @@ -1284,12 +1284,12 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, } if (outputChannelCount > 0) { - if (IsOutputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + if (IsOutputSampleRateSupported(oboeHostApi, sampleRate) != paNoError) { return paInvalidSampleRate; } } if (inputChannelCount > 0) { - if (IsInputSampleRateSupported(oboeHostApi, i_sampleRate) != paNoError) { + if (IsInputSampleRateSupported(oboeHostApi, sampleRate) != paNoError) { return paInvalidSampleRate; } } @@ -1310,13 +1310,13 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *i_hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, - Usage i_androidOutputUsage, double i_sampleRate) { +static PaError InitializeOutputStream(OboeStream* oboeStream, PaOboeHostApiRepresentation *oboeHostApi, + Usage androidOutputUsage, double sampleRate) { - return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + return oboeHostApi->oboeEngine->openStream(oboeStream, Direction::Output, - i_sampleRate, - i_androidOutputUsage, + sampleRate, + androidOutputUsage, Generic); //Input preset won't be used, so we put the default value. } @@ -1333,14 +1333,14 @@ static PaError InitializeOutputStream(OboeStream* i_oboeStream, PaOboeHostApiRep * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeInputStream(OboeStream* i_oboeStream, PaOboeHostApiRepresentation *i_oboeHostApi, - InputPreset i_androidInputPreset, double i_sampleRate) { +static PaError InitializeInputStream(OboeStream* oboeStream, PaOboeHostApiRepresentation *oboeHostApi, + InputPreset androidInputPreset, double sampleRate) { - return i_oboeHostApi->oboeEngine->openStream(i_oboeStream, + return oboeHostApi->oboeEngine->openStream(oboeStream, Direction::Input, - i_sampleRate, + sampleRate, Usage::Media, //Usage won't be used, so we put the default value. - i_androidInputPreset); + androidInputPreset); } @@ -1361,17 +1361,17 @@ static PaError InitializeInputStream(OboeStream* i_oboeStream, PaOboeHostApiRepr * manipulation or checks. * @return paNoError if no errors occur, or other error codes accordingly with what goes wrong. */ -static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, - PaStream **i_paStream, - const PaStreamParameters *i_inputParameters, - const PaStreamParameters *i_outputParameters, - double i_sampleRate, - unsigned long i_framesPerBuffer, - PaStreamFlags i_streamFlags, - PaStreamCallback *i_streamCallback, - void *i_userData) { +static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, + PaStream **paStream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData) { PaError error = paNoError; - auto oboeHostApi = (PaOboeHostApiRepresentation *) i_hostApi; + auto oboeHostApi = (PaOboeHostApiRepresentation *) hostApi; unsigned long framesPerHostBuffer; /* these may not be equivalent for all implementations */ int inputChannelCount, outputChannelCount; PaSampleFormat inputSampleFormat, outputSampleFormat; @@ -1390,25 +1390,25 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, LOGI("[PaOboe - OpenStream]\t OpenStream called."); - if (i_inputParameters) { - inputChannelCount = i_inputParameters->channelCount; - inputSampleFormat = i_inputParameters->sampleFormat; + if (inputParameters) { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; /* Oboe supports alternate device specification with API>=28, but here we reject the use of paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via PaOboe_SetSelectedDevice. */ - if (i_inputParameters->device == paUseHostApiSpecificDeviceSpecification) + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that input device can support inputChannelCount */ - if (inputChannelCount > i_hostApi->deviceInfos[i_inputParameters->device]->maxInputChannels) + if (inputChannelCount > hostApi->deviceInfos[inputParameters->device]->maxInputChannels) return paInvalidChannelCount; /* validate inputStreamInfo */ - if (i_inputParameters->hostApiSpecificStreamInfo) { + if (inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. androidInputPreset = - ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; if (androidInputPreset != InputPreset::Generic && androidInputPreset != InputPreset::Camcorder && androidInputPreset != InputPreset::VoiceRecognition && @@ -1427,25 +1427,25 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->inputFormat = hostInputSampleFormat; } - if (i_outputParameters) { - outputChannelCount = i_outputParameters->channelCount; - outputSampleFormat = i_outputParameters->sampleFormat; + if (outputParameters) { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; /* Oboe supports alternate device specification with API>=28, but here we reject the use of paUseHostApiSpecificDeviceSpecification and stick with the default. Devices can be set via PaOboe_SetSelectedDevice. */ - if (i_outputParameters->device == paUseHostApiSpecificDeviceSpecification) + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) return paInvalidDevice; /* check that output device can support outputChannelCount */ if (outputChannelCount > - i_hostApi->deviceInfos[i_outputParameters->device]->maxOutputChannels) + hostApi->deviceInfos[outputParameters->device]->maxOutputChannels) return paInvalidChannelCount; /* validate outputStreamInfo */ - if (i_outputParameters->hostApiSpecificStreamInfo) { + if (outputParameters->hostApiSpecificStreamInfo) { androidOutputUsage = - ((PaOboeStreamInfo *) i_outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; if (androidOutputUsage != Usage::Media && androidOutputUsage != Usage::Notification && androidOutputUsage != Usage::NotificationEvent && @@ -1469,32 +1469,32 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, } /* validate platform specific flags */ - if ((i_streamFlags & paPlatformSpecificFlags) != 0) + if ((streamFlags & paPlatformSpecificFlags) != 0) return paInvalidFlag; /* unexpected platform specific flag */ - if (i_framesPerBuffer == paFramesPerBufferUnspecified) { - if (i_outputParameters) { + if (framesPerBuffer == paFramesPerBufferUnspecified) { + if (outputParameters) { framesPerHostBuffer = - (unsigned long) (i_outputParameters->suggestedLatency * i_sampleRate); + (unsigned long) (outputParameters->suggestedLatency * sampleRate); } else { framesPerHostBuffer = - (unsigned long) (i_inputParameters->suggestedLatency * i_sampleRate); + (unsigned long) (inputParameters->suggestedLatency * sampleRate); } } else { - framesPerHostBuffer = i_framesPerBuffer; + framesPerHostBuffer = framesPerBuffer; } - if (i_streamCallback) { + if (streamCallback) { PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), &oboeHostApi->callbackStreamInterface, - i_streamCallback, i_userData); + streamCallback, userData); } else { PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), &oboeHostApi->blockingStreamInterface, - i_streamCallback, i_userData); + streamCallback, userData); } - PaUtil_InitializeCpuLoadMeasurer(&(oboeStream->cpuLoadMeasurer), i_sampleRate); + PaUtil_InitializeCpuLoadMeasurer(&(oboeStream->cpuLoadMeasurer), sampleRate); error = PaUtil_InitializeBufferProcessor(&(oboeStream->bufferProcessor), inputChannelCount, @@ -1503,16 +1503,16 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - i_sampleRate, i_streamFlags, - i_framesPerBuffer, + sampleRate, streamFlags, + framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize, - i_streamCallback, i_userData); + streamCallback, userData); if (error != paNoError) goto error; - oboeStream->streamRepresentation.streamInfo.sampleRate = i_sampleRate; - oboeStream->isBlocking = (i_streamCallback == nullptr); + oboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; + oboeStream->isBlocking = (streamCallback == nullptr); oboeStream->framesPerHostCallback = framesPerHostBuffer; oboeStream->bytesPerFrame = sizeof(int16_t); oboeStream->cbFlags = 0; @@ -1527,9 +1527,9 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->streamRepresentation.streamInfo.inputLatency = ((PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( &(oboeStream->bufferProcessor)) + - oboeStream->framesPerHostCallback) / i_sampleRate; + oboeStream->framesPerHostCallback) / sampleRate; ENSURE(InitializeInputStream(oboeStream, oboeHostApi, - androidInputPreset, i_sampleRate), + androidInputPreset, sampleRate), "Initializing input stream failed") } else { oboeStream->hasInput = false; } @@ -1538,13 +1538,13 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, oboeStream->streamRepresentation.streamInfo.outputLatency = ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( &oboeStream->bufferProcessor) - + oboeStream->framesPerHostCallback) / i_sampleRate; + + oboeStream->framesPerHostCallback) / sampleRate; ENSURE(InitializeOutputStream(oboeStream, oboeHostApi, - androidOutputUsage, i_sampleRate), + androidOutputUsage, sampleRate), "Initializing output stream failed"); } else { oboeStream->hasOutput = false; } - *i_paStream = (PaStream *) oboeStream; + *paStream = (PaStream *) oboeStream; return error; error: @@ -1565,8 +1565,8 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *i_hostApi, * portaudio, which holds the information of our OboeStream. * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. */ -static PaError CloseStream(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError CloseStream(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (!(oboeEngine->closeStream(oboeStream))) @@ -1575,7 +1575,7 @@ static PaError CloseStream(PaStream *i_paStream) { PaUtil_TerminateBufferProcessor(&oboeStream->bufferProcessor); PaUtil_TerminateStreamRepresentation(&oboeStream->streamRepresentation); - for (int i = 0; i < g_numberOfBuffers; ++i) { + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { if (oboeStream->hasOutput) PaUtil_FreeMemory(oboeStream->outputBuffers[i]); if (oboeStream->hasInput) @@ -1599,8 +1599,8 @@ static PaError CloseStream(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::startStream fails. */ -static PaError StartStream(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError StartStream(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); PaUtil_ResetBufferProcessor(&oboeStream->bufferProcessor); @@ -1609,27 +1609,27 @@ static PaError StartStream(PaStream *i_paStream) { //TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) if (oboeStream->isActive) { LOGW("[PaOboe - StartStream]\t Stream was already active, stopping..."); - StopStream(i_paStream); + StopStream(paStream); LOGW("[PaOboe - StartStream]\t Restarting..."); - StartStream(i_paStream); + StartStream(paStream); } oboeStream->currentOutputBuffer = 0; oboeStream->currentInputBuffer = 0; /* Initialize buffers */ - for (int i = 0; i < g_numberOfBuffers; ++i) { + for (int i = 0; i < paOboe_numberOfBuffers; ++i) { if (oboeStream->hasOutput) { memset(oboeStream->outputBuffers[oboeStream->currentOutputBuffer], 0, oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.outputChannelCount); - oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; } if (oboeStream->hasInput) { memset(oboeStream->inputBuffers[oboeStream->currentInputBuffer], 0, oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * oboeStream->bufferProcessor.inputChannelCount); - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; } } @@ -1657,9 +1657,9 @@ static PaError StartStream(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::stopStream fails. */ -static PaError StopStream(PaStream *i_paStream) { +static PaError StopStream(PaStream *paStream) { PaError error = paNoError; - auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); if (oboeStream->isStopped) { @@ -1691,9 +1691,9 @@ static PaError StopStream(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::abortStream fails. */ -static PaError AbortStream(PaStream *i_paStream) { +static PaError AbortStream(PaStream *paStream) { PaError error = paNoError; - auto *oboeStream = (OboeStream *) i_paStream; + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); LOGI("[PaOboe - AbortStream]\t Aborting stream."); @@ -1725,22 +1725,22 @@ static PaError AbortStream(PaStream *i_paStream) { * @param frames is the total number of frames to read. * @return paInternalError if OboeEngine::readStream fails, paNoError otherwise. */ -static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_frames) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError ReadStream(PaStream *paStream, void *buffer, unsigned long frames) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); - void *userBuffer = i_buffer; + void *userBuffer = buffer; unsigned framesToRead; PaError error = paNoError; - while (i_frames > 0) { - framesToRead = PA_MIN(oboeStream->framesPerHostCallback, i_frames); + while (frames > 0) { + framesToRead = PA_MIN(oboeStream->framesPerHostCallback, frames); if (!(oboeEngine->readStream(oboeStream, userBuffer, framesToRead * oboeStream->bufferProcessor.inputChannelCount))) error = paInternalError; - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % g_numberOfBuffers; - i_frames -= framesToRead; + oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; + frames -= framesToRead; } return error; @@ -1755,22 +1755,22 @@ static PaError ReadStream(PaStream *i_paStream, void *i_buffer, unsigned long i_ * @param frames is the total number of frames to write. * @return paInternalError if OboeEngine::writeStream fails, paNoError otherwise. */ -static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned long i_frames) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError WriteStream(PaStream *paStream, const void *buffer, unsigned long frames) { + auto *oboeStream = (OboeStream *) paStream; auto *oboeEngine = oboeStream->oboeMediator->getEngine(); - const void *userBuffer = i_buffer; + const void *userBuffer = buffer; unsigned framesToWrite; PaError error = paNoError; - while (i_frames > 0) { - framesToWrite = PA_MIN(oboeStream->framesPerHostCallback, i_frames); + while (frames > 0) { + framesToWrite = PA_MIN(oboeStream->framesPerHostCallback, frames); if (!(oboeEngine->writeStream(oboeStream, userBuffer, framesToWrite * oboeStream->bufferProcessor.outputChannelCount))) error = paInternalError; - oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % g_numberOfBuffers; - i_frames -= framesToWrite; + oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; + frames -= framesToWrite; } return error; @@ -1785,9 +1785,9 @@ static PaError WriteStream(PaStream *i_paStream, const void *i_buffer, unsigned * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be read without waiting. */ -static signed long GetStreamReadAvailable(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; - return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentInputBuffer); +static signed long GetStreamReadAvailable(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; + return oboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - oboeStream->currentInputBuffer); } @@ -1797,9 +1797,9 @@ static signed long GetStreamReadAvailable(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return the minimum number of frames that can be written without waiting. */ -static signed long GetStreamWriteAvailable(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; - return oboeStream->framesPerHostCallback * (g_numberOfBuffers - oboeStream->currentOutputBuffer); +static signed long GetStreamWriteAvailable(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; + return oboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - oboeStream->currentOutputBuffer); } @@ -1809,8 +1809,8 @@ static signed long GetStreamWriteAvailable(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is stopped, or zero (0) when the stream is running. */ -static PaError IsStreamStopped(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError IsStreamStopped(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; return oboeStream->isStopped; } @@ -1821,8 +1821,8 @@ static PaError IsStreamStopped(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return one (1) when the stream is active (ie playing or recording audio), or zero (0) otherwise. */ -static PaError IsStreamActive(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static PaError IsStreamActive(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; return oboeStream->isActive; } @@ -1833,7 +1833,7 @@ static PaError IsStreamActive(PaStream *i_paStream) { * portaudio, which holds the information of our OboeStream. * @return The stream's current time in seconds, or 0 if an error occurred. */ -static PaTime GetStreamTime(PaStream *i_paStream) { +static PaTime GetStreamTime(PaStream *paStream) { return PaUtil_GetTime(); } @@ -1849,8 +1849,8 @@ static PaTime GetStreamTime(PaStream *i_paStream) { * A value of 0.0 will always be returned for a blocking read/write stream, or if an error * occurs. */ -static double GetStreamCpuLoad(PaStream *i_paStream) { - auto *oboeStream = (OboeStream *) i_paStream; +static double GetStreamCpuLoad(PaStream *paStream) { + auto *oboeStream = (OboeStream *) paStream; return PaUtil_GetCpuLoad(&oboeStream->cpuLoadMeasurer); } @@ -1872,29 +1872,29 @@ static unsigned long GetApproximateLowBufferSize() { /*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ -void PaOboe_SetSelectedDevice(Direction i_direction, int32_t i_deviceID) { +void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); - if (i_direction == Direction::Input) - g_inputDeviceId = i_deviceID; + if (direction == Direction::Input) + paOboe_inputDeviceId = deviceID; else - g_outputDeviceId = i_deviceID; + paOboe_outputDeviceId = deviceID; } -void PaOboe_SetPerformanceMode(oboe::Direction i_direction, oboe::PerformanceMode i_performanceMode) { - if (i_direction == Direction::Input) { - g_inputPerfMode = i_performanceMode; +void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode) { + if (direction == Direction::Input) { + paOboe_inputPerfMode = performanceMode; } else { - g_outputPerfMode = i_performanceMode; + paOboe_outputPerfMode = performanceMode; } } -void PaOboe_SetNativeBufferSize(unsigned long i_bufferSize) { - g_nativeBufferSize = i_bufferSize; +void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { + paOboe_nativeBufferSize = bufferSize; } -void PaOboe_SetNumberOfBuffers(unsigned i_numberOfBuffers) { - g_numberOfBuffers = i_numberOfBuffers; +void PaOboe_SetNumberOfBuffers(unsigned numberOfBuffers) { + paOboe_numberOfBuffers = numberOfBuffers; } From f38179dd67faf7b61a8b261103a69fa4fdbe2ee0 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 11:02:18 +0200 Subject: [PATCH 109/125] replaced 'm_' prefix with 'm' prefix --- src/hostapi/oboe/pa_oboe.cpp | 215 +++++++++++++++++------------------ 1 file changed, 106 insertions(+), 109 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index ca1571a59..50e43f6dd 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -206,7 +206,7 @@ typedef struct OboeStream { class OboeMediator: public AudioStreamCallback{ public: OboeMediator(OboeStream* oboeStream) { - m_oboeCallbackStream = oboeStream; + mOboeCallbackStream = oboeStream; } //Callback function for non-blocking streams @@ -215,30 +215,30 @@ class OboeMediator: public AudioStreamCallback{ //Callback utils void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; void resetCallbackCounters(); - void setOutputCallback() { m_outputBuilder.setDataCallback(this)->setErrorCallback(this); } - void setInputCallback() { m_inputBuilder.setDataCallback(this)->setErrorCallback(this); } + void setOutputCallback() { mOutputBuilder.setDataCallback(this)->setErrorCallback(this); } + void setInputCallback() { mInputBuilder.setDataCallback(this)->setErrorCallback(this); } - //getter and setter of m_oboeEngine and m_oboeCallbackStream - OboeEngine *getEngine() { return m_oboeEngine; } - void setEngine(OboeEngine *oboeEngine) { m_oboeEngine = oboeEngine; } + //getter and setter of mOboeEngine and mOboeCallbackStream + OboeEngine *getEngine() { return mOboeEngine; } + void setEngine(OboeEngine *oboeEngine) { mOboeEngine = oboeEngine; } - OboeStream *getStreamAddress() { return m_oboeCallbackStream; } - void setCallbackStream(OboeStream *oboeStream) { m_oboeCallbackStream = oboeStream; } + OboeStream *getStreamAddress() { return mOboeCallbackStream; } + void setCallbackStream(OboeStream *oboeStream) { mOboeCallbackStream = oboeStream; } //The only instances of output and input streams that will be used, and their builders - std::shared_ptr m_outputStream; - AudioStreamBuilder m_outputBuilder; - std::shared_ptr m_inputStream; - AudioStreamBuilder m_inputBuilder; + std::shared_ptr mOutputStream; + AudioStreamBuilder mOutputBuilder; + std::shared_ptr mInputStream; + AudioStreamBuilder mInputBuilder; private: - OboeEngine *m_oboeEngine; + OboeEngine *mOboeEngine; //callback utils - OboeStream *m_oboeCallbackStream; - unsigned long m_framesProcessed{}; - PaStreamCallbackTimeInfo m_timeInfo{}; - struct timespec m_timeSpec{}; + OboeStream *mOboeCallbackStream; + unsigned long mFramesProcessed{}; + PaStreamCallbackTimeInfo mTimeInfo{}; + struct timespec mTimeSpec{}; }; @@ -276,10 +276,10 @@ class OboeEngine { void constructOboeStream(OboeStream* oboeStream); private: - std::shared_ptr m_testStream; - AudioStreamBuilder m_testBuilder; + std::shared_ptr mTestStream; + AudioStreamBuilder mTestBuilder; - OboeMediator* m_terminableMediator; + OboeMediator* mTerminableMediator; //Conversion utils static AudioFormat PaToOboeFormat(PaSampleFormat paFormat); @@ -327,13 +327,13 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan Result result; bool outcome = false; - m_testBuilder.setDeviceId(getSelectedDevice(direction)) + mTestBuilder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. ->setFormat(AudioFormat::Float) ->setDirection(direction) ->setSampleRate(sampleRate) ->setChannelCount(channelCount) - ->openStream(m_testStream); + ->openStream(mTestStream); if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", @@ -342,23 +342,23 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan } if (sampleRate != kUnspecified) { - outcome = (sampleRate == m_testBuilder.getSampleRate()); + outcome = (sampleRate == mTestBuilder.getSampleRate()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - sampleRate, m_testBuilder.getSampleRate()); + sampleRate, mTestBuilder.getSampleRate()); } } else if (channelCount != kUnspecified) { - outcome = (channelCount == m_testBuilder.getChannelCount()); + outcome = (channelCount == mTestBuilder.getChannelCount()); if (!outcome) { LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, m_testBuilder.getChannelCount()); + channelCount, mTestBuilder.getChannelCount()); } } else { LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); outcome = false; } - m_testStream->close(); + mTestStream->close(); return outcome; } @@ -397,7 +397,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 } if (direction == Direction::Input) { - mediator->m_inputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) + mediator->mInputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) ->setSampleRate(sampleRate) ->setDirection(Direction::Input) @@ -410,7 +410,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 mediator->setInputCallback(); } - result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); + result = mediator->mInputBuilder.openStream(mediator->mInputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the input stream: %s", @@ -418,7 +418,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 return paUnanticipatedHostError; } - mediator->m_inputStream->setBufferSizeInFrames(mediator->m_inputStream->getFramesPerBurst() * + mediator->minputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * paOboe_numberOfBuffers); oboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); @@ -433,14 +433,14 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 for (int j = 0; j < i; ++j) PaUtil_FreeMemory(oboeStream->inputBuffers[j]); PaUtil_FreeMemory(oboeStream->inputBuffers); - mediator->m_inputStream->close(); + mediator->mInputStream->close(); error = paInsufficientMemory; break; } } oboeStream->currentInputBuffer = 0; } else { - mediator->m_outputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) + mediator->mOutputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) ->setSampleRate(sampleRate) ->setDirection(Direction::Output) @@ -453,14 +453,14 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 mediator->setOutputCallback(); } - result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); + result = mediator->mOutputBuilder.openStream(mediator->mOutputStream); if (result != Result::OK) { LOGE("[OboeEngine::OpenStream]\t Oboe couldn't open the output stream: %s", convertToText(result)); return paUnanticipatedHostError; } - mediator->m_outputStream->setBufferSizeInFrames(mediator->m_outputStream->getFramesPerBurst() * + mediator->mOutputStream->setBufferSizeInFrames(mediator->mOutputStream->getFramesPerBurst() * paOboe_numberOfBuffers); oboeStream->outputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); @@ -475,7 +475,7 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 for (int j = 0; j < i; ++j) PaUtil_FreeMemory(oboeStream->outputBuffers[j]); PaUtil_FreeMemory(oboeStream->outputBuffers); - mediator->m_outputStream->close(); + mediator->mOutputStream->close(); error = paInsufficientMemory; break; } @@ -498,13 +498,13 @@ bool OboeEngine::startStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->requestStart(); + inputResult = mediator->mInputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->requestStart(); + outputResult = mediator->mOutputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", convertToText(outputResult)); @@ -525,13 +525,13 @@ bool OboeEngine::stopStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->requestStop(); + inputResult = mediator->mInputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->requestStop(); + outputResult = mediator->mOutputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", convertToText(outputResult)); @@ -556,11 +556,11 @@ bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { switch (direction) { case 1: //output-only - result = mediator->m_outputBuilder.openStream(mediator->m_outputStream); + result = mediator->mOutputBuilder.openStream(mediator->mOutputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the output stream: %s", convertToText(result)); - result = mediator->m_outputStream->start(); + result = mediator->mOutputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the output stream: %s", convertToText(result)); @@ -569,11 +569,11 @@ bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { break; case 2: //input-only - result = mediator->m_inputBuilder.openStream(mediator->m_inputStream); + result = mediator->mInputBuilder.openStream(mediator->mInputStream); if (result != Result::OK) LOGE("[OboeEngine::restartStream]\t Oboe couldn't reopen the input stream: %s", convertToText(result)); - result = mediator->m_inputStream->start(); + result = mediator->mInputStream->start(); if (result != Result::OK) { LOGE("[OboeEngine::restartStream]\t Oboe couldn't restart the input stream: %s", convertToText(result)); @@ -609,14 +609,14 @@ bool OboeEngine::closeStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->close(); + outputResult = mediator->mOutputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->close(); + inputResult = mediator->mInputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close input stream, but was already closed."); @@ -643,21 +643,21 @@ bool OboeEngine::abortStream(OboeStream *oboeStream) { OboeMediator* mediator = oboeStream->oboeMediator; if (oboeStream->hasInput) { - inputResult = mediator->m_inputStream->stop(); + inputResult = mediator->mInputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", convertToText(inputResult)); - inputResult = mediator->m_inputStream->close(); + inputResult = mediator->mInputStream->close(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } if (oboeStream->hasOutput) { - outputResult = mediator->m_outputStream->stop(); + outputResult = mediator->mOutputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", convertToText(outputResult)); - outputResult = mediator->m_outputStream->close(); + outputResult = mediator->mOutputStream->close(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to close: %s", convertToText(outputResult)); @@ -680,7 +680,7 @@ bool OboeEngine::writeStream(OboeStream *oboeStream, const void buffer, int32_t bool outcome = true; OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_outputStream->write(buffer, framesToWrite, TIMEOUT_NS); + ResultWithValue result = mediator->mOutputStream->write(buffer, framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -709,7 +709,7 @@ bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t frames bool outcome = true; OboeMediator* mediator = oboeStream->oboeMediator; - ResultWithValue result = mediator->m_inputStream->read(buffer, framesToRead, TIMEOUT_NS); + ResultWithValue result = mediator->mInputStream->read(buffer, framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { @@ -730,7 +730,7 @@ bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t frames * @return the address of the oboeStream. */ void OboeEngine::constructOboeStream(OboeStream* oboeStream) { - m_terminableMediator = oboeStream->oboeMediator = new OboeMediator(oboeStream); + mTerminableMediator = oboeStream->oboeMediator = new OboeMediator(oboeStream); oboeStream->oboeMediator->setEngine(this); } @@ -788,85 +788,85 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { DataCallbackResult OboeMediator::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { - clock_gettime(CLOCK_REALTIME, &m_timeSpec); - m_timeInfo.currentTime = (PaTime)(m_timeSpec.tv_sec + (m_timeSpec.tv_nsec / 1000000000.0)); - m_timeInfo.outputBufferDacTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback + clock_gettime(CLOCK_REALTIME, &mTimeSpec); + mTimeInfo.currentTime = (PaTime)(mTimeSpec.tv_sec + (mTimeSpec.tv_nsec / 1000000000.0)); + mTimeInfo.outputBufferDacTime = (PaTime)(mOboeCallbackStream->framesPerHostCallback / - m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); - m_timeInfo.inputBufferAdcTime = (PaTime)(m_oboeCallbackStream->framesPerHostCallback + mOboeCallbackStream->streamRepresentation.streamInfo.sampleRate + + mTimeInfo.currentTime); + mTimeInfo.inputBufferAdcTime = (PaTime)(mOboeCallbackStream->framesPerHostCallback / - m_oboeCallbackStream->streamRepresentation.streamInfo.sampleRate - + m_timeInfo.currentTime); + mOboeCallbackStream->streamRepresentation.streamInfo.sampleRate + + mTimeInfo.currentTime); /* check if StopStream or AbortStream was called */ - if (m_oboeCallbackStream->doStop) { - m_oboeCallbackStream->callbackResult = paComplete; - } else if (m_oboeCallbackStream->doAbort) { - m_oboeCallbackStream->callbackResult = paAbort; + if (mOboeCallbackStream->doStop) { + mOboeCallbackStream->callbackResult = paComplete; + } else if (mOboeCallbackStream->doAbort) { + mOboeCallbackStream->callbackResult = paAbort; } - PaUtil_BeginCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer); - PaUtil_BeginBufferProcessing(&m_oboeCallbackStream->bufferProcessor, - &m_timeInfo, m_oboeCallbackStream->cbFlags); + PaUtil_BeginCpuLoadMeasurement(&mOboeCallbackStream->cpuLoadMeasurer); + PaUtil_BeginBufferProcessing(&mOboeCallbackStream->bufferProcessor, + &mTimeInfo, mOboeCallbackStream->cbFlags); - if (m_oboeCallbackStream->hasOutput) { - m_oboeCallbackStream->outputBuffers[m_oboeCallbackStream->currentOutputBuffer] = audioData; - PaUtil_SetOutputFrameCount(&m_oboeCallbackStream->bufferProcessor, numFrames); - PaUtil_SetInterleavedOutputChannels(&m_oboeCallbackStream->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeCallbackStream->outputBuffers)[ - m_oboeCallbackStream->currentOutputBuffer], + if (mOboeCallbackStream->hasOutput) { + mOboeCallbackStream->outputBuffers[mOboeCallbackStream->currentOutputBuffer] = audioData; + PaUtil_SetOutputFrameCount(&mOboeCallbackStream->bufferProcessor, numFrames); + PaUtil_SetInterleavedOutputChannels(&mOboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) mOboeCallbackStream->outputBuffers)[ + mOboeCallbackStream->currentOutputBuffer], 0); } - if (m_oboeCallbackStream->hasInput) { - audioData = m_oboeCallbackStream->inputBuffers[m_oboeCallbackStream->currentInputBuffer]; - PaUtil_SetInputFrameCount(&m_oboeCallbackStream->bufferProcessor, 0); - PaUtil_SetInterleavedInputChannels(&m_oboeCallbackStream->bufferProcessor, 0, - (void *) ((PaInt16 **) m_oboeCallbackStream->inputBuffers)[ - m_oboeCallbackStream->currentInputBuffer], + if (mOboeCallbackStream->hasInput) { + audioData = mOboeCallbackStream->inputBuffers[mOboeCallbackStream->currentInputBuffer]; + PaUtil_SetInputFrameCount(&mOboeCallbackStream->bufferProcessor, 0); + PaUtil_SetInterleavedInputChannels(&mOboeCallbackStream->bufferProcessor, 0, + (void *) ((PaInt16 **) mOboeCallbackStream->inputBuffers)[ + mOboeCallbackStream->currentInputBuffer], 0); } /* continue processing user buffers if callback result is paContinue or * if it is paComplete and userBuffers aren't empty yet */ - if (m_oboeCallbackStream->callbackResult == paContinue - || (m_oboeCallbackStream->callbackResult == paComplete - && !PaUtil_IsBufferProcessorOutputEmpty(&m_oboeCallbackStream->bufferProcessor))) { - m_framesProcessed = PaUtil_EndBufferProcessing(&m_oboeCallbackStream->bufferProcessor, - &m_oboeCallbackStream->callbackResult); + if (mOboeCallbackStream->callbackResult == paContinue + || (mOboeCallbackStream->callbackResult == paComplete + && !PaUtil_IsBufferProcessorOutputEmpty(&mOboeCallbackStream->bufferProcessor))) { + mFramesProcessed = PaUtil_EndBufferProcessing(&mOboeCallbackStream->bufferProcessor, + &mOboeCallbackStream->callbackResult); } /* enqueue a buffer only when there are frames to be processed, * this will be 0 when paComplete + empty buffers or paAbort */ - if (m_framesProcessed > 0) { - if (m_oboeCallbackStream->hasOutput) { - m_oboeCallbackStream->currentOutputBuffer = - (m_oboeCallbackStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; + if (mFramesProcessed > 0) { + if (mOboeCallbackStream->hasOutput) { + mOboeCallbackStream->currentOutputBuffer = + (mOboeCallbackStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; } - if (m_oboeCallbackStream->hasInput) { - m_oboeCallbackStream->currentInputBuffer = (m_oboeCallbackStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; + if (mOboeCallbackStream->hasInput) { + mOboeCallbackStream->currentInputBuffer = (mOboeCallbackStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; } } - PaUtil_EndCpuLoadMeasurement(&m_oboeCallbackStream->cpuLoadMeasurer, m_framesProcessed); + PaUtil_EndCpuLoadMeasurement(&mOboeCallbackStream->cpuLoadMeasurer, mFramesProcessed); /* StopStream was called */ - if (m_framesProcessed == 0 && m_oboeCallbackStream->doStop) { - m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; + if (mFramesProcessed == 0 && mOboeCallbackStream->doStop) { + mOboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; } /* if AbortStream or StopStream weren't called, stop from the cb */ - else if (m_framesProcessed == 0 && !(m_oboeCallbackStream->doAbort || m_oboeCallbackStream->doStop)) { - m_oboeCallbackStream->isActive = false; - m_oboeCallbackStream->isStopped = true; - if (m_oboeCallbackStream->streamRepresentation.streamFinishedCallback != nullptr) - m_oboeCallbackStream->streamRepresentation.streamFinishedCallback( - m_oboeCallbackStream->streamRepresentation.userData); - m_oboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) + else if (mFramesProcessed == 0 && !(mOboeCallbackStream->doAbort || mOboeCallbackStream->doStop)) { + mOboeCallbackStream->isActive = false; + mOboeCallbackStream->isStopped = true; + if (mOboeCallbackStream->streamRepresentation.streamFinishedCallback != nullptr) + mOboeCallbackStream->streamRepresentation.streamFinishedCallback( + mOboeCallbackStream->streamRepresentation.userData); + mOboeCallbackStream->oboeCallbackResult = DataCallbackResult::Stop; //TODO: Resume this test (onAudioReady) } - return m_oboeCallbackStream->oboeCallbackResult; + return mOboeCallbackStream->oboeCallbackResult; } @@ -879,11 +879,11 @@ void OboeMediator::onErrorAfterClose(AudioStream *audioStream, Result error) { OboeEngine* oboeEngine = getEngine(); LOGW("[OboeMediator::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); int i = 0; - if(m_oboeCallbackStream->hasOutput) + if(mOboeCallbackStream->hasOutput) i++; - if(m_oboeCallbackStream->hasInput) + if(mOboeCallbackStream->hasInput) i+=2; - if (!oboeEngine->restartStream(m_oboeCallbackStream, i)) + if (!oboeEngine->restartStream(mOboeCallbackStream, i)) LOGE("[OboeMediator::onErrorAfterClose]\t Couldn't restart stream(s)"); } else LOGE("[OboeMediator::onErrorAfterClose]\t Error was %s", oboe::convertToText(error)); @@ -894,8 +894,8 @@ void OboeMediator::onErrorAfterClose(AudioStream *audioStream, Result error) { * \brief Resets callback counters (called at the start of each iteration of onAudioReady). */ void OboeMediator::resetCallbackCounters() { - m_framesProcessed = 0; - m_timeInfo = {0, 0, 0}; + mFramesProcessed = 0; + mTimeInfo = {0, 0, 0}; } @@ -1880,7 +1880,6 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { paOboe_outputDeviceId = deviceID; } - void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode) { if (direction == Direction::Input) { paOboe_inputPerfMode = performanceMode; @@ -1889,12 +1888,10 @@ void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode } } - void PaOboe_SetNativeBufferSize(unsigned long bufferSize) { paOboe_nativeBufferSize = bufferSize; } - void PaOboe_SetNumberOfBuffers(unsigned numberOfBuffers) { paOboe_numberOfBuffers = numberOfBuffers; } From c21247402b0810d5d6c6c60e89b22772f69cb2dd Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 11:25:52 +0200 Subject: [PATCH 110/125] fixed OboeEngine::tryStream as requested --- src/hostapi/oboe/pa_oboe.cpp | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 50e43f6dd..5b0fd683a 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -276,9 +276,6 @@ class OboeEngine { void constructOboeStream(OboeStream* oboeStream); private: - std::shared_ptr mTestStream; - AudioStreamBuilder mTestBuilder; - OboeMediator* mTerminableMediator; //Conversion utils @@ -314,26 +311,28 @@ OboeEngine::OboeEngine() {} /** * \brief Tries to open a stream with the direction direction, sample rate sampleRate and/or - * channel count channelCount. It then checks if the stream was in fact opened with the - * desired settings, and then closes the stream. It's used to see if the requested + * channel count channelCount. It then closes the stream. It's used to see if the requested * parameters are supported by the devices that are going to be used. * @param direction the Direction of the stream; * @param sampleRate the sample rate we want to try; * @param channelCount the channel count we want to try; - * @return true if the requested sample rate / channel count is supported by the device, false if - * they aren't, or if tryStream couldn't open a stream. + * @return true if the stream was opened with Result::OK, false otherwise. */ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { Result result; bool outcome = false; - mTestBuilder.setDeviceId(getSelectedDevice(direction)) + std::shared_ptr stream; + AudioStreamBuilder builder; + + builder.setDeviceId(getSelectedDevice(direction)) // Arbitrary format usually broadly supported. Later, we'll open streams with correct formats. + // FIXME: if needed, modify this format to whatever you require ->setFormat(AudioFormat::Float) ->setDirection(direction) ->setSampleRate(sampleRate) ->setChannelCount(channelCount) - ->openStream(mTestStream); + ->openStream(stream); if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", @@ -341,24 +340,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan return outcome; } - if (sampleRate != kUnspecified) { - outcome = (sampleRate == mTestBuilder.getSampleRate()); - if (!outcome) { - LOGW("[OboeEngine::TryStream]\t Tried sampleRate = %d, built sampleRate = %d", - sampleRate, mTestBuilder.getSampleRate()); - } - } else if (channelCount != kUnspecified) { - outcome = (channelCount == mTestBuilder.getChannelCount()); - if (!outcome) { - LOGW("[OboeEngine::TryStream]\t Tried channelCount = %d, built channelCount = %d", - channelCount, mTestBuilder.getChannelCount()); - } - } else { - LOGE("[OboeEngine::TryStream]\t Logical failure. This message should NEVER occur."); - outcome = false; - } - - mTestStream->close(); + stream->close(); return outcome; } From decac0d4c68e4ddbb9261bb9f1656808c72d3d6c Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 12:52:51 +0200 Subject: [PATCH 111/125] Changed names to improve readability, i.e. OboeStream -> PaOboeStream --- src/hostapi/oboe/pa_oboe.cpp | 502 +++++++++++++++++------------------ 1 file changed, 251 insertions(+), 251 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 5b0fd683a..79a9b35e1 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -161,8 +161,8 @@ using namespace oboe; int32_t paOboe_inputDeviceId = kUnspecified; int32_t paOboe_outputDeviceId = kUnspecified; -PerformanceMode paOboe_inputPerfMode = PerformanceMode::LowLatency; -PerformanceMode paOboe_outputPerfMode = PerformanceMode::LowLatency; +PerformanceMode paOboe_inputPerformanceMode = PerformanceMode::LowLatency; +PerformanceMode paOboe_outputPerformanceMode = PerformanceMode::LowLatency; class OboeEngine; class OboeMediator; @@ -170,7 +170,7 @@ class OboeMediator; /** * Stream structure, useful to store relevant information. It's needed by Portaudio. */ -typedef struct OboeStream { +typedef struct PaOboeStream { PaUtilStreamRepresentation streamRepresentation; PaUtilCpuLoadMeasurer cpuLoadMeasurer; PaUtilBufferProcessor bufferProcessor; @@ -200,13 +200,13 @@ typedef struct OboeStream { unsigned bytesPerFrame; OboeMediator* oboeMediator; -} OboeStream; +} PaOboeStream; class OboeMediator: public AudioStreamCallback{ public: - OboeMediator(OboeStream* oboeStream) { - mOboeCallbackStream = oboeStream; + OboeMediator(PaOboeStream* paOboeStream) { + mOboeCallbackStream = paOboeStream; } //Callback function for non-blocking streams @@ -222,8 +222,8 @@ class OboeMediator: public AudioStreamCallback{ OboeEngine *getEngine() { return mOboeEngine; } void setEngine(OboeEngine *oboeEngine) { mOboeEngine = oboeEngine; } - OboeStream *getStreamAddress() { return mOboeCallbackStream; } - void setCallbackStream(OboeStream *oboeStream) { mOboeCallbackStream = oboeStream; } + PaOboeStream *getStreamAddress() { return mOboeCallbackStream; } + void setCallbackStream(PaOboeStream *paOboeStream) { mOboeCallbackStream = paOboeStream; } //The only instances of output and input streams that will be used, and their builders std::shared_ptr mOutputStream; @@ -235,7 +235,7 @@ class OboeMediator: public AudioStreamCallback{ OboeEngine *mOboeEngine; //callback utils - OboeStream *mOboeCallbackStream; + PaOboeStream *mOboeCallbackStream; unsigned long mFramesProcessed{}; PaStreamCallbackTimeInfo mTimeInfo{}; struct timespec mTimeSpec{}; @@ -243,7 +243,7 @@ class OboeMediator: public AudioStreamCallback{ /** - * Stream engine of the host API - Oboe. We allocate only one instance of the engine per OboeStream, and + * Stream engine of the host API - Oboe. We allocate only one instance of the engine per PaOboeStream, and * we call its functions when we want to operate directly on Oboe. More information on each function is * provided right before its implementation. */ @@ -254,26 +254,26 @@ class OboeEngine { //Stream-managing functions bool tryStream(Direction direction, int32_t sampleRate, int32_t channelCount); - PaError openStream(OboeStream *oboeStream, Direction direction, int32_t sampleRate, + PaError openStream(PaOboeStream *paOboeStream, Direction direction, int32_t sampleRate, Usage outputUsage, InputPreset inputPreset); - bool startStream(OboeStream *oboeStream); + bool startStream(PaOboeStream *paOboeStream); - bool stopStream(OboeStream *oboeStream); + bool stopStream(PaOboeStream *paOboeStream); - bool restartStream(OboeStream *oboeStream, int direction); + bool restartStream(PaOboeStream *paOboeStream, int direction); - bool closeStream(OboeStream *oboeStream); + bool closeStream(PaOboeStream *paOboeStream); - bool abortStream(OboeStream *oboeStream); + bool abortStream(PaOboeStream *paOboeStream); //Blocking read/write functions - bool writeStream(OboeStream *oboeStream, const void *buffer, int32_t framesToWrite); + bool writeStream(PaOboeStream *paOboeStream, const void *buffer, int32_t framesToWrite); - bool readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead); + bool readStream(PaOboeStream *paOboeStream, void *buffer, int32_t framesToRead); //Engine utils - void constructOboeStream(OboeStream* oboeStream); + void constructPaOboeStream(PaOboeStream* paOboeStream); private: OboeMediator* mTerminableMediator; @@ -352,7 +352,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan * direction == Direction::Output) or its preset (if direction == Direction::Input). * Moreover, this function checks if the stream is blocking, and sets its callback * function if not. - * @param oboeStream The stream we want to open + * @param paOboeStream The stream we want to open * @param direction The Oboe::Direction of the stream we want to open; * @param sampleRate The sample rate of the stream we want to open; * @param androidOutputUsage The Oboe::Usage of the output stream we want to open @@ -362,33 +362,33 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan * @return paNoError if everything goes as expected, paUnanticipatedHostError if Oboe fails to open * a stream, and paInsufficientMemory if the memory allocation of the buffers fails. */ -PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int32_t sampleRate, +PaError OboeEngine::openStream(PaOboeStream *paOboeStream, Direction direction, int32_t sampleRate, Usage androidOutputUsage, InputPreset androidInputPreset) { PaError error = paNoError; Result result; - if (oboeStream == nullptr) { - LOGE("[OboeEngine::openStream]\t oboeStream is a nullptr."); + if (paOboeStream == nullptr) { + LOGE("[OboeEngine::openStream]\t paOboeStream is a nullptr."); return paInternalError; } - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; - if(!(oboeStream->isBlocking)){ + if(!(paOboeStream->isBlocking)){ mediator->resetCallbackCounters(); } if (direction == Direction::Input) { - mediator->mInputBuilder.setChannelCount(oboeStream->bufferProcessor.inputChannelCount) - ->setFormat(PaToOboeFormat(oboeStream->inputFormat)) + mediator->mInputBuilder.setChannelCount(paOboeStream->bufferProcessor.inputChannelCount) + ->setFormat(PaToOboeFormat(paOboeStream->inputFormat)) ->setSampleRate(sampleRate) ->setDirection(Direction::Input) ->setDeviceId(getSelectedDevice(Direction::Input)) - ->setPerformanceMode(paOboe_inputPerfMode) + ->setPerformanceMode(paOboe_inputPerformanceMode) ->setInputPreset(androidInputPreset) - ->setFramesPerCallback(oboeStream->framesPerHostCallback); + ->setFramesPerCallback(paOboeStream->framesPerHostCallback); - if (!(oboeStream->isBlocking)) { + if (!(paOboeStream->isBlocking)) { mediator->setInputCallback(); } @@ -402,36 +402,36 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 mediator->minputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * paOboe_numberOfBuffers); - oboeStream->inputBuffers = + paOboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); for (int i = 0; i < paOboe_numberOfBuffers; ++i) { - oboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - oboeStream->framesPerHostCallback * - oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.inputChannelCount); + paOboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + paOboeStream->framesPerHostCallback * + paOboeStream->bytesPerFrame * + paOboeStream->bufferProcessor.inputChannelCount); - if (!oboeStream->inputBuffers[i]) { + if (!paOboeStream->inputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(oboeStream->inputBuffers[j]); - PaUtil_FreeMemory(oboeStream->inputBuffers); + PaUtil_FreeMemory(paOboeStream->inputBuffers[j]); + PaUtil_FreeMemory(paOboeStream->inputBuffers); mediator->mInputStream->close(); error = paInsufficientMemory; break; } } - oboeStream->currentInputBuffer = 0; + paOboeStream->currentInputBuffer = 0; } else { - mediator->mOutputBuilder.setChannelCount(oboeStream->bufferProcessor.outputChannelCount) - ->setFormat(PaToOboeFormat(oboeStream->outputFormat)) + mediator->mOutputBuilder.setChannelCount(paOboeStream->bufferProcessor.outputChannelCount) + ->setFormat(PaToOboeFormat(paOboeStream->outputFormat)) ->setSampleRate(sampleRate) ->setDirection(Direction::Output) ->setDeviceId(getSelectedDevice(Direction::Output)) - ->setPerformanceMode(paOboe_outputPerfMode) + ->setPerformanceMode(paOboe_outputPerformanceMode) ->setUsage(androidOutputUsage) - ->setFramesPerCallback(oboeStream->framesPerHostCallback); + ->setFramesPerCallback(paOboeStream->framesPerHostCallback); - if (!(oboeStream->isBlocking)) { + if (!(paOboeStream->isBlocking)) { mediator->setOutputCallback(); } @@ -444,25 +444,25 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 mediator->mOutputStream->setBufferSizeInFrames(mediator->mOutputStream->getFramesPerBurst() * paOboe_numberOfBuffers); - oboeStream->outputBuffers = + paOboeStream->outputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); for (int i = 0; i < paOboe_numberOfBuffers; ++i) { - oboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( - oboeStream->framesPerHostCallback * - oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.outputChannelCount); + paOboeStream->outputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( + paOboeStream->framesPerHostCallback * + paOboeStream->bytesPerFrame * + paOboeStream->bufferProcessor.outputChannelCount); - if (!oboeStream->outputBuffers[i]) { + if (!paOboeStream->outputBuffers[i]) { for (int j = 0; j < i; ++j) - PaUtil_FreeMemory(oboeStream->outputBuffers[j]); - PaUtil_FreeMemory(oboeStream->outputBuffers); + PaUtil_FreeMemory(paOboeStream->outputBuffers[j]); + PaUtil_FreeMemory(paOboeStream->outputBuffers); mediator->mOutputStream->close(); error = paInsufficientMemory; break; } } - oboeStream->currentOutputBuffer = 0; + paOboeStream->currentOutputBuffer = 0; } return error; @@ -470,22 +470,22 @@ PaError OboeEngine::openStream(OboeStream *oboeStream, Direction direction, int3 /** - * \brief Starts oboeStream - both input and output AudioStreams of the OboeStream are checked + * \brief Starts paOboeStream - both input and output AudioStreams of the paOboeStream are checked * and requested to be started. - * @param oboeStream The stream we want to start. + * @param paOboeStream The stream we want to start. * @return true if the streams we wanted to start are started successfully, false otherwise. */ -bool OboeEngine::startStream(OboeStream *oboeStream) { +bool OboeEngine::startStream(PaOboeStream *paOboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; - if (oboeStream->hasInput) { + if (paOboeStream->hasInput) { inputResult = mediator->mInputStream->requestStart(); if (inputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the input stream: %s", convertToText(inputResult)); } - if (oboeStream->hasOutput) { + if (paOboeStream->hasOutput) { outputResult = mediator->mOutputStream->requestStart(); if (outputResult != Result::OK) LOGE("[OboeEngine::startStream]\t Oboe couldn't start the output stream: %s", @@ -497,22 +497,22 @@ bool OboeEngine::startStream(OboeStream *oboeStream) { /** - * \brief Stops oboeStream - both input and output AudioStreams of the OboeStream are checked + * \brief Stops paOboeStream - both input and output AudioStreams of the PaOboeStream are checked * and requested to be stopped. - * @param oboeStream The stream we want to stop. + * @param paOboeStream The stream we want to stop. * @return true if the streams we wanted to stop are stopped successfully, false otherwise. */ -bool OboeEngine::stopStream(OboeStream *oboeStream) { +bool OboeEngine::stopStream(PaOboeStream *paOboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; - if (oboeStream->hasInput) { + if (paOboeStream->hasInput) { inputResult = mediator->mInputStream->requestStop(); if (inputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the input stream: %s", convertToText(inputResult)); } - if (oboeStream->hasOutput) { + if (paOboeStream->hasOutput) { outputResult = mediator->mOutputStream->requestStop(); if (outputResult != Result::OK) LOGE("[OboeEngine::stopStream]\t Oboe couldn't stop the output stream: %s", @@ -524,17 +524,17 @@ bool OboeEngine::stopStream(OboeStream *oboeStream) { /** - * \brief Called when it's needed to restart the OboeStream's audio stream(s) when the audio device(s) change + * \brief Called when it's needed to restart the PaOboeStream's audio stream(s) when the audio device(s) change * while a stream is started. Oboe will stop and close said streams in that case, * so this function just reopens and restarts them. - * @param oboeStream The stream we want to restart. + * @param paOboeStream The stream we want to restart. * @param direction The direction(s) of the stream that have to be restarted (1 for output, 2 for input, 3 for both). * @return true if the stream is restarted successfully, false otherwise. */ -bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { +bool OboeEngine::restartStream(PaOboeStream* paOboeStream, int direction) { bool outcome = true; Result result; - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; switch (direction) { case 1: //output-only @@ -566,7 +566,7 @@ bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { default: // unspecified direction or both directions: restart both streams LOGW("[OboeEngine::restartStream]\t Unspecified direction, restarting both streams"); - outcome = (restartStream(oboeStream, 1) && restartStream(oboeStream, 2)); + outcome = (restartStream(paOboeStream, 1) && restartStream(paOboeStream, 2)); break; } @@ -575,29 +575,29 @@ bool OboeEngine::restartStream(OboeStream* oboeStream, int direction) { /** - * \brief Closes oboeStream - both input and output AudioStreams of the OboeStream are checked + * \brief Closes paOboeStream - both input and output AudioStreams of the PaOboeStream are checked * and closed if active. - * @param oboeStream The stream we want to close. + * @param paOboeStream The stream we want to close. * @return true if the stream is closed successfully, otherwise returns false. */ -bool OboeEngine::closeStream(OboeStream *oboeStream) { +bool OboeEngine::closeStream(PaOboeStream *paOboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - if (oboeStream == nullptr) { - LOGE("[OboeEngine::closeStream]\t oboeStream is a nullptr."); + if (paOboeStream == nullptr) { + LOGE("[OboeEngine::closeStream]\t paOboeStream is a nullptr."); return false; } - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; - if (oboeStream->hasOutput) { + if (paOboeStream->hasOutput) { outputResult = mediator->mOutputStream->close(); if (outputResult == Result::ErrorClosed) { outputResult = Result::OK; LOGW("[OboeEngine::closeStream]\t Tried to close output stream, but was already closed."); } } - if (oboeStream->hasInput) { + if (paOboeStream->hasInput) { inputResult = mediator->mInputStream->close(); if (inputResult == Result::ErrorClosed) { inputResult = Result::OK; @@ -610,21 +610,21 @@ bool OboeEngine::closeStream(OboeStream *oboeStream) { /** - * \brief Stops oboeStream - both input and output AudioStreams of the OboeStream are checked and forcefully stopped. - * @param oboeStream The stream we want to abort. + * \brief Stops paOboeStream - both input and output AudioStreams of the PaOboeStream are checked and forcefully stopped. + * @param paOboeStream The stream we want to abort. * @return true if the output stream and the input stream are stopped successfully, false otherwise. */ -bool OboeEngine::abortStream(OboeStream *oboeStream) { +bool OboeEngine::abortStream(PaOboeStream *paOboeStream) { Result outputResult = Result::OK, inputResult = Result::OK; - if (oboeStream == nullptr) { - LOGE("[OboeEngine::abortStream]\t oboeStream is a nullptr."); + if (paOboeStream == nullptr) { + LOGE("[OboeEngine::abortStream]\t paOboeStream is a nullptr."); return false; } - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; - if (oboeStream->hasInput) { + if (paOboeStream->hasInput) { inputResult = mediator->mInputStream->stop(); if (inputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to stop: %s", @@ -634,7 +634,7 @@ bool OboeEngine::abortStream(OboeStream *oboeStream) { LOGE("[OboeEngine::abortStream]\t Couldn't force the input stream to close: %s", convertToText(inputResult)); } - if (oboeStream->hasOutput) { + if (paOboeStream->hasOutput) { outputResult = mediator->mOutputStream->stop(); if (outputResult != Result::OK) LOGE("[OboeEngine::abortStream]\t Couldn't force the output stream to stop: %s", @@ -650,23 +650,23 @@ bool OboeEngine::abortStream(OboeStream *oboeStream) { /** - * \brief Writes frames on the output stream of oboeStream. Used by blocking streams. - * @param oboeStream The stream we want to write onto. + * \brief Writes frames on the output stream of paOboeStream. Used by blocking streams. + * @param paOboeStream The stream we want to write onto. * @param buffer The buffer that we want to write on the output stream; * @param framesToWrite The number of frames that we want to write. * @return true if the buffer is written correctly, false if the write function returns an error * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(OboeStream *oboeStream, const void buffer, int32_t framesToWrite) { +bool OboeEngine::writeStream(PaOboeStream *paOboeStream, const void buffer, int32_t framesToWrite) { bool outcome = true; - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; ResultWithValue result = mediator->mOutputStream->write(buffer, framesToWrite, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (restartStream(oboeStream, 1)) + if (restartStream(paOboeStream, 1)) return true; } @@ -679,23 +679,23 @@ bool OboeEngine::writeStream(OboeStream *oboeStream, const void buffer, int32_t /** - * \brief Reads frames from the input stream of oboeStream. Used by blocking streams. - * @param oboeStream The stream we want to read from. + * \brief Reads frames from the input stream of paOboeStream. Used by blocking streams. + * @param paOboeStream The stream we want to read from. * @param buffer The buffer that we want to read from the input stream; * @param framesToWrite The number of frames that we want to read. * @return true if the buffer is read correctly, false if the read function returns an error * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t framesToRead) { +bool OboeEngine::readStream(PaOboeStream *paOboeStream, void *buffer, int32_t framesToRead) { bool outcome = true; - OboeMediator* mediator = oboeStream->oboeMediator; + OboeMediator* mediator = paOboeStream->oboeMediator; ResultWithValue result = mediator->mInputStream->read(buffer, framesToRead, TIMEOUT_NS); // If the stream is interrupted because the device suddenly changes, restart the stream. if (result.error() == Result::ErrorDisconnected) { - if (restartStream(oboeStream, 2)) + if (restartStream(paOboeStream, 2)) return true; } @@ -708,12 +708,12 @@ bool OboeEngine::readStream(OboeStream *oboeStream, void *buffer, int32_t frames /** - * \brief Allocates the memory of an OboeStream, and sets its EngineAddress to this. - * @return the address of the oboeStream. + * \brief Allocates the memory of a PaOboeStream, and sets its EngineAddress to this. + * @return the address of the paOboeStream. */ -void OboeEngine::constructOboeStream(OboeStream* oboeStream) { - mTerminableMediator = oboeStream->oboeMediator = new OboeMediator(oboeStream); - oboeStream->oboeMediator->setEngine(this); +void OboeEngine::constructPaOboeStream(PaOboeStream* paOboeStream) { + mTerminableMediator = paOboeStream->oboeMediator = new OboeMediator(paOboeStream); + paOboeStream->oboeMediator->setEngine(this); } @@ -1282,7 +1282,7 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, /** * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. - * @param oboeStream is the OboeStream we want to initialize in the output direction. + * @param paOboeStream is the PaOboeStream we want to initialize in the output direction. * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of * this file); * @param androidOutputUsage is an attribute that expresses why we are opening the output stream. @@ -1292,10 +1292,10 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeOutputStream(OboeStream* oboeStream, PaOboeHostApiRepresentation *oboeHostApi, +static PaError InitializeOutputStream(PaOboeStream* paOboeStream, PaOboeHostApiRepresentation *oboeHostApi, Usage androidOutputUsage, double sampleRate) { - return oboeHostApi->oboeEngine->openStream(oboeStream, + return oboeHostApi->oboeEngine->openStream(paOboeStream, Direction::Output, sampleRate, androidOutputUsage, @@ -1305,7 +1305,7 @@ static PaError InitializeOutputStream(OboeStream* oboeStream, PaOboeHostApiRepre /** * \brief Calls OboeEngine::openStream to open the outputStream and a Generic input preset. - * @param oboeStream is the OboeStream we want to initialize in the input direction. + * @param paOboeStream is the PaOboeStream we want to initialize in the input direction. * @param oboeHostApi points towards a OboeHostApiRepresentation (see struct defined at the top of * this file); * @param androidInputPreset is an attribute that defines the audio source. This information @@ -1315,10 +1315,10 @@ static PaError InitializeOutputStream(OboeStream* oboeStream, PaOboeHostApiRepre * the correct amount of memory. * @return the value returned by OboeEngine::openStream. */ -static PaError InitializeInputStream(OboeStream* oboeStream, PaOboeHostApiRepresentation *oboeHostApi, +static PaError InitializeInputStream(PaOboeStream* paOboeStream, PaOboeHostApiRepresentation *oboeHostApi, InputPreset androidInputPreset, double sampleRate) { - return oboeHostApi->oboeEngine->openStream(oboeStream, + return oboeHostApi->oboeEngine->openStream(paOboeStream, Direction::Input, sampleRate, Usage::Media, //Usage won't be used, so we put the default value. @@ -1327,11 +1327,11 @@ static PaError InitializeInputStream(OboeStream* oboeStream, PaOboeHostApiRepres /** - * \brief Opens the portaudio audio stream - while initializing our OboeStream. + * \brief Opens the portaudio audio stream - while initializing our PaOboeStream. * @param hostApi points towards a *HostApiRepresentation, which is a structure representing the * interface to a host API (see struct in "pa_hostapi.h"); * @param paStream points to a pointer to a PaStream, which is an audio stream structure used and built - * by portaudio, which will hold the information of our OboeStream; + * by portaudio, which will hold the information of our PaOboeStream; * @param inputParameters points towards the parameters given to the input stream; * @param outputParameters points towards the parameters given to the output stream; * @param sampleRate the sample rate we want for our stream; @@ -1362,10 +1362,10 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; - OboeStream* oboeStream = (OboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(OboeStream));; - oboeHostApi->oboeEngine->constructOboeStream(oboeStream); + PaOboeStream* paOboeStream = (PaOboeStream *) PaUtil_AllocateZeroInitializedMemory(sizeof(PaOboeStream));; + oboeHostApi->oboeEngine->constructPaOboeStream(paOboeStream); - if (!oboeStream) { + if (!paOboeStream) { error = paInsufficientMemory; goto error; } @@ -1402,11 +1402,11 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( paFloat32, inputSampleFormat); - oboeStream->inputFormat = hostInputSampleFormat; + paOboeStream->inputFormat = hostInputSampleFormat; } else { inputChannelCount = 0; inputSampleFormat = hostInputSampleFormat = paFloat32; /* Suppress 'uninitialised var' warnings. */ - oboeStream->inputFormat = hostInputSampleFormat; + paOboeStream->inputFormat = hostInputSampleFormat; } if (outputParameters) { @@ -1443,11 +1443,11 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, */ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paFloat32, outputSampleFormat); - oboeStream->outputFormat = hostOutputSampleFormat; + paOboeStream->outputFormat = hostOutputSampleFormat; } else { outputChannelCount = 0; outputSampleFormat = hostOutputSampleFormat = paFloat32; - oboeStream->outputFormat = hostOutputSampleFormat; + paOboeStream->outputFormat = hostOutputSampleFormat; } /* validate platform specific flags */ @@ -1467,18 +1467,18 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, } if (streamCallback) { - PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), + PaUtil_InitializeStreamRepresentation(&(paOboeStream->streamRepresentation), &oboeHostApi->callbackStreamInterface, streamCallback, userData); } else { - PaUtil_InitializeStreamRepresentation(&(oboeStream->streamRepresentation), + PaUtil_InitializeStreamRepresentation(&(paOboeStream->streamRepresentation), &oboeHostApi->blockingStreamInterface, streamCallback, userData); } - PaUtil_InitializeCpuLoadMeasurer(&(oboeStream->cpuLoadMeasurer), sampleRate); + PaUtil_InitializeCpuLoadMeasurer(&(paOboeStream->cpuLoadMeasurer), sampleRate); - error = PaUtil_InitializeBufferProcessor(&(oboeStream->bufferProcessor), + error = PaUtil_InitializeBufferProcessor(&(paOboeStream->bufferProcessor), inputChannelCount, inputSampleFormat, hostInputSampleFormat, @@ -1493,45 +1493,45 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, if (error != paNoError) goto error; - oboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; - oboeStream->isBlocking = (streamCallback == nullptr); - oboeStream->framesPerHostCallback = framesPerHostBuffer; - oboeStream->bytesPerFrame = sizeof(int16_t); - oboeStream->cbFlags = 0; - oboeStream->isStopped = true; - oboeStream->isActive = false; + paOboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; + paOboeStream->isBlocking = (streamCallback == nullptr); + paOboeStream->framesPerHostCallback = framesPerHostBuffer; + paOboeStream->bytesPerFrame = sizeof(int16_t); + paOboeStream->cbFlags = 0; + paOboeStream->isStopped = true; + paOboeStream->isActive = false; - if (!(oboeStream->isBlocking)) {} + if (!(paOboeStream->isBlocking)) {} // PaUnixThreading_Initialize(); TODO: see if threading works with this version of PortAudio if (inputChannelCount > 0) { - oboeStream->hasInput = true; - oboeStream->streamRepresentation.streamInfo.inputLatency = + paOboeStream->hasInput = true; + paOboeStream->streamRepresentation.streamInfo.inputLatency = ((PaTime) PaUtil_GetBufferProcessorInputLatencyFrames( - &(oboeStream->bufferProcessor)) + - oboeStream->framesPerHostCallback) / sampleRate; - ENSURE(InitializeInputStream(oboeStream, oboeHostApi, + &(paOboeStream->bufferProcessor)) + + paOboeStream->framesPerHostCallback) / sampleRate; + ENSURE(InitializeInputStream(paOboeStream, oboeHostApi, androidInputPreset, sampleRate), "Initializing input stream failed") - } else { oboeStream->hasInput = false; } + } else { paOboeStream->hasInput = false; } if (outputChannelCount > 0) { - oboeStream->hasOutput = true; - oboeStream->streamRepresentation.streamInfo.outputLatency = + paOboeStream->hasOutput = true; + paOboeStream->streamRepresentation.streamInfo.outputLatency = ((PaTime) PaUtil_GetBufferProcessorOutputLatencyFrames( - &oboeStream->bufferProcessor) - + oboeStream->framesPerHostCallback) / sampleRate; - ENSURE(InitializeOutputStream(oboeStream, oboeHostApi, + &paOboeStream->bufferProcessor) + + paOboeStream->framesPerHostCallback) / sampleRate; + ENSURE(InitializeOutputStream(paOboeStream, oboeHostApi, androidOutputUsage, sampleRate), "Initializing output stream failed"); - } else { oboeStream->hasOutput = false; } + } else { paOboeStream->hasOutput = false; } - *paStream = (PaStream *) oboeStream; + *paStream = (PaStream *) paOboeStream; return error; error: - if (oboeStream) - PaUtil_FreeMemory(oboeStream); + if (paOboeStream) + PaUtil_FreeMemory(paOboeStream); LOGE("[PaOboe - OpenStream]\t Error opening stream(s). Error code: %d", error); @@ -1544,32 +1544,32 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, * the stream(s). When CloseStream() is called, the multi-api layer ensures that the stream * has already been stopped or aborted. * @param paStream points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * portaudio, which holds the information of our PaOboeStream. * @return paNoError, but warns in the logs if OboeEngine::closeStream failed. */ static PaError CloseStream(PaStream *paStream) { - auto *oboeStream = (OboeStream *) paStream; - auto *oboeEngine = oboeStream->oboeMediator->getEngine(); + auto *paOboeStream = (PaOboeStream *) paStream; + auto *oboeEngine = paOboeStream->oboeMediator->getEngine(); - if (!(oboeEngine->closeStream(oboeStream))) + if (!(oboeEngine->closeStream(paOboeStream))) LOGW("[PaOboe - CloseStream]\t Some errors have occurred in closing oboe streams - see OboeEngine::CloseStream logs."); - PaUtil_TerminateBufferProcessor(&oboeStream->bufferProcessor); - PaUtil_TerminateStreamRepresentation(&oboeStream->streamRepresentation); + PaUtil_TerminateBufferProcessor(&paOboeStream->bufferProcessor); + PaUtil_TerminateStreamRepresentation(&paOboeStream->streamRepresentation); for (int i = 0; i < paOboe_numberOfBuffers; ++i) { - if (oboeStream->hasOutput) - PaUtil_FreeMemory(oboeStream->outputBuffers[i]); - if (oboeStream->hasInput) - PaUtil_FreeMemory(oboeStream->inputBuffers[i]); + if (paOboeStream->hasOutput) + PaUtil_FreeMemory(paOboeStream->outputBuffers[i]); + if (paOboeStream->hasInput) + PaUtil_FreeMemory(paOboeStream->inputBuffers[i]); } - if (oboeStream->hasOutput) - PaUtil_FreeMemory(oboeStream->outputBuffers); - if (oboeStream->hasInput) - PaUtil_FreeMemory(oboeStream->inputBuffers); + if (paOboeStream->hasOutput) + PaUtil_FreeMemory(paOboeStream->outputBuffers); + if (paOboeStream->hasInput) + PaUtil_FreeMemory(paOboeStream->inputBuffers); - PaUtil_FreeMemory(oboeStream); + PaUtil_FreeMemory(paOboeStream); return paNoError; } @@ -1577,55 +1577,55 @@ static PaError CloseStream(PaStream *paStream) { /** * \brief Allocates the memory of the buffers necessary to start a stream, both for output and * input, then calls OboeEngine::startStream. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::startStream fails. */ static PaError StartStream(PaStream *paStream) { - auto *oboeStream = (OboeStream *) paStream; - auto *oboeEngine = oboeStream->oboeMediator->getEngine(); + auto *paOboeStream = (PaOboeStream *) paStream; + auto *oboeEngine = paOboeStream->oboeMediator->getEngine(); - PaUtil_ResetBufferProcessor(&oboeStream->bufferProcessor); + PaUtil_ResetBufferProcessor(&paOboeStream->bufferProcessor); //Checking if the stream(s) are already active. //TODO: check if it's working as expected (extensive testing needed, no problem spotted with situational tests) - if (oboeStream->isActive) { + if (paOboeStream->isActive) { LOGW("[PaOboe - StartStream]\t Stream was already active, stopping..."); StopStream(paStream); LOGW("[PaOboe - StartStream]\t Restarting..."); StartStream(paStream); } - oboeStream->currentOutputBuffer = 0; - oboeStream->currentInputBuffer = 0; + paOboeStream->currentOutputBuffer = 0; + paOboeStream->currentInputBuffer = 0; /* Initialize buffers */ for (int i = 0; i < paOboe_numberOfBuffers; ++i) { - if (oboeStream->hasOutput) { - memset(oboeStream->outputBuffers[oboeStream->currentOutputBuffer], 0, - oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.outputChannelCount); - oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; + if (paOboeStream->hasOutput) { + memset(paOboeStream->outputBuffers[paOboeStream->currentOutputBuffer], 0, + paOboeStream->framesPerHostCallback * paOboeStream->bytesPerFrame * + paOboeStream->bufferProcessor.outputChannelCount); + paOboeStream->currentOutputBuffer = (paOboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; } - if (oboeStream->hasInput) { - memset(oboeStream->inputBuffers[oboeStream->currentInputBuffer], 0, - oboeStream->framesPerHostCallback * oboeStream->bytesPerFrame * - oboeStream->bufferProcessor.inputChannelCount); - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; + if (paOboeStream->hasInput) { + memset(paOboeStream->inputBuffers[paOboeStream->currentInputBuffer], 0, + paOboeStream->framesPerHostCallback * paOboeStream->bytesPerFrame * + paOboeStream->bufferProcessor.inputChannelCount); + paOboeStream->currentInputBuffer = (paOboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; } } - if (!oboeStream->isBlocking) { - oboeStream->callbackResult = paContinue; - oboeStream->oboeCallbackResult = DataCallbackResult::Continue; + if (!paOboeStream->isBlocking) { + paOboeStream->callbackResult = paContinue; + paOboeStream->oboeCallbackResult = DataCallbackResult::Continue; } - oboeStream->isStopped = false; - oboeStream->isActive = true; - oboeStream->doStop = false; - oboeStream->doAbort = false; + paOboeStream->isStopped = false; + paOboeStream->isActive = true; + paOboeStream->doStop = false; + paOboeStream->doAbort = false; - if (!(oboeEngine->startStream(oboeStream))) + if (!(oboeEngine->startStream(paOboeStream))) return paUnanticipatedHostError; else return paNoError; @@ -1635,31 +1635,31 @@ static PaError StartStream(PaStream *paStream) { /** * \brief Ends the stream callback, if the stream is not blocking, and calls * OboeEngine::stopStream. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. - * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::stopStream fails. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::stopStream fails. */ static PaError StopStream(PaStream *paStream) { PaError error = paNoError; - auto *oboeStream = (OboeStream *) paStream; - auto *oboeEngine = oboeStream->oboeMediator->getEngine(); + auto *paOboeStream = (PaOboeStream *) paStream; + auto *oboeEngine = paOboeStream->oboeMediator->getEngine(); - if (oboeStream->isStopped) { + if (paOboeStream->isStopped) { LOGW("[PaOboe - StopStream]\t Stream was already stopped."); } else { - if (!(oboeStream->isBlocking)) { - oboeStream->doStop = true; + if (!(paOboeStream->isBlocking)) { + paOboeStream->doStop = true; } - if (!(oboeEngine->stopStream(oboeStream))) { + if (!(oboeEngine->stopStream(paOboeStream))) { LOGE("[PaOboe - StopStream]\t Couldn't stop the stream(s) correctly - see OboeEngine::StopStream logs."); error = paUnanticipatedHostError; } - oboeStream->isActive = false; - oboeStream->isStopped = true; - if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) - oboeStream->streamRepresentation.streamFinishedCallback( - oboeStream->streamRepresentation.userData); + paOboeStream->isActive = false; + paOboeStream->isStopped = true; + if (paOboeStream->streamRepresentation.streamFinishedCallback != nullptr) + paOboeStream->streamRepresentation.streamFinishedCallback( + paOboeStream->streamRepresentation.userData); } return error; @@ -1669,31 +1669,31 @@ static PaError StopStream(PaStream *paStream) { /** * \brief Aborts the stream callback, if the stream is not blocking, and calls * OboeEngine::abortStream. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. - * @return paNoError if no errors occur, paUnanticipatedHostError if OboeStream::abortStream fails. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. + * @return paNoError if no errors occur, paUnanticipatedHostError if OboeEngine::abortStream fails. */ static PaError AbortStream(PaStream *paStream) { PaError error = paNoError; - auto *oboeStream = (OboeStream *) paStream; - auto *oboeEngine = oboeStream->oboeMediator->getEngine(); + auto *paOboeStream = (PaOboeStream *) paStream; + auto *oboeEngine = paOboeStream->oboeMediator->getEngine(); LOGI("[PaOboe - AbortStream]\t Aborting stream."); - if (!oboeStream->isBlocking) { - oboeStream->doAbort = true; + if (!paOboeStream->isBlocking) { + paOboeStream->doAbort = true; } /* stop immediately so enqueue has no effect */ - if (!(oboeEngine->abortStream(oboeStream))) { + if (!(oboeEngine->abortStream(paOboeStream))) { LOGE("[PaOboe - AbortStream]\t Couldn't abort the stream - see OboeEngine::abortStream logs."); error = paUnanticipatedHostError; } - oboeStream->isActive = false; - oboeStream->isStopped = true; - if (oboeStream->streamRepresentation.streamFinishedCallback != nullptr) - oboeStream->streamRepresentation.streamFinishedCallback( - oboeStream->streamRepresentation.userData); + paOboeStream->isActive = false; + paOboeStream->isStopped = true; + if (paOboeStream->streamRepresentation.streamFinishedCallback != nullptr) + paOboeStream->streamRepresentation.streamFinishedCallback( + paOboeStream->streamRepresentation.userData); return error; } @@ -1701,27 +1701,27 @@ static PaError AbortStream(PaStream *paStream) { /** * \brief Copies an input stream buffer by buffer, and calls OboeEngine::readStream. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream; + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream; * @param buffer is the address of the first sample of the buffer; * @param frames is the total number of frames to read. * @return paInternalError if OboeEngine::readStream fails, paNoError otherwise. */ static PaError ReadStream(PaStream *paStream, void *buffer, unsigned long frames) { - auto *oboeStream = (OboeStream *) paStream; - auto *oboeEngine = oboeStream->oboeMediator->getEngine(); + auto *paOboeStream = (PaOboeStream *) paStream; + auto *oboeEngine = paOboeStream->oboeMediator->getEngine(); void *userBuffer = buffer; unsigned framesToRead; PaError error = paNoError; while (frames > 0) { - framesToRead = PA_MIN(oboeStream->framesPerHostCallback, frames); + framesToRead = PA_MIN(paOboeStream->framesPerHostCallback, frames); - if (!(oboeEngine->readStream(oboeStream, userBuffer, framesToRead * - oboeStream->bufferProcessor.inputChannelCount))) + if (!(oboeEngine->readStream(paOboeStream, userBuffer, framesToRead * + paOboeStream->bufferProcessor.inputChannelCount))) error = paInternalError; - oboeStream->currentInputBuffer = (oboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; + paOboeStream->currentInputBuffer = (paOboeStream->currentInputBuffer + 1) % paOboe_numberOfBuffers; frames -= framesToRead; } @@ -1731,27 +1731,27 @@ static PaError ReadStream(PaStream *paStream, void *buffer, unsigned long frames /** * \brief Copies an output stream buffer by buffer, and calls OboeEngine::writeStream. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream; + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream; * @param buffer is the address of the first sample of the buffer; * @param frames is the total number of frames to write. * @return paInternalError if OboeEngine::writeStream fails, paNoError otherwise. */ static PaError WriteStream(PaStream *paStream, const void *buffer, unsigned long frames) { - auto *oboeStream = (OboeStream *) paStream; - auto *oboeEngine = oboeStream->oboeMediator->getEngine(); + auto *paOboeStream = (PaOboeStream *) paStream; + auto *oboeEngine = paOboeStream->oboeMediator->getEngine(); const void *userBuffer = buffer; unsigned framesToWrite; PaError error = paNoError; while (frames > 0) { - framesToWrite = PA_MIN(oboeStream->framesPerHostCallback, frames); + framesToWrite = PA_MIN(paOboeStream->framesPerHostCallback, frames); - if (!(oboeEngine->writeStream(oboeStream, userBuffer, framesToWrite * - oboeStream->bufferProcessor.outputChannelCount))) + if (!(oboeEngine->writeStream(paOboeStream, userBuffer, framesToWrite * + paOboeStream->bufferProcessor.outputChannelCount))) error = paInternalError; - oboeStream->currentOutputBuffer = (oboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; + paOboeStream->currentOutputBuffer = (paOboeStream->currentOutputBuffer + 1) % paOboe_numberOfBuffers; frames -= framesToWrite; } @@ -1763,56 +1763,56 @@ static PaError WriteStream(PaStream *paStream, const void *buffer, unsigned long /** * \brief Function needed by portaudio to understand how many frames can be read without waiting. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. * @return the minimum number of frames that can be read without waiting. */ static signed long GetStreamReadAvailable(PaStream *paStream) { - auto *oboeStream = (OboeStream *) paStream; - return oboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - oboeStream->currentInputBuffer); + auto *paOboeStream = (PaOboeStream *) paStream; + return paOboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - paOboeStream->currentInputBuffer); } /** * \brief Function needed by portaudio to understand how many frames can be written without waiting. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. * @return the minimum number of frames that can be written without waiting. */ static signed long GetStreamWriteAvailable(PaStream *paStream) { - auto *oboeStream = (OboeStream *) paStream; - return oboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - oboeStream->currentOutputBuffer); + auto *paOboeStream = (PaOboeStream *) paStream; + return paOboeStream->framesPerHostCallback * (paOboe_numberOfBuffers - paOboeStream->currentOutputBuffer); } /** * \brief Function needed by portaudio to understand if the stream is stopped. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. * @return one (1) when the stream is stopped, or zero (0) when the stream is running. */ static PaError IsStreamStopped(PaStream *paStream) { - auto *oboeStream = (OboeStream *) paStream; - return oboeStream->isStopped; + auto *paOboeStream = (PaOboeStream *) paStream; + return paOboeStream->isStopped; } /** * \brief Function needed by portaudio to understand if the stream is active. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. * @return one (1) when the stream is active (ie playing or recording audio), or zero (0) otherwise. */ static PaError IsStreamActive(PaStream *paStream) { - auto *oboeStream = (OboeStream *) paStream; - return oboeStream->isActive; + auto *paOboeStream = (PaOboeStream *) paStream; + return paOboeStream->isActive; } /** * \brief Function needed by portaudio to get the stream time in seconds. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. * @return The stream's current time in seconds, or 0 if an error occurred. */ static PaTime GetStreamTime(PaStream *paStream) { @@ -1822,8 +1822,8 @@ static PaTime GetStreamTime(PaStream *paStream) { /** * \brief Function needed by portaudio to retrieve CPU usage information for the specified stream. - * @param s points to to a PaStream, which is an audio stream structure used and built by - * portaudio, which holds the information of our OboeStream. + * @param paStream points to to a PaStream, which is an audio stream structure used and built by + * portaudio, which holds the information of our PaOboeStream. * @return A floating point value, typically between 0.0 and 1.0, where 1.0 indicates that the * stream callback is consuming the maximum number of CPU cycles possible to maintain * real-time operation. A value of 0.5 would imply that PortAudio and the stream callback @@ -1832,8 +1832,8 @@ static PaTime GetStreamTime(PaStream *paStream) { * occurs. */ static double GetStreamCpuLoad(PaStream *paStream) { - auto *oboeStream = (OboeStream *) paStream; - return PaUtil_GetCpuLoad(&oboeStream->cpuLoadMeasurer); + auto *paOboeStream = (PaOboeStream *) paStream; + return PaUtil_GetCpuLoad(&paOboeStream->cpuLoadMeasurer); } @@ -1864,9 +1864,9 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode) { if (direction == Direction::Input) { - paOboe_inputPerfMode = performanceMode; + paOboe_inputPerformanceMode = performanceMode; } else { - paOboe_outputPerfMode = performanceMode; + paOboe_outputPerformanceMode = performanceMode; } } From be18a068870866babb004bc92ad4df4ace522490 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 5 Oct 2023 10:14:56 +0200 Subject: [PATCH 112/125] fixed all compiling errors generated by typos --- include/portaudio.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/portaudio.h b/include/portaudio.h index ab1efeb55..2b4e3ba75 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -273,6 +273,7 @@ PaHostApiIndex Pa_GetDefaultHostApi( void ); @see PaHostApiInfo */ + typedef enum PaHostApiTypeId { paInDevelopment=0, /* use while developing support for a new host API */ @@ -290,7 +291,8 @@ typedef enum PaHostApiTypeId paWASAPI=13, paAudioScienceHPI=14, paAudioIO=15, - paPulseAudio=16 + paPulseAudio=16, + paOboe=17 } PaHostApiTypeId; From 512ed423a34fe58c23f26496e134c3cb810443a5 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 14:47:02 +0200 Subject: [PATCH 113/125] Fixed minor problem with TryStream --- src/hostapi/oboe/pa_oboe.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 79a9b35e1..fb8ed9ca7 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -320,7 +320,6 @@ OboeEngine::OboeEngine() {} */ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { Result result; - bool outcome = false; std::shared_ptr stream; AudioStreamBuilder builder; @@ -337,12 +336,12 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", convertToText(result)); - return outcome; + return false; } stream->close(); - return outcome; + return true; } From f88819adc4f5e8304a8bd9afe79f4a057fe6429a Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 10 Oct 2023 10:09:19 +0200 Subject: [PATCH 114/125] fixed long line in FindOboe.cmake --- cmake/modules/FindOboe.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index ea66ec394..8dbf9daa3 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -22,8 +22,8 @@ endif() if(NOT OBOE_DIRECTORY) message(AUTHOR_WARNING - "If you're trying to use Oboe as a Host API, please specify the directory where you - cloned its repository. For further information, please read src/hostapi/oboe/README.md" + "If you're trying to use Oboe as a Host API, please specify the directory where you " + "cloned its repository. For further information, please read src/hostapi/oboe/README.md" ) set(OBOE_FOUND FALSE) else() From 6f84e76382212179d247685aa49fc0d5f086b2c2 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 10 Oct 2023 10:35:11 +0200 Subject: [PATCH 115/125] fixed typos in pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index fb8ed9ca7..f278d3a63 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -399,7 +399,7 @@ PaError OboeEngine::openStream(PaOboeStream *paOboeStream, Direction direction, return paUnanticipatedHostError; } - mediator->minputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * + mediator->mInputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * paOboe_numberOfBuffers); paOboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); @@ -657,7 +657,7 @@ bool OboeEngine::abortStream(PaOboeStream *paOboeStream) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(PaOboeStream *paOboeStream, const void buffer, int32_t framesToWrite) { +bool OboeEngine::writeStream(PaOboeStream *paOboeStream, const void *buffer, int32_t framesToWrite) { bool outcome = true; OboeMediator* mediator = paOboeStream->oboeMediator; From 5ed3d7f54f66ad0e66413b47cd5da912afb297c6 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 10 Oct 2023 10:43:49 +0200 Subject: [PATCH 116/125] set to verbose some logs --- src/hostapi/oboe/pa_oboe.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index f278d3a63..30fab00c3 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -726,19 +726,19 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { switch (paFormat) { case paFloat32: oboeFormat = AudioFormat::Float; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); break; case paInt16: oboeFormat = AudioFormat::I16; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); break; case paInt32: oboeFormat = AudioFormat::I32; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); break; case paInt24: oboeFormat = AudioFormat::I24; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); break; default: oboeFormat = AudioFormat::Unspecified; @@ -1369,7 +1369,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, goto error; } - LOGI("[PaOboe - OpenStream]\t OpenStream called."); + LOGV("[PaOboe - OpenStream]\t OpenStream called."); if (inputParameters) { inputChannelCount = inputParameters->channelCount; From eae9922ffa9ea16206befeefb18393a96d2abc39 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Mon, 9 Oct 2023 14:47:02 +0200 Subject: [PATCH 117/125] Fixed minor problem with TryStream --- src/hostapi/oboe/pa_oboe.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 79a9b35e1..fb8ed9ca7 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -320,7 +320,6 @@ OboeEngine::OboeEngine() {} */ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { Result result; - bool outcome = false; std::shared_ptr stream; AudioStreamBuilder builder; @@ -337,12 +336,12 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", convertToText(result)); - return outcome; + return false; } stream->close(); - return outcome; + return true; } From 87508d9e8aea3ff217b87255f9d6be487fbac383 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 10 Oct 2023 10:09:19 +0200 Subject: [PATCH 118/125] fixed long line in FindOboe.cmake --- cmake/modules/FindOboe.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindOboe.cmake b/cmake/modules/FindOboe.cmake index ea66ec394..8dbf9daa3 100644 --- a/cmake/modules/FindOboe.cmake +++ b/cmake/modules/FindOboe.cmake @@ -22,8 +22,8 @@ endif() if(NOT OBOE_DIRECTORY) message(AUTHOR_WARNING - "If you're trying to use Oboe as a Host API, please specify the directory where you - cloned its repository. For further information, please read src/hostapi/oboe/README.md" + "If you're trying to use Oboe as a Host API, please specify the directory where you " + "cloned its repository. For further information, please read src/hostapi/oboe/README.md" ) set(OBOE_FOUND FALSE) else() From 263427d9f0bd5284ccc3b3b6c47cd499ab133e49 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 10 Oct 2023 10:35:11 +0200 Subject: [PATCH 119/125] fixed typos in pa_oboe.cpp --- src/hostapi/oboe/pa_oboe.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index fb8ed9ca7..f278d3a63 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -399,7 +399,7 @@ PaError OboeEngine::openStream(PaOboeStream *paOboeStream, Direction direction, return paUnanticipatedHostError; } - mediator->minputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * + mediator->mInputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * paOboe_numberOfBuffers); paOboeStream->inputBuffers = (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); @@ -657,7 +657,7 @@ bool OboeEngine::abortStream(PaOboeStream *paOboeStream) { * different from ErrorDisconnected. In case of ErrorDisconnected, the function returns * true if the stream is successfully restarted, and false otherwise. */ -bool OboeEngine::writeStream(PaOboeStream *paOboeStream, const void buffer, int32_t framesToWrite) { +bool OboeEngine::writeStream(PaOboeStream *paOboeStream, const void *buffer, int32_t framesToWrite) { bool outcome = true; OboeMediator* mediator = paOboeStream->oboeMediator; From a5d2b7164bef4e57b3ba26e66294adecf5c49a46 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 10 Oct 2023 10:43:49 +0200 Subject: [PATCH 120/125] set to verbose some logs --- src/hostapi/oboe/pa_oboe.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index f278d3a63..30fab00c3 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -726,19 +726,19 @@ AudioFormat OboeEngine::PaToOboeFormat(PaSampleFormat paFormat) { switch (paFormat) { case paFloat32: oboeFormat = AudioFormat::Float; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: FLOAT"); break; case paInt16: oboeFormat = AudioFormat::I16; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I16"); break; case paInt32: oboeFormat = AudioFormat::I32; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I32"); break; case paInt24: oboeFormat = AudioFormat::I24; - LOGI("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); + LOGV("[OboeEngine::PaToOboeFormat]\t REQUESTED OBOE FORMAT: I24"); break; default: oboeFormat = AudioFormat::Unspecified; @@ -1369,7 +1369,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, goto error; } - LOGI("[PaOboe - OpenStream]\t OpenStream called."); + LOGV("[PaOboe - OpenStream]\t OpenStream called."); if (inputParameters) { inputChannelCount = inputParameters->channelCount; From 8a5497065603ccc639c9038b9aa5efc38b6a7972 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Tue, 10 Oct 2023 13:12:15 +0200 Subject: [PATCH 121/125] Better handling of format in paOboe, removed junk code from CMakeLists --- CMakeLists.txt | 27 --------------------------- src/hostapi/oboe/pa_oboe.cpp | 18 ++++++++---------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67b30b586..2acd9a7f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,33 +43,6 @@ if(PA_WARNINGS_ARE_ERRORS) endif() endif() -set(PA_COMMON_SOURCES - src/common/pa_allocation.c - src/common/pa_allocation.h - src/common/pa_converters.c - src/common/pa_converters.h - src/common/pa_cpuload.c - src/common/pa_cpuload.h - src/common/pa_debugprint.c - src/common/pa_debugprint.h - src/common/pa_dither.c - src/common/pa_dither.h - src/common/pa_endianness.h - src/common/pa_front.c - src/common/pa_hostapi.h - src/common/pa_memorybarrier.h - src/common/pa_process.c - src/common/pa_process.h - src/common/pa_ringbuffer.c - src/common/pa_ringbuffer.h - src/common/pa_stream.c - src/common/pa_stream.h - src/common/pa_trace.c - src/common/pa_trace.h - src/common/pa_types.h - src/common/pa_util.h - ) - add_library(PortAudio ${LIBRARY_BUILD_TYPE} src/common/pa_allocation.c diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 30fab00c3..d2c96ca5f 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -72,6 +72,9 @@ #include "pa_oboe.h" +//FIXME: if your project needs a specific PaFormat, modify this value +#define paOboeDefaultFormat paFloat32 + #define MODULE_NAME "PaOboe" #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__) @@ -1397,14 +1400,12 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, androidInputPreset != InputPreset::VoicePerformance) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paFloat32" with whatever format you prefer - - * PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options */ hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paFloat32, inputSampleFormat); + paOboeDefaultFormat, inputSampleFormat); paOboeStream->inputFormat = hostInputSampleFormat; } else { inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paFloat32; /* Suppress 'uninitialised var' warnings. */ + inputSampleFormat = hostInputSampleFormat = paOboeDefaultFormat; /* Suppress 'uninitialised var' warnings. */ paOboeStream->inputFormat = hostInputSampleFormat; } @@ -1437,15 +1438,12 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, androidOutputUsage != Usage::Game) return paIncompatibleHostApiSpecificStreamInfo; } - /* FIXME: Replace "paFloat32" with whatever format you prefer - - PaUtil_SelectClosestAvailableFormat is a bit faulty when working with multiple options - */ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( - paFloat32, outputSampleFormat); + paOboeDefaultFormat, outputSampleFormat); paOboeStream->outputFormat = hostOutputSampleFormat; } else { outputChannelCount = 0; - outputSampleFormat = hostOutputSampleFormat = paFloat32; + outputSampleFormat = hostOutputSampleFormat = paOboeDefaultFormat; paOboeStream->outputFormat = hostOutputSampleFormat; } @@ -1495,7 +1493,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, paOboeStream->streamRepresentation.streamInfo.sampleRate = sampleRate; paOboeStream->isBlocking = (streamCallback == nullptr); paOboeStream->framesPerHostCallback = framesPerHostBuffer; - paOboeStream->bytesPerFrame = sizeof(int16_t); + paOboeStream->bytesPerFrame = sizeof(paOboeDefaultFormat); paOboeStream->cbFlags = 0; paOboeStream->isStopped = true; paOboeStream->isActive = false; From 097b37d1158d16a139fe972e82979f52846a0a87 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Wed, 18 Oct 2023 09:36:06 +0200 Subject: [PATCH 122/125] Improved readability of some variables --- include/pa_oboe.h | 1 + src/hostapi/oboe/pa_oboe.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 900a1f19f..12d3c0c84 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -51,6 +51,7 @@ */ #include "portaudio.h" +//TODO: remove oboe/Oboe.h from here, so user doesn't have to include Oboe in their project #include "oboe/Oboe.h" #ifdef __cplusplus diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index d2c96ca5f..0e651d06f 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -213,10 +213,10 @@ class OboeMediator: public AudioStreamCallback{ } //Callback function for non-blocking streams - DataCallbackResult onAudioReady(AudioStream *audioStream, void *audioData, + DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override; //Callback utils - void onErrorAfterClose(AudioStream *audioStream, oboe::Result error) override; + void onErrorAfterClose(AudioStream *oboeStream, oboe::Result error) override; void resetCallbackCounters(); void setOutputCallback() { mOutputBuilder.setDataCallback(this)->setErrorCallback(this); } void setInputCallback() { mInputBuilder.setDataCallback(this)->setErrorCallback(this); } @@ -324,7 +324,7 @@ OboeEngine::OboeEngine() {} bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t channelCount) { Result result; - std::shared_ptr stream; + std::shared_ptr oboeStream; AudioStreamBuilder builder; builder.setDeviceId(getSelectedDevice(direction)) @@ -334,7 +334,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan ->setDirection(direction) ->setSampleRate(sampleRate) ->setChannelCount(channelCount) - ->openStream(stream); + ->openStream(oboeStream); if (result != Result::OK) { LOGE("[OboeEngine::TryStream]\t Couldn't open the stream in TryStream. Error: %s", @@ -342,7 +342,7 @@ bool OboeEngine::tryStream(Direction direction, int32_t sampleRate, int32_t chan return false; } - stream->close(); + oboeStream->close(); return true; } @@ -405,7 +405,7 @@ PaError OboeEngine::openStream(PaOboeStream *paOboeStream, Direction direction, mediator->mInputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * paOboe_numberOfBuffers); paOboeStream->inputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); + (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(paOboeDefaultFormat * )); for (int i = 0; i < paOboe_numberOfBuffers; ++i) { paOboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( @@ -770,7 +770,7 @@ int32_t OboeEngine::getSelectedDevice(Direction direction) { * \brief Oboe's callback routine. */ DataCallbackResult -OboeMediator::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) { +OboeMediator::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) { clock_gettime(CLOCK_REALTIME, &mTimeSpec); mTimeInfo.currentTime = (PaTime)(mTimeSpec.tv_sec + (mTimeSpec.tv_nsec / 1000000000.0)); @@ -858,7 +858,7 @@ OboeMediator::onAudioReady(AudioStream *audioStream, void *audioData, int32_t nu * \brief If the data callback ends without returning DataCallbackResult::Stop, this routine tells * what error occurred, and tries to restart the stream if the error was ErrorDisconnected. */ -void OboeMediator::onErrorAfterClose(AudioStream *audioStream, Result error) { +void OboeMediator::onErrorAfterClose(AudioStream *oboeStream, Result error) { if (error == oboe::Result::ErrorDisconnected) { OboeEngine* oboeEngine = getEngine(); LOGW("[OboeMediator::onErrorAfterClose]\t ErrorDisconnected - Restarting stream(s)"); From 6c1e81cd8260d8a7d262da5a077ff163b8d5f515 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 19 Oct 2023 11:06:46 +0200 Subject: [PATCH 123/125] Removed '#include oboe/Oboe.h' from pa_oboe.h, and modified host api implementation accordingly --- include/pa_oboe.h | 50 +++++++++++++++++++++++++++--------- src/hostapi/oboe/pa_oboe.cpp | 32 +++++++---------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 12d3c0c84..2a83c2120 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -51,8 +51,6 @@ */ #include "portaudio.h" -//TODO: remove oboe/Oboe.h from here, so user doesn't have to include Oboe in their project -#include "oboe/Oboe.h" #ifdef __cplusplus extern "C" { @@ -62,29 +60,57 @@ extern "C" { #define LOW_LATENCY_MS 300.0 //Arbitrary value used to automatically determine if low latency performance mode is doable /** - * The android stream type and recording preset as defined in Definitions.h + * Enum class that emulates Oboe::Direction. + */ +enum class PaOboe_Direction: int32_t { Output = 0, Input = 1 }; + +/** + * Enum class that emulates Oboe::Usage. + */ +enum class PaOboe_Usage : int32_t { + Media = 1 , VoiceCommunication = 2 , VoiceCommunicationSignalling = 3 , Alarm = 4 , + Notification = 5 , NotificationRingtone = 6 , NotificationEvent = 10 , AssistanceAccessibility = 11 , + AssistanceNavigationGuidance = 12 , AssistanceSonification = 13 , Game = 14 , Assistant = 16 +}; + +/** + * Enum class that emulates Oboe::InputPreset + */ +enum class PaOboe_InputPreset : int32_t { + Generic = 1 , Camcorder = 5 , VoiceRecognition = 6 , VoiceCommunication = 7 , + Unprocessed = 9 , VoicePerformance = 10 +} + +/** + * Enum class that emulates Oboe::PerformanceMode + */ +enum class PaOboe_PerformanceMode : int32_t { None = 10 , PowerSaving = 11 , LowLatency = 12 } + +/** + * The android stream type and recording preset as defined in Oboe. */ typedef struct PaOboeStreamInfo { - oboe::Usage androidOutputUsage; - oboe::InputPreset androidInputPreset; + PaOboe_Usage androidOutputUsage; + PaOboe_InputPreset androidInputPreset; } PaOboeStreamInfo; /** - * Provide PA Oboe with the ID of the device the user chose - oboe cannot build a device list, + * Provide PaOboe with the ID of the device the user chose - oboe cannot build a device list, * but can select the device if provided with its ID. * @param direction - the direction of the stream for which we want to set the device. - * @param deviceID - the ID of the device chosen by the user. + * @param deviceID - the ID of the chosen device chosen by the user. */ -void PaOboe_SetSelectedDevice(oboe::Direction direction, int32_t deviceID); +void PaOboe_SetSelectedDevice(PaOboe_Direction direction, int32_t deviceID); /** - * Provide PA Oboe with the performance mode chosen by the user. - * @param direction - the direction of the stream for which we want to set the performance mode. - * @param performanceMode - the performance mode chosen by the user. + * \brief Provide PaOboe with the performance mode chosen by the user. If this method isn't called, the default mode + * is LowLatency. + * @param direction - the direction of the stream for which we want to set the performance mode. + * @param performanceMode - the performance mode chosen by the user. */ -void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode); +void PaOboe_SetPerformanceMode(PaOboe_Direction direction, PaOboe_PerformanceMode performanceMode); /** diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 0e651d06f..1e1558c96 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -1212,13 +1212,13 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, /* validate inputStreamInfo */ if (inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. - InputPreset androidRecordingPreset = + InputPreset androidInputPreset = ((PaOboeStreamInfo *) inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; - if (androidRecordingPreset != InputPreset::Generic && - androidRecordingPreset != InputPreset::Camcorder && - androidRecordingPreset != InputPreset::VoiceRecognition && - androidRecordingPreset != InputPreset::VoiceCommunication && - androidRecordingPreset != InputPreset::VoicePerformance) { + if (androidInputPreset != InputPreset::Generic && + androidInputPreset != InputPreset::Camcorder && + androidInputPreset != InputPreset::VoiceRecognition && + androidInputPreset != InputPreset::VoiceCommunication && + androidInputPreset != InputPreset::VoicePerformance) { return paIncompatibleHostApiSpecificStreamInfo; } } @@ -1361,6 +1361,7 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, PaSampleFormat inputSampleFormat, outputSampleFormat; PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + //Initialization to generic values, in the event that these hostApiSpecificStreamInfo were not set Usage androidOutputUsage = Usage::VoiceCommunication; InputPreset androidInputPreset = InputPreset::Generic; @@ -1393,12 +1394,6 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, // Only has an effect on ANDROID_API>=28. androidInputPreset = ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; - if (androidInputPreset != InputPreset::Generic && - androidInputPreset != InputPreset::Camcorder && - androidInputPreset != InputPreset::VoiceRecognition && - androidInputPreset != InputPreset::VoiceCommunication && - androidInputPreset != InputPreset::VoicePerformance) - return paIncompatibleHostApiSpecificStreamInfo; } hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( paOboeDefaultFormat, inputSampleFormat); @@ -1428,15 +1423,6 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, if (outputParameters->hostApiSpecificStreamInfo) { androidOutputUsage = ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; - if (androidOutputUsage != Usage::Media && - androidOutputUsage != Usage::Notification && - androidOutputUsage != Usage::NotificationEvent && - androidOutputUsage != Usage::NotificationRingtone && - androidOutputUsage != Usage::VoiceCommunication && - androidOutputUsage != Usage::VoiceCommunicationSignalling && - androidOutputUsage != Usage::Alarm && - androidOutputUsage != Usage::Game) - return paIncompatibleHostApiSpecificStreamInfo; } hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paOboeDefaultFormat, outputSampleFormat); @@ -1851,7 +1837,7 @@ static unsigned long GetApproximateLowBufferSize() { /*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ -void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { +void PaOboe_SetSelectedDevice(PaOboe_Direction direction, int32_t deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); if (direction == Direction::Input) paOboe_inputDeviceId = deviceID; @@ -1859,7 +1845,7 @@ void PaOboe_SetSelectedDevice(Direction direction, int32_t deviceID) { paOboe_outputDeviceId = deviceID; } -void PaOboe_SetPerformanceMode(oboe::Direction direction, oboe::PerformanceMode performanceMode) { +void PaOboe_SetPerformanceMode(PaOboe_Direction direction, PaOboe_PerformanceMode performanceMode) { if (direction == Direction::Input) { paOboe_inputPerformanceMode = performanceMode; } else { From 9669f5bdfa2cb7529fcb2de4af021487055adb52 Mon Sep 17 00:00:00 2001 From: Carlo Benfatti Date: Thu, 19 Oct 2023 11:37:28 +0200 Subject: [PATCH 124/125] static cast fixes --- .gitignore | 1 + include/pa_oboe.h | 4 ++-- src/hostapi/oboe/pa_oboe.cpp | 27 +++++++++++++++++---------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 1adfe452d..37f3e2e67 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ autom4te.cache/* # build_all_PaOboe.sh CMake output folder build +build_old # Precompiled Headers *.gch diff --git a/include/pa_oboe.h b/include/pa_oboe.h index 2a83c2120..6e435eadb 100644 --- a/include/pa_oboe.h +++ b/include/pa_oboe.h @@ -79,12 +79,12 @@ enum class PaOboe_Usage : int32_t { enum class PaOboe_InputPreset : int32_t { Generic = 1 , Camcorder = 5 , VoiceRecognition = 6 , VoiceCommunication = 7 , Unprocessed = 9 , VoicePerformance = 10 -} +}; /** * Enum class that emulates Oboe::PerformanceMode */ -enum class PaOboe_PerformanceMode : int32_t { None = 10 , PowerSaving = 11 , LowLatency = 12 } +enum class PaOboe_PerformanceMode : int32_t { None = 10 , PowerSaving = 11 , LowLatency = 12 }; /** * The android stream type and recording preset as defined in Oboe. diff --git a/src/hostapi/oboe/pa_oboe.cpp b/src/hostapi/oboe/pa_oboe.cpp index 1e1558c96..6d4b087b4 100644 --- a/src/hostapi/oboe/pa_oboe.cpp +++ b/src/hostapi/oboe/pa_oboe.cpp @@ -405,7 +405,7 @@ PaError OboeEngine::openStream(PaOboeStream *paOboeStream, Direction direction, mediator->mInputStream->setBufferSizeInFrames(mediator->mInputStream->getFramesPerBurst() * paOboe_numberOfBuffers); paOboeStream->inputBuffers = - (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(paOboeDefaultFormat * )); + (void **) PaUtil_AllocateZeroInitializedMemory(paOboe_numberOfBuffers * sizeof(int32_t * )); for (int i = 0; i < paOboe_numberOfBuffers; ++i) { paOboeStream->inputBuffers[i] = (void *) PaUtil_AllocateZeroInitializedMemory( @@ -1213,7 +1213,9 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, if (inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. InputPreset androidInputPreset = - ((PaOboeStreamInfo *) inputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + static_cast( + ((PaOboeStreamInfo *) inputParameters->hostApiSpecificStreamInfo)->androidInputPreset + ); if (androidInputPreset != InputPreset::Generic && androidInputPreset != InputPreset::Camcorder && androidInputPreset != InputPreset::VoiceRecognition && @@ -1251,7 +1253,9 @@ static PaError IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, if (outputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. Usage androidOutputUsage = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + static_cast( + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage + ); if (androidOutputUsage != Usage::Media && androidOutputUsage != Usage::Notification && androidOutputUsage != Usage::NotificationEvent && @@ -1393,7 +1397,9 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, if (inputParameters->hostApiSpecificStreamInfo) { // Only has an effect on ANDROID_API>=28. androidInputPreset = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset; + static_cast( + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidInputPreset + ); } hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( paOboeDefaultFormat, inputSampleFormat); @@ -1422,7 +1428,9 @@ static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, /* validate outputStreamInfo */ if (outputParameters->hostApiSpecificStreamInfo) { androidOutputUsage = - ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage; + static_cast( + ((PaOboeStreamInfo *) outputParameters->hostApiSpecificStreamInfo)->androidOutputUsage + ); } hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paOboeDefaultFormat, outputSampleFormat); @@ -1834,22 +1842,21 @@ static unsigned long GetApproximateLowBufferSize() { return 192; } - /*----------------------------- Implementation of PaOboe.h functions -----------------------------*/ void PaOboe_SetSelectedDevice(PaOboe_Direction direction, int32_t deviceID) { LOGI("[PaOboe - SetSelectedDevice] Selecting device..."); - if (direction == Direction::Input) + if (static_cast(direction) == Direction::Input) paOboe_inputDeviceId = deviceID; else paOboe_outputDeviceId = deviceID; } void PaOboe_SetPerformanceMode(PaOboe_Direction direction, PaOboe_PerformanceMode performanceMode) { - if (direction == Direction::Input) { - paOboe_inputPerformanceMode = performanceMode; + if (static_cast(direction) == Direction::Input) { + paOboe_inputPerformanceMode = static_cast(performanceMode); } else { - paOboe_outputPerformanceMode = performanceMode; + paOboe_outputPerformanceMode = static_cast(performanceMode); } } From afb62bf5a0a154062dcab1829b76822b37a747ee Mon Sep 17 00:00:00 2001 From: Carlo Benfatti <116260612+hopefulGiupplo@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:26:57 +0200 Subject: [PATCH 125/125] Removed typo in portaudio.h --- include/portaudio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/portaudio.h b/include/portaudio.h index bf0f0ea2e..923ee4990 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -292,7 +292,7 @@ typedef enum PaHostApiTypeId paAudioScienceHPI=14, paAudioIO=15, paPulseAudio=16, - paSndio=17 + paSndio=17, paOboe=18 } PaHostApiTypeId;