In my app, I use an AudioRecorder to detect when an audio signal is received. I have the app working on a single Android device but am getting errors testing on other devices. Namely, I get the error
start() status -38
Here is my code:
protected AudioTrack mAudioTrack;
protected AudioRecord mRecorder;
protected Runnable mRecordFeed = new Runnable() {
#Override
public void run() {
while (mRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
short[] data = new short[mBufferSize/2]; //the buffer size is in bytes
// gets the audio output from microphone to short array samples
mRecorder.read(data, 0, mBufferSize/2);
mDecoder.appendSignal(data);
}
}
};
protected void setupAudioRecorder(){
Log.d(TAG, "set up audio recorder");
//make sure that the settings of the recorder match the settings of the decoder
//most devices cant record anything but 44100 samples in 16bit PCM format...
mBufferSize = AudioRecord.getMinBufferSize(FSKConfig.SAMPLE_RATE_44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
//scale up the buffer... reading larger amounts of data
//minimizes the chance of missing data because of thread priority
mBufferSize *= 10;
//again, make sure the recorder settings match the decoder settings
mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, FSKConfig.SAMPLE_RATE_44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mBufferSize);
if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
mRecorder.startRecording();
//start a thread to read the audio data
Thread thread = new Thread(mRecordFeed);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
else {
Log.i(TAG, "Please check the recorder settings, something is wrong!");
}
}
What does this status -38 mean, and how can I resolve it? I can't seem to find any documentation anywhere.
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();
}
I want to make app like appy birthday, in which i can detect blow whenever user blows in mic, Currently i am using a code which is not efficient as it also detect the noise and normal sound, i am using Media Record class .
I have tried this approach for detecting blow
public boolean isBlowing()
{
boolean recorder=true;
int minSize = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
AudioRecord ar = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000,AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,minSize);
short[] buffer = new short[minSize];
ar.startRecording();
while(recorder)
{
ar.read(buffer, 0, minSize);
for (short s : buffer)
{
if (Math.abs(s) > 27000) //DETECT VOLUME (IF I BLOW IN THE MIC)
{
blow_value=Math.abs(s);
System.out.println("Blow Value="+blow_value);
ar.stop();
recorder=false;
return true;
}
}
}
return false;
}
Guys if you have code that detect only blow not normal sound please provide,
Thanks in advance.
I want to stream an audio file from my android device to another via the MediaRouter Class. As far as I understood I need the
mediarouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_AUDIO, mCallback);
function to listen if a connected Device is playing audio. However I'm not sure about that.
My Question is how do I get the audio stream ?
If you play your audio using AudioTrack you need to make sure that the streamType matches the route's streamType. To get the route's streamType, simply do:
private final MediaRouter.Callback mMediaRouterCallback =
new MediaRouter.Callback() {
#Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
Log.i(TAG, "streamType = " + route.getPlaybackStream());
}
};
Usually it will be AudioManager.STREAM_MUSIC. So when you initialize your AudioTrack make sure to:
_audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
sampleRate,
channelConfig,
audioFormat,
bufferSize,
mode);
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.