From 520a275cffc43b55e170ed37175fe6bd46a97f5e Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 3 Oct 2025 14:50:08 -0700 Subject: [PATCH] Remove audio samples in favor of Oboe's repo. We shouldn't be recommending OpenSLES any more, and we don't need Oboe samples because Oboe's own samples are much more thorough than ours will ever be. --- audio-echo/README.md | 79 +---- audio-echo/app/build.gradle | 31 -- audio-echo/app/src/main/AndroidManifest.xml | 24 -- audio-echo/app/src/main/cpp/CMakeLists.txt | 39 --- audio-echo/app/src/main/cpp/android_debug.h | 47 --- audio-echo/app/src/main/cpp/audio_common.cpp | 66 ---- audio-echo/app/src/main/cpp/audio_common.h | 79 ----- audio-echo/app/src/main/cpp/audio_effect.cpp | 171 ---------- audio-echo/app/src/main/cpp/audio_effect.h | 67 ---- audio-echo/app/src/main/cpp/audio_main.cpp | 246 -------------- audio-echo/app/src/main/cpp/audio_player.cpp | 261 --------------- audio-echo/app/src/main/cpp/audio_player.h | 56 ---- .../app/src/main/cpp/audio_recorder.cpp | 214 ------------ audio-echo/app/src/main/cpp/audio_recorder.h | 56 ---- audio-echo/app/src/main/cpp/buf_manager.h | 170 ---------- audio-echo/app/src/main/cpp/debug_utils.cpp | 106 ------ audio-echo/app/src/main/cpp/debug_utils.h | 61 ---- audio-echo/app/src/main/cpp/jni.cpp | 37 --- audio-echo/app/src/main/cpp/jni_interface.h | 31 -- audio-echo/app/src/main/cpp/libecho.map.txt | 6 - .../com/google/sample/echo/MainActivity.java | 304 ------------------ .../app/src/main/res/layout/activity_main.xml | 109 ------- .../app/src/main/res/menu/menu_main.xml | 7 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3418 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2206 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4842 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7718 -> 0 bytes .../app/src/main/res/values-v21/styles.xml | 5 - .../app/src/main/res/values-w820dp/dimens.xml | 6 - audio-echo/app/src/main/res/values/dimens.xml | 5 - .../app/src/main/res/values/strings.xml | 24 -- audio-echo/app/src/main/res/values/styles.xml | 8 - hello-oboe/.gitignore | 15 - hello-oboe/README.md | 30 +- hello-oboe/app/build.gradle | 40 --- hello-oboe/app/src/main/AndroidManifest.xml | 21 -- hello-oboe/app/src/main/cpp/CMakeLists.txt | 33 -- hello-oboe/app/src/main/cpp/OboeSinePlayer.h | 94 ------ hello-oboe/app/src/main/cpp/hello-oboe.cpp | 73 ----- .../app/src/main/cpp/libhello-oboe.map.txt | 6 - .../google/example/hellooboe/MainActivity.kt | 81 ----- .../drawable-v24/ic_launcher_foreground.xml | 34 -- .../res/drawable/ic_launcher_background.xml | 170 ---------- .../app/src/main/res/layout/activity_main.xml | 20 -- .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 2963 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 4905 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2060 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2783 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4490 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 6895 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 6387 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 10413 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 9128 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 15132 -> 0 bytes hello-oboe/app/src/main/res/values/colors.xml | 6 - .../app/src/main/res/values/strings.xml | 6 - hello-oboe/app/src/main/res/values/styles.xml | 11 - hello-oboe/screenshot.png | Bin 31650 -> 0 bytes settings.gradle | 2 - 61 files changed, 8 insertions(+), 2959 deletions(-) delete mode 100644 audio-echo/app/build.gradle delete mode 100644 audio-echo/app/src/main/AndroidManifest.xml delete mode 100644 audio-echo/app/src/main/cpp/CMakeLists.txt delete mode 100644 audio-echo/app/src/main/cpp/android_debug.h delete mode 100644 audio-echo/app/src/main/cpp/audio_common.cpp delete mode 100644 audio-echo/app/src/main/cpp/audio_common.h delete mode 100644 audio-echo/app/src/main/cpp/audio_effect.cpp delete mode 100644 audio-echo/app/src/main/cpp/audio_effect.h delete mode 100644 audio-echo/app/src/main/cpp/audio_main.cpp delete mode 100644 audio-echo/app/src/main/cpp/audio_player.cpp delete mode 100644 audio-echo/app/src/main/cpp/audio_player.h delete mode 100644 audio-echo/app/src/main/cpp/audio_recorder.cpp delete mode 100644 audio-echo/app/src/main/cpp/audio_recorder.h delete mode 100644 audio-echo/app/src/main/cpp/buf_manager.h delete mode 100644 audio-echo/app/src/main/cpp/debug_utils.cpp delete mode 100644 audio-echo/app/src/main/cpp/debug_utils.h delete mode 100644 audio-echo/app/src/main/cpp/jni.cpp delete mode 100644 audio-echo/app/src/main/cpp/jni_interface.h delete mode 100644 audio-echo/app/src/main/cpp/libecho.map.txt delete mode 100644 audio-echo/app/src/main/java/com/google/sample/echo/MainActivity.java delete mode 100644 audio-echo/app/src/main/res/layout/activity_main.xml delete mode 100644 audio-echo/app/src/main/res/menu/menu_main.xml delete mode 100644 audio-echo/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 audio-echo/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 audio-echo/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 audio-echo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 audio-echo/app/src/main/res/values-v21/styles.xml delete mode 100644 audio-echo/app/src/main/res/values-w820dp/dimens.xml delete mode 100644 audio-echo/app/src/main/res/values/dimens.xml delete mode 100644 audio-echo/app/src/main/res/values/strings.xml delete mode 100644 audio-echo/app/src/main/res/values/styles.xml delete mode 100644 hello-oboe/.gitignore delete mode 100644 hello-oboe/app/build.gradle delete mode 100644 hello-oboe/app/src/main/AndroidManifest.xml delete mode 100644 hello-oboe/app/src/main/cpp/CMakeLists.txt delete mode 100644 hello-oboe/app/src/main/cpp/OboeSinePlayer.h delete mode 100644 hello-oboe/app/src/main/cpp/hello-oboe.cpp delete mode 100644 hello-oboe/app/src/main/cpp/libhello-oboe.map.txt delete mode 100644 hello-oboe/app/src/main/java/com/google/example/hellooboe/MainActivity.kt delete mode 100644 hello-oboe/app/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 hello-oboe/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 hello-oboe/app/src/main/res/layout/activity_main.xml delete mode 100644 hello-oboe/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 hello-oboe/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 hello-oboe/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 hello-oboe/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 hello-oboe/app/src/main/res/values/colors.xml delete mode 100644 hello-oboe/app/src/main/res/values/strings.xml delete mode 100644 hello-oboe/app/src/main/res/values/styles.xml delete mode 100755 hello-oboe/screenshot.png diff --git a/audio-echo/README.md b/audio-echo/README.md index 67bae273e..0ebe5cc35 100644 --- a/audio-echo/README.md +++ b/audio-echo/README.md @@ -1,77 +1,6 @@ -# Audio-Echo +# Sample removed -The sample demos how to use OpenSL ES to create a player and recorder in Android -Fast Audio Path, and connect them to loopback audio. On most android devices, -there is a optimized audio path that is tuned up for low latency purpose. The -sample creates player/recorder to work in this highly optimized audio -path(sometimes called native audio path, -[low latency path](http://stackoverflow.com/questions/14842803/low-latency-audio-playback-on-android?rq=1), -or fast audio path). +This sample has been removed because the API it demonstrated (OpenSLES) is +deprecated. New apps should instead use [Oboe], which has its own samples. -***Note that OpenSL ES is -[deprecated from Android 11](https://developer.android.com/preview/features#deprecate-opensl), -developers are recommended to use [Oboe](https://github.com/google/oboe) library -instead.*** - -## Usage - -App will capture audio from android devices and playback on the same device; the -playback on speaker will be captured immediately and played back...! So to -verify it, it is recommended to "mute" the playback audio with a -earspeaker/earphone/earbug so it does not get looped back. Some device like -Nexus 9, once you plug in an external headphone/headspeaker, it stops to use -onboard microphone AND speaker anymore -- in this case, you need turn on the -microphone coming with your headphone. Another point, when switching between -external headphone and internal one, the volume is sometimes very low/muted; -recommend to increase the playback volume with volume buttons on the phone/pad -after plugging external headphone. - -## Low Latency Verification - -1. execute "adb shell dumpsys media.audio_flinger". Find a list of the running - processes - - Name Active Client Type Fmt Chn mask Session fCount S F SRate L dB R dB - Server Main buf Aux Buf Flags UndFrmCnt\ - F 2 no 704 1 00000001 00000003 562 - 13248 S 1 48000 -inf -inf 000033C0 0xabab8480 0x0 0x600 0\ - F 6 yes 9345 3 - 00000001 00000001 576 128 A 1 48000 0 0 0376AA00 0xabab8480 0x0 0x400 256 - -1. execute adb shell ps | grep echo - -- find the sample app pid -- check with result on step 1.\ - if there is one "F" in the front of your echo - pid, **player** is on fast audio path\ - For fast audio capture \[it is totally - different story\], if you do **NOT** see\ - com.example.nativeaudio - W/AudioRecordīš• AUDIO_INPUT_FLAG_FAST denied by client\ - in your logcat output - when you are creating audio recorder, you could "assume" you are on the fast - path.\ - If your system image was built with muted ALOGW, you will not be able - to see the above warning message. - -## Tune-ups - -A couple of knobs in the code for lower latency purpose: - -- audio buffer size -- number of audio buffers cached before kicking start player The lower you go - with them, the lower latency you get and also the lower budget for audio - processing. All audio processing has to be completed in the time period they - are captured / played back, plus extra time needed for: -- audio driver -- audio flinger framework, -- bufferqueue callbacks etc Besides those, the irregularity of the buffer queue - player/capture callback time is another factor. The callback from openSL may - not as regular as you assumed, the more irregularity it is, the more likely - have choopy audio. To fight that, more buffering is needed, which defeats the - low-latency purpose! The low latency path is highly tuned up so you have - better chance to get more regular callbacks. You may experiment with your - platform to find the best parameters for lower latency and continuously - playback audio experience. The app capture and playback on the same device - \[most of times the same chip\], capture and playback clocks are assumed - synchronized naturally \[so we are not dealing with it\] +[Oboe]: https://github.com/google/oboe diff --git a/audio-echo/app/build.gradle b/audio-echo/app/build.gradle deleted file mode 100644 index efc5a7554..000000000 --- a/audio-echo/app/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - id "ndksamples.android.application" -} - -android { - defaultConfig { - applicationId 'com.google.sample.echo' - versionCode 1 - versionName '1.0' - externalNativeBuild { - cmake { - arguments '-DANDROID_STL=c++_static' - } - } - } - externalNativeBuild { - cmake { - path 'src/main/cpp/CMakeLists.txt' - } - } - namespace 'com.google.sample.echo' - - buildFeatures { - prefab true - } -} - -dependencies { - implementation libs.appcompat - implementation project(":base") -} diff --git a/audio-echo/app/src/main/AndroidManifest.xml b/audio-echo/app/src/main/AndroidManifest.xml deleted file mode 100644 index cac51786f..000000000 --- a/audio-echo/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - diff --git a/audio-echo/app/src/main/cpp/CMakeLists.txt b/audio-echo/app/src/main/cpp/CMakeLists.txt deleted file mode 100644 index a71365f83..000000000 --- a/audio-echo/app/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -cmake_minimum_required(VERSION 3.22.1) -project(echo LANGUAGES C CXX) - -include(AppLibrary) -find_package(base CONFIG REQUIRED) - -add_app_library(echo - SHARED - audio_main.cpp - audio_player.cpp - audio_recorder.cpp - audio_effect.cpp - audio_common.cpp - debug_utils.cpp - jni.cpp -) - -target_link_libraries(echo - PRIVATE - base::base - OpenSLES - android - log - atomic -) - -if(ANDROID_ABI STREQUAL riscv64) - # This sample uses OpenSLES, which was deprecated in API 26. Our - # minSdkVersion is 21, but we also build for riscv64, which isn't a - # supported ABI yet and so that configuration is built for the latest API - # level supported by the NDK. - # - # Longer term, this sample should probably be deleted. App developers - # should be using Oboe rather than OpenSLES, and we already have a separate - # sample for Oboe. There is some potentially useful audio latency - # measurement in this sample that should be moved to the Oboe sample before - # we delete it though. - target_compile_options(echo PRIVATE -Wno-deprecated-declarations) -endif() diff --git a/audio-echo/app/src/main/cpp/android_debug.h b/audio-echo/app/src/main/cpp/android_debug.h deleted file mode 100644 index 8ea56c00b..000000000 --- a/audio-echo/app/src/main/cpp/android_debug.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#ifndef NATIVE_AUDIO_ANDROID_DEBUG_H_H -#define NATIVE_AUDIO_ANDROID_DEBUG_H_H -#include - -#if 1 - -#define MODULE_NAME "AUDIO-ECHO" -#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__) - -#else - -#define LOGV(...) -#define LOGD(...) -#define LOGI(...) -#define LOGW(...) -#define LOGE(...) -#define LOGF(...) -#endif - -#endif // NATIVE_AUDIO_ANDROID_DEBUG_H_H diff --git a/audio-echo/app/src/main/cpp/audio_common.cpp b/audio-echo/app/src/main/cpp/audio_common.cpp deleted file mode 100644 index f56f39774..000000000 --- a/audio-echo/app/src/main/cpp/audio_common.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "audio_common.h" - -void ConvertToSLSampleFormat(SLAndroidDataFormat_PCM_EX* pFormat, - SampleFormat* pSampleInfo_) { - assert(pFormat); - memset(pFormat, 0, sizeof(*pFormat)); - - pFormat->formatType = SL_DATAFORMAT_PCM; - // Only support 2 channels - // For channelMask, refer to wilhelm/src/android/channels.c for details - if (pSampleInfo_->channels_ <= 1) { - pFormat->numChannels = 1; - pFormat->channelMask = SL_SPEAKER_FRONT_LEFT; - } else { - pFormat->numChannels = 2; - pFormat->channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - } - pFormat->sampleRate = pSampleInfo_->sampleRate_; - - pFormat->endianness = SL_BYTEORDER_LITTLEENDIAN; - pFormat->bitsPerSample = pSampleInfo_->pcmFormat_; - pFormat->containerSize = pSampleInfo_->pcmFormat_; - - /* - * fixup for android extended representations... - */ - pFormat->representation = pSampleInfo_->representation_; - switch (pFormat->representation) { - case SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT: - pFormat->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8; - pFormat->containerSize = SL_PCMSAMPLEFORMAT_FIXED_8; - pFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - break; - case SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT: - pFormat->bitsPerSample = - SL_PCMSAMPLEFORMAT_FIXED_16; // supports 16, 24, and 32 - pFormat->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - pFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - break; - case SL_ANDROID_PCM_REPRESENTATION_FLOAT: - pFormat->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; - pFormat->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32; - pFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - break; - case 0: - break; - default: - assert(0); - } -} diff --git a/audio-echo/app/src/main/cpp/audio_common.h b/audio-echo/app/src/main/cpp/audio_common.h deleted file mode 100644 index 88232b8dd..000000000 --- a/audio-echo/app/src/main/cpp/audio_common.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef NATIVE_AUDIO_AUDIO_COMMON_H -#define NATIVE_AUDIO_AUDIO_COMMON_H - -#include -#include - -#include "android_debug.h" -#include "buf_manager.h" -#include "debug_utils.h" - -/* - * Audio Sample Controls... - */ -#define AUDIO_SAMPLE_CHANNELS 1 - -/* - * Sample Buffer Controls... - */ -#define RECORD_DEVICE_KICKSTART_BUF_COUNT 2 -#define PLAY_KICKSTART_BUFFER_COUNT 3 -#define DEVICE_SHADOW_BUFFER_QUEUE_LEN 4 -#define BUF_COUNT 16 - -struct SampleFormat { - uint32_t sampleRate_; - uint32_t framesPerBuf_; - uint16_t channels_; - uint16_t pcmFormat_; // 8 bit, 16 bit, 24 bit ... - uint32_t representation_; // android extensions -}; -extern void ConvertToSLSampleFormat(SLAndroidDataFormat_PCM_EX* pFormat, - SampleFormat* format); - -/* - * GetSystemTicks(void): return the time in micro sec - */ -__inline__ uint64_t GetSystemTicks(void) { - struct timeval Time; - gettimeofday(&Time, NULL); - - return (static_cast(1000000) * Time.tv_sec + Time.tv_usec); -} - -#define SLASSERT(x) \ - do { \ - assert(SL_RESULT_SUCCESS == (x)); \ - (void)(x); \ - } while (0) - -/* - * Interface for player and recorder to communicate with engine - */ -#define ENGINE_SERVICE_MSG_KICKSTART_PLAYER 1 -#define ENGINE_SERVICE_MSG_RETRIEVE_DUMP_BUFS 2 -#define ENGINE_SERVICE_MSG_RECORDED_AUDIO_AVAILABLE 3 -typedef bool (*ENGINE_CALLBACK)(void* pCTX, uint32_t msg, void* pData); - -/* - * flag to enable file dumping - */ -// #define ENABLE_LOG 1 - -#endif // NATIVE_AUDIO_AUDIO_COMMON_H diff --git a/audio-echo/app/src/main/cpp/audio_effect.cpp b/audio-echo/app/src/main/cpp/audio_effect.cpp deleted file mode 100644 index 903a9ee8a..000000000 --- a/audio-echo/app/src/main/cpp/audio_effect.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "audio_effect.h" - -#include -#include - -#include "audio_common.h" - -/* - * Mixing Audio in integer domain to avoid FP calculation - * (FG * ( MixFactor * 16 ) + BG * ( (1.0f-MixFactor) * 16 )) / 16 - */ -static const int32_t kFloatToIntMapFactor = 128; -static const uint32_t kMsPerSec = 1000; -/** - * Constructor for AudioDelay - * @param sampleRate - * @param channelCount - * @param format - * @param delayTimeInMs - */ -AudioDelay::AudioDelay(int32_t sampleRate, int32_t channelCount, - SLuint32 format, size_t delayTimeInMs, float decayWeight) - : AudioFormat(sampleRate, channelCount, format), - delayTime_(delayTimeInMs), - decayWeight_(decayWeight) { - feedbackFactor_ = static_cast(decayWeight_ * kFloatToIntMapFactor); - liveAudioFactor_ = kFloatToIntMapFactor - feedbackFactor_; - allocateBuffer(); -} - -/** - * Destructor - */ -AudioDelay::~AudioDelay() { - if (buffer_) delete static_cast(buffer_); -} - -/** - * Configure for delay time ( in miliseconds ), dynamically adjustable - * @param delayTimeInMS in miliseconds - * @return true if delay time is set successfully - */ -bool AudioDelay::setDelayTime(size_t delayTimeInMS) { - if (delayTimeInMS == delayTime_) return true; - - std::lock_guard lock(lock_); - - if (buffer_) { - delete static_cast(buffer_); - buffer_ = nullptr; - } - - delayTime_ = delayTimeInMS; - allocateBuffer(); - return buffer_ != nullptr; -} - -/** - * Internal helper function to allocate buffer for the delay - * - calculate the buffer size for the delay time - * - allocate and zero out buffer (0 means silent audio) - * - configure bufSize_ to be size of audioFrames - */ -void AudioDelay::allocateBuffer(void) { - float floatDelayTime = (float)delayTime_ / kMsPerSec; - float fNumFrames = floatDelayTime * (float)sampleRate_ / kMsPerSec; - size_t sampleCount = static_cast(fNumFrames + 0.5f) * channelCount_; - - uint32_t bytePerSample = format_ / 8; - assert(bytePerSample <= 4 && bytePerSample); - - uint32_t bytePerFrame = channelCount_ * bytePerSample; - - // get bufCapacity in bytes - bufCapacity_ = sampleCount * bytePerSample; - bufCapacity_ = - ((bufCapacity_ + bytePerFrame - 1) / bytePerFrame) * bytePerFrame; - - buffer_ = new uint8_t[bufCapacity_]; - assert(buffer_); - - memset(buffer_, 0, bufCapacity_); - curPos_ = 0; - - // bufSize_ is in Frames ( not samples, not bytes ) - bufSize_ = bufCapacity_ / bytePerFrame; -} - -size_t AudioDelay::getDelayTime(void) const { return delayTime_; } - -/** - * setDecayWeight(): set the decay factor - * ratio: value of 0.0 -- 1.0f; - * - * the calculation is in integer ( not in float ) - * for performance purpose - */ -void AudioDelay::setDecayWeight(float weight) { - if (weight > 0.0f && weight < 1.0f) { - float feedback = (weight * kFloatToIntMapFactor + 0.5f); - feedbackFactor_ = static_cast(feedback); - liveAudioFactor_ = kFloatToIntMapFactor - feedbackFactor_; - } -} - -float AudioDelay::getDecayWeight(void) const { return decayWeight_; } - -/** - * process() filter live audio with "echo" effect: - * delay time is run-time adjustable - * decay time could also be adjustable, but not used - * in this sample, hardcoded to .5 - * - * @param liveAudio is recorded audio stream - * @param channelCount for liveAudio, must be 2 for stereo - * @param numFrames is length of liveAudio in Frames ( not in byte ) - */ -void AudioDelay::process(int16_t* liveAudio, uint32_t numFrames) { - if (feedbackFactor_ == 0 || bufSize_ < numFrames) { - return; - } - - if (!lock_.try_lock()) { - return; - } - - if (numFrames + curPos_ > bufSize_) { - curPos_ = 0; - } - - // process every sample - auto sampleCount = channelCount_ * numFrames; - int16_t* samples = &static_cast(buffer_)[curPos_ * channelCount_]; - for (size_t idx = 0; idx < sampleCount; idx++) { -#if 1 - int32_t curSample = - (samples[idx] * feedbackFactor_ + liveAudio[idx] * liveAudioFactor_) / - kFloatToIntMapFactor; - if (curSample > SHRT_MAX) - curSample = SHRT_MAX; - else if (curSample < SHRT_MIN) - curSample = SHRT_MIN; - - liveAudio[idx] = samples[idx]; - samples[idx] = static_cast(curSample); -#else - // Pure delay - int16_t tmp = liveAudio[idx]; - liveAudio[idx] = samples[idx]; - samples[idx] = tmp; -#endif - } - - curPos_ += numFrames; - lock_.unlock(); -} diff --git a/audio-echo/app/src/main/cpp/audio_effect.h b/audio-echo/app/src/main/cpp/audio_effect.h deleted file mode 100644 index 98b1d05c8..000000000 --- a/audio-echo/app/src/main/cpp/audio_effect.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef EFFECT_PROCESSOR_H -#define EFFECT_PROCESSOR_H - -#include - -#include -#include -#include - -class AudioFormat { - protected: - int32_t sampleRate_ = SL_SAMPLINGRATE_48; - int32_t channelCount_ = 2; - SLuint32 format_ = SL_PCMSAMPLEFORMAT_FIXED_16; - - AudioFormat(int32_t sampleRate, int32_t channelCount, SLuint32 format) - : sampleRate_(sampleRate), channelCount_(channelCount), format_(format){}; - - virtual ~AudioFormat() {} -}; - -/** - * An audio delay effect: - * - decay is for feedback(echo)weight - * - delay time is adjustable - */ -class AudioDelay : public AudioFormat { - public: - ~AudioDelay(); - - explicit AudioDelay(int32_t sampleRate, int32_t channelCount, SLuint32 format, - size_t delayTimeInMs, float Weight); - bool setDelayTime(size_t delayTimeInMiliSec); - size_t getDelayTime(void) const; - void setDecayWeight(float weight); - float getDecayWeight(void) const; - void process(int16_t* liveAudio, uint32_t numFrames); - - private: - size_t delayTime_ = 0; - float decayWeight_ = 0.5; - void* buffer_ = nullptr; - size_t bufCapacity_ = 0; - size_t bufSize_ = 0; - size_t curPos_ = 0; - std::mutex lock_; - int32_t feedbackFactor_; - int32_t liveAudioFactor_; - void allocateBuffer(void); -}; -#endif // EFFECT_PROCESSOR_H diff --git a/audio-echo/app/src/main/cpp/audio_main.cpp b/audio-echo/app/src/main/cpp/audio_main.cpp deleted file mode 100644 index 43d0b54db..000000000 --- a/audio-echo/app/src/main/cpp/audio_main.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include -#include - -#include "audio_common.h" -#include "audio_effect.h" -#include "audio_player.h" -#include "audio_recorder.h" -#include "jni_interface.h" - -struct EchoAudioEngine { - SLmilliHertz fastPathSampleRate_; - uint32_t fastPathFramesPerBuf_; - uint16_t sampleChannels_; - uint16_t bitsPerSample_; - - SLObjectItf slEngineObj_; - SLEngineItf slEngineItf_; - - AudioRecorder* recorder_; - AudioPlayer* player_; - AudioQueue* freeBufQueue_; // Owner of the queue - AudioQueue* recBufQueue_; // Owner of the queue - - sample_buf* bufs_; - uint32_t bufCount_; - uint32_t frameCount_; - int64_t echoDelay_; - float echoDecay_; - AudioDelay* delayEffect_; -}; -static EchoAudioEngine engine; - -bool EngineService(void* ctx, uint32_t msg, void* data); - -void CreateSlEngine(JNIEnv*, jclass, jint sampleRate, jint framesPerBuf, - jlong delayInMs, jfloat decay) { - SLresult result; - memset(&engine, 0, sizeof(engine)); - - engine.fastPathSampleRate_ = static_cast(sampleRate) * 1000; - engine.fastPathFramesPerBuf_ = static_cast(framesPerBuf); - engine.sampleChannels_ = AUDIO_SAMPLE_CHANNELS; - engine.bitsPerSample_ = SL_PCMSAMPLEFORMAT_FIXED_16; - - result = slCreateEngine(&engine.slEngineObj_, 0, NULL, 0, NULL, NULL); - SLASSERT(result); - - result = - (*engine.slEngineObj_)->Realize(engine.slEngineObj_, SL_BOOLEAN_FALSE); - SLASSERT(result); - - result = (*engine.slEngineObj_) - ->GetInterface(engine.slEngineObj_, SL_IID_ENGINE, - &engine.slEngineItf_); - SLASSERT(result); - - // compute the RECOMMENDED fast audio buffer size: - // the lower latency required - // *) the smaller the buffer should be (adjust it here) AND - // *) the less buffering should be before starting player AFTER - // receiving the recorder buffer - // Adjust the bufSize here to fit your bill [before it busts] - uint32_t bufSize = engine.fastPathFramesPerBuf_ * engine.sampleChannels_ * - engine.bitsPerSample_; - bufSize = (bufSize + 7) >> 3; // bits --> byte - engine.bufCount_ = BUF_COUNT; - engine.bufs_ = allocateSampleBufs(engine.bufCount_, bufSize); - assert(engine.bufs_); - - engine.freeBufQueue_ = new AudioQueue(engine.bufCount_); - engine.recBufQueue_ = new AudioQueue(engine.bufCount_); - assert(engine.freeBufQueue_ && engine.recBufQueue_); - for (uint32_t i = 0; i < engine.bufCount_; i++) { - engine.freeBufQueue_->push(&engine.bufs_[i]); - } - - engine.echoDelay_ = delayInMs; - engine.echoDecay_ = decay; - engine.delayEffect_ = new AudioDelay( - engine.fastPathSampleRate_, engine.sampleChannels_, engine.bitsPerSample_, - engine.echoDelay_, engine.echoDecay_); - assert(engine.delayEffect_); -} - -jboolean ConfigureEcho(JNIEnv*, jclass, jint delayInMs, jfloat decay) { - engine.echoDelay_ = delayInMs; - engine.echoDecay_ = decay; - - engine.delayEffect_->setDelayTime(delayInMs); - engine.delayEffect_->setDecayWeight(decay); - return JNI_FALSE; -} - -jboolean CreateSlBufferQueueAudioPlayer(JNIEnv*, jclass) { - SampleFormat sampleFormat; - memset(&sampleFormat, 0, sizeof(sampleFormat)); - sampleFormat.pcmFormat_ = (uint16_t)engine.bitsPerSample_; - sampleFormat.framesPerBuf_ = engine.fastPathFramesPerBuf_; - - // SampleFormat.representation_ = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; - sampleFormat.channels_ = (uint16_t)engine.sampleChannels_; - sampleFormat.sampleRate_ = engine.fastPathSampleRate_; - - engine.player_ = new AudioPlayer(&sampleFormat, engine.slEngineItf_); - assert(engine.player_); - if (engine.player_ == nullptr) return JNI_FALSE; - - engine.player_->SetBufQueue(engine.recBufQueue_, engine.freeBufQueue_); - engine.player_->RegisterCallback(EngineService, (void*)&engine); - - return JNI_TRUE; -} - -void DeleteSlBufferQueueAudioPlayer(JNIEnv*, jclass) { - if (engine.player_) { - delete engine.player_; - engine.player_ = nullptr; - } -} - -jboolean CreateAudioRecorder(JNIEnv*, jclass) { - SampleFormat sampleFormat; - memset(&sampleFormat, 0, sizeof(sampleFormat)); - sampleFormat.pcmFormat_ = static_cast(engine.bitsPerSample_); - - // SampleFormat.representation_ = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; - sampleFormat.channels_ = engine.sampleChannels_; - sampleFormat.sampleRate_ = engine.fastPathSampleRate_; - sampleFormat.framesPerBuf_ = engine.fastPathFramesPerBuf_; - engine.recorder_ = new AudioRecorder(&sampleFormat, engine.slEngineItf_); - if (!engine.recorder_) { - return JNI_FALSE; - } - engine.recorder_->SetBufQueues(engine.freeBufQueue_, engine.recBufQueue_); - engine.recorder_->RegisterCallback(EngineService, (void*)&engine); - return JNI_TRUE; -} - -void DeleteAudioRecorder(JNIEnv*, jclass) { - if (engine.recorder_) delete engine.recorder_; - - engine.recorder_ = nullptr; -} - -void StartPlay(JNIEnv*, jclass) { - engine.frameCount_ = 0; - /* - * start player: make it into waitForData state - */ - if (SL_BOOLEAN_FALSE == engine.player_->Start()) { - LOGE("====%s failed", __FUNCTION__); - return; - } - engine.recorder_->Start(); -} - -void StopPlay(JNIEnv*, jclass) { - engine.recorder_->Stop(); - engine.player_->Stop(); - - delete engine.recorder_; - delete engine.player_; - engine.recorder_ = NULL; - engine.player_ = NULL; -} - -void DeleteSlEngine(JNIEnv*, jclass) { - delete engine.recBufQueue_; - delete engine.freeBufQueue_; - releaseSampleBufs(engine.bufs_, engine.bufCount_); - if (engine.slEngineObj_ != NULL) { - (*engine.slEngineObj_)->Destroy(engine.slEngineObj_); - engine.slEngineObj_ = NULL; - engine.slEngineItf_ = NULL; - } - - if (engine.delayEffect_) { - delete engine.delayEffect_; - engine.delayEffect_ = nullptr; - } -} - -uint32_t dbgEngineGetBufCount(void) { - uint32_t count = engine.player_->dbgGetDevBufCount(); - count += engine.recorder_->dbgGetDevBufCount(); - count += engine.freeBufQueue_->size(); - count += engine.recBufQueue_->size(); - - LOGE( - "Buf Disrtibutions: PlayerDev=%d, RecDev=%d, FreeQ=%d, " - "RecQ=%d", - engine.player_->dbgGetDevBufCount(), - engine.recorder_->dbgGetDevBufCount(), engine.freeBufQueue_->size(), - engine.recBufQueue_->size()); - if (count != engine.bufCount_) { - LOGE("====Lost Bufs among the queue(supposed = %d, found = %d)", BUF_COUNT, - count); - } - return count; -} - -/* - * simple message passing for player/recorder to communicate with engine - */ -bool EngineService([[maybe_unused]] void* ctx, uint32_t msg, void* data) { - assert(ctx == &engine); - switch (msg) { - case ENGINE_SERVICE_MSG_RETRIEVE_DUMP_BUFS: { - *(static_cast(data)) = dbgEngineGetBufCount(); - break; - } - case ENGINE_SERVICE_MSG_RECORDED_AUDIO_AVAILABLE: { - // adding audio delay effect - sample_buf* buf = static_cast(data); - assert(engine.fastPathFramesPerBuf_ == - buf->size_ / engine.sampleChannels_ / (engine.bitsPerSample_ / 8)); - engine.delayEffect_->process(reinterpret_cast(buf->buf_), - engine.fastPathFramesPerBuf_); - break; - } - default: - assert(false); - return false; - } - - return true; -} diff --git a/audio-echo/app/src/main/cpp/audio_player.cpp b/audio-echo/app/src/main/cpp/audio_player.cpp deleted file mode 100644 index 259679fdc..000000000 --- a/audio-echo/app/src/main/cpp/audio_player.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "audio_player.h" - -#include - -/* - * Called by OpenSL SimpleBufferQueue for every audio buffer played - * directly pass thru to our handler. - * The regularity of this callback from openSL/Android System affects - * playback continuity. If it does not callback in the regular time - * slot, you are under big pressure for audio processing[here we do - * not do any filtering/mixing]. Callback from fast audio path are - * much more regular than other audio paths by my observation. If it - * very regular, you could buffer much less audio samples between - * recorder and player, hence lower latency. - */ -void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* ctx) { - (static_cast(ctx))->ProcessSLCallback(bq); -} -void AudioPlayer::ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq) { -#ifdef ENABLE_LOG - logFile_->logTime(); -#endif - std::lock_guard lock(stopMutex_); - - // retrieve the finished device buf and put onto the free queue - // so recorder could re-use it - sample_buf* buf; - if (!devShadowQueue_->front(&buf)) { - /* - * This should not happen: we got a callback, - * but we have no buffer in deviceShadowedQueue - * we lost buffers this way...(ERROR) - */ - if (callback_) { - uint32_t count; - callback_(ctx_, ENGINE_SERVICE_MSG_RETRIEVE_DUMP_BUFS, &count); - } - return; - } - devShadowQueue_->pop(); - - if (buf != &silentBuf_) { - buf->size_ = 0; - freeQueue_->push(buf); - - if (!playQueue_->front(&buf)) { -#ifdef ENABLE_LOG - logFile_->log("%s", "====Warning: running out of the Audio buffers"); -#endif - return; - } - - devShadowQueue_->push(buf); - (*bq)->Enqueue(bq, buf->buf_, buf->size_); - playQueue_->pop(); - return; - } - - if (playQueue_->size() < PLAY_KICKSTART_BUFFER_COUNT) { - (*bq)->Enqueue(bq, buf->buf_, buf->size_); - devShadowQueue_->push(&silentBuf_); - return; - } - - assert(PLAY_KICKSTART_BUFFER_COUNT <= - (DEVICE_SHADOW_BUFFER_QUEUE_LEN - devShadowQueue_->size())); - for (int32_t idx = 0; idx < PLAY_KICKSTART_BUFFER_COUNT; idx++) { - playQueue_->front(&buf); - playQueue_->pop(); - devShadowQueue_->push(buf); - (*bq)->Enqueue(bq, buf->buf_, buf->size_); - } -} - -AudioPlayer::AudioPlayer(SampleFormat* sampleFormat, SLEngineItf slEngine) - : freeQueue_(nullptr), - playQueue_(nullptr), - devShadowQueue_(nullptr), - callback_(nullptr) { - SLresult result; - assert(sampleFormat); - sampleInfo_ = *sampleFormat; - - result = (*slEngine)->CreateOutputMix(slEngine, &outputMixObjectItf_, 0, NULL, - NULL); - SLASSERT(result); - - // realize the output mix - result = - (*outputMixObjectItf_)->Realize(outputMixObjectItf_, SL_BOOLEAN_FALSE); - SLASSERT(result); - - // configure audio source - SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { - SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, DEVICE_SHADOW_BUFFER_QUEUE_LEN}; - - SLAndroidDataFormat_PCM_EX format_pcm; - ConvertToSLSampleFormat(&format_pcm, &sampleInfo_); - SLDataSource audioSrc = {&loc_bufq, &format_pcm}; - - // configure audio sink - SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, - outputMixObjectItf_}; - SLDataSink audioSnk = {&loc_outmix, NULL}; - /* - * create fast path audio player: SL_IID_BUFFERQUEUE and SL_IID_VOLUME - * and other non-signal processing interfaces are ok. - */ - SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME}; - SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; - result = (*slEngine)->CreateAudioPlayer( - slEngine, &playerObjectItf_, &audioSrc, &audioSnk, - sizeof(ids) / sizeof(ids[0]), ids, req); - SLASSERT(result); - - // realize the player - result = (*playerObjectItf_)->Realize(playerObjectItf_, SL_BOOLEAN_FALSE); - SLASSERT(result); - - // get the play interface - result = (*playerObjectItf_) - ->GetInterface(playerObjectItf_, SL_IID_PLAY, &playItf_); - SLASSERT(result); - - // get the buffer queue interface - result = (*playerObjectItf_) - ->GetInterface(playerObjectItf_, SL_IID_BUFFERQUEUE, - &playBufferQueueItf_); - SLASSERT(result); - - // register callback on the buffer queue - result = (*playBufferQueueItf_) - ->RegisterCallback(playBufferQueueItf_, bqPlayerCallback, this); - SLASSERT(result); - - result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED); - SLASSERT(result); - - // create an empty queue to track deviceQueue - devShadowQueue_ = new AudioQueue(DEVICE_SHADOW_BUFFER_QUEUE_LEN); - assert(devShadowQueue_); - - silentBuf_.cap_ = (format_pcm.containerSize >> 3) * format_pcm.numChannels * - sampleInfo_.framesPerBuf_; - silentBuf_.buf_ = new uint8_t[silentBuf_.cap_]; - memset(silentBuf_.buf_, 0, silentBuf_.cap_); - silentBuf_.size_ = silentBuf_.cap_; - -#ifdef ENABLE_LOG - std::string name = "play"; - logFile_ = new AndroidLog(name); -#endif -} - -AudioPlayer::~AudioPlayer() { - std::lock_guard lock(stopMutex_); - - // destroy buffer queue audio player object, and invalidate all associated - // interfaces - if (playerObjectItf_ != NULL) { - (*playerObjectItf_)->Destroy(playerObjectItf_); - } - // Consume all non-completed audio buffers - sample_buf* buf = NULL; - while (devShadowQueue_->front(&buf)) { - buf->size_ = 0; - devShadowQueue_->pop(); - if (buf != &silentBuf_) { - freeQueue_->push(buf); - } - } - delete devShadowQueue_; - - while (playQueue_->front(&buf)) { - buf->size_ = 0; - playQueue_->pop(); - freeQueue_->push(buf); - } - - // destroy output mix object, and invalidate all associated interfaces - if (outputMixObjectItf_) { - (*outputMixObjectItf_)->Destroy(outputMixObjectItf_); - } - - delete[] silentBuf_.buf_; -} - -void AudioPlayer::SetBufQueue(AudioQueue* playQ, AudioQueue* freeQ) { - playQueue_ = playQ; - freeQueue_ = freeQ; -} - -SLresult AudioPlayer::Start(void) { - SLuint32 state; - SLresult result = (*playItf_)->GetPlayState(playItf_, &state); - if (result != SL_RESULT_SUCCESS) { - return SL_BOOLEAN_FALSE; - } - if (state == SL_PLAYSTATE_PLAYING) { - return SL_BOOLEAN_TRUE; - } - - result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED); - SLASSERT(result); - - result = - (*playBufferQueueItf_) - ->Enqueue(playBufferQueueItf_, silentBuf_.buf_, silentBuf_.size_); - SLASSERT(result); - devShadowQueue_->push(&silentBuf_); - - result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_PLAYING); - SLASSERT(result); - return SL_BOOLEAN_TRUE; -} - -void AudioPlayer::Stop(void) { - SLuint32 state; - - SLresult result = (*playItf_)->GetPlayState(playItf_, &state); - SLASSERT(result); - - if (state == SL_PLAYSTATE_STOPPED) return; - - std::lock_guard lock(stopMutex_); - - result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED); - SLASSERT(result); - (*playBufferQueueItf_)->Clear(playBufferQueueItf_); - -#ifdef ENABLE_LOG - if (logFile_) { - delete logFile_; - logFile_ = nullptr; - } -#endif -} - -void AudioPlayer::RegisterCallback(ENGINE_CALLBACK cb, void* ctx) { - callback_ = cb; - ctx_ = ctx; -} - -uint32_t AudioPlayer::dbgGetDevBufCount(void) { - return (devShadowQueue_->size()); -} \ No newline at end of file diff --git a/audio-echo/app/src/main/cpp/audio_player.h b/audio-echo/app/src/main/cpp/audio_player.h deleted file mode 100644 index 0fdf523b5..000000000 --- a/audio-echo/app/src/main/cpp/audio_player.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef NATIVE_AUDIO_AUDIO_PLAYER_H -#define NATIVE_AUDIO_AUDIO_PLAYER_H -#include - -#include "audio_common.h" -#include "buf_manager.h" -#include "debug_utils.h" - -class AudioPlayer { - // buffer queue player interfaces - SLObjectItf outputMixObjectItf_; - SLObjectItf playerObjectItf_; - SLPlayItf playItf_; - SLAndroidSimpleBufferQueueItf playBufferQueueItf_; - - SampleFormat sampleInfo_; - AudioQueue* freeQueue_; // user - AudioQueue* playQueue_; // user - AudioQueue* devShadowQueue_; // owner - - ENGINE_CALLBACK callback_; - void* ctx_; - sample_buf silentBuf_; -#ifdef ENABLE_LOG - AndroidLog* logFile_; -#endif - std::mutex stopMutex_; - - public: - explicit AudioPlayer(SampleFormat* sampleFormat, SLEngineItf engine); - ~AudioPlayer(); - void SetBufQueue(AudioQueue* playQ, AudioQueue* freeQ); - SLresult Start(void); - void Stop(void); - void ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq); - uint32_t dbgGetDevBufCount(void); - void RegisterCallback(ENGINE_CALLBACK cb, void* ctx); -}; - -#endif // NATIVE_AUDIO_AUDIO_PLAYER_H diff --git a/audio-echo/app/src/main/cpp/audio_recorder.cpp b/audio-echo/app/src/main/cpp/audio_recorder.cpp deleted file mode 100644 index cf6793c7b..000000000 --- a/audio-echo/app/src/main/cpp/audio_recorder.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "audio_recorder.h" - -#include -#include -/* - * bqRecorderCallback(): called for every buffer is full; - * pass directly to handler - */ -void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* rec) { - (static_cast(rec))->ProcessSLCallback(bq); -} - -void AudioRecorder::ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq) { -#ifdef ENABLE_LOG - recLog_->logTime(); -#endif - assert(bq == recBufQueueItf_); - sample_buf* dataBuf = NULL; - devShadowQueue_->front(&dataBuf); - devShadowQueue_->pop(); - dataBuf->size_ = dataBuf->cap_; // device only calls us when it is really - // full - - callback_(ctx_, ENGINE_SERVICE_MSG_RECORDED_AUDIO_AVAILABLE, dataBuf); - recQueue_->push(dataBuf); - - sample_buf* freeBuf; - while (freeQueue_->front(&freeBuf) && devShadowQueue_->push(freeBuf)) { - freeQueue_->pop(); - SLresult result = (*bq)->Enqueue(bq, freeBuf->buf_, freeBuf->cap_); - SLASSERT(result); - } - - ++audioBufCount; - - // should leave the device to sleep to save power if no buffers - if (devShadowQueue_->size() == 0) { - (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED); - } -} - -AudioRecorder::AudioRecorder(SampleFormat* sampleFormat, SLEngineItf slEngine) - : freeQueue_(nullptr), - recQueue_(nullptr), - devShadowQueue_(nullptr), - callback_(nullptr) { - SLresult result; - sampleInfo_ = *sampleFormat; - SLAndroidDataFormat_PCM_EX format_pcm; - ConvertToSLSampleFormat(&format_pcm, &sampleInfo_); - - // configure audio source - SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, - SL_IODEVICE_AUDIOINPUT, - SL_DEFAULTDEVICEID_AUDIOINPUT, NULL}; - SLDataSource audioSrc = {&loc_dev, NULL}; - - // configure audio sink - SLDataLocator_AndroidSimpleBufferQueue loc_bq = { - SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, DEVICE_SHADOW_BUFFER_QUEUE_LEN}; - - SLDataSink audioSnk = {&loc_bq, &format_pcm}; - - // create audio recorder - // (requires the RECORD_AUDIO permission) - const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - SL_IID_ANDROIDCONFIGURATION}; - const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; - result = (*slEngine)->CreateAudioRecorder( - slEngine, &recObjectItf_, &audioSrc, &audioSnk, - sizeof(id) / sizeof(id[0]), id, req); - SLASSERT(result); - - // Configure the voice recognition preset which has no - // signal processing for lower latency. - SLAndroidConfigurationItf inputConfig; - result = (*recObjectItf_) - ->GetInterface(recObjectItf_, SL_IID_ANDROIDCONFIGURATION, - &inputConfig); - if (SL_RESULT_SUCCESS == result) { - SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - (*inputConfig) - ->SetConfiguration(inputConfig, SL_ANDROID_KEY_RECORDING_PRESET, - &presetValue, sizeof(SLuint32)); - } - result = (*recObjectItf_)->Realize(recObjectItf_, SL_BOOLEAN_FALSE); - SLASSERT(result); - result = - (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_RECORD, &recItf_); - SLASSERT(result); - - result = (*recObjectItf_) - ->GetInterface(recObjectItf_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - &recBufQueueItf_); - SLASSERT(result); - - result = (*recBufQueueItf_) - ->RegisterCallback(recBufQueueItf_, bqRecorderCallback, this); - SLASSERT(result); - - devShadowQueue_ = new AudioQueue(DEVICE_SHADOW_BUFFER_QUEUE_LEN); - assert(devShadowQueue_); -#ifdef ENABLE_LOG - std::string name = "rec"; - recLog_ = new AndroidLog(name); -#endif -} - -SLboolean AudioRecorder::Start(void) { - if (!freeQueue_ || !recQueue_ || !devShadowQueue_) { - LOGE("====NULL poiter to Start(%p, %p, %p)", freeQueue_, recQueue_, - devShadowQueue_); - return SL_BOOLEAN_FALSE; - } - audioBufCount = 0; - - SLresult result; - // in case already recording, stop recording and clear buffer queue - result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED); - SLASSERT(result); - result = (*recBufQueueItf_)->Clear(recBufQueueItf_); - SLASSERT(result); - - for (int i = 0; i < RECORD_DEVICE_KICKSTART_BUF_COUNT; i++) { - sample_buf* buf = NULL; - if (!freeQueue_->front(&buf)) { - LOGE("=====OutOfFreeBuffers @ startingRecording @ (%d)", i); - break; - } - freeQueue_->pop(); - assert(buf->buf_ && buf->cap_ && !buf->size_); - - result = (*recBufQueueItf_)->Enqueue(recBufQueueItf_, buf->buf_, buf->cap_); - SLASSERT(result); - devShadowQueue_->push(buf); - } - - result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_RECORDING); - SLASSERT(result); - - return (result == SL_RESULT_SUCCESS ? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE); -} - -SLboolean AudioRecorder::Stop(void) { - // in case already recording, stop recording and clear buffer queue - SLuint32 curState; - - SLresult result = (*recItf_)->GetRecordState(recItf_, &curState); - SLASSERT(result); - if (curState == SL_RECORDSTATE_STOPPED) { - return SL_BOOLEAN_TRUE; - } - result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED); - SLASSERT(result); - result = (*recBufQueueItf_)->Clear(recBufQueueItf_); - SLASSERT(result); - -#ifdef ENABLE_LOG - recLog_->flush(); -#endif - - return SL_BOOLEAN_TRUE; -} - -AudioRecorder::~AudioRecorder() { - // destroy audio recorder object, and invalidate all associated interfaces - if (recObjectItf_ != NULL) { - (*recObjectItf_)->Destroy(recObjectItf_); - } - - if (devShadowQueue_) { - sample_buf* buf = NULL; - while (devShadowQueue_->front(&buf)) { - devShadowQueue_->pop(); - freeQueue_->push(buf); - } - delete (devShadowQueue_); - } -#ifdef ENABLE_LOG - if (recLog_) { - delete recLog_; - } -#endif -} - -void AudioRecorder::SetBufQueues(AudioQueue* freeQ, AudioQueue* recQ) { - assert(freeQ && recQ); - freeQueue_ = freeQ; - recQueue_ = recQ; -} - -void AudioRecorder::RegisterCallback(ENGINE_CALLBACK cb, void* ctx) { - callback_ = cb; - ctx_ = ctx; -} -int32_t AudioRecorder::dbgGetDevBufCount(void) { - return devShadowQueue_->size(); -} diff --git a/audio-echo/app/src/main/cpp/audio_recorder.h b/audio-echo/app/src/main/cpp/audio_recorder.h deleted file mode 100644 index 2e29853bf..000000000 --- a/audio-echo/app/src/main/cpp/audio_recorder.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef NATIVE_AUDIO_AUDIO_RECORDER_H -#define NATIVE_AUDIO_AUDIO_RECORDER_H -#include -#include -#include - -#include "audio_common.h" -#include "buf_manager.h" -#include "debug_utils.h" - -class AudioRecorder { - SLObjectItf recObjectItf_; - SLRecordItf recItf_; - SLAndroidSimpleBufferQueueItf recBufQueueItf_; - - SampleFormat sampleInfo_; - AudioQueue* freeQueue_; // user - AudioQueue* recQueue_; // user - AudioQueue* devShadowQueue_; // owner - uint32_t audioBufCount; - - ENGINE_CALLBACK callback_; - void* ctx_; - - public: - explicit AudioRecorder(SampleFormat*, SLEngineItf engineEngine); - ~AudioRecorder(); - SLboolean Start(void); - SLboolean Stop(void); - void SetBufQueues(AudioQueue* freeQ, AudioQueue* recQ); - void ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq); - void RegisterCallback(ENGINE_CALLBACK cb, void* ctx); - int32_t dbgGetDevBufCount(void); - -#ifdef ENABLE_LOG - AndroidLog* recLog_; -#endif -}; - -#endif // NATIVE_AUDIO_AUDIO_RECORDER_H diff --git a/audio-echo/app/src/main/cpp/buf_manager.h b/audio-echo/app/src/main/cpp/buf_manager.h deleted file mode 100644 index 2810a4dfa..000000000 --- a/audio-echo/app/src/main/cpp/buf_manager.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef NATIVE_AUDIO_BUF_MANAGER_H -#define NATIVE_AUDIO_BUF_MANAGER_H -#include -#include - -#include -#include -#include -#include - -#ifndef CACHE_ALIGN -#define CACHE_ALIGN 64 -#endif - -/* - * ProducerConsumerQueue, borrowed from Ian NiLewis - */ -template -class ProducerConsumerQueue { - public: - explicit ProducerConsumerQueue(int size) - : ProducerConsumerQueue(size, new T[size]) {} - - explicit ProducerConsumerQueue(int size, T* buffer) - : size_(size), buffer_(buffer) { - // This is necessary because we depend on twos-complement wraparound - // to take care of overflow conditions. - assert(size < std::numeric_limits::max()); - } - - bool push(const T& item) { - return push([&](T* ptr) -> bool { - *ptr = item; - return true; - }); - } - - // writer() can return false, which indicates that the caller - // of push() changed its mind while writing (e.g. ran out of bytes) - template - bool push(const F& writer) { - bool result = false; - int readptr = read_.load(std::memory_order_acquire); - int writeptr = write_.load(std::memory_order_relaxed); - - // note that while readptr and writeptr will eventually - // wrap around, taking their difference is still valid as - // long as size_ < MAXINT. - int space = size_ - (int)(writeptr - readptr); - if (space >= 1) { - result = true; - - // writer - if (writer(buffer_.get() + (writeptr % size_))) { - ++writeptr; - write_.store(writeptr, std::memory_order_release); - } - } - return result; - } - // front out the queue, but not pop-out - bool front(T* out_item) { - return front([&](T* ptr) -> bool { - *out_item = *ptr; - return true; - }); - } - - void pop(void) { - int readptr = read_.load(std::memory_order_relaxed); - ++readptr; - read_.store(readptr, std::memory_order_release); - } - - template - bool front(const F& reader) { - bool result = false; - - int writeptr = write_.load(std::memory_order_acquire); - int readptr = read_.load(std::memory_order_relaxed); - - // As above, wraparound is ok - int available = (int)(writeptr - readptr); - if (available >= 1) { - result = true; - reader(buffer_.get() + (readptr % size_)); - } - - return result; - } - uint32_t size(void) { - int writeptr = write_.load(std::memory_order_acquire); - int readptr = read_.load(std::memory_order_relaxed); - - return (uint32_t)(writeptr - readptr); - } - - private: - int size_; - std::unique_ptr buffer_; - - // forcing cache line alignment to eliminate false sharing of the - // frequently-updated read and write pointers. The object is to never - // let these get into the "shared" state where they'd cause a cache miss - // for every write. - alignas(CACHE_ALIGN) std::atomic read_{0}; - alignas(CACHE_ALIGN) std::atomic write_{0}; -}; - -struct sample_buf { - uint8_t* buf_; // audio sample container - uint32_t cap_; // buffer capacity in byte - uint32_t size_; // audio sample size (n buf) in byte -}; - -using AudioQueue = ProducerConsumerQueue; - -__inline__ void releaseSampleBufs(sample_buf* bufs, uint32_t& count) { - if (!bufs || !count) { - return; - } - for (uint32_t i = 0; i < count; i++) { - if (bufs[i].buf_) delete[] bufs[i].buf_; - } - delete[] bufs; -} -__inline__ sample_buf* allocateSampleBufs(uint32_t count, uint32_t sizeInByte) { - if (count <= 0 || sizeInByte <= 0) { - return nullptr; - } - sample_buf* bufs = new sample_buf[count]; - assert(bufs); - memset(bufs, 0, sizeof(sample_buf) * count); - - uint32_t allocSize = (sizeInByte + 3) & ~3; // padding to 4 bytes aligned - uint32_t i; - for (i = 0; i < count; i++) { - bufs[i].buf_ = new uint8_t[allocSize]; - if (bufs[i].buf_ == nullptr) { - LOGW("====Requesting %d buffers, allocated %d in %s", count, i, - __FUNCTION__); - break; - } - bufs[i].cap_ = sizeInByte; - bufs[i].size_ = 0; // 0 data in it - } - if (i < 2) { - releaseSampleBufs(bufs, i); - bufs = nullptr; - } - count = i; - return bufs; -} - -#endif // NATIVE_AUDIO_BUF_MANAGER_H diff --git a/audio-echo/app/src/main/cpp/debug_utils.cpp b/audio-echo/app/src/main/cpp/debug_utils.cpp deleted file mode 100644 index 1dbeb233a..000000000 --- a/audio-echo/app/src/main/cpp/debug_utils.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "debug_utils.h" - -#include -#include - -#include - -#include "android_debug.h" - -static const char* FILE_PREFIX = "/sdcard/data/audio"; - -volatile uint32_t AndroidLog::fileIdx_ = 0; -AndroidLog::AndroidLog() : fp_(NULL), prevTick_(static_cast(0)) { - fileName_ = FILE_PREFIX; - openFile(); -} - -AndroidLog::AndroidLog(std::string& file_name) - : fp_(NULL), prevTick_(static_cast(0)) { - fileName_ = std::string(FILE_PREFIX) + std::string("_") + file_name; - openFile(); -} - -AndroidLog::~AndroidLog() { flush(); } - -void AndroidLog::flush() { - if (fp_) { - fflush(fp_); - fclose(fp_); - fp_ = NULL; - } - prevTick_ = static_cast(0); -} - -void AndroidLog::log(void* buf, uint32_t size) { - Lock fileLock(&mutex_); - if (!buf || !size) return; - - if (fp_ || openFile()) { - fwrite(buf, size, 1, fp_); - } -} - -void AndroidLog::log(const char* fmt, ...) { - Lock fileLock(&mutex_); - if (!fmt) { - return; - } - if (fp_ || openFile()) { - va_list vp; - va_start(vp, fmt); - vfprintf(fp_, fmt, vp); - va_end(vp); - } -} - -FILE* AndroidLog::openFile() { - Lock fileLock(&mutex_); - - if (fp_) { - return fp_; - } - - char fileName[64]; - sprintf(fileName, "%s_%d", fileName_.c_str(), AndroidLog::fileIdx_++); - fp_ = fopen(fileName, "wb"); - if (fp_ == NULL) { - LOGE("====failed to open file %s", fileName); - } - return fp_; -} -void AndroidLog::logTime() { - if (prevTick_ == static_cast(0)) { - /* - * init counter, bypass the first one - */ - prevTick_ = getCurrentTicks(); - return; - } - uint64_t curTick = getCurrentTicks(); - uint64_t delta = curTick - prevTick_; - log("%" PRIu64 " %" PRIu64 "\n", curTick, delta); - prevTick_ = curTick; -} - -uint64_t AndroidLog::getCurrentTicks() { - struct timeval Time; - gettimeofday(&Time, NULL); - - return (static_cast(1000000) * Time.tv_sec + Time.tv_usec); -} \ No newline at end of file diff --git a/audio-echo/app/src/main/cpp/debug_utils.h b/audio-echo/app/src/main/cpp/debug_utils.h deleted file mode 100644 index 95a06fc65..000000000 --- a/audio-echo/app/src/main/cpp/debug_utils.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef NATIVE_AUDIO_DEBUG_UTILS_H -#define NATIVE_AUDIO_DEBUG_UTILS_H -#include -#include -#include - -/* - * debug_write_file() - * Write given data to a file as binary file. File name is - * "/sdcard/data/audio_%d", file_index++ - * requirement: must have /sdcard/data already created on android device - */ -class Lock { - public: - explicit Lock(std::recursive_mutex* mtx) { - mutex_ = mtx; - mutex_->lock(); - } - ~Lock() { mutex_->unlock(); } - - private: - std::recursive_mutex* mutex_; -}; -class AndroidLog { - public: - AndroidLog(); - AndroidLog(std::string& fileName); - ~AndroidLog(); - void log(void* buf, uint32_t size); - void log(const char* fmt, ...); - void logTime(); - void flush(); - static volatile uint32_t fileIdx_; - - private: - uint64_t getCurrentTicks(); - FILE* fp_; - FILE* openFile(); - uint64_t prevTick_; // Tick in milisecond - std::recursive_mutex mutex_; - std::string fileName_; -}; - -void debug_write_file(void* buf, uint32_t size); - -#endif // NATIVE_AUDIO_DEBUG_UTILS_H diff --git a/audio-echo/app/src/main/cpp/jni.cpp b/audio-echo/app/src/main/cpp/jni.cpp deleted file mode 100644 index aa0441809..000000000 --- a/audio-echo/app/src/main/cpp/jni.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2025 The Android Open Source Project -// SPDX-License-Identifier: Apache-2.0 - -#include -#include - -#include "jni_interface.h" - -extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* _Nonnull vm, void* _Nullable) { - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - return JNI_ERR; - } - - jclass c = env->FindClass("com/google/sample/echo/MainActivity"); - if (c == nullptr) return JNI_ERR; - - static const JNINativeMethod methods[] = { - {"createSLEngine", "(IIJF)V", reinterpret_cast(CreateSlEngine)}, - {"deleteSLEngine", "()V", reinterpret_cast(DeleteSlEngine)}, - {"createSLBufferQueueAudioPlayer", "()Z", - reinterpret_cast(CreateSlBufferQueueAudioPlayer)}, - {"deleteSLBufferQueueAudioPlayer", "()V", - reinterpret_cast(DeleteSlBufferQueueAudioPlayer)}, - {"createAudioRecorder", "()Z", - reinterpret_cast(CreateAudioRecorder)}, - {"deleteAudioRecorder", "()V", - reinterpret_cast(DeleteAudioRecorder)}, - {"startPlay", "()V", reinterpret_cast(StartPlay)}, - {"stopPlay", "()V", reinterpret_cast(StopPlay)}, - {"configureEcho", "(IF)Z", reinterpret_cast(ConfigureEcho)}, - }; - int rc = env->RegisterNatives(c, methods, arraysize(methods)); - if (rc != JNI_OK) return rc; - - return JNI_VERSION_1_6; -} diff --git a/audio-echo/app/src/main/cpp/jni_interface.h b/audio-echo/app/src/main/cpp/jni_interface.h deleted file mode 100644 index 5dd4edd5f..000000000 --- a/audio-echo/app/src/main/cpp/jni_interface.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void CreateSlEngine(JNIEnv* env, jclass, jint, jint, jlong delayInMs, - jfloat decay); -void DeleteSlEngine(JNIEnv* env, jclass type); -jboolean CreateSlBufferQueueAudioPlayer(JNIEnv* env, jclass); -void DeleteSlBufferQueueAudioPlayer(JNIEnv* env, jclass type); - -jboolean CreateAudioRecorder(JNIEnv* env, jclass type); -void DeleteAudioRecorder(JNIEnv* env, jclass type); -void StartPlay(JNIEnv* env, jclass type); -void StopPlay(JNIEnv* env, jclass type); -jboolean ConfigureEcho(JNIEnv* env, jclass type, jint delayInMs, jfloat decay); diff --git a/audio-echo/app/src/main/cpp/libecho.map.txt b/audio-echo/app/src/main/cpp/libecho.map.txt deleted file mode 100644 index a81ec0257..000000000 --- a/audio-echo/app/src/main/cpp/libecho.map.txt +++ /dev/null @@ -1,6 +0,0 @@ -LIBECHO { - global: - JNI_OnLoad; - local: - *; -}; diff --git a/audio-echo/app/src/main/java/com/google/sample/echo/MainActivity.java b/audio-echo/app/src/main/java/com/google/sample/echo/MainActivity.java deleted file mode 100644 index 9258a6e02..000000000 --- a/audio-echo/app/src/main/java/com/google/sample/echo/MainActivity.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.sample.echo; - -import android.Manifest; -import android.app.Activity; -import android.content.Context; -import android.content.pm.PackageManager; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioRecord; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.SeekBar; -import android.widget.TextView; -import android.widget.Toast; - -public class MainActivity extends Activity - implements ActivityCompat.OnRequestPermissionsResultCallback { - private static final int AUDIO_ECHO_REQUEST = 0; - - private Button controlButton; - private TextView statusView; - private String nativeSampleRate; - private String nativeSampleBufSize; - - private SeekBar delaySeekBar; - private TextView curDelayTV; - private int echoDelayProgress; - - private SeekBar decaySeekBar; - private TextView curDecayTV; - private float echoDecayProgress; - - private boolean supportRecording; - private Boolean isPlaying = false; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - controlButton = (Button)findViewById((R.id.capture_control_button)); - statusView = (TextView)findViewById(R.id.statusView); - queryNativeAudioParameters(); - - delaySeekBar = (SeekBar)findViewById(R.id.delaySeekBar); - curDelayTV = (TextView)findViewById(R.id.curDelay); - echoDelayProgress = delaySeekBar.getProgress() * 1000 / delaySeekBar.getMax(); - delaySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - float curVal = (float)progress / delaySeekBar.getMax(); - curDelayTV.setText(String.format("%s", curVal)); - setSeekBarPromptPosition(delaySeekBar, curDelayTV); - if (!fromUser) return; - - echoDelayProgress = progress * 1000 / delaySeekBar.getMax(); - configureEcho(echoDelayProgress, echoDecayProgress); - } - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} - @Override - public void onStopTrackingTouch(SeekBar seekBar) {} - }); - delaySeekBar.post(new Runnable() { - @Override - public void run() { - setSeekBarPromptPosition(delaySeekBar, curDelayTV); - } - }); - - decaySeekBar = (SeekBar)findViewById(R.id.decaySeekBar); - curDecayTV = (TextView)findViewById(R.id.curDecay); - echoDecayProgress = (float)decaySeekBar.getProgress() / decaySeekBar.getMax(); - decaySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - float curVal = (float)progress / seekBar.getMax(); - curDecayTV.setText(String.format("%s", curVal)); - setSeekBarPromptPosition(decaySeekBar, curDecayTV); - if (!fromUser) - return; - - echoDecayProgress = curVal; - configureEcho(echoDelayProgress, echoDecayProgress); - } - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} - @Override - public void onStopTrackingTouch(SeekBar seekBar) {} - }); - decaySeekBar.post(new Runnable() { - @Override - public void run() { - setSeekBarPromptPosition(decaySeekBar, curDecayTV); - } - }); - - // initialize native audio system - updateNativeAudioUI(); - - if (supportRecording) { - createSLEngine( - Integer.parseInt(nativeSampleRate), - Integer.parseInt(nativeSampleBufSize), - echoDelayProgress, - echoDecayProgress); - } - } - - private void setSeekBarPromptPosition(SeekBar seekBar, TextView label) { - float thumbX = (float)seekBar.getProgress()/ seekBar.getMax() * - seekBar.getWidth() + seekBar.getX(); - label.setX(thumbX - label.getWidth()/2.0f); - } - - @Override - protected void onDestroy() { - if (supportRecording) { - if (isPlaying) { - stopPlay(); - } - deleteSLEngine(); - isPlaying = false; - } - super.onDestroy(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_settings) { - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void startEcho() { - if(!supportRecording){ - return; - } - if (!isPlaying) { - if(!createSLBufferQueueAudioPlayer()) { - statusView.setText(getString(R.string.player_error_msg)); - return; - } - if(!createAudioRecorder()) { - deleteSLBufferQueueAudioPlayer(); - statusView.setText(getString(R.string.recorder_error_msg)); - return; - } - startPlay(); // startPlay() triggers startRecording() - statusView.setText(getString(R.string.echoing_status_msg)); - } else { - stopPlay(); // stopPlay() triggers stopRecording() - updateNativeAudioUI(); - deleteAudioRecorder(); - deleteSLBufferQueueAudioPlayer(); - } - isPlaying = !isPlaying; - controlButton.setText(getString(isPlaying ? - R.string.cmd_stop_echo: R.string.cmd_start_echo)); - } - public void onEchoClick(View view) { - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != - PackageManager.PERMISSION_GRANTED) { - statusView.setText(getString(R.string.request_permission_status_msg)); - ActivityCompat.requestPermissions( - this, - new String[] { Manifest.permission.RECORD_AUDIO }, - AUDIO_ECHO_REQUEST); - return; - } - startEcho(); - } - - public void getLowLatencyParameters(View view) { - updateNativeAudioUI(); - } - - private void queryNativeAudioParameters() { - supportRecording = true; - AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - if(myAudioMgr == null) { - supportRecording = false; - return; - } - nativeSampleRate = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); - nativeSampleBufSize =myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); - - // hardcoded channel to mono: both sides -- C++ and Java sides - int recBufSize = AudioRecord.getMinBufferSize( - Integer.parseInt(nativeSampleRate), - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT); - if (recBufSize == AudioRecord.ERROR || - recBufSize == AudioRecord.ERROR_BAD_VALUE) { - supportRecording = false; - } - - } - private void updateNativeAudioUI() { - if (!supportRecording) { - statusView.setText(getString(R.string.mic_error_msg)); - controlButton.setEnabled(false); - return; - } - - statusView.setText(getString(R.string.fast_audio_info_msg, - nativeSampleRate, nativeSampleBufSize)); - } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - /* - * if any permission failed, the sample could not play - */ - if (AUDIO_ECHO_REQUEST != requestCode) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - return; - } - - if (grantResults.length != 1 || - grantResults[0] != PackageManager.PERMISSION_GRANTED) { - /* - * When user denied permission, throw a Toast to prompt that RECORD_AUDIO - * is necessary; also display the status on UI - * Then application goes back to the original state: it behaves as if the button - * was not clicked. The assumption is that user will re-click the "start" button - * (to retry), or shutdown the app in normal way. - */ - statusView.setText(getString(R.string.permission_error_msg)); - Toast.makeText(getApplicationContext(), - getString(R.string.permission_prompt_msg), - Toast.LENGTH_SHORT).show(); - return; - } - - /* - * When permissions are granted, we prompt the user the status. User would - * re-try the "start" button to perform the normal operation. This saves us the extra - * logic in code for async processing of the button listener. - */ - statusView.setText(getString(R.string.permission_granted_msg,getString(R.string.cmd_start_echo))); - - - // The callback runs on app's thread, so we are safe to resume the action - startEcho(); - } - - /* - * Loading our lib - */ - static { - System.loadLibrary("echo"); - } - - /* - * jni function declarations - */ - static native void createSLEngine(int rate, int framesPerBuf, - long delayInMs, float decay); - static native void deleteSLEngine(); - static native boolean configureEcho(int delayInMs, float decay); - static native boolean createSLBufferQueueAudioPlayer(); - static native void deleteSLBufferQueueAudioPlayer(); - - static native boolean createAudioRecorder(); - static native void deleteAudioRecorder(); - static native void startPlay(); - static native void stopPlay(); -} diff --git a/audio-echo/app/src/main/res/layout/activity_main.xml b/audio-echo/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 21eef87ea..000000000 --- a/audio-echo/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - -