Tuesday, 5 June 2012

Implement Voice Changer, by changing sampleRateInHz of AudioRecord and AudioTrack

Refer to the last previous exercise of "AudioRecord and AudioTrack". We can assign sampling frequency (sampleRateInHz) on AudioRecord and AudioTrack.

With different sampleRateInHz assigned on AudioRecord and AudioTrack, we can implement Voice Changer easily.

Selectable sampleRateInHz on AudioRecord and AudioTrack


Please note that currently 44100Hz is currently the only rate that is guaranteed to work on all devices, but other rates such as 22050, 16000, and 11025 may work on some devices .

package com.exercise.AndroidAudioRecord;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Toast;

public class AndroidAudioRecordActivity extends Activity {

Integer[] freqset = {11025, 16000, 22050, 44100};
private ArrayAdapter<Integer> adapter;

Spinner spFrequency;
Button startRec, stopRec, playBack;

Boolean recording;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startRec = (Button)findViewById(R.id.startrec);
stopRec = (Button)findViewById(R.id.stoprec);
playBack = (Button)findViewById(R.id.playback);

startRec.setOnClickListener(startRecOnClickListener);
stopRec.setOnClickListener(stopRecOnClickListener);
playBack.setOnClickListener(playBackOnClickListener);

spFrequency = (Spinner)findViewById(R.id.frequency);
adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item, freqset);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spFrequency.setAdapter(adapter);

}

OnClickListener startRecOnClickListener
= new OnClickListener(){

@Override
public void onClick(View arg0) {

Thread recordThread = new Thread(new Runnable(){

@Override
public void run() {
recording = true;
startRecord();
}

});

recordThread.start();

}};

OnClickListener stopRecOnClickListener
= new OnClickListener(){

@Override
public void onClick(View arg0) {
recording = false;
}};

OnClickListener playBackOnClickListener
= new OnClickListener(){

@Override
public void onClick(View v) {
playRecord();
}

};

private void startRecord(){

File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");

int sampleFreq = (Integer)spFrequency.getSelectedItem();

try {
file.createNewFile();

OutputStream outputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);

int minBufferSize = AudioRecord.getMinBufferSize(sampleFreq,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);

short[] audioData = new short[minBufferSize];

AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
sampleFreq,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
minBufferSize);

audioRecord.startRecording();

while(recording){
int numberOfShort = audioRecord.read(audioData, 0, minBufferSize);
for(int i = 0; i < numberOfShort; i++){
dataOutputStream.writeShort(audioData[i]);
}
}

audioRecord.stop();
dataOutputStream.close();

} catch (IOException e) {
e.printStackTrace();
}

}

void playRecord(){

File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");

int shortSizeInBytes = Short.SIZE/Byte.SIZE;

int bufferSizeInBytes = (int)(file.length()/shortSizeInBytes);
short[] audioData = new short[bufferSizeInBytes];

try {
InputStream inputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

int i = 0;
while(dataInputStream.available() > 0){
audioData[i] = dataInputStream.readShort();
i++;
}

dataInputStream.close();

int sampleFreq = (Integer)spFrequency.getSelectedItem();

AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
sampleFreq,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSizeInBytes,
AudioTrack.MODE_STREAM);

audioTrack.play();
audioTrack.write(audioData, 0, bufferSizeInBytes);


} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Sampling Frequency" />
<Spinner
android:id = "@+id/frequency"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/startrec"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Start Recording" />
<Button
android:id="@+id/stoprec"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stop Recording" />
<Button
android:id="@+id/playback"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Play Back" />

</LinearLayout>


Permission of "android.permission.RECORD_AUDIO" and "android.permission.WRITE_EXTERNAL_STORAGE" are needed.

Download the files.


No comments:

Post a Comment