Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LiveEffect no audio out when input is Bluetooth SCO headset #155

Closed
jamescookmd opened this issue Jul 10, 2018 · 12 comments
Closed

LiveEffect no audio out when input is Bluetooth SCO headset #155

jamescookmd opened this issue Jul 10, 2018 · 12 comments
Assignees
Labels
Milestone

Comments

@jamescookmd
Copy link

I'm new to audio, please excuse me if I'm doing something obviously wrong. I'm trying to write an app to help my son with a hearing condition, so I'm trying to get input from a remote Bluetooth microphone, process it, and output to headphones.

I'm using oobe eb236cc (tip-of-master) building the samples Android Studio 3.1.3. I haven't modified the code.

I'm running on a Pixel 1, OMR1 build OPM4.171019.021.D1 (latest stable build with June 5th security updates).

LiveEffect works fine with input Pixel mic and output Pixel speaker (with annoying feedback). Likewise it works fine with output to wired headphones.

I have this Bluetooth "microphone":
https://www.amazon.com/Hey-Mic-Bluetooth-Microphone-Recording/dp/B075K3K8M3/
I think it's actually a headset under the hood (it has an audio output jack!).

After pairing it shows up as an input in LiveEffect, as "HeyMic Bluetooth device typically used for telephony". However, when I tap "Start" and talk into the mic I don't get audio out the headphones.

The mic works with the company's iPhone app, so I don't think the hardware is bad.

Logcat shows AAudio receiving an OPEN_STREAM error below, but then does fallback and appears to succeed.

Should a Bluetooth headset work out-of-the-box as an input? Am I doing something wrong?

07-09 20:08:24.167 7505-7510/com.google.sample.oboe.liveeffect I/zygote64: Compiler allocated 6MB to compile void android.view.ViewRootImpl.performTraversals()
07-09 20:08:26.622 7505-7505/com.google.sample.oboe.liveeffect D/com.google.sample.oboe.liveEffect.MainActivity: Attempting to start
07-09 20:08:26.624 7505-7505/com.google.sample.oboe.liveeffect D/OboeAudio: AudioStreamAAudio() call isSupported()
AudioStreamAAudio(): AAudio_createStreamBuilder()
AudioStreamAAudio.open() try with deviceId = 218
07-09 20:08:26.624 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() called ----------------------------------------
AudioStreamBuilder(): mmapPolicy = 2, mapExclusivePolicy = 2
07-09 20:08:26.624 7505-7505/com.google.sample.oboe.liveeffect D/AudioStreamInternal_Client: AudioStreamInternal(): mWakeupDelayNanos = 200000, mMinimumSleepNanos = 200000
07-09 20:08:26.624 7505-7505/com.google.sample.oboe.liveeffect I/AAudioStream: AudioStream::open() rate = 0, channels = 2, format = 1, sharing = EX, dir = OUTPUT
AudioStream::open() device = 218, perfMode = 12, callback: ON with frames = 0
07-09 20:08:26.633 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() returns 0 = AAUDIO_OK for (0x784724e500) ----------------
07-09 20:08:26.634 7505-7505/com.google.sample.oboe.liveeffect D/OboeAudio: AudioStreamAAudio.open() app format = 1
AudioStreamAAudio.open() native format = 1
AudioStreamAAudio.open() sample rate = 48000
AudioStreamAAudio.open() capacity = 3072
AudioStreamAAudio.open: AAudioStream_Open() returned AAUDIO_OK, mAAudioStream = 0x784724e500
AudioStreamAAudio() call isSupported()
AudioStreamAAudio(): AAudio_createStreamBuilder()
AudioStreamAAudio.open() try with deviceId = 204
07-09 20:08:26.634 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() called ----------------------------------------
AudioStreamBuilder(): mmapPolicy = 2, mapExclusivePolicy = 2
07-09 20:08:26.634 7505-7505/com.google.sample.oboe.liveeffect D/AudioStreamInternal_Client: AudioStreamInternal(): mWakeupDelayNanos = 200000, mMinimumSleepNanos = 200000
07-09 20:08:26.634 7505-7505/com.google.sample.oboe.liveeffect I/AAudioStream: AudioStream::open() rate = 48000, channels = 2, format = 1, sharing = EX, dir = INPUT
AudioStream::open() device = 204, perfMode = 12, callback: OFF with frames = 0
07-09 20:08:26.646 7505-7505/com.google.sample.oboe.liveeffect E/AAudio: BpAAudioService::client OPEN_STREAM passed stream -889
07-09 20:08:26.646 7505-7505/com.google.sample.oboe.liveeffect E/AudioStreamInternal_Client: AudioStreamInternal::open(): openStream() returned -889

