public AndroidAudioInAEC(Voice.ILogger logger, bool enableAEC = false, bool enableAGC = false, bool enableNS = false)
        {
            // true means to use a route-dependent value which is usually the sample rate of the source
            // otherwise, 44100 Hz requested
            // On Android 4.4.4 (probably on all < 6.0), auto does not work: java.lang.IllegalArgumentException: 0Hz is not a supported sample rate.
            const bool SAMPLE_RATE_AUTO = false;

            // 44100Hz is currently the only rate that is guaranteed to work on all devices
            // used for GetMinBufferSize call even if SAMPLE_RATE_AUTO = true
            const int SAMPLE_RATE_44100       = 44100;
            const int SAMPLE_RATE_UNSPECIFIED = 0;
            const int SAMPLE_RATE_REQUEST     = SAMPLE_RATE_AUTO ? SAMPLE_RATE_UNSPECIFIED : SAMPLE_RATE_44100;

            this.logger = logger;
            try
            {
                this.callback = new DataCallback();
                audioIn       = new AndroidJavaObject("com.exitgames.photon.audioinaec.AudioInAEC");
                //bool aecAvailable = audioIn.Call<bool>("AECIsAvailable");
                int minBufSize = audioIn.Call <int>("GetMinBufferSize", SAMPLE_RATE_44100, Channels);
                logger.LogInfo("[PV] AndroidAudioInAEC: AndroidJavaObject created: aec: {0}/{1}, agc: {2}/{3}, ns: {4}/{5} minBufSize: {6}",
                               enableAEC, audioIn.Call <bool>("AECIsAvailable"),
                               enableAGC, audioIn.Call <bool>("AGCIsAvailable"),
                               enableNS, audioIn.Call <bool>("NSIsAvailable"),
                               minBufSize);

                AndroidJavaClass  app      = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                AndroidJavaObject activity = app.GetStatic <AndroidJavaObject>("currentActivity");
                // Set buffer IntPtr reference separately via pure jni call, pass other values and start capture via AndroidJavaObject helper

                var ok = audioIn.Call <bool>("Start", activity, this.callback, SAMPLE_RATE_REQUEST, Channels, minBufSize * 4, enableAEC, enableAGC, enableNS);
                if (ok)
                {
                    audioInSampleRate = audioIn.Call <int>("GetSampleRate");
                    logger.LogInfo("[PV] AndroidAudioInAEC: AndroidJavaObject started: {0}, sampling rate: {1}, channels: {2}, record buffer size: {3}", ok, SamplingRate, Channels, minBufSize * 4);
                }
                else
                {
                    Error = "[PV] AndroidAudioInAEC constructor: calling Start java method failure";
                    logger.LogError("[PV] AndroidAudioInAEC: {0}", Error);
                }
            }
            catch (Exception e)
            {
                Error = e.ToString();
                if (Error == null) // should never happen but since Error used as validity flag, make sure that it's not null
                {
                    Error = "Exception in AndroidAudioInAEC constructor";
                }
                logger.LogError("[PV] AndroidAudioInAEC: {0}", Error);
            }
        }
 public MicWrapper(string device, int suggestedFrequency, Voice.ILogger logger)
 {
     try
     {
         this.device = device;
         if (Microphone.devices.Length < 1)
         {
             Error = "No microphones found (Microphone.devices is empty)";
             logger.LogError("[PV] MicWrapper: " + Error);
             return;
         }
         if (!string.IsNullOrEmpty(device) && !Microphone.devices.Contains(device))
         {
             logger.LogError(string.Format("[PV] MicWrapper: \"{0}\" is not a valid Unity microphone device, falling back to default one", device));
             device = null;
         }
         int minFreq;
         int maxFreq;
         logger.LogInfo("[PV] MicWrapper: initializing microphone '{0}', suggested frequency = {1}).", device, suggestedFrequency);
         Microphone.GetDeviceCaps(device, out minFreq, out maxFreq);
         var frequency = suggestedFrequency;
         //        minFreq = maxFreq = 44100; // test like android client
         if (suggestedFrequency < minFreq || maxFreq != 0 && suggestedFrequency > maxFreq)
         {
             logger.LogWarning("[PV] MicWrapper does not support suggested frequency {0} (min: {1}, max: {2}). Setting to {2}",
                               suggestedFrequency, minFreq, maxFreq);
             frequency = maxFreq;
         }
         this.mic = Microphone.Start(device, true, 1, frequency);
         logger.LogInfo("[PV] MicWrapper: microphone '{0}' initialized, frequency = {1}, channels = {2}.", device, this.mic.frequency, this.mic.channels);
     }
     catch (Exception e)
     {
         Error = e.ToString();
         if (Error == null) // should never happen but since Error used as validity flag, make sure that it's not null
         {
             Error = "Exception in MicWrapper constructor";
         }
         logger.LogError("[PV] MicWrapper: " + Error);
     }
 }
        public UnityAndroidAudioInAEC(Voice.ILogger logger)
        {
            this.logger = logger;
            try
            {
                this.callback = new DataCallback();
                audioIn       = new AndroidJavaObject("com.exitgames.photon.audioinaec.AudioInAEC");
                bool aecAvailable = audioIn.Call <bool>("AECIsAvailable");
                int  minBufSize   = audioIn.Call <int>("GetMinBufferSize", SamplingRate, Channels);
                logger.LogInfo("[PV] UnityAndroidAudioInAEC: AndroidJavaObject created: aecAvailable: {0}, minBufSize: {1}", aecAvailable, minBufSize);

                AndroidJavaClass  app      = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                AndroidJavaObject activity = app.GetStatic <AndroidJavaObject>("currentActivity");
                // Set buffer IntPtr reference separately via pure jni call, pass other values and start capture via AndroidJavaObject helper

                var ok = audioIn.Call <bool>("Start", activity, this.callback, SamplingRate, Channels, minBufSize * 4, aecAvailable);
                if (ok)
                {
                    logger.LogInfo("[PV] UnityAndroidAudioInAEC: AndroidJavaObject started: {0}, sampling rate: {1}, channels: {2}, record buffer size: {3}, aec: {4}", ok, SamplingRate, Channels, minBufSize * 4, aecAvailable);
                }
                else
                {
                    Error = "[PV] UnityAndroidAudioInAEC constructor: calling Start java method failure";
                    logger.LogError("[PV] UnityAndroidAudioInAEC: {0}", Error);
                }
            }
            catch (Exception e)
            {
                Error = e.ToString();
                if (Error == null) // should never happen but since Error used as validity flag, make sure that it's not null
                {
                    Error = "Exception in WindowsAudioInPusher constructor";
                }
                logger.LogError("[PV] UnityAndroidAudioInAEC: {0}", Error);
            }
        }