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;
}
Related
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);
}
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
I am developing an Android application for a VoIP call and have following issue:
If any other app like Hangouts or Skype is already having a call active, then the mic/recording control stays with it , even if I have called requestAudioFocus API of AudioManager from my application. Playback control comes to my application , but recording control stays with earlier apps.
Is there a way in Android to request the recording focus as well ?
Just like Camera access, only one app at a time can access microphone. All that you can take care of in this scenario is just request microphone and if you don't get it, notify user related with the issue - 'Microphone is used by other application or Microphone is busy'
You can check mic availablity using following code -
private boolean validateMicAvailability(){
Boolean available = true;
AudioRecord recorder =
new AudioRecord(MediaRecorder.AudioSource.MIC, 44100,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_DEFAULT, 44100);
try{
if(recorder.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED ){
available = false;
}
recorder.startRecording();
if(recorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING){
recorder.stop();
available = false;
}
recorder.stop();
} finally{
recorder.release();
recorder = null;
}
return available;
}
You can check indetail post here
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 currently programming an app that uses the microphone to query the sound level. I am using AlarmManager to query the sound level every minute. The problem I am facing is that I discovered if I am using another app that uses the microphone too (e.g. decibel level reader) my app will crash because the microphone is not available. Is there a way to check if the microphone is currently being used or not?
Try Catching exception, as you get exception when you try to use microphone you can handle it.
"The microphone will actually prepare fine even if the microphone is in use"
OR
this code snippet may give you an idea
//returns whether the microphone is available
public static boolean getMicrophoneAvailable(Context context) {
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
recorder.setOutputFile(new File(context.getCacheDir(), "MediaUtil#micAvailTestFile").getAbsolutePath());
boolean available = true;
try {
recorder.prepare();
recorder.start();
}
catch (Exception exception) {
available = false;
}
recorder.release();
return available;
}
if you use AudioRecord then call startRecording() and after that you should check recorder's state: getRecordingState(). If recording was started successfully (it means mic is available), it will return 3 (AudioRecord.RECORDSTATE_RECORDING) otherwise it will return 1 (AudioRecord.RECORDSTATE_STOPPED)
Here's code for this function in Kotlin:
private fun isMicAvailable(audioRecord: AudioRecord): Boolean {
audioRecord.startRecording()
val isAvailable = audioRecord.recordingState == AudioRecord.RECORDSTATE_RECORDING
audioRecord.stop()
audioRecord.release()
return isAvailable
}
I also want to detect whether microphone is being used or not. My solution is using AudioManager to get current mode.
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (am.getMode() == AudioManager.MODE_NORMAL){
//microphone is available.
}
Other modes usage like MODE_IN_COMMUNICATION, MODE_IN_CALL, please check
https://developer.android.com/reference/android/media/AudioManager.html#MODE_NORMAL