34

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?

B770
  • 1,272
  • 3
  • 17
  • 34
Donal Rafferty
  • 19,707
  • 39
  • 114
  • 191

8 Answers8

29

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); 
Matt Clark
  • 27,671
  • 19
  • 68
  • 123
Piwaf
  • 808
  • 8
  • 9
  • Do you need to set the audio back to the original state before setting to IN_CALL ? something like this: int currAudioMode = audioManager.getMode(); audioManager.setMode(AudioManager.MODE_IN_CALL); audioManager.setSpeakerphoneOn(actionData.speakerOn); audioManager.setMode(currAudioMode); – Muzikant Jun 16 '11 at 09:04
  • I set it back to MODE_NORMAL to get the speaker phone back on. So the above code turns the speaker off and routes to the earpiece, and the below code route sit back to the default of the Speaker. `m_amAudioManager.setMode(AudioManager.MODE_NORMAL);` – Piwaf Aug 10 '11 at 15:39
  • does it disturbs global settings of the phone?? – Awais Tariq Oct 31 '12 at 09:17
  • yes I've noticed when someone call you after the speakerphone are on if you haven't reset them back – plus- Dec 10 '12 at 17:09
  • 3
    This solution does not work on all android devices / versions. See related question -> http://stackoverflow.com/questions/13960313/android-play-audio-from-earpiece/ – Deepak Bala Mar 06 '13 at 18:48
  • 1
    The post referenced by Deepak is worth investigation. I have not tried it but as I posted over there, it does sounds like a better solution. – Piwaf Sep 11 '13 at 18:47
  • Thanks for this, I'm having trouble switching between earpiece and speaker and it is now 2021, you saved me! Basically it needs 3 things if your app is a VOIP app (1) Modify_Audio_Settings permission (2) setMode to either MODE_IN_CALL (if only voice) or MODE_IN_COMMUNICATION (for video + voice) (3) then use setSpeakerphoneOn true or false to toggle between earpiece and speaker without any issues (4) after call is ended, call setMode to switch back to MODE_NORMAL Cheers! – Bruce Mar 04 '21 at 05:39
7

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);
João M
  • 1,939
  • 21
  • 14
4

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.

Community
  • 1
  • 1
Christopher Orr
  • 110,418
  • 27
  • 198
  • 193
  • 1
    Ahh, so I think the device I saw this working on was running 1.5. Unfortunately the AudioManager APIs seem to be often changing between releases. The routing and speakerphone stuff was definitely one area that changed between 1.5 and 1.6. Which sucks. :( – Christopher Orr Jan 25 '10 at 16:42
  • Hi Christopher, I currently use this code to do it and it works in 1.5 audio_service.setSpeakerphoneOn(false); audio_service.setMode(AudioManager.MODE_IN_CALL); audio_service.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL); However its broken from 1.6 on and I cant find the new solution. I have tried setting the SpeakerPhoneOn method to false but it still places out the speaker, I have tried changing all the streams around and still no luck – Donal Rafferty Jan 25 '10 at 16:42
  • Yes,it seems to be fairly broken from 1.6 on which is fairly annoying at the minute, most new devices are using a higher version than 1.5! – Donal Rafferty Jan 25 '10 at 17:54
3

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();
Haisea
  • 1,864
  • 1
  • 13
  • 7
  • 1
    Actually you don't need `audioManager.setSpeakerphoneOn(false);` . And also, you need to create MediaPlayer not with static method `MediaPlayer.create()`, but you need to create it with `new MediaPlayer()...` – Andranik Jun 12 '14 at 18:40
1

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

Gaffi
  • 4,307
  • 8
  • 43
  • 73
Donal Rafferty
  • 19,707
  • 39
  • 114
  • 191
1
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);
sth
  • 222,467
  • 53
  • 283
  • 367
Achigo
  • 17
  • 2
  • The code given by Achigo worked perfectly fine. Your code needs to have **android.permission.MODIFY_AUDIO_SETTINGS** permission set in your manifest file because *setSpeakerPhoneOn()* requires that permission. – MegaMind Nov 23 '10 at 07:42
  • @MegaMind What does this permission provide exactly? I've noticed that volume modification is ok and doesn't need a permission. Is this true? – android developer Jun 07 '16 at 10:18
1

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!!");
}
snachmsm
  • 17,866
  • 3
  • 32
  • 74
0

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.

Jonas Van der Aa
  • 1,441
  • 12
  • 27
  • 2
    It doesn't sound like he's using a Bluetooth headset. The earpiece is the built-in speaker you have your ear against during phone calls. – Christopher Orr Jan 23 '10 at 18:30
  • 1
    Yep as christopher says I'm trying to route the audio through the ear piece that is on the device itself, where people listen to for normal phone calls. I can use the setRouting method and it works in 1.5, however I cant find a solution to routing the audio to the ear piece from 1.6 on – Donal Rafferty Jan 25 '10 at 15:58