Android - Getting audio to play through earpiece - android

I currently have code that reads a recording in from the devices mic using the AudioRecord class and then playing it back out using the AudioTrack class.
My problem is that when I play it out it plays via the speaker phone.
I want it to play out via the ear piece on the device.
Here is my code:
public class LoopProg extends Activity {
boolean isRecording; //currently not used
AudioManager am;
int count = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am.setMicrophoneMute(true);
while(count <= 1000000){
Record record = new Record();
record.run();
count ++;
Log.d("COUNT", "Count is : " + count);
}
}
public class Record extends Thread{
static final int bufferSize = 200000;
final short[] buffer = new short[bufferSize];
short[] readBuffer = new short[bufferSize];
public void run() {
isRecording = true;
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
int buffersize = AudioRecord.getMinBufferSize(11025, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
AudioRecord arec = new AudioRecord(MediaRecorder.AudioSource.MIC, 11025, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, buffersize);
AudioTrack atrack = new AudioTrack(AudioManager.STREAM_MUSIC, 11025, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, buffersize, AudioTrack.MODE_STREAM);
am.setRouting(AudioManager.MODE_NORMAL,1, AudioManager.STREAM_MUSIC);
int ok = am.getRouting(AudioManager.ROUTE_EARPIECE);
Log.d("ROUTING", "getRouting = " + ok);
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
//am.setSpeakerphoneOn(true);
Log.d("SPEAKERPHONE", "Is speakerphone on? : " + am.isSpeakerphoneOn());
am.setSpeakerphoneOn(false);
Log.d("SPEAKERPHONE", "Is speakerphone on? : " + am.isSpeakerphoneOn());
atrack.setPlaybackRate(11025);
byte[] buffer = new byte[buffersize];
arec.startRecording();
atrack.play();
while(isRecording) {
arec.read(buffer, 0, buffersize);
atrack.write(buffer, 0, buffer.length);
}
arec.stop();
atrack.stop();
isRecording = false;
}
}
}
As you can see if the code I have tried using the AudioManager class and its methods including the deprecated setRouting method and nothing works, the setSpeakerphoneOn method seems to have no effect at all, neither does the routing method.
Has anyone got any ideas on how to get it to play via the earpiece instead of the spaker phone?

Just got it to work on 2.2. I still needed the In_Call setup which I don't really like but I'll deal with it for now. I was able to ditch the call routing stuff which is deprecated now. I found you definitely need the Modify_Audio_Settings permission, no error without it but it the setSpeakerPhone method just does nothing. Here is the mock up of the code I used.
private AudioManager m_amAudioManager;
m_amAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
m_amAudioManager.setMode(AudioManager.MODE_IN_CALL);
m_amAudioManager.setSpeakerphoneOn(false);

Please use this code, works well:
//PLAY ON EARPIECE
mPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(false);
//PLAY ON SPEAKER
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(true);

There was some related discussion in this recent question:
Android - can I mute currently playing audio applications?
Based on the AudioManager source code, it seems that you must be in "call mode" before the setSpeakerphoneOn method has any effect.
However, I recently saw an application that could seamlessly switch between earpiece and speakerphone while still showing the current stream as the "media" stream, so I would be interested in any further answers.

Misleaded by some answers here for quite a lot of time. I'm using Android 2.2. "audioManager.setSpeakerphoneOn(false);" is working.
audioManager.setSpeakerphoneOn(false);
...
mediaPlayer.setDataSource(..);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
mediaPlayer.prepare();

public MediaPlayer m_mpSpeakerPlayer;
private AudioManager m_amAudioManager;
m_amAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
// 從Receiver Earpiece發音
m_amAudioManager.setSpeakerphoneOn(false);
m_amAudioManager.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
Log.i(TAG, String.valueOf(m_amAudioManager.getRouting(AudioManager.ROUTE_EARPIECE)));
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
// 把聲音設定成從Earpiece出來
// 重點在這行,要把它設定為正在通話中
m_amAudioManager.setMode(AudioManager.MODE_IN_CALL);
// 開始放音樂
m_mpSpeakerPlayer.reset();
m_mpSpeakerPlayer.setDataSource("sdcard/receiver.mp3");
m_mpSpeakerPlayer.prepare();
m_mpSpeakerPlayer.start();
//最後再把它設定從Speaker放音,達成!
m_amAudioManager.setMode(AudioManager.MODE_NORMAL);

