Skip to content

Instantly share code, notes, and snippets.

@svenoaks
Created April 3, 2014 22:45
Show Gist options
  • Save svenoaks/9964397 to your computer and use it in GitHub Desktop.
Save svenoaks/9964397 to your computer and use it in GitHub Desktop.
package com.smp.soundtouchandroid;
import android.os.AsyncTask;
import android.util.Log;
import com.smp.soundtouchandroid.*;
import java.io.FileOutputStream;
import java.io.IOException;
public class SoundTouchRecorder
{
private static final String TAG = "SoundTouchRecorder";
private static final int DEFAULT_ID = 0;
private String outputPath;
private String inputPath;
private OnStartedRecordingListener startedRecordingListener;
private OnEndedRecordingListener endedRecordingListener;
public SoundTouchRecorder(String inputPath, String outputPath)
{
this.inputPath = inputPath;
this.outputPath = outputPath;
}
public void setStartedRecordingListener(OnStartedRecordingListener startedRecordingListener)
{
this.startedRecordingListener = startedRecordingListener;
}
public void setEndedRecordingListener(OnEndedRecordingListener endedRecordingListener)
{
this.endedRecordingListener = endedRecordingListener;
}
public void start(float tempo, float pitchSemi)
{
Log.d(TAG, String.format("Starting audio engine for tempo = %.2f, pitchSemi = %.2f", tempo, pitchSemi));
SoundTouchTask task = new SoundTouchTask();
task.execute(tempo, pitchSemi);
}
private class SoundTouchTask extends AsyncTask<Float, Integer, Boolean>
{
private AudioDecoder audioDecoder;
private SoundTouch soundTouch;
private FileOutputStream os;
@Override
protected void onPreExecute()
{
super.onPreExecute();
if (SoundTouchRecorder.this.startedRecordingListener != null)
{
Log.d(TAG, "calling startedRecordingListener.startedRecording()");
SoundTouchRecorder.this.startedRecordingListener.startedRecording();
}
}
@Override
protected Boolean doInBackground(Float... params)
{
try
{
Log.d(TAG, String.format("calling media decoder for inputPath = %s", inputPath));
audioDecoder = new MediaCodecAudioDecoder(inputPath);
os = new FileOutputStream(outputPath);
processSoundFile(params[0], params[1]);
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
finally
{
if (soundTouch != null) soundTouch.clearBuffer();
if (audioDecoder != null) audioDecoder.close();
try
{
if (os != null) os.close();
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
Log.d(TAG, "cleaned up with all resources.");
}
return true;
}
/**
* Apply tempo and pitch to the audio file and write the output to a
* File.
*
* @param tempo
* @param pitchSemi
* @throws IOException
*/
private void processSoundFile(float tempo, float pitchSemi) throws IOException
{
int bytesReceived = 0;
byte[] input = null;
initSoundTouch(DEFAULT_ID, tempo, pitchSemi);
do
{
if (soundTouch.getOutputBufferSize() <= Constants.MAX_OUTPUT_BUFFER_SIZE)
{
input = audioDecoder.decodeChunk();
processChunk(input, os, true);
}
else
{
processChunk(input, os, false);
}
}
while (!audioDecoder.sawOutputEOS());
Log.d(TAG, "audioDecoder.sawOutputEOS is true");
soundTouch.finish();
do
{
bytesReceived = processChunk(input, os, false);
}
while (bytesReceived > 0);
Log.d(TAG, "finished writing to file");
}
private void initSoundTouch(int id, float tempo, float pitchSemi) throws IOException
{
int channels = audioDecoder.getChannels();
int samplingRate = audioDecoder.getSamplingRate();
int bytesPerSample = Constants.DEFAULT_BYTES_PER_SAMPLE;
Log.d(TAG, String.format("starting soundtouch with params channels, sampling_rate, bytesPerSample, tempo, " +
"pitchSemi = %d, %d, %d, %.2f, %.2f", channels, samplingRate, bytesPerSample, tempo, pitchSemi));
soundTouch = new SoundTouch(id, channels, samplingRate, bytesPerSample, tempo, pitchSemi);
}
private int processChunk(final byte[] input, FileOutputStream outputStream, boolean putBytes) throws IOException
{
int bytesReceived = 0;
if (input != null)
{
if (putBytes)
soundTouch.putBytes(input);
bytesReceived = soundTouch.getBytes(input);
outputStream.write(input, 0, bytesReceived);
}
return bytesReceived;
}
@Override
protected void onPostExecute(Boolean success)
{
super.onPostExecute(success);
if (SoundTouchRecorder.this.endedRecordingListener != null)
{
Log.d(TAG, "calling endedRecordingListener.endedRecording()");
SoundTouchRecorder.this.endedRecordingListener.endedRecording(success);
}
}
}
public interface OnStartedRecordingListener
{
public void startedRecording();
}
public interface OnEndedRecordingListener
{
public void endedRecording(boolean success);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment