22// See the LICENCE file in the repository root for full licence text.
33
44#include " oboe_bridge.h"
5+ #include < oboe/OboeExtensions.h>
56#include < android/log.h>
67#include < cstdint>
78#include < cstring>
@@ -22,16 +23,29 @@ OboeBridge::~OboeBridge() {
2223bool OboeBridge::open () {
2324 std::lock_guard<std::mutex> lock (streamLock_);
2425
26+ // Request MMAP mode globally before opening the stream.
27+ // MMAP provides a hardware-level DMA path that bypasses the kernel audio
28+ // copy, shaving ~1-2 ms off the round-trip latency on supported devices.
29+ oboe::OboeExtensions::setMMapEnabled (true );
30+
2531 oboe::AudioStreamBuilder builder;
2632 builder.setDirection (oboe::Direction::Output)
2733 ->setPerformanceMode (oboe::PerformanceMode::LowLatency)
2834 ->setSharingMode (oboe::SharingMode::Exclusive)
2935 ->setFormat (oboe::AudioFormat::Float)
30- ->setChannelCount (oboe::ChannelCount::Stereo)
36+ // Mono — this stream outputs silence for latency measurement only.
37+ // Mono halves the per-callback buffer vs stereo, reducing the
38+ // minimum achievable latency.
39+ ->setChannelCount (oboe::ChannelCount::Mono)
3140 // Let Oboe pick the device's native sample rate.
3241 // Hardcoding (e.g. 48000) would force Android's SRC resampler when the
3342 // device native rate differs, adding measurable latency.
3443 ->setSampleRate (oboe::kUnspecified )
44+ // Explicitly forbid all internal conversions so that no resampler,
45+ // channel mixer, or format converter sits in the audio path.
46+ ->setChannelConversionAllowed (false )
47+ ->setFormatConversionAllowed (false )
48+ ->setSampleRateConversionQuality (oboe::SampleRateConversionQuality::None)
3549 // Semantic hints help Android route through the optimal audio path.
3650 ->setContentType (oboe::ContentType::Music)
3751 ->setUsage (oboe::Usage::Game)
@@ -62,13 +76,14 @@ bool OboeBridge::open() {
6276 optimiseBufferSize ();
6377
6478 LOGI (" Oboe stream opened: api=%s, sampleRate=%d, framesPerBurst=%d, "
65- " bufferSize=%d, bufferCapacity=%d, sharingMode=%s" ,
79+ " bufferSize=%d, bufferCapacity=%d, sharingMode=%s, mmap=%s " ,
6680 stream_->getAudioApi () == oboe::AudioApi::AAudio ? " AAudio" : " OpenSLES" ,
6781 stream_->getSampleRate (),
6882 stream_->getFramesPerBurst (),
6983 stream_->getBufferSizeInFrames (),
7084 stream_->getBufferCapacityInFrames (),
71- stream_->getSharingMode () == oboe::SharingMode::Exclusive ? " Exclusive" : " Shared" );
85+ stream_->getSharingMode () == oboe::SharingMode::Exclusive ? " Exclusive" : " Shared" ,
86+ oboe::OboeExtensions::isMMapUsed (stream_.get ()) ? " yes" : " no" );
7287
7388 return true ;
7489}
@@ -121,6 +136,7 @@ void OboeBridge::stop() {
121136 }
122137
123138 latencyMs_.store (-1.0 );
139+ callbackCount_.store (0 );
124140 LOGI (" Oboe stream stopped" );
125141}
126142
@@ -152,6 +168,11 @@ bool OboeBridge::isAAudio() const {
152168 return stream_ && stream_->getAudioApi () == oboe::AudioApi::AAudio;
153169}
154170
171+ bool OboeBridge::isMMap () const {
172+ std::lock_guard<std::mutex> lock (const_cast <std::mutex&>(streamLock_));
173+ return stream_ && oboe::OboeExtensions::isMMapUsed (stream_.get ());
174+ }
175+
155176oboe::DataCallbackResult OboeBridge::onAudioReady (
156177 oboe::AudioStream* stream, void * audioData, int32_t numFrames) {
157178
@@ -163,7 +184,13 @@ oboe::DataCallbackResult OboeBridge::onAudioReady(
163184 * sizeof (float );
164185 memset (audioData, 0 , byteCount);
165186
166- updateLatency ();
187+ // Sample latency every 128 callbacks (~250 ms at typical burst/sample rates)
188+ // instead of every single callback. calculateLatencyMillis() issues a
189+ // system call; keeping it out of the majority of callbacks reduces jitter
190+ // in this real-time audio thread.
191+ if ((callbackCount_.fetch_add (1 , std::memory_order_relaxed) & 127 ) == 0 ) {
192+ updateLatency ();
193+ }
167194
168195 return oboe::DataCallbackResult::Continue;
169196}
@@ -297,4 +324,9 @@ OSU_EXPORT unsigned char nOboeIsAAudio(intptr_t ptr) {
297324 return (bridge && bridge->isAAudio ()) ? 1 : 0 ;
298325}
299326
327+ OSU_EXPORT unsigned char nOboeIsMMap (intptr_t ptr) {
328+ auto * bridge = reinterpret_cast <OboeBridge*>(ptr);
329+ return (bridge && bridge->isMMap ()) ? 1 : 0 ;
330+ }
331+
300332} // extern "C"
0 commit comments