Android AudioRecord MP3 encoding AudioFormat.CHANNEL_IN_STEREO - android

I seem to be stuck with this problem,
I am trying to get
https://github.com/yhirano/SimpleLameLibForAndroid
to work on channelConfig AudioFormat.CHANNEL_IN_STEREO mode.
Below code works perfectly if i call it with channelConfig = AudioFormat.CHANNEL_IN_MONO but not with STEREO.
I have played around with
short[] buffer = new short[mSampleRate * (16 / 8) * nChannels * 5]
byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];
bu cannot seem to get it working. I mean it works but recorded sound is like very very slow. Listen to this example https://dl.dropboxusercontent.com/u/1465252/1381762795295.mp3
There seems to be another similar question at Lame encoded mp3 audio slowed down - Android without a solution.
Can anybody help?
Here is the code:
new Mp3Audio(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_STEREO, A udioFormat.ENCODING_PCM_16BIT, 128);
public Mp3Audio(int audioSource, int sampleRate, int channelConfig, int audioFormat, int bitRate) {
if (sampleRate <= 0) {
throw new InvalidParameterException(
"Invalid sample rate specified.");
}
mSampleRate = sampleRate;
mBitRate = bitRate;
if (channelConfig == AudioFormat.CHANNEL_IN_MONO) {
nChannels = 1;
} else {
nChannels = 2;
}
builder = new Builder(mSampleRate, nChannels, mSampleRate, mBitRate);
//builder = new Builder(44100, 1, 44100, 128);
builder.quality(6);
mEncoder = builder.create();
cAmplitude = 0;
payloadSize = 0;
aFormat = audioFormat;
aSource = audioSource;
mChannelConfig = channelConfig;
}
public void start() {
final int minBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannelConfig, aFormat) * mBufferSizeFactor;
if (minBufferSize < 0) {
AppHelper.Log(tag, "MSG_ERROR_GET_MIN_BUFFERSIZE");
return;
}
AppHelper.Log(tag, "minBufferSize: " + AppHelper.humanReadableByteCount(minBufferSize, true));
aRecorder = new AudioRecord(
aSource,
mSampleRate,
mChannelConfig,
aFormat,
minBufferSize);
short[] buffer = new short[mSampleRate * (16 / 8) * nChannels * 5]; // SampleRate[Hz] * 16bit * Mono * 5sec
AppHelper.Log(tag, "buffer: " + AppHelper.humanReadableByteCount(buffer.length, true));
byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];
AppHelper.Log(tag, "mp3buffer: " + AppHelper.humanReadableByteCount(mp3buffer.length, true));
......
.......

To give you a pointer, you need to invoke lame_encode_buffer_interleaved() if you use 2 channels (.stereo) to record.
It took me a few days to figure it out, this is the code you can use:
if (lame_get_num_channels(glf) == 2)
{
result = lame_encode_buffer_interleaved(glf, j_buffer_l, samples/2, j_mp3buf, mp3buf_size);
}
else
{
result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r, samples, j_mp3buf, mp3buf_size);
}

Related

Android: confused about how to get amplitude of a frequency generated and played by AudioTrack