07-09 20:08:26.646 7505-7505/com.google.sample.oboe.liveeffect D/AAudioStream: destroying 0x7847250100, state = AAUDIO_STREAM_STATE_UNINITIALIZED
07-09 20:08:26.646 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AudioStreamBuilder.build() MMAP stream did not open so try Legacy path
07-09 20:08:26.646 7505-7505/com.google.sample.oboe.liveeffect I/AAudioStream: AudioStream::open() rate = 48000, channels = 2, format = 1, sharing = EX, dir = INPUT
AudioStream::open() device = 204, perfMode = 12, callback: OFF with frames = 0
07-09 20:08:26.646 7505-7505/com.google.sample.oboe.liveeffect D/AudioStreamRecord: AudioStreamRecord::open(), request notificationFrames = 0, frameCount = 0
07-09 20:08:26.657 7505-7505/com.google.sample.oboe.liveeffect I/AudioRecord: AUDIO_INPUT_FLAG_FAST successful; frameCount 0 -> 4096
07-09 20:08:26.657 7505-7505/com.google.sample.oboe.liveeffect W/AudioStreamRecord: AudioStreamRecord::open() flags changed from 0x00000005 to 0x00000001
07-09 20:08:26.659 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() returns 0 = AAUDIO_OK for (0x782fb69980) ----------------
07-09 20:08:26.659 7505-7505/com.google.sample.oboe.liveeffect D/OboeAudio: AudioStreamAAudio.open() app format = 1
AudioStreamAAudio.open() native format = 1
AudioStreamAAudio.open() sample rate = 48000
AudioStreamAAudio.open() capacity = 4096
AudioStreamAAudio.open: AAudioStream_Open() returned AAUDIO_OK, mAAudioStream = 0x782fb69980
07-09 20:08:26.659 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AAudioStream_requestStart(0x782fb69980) called --------------
07-09 20:08:26.663 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AAudioStream_requestStart(0x782fb69980) returned 0 ---------
AAudioStream_requestStart(0x784724e500) called --------------
07-09 20:08:26.663 7505-7518/com.google.sample.oboe.liveeffect D/AudioStreamLegacy: onAudioDeviceUpdate() deviceId 204
07-09 20:08:26.779 7505-7505/com.google.sample.oboe.liveeffect D/AAudio: AAudioStream_requestStart(0x784724e500) returned 0 ---------
07-09 20:08:26.783 7505-8707/com.google.sample.oboe.liveeffect D/AudioStreamInternal_Client: AudioStreamInternal::onEventFromServer() AAUDIO_SERVICE_EVENT_VOLUME 1.000000
AudioStreamInternal::onEventFromServer() AAUDIO_SERVICE_EVENT_VOLUME 0.023646
AudioStreamInternal::onEventFromServer() got AAUDIO_SERVICE_EVENT_STARTED
07-09 20:08:26.783 7505-8707/com.google.sample.oboe.liveeffect D/AAudio: advanceClientToMatchServerPosition() readN = 2592, writeN = 0, offset = -2592

@jamescookmd
Copy link
Author

BTW, I'm not wedded to this mic hardware, so if there's some other way to do a remote wireless microphone that works with Android low-latency audio I'd be happy to switch. I just need to be able to support output to headphones, so I'm hoping not to use the headphone jack for input.

