I am writing an Android Application for Recording and playing back audio in a loop. Like an echo (Record, Play, run in a loop).
I have successfully written the application for Recording and playing using a temp file, (write into file, read from the same file).
Doing the same using a intermediate buffer is failing.
Here is my code:
public class BufferMain extends ActionBarActivity {
private String TAG = "AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER";
private boolean isRunning = false;
private AudioRecord recorder = null;
private AudioTrack track = null;
/* Buffer used to record and Playback Audio */
byte buffer[] = new byte[640];
int bufferSize;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buffer_main);
enableButton(R.id.StartButton,true);
enableButton(R.id.StopButton,false);
/* Assign Button Click Handlers */
((Button)findViewById(R.id.StartButton)).setOnClickListener(btnClick);
((Button)findViewById(R.id.StopButton)).setOnClickListener(btnClick);
Log.d(TAG, "\n ==Starting Application.. ==");
}
/* Method for Recording and Playing in a loop */
public void run_loop (boolean isRunning)
{
int readbytes, writebytes, state;
Log.d(TAG, "===== Entering Loop ===== ");
if (isRunning == true) {
do {
bufferSize = 640; /* since configuration would return 640 size in buffer */
recorder = findAudioRecord(recorder);
if (recorder == null) {
Log.e(TAG, "====== findAudioRecord : Returned Error! =======");
return;
}
if (recorder.STATE_INITIALIZED == recorder.getState())
{
recorder.startRecording();
readbytes = 0; /* Reset read for next Iteration */
readbytes = recorder.read(buffer, 0, bufferSize);
/* Error Checking Code for AudioRecord */
if(-1 == checkAudioRecordforError(readbytes))
{
Log.d(TAG, "========= Read Error =========");
return;
}
Log.d(TAG, "\n\n===== Read : Sizeof Buffer:"+buffer.length+"=== readbytes: ["+readbytes+"]=====");
recorder.stop();
recorder.release();
}else {
state = recorder.getState();
Log.d(TAG, "========= Else Part : state : "+state+"=========");
}
Log.d(TAG, "========= Reading Completed =========");
/** ======== API for Playing =========== **/
track = findAudioTrack(track);
if (track == null) {
Log.e(TAG, "========= findAudioTrack : Returned Error! ========== ");
return;
}
if (track.STATE_INITIALIZED == track.getState())
{
writebytes = 0; /* Reset write bytes for next Iteration */
track.play();
writebytes = track.write(buffer, 0, bufferSize);
Log.d(TAG, "\n\n===== Write : Sizeof Buffer:"+ bufferSize +"==== writebytes: ["+writebytes+"]======== ");
/* Error Checking Code for AudioRecord */
if (-1 == checkAudioTrackforError(writebytes))
{
Log.d(TAG, "========= Write Error =========");
return;
}
track.stop();
track.release();
}
else {
state = track.getState();
Log.d(TAG, "========= Else Part : state : "+state+"=========");
}
} while(true);
}
return;
}
/* Method for Initializing AudioTrack for Playing Purpose */
public AudioTrack findAudioTrack (AudioTrack track)
{
Log.d(TAG, "========== Initialising Playing API ============");
bufferSize = AudioTrack.getMinBufferSize(8000,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT); /* Return 640 */
Log.d(TAG, "========= AudioTrack ==> bufferSize : "+bufferSize+"=========");
if (bufferSize != AudioTrack.ERROR_BAD_VALUE)
{
track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize,
AudioTrack.MODE_STREAM);
if (track.getState() == AudioTrack.STATE_UNINITIALIZED) {
Log.e(TAG, "=========== AudioTrack UnInitilaised ===========");
return null;
}
}
return track;
}
/* Method for Initializing AudioRecord for Recording Purpose */
public AudioRecord findAudioRecord (AudioRecord recorder)
{
Log.d(TAG, "======== Initializing Record API ==========");
bufferSize = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
if (bufferSize != AudioRecord.ERROR_BAD_VALUE)
{
recorder = new AudioRecord(AudioSource.DEFAULT, 8000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize);
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
Log.e(TAG, "========= AudioRecord UnInitilaised ============= ");
return null;
}
}
return recorder;
}
/* Method for Error Checking - AudioRecord */
public int checkAudioRecordforError(int readbytes)
{
if (readbytes == recorder.ERROR_INVALID_OPERATION || readbytes == recorder.ERROR_BAD_VALUE)
{
if(readbytes == AudioRecord.ERROR_INVALID_OPERATION)
Log.d(TAG, "========= read Error : ERROR_INVALID_OPERATION ===========");
else if (readbytes == AudioRecord.ERROR_BAD_VALUE)
Log.d(TAG, "========= read Error : ERROR_BAD_VALUE ===========");
else if (readbytes == AudioRecord.ERROR)
Log.d(TAG, "========= read Error : ERROR Unknown ===========");
return -1;
}
return readbytes;
}
/* Method for Error Checking - AudioTrack */
public int checkAudioTrackforError(int writebytes)
{
if (writebytes == track.ERROR_INVALID_OPERATION || writebytes == track.ERROR_BAD_VALUE)
{
if(writebytes == track.ERROR_INVALID_OPERATION)
Log.d(TAG, "========= read Error : ERROR_INVALID_OPERATION ===========");
else if (writebytes == track.ERROR_BAD_VALUE)
Log.d(TAG, "========= read Error : ERROR_BAD_VALUE ===========");
else if (writebytes == track.ERROR)
Log.d(TAG, "========= read Error : ERROR Unknown ===========");
return -1;
}
return writebytes;
}
/* Method for Mapping Button Click */
private View.OnClickListener btnClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.StartButton:
{
Log.d(TAG, "======= Start Recording =======");
isRunning = true;
Log.d(TAG, "========== isRunning = true =============");
run_loop (isRunning);
enableButton(R.id.StartButton,false);
enableButton(R.id.StopButton,true);
break;
}
case R.id.StopButton:
{
Log.d(TAG, "======== Stop Recording =========");
isRunning = false;
Log.d(TAG, "========== isRunning = false =============");
run_loop (isRunning);
enableButton(R.id.StopButton,false);
enableButton(R.id.StartButton,true);
break;
}
}
}
};
/* Function to Enable/Disable Buttons */
private void enableButton(int id,boolean isEnable){
((Button)findViewById(id)).setEnabled(isEnable);
}
}
There is no read write failure anywhere.
Logcat Output:
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ==Starting Application.. ==
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ========Start Recording========
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ========== isRunning = true =============
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ===== Entering Loop =====
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): =========== Initializing Record API =======
E/ACDB-LOADER( 213): Error: ACDB AudProc vol returned = -19
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ===== Read : Sizeof Buffer:640=== readbytes: [640]=====
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ========= Reading Completed =========
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ========== Initializing Playing API ========
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ========= AudioTrack ==> bufferSize : 640=========
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): ===== Write : Sizeof Buffer:640==== writebytes: [640]========
D/AUDIO_RECORD_PLAYBACK_SAURABH_BUFFER( 3885): =========Initialising Record API =========
E/ACDB-LOADER( 213): Error: ACDB AudProc vol returned = -19
I need to verify if E/ACDB-LOADER( 213): Error: ACDB AudProc vol returned = -19
is responsible for the same.
The above Audio Configuration is for Google Nexus 5
. Might fails to initialize on other devices.
The Android GUI contains two buttons Start
and Stop
.
Please help me resolve the above Issue.
The above Issue has been fixed.
The Problem wasn't with :
E/ACDB-LOADER( 213): Error: ACDB AudProc vol returned = -19
I have corrected the Code and Updated it in this Answer: Audio Recording and Streaming in Android
Code Changes:
AudioRecord
and AudioTrack
Object more than once
(Must not be part of loop).This way I'm able to Record Audio and Play the same in a loop, using a buffer.