In my app I generate list of sounds with different frequencies, for example 1000Hz, 2000Hz, 4000Hz ... for left and right channel:
private AudioTrack generateTone(Ear ear) {
// fill out the array
int numSamples = SAMPLE_RATE * getDurationInSeconds();
double[] sample = new double[numSamples];
byte[] generatedSnd = new byte[2 * numSamples];
for (int i = 0; i < numSamples; ++i) {
sample[i] = Math.sin(2 * Math.PI * i / (SAMPLE_RATE / getLatestFreqInHz()));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
for (final double dVal : sample) {
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, numSamples,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
int channel;
if (ear == Ear.LEFT) {
audioTrack.setStereoVolume(1.0f, 0.0f);
} else if (ear == Ear.RIGHT) {
audioTrack.setStereoVolume(0.0f, 1.0f);
}
return audioTrack;
}
I use this code to control volume:
audioManager = (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
MAX_VOLUME = audioManager.getStreamMaxVolume(STREAM_MUSIC);
MIN_VOLUME = audioManager.getStreamMinVolume(STREAM_MUSIC);
APP_DEFAULT_VOLUME = (MAX_VOLUME + MIN_VOLUME) /2 ;
#Override
public void setToAppDefaultVolume() {
Timber.tag(TAG).d("Resetting to app default sound volume.");
audioManager.setStreamVolume(STREAM_MUSIC, APP_DEFAULT_VOLUME, 0);
}
#Override
public void increaseVolume() {
if (!isReachedMaxVolume()) {
Timber.tag(TAG).d("Increasing device sound volume from %d to %d", currentVolumeLevel(), currentVolumeLevel() + STEP);
audioManager.setStreamVolume(STREAM_MUSIC, currentVolumeLevel() + STEP, 0);
} else {
Timber.tag(TAG).d("Reached the maximum device volume.");
}
}
#Override
public void decreaseVolume() {
if (!isReachedMinVolume()) {
Timber.tag(TAG).d("Decreasing device sound volume from %d to %d", currentVolumeLevel(), currentVolumeLevel() - STEP);
audioManager.setStreamVolume(STREAM_MUSIC, currentVolumeLevel() - STEP, 0);
} else {
Timber.tag(TAG).d("Reached the preferred minimum volume");
}
}
I start playing each tone (frequency) with APP_DEFAULT_VOLUME and gradually increase tone. When user confirm he/she heard specific tone with specific volume, I want to calculate it's amplitude and log it for latter so I can review user hearing ...
But i have no clue how to do so!
All solutions I found was about reading data from microphone and calculate the amplitude to visualize it on screen ...
My scenario is much simpler. I have device volume recorded, frequency is fixed and recorded, and Audio is generated dynamically and is not a file.
Will anyone help me with this scenario ?
Thanks in advance.

Read continues audio stream from socket

I am making an app where I need to read continues stream of sound which is sent in the form of an byte array. The server side records sound like this (based on an example here on SO):
// Get the minimum buffer size required for the successful creation of an AudioRecord object.
int bufferSizeInBytes = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING);
bufferSizeInBytes = 30000;
// Initialize Audio Recorder.
_audio_recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE,
RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSizeInBytes);
// Start Recording.
_audio_recorder.startRecording();
int numberOfReadBytes = 0;
byte audioBuffer[] = new byte[bufferSizeInBytes];
boolean recording = false;
float tempFloatBuffer[] = new float[3];
int tempIndex = 0;
byte totalByteBuffer[] = new byte[60 * 44100 * 2];
while (true)
{
float totalAbsValue = 0.0f;
short sample = 0;
numberOfReadBytes = _audio_recorder.read(audioBuffer, 0, bufferSizeInBytes);
for (int i = 0; i < bufferSizeInBytes; i += 2)
{
sample = (short) ((audioBuffer[i]) | audioBuffer[i + 1] << 8);
totalAbsValue += Math.abs(sample) / (numberOfReadBytes / 2);
}
tempFloatBuffer[tempIndex % 3] = totalAbsValue;
float temp = 0.0f;
for (int i = 0; i < 3; ++i)
temp += tempFloatBuffer[i];
if ((temp >= 0 && temp <= _sensitivity) && recording == false)
{
Log.i("TAG", "1");
tempIndex++;
continue;
}
if (temp > _sensitivity && recording == false)
{
Log.i("TAG", "2");
recording = true;
}
if(temp < _sensitivity && recording == true)
{
recording = false;
continue;
}
for (int i = 0; i < numberOfReadBytes; i++)
totalByteBuffer[i] = audioBuffer[i];
if (prepare_sound(totalByteBuffer, numberOfReadBytes))
{
totalByteBuffer = new byte[60 * 44100 * 2];
tempIndex++;
}
}
The example this is taken from is recording sound and saves it to a file when there is no more sound to record. My goal on the other hand is to record sound when there is sound and send this sound on the fly when still recording. Hence, I want to send sounds right a way and not store it to a file when there is no more sound to record. So far I am taking the byte[] with data and stores it in an object an sends it to a client using ObjectOutputStream. The client will then create a temp sound file and play it using MediaPlayer. But I feel that this is not the most effect way to achieve my goal. So, is there any more efficient way to do this with respect to send an continues stream of data as media player does not support playing pure byte[] of data?
Thanks for any help and tips!
Found out that the best solutions for me is to record the sound and when the buffer is full it is sent to the client side. The client then uses an AudioTrack instance to play the byte[] that contains the data like this:
public void onSoundReceived(byte[] sound)
{
_audio_input_stream.write(sound, 0, sound.length);
}
This also makes the sound more "non-lagging" as this is not a MediaPlayer instance which will stop the sound after each time the data was done playing.

Android sound wave - not smooth and duration issue

I have a problem to generate smooth sinus wave.
I've done it few years ago on C++ and everything worked perfect. Now I am trying to do this using AudioTrack and I do not know what is wrong.
This is my test case:
I want to produce for five second a sinus wave which is smooth (no crack etc.). For one second I generate 44100 samples and divided it on couple of buffer with size 8192 (probably this is the reason of cracks, but how can I fix it, without giving bigger size of buffer).
Unfortunatelly using my code the sound is not smooth and instead of 5 second it takes about 1 second. I would be gratefull for any help.
Please let me now if this piece of code is not enough.
class Constants:
//<---
public final static int SAMPLING = 44100;
public final static int DEFAULT_GEN_DURATION = 1000;
public final static int DEFAULT_NUM_SAMPLES = DEFAULT_GEN_DURATION * SAMPLING / 1000; //44100 per second
public final static int DEFAULT_BUFFER_SIZE = 8192;
//--->
//preparing buffers to play;
Buffer buffer = new Buffer();
short[] buffer_values = new short[Constants.DEFAULT_BUFFER_SIZE];
float[] samples = new float[Constants.DEFAULT_BUFFER_SIZE];
float d = (float) (( Constants.FREQUENCIES[index] * 2 * Math.PI ) / Constants.SAMPLING);
int numSamples = Constants.DEFAULT_NUM_SAMPLES; //44100 per second - for test
float x = 0;
int index_in_buffer = 0;
for(int i = 0; i < numSamples; i++){
if(index_in_buffer >= Constants.DEFAULT_BUFFER_SIZE - 1){
buffer.setBufferShort(buffer_values);
buffer.setBufferSizeShort(index_in_buffer);
queue_with_data_AL.add(buffer); //add buffer to queue
buffer_values = new short[Constants.DEFAULT_BUFFER_SIZE];
samples = new float[Constants.DEFAULT_BUFFER_SIZE];
index_in_buffer = 0;
}
samples[index_in_buffer] = (float) Math.sin(x);
buffer_values[index_in_buffer] = (short) (samples[index_in_buffer] * Short.MAX_VALUE);
x += d;
index_in_buffer++;
}
buffer.setBufferShort(buffer_values);
buffer.setBufferSizeShort(index_in_buffer+1);
queue_with_data_AL.add(buffer);
index_in_buffer = 0;
}
//class AudioPlayer
public AudioPlayer(int sampleRate) { //44100
int minSize = AudioTrack.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
audiotrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
minSize, AudioTrack.MODE_STREAM);
}
public void play(byte[] audioData, int sizeOfBuffer) {
audiotrack.write(audioData, 0, sizeOfBuffer);
}
public void start() {
if (state == Constants.STOP_STATE) {
state = Constants.START_STATE;
int startLength = 0;
while (state == Constants.START_STATE) {
Buffer buffer = getBufferFromQueueAL(); //getting buffer from prepared list
if (buffer != null) {
short[] data = buffer.getBufferShort();
int size_of_data = buffer.getBufferSizeShort();
if (data != null) {
int len = audiotrack.write(data, 0, size_of_data);
if (startLength == 0) {
audiotrack.play();
}
startLength += len;
} else {
break;
}
} else {
MessagesLog.e(TAG, "get null data");
break;
}
}
if (audiotrack != null) {
audiotrack.pause();
audiotrack.flush();
audiotrack.stop();
}
}
}
You are playing only 1 second because 44100 samples at a samplerate of 44100Hz result in exactly 1 second of sound.
You have to generate 5 times more samples if you want to play 5 seconds of sound (e.g. multiply DEFAULT_NUM_SAMPLES by 5) in your code.
I've found the solution by myself. After adding Buffer to queue_with_data_AL I've forgotten create new instance of Buffer object. So in queue was couple of buffer with the same instance, hence sinus wave were not continuous.
Thanks if someone was trying to solve my problem. Unfortunatelly it was my programming mistake.
Best regards.

