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);
Related
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'm creating an Android app that uses a Bluetooth headset to record and stream simultaneously.
When streaming audio while recording with bluetooth headset microphone, It is streamed to a device speaker other than a Bluetooth headset.
(audio stream in webview)
I want to stream to audio with a Bluetooth headset while recording with a Bluetooth headset.
My sample code looks like this.
Connect bluetooth headset :
private BroadcastReceiver mBluetoothScoReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
Log.i(LOG_TAG, "ANDROID Audio SCO state: " + state);
if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
Log.i(LOG_TAG, "bluetooth connected.");
}
}
};
public void setBluetoothMic(Context context){
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
context.registerReceiver(mBluetoothScoReceiver, intentFilter);
audioManager = (AudioManager) context.getSystemService(context.AUDIO_SERVICE);
audioManager.setMode(audioManager.MODE_NORMAL);
audioManager.setBluetoothScoOn(true);
audioManager.startBluetoothSco();
audioManager.setSpeakerphoneOn(false);
}
Recording audio :
AudioRecord record =
new AudioRecord(
MediaRecorder.AudioSource.MIC,
SAMPLE_RATE,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize);
record.startRecording();
Stream audio in webview :
<audio controls id="myAudio">
<source src="001.mp3" type="audio/mpeg">
</audio>
When audioManager.stopBluetoothSco() code run, audio is streamed to the headset.
But audioManager.startBluetoothSco() code run, audio is streamed to the device speaker.
Is it possible to stream audio to the bluetooth headset and record at the same time with the bluetooth headset?
Thanks for reading.
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 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));
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();