@dturner
Copy link
Collaborator

dturner commented Jul 10, 2018

Thanks for raising this issue. The logs actually seem fine. The error you see is an attempt to open an AAudio MMAP (memory mapped) stream which is only supported on specific hardware, and definitely won't be supported on a bluetooth headset (for now at least) so Oboe falls back to opening a "legacy" stream which then succeeds.

Are there any errors shown further down the logs when the output stream is opened?

@philburk philburk self-assigned this Jul 10, 2018
@dturner
Copy link
Collaborator

dturner commented Jul 10, 2018

Ignore my comment about the output stream, I can see this is opened successfully with device id 218 before the input stream.

Would you mind detailing the reproduction steps exactly. For example:

  1. Connect bluetooth headset
  2. Load LiveEffect app
  3. and so on...

It just helps us ensure we're doing the exact same thing when we test.

@jamescookmd
Copy link
Author

I'm a Googler in MTV, I can bring the hardware to someone in MTV if that's helpful.

Repro:

  • Unpair HeyMic and reboot phone, just to start fresh
  • Pair HeyMic
  • Plug wired headphones into headphone jack
  • Plug Pixel phone into Mac over USB-C
  • Run LiveEffect from Android Studio on phone
  • Select Recording Device "HeyMic Bluetooth device typically used for telephony"
  • Select Playback Device "Pixel wired headphones"
  • Leave API "AAudio" selected
  • Tap "Start"

Results in no audio in headphones.

I get the same result with a Plantronics M165 Bluetooth headset as the input source. The headset works properly for phone calls.

I added some logging to LiveEffectEngine::onAudioReady(). I see the initial 1/2 second audio drain, the n a bunch of calls mRecordingStream->read() that return framesRead = 96. However, the audioData is always full of 0 after each read().

