how to play audio through the phone speaker using MediaPlayer while the user is on a phone call through BT headphones.
I tried this:
...
AudioDeviceInfo audioDeviceInfo = getPhoneSpeaker(context);
if (audioDeviceInfo != null) {
setPreferredDevice(audioDeviceInfo);
}
...
#RequiresApi(Build.VERSION_CODES.M)
public AudioDeviceInfo getPhoneSpeaker(Context context) {
AudioDeviceInfo audioDeviceInfo = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
AudioManager manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (manager != null) {
AudioDeviceInfo[] audioDeviceInfos;
audioDeviceInfos = manager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
if (audioDeviceInfos != null) {
for (AudioDeviceInfo adi : audioDeviceInfos) {
if (adi.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
audioDeviceInfo = adi;
}
}
}
}
}
return audioDeviceInfo;
}
This causes audio to play through the phone speaker, but the problem is that when I play audio the call is terminated in headphones and played through the phone speaker.
Any ideas about how I can avoid this conflict?
Audio Can only be played to standard output devices, speakers etc. It is not possible to play sound during a call.
Try this Twilio Audio Switch SDK
Link: Audio Switch Library
SDK: implementation 'com.twilio:audioswitch:$version-SNAPSHOT'
Listener for the devices
audioSwitch.start { audioDevices, selectedDevice ->
// TODO update UI with audio devices
}
Finding available devices
val devices: List<AudioDevice> = audioSwitch.availableAudioDevices
val selectedDevice: AudioDevice? = audioSwitch.selectedAudioDevice
Related
I have to try create function to change input audio in native module. When I test on samsung(android 13) and realme(android 12) it okay, but when I test on sharp(android 11) and some device have android version lower, it not work, my app always use built-in mic. How can I change input audio in android 11 or lower? thank you.
Code snippet:
#ReactMethod
public void setAudioInput(String route) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
if (route.contains("Bluetooth")) {
audioManager.startBluetoothSco();
audioManager.setBluetoothScoOn(true);
Log.d("setAudioRoute", "bluetooth");
} else if (route.contains("Wired")) {
for (AudioDeviceInfo device : devices) {
if (device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
audioManager.setBluetoothScoOn(false);
audioManager.stopBluetoothSco();
audioManager.setMicrophoneMute(false);
break;
}
}
Log.d("setAudioRoute", "wired headset");
} else {
audioManager.setMicrophoneMute(false);
audioManager.setBluetoothScoOn(false);
audioManager.stopBluetoothSco();
Log.d("setAudioRoute", "Built-in mic");
}
}
Subject
startBluetoothSco do not work when audio mode was set to MODE_IN_COMMUNICATION firstly by other app on android 12 or higher.
Question
I want to let startBluetoothSco work even when it has been set to MODE_IN_COMMUNICATION firstly by another app
Steps
My test steps are as follows:
Connect the device with a Bluetooth headset,
open the first app, let the app set the audio mode to MODE_IN_COMMUNICATION, and keep the recorder or player running. Code example:
AudioManager audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
44100, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
11000);
mAudioRecord.startRecording();
Open the second app, and still set the audio mode to AudioManager.MODE_IN_COMMUNICATION, by the way, startBluetoothSco.
Then the second can not receive any Bluetooth broadcast and SCO does not works.
However, when I kill the first app and rerun the second app, SCO works.
Analysis
According the source code, here is the code:
private CommunicationRouteClient topCommunicationRouteClient() {
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
if (crc.getPid() == mModeOwnerPid) {
return crc;
}
}
if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
return mCommunicationRouteClients.get(0);
}
return null;
}
when the first app sets the mode to MODE_IN_COMMUNICATION and when will become the mode owner
the second app runs startBluetoothSco, and will get null client, and then will do not start SCO.
private void onUpdateCommunicationRoute(String eventSource) {
AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice();
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG,
"onUpdateCommunicationRoute, preferredCommunicationDevice: " +
preferredCommunicationDevice +
" eventSource: " +
eventSource);
}
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
"onUpdateCommunicationRoute, preferredCommunicationDevice: " +
preferredCommunicationDevice + " eventSource: " + eventSource)));
if (preferredCommunicationDevice == null ||
preferredCommunicationDevice.getType() !=
AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
AudioSystem.setParameters("BT_SCO=off");
} else {
AudioSystem.setParameters("BT_SCO=on");
}
if (preferredCommunicationDevice == null) {
removePreferredDevicesForStrategySync(mCommunicationStrategyId);
removePreferredDevicesForStrategySync(mAccessibilityStrategyId);
} else {
setPreferredDevicesForStrategySync(
mCommunicationStrategyId,
Arrays.asList(preferredCommunicationDevice));
setPreferredDevicesForStrategySync(
mAccessibilityStrategyId,
Arrays.asList(preferredCommunicationDevice));
}
onUpdatePhoneStrategyDevice(preferredCommunicationDevice);
}
My app is recording voice, but when another app uses the microphone,
(for example, WhatsApp) then the WhatsApp cannot use microphone because the microphone is being used by my app.
How can I let the microphone share audio with other applications and my app?
Unfortunately another app can not use microphone resource when already one app is using it.
The case is similar to Camera, can not be used by two apps at the same time.
Solution:
Check if microphone is available If not available then show some message that "currently microphone is in use by some other app and can't be used".
Code to check availability of microphone:
private boolean isMicrophoneAvailable() {
Boolean isAvailable = true;
AudioRecord recorder =
new AudioRecord(MediaRecorder.AudioSource.MIC, 44100,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_DEFAULT, 44100);
try {
if (recorder.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) {
isAvailable = false;
}
recorder.startRecording();
if (recorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
recorder.stop();
isAvailable = false;
}
recorder.stop();
} finally {
recorder.release();
recorder = null;
}
return isAvailable;
}
I have a bluetooth headset (which can play stereo music) connected to my android phone (Android 4.4.3). Now I want my code to play a stereo music and record audio from that headset, both at high sampling rates (44100). I followed the solutions in the following posts.
How to record sound using bluetooth headset
Capture Audio through Bluetooth Headset paired with Android Device
My basic code looks like this.
Permissions:
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.RECORD_AUDIO
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.BROADCAST_STICKY
android.permission.BLUETOOTH
Code to turn on Bluetooth Sco:
m_amAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
public void turnOnBluetooth() {
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
System.err.println("bluetooth connected");
unregisterReceiver(this);
} else if (AudioManager.SCO_AUDIO_STATE_DISCONNECTED == state) {
System.err.println("bluetooth disconnected");
}
}
};
registerReceiver(broadcastReceiver, new IntentFilter(
AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
try {
if (m_amAudioManager.isBluetoothScoAvailableOffCall()) {
if (m_amAudioManager.isBluetoothScoOn()) {
m_amAudioManager.stopBluetoothSco();
m_amAudioManager.startBluetoothSco();
System.err.println("Bluetooth SCO On!");
} else {
System.err.println("Bluetooth Sco Off!");
m_amAudioManager.startBluetoothSco();
}
} else {
System.err.println("Bluetooth SCO not available");
}
} catch (Exception e) {
System.err.println("sco elsepart startBluetoothSCO " + e);
unregisterReceiver(broadcastReceiver);
}
}
Code to play a stereo music:
public void playMusic(){
this.mediaPlayer = new MediaPlayer();
this.mediaPlayer
.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
this.mediaPlayer.setDataSource(Environment.getExternalStorageDirectory().
getAbsolutePath()+ "/"+ folderName + "/stereo.wav");
// change type to STREAM_VOICE_CALL can partly solve the problem
// but reduces the quality of the music, which is critical in my case
this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
this.mediaPlayer.prepare();
this.mediaPlayer.start();
}
Code to record audio:
public void recordAudio() {
AudioRecorder recorder = new AudioRecord(
audioSource, // MediaRecorder.AudioSource.MIC
RECORDER_SAMPLERATE, // 44100
RECORDER_CHANNELS, // AudioFormat.CHANNEL_IN_MONO
RECORDER_AUDIO_ENCODING, // AudioFormat.ENCODING_PCM_16BIT
bufferSize // obtained by AudioRecord.getMinBufferSize()
);
int i = recorder.getState();
if (i == 1)
recorder.startRecording();
// then read bytes from the recorder
}
How here are the problems.
Case 1: if I call the following sequence
turnOnBluetooth();
playMusic();
recordAudio();
The music plays through the phone's speaker rather than the bluetooth headset. The recorder can record sound from the bluetooth headset's mic but at very low sampling rate (8kHz).
Case 2: if I do not call turnOnBluetooth(), i.e., execute the following sequence
playMusic();
recordAudio();
The music plays through the bluetooth headset now, but the recorder only records audio from the phone's built-in mic.
I also tried to change the mode of the AudioManager by
m_amAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
and set the audio route by
m_amAudioManager.setSpeakerphoneOn(false);
m_amAudioManager.setBluetoothScoOn(true);
The result is the same to case 1. And if I set
m_amAudioManager.setBluetoothScoOn(false);
It repeats case 2.
I have worked on this for a few days and the above behavior is puzzling me a lot. Have I missed anything in audio settings? Or do I need more sophisticated control with the bluetooth headset's settings? Thanks for reading this and any suggestion is welcome. Thank you!
This may be too late, but here it is my solution.
you need to setup the mediarecorder as follows
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
mediaRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
mediaRecorder.setOutputFile(AudioSavePathInDevice);
in order to play on the bluetooth headset you need to turn on bluetoothsco and turnoff speakers
audioManager.setBluetoothScoOn(true);
audioManager.startBluetoothSco();
audioManager.setSpeakerphoneOn(false);
audioManager.setMode(audioManager.MODE_NORMAL);
for recording at other frequency that 8KHz you need to use the AudioRecorder class
new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, audioBufferSize);
I am trying to redirect all the audio I play in my app to a bluetooth speaker. At first I pair the bluetooth device and then I try to 'say' to the audioManager, that all audio I play should be sent to the bluetooth speeker:
private final BluetoothAdapter _bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
public void pairBluetoothDevice(BluetoothDevice bluetoothDevice)
{
BluetoothSocket socket= bluetoothDevice.createInsecureRfcommSocketToServiceRecord( UUID.fromString( "0000111E-0000-1000-8000-00805F9B34FB" ) );
socket.connect();
_bluetoothAdapter.getProfileProxy( _appContext, _profileListener, BluetoothProfile.HEADSET );
}
private BluetoothProfile.ServiceListener _profileListener = new BluetoothProfile.ServiceListener()
{
public void onServiceConnected( int profile, BluetoothProfile proxy )
{
if ( profile == BluetoothProfile.HEADSET )
{
_bluetoothHeadset = (BluetoothHeadset) proxy;
_bluetoothHeadset.startVoiceRecognition(_device);
AudioManager audioManager = (AudioManager) _appContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.startBluetoothSco();
audioManager.setBluetoothScoOn(true);
audioManager.setSpeakerphoneOn(false);
}
}
public void onServiceDisconnected( int profile )
{
if ( profile == BluetoothProfile.HEADSET )
{
AudioManager audioManager= (AudioManager) _appContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.setBluetoothScoOn(false);
audioManager.stopBluetoothSco();
audioManager.setMode(AudioManager.MODE_NORMAL);
audioManager.setSpeakerphoneOn(true);
_bluetoothHeadset.stopVoiceRecognition(_device);
_bluetoothHeadset= null;
}
}
};
When I play audio ...
_soundPool.play( _soundPoolMap.get( index ), streamVolume, streamVolume, 1, 0, speed );
... I hear nothing.
Thanks for any hint :-)
I found the following workaround:
I open the default bluetooth settings where I can pair the bluetooth speaker and than the audio will be sent to the speaker automatically.
startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));