oboe: add calculateLatencyMillis()
Based on getTimestamp().
Fixes #69
diff --git a/include/oboe/AudioStream.h b/include/oboe/AudioStream.h
index db171cd..2a825f1 100644
--- a/include/oboe/AudioStream.h
+++ b/include/oboe/AudioStream.h
@@ -158,9 +158,30 @@
virtual int64_t getFramesRead() const { return mFramesRead; }
+ /**
+ * Calculate the latency of a stream based on getTimestamp().
+ *
+ * Latency is the time it takes for a given frame to travel from the
+ * app to the edge of the device or vice versa.
+ *
+ * Note that the latency of an OUTPUT stream will increase when you write data to it
+ * and then decrease over time.
+ *
+ * The latency of an INPUT stream will decrease when you read data from it
+ * and then increase over time.
+ *
+ * The latency of an OUTPUT stream is generally higher than the INPUT latency
+ * because an tries to keep the OUTPUT buffer full and the INPUT buffer empty.
+ *
+ * @return The latency in millisecondssec and Result::OK, or a negative error.
+ */
+ virtual ErrorOrValue<double> calculateLatencyMillis() {
+ return ErrorOrValue<double>(Result::ErrorUnimplemented);
+ }
+
virtual Result getTimestamp(clockid_t clockId,
- int64_t *framePosition,
- int64_t *timeNanoseconds) {
+ int64_t *framePosition,
+ int64_t *timeNanoseconds) {
return Result::ErrorUnimplemented;
}
@@ -173,7 +194,7 @@
* @param buffer The address of the first sample.
* @param numFrames Number of frames to write. Only complete frames will be written.
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
- * @return The number of frames actually written or a negative error.
+ * @return The number of frames actually written and Result::OK, or a negative error.
*/
virtual ErrorOrValue<int32_t> write(const void *buffer,
int32_t numFrames,
diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp
index 0bc6588..6d4b7c0 100644
--- a/src/aaudio/AudioStreamAAudio.cpp
+++ b/src/aaudio/AudioStreamAAudio.cpp
@@ -390,4 +390,46 @@
}
}
+ErrorOrValue<double> AudioStreamAAudio::calculateLatencyMillis() {
+ AAudioStream *stream = mAAudioStream.load();
+ if (stream != nullptr) {
+ bool isOutput = (getDirection() == oboe::Direction::Output);
+
+ // Get the time that a known audio frame was presented.
+ int64_t hardwareFrameIndex;
+ int64_t hardwareFrameHardwareTime;
+ auto result = getTimestamp(CLOCK_MONOTONIC,
+ &hardwareFrameIndex,
+ &hardwareFrameHardwareTime);
+ if (result != oboe::Result::OK) {
+ return ErrorOrValue<double>(static_cast<Result>(result));
+ }
+
+ // Get counter closest to the app.
+ int64_t appFrameIndex = isOutput ? getFramesWritten() : getFramesRead();
+
+ // Assume that the next frame will be processed at the current time
+ using namespace std::chrono;
+ int64_t appFrameAppTime =
+ duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
+
+ // Calculate the number of frames between app and hardware
+ int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex;
+
+ // Calculate the time which the next frame will be or was presented
+ int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / getSampleRate();
+ int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta;
+
+ // The current latency is the difference in time between when the current frame is at
+ // the app and when it is at the hardware.
+ int64_t latencyNanos = (isOutput)
+ ? (appFrameHardwareTime - appFrameAppTime) // hardware is later
+ : (appFrameAppTime - appFrameHardwareTime); // hardware is earlier
+ double latencyMillis = (double) latencyNanos / kNanosPerMillisecond;
+ return ErrorOrValue<double>(latencyMillis);
+ } else {
+ return ErrorOrValue<double>(Result::ErrorNull);
+ }
+}
+
} // namespace oboe
diff --git a/src/aaudio/AudioStreamAAudio.h b/src/aaudio/AudioStreamAAudio.h
index 61b4928..928e0fb 100644
--- a/src/aaudio/AudioStreamAAudio.h
+++ b/src/aaudio/AudioStreamAAudio.h
@@ -76,6 +76,8 @@
int64_t getFramesRead() const override;
int64_t getFramesWritten() const override;
+ ErrorOrValue<double> calculateLatencyMillis() override;
+
Result waitForStateChange(StreamState currentState,
StreamState *nextState,
int64_t timeoutNanoseconds) override;
@@ -86,7 +88,6 @@
StreamState getState() override;
-
AudioApi getAudioApi() const override {
return AudioApi::AAudio;
}