Logcat shows:
07-10 19:18:42.536 8995-8995/? D/OboeAudio: AAudioLoader(): dlopen(libaaudio.so) returned 0xc86d87f3332faf3b
07-10 19:18:42.536 8995-8995/? V/OboeAudio: AAudioLoader(): dlsym(AAudioStreamBuilder_setChannelCount) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setBufferCapacityInFrames) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setDeviceId) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setDirection) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setFormat) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setFramesPerDataCallback) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setSharingMode) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setPerformanceMode) succeeded.
AAudioLoader(): dlsym(AAudioStreamBuilder_setSampleRate) succeeded.
07-10 19:18:42.536 8995-8995/? E/OboeAudio: AAudioLoader could not find AAudioStreamBuilder_setUsage
AAudioLoader could not find AAudioStreamBuilder_setContentType
AAudioLoader could not find AAudioStreamBuilder_setInputPreset
AAudioLoader could not find AAudioStreamBuilder_setSessionId
07-10 19:18:42.536 8995-8995/? V/OboeAudio: AAudioLoader(): dlsym(AAudioStreamBuilder_delete) succeeded.
AAudioLoader(): dlsym(AAudioStream_getChannelCount) succeeded.
AAudioLoader(): dlsym(AAudioStream_close) succeeded.
AAudioLoader(): dlsym(AAudioStream_getBufferSizeInFrames) succeeded.
AAudioLoader(): dlsym(AAudioStream_getDeviceId) succeeded.
AAudioLoader(): dlsym(AAudioStream_getDirection) succeeded.
AAudioLoader(): dlsym(AAudioStream_getBufferCapacityInFrames) succeeded.
AAudioLoader(): dlsym(AAudioStream_getFramesPerBurst) succeeded.
AAudioLoader(): dlsym(AAudioStream_getFramesRead) succeeded.
AAudioLoader(): dlsym(AAudioStream_getFramesWritten) succeeded.
AAudioLoader(): dlsym(AAudioStream_getPerformanceMode) succeeded.
AAudioLoader(): dlsym(AAudioStream_getSampleRate) succeeded.
AAudioLoader(): dlsym(AAudioStream_getSharingMode) succeeded.
AAudioLoader(): dlsym(AAudioStream_getState) succeeded.
AAudioLoader(): dlsym(AAudioStream_getXRunCount) succeeded.
AAudioLoader(): dlsym(AAudioStream_requestStart) succeeded.
AAudioLoader(): dlsym(AAudioStream_requestPause) succeeded.
AAudioLoader(): dlsym(AAudioStream_requestFlush) succeeded.
AAudioLoader(): dlsym(AAudioStream_requestStop) succeeded.
AAudioLoader(): dlsym(AAudioStream_setBufferSizeInFrames) succeeded.
AAudioLoader(): dlsym(AAudio_convertResultToText) succeeded.
AAudioLoader(): dlsym(AAudio_convertStreamStateToText) succeeded.
07-10 19:18:42.537 8995-8995/? E/OboeAudio: AAudioLoader could not find AAudioStream_getUsage
AAudioLoader could not find AAudioStream_getContentType
AAudioLoader could not find AAudioStream_getInputPreset
AAudioLoader could not find AAudioStream_getSessionId
07-10 19:18:42.555 8995-9002/? I/zygote64: Do partial code cache collection, code=29KB, data=22KB
07-10 19:18:42.556 8995-9002/? I/zygote64: After code cache collection, code=29KB, data=22KB
Increasing code cache capacity to 128KB
07-10 19:18:42.557 8995-9067/? D/OpenGLRenderer: HWUI GL Pipeline
07-10 19:18:42.641 8995-9067/? I/Adreno: QUALCOMM build : 2941438, I916dfac403
Build Date : 10/03/17
OpenGL ES Shader Compiler Version: EV031.21.02.00
Local Branch : O18A
Remote Branch :
Remote Branch :
Reconstruct Branch :
07-10 19:18:42.646 8995-9067/? I/Adreno: PFP: 0x005ff087, ME: 0x005ff063
07-10 19:18:42.650 8995-9067/? I/zygote64: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0
07-10 19:18:42.650 8995-9067/? I/OpenGLRenderer: Initialized EGL, version 1.4
07-10 19:18:42.650 8995-9067/? D/OpenGLRenderer: Swap behavior 2
07-10 19:18:42.688 8995-9002/? I/zygote64: Do partial code cache collection, code=33KB, data=49KB
After code cache collection, code=33KB, data=49KB
Increasing code cache capacity to 256KB
Compiler allocated 7MB to compile void android.widget.TextView.(android.content.Context, android.util.AttributeSet, int, int)
07-10 19:19:24.258 8995-9002/com.google.sample.oboe.liveeffect I/zygote64: Do full code cache collection, code=112KB, data=98KB
After code cache collection, code=102KB, data=72KB
07-10 19:19:24.504 8995-9067/com.google.sample.oboe.liveeffect D/OpenGLRenderer: endAllActiveAnimators on 0x744b5cc400 (DropDownListView) with handle 0x743ee70680
07-10 19:19:33.888 8995-9067/com.google.sample.oboe.liveeffect D/OpenGLRenderer: endAllActiveAnimators on 0x743ee44800 (DropDownListView) with handle 0x743ee70d20
07-10 19:19:53.002 8995-9002/com.google.sample.oboe.liveeffect I/zygote64: Do partial code cache collection, code=124KB, data=98KB
After code cache collection, code=124KB, data=98KB
07-10 19:19:53.003 8995-9002/com.google.sample.oboe.liveeffect I/zygote64: Increasing code cache capacity to 512KB
07-10 19:19:53.025 8995-8995/com.google.sample.oboe.liveeffect D/com.google.sample.oboe.liveEffect.MainActivity: Attempting to start
07-10 19:19:53.028 8995-8995/com.google.sample.oboe.liveeffect D/OboeAudio: AudioStreamAAudio() call isSupported()
AudioStreamAAudio(): AAudio_createStreamBuilder()
AudioStreamAAudio.open() try with deviceId = 20
07-10 19:19:53.028 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() called ----------------------------------------
AudioStreamBuilder(): mmapPolicy = 2, mapExclusivePolicy = 2
07-10 19:19:53.029 8995-8995/com.google.sample.oboe.liveeffect D/AudioStreamInternal_Client: AudioStreamInternal(): mWakeupDelayNanos = 200000, mMinimumSleepNanos = 200000
07-10 19:19:53.029 8995-8995/com.google.sample.oboe.liveeffect I/AAudioStream: AudioStream::open() rate = 0, channels = 2, format = 1, sharing = EX, dir = OUTPUT
AudioStream::open() device = 20, perfMode = 12, callback: ON with frames = 0
07-10 19:19:53.040 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() returns 0 = AAUDIO_OK for (0x745664e500) ----------------
07-10 19:19:53.041 8995-8995/com.google.sample.oboe.liveeffect D/OboeAudio: AudioStreamAAudio.open() app format = 1
AudioStreamAAudio.open() native format = 1
AudioStreamAAudio.open() sample rate = 48000
AudioStreamAAudio.open() capacity = 3072
AudioStreamAAudio.open: AAudioStream_Open() returned AAUDIO_OK, mAAudioStream = 0x745664e500
AudioStreamAAudio() call isSupported()
AudioStreamAAudio(): AAudio_createStreamBuilder()
AudioStreamAAudio.open() try with deviceId = 16
07-10 19:19:53.041 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() called ----------------------------------------
AudioStreamBuilder(): mmapPolicy = 2, mapExclusivePolicy = 2
07-10 19:19:53.042 8995-8995/com.google.sample.oboe.liveeffect D/AudioStreamInternal_Client: AudioStreamInternal(): mWakeupDelayNanos = 200000, mMinimumSleepNanos = 200000
07-10 19:19:53.042 8995-8995/com.google.sample.oboe.liveeffect I/AAudioStream: AudioStream::open() rate = 48000, channels = 2, format = 1, sharing = EX, dir = INPUT
AudioStream::open() device = 16, perfMode = 12, callback: OFF with frames = 0
07-10 19:19:53.055 8995-8995/com.google.sample.oboe.liveeffect E/AAudio: BpAAudioService::client OPEN_STREAM passed stream -889
07-10 19:19:53.055 8995-8995/com.google.sample.oboe.liveeffect E/AudioStreamInternal_Client: AudioStreamInternal::open(): openStream() returned -889
07-10 19:19:53.055 8995-8995/com.google.sample.oboe.liveeffect D/AAudioStream: destroying 0x7456650100, state = AAUDIO_STREAM_STATE_UNINITIALIZED
07-10 19:19:53.055 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AudioStreamBuilder.build() MMAP stream did not open so try Legacy path
07-10 19:19:53.056 8995-8995/com.google.sample.oboe.liveeffect I/AAudioStream: AudioStream::open() rate = 48000, channels = 2, format = 1, sharing = EX, dir = INPUT
AudioStream::open() device = 16, perfMode = 12, callback: OFF with frames = 0
07-10 19:19:53.056 8995-8995/com.google.sample.oboe.liveeffect D/AudioStreamRecord: AudioStreamRecord::open(), request notificationFrames = 0, frameCount = 0
07-10 19:19:53.066 8995-8995/com.google.sample.oboe.liveeffect I/AudioRecord: AUDIO_INPUT_FLAG_FAST successful; frameCount 0 -> 4096
07-10 19:19:53.066 8995-8995/com.google.sample.oboe.liveeffect W/AudioStreamRecord: AudioStreamRecord::open() flags changed from 0x00000005 to 0x00000001
07-10 19:19:53.068 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStreamBuilder_openStream() returns 0 = AAUDIO_OK for (0x743ee74100) ----------------
07-10 19:19:53.068 8995-8995/com.google.sample.oboe.liveeffect D/OboeAudio: AudioStreamAAudio.open() app format = 1
AudioStreamAAudio.open() native format = 1
AudioStreamAAudio.open() sample rate = 48000
AudioStreamAAudio.open() capacity = 4096
AudioStreamAAudio.open: AAudioStream_Open() returned AAUDIO_OK, mAAudioStream = 0x743ee74100
07-10 19:19:53.068 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStream_requestStart(0x743ee74100) called --------------
07-10 19:19:53.072 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStream_requestStart(0x743ee74100) returned 0 ---------
07-10 19:19:53.072 8995-9009/com.google.sample.oboe.liveeffect D/AudioStreamLegacy: onAudioDeviceUpdate() deviceId 16
07-10 19:19:53.072 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStream_requestStart(0x745664e500) called --------------
07-10 19:19:53.178 8995-9009/com.google.sample.oboe.liveeffect W/AudioSystem: ioConfigChanged() modifying unknown output! 85
07-10 19:19:53.675 8995-8995/com.google.sample.oboe.liveeffect D/AAudio: AAudioStream_requestStart(0x745664e500) returned 0 ---------
07-10 19:19:53.676 8995-9298/com.google.sample.oboe.liveeffect D/AudioStreamInternal_Client: AudioStreamInternal::onEventFromServer() AAUDIO_SERVICE_EVENT_VOLUME 1.000000
AudioStreamInternal::onEventFromServer() AAUDIO_SERVICE_EVENT_VOLUME 0.023646
AudioStreamInternal::onEventFromServer() got AAUDIO_SERVICE_EVENT_STARTED
07-10 19:19:53.676 8995-9298/com.google.sample.oboe.liveeffect D/AAudio: processDataNow() wait for valid timestamps
07-10 19:19:53.678 8995-9298/com.google.sample.oboe.liveeffect D/AAudio: processDataNow() wait for valid timestamps
07-10 19:19:53.680 8995-9298/com.google.sample.oboe.liveeffect D/AAudio: processDataNow() wait for valid timestamps
07-10 19:19:53.682 8995-9298/com.google.sample.oboe.liveeffect D/AAudio: advanceClientToMatchServerPosition() readN = 25824, writeN = 0, offset = -25824
07-10 19:19:53.683 8995-8995/com.google.sample.oboe.liveeffect I/Choreographer: Skipped 39 frames! The application may be doing too much work on its main thread.

