I have made an application that records from the phones microphone using the AudioRecord and 16-bit encoding, and I am able to playback the recording. For some compatibility reason I need to use 8-bit encoding, but when I try to run the same program using that encoding I keep getting an Invalid Audio Format. my code is :
int bufferSize = AudioRecord.getMinBufferSize(11025,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_8BIT);
AudioRecord recordInstance = new AudioRecord(
MediaRecorder.AudioSource.MIC, 11025,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_8BIT,
bufferSize);
Any one knows what is the problem? According to the documentation AudioRecord is capable of 8-bit encoding.
If you look at the source, it only supports little endian, but Android is writing out big endian. So you have to convert to little endian and then 8-bit. This worked for me and you can probably combine the two:
for (int i = 0; (offset + i + 1) < bytes.length; i += 2) {
lens[i] = bytes[offset + i + 1];
lens[i + 1] = bytes[offset + i];
}
for (int i = 1, j = 0; i < length; i += 2, j++) {
lens[j] = lens[i];
}
Here is a simpler version without endian
for (int i = 0, j = 0; (offset + i) < bytes.length; i += 2, j++) {
lens[j] = bytes[offset + i];
}
Related
I'm trying to take raw data from the AudioRecord object and save it in a file using a MediaMuxer and MediaCodec.
I start the codec, start the muxer, load data into the input buffers and no such luck.
From debugging investigation, I've found that the problem is occurring in the call to dequeueInputBuffer(). It appears that the first few chunks of data succeed, but eventually dequeueInputBuffer() just returns -1 constantly.
Is there something obvious that I'm missing? This seems like what's happening is I'm filling up the input buffers but they're never being released by the codec.
Snippet of relevant code:
int numChunks = input.length / CHUNKSIZE;
mAudioEncoder.start();
for (int chunk = 0; chunk <= numChunks; chunk++) {
byte[] passMe = new byte[CHUNKSIZE];
int inputBufferIndex = -1;
Log.d("offerAudioEncoder","printing chunk #" + chunk + "of " + numChunks);
//Copy the data into the chunk array
if (chunk < input.length / CHUNKSIZE)
for (int i = 0; i < CHUNKSIZE; i++)
passMe[i] = input[chunk * CHUNKSIZE + i];
else {
eosReceived = true;
for (int i = 0; chunk * CHUNKSIZE + i < input.length; i++)
passMe[i] = input[chunk * CHUNKSIZE + i];
}
//Get the input buffer
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
while(inputBufferIndex < 0)//justk keep trying.
inputBufferIndex = mAudioEncoder.dequeueInputBuffer(100);
inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex);
} else {
//backwards compatibility.
ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
inputBuffer = inputBuffers[inputBufferIndex];
}
//Plop the data into the input buffer
if (inputBuffer != null) {
inputBuffer.clear();
inputBuffer.put(passMe);
}
long presentationTimeUs = chunk * 10000000; //each encoded chunk represents one second of audio
//this is what the frame should be labeled as
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, passMe.length, presentationTimeUs, 0);
//Pull the output buffer.
int encoderStatus = -1;
while(encoderStatus < 0) //Like, seriously, WAIT forever.
encoderStatus = mAudioEncoder.dequeueOutputBuffer(mAudioBufferInfo, -1);//wait forever, why not?
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)
outputBuffer = mAudioEncoder.getOutputBuffer(encoderStatus);
else {
ByteBuffer[] encoderOutputBuffers = mAudioEncoder.getOutputBuffers();
outputBuffer = encoderOutputBuffers[encoderStatus];
}
if(encoderStatus >= 0) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)
mMuxer.writeSampleData(audioTrackIndex, outputBuffer, mAudioBufferInfo);
//Done with the output buffer, release it.
mAudioEncoder.releaseOutputBuffer(encoderStatus, false);
}//TODO: Add cases for what to do when the output format changes
Okay, I figured it out. Ultimately I dumped the chunking logic and just increased the size of the input buffer by setting
audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 14000000);
For the MediaFormat object passed to the MediaCodec's configure method.
Also a good tip: Make sure to use 16-bit audio encoding and to use the AudioRecord.read method that spits out shorts. Bytes seem to produce screwy audio (probably because AudioRecord wants to be operating in 16 bit).
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.
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
Below is the code for my play() method which simply generates an arbitrary set of frequencies and blends them into one tone.
The problem is that it only plays for a split second - I need is to play it continuously. I would appreciate suggestions on how to constantly generate the sound using the AudioTrack class in Android. I believe it has something to do with the MODE_STREAM constant, but I can't quite work out how.
Here is the link to AudioTrack class documentation:
http://developer.android.com/reference/android/media/AudioTrack.html
EDIT: I forgot to mention one important aspect, it can't loop. Due to the mixing of sometimes up to 50+ frequencies, it will sound choppy because there is no least common denominator for all frequency peaks - or it's too far down the waveform to store as one sound.
/**
* play - begins playing the sound
*/
public void play() {
// Get array of frequencies with their relative strengths
double[][] soundData = getData();
// Track samples array
final double samples[] = new double[1024];
// Calculate the average sum in the array and write it to sample
for (int i = 0; i < samples.length; ++i) {
double valueSum = 0;
for (int j = 0; j < soundData.length; j++) {
valueSum += Math.sin(2 * Math.PI * i / (SAMPLE_RATE / soundData[j][0]));
}
samples[i] = valueSum / soundData.length;
}
// Obtain a minimum buffer size
int minBuffer = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
if (minBuffer > 0) {
// Create an AudioTrack
mTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBuffer, AudioTrack.MODE_STREAM);
// Begin playing track
mTrack.play();
// Fill the buffer
if (mBuffer.length < samples.length) {
mBuffer = new short[samples.length];
}
for (int k = 0; k < samples.length; k++) {
mBuffer[k] = (short) (samples[k] * Short.MAX_VALUE);
}
// Write audio data to track for real-time audio sythesis
mTrack.write(mBuffer, 0, samples.length);
}
// Once everything has successfully begun, indicate such.
isPlaying = true;
}
It looks like the code is almost there. It just needs a loop to keep generating the samples, putting them in the buffer, and writing them to the AudioTrack. Right now just one buffer full gets written before it exits which is why it stops so quickly.
void getSamples(double[] samples) {
// Get array of frequencies with their relative strengths
double[][] soundData = getData();
// Calculate the average sum in the array and write it to sample
for (int i = 0; i < samples.length; ++i) {
double valueSum = 0;
for (int j = 0; j < soundData.length; j++) {
valueSum += Math.sin(2 * Math.PI * i / (SAMPLE_RATE / soundData[j][0]));
}
samples[i] = valueSum / soundData.length;
}
}
public void endPlay() {
done = true;
}
/**
* play - begins playing the sound
*/
public void play() {
// Obtain a minimum buffer size
int minBuffer = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
if (minBuffer > 0) {
// Create an AudioTrack
mTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBuffer, AudioTrack.MODE_STREAM);
// Begin playing track
mTrack.play();
// Track samples array
final double samples[] = new double[1024];
while (!done) {
// Fill the buffer
if (mBuffer.length < samples.length) {
mBuffer = new short[samples.length];
}
getSamples(samples);
for (int k = 0; k < samples.length; k++) {
mBuffer[k] = (short) (samples[k] * Short.MAX_VALUE);
}
// Write audio data to track for real-time audio sythesis
mTrack.write(mBuffer, 0, samples.length);
// Once everything has successfully begun, indicate such.
isPlaying = true;
}
}
// Once everything is done, indicate such.
isPlaying = false;
}
I am mixing two 16bit PCM samples into a short buffer.
// This is our buffer for PCM audio data
mp3Buffer = new short[minBufferSize];
wavBuffer = new short[minBufferSize];
mixedBuffer = new short[minBufferSize];
I am filling these buffers with samples from both the mp3 and wav files. I found out that the wav file will always be in mono and the mp3 will always be stereo.
I've read that if you "Just allocate a buffer twice the size of the original PCM data, and for every sample in the original buffer put it twice in the new buffer"
short[] stereoWavBuffer = new short[minBufferSize];
int k = 1;
for (int j = 0; j < minBufferSize / 2; j += 2)
{
stereoWavBuffer[j] = wavBuffer[j];
stereoWavBuffer[k] = wavBuffer[k];
k += 2;
}
// TO DO - Add the 2 buffers together
for (int i = 0; i < minBufferSize; i++){
mixedBuffer[i] = (short)(mp3Buffer[i] + stereoWavBuffer[i]);
}
track.write(mixedBuffer, 0, minBufferSize);
}
How can I accomplish this? I tried this but the wav audio now is at regular speed but sounds like chipmunk.
It looks to me as if your first for loop should be
j < minBufferSize - 1
/2 would mean you will never read all of of the wave buffer or write your entire stereo buffer, - even if you only read half the wave buffer because that's all the data since it's mono you still need to write the entire stereo buffer. Also you need to increment J by 1 not 2 so you read each mono sample.
The speed issue appears to be because you should set stereowavebuffer at j and k both equal to wavebuffer at j. it seems that you are in fact just duplicating half of the original mono file. and then playing it back as stereo (ie: double the byterate).
I would think the first loop should look something more like this
int k = 0;
for (int j = 0; j < minBufferSize / 2; j++) //Assuming you only have half a buffer since mono???
{
stereoWavBuffer[k] = wavBuffer[j];
stereoWavBuffer[k+1] = wavBuffer[j];
k += 2;
}
-edited to fix bad ipad typing!