How to generate and play a 20Hz square wave with AudioTrack?

I'm trying to generate and play a square wave with AudioTrack(Android). I've read lots of tutorials but still have some confusions.
int sampleRate = 44100;
int channelConfig = AudioFormat.CHANNEL_IN_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
AudioTrack audioTrack;
int buffer = AudioTrack.getMinBufferSize(sampleRate, channelConfig,
audioFormat);
audioTrack.write(short[] audioData, int offsetInShorts, int sizeInShorts);
In the codes, what makes me confused is How to write the short array "audioData" ...
Anyone can help me? Thanks in advance !
You should use Pulse-code modulation. The linked article has an example of encoding a sine wave, a square wave is even simpler. Remember that the maximum amplitude is encoded by the maximum value of short (32767) , and that the "effective" frequency depends on your sampling rate.
This method generates Square, Sin and Saw Tooth wave forms
// Process audio
protected void processAudio()
{
short buffer[];
int rate =
AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
int minSize =
AudioTrack.getMinBufferSize(rate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
// Find a suitable buffer size
int sizes[] = {1024, 2048, 4096, 8192, 16384, 32768};
int size = 0;
for (int s : sizes)
{
if (s > minSize)
{
size = s;
break;
}
}
final double K = 2.0 * Math.PI / rate;
// Create the audio track
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, rate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
size, AudioTrack.MODE_STREAM);
// Check audiotrack
if (audioTrack == null)
return;
// Check state
int state = audioTrack.getState();
if (state != AudioTrack.STATE_INITIALIZED)
{
audioTrack.release();
return;
}
audioTrack.play();
// Create the buffer
buffer = new short[size];
// Initialise the generator variables
double f = frequency;
double l = 0.0;
double q = 0.0;
while (thread != null)
{
// Fill the current buffer
for (int i = 0; i < buffer.length; i++)
{
f += (frequency - f) / 4096.0;
l += ((mute ? 0.0 : level) * 16384.0 - l) / 4096.0;
q += (q < Math.PI) ? f * K : (f * K) - (2.0 * Math.PI);
switch (waveform)
{
case SINE:
buffer[i] = (short) Math.round(Math.sin(q) * l);
break;
case SQUARE:
buffer[i] = (short) ((q > 0.0) ? l : -l);
break;
case SAWTOOTH:
buffer[i] = (short) Math.round((q / Math.PI) * l);
break;
}
}
audioTrack.write(buffer, 0, buffer.length);
}
audioTrack.stop();
audioTrack.release();
}
}
Credit goes to billthefarmer.
Complete Source code:
https://github.com/billthefarmer/sig-gen

