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 @@ - - - - - - - - - - - - - - -