android(audio): Usage::VoiceCommunication + MODE_IN_COMMUNICATION, default handset
Withda106bd(Usage::Media + MODE_NORMAL) audio works but is always on the loudspeaker — we want handset as the default with a user-driven toggle for speaker (and later bluetooth). The right Oboe usage for a VoIP app is VoiceCommunication, which honours AudioManager.setSpeakerphoneOn / setBluetoothScoOn for routing. Bisection across previous builds showed that setAudioApi(AAudio) + Usage::VoiceCommunication made the playout callback stop draining the ring after cb#0 (build8c36fb5logs). Letting Oboe pick the AudioApi implicitly keeps the callback alive — 96be740's Media-usage callbacks fired at steady 50Hz without any explicit setAudioApi. So: keep the Usage change, DROP the explicit AAudio force. - oboe_bridge.cpp: Usage::VoiceCommunication, no setAudioApi, no ContentType override. - MainActivity.kt: setMode(MODE_IN_COMMUNICATION) + setSpeakerphoneOn(false) = handset default, plus max both STREAM_VOICE_CALL and STREAM_MUSIC volumes for belt-and-braces. Next build will add a JNI-based Tauri command to flip speakerphoneOn at runtime so the user can toggle handset↔speaker during a call.
This commit is contained in:
@@ -297,6 +297,20 @@ int wzp_oboe_start(const WzpOboeConfig* config, const WzpOboeRings* rings) {
|
||||
// config that at least drove callbacks correctly, plus the
|
||||
// Kotlin-side MODE_IN_COMMUNICATION + setSpeakerphoneOn(true)
|
||||
// handled in MainActivity.kt to route audio to the loud speaker.
|
||||
// Usage::VoiceCommunication is the correct Oboe usage for a VoIP app
|
||||
// — it respects Android's in-call audio routing and lets
|
||||
// AudioManager.setSpeakerphoneOn/setBluetoothScoOn actually switch
|
||||
// between earpiece, loudspeaker, and Bluetooth headset. Combined with
|
||||
// MODE_IN_COMMUNICATION set from MainActivity.kt and
|
||||
// speakerphoneOn=false by default, this produces handset/earpiece as
|
||||
// the default output.
|
||||
//
|
||||
// IMPORTANT: do NOT add setAudioApi(AAudio) here. Build 8c36fb5 proved
|
||||
// forcing AAudio with Usage::VoiceCommunication makes the playout
|
||||
// callback stop draining the ring after cb#0, even though the stream
|
||||
// opens successfully. Letting Oboe pick the API (which will be AAudio
|
||||
// on API ≥ 27 but via a different codepath) kept callbacks firing in
|
||||
// every other build.
|
||||
oboe::AudioStreamBuilder playoutBuilder;
|
||||
playoutBuilder.setDirection(oboe::Direction::Output)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
@@ -305,7 +319,7 @@ int wzp_oboe_start(const WzpOboeConfig* config, const WzpOboeRings* rings) {
|
||||
->setChannelCount(config->channel_count)
|
||||
->setSampleRate(config->sample_rate)
|
||||
->setFramesPerDataCallback(config->frames_per_burst)
|
||||
->setUsage(oboe::Usage::Media)
|
||||
->setUsage(oboe::Usage::VoiceCommunication)
|
||||
->setDataCallback(&g_playout_cb);
|
||||
|
||||
result = playoutBuilder.openStream(g_playout_stream);
|
||||
|
||||
@@ -57,19 +57,20 @@ class MainActivity : TauriActivity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Max out STREAM_MUSIC so the Oboe playout stream (opened with
|
||||
* Usage::Media, which routes to STREAM_MUSIC) is actually audible.
|
||||
* Put the phone into VoIP call mode with handset (earpiece) as the
|
||||
* default output. The Oboe playout stream is opened with
|
||||
* Usage::VoiceCommunication which honours this routing, so:
|
||||
*
|
||||
* DELIBERATELY does NOT call setMode(IN_COMMUNICATION) or
|
||||
* setSpeakerphoneOn: build 8c36fb5 confirmed that combining those with
|
||||
* Usage::Media OR with Usage::VoiceCommunication (both tried) broke the
|
||||
* Oboe playout callback entirely — the ring filled once at startup and
|
||||
* Oboe stopped draining it. Keeping audio mode in MODE_NORMAL so the
|
||||
* Media stream follows the normal speaker-output path, controlled by
|
||||
* the media volume slider.
|
||||
* MODE_IN_COMMUNICATION + speakerphoneOn=false → earpiece (handset)
|
||||
* MODE_IN_COMMUNICATION + speakerphoneOn=true → loudspeaker
|
||||
* MODE_IN_COMMUNICATION + bluetoothScoOn=true → bluetooth headset
|
||||
*
|
||||
* A polished version of the app will setMode/setSpeakerphoneOn on a
|
||||
* per-call basis once we've figured out the correct combo with AAudio.
|
||||
* The speaker/handset/BT toggle itself is wired up via the Tauri
|
||||
* command `set_speakerphone(on)` in a follow-up build. For now the
|
||||
* default is handset, matching the user's stated preference.
|
||||
*
|
||||
* STREAM_VOICE_CALL volume is cranked to max since the in-call volume
|
||||
* slider is separate from media volume on most devices.
|
||||
*/
|
||||
private fun configureAudioForCall() {
|
||||
try {
|
||||
@@ -80,12 +81,19 @@ class MainActivity : TauriActivity() {
|
||||
"musicVol=${am.getStreamVolume(AudioManager.STREAM_MUSIC)}/" +
|
||||
"${am.getStreamMaxVolume(AudioManager.STREAM_MUSIC)}")
|
||||
|
||||
// Crank media volume to max — STREAM_MUSIC is what Usage::Media
|
||||
// plays through. User can adjust with hardware volume buttons.
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
am.isSpeakerphoneOn = false // default: handset / earpiece
|
||||
|
||||
// Crank both voice-call and music volumes so nothing silent slips
|
||||
// through regardless of which stream actually ends up driving.
|
||||
val maxVoice = am.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)
|
||||
am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, maxVoice, 0)
|
||||
val maxMusic = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
am.setStreamVolume(AudioManager.STREAM_MUSIC, maxMusic, 0)
|
||||
|
||||
Log.i(TAG, "audio state after: mode=${am.mode} musicVol=${am.getStreamVolume(AudioManager.STREAM_MUSIC)}/$maxMusic")
|
||||
Log.i(TAG, "audio state after: mode=${am.mode} speaker=${am.isSpeakerphoneOn} " +
|
||||
"voiceVol=${am.getStreamVolume(AudioManager.STREAM_VOICE_CALL)}/$maxVoice " +
|
||||
"musicVol=${am.getStreamVolume(AudioManager.STREAM_MUSIC)}/$maxMusic")
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "configureAudioForCall failed: ${e.message}", e)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user