content: 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);
      }
  }
  