oboe: fix sample rate converter
Change pullData indexing.
Add pullReset() to reset framePositions.
Add simple test.
diff --git a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
index 6a41c70..e89f914 100644
--- a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
+++ b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
@@ -22,8 +22,6 @@
using namespace flowgraph;
-int64_t AudioStreamGateway::mFramePosition = 0;
-
oboe::DataCallbackResult AudioStreamGateway::onAudioReady(
oboe::AudioStream *audioStream,
void *audioData,
diff --git a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
index 2192094..3c147bc 100644
--- a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
+++ b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
@@ -36,6 +36,7 @@
void setAudioSink(std::shared_ptr<flowgraph::AudioSink> sink) {
mAudioSink = sink;
+ mFramePosition = sink->getLastFramePosition();
}
/**
@@ -49,10 +50,7 @@
int getScheduler();
private:
- // Use a static position so that it will monotonically increase.
- // Note only one gateway can be used.
- // TODO remove single gateway limitation. Add Graph class.
- static int64_t mFramePosition;
+ int64_t mFramePosition = 0;
bool mSchedulerChecked = false;
int mScheduler;
std::shared_ptr<flowgraph::AudioSink> mAudioSink;
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
index baf46d8..38d40a8 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
@@ -347,6 +347,10 @@
manyToMulti->output.connect(&(mSinkFloat.get()->input));
manyToMulti->output.connect(&(mSinkI16.get()->input));
+ // Clear framePosition in sine oscillators.
+ mSinkFloat->pullReset();
+ mSinkI16->pullReset();
+
configureStreamGateway();
}
diff --git a/apps/OboeTester/app/src/main/res/values/colors.xml b/apps/OboeTester/app/src/main/res/values/colors.xml
index ea15065..fb81fc2 100644
--- a/apps/OboeTester/app/src/main/res/values/colors.xml
+++ b/apps/OboeTester/app/src/main/res/values/colors.xml
@@ -17,6 +17,6 @@
<resources>
<color name="waveform_line">#ff101080</color>
<color name="waveform_background">#ffc0f0c0</color>
- <color name="version_draft">#ffffe0c0</color>
+ <color name="version_draft">#ffffb0b0</color>
<color name="version_release">#fff0f0f0</color>
</resources>
diff --git a/src/common/OboeFlowGraph.cpp b/src/common/OboeFlowGraph.cpp
index 2c3ee8a..3c13050 100644
--- a/src/common/OboeFlowGraph.cpp
+++ b/src/common/OboeFlowGraph.cpp
@@ -34,7 +34,6 @@
using namespace flowgraph;
void OboeFlowGraph::setSource(const void *buffer, int32_t numFrames) {
- LOGE("OboeFlowGraph::setSource(,%d) ===================", numFrames);
mSource->setData(buffer, numFrames);
}
@@ -117,6 +116,8 @@
}
lastOutput->connect(&mSink->input);
+ mFramePosition = 0;
+
return Result::OK;
}
diff --git a/src/common/OboeFlowGraph.h b/src/common/OboeFlowGraph.h
index 7323353..7dbe497 100644
--- a/src/common/OboeFlowGraph.h
+++ b/src/common/OboeFlowGraph.h
@@ -33,7 +33,6 @@
class AudioSourceCaller;
class OboeFlowGraph {
-
public:
void setSource(const void *buffer, int32_t numFrames);
diff --git a/src/common/SourceFloatCaller.h b/src/common/SourceFloatCaller.h
index d5d8469..f3bdaf6 100644
--- a/src/common/SourceFloatCaller.h
+++ b/src/common/SourceFloatCaller.h
@@ -32,6 +32,10 @@
explicit SourceFloatCaller(int32_t channelCount);
int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SourceFloatCaller";
+ }
};
}
diff --git a/src/flowgraph/AudioProcessorBase.cpp b/src/flowgraph/AudioProcessorBase.cpp
index 0445069..ef7553b 100644
--- a/src/flowgraph/AudioProcessorBase.cpp
+++ b/src/flowgraph/AudioProcessorBase.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "stdio.h"
#include <algorithm>
#include <sys/types.h>
#include "AudioProcessorBase.h"
@@ -24,8 +25,8 @@
int32_t AudioProcessorBase::pullData(int64_t framePosition, int32_t numFrames) {
int32_t frameCount = numFrames;
// Prevent recursion and multiple execution of nodes.
- if (framePosition > mLastFramePosition) {
- mLastFramePosition = framePosition;
+ if (framePosition <= mLastFramePosition && !mBlockRecursion) {
+ mBlockRecursion = true; // for cyclic graphs
if (mDataPulledAutomatically) {
// Pull from all the upstream nodes.
for (auto &port : mInputPorts) {
@@ -36,10 +37,31 @@
if (frameCount > 0) {
frameCount = onProcess(frameCount);
}
+ mLastFramePosition += frameCount;
+ mBlockRecursion = false;
+ mLastFrameCount = frameCount;
+ } else {
+ frameCount = mLastFrameCount;
}
return frameCount;
}
+void AudioProcessorBase::pullReset() {
+ if (!mBlockRecursion) {
+ mBlockRecursion = true; // for cyclic graphs
+ // Pull reset from all the upstream nodes.
+ for (auto &port : mInputPorts) {
+ port.get().pullReset();
+ }
+ mBlockRecursion = false;
+ reset();
+ }
+}
+
+void AudioProcessorBase::reset() {
+ mLastFrameCount = 0;
+}
+
/***************************************************************************/
AudioFloatBufferPort::AudioFloatBufferPort(AudioProcessorBase &parent,
int32_t samplesPerFrame,
@@ -57,6 +79,10 @@
return mParent.pullData(framePosition, numFrames);
}
+void AudioFloatOutputPort::pullReset() {
+ mParent.pullReset();
+}
+
// These need to be in the .cpp file because of forward cross references.
void AudioFloatOutputPort::connect(AudioFloatInputPort *port) {
port->connect(this);
@@ -72,6 +98,9 @@
? std::min(getFramesPerBuffer(), numFrames)
: mConnected->pullData(framePosition, numFrames);
}
+void AudioFloatInputPort::pullReset() {
+ if (mConnected != NULL) mConnected->pullReset();
+}
float *AudioFloatInputPort::getBuffer() {
if (mConnected == NULL) {
diff --git a/src/flowgraph/AudioProcessorBase.h b/src/flowgraph/AudioProcessorBase.h
index d5875f4..b4097e5 100644
--- a/src/flowgraph/AudioProcessorBase.h
+++ b/src/flowgraph/AudioProcessorBase.h
@@ -79,6 +79,16 @@
*/
int32_t pullData(int64_t framePosition, int32_t numFrames);
+ /**
+ * Recursively reset all the nodes in the graph, starting from a Sink.
+ */
+ void pullReset();
+
+ /**
+ * Reset framePosition counters.
+ */
+ virtual void reset();
+
void addInputPort(AudioPort &port) {
mInputPorts.push_back(port);
}
@@ -100,14 +110,23 @@
mDataPulledAutomatically = automatic;
}
+ virtual const char *getName() {
+ return "AudioProcessorBase";
+ }
+
+ int64_t getLastFramePosition() {
+ return mLastFramePosition;
+ }
+
protected:
- int64_t mLastFramePosition = -1; // Start at -1 so that the first pull works.
+ int64_t mLastFramePosition = 0;
std::vector<std::reference_wrapper<AudioPort>> mInputPorts;
private:
- // FIXME int32_t mFramesValid = 0; // num valid frames in the block
bool mDataPulledAutomatically = true;
+ bool mBlockRecursion = false;
+ int32_t mLastFrameCount = 0;
};
@@ -136,6 +155,8 @@
virtual int32_t pullData(int64_t framePosition, int32_t numFrames) = 0;
+ virtual void pullReset() {}
+
protected:
AudioProcessorBase &mParent;
@@ -217,6 +238,9 @@
*/
int32_t pullData(int64_t framePosition, int32_t numFrames) override;
+
+ void pullReset() override;
+
};
/***************************************************************************/
@@ -283,6 +307,8 @@
*/
int32_t pullData(int64_t framePosition, int32_t numFrames) override;
+ void pullReset() override;
+
private:
AudioFloatOutputPort *mConnected = nullptr;
};
diff --git a/src/flowgraph/ClipToRange.h b/src/flowgraph/ClipToRange.h
index 99b3a95..138c14d 100644
--- a/src/flowgraph/ClipToRange.h
+++ b/src/flowgraph/ClipToRange.h
@@ -57,6 +57,10 @@
AudioFloatInputPort input;
AudioFloatOutputPort output;
+ const char *getName() override {
+ return "ClipToRange";
+ }
+
private:
float mMinimum = kDefaultMinHeadroom;
float mMaximum = kDefaultMaxHeadroom;
diff --git a/src/flowgraph/ManyToMultiConverter.h b/src/flowgraph/ManyToMultiConverter.h
index 5fb2b2b..8a2c480 100644
--- a/src/flowgraph/ManyToMultiConverter.h
+++ b/src/flowgraph/ManyToMultiConverter.h
@@ -39,6 +39,10 @@
std::vector<std::unique_ptr<flowgraph::AudioFloatInputPort>> inputs;
flowgraph::AudioFloatOutputPort output;
+ const char *getName() override {
+ return "ManyToMultiConverter";
+ }
+
private:
};
diff --git a/src/flowgraph/MonoToMultiConverter.h b/src/flowgraph/MonoToMultiConverter.h
index 21972a7..9426e9d 100644
--- a/src/flowgraph/MonoToMultiConverter.h
+++ b/src/flowgraph/MonoToMultiConverter.h
@@ -36,6 +36,10 @@
int32_t onProcess(int32_t numFrames) override;
+ const char *getName() override {
+ return "MonoToMultiConverter";
+ }
+
AudioFloatInputPort input;
AudioFloatOutputPort output;
};
diff --git a/src/flowgraph/RampLinear.h b/src/flowgraph/RampLinear.h
index 44debf6..899f9e6 100644
--- a/src/flowgraph/RampLinear.h
+++ b/src/flowgraph/RampLinear.h
@@ -74,6 +74,10 @@
mLevelTo = level;
}
+ const char *getName() override {
+ return "RampLinear";
+ }
+
AudioFloatInputPort input;
AudioFloatOutputPort output;
diff --git a/src/flowgraph/SampleRateConverter.cpp b/src/flowgraph/SampleRateConverter.cpp
index 58000b6..a1f34c9 100644
--- a/src/flowgraph/SampleRateConverter.cpp
+++ b/src/flowgraph/SampleRateConverter.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "common/OboeDebug.h"
#include "SampleRateConverter.h"
using namespace flowgraph;
@@ -26,14 +25,14 @@
setDataPulledAutomatically(false);
}
-int32_t SampleRateConverter::checkInputFrames() {
+// Return true if these is a sample available.
+bool SampleRateConverter::isInputAvailable() {
if (mInputCursor >= mInputValid) {
mInputValid = input.pullData(mInputFramePosition, input.getFramesPerBuffer());
- LOGD("SampleRateConverter::checkInputFrames: pulled %d", mInputValid);
mInputFramePosition += mInputValid;
mInputCursor = 0;
}
- return mInputValid;
+ return (mInputCursor < mInputValid);
}
const float *SampleRateConverter::getNextInputFrame() {
@@ -44,14 +43,11 @@
int32_t SampleRateConverter::onProcess(int32_t numFrames) {
float *outputBuffer = output.getBuffer();
int32_t channelCount = output.getSamplesPerFrame();
-
int framesLeft = numFrames;
while (framesLeft > 0) {
// Gather input samples as needed.
while(mPhase >= 1.0) {
- if (checkInputFrames() <= 0) {
- break;
- }
+ if (!isInputAvailable()) break;
mPhase -= 1.0;
const float *frame = getNextInputFrame();
mResampler.writeFrame(frame);
diff --git a/src/flowgraph/SampleRateConverter.h b/src/flowgraph/SampleRateConverter.h
index 1897eb3..dd79d3e 100644
--- a/src/flowgraph/SampleRateConverter.h
+++ b/src/flowgraph/SampleRateConverter.h
@@ -44,14 +44,19 @@
AudioFloatInputPort input;
AudioFloatOutputPort output;
+ const char *getName() override {
+ return "SampleRateConverter";
+ }
+
private:
- int32_t checkInputFrames();
+ // Return true if these is a sample available.
+ bool isInputAvailable();
const float *getNextInputFrame();
MultiChannelRateConverter mResampler;
- double mPhase = 0.0;
+ double mPhase = 1.0;
double mPhaseIncrement = 1.0;
int32_t mInputCursor = 0;
int32_t mInputValid = 0;
diff --git a/src/flowgraph/SinkFloat.cpp b/src/flowgraph/SinkFloat.cpp
index 91c50de..bd3435f 100644
--- a/src/flowgraph/SinkFloat.cpp
+++ b/src/flowgraph/SinkFloat.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "common/OboeDebug.h"
#include <algorithm>
#include <unistd.h>
#include "AudioProcessorBase.h"
@@ -27,7 +26,7 @@
}
int32_t SinkFloat::read(int64_t framePosition, void *data, int32_t numFrames) {
- LOGD("SinkFloat::read(,,%d)", numFrames);
+ // printf("SinkFloat::read(,,%d)\n", numFrames);
float *floatData = (float *) data;
int32_t channelCount = input.getSamplesPerFrame();
@@ -35,7 +34,7 @@
while (framesLeft > 0) {
// Run the graph and pull data through the input port.
int32_t framesPulled = pullData(framePosition, framesLeft);
- LOGD("SinkFloat::read: framesLeft = %d, framesPulled = %d", framesLeft, framesPulled);
+ // printf("SinkFloat::read: framesLeft = %d, framesPulled = %d\n", framesLeft, framesPulled);
if (framesPulled <= 0) {
break;
}
@@ -46,5 +45,6 @@
framesLeft -= framesPulled;
framePosition += framesPulled;
}
+ // printf("SinkFloat returning %d\n", numFrames - framesLeft);
return numFrames - framesLeft;
}
diff --git a/src/flowgraph/SinkFloat.h b/src/flowgraph/SinkFloat.h
index 9ff31e9..a0335fd 100644
--- a/src/flowgraph/SinkFloat.h
+++ b/src/flowgraph/SinkFloat.h
@@ -34,6 +34,9 @@
int32_t read(int64_t framePosition, void *data, int32_t numFrames) override;
+ const char *getName() override {
+ return "SinkFloat";
+ }
};
} /* namespace flowgraph */
diff --git a/src/flowgraph/SinkI16.h b/src/flowgraph/SinkI16.h
index faea5ba..f91903b 100644
--- a/src/flowgraph/SinkI16.h
+++ b/src/flowgraph/SinkI16.h
@@ -32,6 +32,10 @@
explicit SinkI16(int32_t channelCount);
int32_t read(int64_t framePosition, void *data, int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SinkI16";
+ }
};
} /* namespace flowgraph */
diff --git a/src/flowgraph/SinkI24.h b/src/flowgraph/SinkI24.h
index 93fdadb..980419b 100644
--- a/src/flowgraph/SinkI24.h
+++ b/src/flowgraph/SinkI24.h
@@ -33,6 +33,10 @@
explicit SinkI24(int32_t channelCount);
int32_t read(int64_t framePosition, void *data, int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SinkI24";
+ }
};
} /* namespace flowgraph */
diff --git a/src/flowgraph/SourceFloat.h b/src/flowgraph/SourceFloat.h
index b87a27b..e89aef1 100644
--- a/src/flowgraph/SourceFloat.h
+++ b/src/flowgraph/SourceFloat.h
@@ -32,6 +32,10 @@
explicit SourceFloat(int32_t channelCount);
int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SourceFloat";
+ }
};
} /* namespace flowgraph */
diff --git a/src/flowgraph/SourceI16.h b/src/flowgraph/SourceI16.h
index 34055c5..c2c4aac 100644
--- a/src/flowgraph/SourceI16.h
+++ b/src/flowgraph/SourceI16.h
@@ -31,6 +31,10 @@
explicit SourceI16(int32_t channelCount);
int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SourceI16";
+ }
};
} /* namespace flowgraph */
diff --git a/src/flowgraph/SourceI24.h b/src/flowgraph/SourceI24.h
index 9176506..9610cfc 100644
--- a/src/flowgraph/SourceI24.h
+++ b/src/flowgraph/SourceI24.h
@@ -32,6 +32,10 @@
explicit SourceI24(int32_t channelCount);
int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SourceI24";
+ }
};
} /* namespace flowgraph */
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index a254ecc..2b73f53 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -28,10 +28,10 @@
testOboe
testUtilities.cpp
testFlowgraph.cpp
-# testStreamClosedMethods.cpp
-# testStreamStates.cpp
-# testStreamWaitState.cpp
-# testStreamOpen.cpp
-# testXRunBehaviour.cpp
+ testStreamClosedMethods.cpp
+ testStreamStates.cpp
+ testStreamWaitState.cpp
+ testStreamOpen.cpp
+ testXRunBehaviour.cpp
)
target_link_libraries(testOboe gtest oboe)
diff --git a/tests/testFlowgraph.cpp b/tests/testFlowgraph.cpp
index eb63ef9..3ae42a7 100644
--- a/tests/testFlowgraph.cpp
+++ b/tests/testFlowgraph.cpp
@@ -18,6 +18,8 @@
* Test FlowGraph
*/
+#include "stdio.h"
+
#include <iostream>
#include <gtest/gtest.h>
@@ -163,12 +165,14 @@
//void foo() {
static const double rateScaler = 0.5;
static const float input[] = {0.0, 1.0, 2.0};
- static const float expected[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 1.0, 1.5};
+ static const float expected[] = {0.0, 0.0, 0.0, 0.5, 1.0, 1.5};
float output[100];
SourceFloat sourceFloat{1};
SampleRateConverter resampler{1};
SinkFloat sinkFloat{1};
+ // printf("test_flowgraph::module_sample_rate_converter ===========================\n");
+
resampler.setPhaseIncrement(rateScaler);
int numInputFrames = sizeof(input) / sizeof(input[0]);
@@ -179,12 +183,16 @@
int numExpectedFrames = sizeof(expected) / sizeof(expected[0]);
int numOutputFrames = sizeof(output) / sizeof(output[0]);
+ // printf("test_flowgraph::module_sample_rate_converter first read -------------\n");
int32_t numRead = sinkFloat.read(0 /* framePosition */, output, numOutputFrames);
EXPECT_EQ(numExpectedFrames, numRead);
constexpr float tolerance = 0.000001f; // arbitrary
for (int i = 0; i < numRead; i++) {
EXPECT_NEAR(expected[i], output[i], tolerance);
+ // printf("test_flowgraph::module_sample_rate_converter output = %f\n", output[i]);
}
+
+ // printf("test_flowgraph::module_sample_rate_converter second read -------------\n");
numRead = sinkFloat.read(numRead /* framePosition */, output, numOutputFrames);
EXPECT_EQ(0, numRead);
}
\ No newline at end of file