package im.actor.sdk.core.audio;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.SystemClock;
import android.os.Vibrator;
import java.util.concurrent.atomic.AtomicInteger;
import im.actor.runtime.actors.Actor;
import im.actor.runtime.actors.ActorCreator;
import im.actor.runtime.actors.ActorRef;
import im.actor.runtime.actors.Props;
public class VoiceCaptureActor extends Actor {
private static final int BUFFER_SIZE = 16 * 1024;
private static final int STATE_STOPPED = 0;
private static final int STATE_STARTED = 1;
private int state = STATE_STOPPED;
private AudioRecord audioRecord;
private ActorRef opusActor;
private int bufferSize;
private long playStartTime;
Context context;
VoiceCaptureCallback callback;
public VoiceCaptureActor(Context context, VoiceCaptureCallback callback) {
this.context = context;
this.callback = callback;
}
protected void onStartMessage(String fileName) {
if (state == STATE_STARTED) {
return;
}
int minBufferSize = AudioRecord.getMinBufferSize(16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
bufferSize = 16 * minBufferSize;
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize);
audioRecord.startRecording();
opusActor = system().actorOf(Props.create(new ActorCreator() {
@Override
public OpusEncoderActor create() {
return new OpusEncoderActor();
}
}), "actor/opus_encoder");
opusActor.send(new OpusEncoderActor.Start(fileName));
state = STATE_STARTED;
playStartTime = SystemClock.uptimeMillis();
vibrate(context);
self().send(new Iterate());
}
protected void onIterateMessage() {
if (state != STATE_STARTED) {
return;
}
byte[] buffer = VoiceBuffers.getInstance().obtainBuffer(BUFFER_SIZE);
int len = audioRecord.read(buffer, 0, buffer.length);
if (len > 0) {
opusActor.send(new OpusEncoderActor.Write(buffer, len));
} else {
VoiceBuffers.getInstance().releaseBuffer(buffer);
}
callback.onRecordProgress(SystemClock.uptimeMillis() - playStartTime);
self().send(new Iterate());
}
protected void onStopMessage(boolean cancel) {
if (state != STATE_STARTED) {
return;
}
audioRecord.stop();
audioRecord.release();
audioRecord = null;
opusActor.send(new OpusEncoderActor.Stop());
if (!cancel) {
callback.onRecordStop(SystemClock.uptimeMillis() - playStartTime);
}
state = STATE_STOPPED;
}
protected void onCrashMessage() {
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
if (opusActor != null) {
opusActor.send(new OpusEncoderActor.Stop());
}
callback.onRecordCrash();
state = STATE_STOPPED;
}
private void vibrate(Context context) {
try {
Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(20);
} catch (Exception e) {
}
}
@Override
public void onReceive(Object message) {
if (message instanceof Start) {
onStartMessage(((Start) message).getFilename());
} else if (message instanceof Iterate) {
onIterateMessage();
} else if (message instanceof Stop) {
onStopMessage(((Stop) message).isCancel());
} else if (message instanceof Crash) {
onCrashMessage();
}
}
public static class Start {
String filename;
public Start(String filename) {
this.filename = filename;
}
public String getFilename() {
return filename;
}
}
public static class Iterate {
}
public static class Crash {
}
public static class Stop {
private boolean cancel;
public Stop(boolean cancel) {
this.cancel = cancel;
}
public boolean isCancel() {
return cancel;
}
}
public interface VoiceCaptureCallback {
void onRecordProgress(long time);
void onRecordCrash();
void onRecordStop(long time);
}
}