OboeTester: add InterpolatingDelayLine
Cleanup AudioStreamGateway
Bump test to version 1.3.00
diff --git a/apps/OboeTester/app/build.gradle b/apps/OboeTester/app/build.gradle
index c32b8b5..97c5aac 100644
--- a/apps/OboeTester/app/build.gradle
+++ b/apps/OboeTester/app/build.gradle
@@ -6,8 +6,8 @@
applicationId = "com.google.sample.oboe.manualtest"
minSdkVersion 26
targetSdkVersion 26
- versionCode 3
- versionName "1.2.02"
+ versionCode 4
+ versionName "1.3.00"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
diff --git a/apps/OboeTester/app/src/main/AndroidManifest.xml b/apps/OboeTester/app/src/main/AndroidManifest.xml
index c393f56..3ea2dfa 100644
--- a/apps/OboeTester/app/src/main/AndroidManifest.xml
+++ b/apps/OboeTester/app/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.sample.oboe.manualtest"
- android:versionCode="3"
- android:versionName="1.2.02">
+ android:versionCode="4"
+ android:versionName="1.3.00">
<!-- versionCode and versionName also have to be updated in build.gradle -->
<uses-feature android:name="android.hardware.microphone" android:required="true" />
diff --git a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
index 6d21ebe..431db4d 100644
--- a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
+++ b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
@@ -22,13 +22,13 @@
using namespace flowgraph;
-AudioStreamGateway::AudioStreamGateway(int samplesPerFrame)
-{
-}
-
-AudioStreamGateway::~AudioStreamGateway()
-{
-}
+//AudioStreamGateway::AudioStreamGateway(int samplesPerFrame)
+//{
+//}
+//
+//AudioStreamGateway::~AudioStreamGateway()
+//{
+//}
int64_t AudioStreamGateway::mFramePosition = 0;
diff --git a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
index 8eda5d1..2192094 100644
--- a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
+++ b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
@@ -25,14 +25,14 @@
using namespace flowgraph;
/**
- * Bridge between an audio graph and an audio device.
+ * Bridge between an audio flowgraph and an audio device.
* Pass in an AudioSink and then pass
* this object to the AudioStreamBuilder as a callback.
*/
class AudioStreamGateway : public oboe::AudioStreamCallback {
public:
- AudioStreamGateway(int samplesPerFrame);
- virtual ~AudioStreamGateway();
+// AudioStreamGateway(int samplesPerFrame);
+ virtual ~AudioStreamGateway() = default;
void setAudioSink(std::shared_ptr<flowgraph::AudioSink> sink) {
mAudioSink = sink;
diff --git a/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.cpp b/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.cpp
index 3f37d8a..1b34692 100644
--- a/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.cpp
+++ b/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.cpp
@@ -18,9 +18,9 @@
#include "FullDuplexEcho.h"
oboe::Result FullDuplexEcho::start() {
- mDelaySizeFrames = 3 * getOutputStream()->getSampleRate();
- static const int32_t maxDelaySamples = mDelaySizeFrames * getOutputStream()->getChannelCount();
- mDelayLine = std::make_unique<float[]>(maxDelaySamples);
+ int32_t delaySize = 3 * getOutputStream()->getSampleRate();
+ mDelayFrames = delaySize - 2;
+ mDelayLine = std::make_unique<InterpolatingDelayLine>(delaySize);
return FullDuplexStream::start();
}
@@ -30,20 +30,31 @@
void *outputData,
int numOutputFrames) {
// FIXME only handles matching stream formats.
-// LOGE("FullDuplexEcho::%s() called, numInputFrames = %d, numOutputFrames = %d",
-// __func__, numInputFrames, numOutputFrames);
- int32_t framesLeft = std::min(numInputFrames, numOutputFrames);
- while (framesLeft > 0) {
- float *delayAddress = mDelayLine.get() + (mCursorFrames * getOutputStream()->getChannelCount());
- memcpy(outputData, delayAddress, getOutputStream()->getBytesPerFrame());
- memcpy(delayAddress, inputData, getOutputStream()->getBytesPerFrame());
- mCursorFrames++;
- if (mCursorFrames >= mDelaySizeFrames) {
- mCursorFrames = 0;
+ // TODO use array of delays
+ // TODO Add delay node
+ // TODO use flowgraph to handle format conversion
+ int32_t framesToEcho = std::min(numInputFrames, numOutputFrames);
+ float *inputFloat = (float *)inputData;
+ float *outputFloat = (float *)outputData;
+ int32_t inputStride = getInputStream()->getChannelCount();
+ int32_t outputStride = getOutputStream()->getChannelCount();
+ if (outputStride == 1) {
+ while (framesToEcho-- > 0) {
+ *outputFloat++ = mDelayLine->process(mDelayFrames, *inputFloat); // mono delay
+ inputFloat += inputStride;
}
- inputData = ((uint8_t *)inputData) + getOutputStream()->getBytesPerFrame();
- outputData = ((uint8_t *)outputData) + getOutputStream()->getBytesPerFrame();
- framesLeft--;
+ } else if (outputStride == 2) {
+ while (framesToEcho-- > 0) {
+ *outputFloat++ = mDelayLine->process(mDelayFrames, *inputFloat); // mono delay
+ *outputFloat++ = 0.0f; // FIXME *inputFloat; // mono
+ inputFloat += inputStride;
+ }
+ } // else TODO
+
+ // zero out remainder of output array
+ int32_t framesLeft = numOutputFrames - numInputFrames;
+ if (framesLeft > 0) {
+ memset(outputFloat, 0, framesLeft * getOutputStream()->getBytesPerFrame());
}
return oboe::DataCallbackResult::Continue;
};
diff --git a/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.h b/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.h
index 668bf42..7a755ec 100644
--- a/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.h
+++ b/apps/OboeTester/app/src/main/cpp/FullDuplexEcho.h
@@ -22,6 +22,7 @@
#include "oboe/Oboe.h"
#include "FullDuplexStream.h"
+#include "InterpolatingDelayLine.h"
class FullDuplexEcho : public FullDuplexStream {
public:
@@ -41,9 +42,8 @@
oboe::Result start() override;
private:
- std::unique_ptr<float[]> mDelayLine;
- int32_t mCursorFrames = 0;
- int32_t mDelaySizeFrames = 0;
+ std::unique_ptr<InterpolatingDelayLine> mDelayLine;
+ float mDelayFrames = 0.0f;
};
diff --git a/apps/OboeTester/app/src/main/cpp/InterpolatingDelayLine.cpp b/apps/OboeTester/app/src/main/cpp/InterpolatingDelayLine.cpp
new file mode 100644
index 0000000..e69c711
--- /dev/null
+++ b/apps/OboeTester/app/src/main/cpp/InterpolatingDelayLine.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 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 "common/OboeDebug.h"
+#include "InterpolatingDelayLine.h"
+
+InterpolatingDelayLine::InterpolatingDelayLine(int32_t delaySize) {
+ mDelaySize = delaySize;
+ mDelayLine = std::make_unique<float[]>(delaySize);
+}
+
+float InterpolatingDelayLine::process(float delay, float input) {
+ float *writeAddress = mDelayLine.get() + mCursor;
+ *writeAddress = input;
+ mDelayLine.get()[mCursor] = input;
+ int32_t delayInt = std::min(mDelaySize - 1, (int32_t) delay);
+ int32_t readIndex = mCursor - delayInt;
+ if (readIndex < 0) {
+ readIndex += mDelaySize;
+ }
+ // TODO interpolate
+ float *readAddress = mDelayLine.get() + readIndex;
+ float output = *readAddress;
+ mCursor++;
+ if (mCursor >= mDelaySize) {
+ mCursor = 0;
+ }
+ return output;
+};
diff --git a/apps/OboeTester/app/src/main/cpp/InterpolatingDelayLine.h b/apps/OboeTester/app/src/main/cpp/InterpolatingDelayLine.h
new file mode 100644
index 0000000..b3a510d
--- /dev/null
+++ b/apps/OboeTester/app/src/main/cpp/InterpolatingDelayLine.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 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 OBOETESTER_INTERPOLATING_DELAY_LINE_H
+#define OBOETESTER_INTERPOLATING_DELAY_LINE_H
+
+#include <memory>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "oboe/Oboe.h"
+#include "FullDuplexStream.h"
+
+/**
+ * Monophonic delay line.
+ */
+class InterpolatingDelayLine {
+public:
+ explicit InterpolatingDelayLine(int32_t delaySize);
+
+ /**
+ * @param input sample to be written to the delay line
+ * @param delay number of samples to delay the output
+ * @return delayed value
+ */
+ float process(float delay, float input);
+
+private:
+ std::unique_ptr<float[]> mDelayLine;
+ int32_t mCursor = 0;
+ int32_t mDelaySize = 0;
+};
+
+
+#endif //OBOETESTER_INTERPOLATING_DELAY_LINE_H
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
index de1124c..9a72bbd 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
@@ -48,7 +48,6 @@
LOGD("%s() delete nodes", __func__);
manyToMulti.reset(nullptr);
monoToMulti.reset(nullptr);
- audioStreamGateway.reset(nullptr);
mSinkFloat.reset();
mSinkI16.reset();
}
@@ -328,15 +327,14 @@
// We needed the proxy because we did not know the channelCount
// when we setup the Builder.
- audioStreamGateway = std::make_unique<AudioStreamGateway>(mChannelCount);
if (outputStream->getFormat() == oboe::AudioFormat::I16) {
- audioStreamGateway->setAudioSink(mSinkI16);
+ audioStreamGateway.setAudioSink(mSinkI16);
} else if (outputStream->getFormat() == oboe::AudioFormat::Float) {
- audioStreamGateway->setAudioSink(mSinkFloat);
+ audioStreamGateway.setAudioSink(mSinkFloat);
}
if (useCallback) {
- oboeCallbackProxy.setCallback(audioStreamGateway.get());
+ oboeCallbackProxy.setCallback(&audioStreamGateway);
}
// Set starting size of buffer.
@@ -441,9 +439,9 @@
callbackResult = mInputAnalyzer.onAudioReady(oboeStream,
dataBuffer.get(),
framesRead);
- } else if (audioStreamGateway != nullptr) { // OUTPUT?
+ } else { // OUTPUT?
// generate output by calling the callback
- callbackResult = audioStreamGateway->onAudioReady(oboeStream,
+ callbackResult = audioStreamGateway.onAudioReady(oboeStream,
dataBuffer.get(),
framesPerBlock);
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
index 6cec33b..ec0d3e9 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
@@ -100,10 +100,8 @@
}
void printScheduler() {
- if (audioStreamGateway != nullptr) {
- int scheduler = audioStreamGateway->getScheduler();
- LOGI("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n", scheduler, SCHED_FIFO);
- }
+ int scheduler = audioStreamGateway.getScheduler();
+ LOGI("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n", scheduler, SCHED_FIFO);
}
oboe::Result pause() {
@@ -237,7 +235,10 @@
mActivityType = (ActivityType) activityType;
}
- InputStreamCallbackAnalyzer mInputAnalyzer;
+ double getPeakLevel(int index) {
+ return mInputAnalyzer.getPeakLevel(index);
+ }
+
bool useCallback = true;
bool callbackReturnStop = false;
int callbackSize = 0;
@@ -299,6 +300,9 @@
std::thread *dataThread = nullptr;
OboeStreamCallbackProxy oboeCallbackProxy;
+ AudioStreamGateway audioStreamGateway;
+ InputStreamCallbackAnalyzer mInputAnalyzer;
+
std::vector<SineOscillator> sineOscillators;
std::vector<SawtoothOscillator> sawtoothOscillators;
@@ -311,7 +315,6 @@
std::unique_ptr<MonoToMultiConverter> monoToMulti;
std::shared_ptr<flowgraph::SinkFloat> mSinkFloat;
std::shared_ptr<flowgraph::SinkI16> mSinkI16;
- std::unique_ptr<AudioStreamGateway> audioStreamGateway{};
std::unique_ptr<MultiChannelRecording> mRecording{};
std::unique_ptr<FullDuplexEcho> mFullDuplexEcho{};
diff --git a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
index ce7b803..be75f63 100644
--- a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
+++ b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
@@ -363,7 +363,7 @@
Java_com_google_sample_oboe_manualtest_AudioInputTester_getPeakLevel(JNIEnv *env,
jobject instance,
jint index) {
- return engine.mInputAnalyzer.getPeakLevel(index);
+ return engine.getPeakLevel(index);
}
JNIEXPORT void JNICALL
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java
index bb537b6..560610c 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java
@@ -88,7 +88,7 @@
AudioStreamBase.StreamStatus status = streamContext.tester.getCurrentAudioStream().getStreamStatus();
int framesPerBurst = streamContext.tester.getCurrentAudioStream().getFramesPerBurst();
final String msg = status.dump(framesPerBurst);
- mStreamContexts.get(0).configurationView.setStatusText(msg);
+ streamContext.configurationView.setStatusText(msg);
updateStreamDisplay();
}
diff --git a/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml b/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml
index 7527fa0..a366a8f 100644
--- a/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml
+++ b/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml
@@ -55,48 +55,28 @@
android:lines="3"
android:text="@string/init_status" />
- <TextView
- android:id="@+id/volumeText0"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="\?" />
-
<com.google.sample.oboe.manualtest.VolumeBarView
android:id="@+id/volumeBar0"
android:layout_width="fill_parent"
+ android:layout_marginBottom="4dp"
android:layout_height="20dp" />
- <TextView
- android:id="@+id/volumeText1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="\?" />
-
<com.google.sample.oboe.manualtest.VolumeBarView
android:id="@+id/volumeBar1"
android:layout_width="fill_parent"
+ android:layout_marginBottom="4dp"
android:layout_height="20dp" />
- <TextView
- android:id="@+id/volumeText2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="\?" />
-
<com.google.sample.oboe.manualtest.VolumeBarView
android:id="@+id/volumeBar2"
android:layout_width="fill_parent"
+ android:layout_marginBottom="4dp"
android:layout_height="20dp" />
- <TextView
- android:id="@+id/volumeText3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="\?" />
-
<com.google.sample.oboe.manualtest.VolumeBarView
android:id="@+id/volumeBar3"
android:layout_width="fill_parent"
+ android:layout_marginBottom="4dp"
android:layout_height="20dp" />
</LinearLayout>
diff --git a/apps/OboeTester/build.gradle b/apps/OboeTester/build.gradle
index 4f94500..c78c3f9 100644
--- a/apps/OboeTester/build.gradle
+++ b/apps/OboeTester/build.gradle
@@ -6,7 +6,7 @@
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.1'
+ classpath 'com.android.tools.build:gradle:3.2.1'
}
}