How to test sound level rms algorithm

My app. is calculating noise level and peak of frequency of input sound.
I used FFT to get array of shorts[] buffer , and this is the code :
bufferSize = 1024, sampleRate = 44100
int bufferSize = AudioRecord.getMinBufferSize(sapleRate,
channelConfiguration, audioEncoding);
AudioRecord audioRecord = new AudioRecord(
MediaRecorder.AudioSource.DEFAULT, sapleRate,
channelConfiguration, audioEncoding, bufferSize);
and this is converting code :
short[] buffer = new short[blockSize];
try {
audioRecord.startRecording();
} catch (IllegalStateException e) {
Log.e("Recording failed", e.toString());
}
while (started) {
int bufferReadResult = audioRecord.read(buffer, 0, blockSize);
/*
* Noise level meter begins here
*/
// Compute the RMS value. (Note that this does not remove DC).
double rms = 0;
for (int i = 0; i < buffer.length; i++) {
rms += buffer[i] * buffer[i];
}
rms = Math.sqrt(rms / buffer.length);
mAlpha = 0.9; mGain = 0.0044;
/*Compute a smoothed version for less flickering of the
// display.*/
mRmsSmoothed = mRmsSmoothed * mAlpha + (1 - mAlpha) * rms;
double rmsdB = 20.0 * Math.log10(mGain * mRmsSmoothed);
Now I want to know if this algorithm works correctly or i'm missing something ?
And I want to know if it was correct and i have sound in dB displayed on mobile , how to test it ?
I need any help please , Thanks in advance :)
The code looks correct but you should probably handle the case where the buffer initially contains zeroes, which could cause Math.log10 to fail, e.g. change:
double rmsdB = 20.0 * Math.log10(mGain * mRmsSmoothed);
to:
double rmsdB = mGain * mRmsSmoothed >.0 0 ?
20.0 * Math.log10(mGain * mRmsSmoothed) :
-999.99; // choose some appropriate large negative value here for case where you have no input signal

Categories

Resources