AudioRecord buffer size - android

I initialize my AudioRecord instance as follows:
// TODO: remember to add RECORD_AUDIO permission
int audioSource = MediaRecorder.AudioSource.MIC;
// TODO: I should consider Nyquist frequency and tell the user if her device can correctly detect frequencies in the range of her instrument
int sampleRateInHz = getDeviceSampleRate(context);
int channelConfig = AudioFormat.CHANNEL_IN_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
// TODO: bufferSizeInBytes should be set according to minimum detectable frequency in order to have at least three periods
int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
AudioRecord audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);
Here are my questions:
I have to read shorts from the buffer because I specified ENCODING_PCM_16BIT. Is it correct?
If minimum buffer size in bytes is 1000 I'll have 500 shorts. So if I need 4096 samples I have to set bufferSizeInBytes to 8192. Is it correct?
Thanks.

I have to read shorts from the buffer because I specified ENCODING_PCM_16BIT. Is it correct?
You should, but you don't necessarily have to. You may read the samples into a byte[], but it's up to you to handle the endianness when converting the bytes to shorts.
If minimum buffer size in bytes is 1000 I'll have 500 shorts. So if I need 4096 samples I have to set bufferSizeInBytes to 8192. Is it correct?
Actually, no.
The minimum buffer size is the very minimum size that an AudioRecord instance will accept. It's like a threshold. AudioRecord constructor documentation says:
Using values smaller than getMinBufferSize() will result in an initialization failure.
In some cases you may want to use a buffer size bigger than the minimum. AudioRecord.getMinBufferSize() documentation` says:
Note that this size doesn't guarantee a smooth recording under load, and higher values should be chosen according to the expected frequency at which the AudioRecord instance will be polled for new data.
Here's an algorithm for reading 4096 16-bit samples:
ByteArrayOutputStream mainBuffer = new ByteArrayOutputStream();
int minimumBufferSize = AudioRecord.getMinBufferSize(...);
byte[] readBuffer = new byte[minimumBufferSize];
AudioRecord recorder = new AudioRecord(..., minimumBufferSize);
recorder.startRecording();
while (mainBuffer.size() < 8192) {
// read() is a blocking call
int bytesRead = recorder.read(readBuffer, 0, minimumBufferSize);
mainBuffer.write(readBuffer, 0, bytesRead);
}
recorder.stop();
recorder.release();

Related

Audio routing between Android and PC produces white noise

I am trying to send audio between windows and android, I was successfully able to do that windows to windows but when I stream audio from android, it produces a white noise only. I think it is an issue with the AudioFormat in android and Windows because when I changed the sample Bits to 8 I guess, I heard the voice in one side of my headphones but then it went away too.
On Android Side
int BUFFER_MS = 15; // do not buffer more than BUFFER_MS milliseconds
int bufferSize = 48000 * 2 * BUFFER_MS / 1000;
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, 2,
AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
byte[] buffer = new byte[bufferSize];
int bytesRead;
audioTrack.play();
while (socket.isConnected()) {
bytesRead = inputStream.read(buffer, 0, buffer.length);
audioTrack.write(buffer,0,bytesRead);
}
On Windows Side
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
throw new LineUnavailableException(
"The system does not support the specified format.");
}
TargetDataLine audioLine = AudioSystem.getTargetDataLine(format);
audioLine.open(format);
audioLine.start();
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while (socket.isConnected()) {
bytesRead = audioLine.read(buffer, 0, buffer.length);
outputStream.write(buffer,0,bytesRead);
}
and getAudioFormat function is
AudioFormat getAudioFormat() {
float sampleRate = 48000;
int sampleSizeInBits = 16;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,
bigEndian);
}
Only hearing a white noise, if someone can help please do.
Okayyyy So I found out the problem. I just had to put bigEndian to false -_-
It's the byte order difference. I don't understand why it's different in android and pc but seems like it does the trick.

audiotrack: playing noise for a raw pcm 16bit wav file

I am trying to play a wav file of size 230mb and 20 min whose properties are as below:
ffmpeg -i 1.wav
Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
I am learning how to use audiotrack.
I found two solutions to play this audio play using audiotrack.
Solution 1: the following plays the audio
int frequency = 44100;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration,audioEncoding);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency,
channelConfiguration, audioEncoding, bufferSize,
AudioTrack.MODE_STREAM);
int count = 0;
byte[] data = new byte[bufferSize];
try{
FileInputStream fileInputStream = new FileInputStream(listMusicFiles.get(0).listmusicfiles_fullfilepath);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
audioTrack.play();
while((count = dataInputStream.read(data, 0, bufferSize)) > -1){
audioTrack.write(data, 0, count);
}
audioTrack.stop();
audioTrack.release();
dataInputStream.close();
fileInputStream.close();
}
catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Second Solution: Playing noise
int frequency = 44100;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration,audioEncoding);
short[] audiodata = new short[bufferSize];
try {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(
listMusicFiles.get(0).listmusicfiles_fullfilepath)));
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency,
channelConfiguration, audioEncoding, bufferSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
while (dis.available() > 0) {
int i = 0;
while (dis.available() > 0 && i < audiodata.length) {
audiodata[i] = dis.readShort();
i++;
}
audioTrack.write(audiodata, 0, audiodata.length);
}
dis.close();
} catch (Throwable t) {
Log.e("AudioTrack", "Playback Failed");
}
I am new to short sample and byte samples. I tried to understand but it not so easy.
I could understand the first solution is using byte sample and the second solution is using short samples.
So why is the second solution not working.
The default size of a short type is two bytes. You might have a look at this documentation as well.
The audio track has a recommended buffer size and sample rate which can be found using the way suggested here in this answer. Please have a look.
However, it is important to play the audio using the recommended sample rate which is 44100 Hz in your case and the recommended buffer size that you get using the following code segment.
AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding)
In your implementation with short array, the buffer size is doubled and hence its creating noises in case of playing the audio. I would suggest, you might consider changing the buffer size by dividing the size by two in your implementation using short.
short[] audiodata = new short[(int) bufferSize / 2];
Hope you have understood the problem.

Change Bitrate of AudioRecord on Android

How can on Android the bits per sample be changed from 16 bit to 8 bit (or other bitrates) in a AudioRecord object when recording in wav format?
This doesn't work (as discussed for example here: Using AudioRecord with 8-bit encoding in android):
private static int RECORDER_AUDIO_ENCODING =
AudioFormat.ENCODING_PCM_8BIT; -->ERROR: Invalid Audio Format.
bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,RECORDER_SAMPLERATE,
RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
With the header:
private void WriteWaveFileHeader(
FileOutputStream out, long totalAudioLen,
long totalDataLen, long longSampleRate, int channels,
long byteRate) throws IOException {
byte[] header = new byte[44];
header[0] = 'R'; // RIFF/WAVE header
...header content...
header[32] = (byte) (2 * 16 / 8); // block align
header[33] = 0;
header[34] = (byte)RECORDER_BPP; // bits per sample, set to 8
...header content...
out.write(header, 0, 44);
}
You need to record 16 Bit samples by using AudioFormat.ENCODING_PCM_16BIT because 8 Bit encoding is not generally working on Android devices.
Then convert your 16 Bit buffer to 8 Bit on the fly prior to writing it into a wave sound file. But because 8 Bit wave files use unsigned integers (0-255) with silence being 128 instead of 0, my suggested conversion corrects for that. The following is just a code skeleton:
// Your source above already defined a value for bufferSize and the recorder object
sampleBufferShorts = new short[bufferSize];
sampleBufferBytes = new short[bufferSize];
int numberOfShortsRead = recorder.read(samleBufferShorts, 0, bufferSize);
if (numberOfShortsRead != ERROR_INVALID_OPERATION && numberOfShortsRead != ERROR_BAD_VALUE) {
for (int i = 0 ; i < numberOfShortsRead ; ++i)
sampleBufferBytes[i] = (byte)((sampleBufferShorts[i]>>8)+128);
}
See https://stackoverflow.com/a/18777515/1986583 for a useful explanation why additional dithering would improve the resulting quality.
Its not possible, you'll have to postprocess your .wav file.

AudioRecord: buffer overflow?

I'm getting buffer overflow while RECORDING with my app. The recording is performed in a Service. I could not figure out why I'm getting this message from AudioFlinger.
Below I instantiate the AudioRecord object and set it's callbacks.
bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
aRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize);
aRecorder.setRecordPositionUpdateListener(updateListener);
bytesPerSample = bitsPerSample / 8;
int bytesPerFrame = nChannels * bytesPerSample;
framePeriod = bufferSize / bytesPerFrame; // nr of frames that can be kept in a bufferSize dimension
int result = aRecorder.setPositionNotificationPeriod(framePeriod);
buffer = new byte[bufferSize];
The audioRecord callback:
private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener(){
public void onPeriodicNotification(AudioRecord recorder){
int result = aRecorder.read(buffer, 0, buffer.length);
}
public void onMarkerReached(AudioRecord recorder)
{}
};
I suspect the problem is related to the:aRecorder.setPositionNotificationPeriod(framePeriod); - maybe the period is too big for this bufferSize and a faster(smaller) period will solve the issue.
Could someone tells me how to get rid of the buffer overflow?
To fix that issue, change the buffer size of AudioRecord to 2 times the minimum buffer size.
You can use AudioRecord.getMinBufferSize() static method. This will give you the minimum buffer size to use for your current format.
The syntax of getMinBufferSize() method is:
public static int getMinBufferSize (
int sampleRateInHz, int channelConfig, int audioFormat)
Anything less than this number will result in failure while creating the AudioRecord object.
You should have been reducing the buffer size, so as not to overwhelm the audio subsystem with demands for data.
Remember to put the overridden methods (#Override) for audioRecord callback as follows:
private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener(){
#Override
public void onPeriodicNotification(AudioRecord recorder){
int result = aRecorder.read(buffer, 0, buffer.length);
}
#Override
public void onMarkerReached(AudioRecord recorder)
{}
};
I recommend to read the post: Android audio recording, part 2
One more thing that you could try is to use threads on recording and the other process on the recorded bytes, thus avoiding too much overload on the main UI thread.
The open source sample code for this approach: musicg_android_demo
Check this post for more - android-audiorecord-class-process-live-mic-audio-quickly-set-up-callback-func
Thats because :
framePeriod = bufferSize / bytesPerFrame;
You need to multiply and not divide your buffersize.
Try with :
framePeriod = bufferSize * bytesPerFrame;
And if you need a sample : here is a complete audio capture class
hope it helps

Detecting the current volume on Android

I'm building an application that is always recording audio from the microphone, whenever the audio reaches a certain threshold I perform a certain action.
However how should I calc the appropriate volume for the threshold ? I've a static volume coded which works well across some devices but not all devices (in somes cases it is too sensitive or vice versa).
I'm using AudioRecord, here's part of the code:
int bufferSize = AudioRecord.getMinBufferSize(Constants.RECORDING_FREQUENCY,Constants.channelConfiguration,Constants.audioEncoding);
AudioRecord audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC,Constants.RECORDING_FREQUENCY,Constants.channelConfiguration,Constants.audioEncoding, bufferSize);
short[] buffer = new short[bufferSize];
while(true) {
int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
for (int i = 0; i < bufferReadResult; i++) {
currentVolume = java.lang.Math.abs(buffer[i]);
if (currentVolume > Constants.NO_VOLUME_AMPLITUDE)
// ALRIGHT ! This is what I'm looking for :D
}
}
So, my question is: how do I calculate Constants.NO_VOLUME_AMPLITUDE instead of having it hard coded ?
Thanks so much in advance,
Ze

Categories

Resources