I am writing an android app for Recording Audio from Blue tooth, but I Unable to record Audio via Blue tooth in Android.
You can see below the code
AudioManager am;
am = (AudioManager) getSystemService(AUDIO_SERVICE);
am.setMode(AudioManager.MODE_IN_CALL);
am.startBluetoothSco();
am.setBluetoothScoOn(true);
Intent intent = getIntent();
if (intent.getBooleanExtra("privacy", false)) {
showServerPrompt(true);
return;
}
// If the Ringdroid media select activity was launched via a
// GET_CONTENT intent, then we shouldn't display a "saved"
// message when the user saves, we should just return whatever
// they create.
mWasGetContentIntent = intent.getBooleanExtra(
"was_get_content_intent", false);
mFilename = intent.getData().toString();
mSoundFile = null;
mKeyDown = false;
if (mFilename.equals("record")) {
try {Intent recordIntent = new Intent(
MediaStore.Audio.Media.RECORD_SOUND_ACTION);
startActivityForResult(recordIntent, REQUEST_CODE_RECORD);
} catch (Exception e) {
showFinalAlert(e, R.string.record_error);
}
}
mHandler = new Handler();
loadGui();
mHandler.postDelayed(mTimerRunnable, 100);
if (!mFilename.equals("record")) {
loadFromFile();
}
}
This works well when using the phone in a normal fashion. However, it does not detect the presence of a bluetooth headset and still uses the phone's own microphone even when the headset is plugged in.
working code below
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
Log.d(TAG, "Audio SCO state: " + state);
if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
/*
* 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);
}
}
}, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
Log.d(TAG, "starting bluetooth");
am.startBluetoothSco();
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);
}
Same as this question and many others from a few years ago:
how to turn speaker on/off programmatically in android 4.0
It seems that Android has changed the way it handles this.
here are the things I tried to make an Outgoing call to have a speakerphone programmatically. None of these solutions worked for me on Android Pie.
while they seem to work well on Android Nougat and Oreo
Solution 1.
final static int FOR_MEDIA = 1;
final static int FORCE_NONE = 0;
final static int FORCE_SPEAKER = 1;
Class audioSystemClass = Class.forName("android.media.AudioSystem");
Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class);
setForceUse.invoke(null, FOR_MEDIA, FORCE_SPEAKER);
2.
AudioManager audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
if (audioManager != null) {
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.setSpeakerphoneOn(true);
3.
AudioManager audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
if (audioManager != null) {
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(true);
4.
Thread thread = new Thread() {
#Override
public void run() {
try {
while(true) {
sleep(1000);
audioManager.setMode(AudioManager.MODE_IN_CALL);
if (!audioManager.isSpeakerphoneOn())
audioManager.setSpeakerphoneOn(true);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
the app has the following permissions granted among many others:
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
I have also tried the following solution only for outgoing calls. and it also didn't work.
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.putExtra("speaker", true);
callIntent.setData(Uri.parse("tel:" + number));
context.startActivity(callIntent);
In Android Pie, I had the same problem. I resolved it using an InCallService with
setAudioRoute(ROUTE_SPEAKER)
Your app needs to be Default phone app.
I am surprised nobody mentioned TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE.
As an intent:
val uri = Uri.fromParts("tel", PHONE_NUMBER, null)
val intentCall = Intent(Intent.ACTION_CALL, uri).apply {
putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intentCall)
Using TelecomManager.placeCall:
val uri = Uri.fromParts("tel", PHONE_NUMBER, null)
val extras = Bundle().apply { putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true) }
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
telecomManager.placeCall(uri, extras)
The default phone app should handle that extra data and enable the speakers.
Remember to ask for android.permission.CALL_PHONE permission.
in my self managed voice calling app I managed by changing connection service audio route like below beside using audioManager,
public void setSpeakerphoneOn(final boolean enabled) {
if (enabled != audioManager.isSpeakerphoneOn()) {
Log.d(TAG, "setSpeakerphoneOn(): " + enabled);
audioManager.setSpeakerphoneOn(enabled);
}
}
and in my connectionservice class,
public void speakerOnOff(String uuid,boolean on) {
Log.d(TAG,"SPEAKER is" + String.valueOf(on));
Connection connection = VoipConnectionService.getConnection(uuid);
if (on) {
connection.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
} else {
connection.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
}
}
or you can change route according to your audio device for output
public void speakerOnOff(String uuid,String device) {
Log.d(TAG,"Device to route is" + device);
Connection connection = VoipConnectionService.getConnection(uuid);
int state = CallAudioState.ROUTE_EARPIECE ;
switch (device){
case "SPEAKER_PHONE":
state = CallAudioState.ROUTE_SPEAKER ;
break;
case "WIRED_HEADSET":
state = CallAudioState.ROUTE_WIRED_HEADSET ;
break;
case "EARPIECE" :
case "NONE" :
state = CallAudioState.ROUTE_EARPIECE ;
break;
case "BLUETOOTH" :
state = CallAudioState.ROUTE_BLUETOOTH ;
break;
}
connection.setAudioRoute(state);
}
For this problem please try the following code it is working for me.
AudioManager myAudioManager;
myAudioManager.setMode(AudioManager.MODE_NORMAL);
myAudioManager.setSpeakerphoneOn(true);
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));
how to record Audio from Bluetooth device in Android??? not from device Mic..
when I connect Bluetooth device with Android device then sound recorded via Android device not from Bluetooth device...
manifest file
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
code
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
Log.d(TAG, "Audio SCO state: " + state);
if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
/*
* 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);
}
}
}, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
Log.d(TAG, "starting bluetooth");
am.startBluetoothSco();