I am writing an android app for storing and managing voice memos with some basic metadata and tagging. When recording sound I use:
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(currentRecordingFileName);
// and so on
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.
I also tried using MediaRecorder.AudioSource.DEFAULT, hoping it would automatically choose the correct source, but then no sound was recorded at all.
How can I a) detect if a bluetooth headset is plugged in and/or b) use a bluetooth headset as audio source for the media recorder?
olivierg is basically right (AudioSource can still be MIC), some basic code would look like this:
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();
This I stumbled upon this myself just again, I want to point out the importance of slott's comment to include the right permissions, most importantly to set
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
in your manifest file. Without it you will not get any error message but the state will simply not change to connected.
According to the documentation you need to start a SCO audio connection with AudioManager.startBluetoothSco(), and then it seems like you need to use MediaRecorder.AudioSource.VOICE_CALL.
As far as I can see, you can't select a particular device and such. This is performed at system level, ie after the user pairs the headset with the phone.
EDIT:
As mentioned by Stefan, the AudioSource needs to be MIC.
VOICE_CALL doesn't seem to work.
You can detect connected bluetooth devices like this:
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
However, I'm not sure how you make it record from the headset and not the regular MIC
Related
I want to get current playing audio device (like phone, wired headphones or bluetooth device). Do you know how to get?
My code:
val manager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
return if (manager.isBluetoothScoOn) "Bluetooth"
else if (!manager.isBluetoothScoOn && !manager.isSpeakerphoneOn) "Wired"
else "Phone"
I am working on a webrtc android. In this project, I developed Screen Sharing functionality it is working good. but I have two filters during screen sharing user can mute the microphone or mute phone audio. when user mute the microphone then receiver didn't listen to the voice of sender and when user mute the phone audio then receiver only listen the sender audio, not phone audio it is working but my concern is when sender mute microphone but allowed phone audio then receiver has to receiver phone audio, not sender audio e.g. when sender share the screen and play youtube video then receiver has to listen youtube audio, not sender audio. how to get achieve this functionality.
I disable audiotrack but receiver not able to listen both audio means phone audio and microphone audio I also tried AudioDeviceModule adm; adm.setMicrophoneMute(true); it also did the same thing which I explain above
1) final AudioDeviceModule adm = createJavaAudioDevice();
adm.setMicrophoneMute(true);
2) AudioTrack localAudioTrack;
localAudioTrack.setEnabled(true);
I expect when I mute microphone then receiver not listen to the sender's voice and when I mute phone audio receiver will not listen any audio from the background and when I mute microphone and enable phone audio then the receiver has to listen the background audio i.e. youtube or any other audio from the phone. but I am only able to do the mute functionality of the microphone.
You can achieve sharing system audio with AudioPlayback capture API
that is available for android 10 and above Click here for more
detailed implementation
In the WebRtcAudioRecord Class, create an audio record object with
AudioPlaybackCaptureConfiguration, add a method to switch the audio
record object between normal audio record and AudioPlaybackCapture
audio record. We will be able to share only one audio resource at a time , either mic or system audio, not together at a time
public void switchAudioTrack(MediaProjection mediaProjection) {
if(mediaProjection!=null) {
final int channelConfig = channelCountToConfiguration(channels);
int minBufferSize = AudioRecord.getMinBufferSize(samplerate, channelConfig, audioFormat);
int bufferSizeInBytes = Math.max(BUFFER_SIZE_FACTOR * minBufferSize, byteBuffer.capacity());
AudioRecord screenShareAudioRecord = createAudioRecordOnMOrHigher(audioSource, samplerate, channelConfig, audioFormat, bufferSizeInBytes, mediaProjection);
stopRecording();
audioRecord = screenShareAudioRecord;
startRecording();
}else{
stopRecording();
initRecording(samplerate,channels);
startRecording();
}
}
private static AudioRecord createAudioRecordOnMOrHigher(
int audioSource, int sampleRate, int channelConfig, int audioFormat, int bufferSizeInBytes,MediaProjection mediaProjection) {
if(mediaProjection!=null){
AudioPlaybackCaptureConfiguration config = new AudioPlaybackCaptureConfiguration
.Builder(mediaProjection)
.addMatchingUsage(AudioAttributes.USAGE_MEDIA)
.build();
return new AudioRecord.Builder()
.setAudioPlaybackCaptureConfig(config)
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(audioFormat)
.setSampleRate(sampleRate)
.setChannelMask(channelConfig)
.build())
.setBufferSizeInBytes(bufferSizeInBytes)
.build();
}
return new AudioRecord.Builder()
.setAudioSource(audioSource)
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(audioFormat)
.setSampleRate(sampleRate)
.setChannelMask(channelConfig)
.build())
.setBufferSizeInBytes(bufferSizeInBytes)
.build();
}
I am creating Media Player, but it should never play on Speaker. If head phone jack or bluetooth is not available, still Audio should not be played over speaker.
I used below Android API but it still plays over speaker:
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am.setSpeakerphoneOn(false);
You can check the whether Bluetooth and headphone connected or not by using Broadcast receiver using this link http://blog.urvatechlabs.com/detect-programatically-if-headphone-or-bluetooth-headsets-attached-with-android-phone/ . If it is not connected/removed pause/stop the Android Media Player.
From AudioManager official documentation
audioManager.setSpeakerphoneOn(boolean)
Sets the speakerphone on or off.
It means if you set the false it will disable the speaker sound i.e playing out from the speaker and if you set true it will play from the speaker.
In your case, you don't want to play your music from the outer speaker but still your using the am.setSpeakerphoneOn(true); which is actually enables the outer speaker.
So set am.setSpeakerphoneOn(false); so that it won't play the music from outer speaker
You can also set the Mode ( Call / Voice Communication / Music etc) for your AudioManager
audioManager.setMode(AudioManager.STREAM_MUSIC);
Note:: For changing the audio manager settings you need to set Permission: MODIFY_AUDIO_SETTINGS in manifest
add this line in manifest
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
In additional, this is the code to check which type of audio conncection
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
PackageManager packageManager = getPackageManager();
if (audioManager.isBluetoothA2dpOn()) {
// Adjust output for Bluetooth.
Log.d("debug","BluetoothA2dpOn");
} else if (audioManager.isBluetoothScoOn()) {
// Adjust output for Bluetooth of sco.
Log.d("debug","BluetoothScoOn");
} else if (audioManager.isWiredHeadsetOn()) {
// Adjust output for headsets
Log.d("debug","WiredHeadsetOn");
} else if (audioManager.isSpeakerphoneOn()) {
// Adjust output for Speakerphone.
Log.d("debug","SpeakerphoneOn");
} else if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
// Has internal speaker or other form of audio output.
Log.d("debug","Internal Speaker On");
} else {
// No device for audio output.
Log.d("debug","No Audio Device");
}
I'm trying to play an alarm sound through the speakers via the alarm channel at max volume. For that I'm using the AudioManager and a MediaPlayer. If I plug in headphones, the alarm is still played through the speakers, however the volume of the alarm played through the speakers decreases drastically making it useless for my purpose.
Is there a way to prevent this decrease in volume?
The code I'm using is this:
public void startAlarmSound() {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.setSpeakerphoneOn(false);
audioManager.setStreamVolume(AudioManager.STREAM_ALARM, audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
if (!alreadyPlaying)
playAlarmSound();
alreadyPlaying = true;
}
private void playAlarmSound() {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
}
});
try {
mediaPlayer.setDataSource(this, Uri.parse("android.resource://com.mystuff.mine/" + R.raw.alarm_sound));
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
To ensure that the volume has not been lowered, I'm calling the following every 5 seconds.
audioManager.setStreamVolume(AudioManager.STREAM_ALARM, audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
I'm located within the EU, so it could be caused by that regulation that deals with max volume when plugging in headphones. Since I only care about speaker output I need a workaround even if that is the case.
Edit:
This problem occurs both with my app as well as with system apps (like the alarm clock), and with both Nexus 5 and 6. As I've also read reports of that issue from other phone manufacturers, so I don't think the problem is exclusive to the nexus line of phones. I need a workaround.
I just checked the result of both getStreamMaxVolume(AudioManager.STREAM_ALARM) and getStreamVolume(AudioManager.STREAM_ALARM). Both display 7, regardless of if the headphones are plugged in or not.
I did notice that with headphones plugged in, while the volume indicator is set to max, if I reduce it and quickly increase it again, it will increase to the volume that it has without headphones. However as this requires user interaction, it's not the solution I'm looking for.
According to john saying,
This is built in feature of devices, you cannot set volume so high until user will not allow it by himself as it can hurt ears, and the problem is that speaker and headphone volume is not separated
I think that you may not be able to make your volume full. I'dd suggest you trying to look if you can disable headphones (even if plugged in) then play the alarm (full volume as headphones are disabled), then re-enable headphone after alarm is shut down.
Take a look at this or this in order to disable headphones.
I'm doing some test with Intent.ACTION_HEADSET_PLUG.
Giving the fact that the following code should be the one who give the responses (From com.android.server.HeadsetObserver class 2.2.1 r1):
private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
int state = 0;
int microphone = 0;
if ((headset & HEADSETS_WITH_MIC) != 0) {
microphone = 1;
}
if ((headsetState & headset) != 0) {
state = 1;
}
intent.putExtra("state", state);
intent.putExtra("name", headsetName);
intent.putExtra("microphone", microphone);
if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
// TODO: Should we require a permission?
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
}
And in the documentation they say: state - 0 for unplugged, 1 for plugged.
I strangely get two different state by plugging two different headsets:
0 = unplugged
1 = Headset with microphone
2 = Headset without microphone
The question is: where the State 2 (two) come from? Can someone enlighten me?
Thanks
I am using that extra state myself in one of my applications. One of your headsets has a mic the other doesn't. Also make sure you a plugging it in all the way, but don't break anything :)
0 - unplugged as in no headset attached to the device
1 - headset with microphone as in wired headset that had a mic so you can talk and the device uses it as a input as you talk
2 - a headset with no microphone as in your regular old stereo headset that you would normally hook up to your stereo system to listen to music with
This is extremely good info to verify that what was just connected is a wired headset that you expect to be able to talk in to and be heard correctly.