2022-06-29 14:45:17 +03:00

151 lines
6.2 KiB
C#

using System;
using UnityEngine;
namespace Photon.Voice.Unity
{
public class AndroidAudioInParameters
{
public bool EnableAEC = false;
public bool EnableAGC = false;
public bool EnableNS = false;
}
// depends on Unity's AndroidJavaProxy
public class AndroidAudioInAEC : Voice.IAudioPusher<short>, IResettable
{
class DataCallback : AndroidJavaProxy
{
Action<short[]> callback;
IntPtr javaBuf;
public DataCallback() : base("com.exitgames.photon.audioinaec.AudioInAEC$DataCallback") { }
public void SetCallback(Action<short[]> callback, IntPtr javaBuf)
{
this.callback = callback;
this.javaBuf = javaBuf;
}
public void OnData()
{
if (callback != null)
{
//TODO: copy to LocalVoiceFramed.PushDataBufferPool element instead
var buf = AndroidJNI.FromShortArray(javaBuf);
cntFrame++;
cntShort += buf.Length;
this.callback(buf);
}
}
public void OnStop()
{
AndroidJNI.DeleteGlobalRef(javaBuf);
}
int cntFrame;
int cntShort;
}
AndroidJavaObject audioIn;
IntPtr javaBuf;
Voice.ILogger logger;
int audioInSampleRate = 0;
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);
}
}
// Supposed to be called once at voice initialization.
// Otherwise recreate native object (instead of adding 'set callback' method to java interface)
public void SetCallback(Action<short[]> callback, ObjectFactory<short[], int> bufferFactory)
{
if (Error == null)
{
var voiceFrameSize = bufferFactory.Info;
// setting to voice FrameSize lets to avoid framing procedure
javaBuf = AndroidJNI.NewGlobalRef(AndroidJNI.NewShortArray(voiceFrameSize));
this.callback.SetCallback(callback, javaBuf);
var meth = AndroidJNI.GetMethodID(audioIn.GetRawClass(), "SetBuffer", "([S)Z");
bool ok = AndroidJNI.CallBooleanMethod(audioIn.GetRawObject(), meth, new jvalue[] { new jvalue() { l = javaBuf } });
if (!ok)
{
Error = "AndroidAudioInAEC.SetCallback(): calling SetBuffer java method failure";
}
}
if (Error != null)
{
logger.LogError("[PV] AndroidAudioInAEC: {0}", Error);
}
}
DataCallback callback;
public int Channels { get { return 1; } }
public int SamplingRate { get { return audioInSampleRate; } }
public string Error { get; private set; }
public void Reset()
{
if (audioIn != null)
{
audioIn.Call("Reset");
}
}
public void Dispose()
{
if (audioIn != null)
{
audioIn.Call<bool>("Stop");
}
}
}
}