I'm trying to get Android's AudioTrack play a squarewave with the following code
public Synthesizer() {
bufferSize = android.media.AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
player = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize,
AudioTrack.MODE_STREAM);
}
public void writeSamples(byte[] samples) {
fillBuffer(samples);
player.write(buffer, 0, samples.length);
}
private void putBuffer(byte[] samples) {
if (buffer.length < samples.length)
buffer = new byte[samples.length];
for (int i = 0; i < samples.length; i++)
buffer[i] = samples[i];
Even samples will be negative, the others will be positive, to get a square wave:
btnPlay.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
int frequency = 44100;
byte[] sample = new byte[frequency];
for (int i = 0; i < frequency; i++) {
if (i % 2 == 0) {
sample[i] = Byte.MAX_VALUE;
}
else {
sample[i] = Byte.MIN_VALUE;
}
}
squareSynth.writeSamples(sample);
Unfortunately I get no sound at all, i've checked my volume but couldn't even get static or some crackle. I find this very strange. Anyone knows how to fix it?
Related
I'm developing an android (compileSdkVersion 23) app to record audio by using AudioRecord and the reason of using it is to get frequency after FFT in real time.
Not only this work, I need to save the recorded sound to check the sound(In this process, tracking the frequency is unnecessary.)
How to save recorded sound to file by using the AudioRecord in android?
Thus, am I using the AudioRecord correctly?
Here is code:
public class MainActivity extends Activity {
int frequency = 8000;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
AudioRecord audioRecord;
RecordAudio recordTask;
int blockSize;// = 256;
boolean started = false;
boolean CANCELLED_FLAG = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
blockSize = 256;
final Button btRec = (Button) findViewById(R.id.btRec);
btRec.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (started == true) {
//started = false;
CANCELLED_FLAG = true;
//recordTask.cancel(true);
try{
audioRecord.stop();
}
catch(IllegalStateException e){
Log.e("Stop failed", e.toString());
}
btRec.setText("Start");
// canvasDisplaySpectrum.drawColor(Color.BLACK);
}
else {
started = true;
CANCELLED_FLAG = false;
btRec.setText("Stop");
recordTask = new RecordAudio();
recordTask.execute();
}
}
});
}
private class RecordAudio extends AsyncTask<Void, double[], Boolean> {
#Override
protected Boolean doInBackground(Void... params) {
int bufferSize = AudioRecord.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
audioRecord = new AudioRecord(
MediaRecorder.AudioSource.DEFAULT, frequency,
channelConfiguration, audioEncoding, bufferSize);
int bufferReadResult;
short[] buffer = new short[blockSize];
double[] toTransform = new double[blockSize];
try {
audioRecord.startRecording();
} catch (IllegalStateException e) {
Log.e("Recording failed", e.toString());
}
while (started) {
if (isCancelled() || (CANCELLED_FLAG == true)) {
started = false;
//publishProgress(cancelledResult);
Log.d("doInBackground", "Cancelling the RecordTask");
break;
} else {
bufferReadResult = audioRecord.read(buffer, 0, blockSize);
for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
toTransform[i] = (double) buffer[i] / 32768.0; // signed 16 bit
}
//transformer.ft(toTransform);
//publishProgress(toTransform);
}
}
return true;
}
}
}
You have to download your file and save in cache, than for any request you have to check for cahce file if it is available use otherwise request new file
For complete help look into one of my answer Download and cache media files
I have a thread that uses AudioTrack to synthesize a sequence of notes, with a seekbar to change the tempo.
t = new Thread()
{
public void run()
{
setPriority(Thread.MAX_PRIORITY);
int buffersize = AudioTrack.getMinBufferSize(sr, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audiotrack = new AudioTrack(AudioManager.STREAM_MUSIC, sr,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
buffersize, AudioTrack.MODE_STREAM);
short samples[] = new short[buffersize];
int amplitude = 10000;
double twopi = 2*Math.PI;
double ph = 0.0;
audiotrack.play();
double r = 1.0594630943593; //the 12th root of 2
frequency=261.63;
for(int k = 1; k<9; k++) //number of notes played
{
frequency*=r;
for (int i = 0; i < 4+4*temposliderval; i++) //duration of each note, if i = 50, note duration = 12 seconds
{
for (int j = 0; j < buffersize; j++)
{
samples[j] = (short) (amplitude * Math.sin(ph));
ph += twopi * frequency / sr;
}
audiotrack.write(samples, 0, buffersize);
}
}
audiotrack.stop();
audiotrack.release();
}
};
//play button
play = (Button)findViewById(R.id.play);
play.setBackgroundResource(R.mipmap.play);
play.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
t.start();
}
});
//stop button
stop = (Button)findViewById(R.id.stop);
stop.setBackgroundResource(R.mipmap.stop);
stop.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
t.interrupt();
}
});
I want to be able to stop the audio when the stop button is pushed. Calling t.interrupt() doesn't work, so I'm wondering how to properly stop the thread when the stop button is pushed. The goal is to be able to stop the audio whenever desired, and start it back up again when the play button is pressed again.
The easiest thing to do might be to declare a volatile member variable:
private volatile boolean mStop = false;
that both threads have access to. You could poll the variable inside the inner the loop of the thread and stop looping when it gets set, and set the variable to true when the button is pressed.
I figured it out! This method doesn't seem to be listed anywhere else, so I must have gotten pretty lucky:
Simply put the thread in a new method, and call that method on playbutton click, and rather than try to stop the thread, just stop the audiotrack's audio:
play = (Button)findViewById(R.id.play);
play.setBackgroundResource(R.mipmap.play);
play.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
playScale();
}
});
//stop button
stop = (Button)findViewById(R.id.stop);
stop.setBackgroundResource(R.mipmap.stop);
stop.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
audiotrack.stop();
}
});
Now every time the play button is hit, the thread will simply restart, despite not being actually stopped.
public void playScale()
{
t = new Thread()
{
public void run()
{
t.setPriority(Thread.MAX_PRIORITY);
int buffersize = AudioTrack.getMinBufferSize(sr, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audiotrack = new AudioTrack(AudioManager.STREAM_MUSIC, sr,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
buffersize, AudioTrack.MODE_STREAM);
short samples[] = new short[buffersize];
int amplitude = 10000;
double twopi = 2 * Math.PI;
double ph = 0.0;
audiotrack.play();
double r = 1.0594630943593; //the 12th root of 2
frequency = 261.63;
for (int k = 1; k < 9; k++) //number of notes played
{
frequency *= r;
for (int i = 0; i < 4 + 4 * temposliderval; i++) //duration of each note, if i = 50, note duration = 12 seconds; minimum tempo = 60 bpm
{
for (int j = 0; j < buffersize; j++)
{
samples[j] = (short) (amplitude * Math.sin(ph));
ph += twopi * frequency / sr;
}
audiotrack.write(samples, 0, buffersize);
}
}
}
};
t.start();
}
edit: I've edited the code to show my fruitless (and maybe completely stupid) attempt to solve the problem myself. With this code I only get an awful rattle-like sound.
I’m rather new to Android app development and now my uncle asked me to develop an app for him, which records audio and simultaneously plays it. As if this wasn't enough, he also wants me to add a frequency filter. Actually, that’s beyond my skills, but I told him I would try, anyway.
I am able to record audio and play it with the RecordAudio and AudioTrack classes, respectively, but I have big problems with the frequency filter. I’ve used Google and searched this forum, of course, and could find some promising code snippets, but nothing really worked.
This is the (working) code I have so far:
public class MainActivity extends ActionBarActivity {
float freq_min;
float freq_max;
boolean isRecording = false;
int SAMPLERATE = 8000;
int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
Thread recordingThread = null;
AudioRecord recorder;
Button cmdPlay;
EditText txtMinFrequency, txtMaxFrequency;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cmdPlay = (Button)findViewById(R.id.bPlay);
cmdPlay.setOnClickListener(onClickListener);
txtMinFrequency = (EditText)findViewById(R.id.frequency_min);
txtMaxFrequency = (EditText)findViewById(R.id.frequency_max);
}
private OnClickListener onClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
if (!isRecording) {
freq_min = Float.parseFloat(txtMinFrequency.getText().toString());
freq_max = Float.parseFloat(txtMaxFrequency.getText().toString());
isRecording = true;
cmdPlay.setText("stop");
startRecording();
}
else {
isRecording = false;
cmdPlay.setText("play");
stopRecording();
}
}
};
public void startRecording() {
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLERATE,
AudioFormat.CHANNEL_IN_MONO, AUDIO_FORMAT, 1024);
recorder.startRecording();
recordingThread = new Thread(new Runnable(){
public void run() {
recordAndWriteAudioData();
}
});
recordingThread.start();
}
public void stopRecording() {
isRecording = false;
recorder.stop();
recorder.release();
recorder = null;
recordingThread = null;
}
private void recordAndWriteAudioData() {
byte audioData[] = new byte[1024];
AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLERATE, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, 1024, AudioTrack.MODE_STREAM);
at.play();
while (isRecording) {
recorder.read(audioData, 0, 1024);
// Converting from byte array to float array and dividing floats by 32768 to get values between 0 and 1
float[] audioDataF = shortToFloat(byteToShort(audioData));
for (int i = 0; i < audioDataF.length; i++) {
audioDataF[i] /= 32768.0;
}
// Fast Fourier Transform
FloatFFT_1D fft = new FloatFFT_1D(512);
fft.realForward(audioDataF);
// fiter frequencies
for(int fftBin = 0; fftBin < 512; fftBin++){
float frequency = (float)fftBin * (float)SAMPLERATE / (float)512;
if(frequency < freq_min || frequency > freq_max){
int real = 2 * fftBin;
int imaginary = 2 * fftBin + 1;
audioDataF[real] = 0;
audioDataF[imaginary] = 0;
}
}
//inverse FFT
fft.realInverse(audioDataF, false);
// multiplying the floats by 32768
for (int i = 0; i < audioDataF.length; i++) {
audioDataF[i] *= 32768.0;
}
// converting float array back to short array
audioData = shortToByte(floatToShort(audioDataF));
at.write(audioData, 0, 1024);
}
at.stop();
at.release();
}
public static short[] byteToShort (byte[] byteArray){
short[] shortOut = new short[byteArray.length / 2];
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
for (int i = 0; i < shortOut.length; i++) {
shortOut[i] = byteBuffer.getShort();
}
return shortOut;
}
public static float[] shortToFloat (short[] shortArray){
float[] floatOut = new float[shortArray.length];
for (int i = 0; i < shortArray.length; i++) {
floatOut[i] = shortArray[i];
}
return floatOut;
}
public static short[] floatToShort (float[] floatArray){
short[] shortOut = new short[floatArray.length];
for (int i = 0; i < floatArray.length; i++) {
shortOut[i] = (short) floatArray[i];
}
return shortOut;
}
public static byte[] shortToByte (short[] shortArray){
byte[] byteOut = new byte[shortArray.length * 2];
ByteBuffer.wrap(byteOut).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shortArray);
return byteOut;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
On the site, Filter AudioRecord Frequencies, I found a code, which uses FFT to filter frequencies:
I hope this code is correct, because - to be honest - I wouldn’t know at all how to alter it, if it wasn’t. But the actual problem is, that the audio buffer is a ByteArray, but I need a Float Array for the FFT with values between 0 and 1 (and after the reverse FFT I have to convert the float array back to a ByteArray).
I simply can’t find code anywhere to do this, so any help would be highly appreciated!
byteToShort conversion is incorrect. While the data and most android devices are littlendian, ByteBuffer by default uses big-endian order. So we need to force it little-endian before conversion to short:
public static short[] byteToShort (byte[] byteArray){
short[] shortOut = new short[byteArray.length / 2];
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortOut);
return shortOut;
}
I have helped from the book Pro Android media...
Here is the code:
public class MicMeter extends Activity implements OnClickListener {
RecordAudio recordTask;
int blocksize = 256;
int frequency = 8000;
int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
TextView txt;
Button start;
boolean started = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mic_meter);
start = (Button)findViewById(R.id.button1);
txt = (TextView)findViewById(R.id.textView1);
start.setOnClickListener(this);
}
private class RecordAudio extends AsyncTask <Void,double[],Void>{
#Override
protected Void doInBackground(Void... params) {
try{
int bufferSize = AudioRecord.getMinBufferSize(frequency,channelConfig,audioEncoding);
AudioRecord audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, frequency, channelConfig, audioEncoding, bufferSize);
short[] buffer = new short[blocksize];
double[] meter = new double[blocksize];
audioRecord.startRecording();
while(started){
int bufferReadResult = audioRecord.read(buffer, 0, blocksize);
for (int i = 0; i < blocksize && i < bufferReadResult; i++) {
meter[i] = (double) buffer[i] / 32768.0; // signed 16 bit
}
publishProgress(meter);
}
audioRecord.stop();
}catch (Throwable t) {
Log.e("AudioRecord","RecordingFail");
}
return null;
}
#Override
protected void onProgressUpdate(double[]... meter) {
for(int i = 0 ; i < meter[0].length ; i++){
double[] helper = meter[i];
txt.setText(Double.toString(helper));
}
}
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(started){
recordTask.cancel(true);
}else{
started = true;
recordTask = new RecordAudio();
recordTask.execute();
}
}
}
while i press the button.
It shows 255.0 and then it doesn't response...
Are there any way to fix it??
Are there any beter version about this?
thank
for(int i = 0 ; i < meter[0].length ; i++){
double helper = i;
txt.setText(Double.toString(helper));
setText overwrites the old value. So only the last call will show. The last call sets it to helper, which will always be meter[0].length. Since that's a fixed number, it won't change.
What I want is to be able to get the current noise level in decibels (dB) on the click of a Button. I have been playing around with the sensors and can get them to work easily but this.. I'm stumped. I've tried a few codes but none work, or helped me understand this.
How can this be achieved?
EDIT:
I use the following code:
private Thread recordingThread;
private int bufferSize = 800;
private short[][] buffers = new short[256][bufferSize];
private int[] averages = new int[256];
private int lastBuffer = 0;
AudioRecord recorder;
boolean recorderStarted = false;
protected void startListenToMicrophone()
{
if (!recorderStarted)
{
recordingThread = new Thread()
{
#Override
public void run()
{
int minBufferSize = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
recorder = new AudioRecord(AudioSource.MIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 10);
recorder.setPositionNotificationPeriod(bufferSize);
recorder.setRecordPositionUpdateListener(new OnRecordPositionUpdateListener()
{
#Override
public void onPeriodicNotification(AudioRecord recorder)
{
short[] buffer = buffers[++lastBuffer
% buffers.length];
recorder.read(buffer, 0, bufferSize);
long sum = 0;
for (int i = 0; i < bufferSize; ++i)
{
sum += Math.abs(buffer[i]);
}
averages[lastBuffer % buffers.length] = (int) (sum / bufferSize);
lastBuffer = lastBuffer % buffers.length;
Log.i("dB", ""+averages);
tv4.setText("" + averages[1]);
}
#Override
public void onMarkerReached(AudioRecord recorder)
{
}
});
recorder.startRecording();
short[] buffer = buffers[lastBuffer % buffers.length];
recorder.read(buffer, 0, bufferSize);
while (true)
{
if (isInterrupted())
{
recorder.stop();
recorder.release();
break;
}
}
}
};
recordingThread.start();
recorderStarted = true;
}
}
private void stopListenToMicrophone()
{
if (recorderStarted)
{
if (recordingThread != null && recordingThread.isAlive()
&& !recordingThread.isInterrupted())
{
recordingThread.interrupt();
}
recorderStarted = false;
}
}
}
I have two buttons in my app. First one calls startListenToMicrophone and second calls the stop. I don't understand how this works. I got the code from here.
The textview gets a weird and very big value. What I need is the sound level in decibels.
Just a passing thought and I may be ver very wrong but, amplitude in dB=20xlog(S1/S2).
I couldn't find this calculation anywhere in your code. what you need to do is get S1, which will be the current recorded level and get S2 which needs to be the maximum possible value to record. Then calculate the dB value.