I appear to have got it working on 1.6.
So I said I'd post here how I done it.
To get it working in 1.6 I:
Used the AudioManager class to set setSpeakerphoneOn(false), I then used the Voice_Call_Stream and add volume control to the Voice_Call_Stream.
The setSpeakerphoneOn(false) method is used in onCreate() of the activity and this appears to route to the headset, I then used a button and used the setSpeakerphoneOn(true) method and the audio gets routed to the speaker.
The method only appears to work when it is used in onCreate() for me and I haven't tested it extensively but for the moment it allows me to switch between headset and speaker on a 1.6 device

now in 2022 in Android S (12, API31) setSpeakerphoneOn(false) isn't working reliablie, it is in fact deprecated! instead we can use new API and setCommunicationDevice(AudioDeviceInfo) method. (works with AudioManager.STREAM_VOICE_CALL)
some sample
Boolean result = null;
ArrayList<Integer> targetTypes = new ArrayList<>();
if (earpieceMode) {
targetTypes.add(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
} else { // play out loud
targetTypes.add(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
AudioDeviceInfo currentDevice = audioManager.getCommunicationDevice();
if (currentDevice!=null) {
for (Integer targetType : targetTypes) {
if (targetType == currentDevice.getType()) {
Log.i("AUDIO_MANAGER", "setCommunicationDevice targetType ALREADY SET UP!!");
result = true;
break;
}
}
}
if (result == null) {
List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
outer:
for (Integer targetType : targetTypes) {
for (AudioDeviceInfo device : devices) {
if (device.getType() == targetType) {
result = audioManager.setCommunicationDevice(device);
Log.i("AUDIO_MANAGER", "setCommunicationDevice type:" + targetType + " result:" + result);
if (result) break outer;
}
}
}
}
if (result == null) {
Log.i("AUDIO_MANAGER", "setCommunicationDevice targetType NOT FOUND!!");
}
else if (!result) {
Log.i("AUDIO_MANAGER", "setCommunicationDevice targetType FAILED TO SET!!");
}

If the ear piece is connected to the phone with bluetooth (which I assume it is), have you tried calling AudioManager.setBluetoothScoOn(true)?
I've been through the Android reference and this is the only thing I could find that you didn't mention trying.

Related

Android force AudioRecord to use headphone mic

I use AudioRecord to record music but when I record it uses the phone mic.
how can I force him to use the channel of the Headphone?
I use this code:
int minSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
AudioRecord ar = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
recorder[0] = false;
}
}, timeInSecondsToRecord * 1000);
short[] buffer = new short[minSize];
ar.startRecording();
Log.d("Started","Reording");
while (recorder[0]) {
ar.read(buffer, 0, minSize);
for (short s : buffer) {
if (s>1000)
System.out.println("signalVal=" + s);
}
}
Log.d("Finished","Reording");
ar.stop();
Thank you
You can use AudioManager.isWiredHeadsetOn() for checking if the headset are plugged in or not. If the above value is false dont perform any action or whatever you want to do. And also you need permission first: MODIFY_AUDIO_SETTINGS
Hope this helps. :)
I had this problem in reverse (trying to force use of the phone's mic instead of the headphone mic). You can choose a mic by scanning the AudioDeviceInfo array in AudioManager. Here's what I think it would look like for you (if TYPE_LINE_ANALOG doesn't work, check out the AudioDeviceInfo page for more possibilities or debug what devices come up):
AudioRecord audioRecord =
new AudioRecord(...);
// Force use of the line in mic
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
for ( AudioDeviceInfo device : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
if ( device.getType() == AudioDeviceInfo.TYPE_LINE_ANALOG) {
audioRecord.setPreferredDevice(device);
break;
}
}

AudioTrack not playing samples at button clicked sometimes

My android OS is Android M. Nexus 6.
I implemented a AndroidSpeakerWriter as
public class AndroidSpeakerWriter {
private final static String TAG= "AndroidSpeakerWriter";
private AudioTrack audioTrack;
short[] buffer;
public AndroidSpeakerWriter() {
buffer = new short[1024];
}
public void init(int sampleRateInHZ){
int minBufferSize = AudioTrack.getMinBufferSize(sampleRateInHZ,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHZ,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize,
AudioTrack.MODE_STREAM); // 0-static 1-stream
}
public void fillBuffer(short[] samples) {
if (buffer.length<samples.length) {
buffer = new short[samples.length];
}
System.arraycopy(samples, 0, buffer, 0, samples.length);
}
public void writeSamples(short[] samples) {
fillBuffer(samples);
audioTrack.write(buffer, 0, samples.length);
}
public void stop() {
audioTrack.stop();
}
public void play() {
audioTrack.play();
}
}
Then I just send samples when I click a button
public void play(final short[] signal) {
if (signal == null){
Log.d(TAG, "play: a null signal");
return;
}
Thread t = new Thread(new Runnable() {
#Override
public void run() {
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
androidSpeakerWriter.play();
androidSpeakerWriter.writeSamples(signal);
androidSpeakerWriter.stop();
}
});
t.start();
}
The problem is the device does not beep every time I click the button.
Sometimes it works, sometimes it doesn't.
There is no such a problem when I run this on an old nexus galaxy phone android 4.3. Anybody has encountered a similar problem? Thanks in advance for any help.
One thing is that currently my beep is pretty short (256 samples), not even close to the minBufferSize.
The bufferSizeInBytes in the constructor of AudioTrack for static mode should be the audio sample length you wanna play according to the vague document.
So is it still has a minimal size constraint on the buffer even for static mode? Why a nexus galaxy can play a 256 sample audio in static mode and a nexus 6 can not.
I use AudioManager to get the native buffer size/ sampling rate
nexus galaxy: 144/44100 nexus 6: 192/48000
I found those related:
AudioRecord and AudioTrack latency
Does AudioTrack buffer need to be full always in streaming mode?
https://github.com/igorski/MWEngine/wiki/Understanding-Android-audio-towards-achieving-low-latency-response
I believe it is caused by improper synchronization between thread. Your androidSpeakerWriter instance is running continously in different thread calling play(), writeSamples(), stop() respectively. Click of button will trigger creation of new thread with same androidSpeakerWriter instance.
So while Thread A is executing androidSpeakerWriter.play(), Thread B might be executing androidSpeakerWriter.writeSamples() which might overwrite current audio data being played.
Try
synchronized(androidSpeakerWriter) {
androidSpeakerWriter.play();
androidSpeakerWriter.writeSamples(signal);
androidSpeakerWriter.stop();
}
MODE_STREAM is used if you must play long audio data that will not fit into memory. If you need to play short audio file such beep sound, you can use MODE_STATIC when creating AudioTrack. then change your playback code such following:
synchronized(androidSpeakerWriter) {
androidSpeakerWriter.writeSamples(signal);
androidSpeakerWriter.play();
}

Android Java: how to disable microphone input while streaming system audio?

Basically, my program records user input via the microphone and store it as a .pcm file in the sdcard/ directory. It'll be overwritten should there be an existing one. The file is then later sent for playback and analysis (mainly FFT, RMS computation).
I have added another function which allows the program to record system audio, so it allows user's mp3 files to be analyzed as well. It streams the system audio and store it as a .pcm file for later playback and analysis.
It's all functioning well. However, there's a slight issue, when the program streams audio, it captures input from the mic and there'll be noises in the playback. I do not want this as it'll affect the analysis reading. I googled for a solution and found that I can actually mute the mic. So now, I want to mute the mic when the mp3 file is being streamed.
The code I have found is,
AudioManager.setMicrophoneMute(true);
I tried to implement it but it just crashes my application. I tried to find for solutions these few days but I cannot seem to get any.
Here is my code snippet for the part where I want to stream system audio and muting the microphone before it starts streaming.
//create a new AudioRecord object to record the audio data of an mp3 file
int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioRecord = new AudioRecord(AudioManager.STREAM_MUSIC,
frequency, channelConfiguration,
audioEncoding, bufferSize);
//a short array to store raw pcm data
short[] buffer = new short[bufferSize];
Log.i("decoder", "The audio record created fine ready to record");
try {
audioManager.setMicrophoneMute(true);
} catch (Exception e) {
e.printStackTrace();
}
audioRecord.startRecording();
isDecoding = true;
When the setMicrophoneMute(true) line is surrounded with try-catch, the program would only crash when I want to send the recording for play back. Errors are as follow:
"AudioFlinger could not create track, status: -12"
"Error initializing AudioTrack"
"[android.media.AudioTrack] Error code -20 when initializing AudioTrack."
When it is not surrounded with try-catch, the program would just crash the moment I click on the start streaming button.
"Decoding failed" < this is an error log from catching a throwable.
How can I mute the microphone input while streaming the system audio? Let me know if I can provide you with more codes. Thank you!
**EDIT
I have implemented my mutemicrophone successfully, it even returns me a true for isMicrophoneMute(), however, it's not muted as it still records from the microphone; it's a false true.
Based on the suggested answer, I have already created a class for audio focus as below:
private final Context c;
private final AudioManager.OnAudioFocusChangeListener changeListener =
new AudioManager.OnAudioFocusChangeListener()
{
public void onAudioFocusChange(int focusChange)
{
//nothing to do
}
};
AudioFocus(Context context)
{
c = context;
}
public void grabFocus()
{
final AudioManager am = (AudioManager) c.getSystemService(Context.AUDIO_SERVICE);
final int result = am.requestAudioFocus(changeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
Log.d("audiofocus","Grab audio focus: " + result);
}
public void releaseFocus()
{
final AudioManager am = (AudioManager) c.getSystemService(Context.AUDIO_SERVICE);
final int result = am.abandonAudioFocus(changeListener);
Log.d("audiofocus","Abandon audio focus: " + result);
}
I then call the method from my Decoder class to request for audio focus:
int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioFocus.grabFocus();
audioRecord = new AudioRecord(AudioManager.STREAM_MUSIC,
frequency, channelConfiguration,
audioEncoding, bufferSize);
//a short array to store raw pcm data
short[] buffer = new short[bufferSize];
Log.i("decoder", "The audio record created fine ready to record");
audioRecord.startRecording();
isDecoding = true;
Log.i("decoder", "Start recording fine");
And then release the focus when stop decoding is pressed:
//stops recording
public void stopDecoding(){
isDecoding = false;
Log.i("decoder", "Out of recording");
audioRecord.stop();
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
mp.stop();
mp.release();
audioFocus.releaseFocus();
}
However, this makes my application crash. Where did I went wrong?
The following snippet requests permanent audio focus on the music audio stream. You should request the audio focus immediately before you begin playback, such as when the user presses play. I think this would be the way to go rather than muting the input microphone. Check out the developer audio focus docs for more information
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
// Start playback.
}

How To Record Sound in Android with Better Quality and Reduce Noise

I’m trying to build a music analytics app for android platform.
the app is using MediaRecorder.AudioSource.MIC
to record the music form the MIC and them encode it PCM 16BIT with 11025 freq, but the recorded audio sample are very low quality is there any way to make it better, decrease the noise?
mRecordInstance = new AudioRecord(MediaRecorder.AudioSource.MIC,FREQUENCY, CHANNEL,ENCODING, minBufferSize);
mRecordInstance.startRecording();
do
{
samplesIn += mRecordInstance.read(audioData, samplesIn, bufferSize - samplesIn);
if(mRecordInstance.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED)
break;
}
while (samplesIn < bufferSize);
Thanks in Advance
The solution above didnt work for me.
So, i searched around and found this article.
Long story short, I used MediaRecorder.AudioSource.VOICE_RECOGNITION instead of AudioSource.MIC, which gave me really good results and noise in the background did reduce very much.
The great thing about this solution is, it can be used with both AudioRecord and MediaRecorder class.
The best combination of SR and buffer size is very device dependant, so your results will vary depending on the hardware. I use this utility to figure out what the best combination is for devices running Android 4.2 and above;
public static DeviceValues getDeviceValues(Context context) {
try {
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
try {
Method getProperty = AudioManager.class.getMethod("getProperty", String.class);
Field bufferSizeField = AudioManager.class.getField("PROPERTY_OUTPUT_FRAMES_PER_BUFFER");
Field sampleRateField = AudioManager.class.getField("PROPERTY_OUTPUT_SAMPLE_RATE");
int bufferSize = Integer.valueOf((String)getProperty.invoke(am, (String)bufferSizeField.get(am)));
int sampleRate = Integer.valueOf((String)getProperty.invoke(am, (String)sampleRateField.get(am)));
return new DeviceValues(sampleRate, bufferSize);
} catch(NoSuchMethodException e) {
return selectBestValue(getValidSampleRates(context));
}
} catch(Exception e) {
return new DeviceValues(DEFAULT_SAMPLE_RATE, DEFAULT_BUFFER_SIZE);
}
}
This uses reflection to check if the getProperty method is available, because this method was introduced in API level 17. If you are developing for a specific device type, you might want to experiment with various buffer sizes and sample rates. The defaults that I use as a fallback are;
private static final int DEFAULT_SAMPLE_RATE = 22050;
private static final int DEFAULT_BUFFER_SIZE = 1024;
Additionally I check the various SR by seeing if getMinBufferSize returns a reasonable value for use;
private static List<DeviceValues> getValidSampleRates(Context context) {
List<DeviceValues> available = new ArrayList<DeviceValues>();
for (int rate : new int[] {8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000}) { // add the rates you wish to check against
int bufferSize = AudioRecord.getMinBufferSize(rate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
if (bufferSize > 0 && bufferSize < 2048) {
available.add(new DeviceValues(rate, bufferSize * 2));
}
}
return available;
}
This depends on the logic that if getMinBufferSize returns 0, the sample rate is not available in the device. You should experiment with these values for your particular use case.
Though it is an old question following solution will be helpful.
We can use MediaRecorder to record audio with ease.
private void startRecording() {
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setAudioEncodingBitRate(96000)
recorder.setAudioSamplingRate(44100)
recorder.setOutputFile(".../audioName.m4a");
try {
recorder.prepare();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
}
recorder.start();
}
Note:
MediaRecorder.AudioEncoder.AAC is used as MediaRecorder.AudioEncoder.AMR_NB encoding is no longer supported in iOS. Reference
AudioEncodingBitRate should be used either 96000 or 128000 as required for clarity of sound.

Can we check the state of microphone of an android phone, if we can then how can I do this

I want to write a program to check if the internal microphone of android phone is on, off or in use by some other application.
If this is possible then how can I do this?
I read related questions at stack overflow but did not find a solution.
Here's what I'm using to check if the microphone is busy (based on Odaym answer and my own tests):
(Updated with Android 6.0 Marshmallow compatibility, as suggested in comments)
public static boolean checkIfMicrophoneIsBusy(Context ctx){
AudioRecord audio = null;
boolean ready = true;
try{
int baseSampleRate = 44100;
int channel = AudioFormat.CHANNEL_IN_MONO;
int format = AudioFormat.ENCODING_PCM_16BIT;
int buffSize = AudioRecord.getMinBufferSize(baseSampleRate, channel, format );
audio = new AudioRecord(MediaRecorder.AudioSource.MIC, baseSampleRate, channel, format, buffSize );
audio.startRecording();
short buffer[] = new short[buffSize];
int audioStatus = audio.read(buffer, 0, buffSize);
if(audioStatus == AudioRecord.ERROR_INVALID_OPERATION || audioStatus == AudioRecord.STATE_UNINITIALIZED /* For Android 6.0 */)
ready = false;
}
catch(Exception e){
ready = false;
}
finally {
try{
audio.release();
}
catch(Exception e){}
}
return ready;
}
If you are using an AudioRecord object to record audio, like:
AudioRecord audio = new AudioRecord(MediaRecorder.AudioSource.MIC,
Constants.SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,Constants.BUFFER_SIZE_BYTES);
audio.startRecording();
Then right after audio.startRecording(), you're going to have to provide a buffer for reading the audio data into, and begin reading. You do that with:
int audioStatus = audio.read(bufferObject, 0, bufferSize);
The Android documentation for read() mentions the return value ERROR_INVALID_OPERATION (Constant Value: -3), this is only returned when the Mic is busy so you can check for that in your code and show a message that the Audio source is busy with another app.
As far as I know, there is no way to know the microphone's state (Busy, Available,..). Sorry

Categories

Resources