Created
April 3, 2014 22:45
-
-
Save svenoaks/9964397 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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