Hope that helps!

@dturner
Copy link
Collaborator

dturner commented Jul 11, 2018

Thanks James. philburk@ is taking a look at this well who is also in MTV.

I have tried 2 sets of different bluetooth headsets (a Google branded noise-cancelling set and a TaoTronics TT-BH03). I can reproduce the issue on both.

I also tried a recording app. This was unable to record anything using the headset's built-in microphone.

One possible cause is that this is a permissions issue where an app is not allowed to record data from an audio device which is used for telephony. Could be wrong though. Phil will have more info.

@jamescookmd
Copy link
Author

I'll bring the HeyMic to work today. Ping me at jamescook@ if it would be useful to have for testing.

@stereomatch
Copy link

stereomatch commented Jul 22, 2018

Bluetooth microphones will have higher latency compared to a wired headphone (to the point of being unusable for full-duplex use - not because the audio will visibly mismatch the lip movement (not significant problem), but if the user has a good ear and are thus hearing both the real-time audio over the air, as well as through the earphones - a latency of 100ms or more will make it unusable (confusing to the user). If the user can only hear perceptibly through the headphones, latency will be less of an issue (for example the latency on Bluetooth headsets is not a significant issue for phone calls).

If the user want to have ability to hear the android device's own output, you can use an equalizer for system sound like Viper4Android (needs root) - and there may be some other apps for that as well. This provides a 10-band equalizer (I think) - so will help in boosting audio for movies, and android audio.

From an android programming perspective, if you want to use Bluetooth, that has to be enabled explicitly in your code - you have to add extra permissions for BLUETOOTH in AndroidManifest.xml, plus in the java code you have to enable Bluetooth and also register some receivers to monitor the availability of Bluetooth device etc. This is covered in many stackoverflow threads - try to find one which is recent.

Bluetooth in general on android is problematic (on some devices it will give issues - on others you won't get higher than 8000 Hz audio quality). This is why Bluetooth support in audio app is so uneven on Android. iOS have forked their own Bluetooth and adapted it to their needs (which has positives and negatives).

If you want to see how the lowest latency audio sounds like on a hearing aid app on Android Oreo 8.1, try the Dectone app on Google Play (not related to us), or search for Hearing Aid for Oreo 8.1 (which is our app - download the free version - let me know and I can send a Promo Code) - it has few downloads, but we already have gotten good feedback from an audiologist user, and some hearing impaired users via e-mail. These will work best with wired headphones or headset.

Also you need to have Oreo 8.1 to benefit from new low latency AAudio (used only by Oboe for Oreo 8.1+). However, if you have an Oreo 8.0 device (and if you are lucky and have the right android device), you may still be able to benefit from lowest latency using our Hearing Aid for Oreo 8.0 app - the reason I say lucky is because half the android Oreo 8.0 devices out there will crash with AAudio (because of a bugfix in Oreo 8.0 which was not picked by most major manufacters like Samsung, HTC, Xperia).

So if you have an Oreo 8.0 device, the only lowest latency app is our Hearing Aid for Oreo 8.0 app - and that too is not guaranteed to work on all Oreo 8.0 devices (there is a reason we created a separate app for Oreo 8.0).

Oreo 8.1 is the first android version where all android devices are guaranteed to work with AAudio - which is why Oboe later had to switch to using AAudio only in Oreo 8.1+ (skipping Oreo 8.0).

Let me know if you have issues - click Help - Contact from with the Hearing Aid for Oreo 8.1 app for help via e-mail.


As feedback, if you see an increase in latency over time - i.e. starting off with very low latency, but a few minutes later the latency is noticeably higher, let me know. This is a known issue covered here:

#165
AAudio full-duplex latency increasing over time reason identified and needs fix to dataCallback() API in AAudio #165

@philburk philburk added this to the future milestone Oct 2, 2018
@antonkovalyov351
Copy link

@philburk @dturner
Any updates on this issue?
Will Oboe library support BT headset as input?
Will it work for both AAudio and OpenSL?

@jamescookmd
Copy link
Author

Just FYI, I was able to use a Bluetooth headset for input by doing this:

    remoteButton = findViewById(R.id.remoteButton);
    remoteButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.d(TAG, "JAMES Starting bluetooth SCO");
            AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
            am.startBluetoothSco();
        }
    });

    registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
            if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) {
                Log.d(TAG, "JAMES Audio SCO connecting " + state);
            } else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
                Log.d(TAG, "JAMES Connected!");
                /*
                 * Now the connection has been established to the bluetooth device.
                 * Record audio or whatever (on another thread).With AudioRecord you can record with an object created like this:
                 * new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                 * AudioFormat.ENCODING_PCM_16BIT, audioBufferSize);
                 *
                 * After finishing, don't forget to unregister this receiver and
                 * to stop the bluetooth connection with am.stopBluetoothSco();
                 */
                unregisterReceiver(this);
            } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
                Log.d(TAG, "JAMES Audio SCO disconnected " + state);
            }
        }
    }, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));

However, latency is quite high, just due to the Bluetooth connection. It's not really suitable for live effects.

@antonkovalyov351
Copy link

@jamescookmd Thanks for your answer.
I'm curious if it possible to use Bluetooth headset as input using Oboe library.

@philburk
Copy link
Collaborator

philburk commented May 10, 2019

am.startBluetoothSco();

That is the key step.

Do we want to allow the user to control this from a Button?
Or should we start/stop it automatically based on the BroadcastReceiver?

This is related to #471

@philburk
Copy link
Collaborator

I implemented Bluetooth SCO in this PR #896

We should add startBluetoothSco() to the samples and docs. See #909 and 910.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants