// 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, Voice.LocalVoice localVoice) { if (audioIn != null) { Dispose(); } var voiceFrameSize = ((Voice.LocalVoiceFramed)localVoice).FrameSize; // setting to voice FrameSize lets to avoid framing procedure javaBuf = AndroidJNI.NewGlobalRef(AndroidJNI.NewShortArray(voiceFrameSize)); this.callback = new DataCallback(callback, javaBuf); audioIn = new AndroidJavaObject("com.exitgames.photon.audioinaec.AudioInAEC"); bool aecAvailable = audioIn.Call <bool>("AECIsAvailable"); int minBufSize = audioIn.Call <int>("GetMinBufferSize", SamplingRate, Channels); Debug.LogFormat("AndroidAudioInAEC: 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 meth = AndroidJNI.GetMethodID(audioIn.GetRawClass(), "SetBuffer", "([S)Z"); bool ok = AndroidJNI.CallBooleanMethod(audioIn.GetRawObject(), meth, new jvalue[] { new jvalue() { l = javaBuf } }); if (ok) { ok = audioIn.Call <bool>("Start", activity, this.callback, SamplingRate, Channels, minBufSize * 4, aecAvailable); } Debug.LogFormat("AndroidAudioInAEC: AndroidJavaObject started: {0}, buffer size: {1}, sampling rate: {2}, channels: {3}, record buffer size: {4}, aec: {5}", ok, voiceFrameSize, SamplingRate, Channels, minBufSize * 4, aecAvailable); }
internal object[] buildVoiceRemoveMessage(LocalVoice v) { byte[] ids = new byte[] { v.id }; object[] content = new object[] { (byte)0, EventSubcode.VoiceRemove, ids }; this.frontend.LogInfo(v.LogPrefix + " remove sent"); return(content); }
private void updateAudioSource() { // update local voice's mic audio source if (this.voice != Voice.LocalVoiceAudio.Dummy && (Source == AudioSource.Microphone)) { // first remove voice and stop mic, then create new mic wrapper this.audioSource.Dispose(); this.voice.RemoveSelf(); gameObject.SendMessage("PhotonVoiceRemoved", SendMessageOptions.DontRequireReceiver); var debugEchoMode = this.DebugEchoMode; this.DebugEchoMode = false; var prevVoice = this.voice; this.voice = createLocalVoiceAudioAndSource(); this.voice.Group = prevVoice.Group; this.voice.Transmit = prevVoice.Transmit; this.voiceAudio.VoiceDetector.On = voiceAudio.VoiceDetector.On; this.voiceAudio.VoiceDetector.Threshold = voiceAudio.VoiceDetector.Threshold; sendPhotonVoiceCreatedMessage(); this.DebugEchoMode = debugEchoMode; } }
private void Start() { if (photonView.isMine) { switch (this.TypeConvert) { case SampleTypeConv.Short: forceShort = true; if (PhotonVoiceSettings.Instance.DebugInfo) { Debug.LogFormat( "PUNVoice: Type Conversion set to Short. Audio samples will be converted if source samples type differs."); } break; } this.voice = createLocalVoiceAudioAndSource(); this.VoiceDetector.On = PhotonVoiceSettings.Instance.VoiceDetection; this.VoiceDetector.Threshold = PhotonVoiceSettings.Instance.VoiceDetectionThreshold; if (this.voice != Voice.LocalVoiceAudio.Dummy) { this.voice.Transmit = PhotonVoiceSettings.Instance.AutoTransmit; } else if (PhotonVoiceSettings.Instance.AutoTransmit) { Debug.LogWarning("PUNVoice: Cannot Transmit."); } sendPhotonVoiceCreatedMessage(); } }
public override void Service(LocalVoice localVoice) { while (this.reader.Read(this.buffer)) { ((LocalVoiceFramed <T>)localVoice).PushData(this.buffer); } }
/// <summary> /// Creates new local voice (outgoing audio stream). /// </summary> /// <param name="audioStream">Object providing audio data for the outgoing stream.</param> /// <param name="voiceInfo">Outgoing audio stream parameters (should be set according to Opus encoder restrictions).</param> /// <returns>Outgoing stream handler.</returns> /// <remarks> /// audioStream.SamplingRate and voiceInfo.SamplingRate may do not match. Automatic resampling will occur in this case. /// </remarks> public LocalVoice CreateLocalVoice(IAudioStream audioStream, VoiceInfo voiceInfo, int channelId) { // id assigned starting from 1 and up to 255 byte newId = 0; // non-zero if successfully assigned if (voiceIdCnt == 255) { // try to reuse id var ids = new bool[256]; foreach (var v in localVoices) { ids[v.Value.id] = true; } // ids[0] is not used for (byte id = 1; id != 0 /* < 256 */; id++) { if (!ids[id]) { newId = id; break; } } } else { voiceIdCnt++; newId = voiceIdCnt; } if (newId != 0) { var v = new LocalVoice(this.frontend, newId, audioStream, voiceInfo, channelId); localVoices[newId] = v; List <LocalVoice> voiceList; if (!localVoicesPerChannel.TryGetValue(channelId, out voiceList)) { voiceList = new List <LocalVoice>(); localVoicesPerChannel[channelId] = voiceList; } voiceList.Add(v); this.frontend.DebugReturn(DebugLevel.INFO, "[PV] Local voice #" + v.id + " at channel " + this.channelStr(channelId) + " added: src_f=" + audioStream.SamplingRate + " enc_f=" + v.info.SamplingRate + " ch=" + v.info.Channels + " d=" + v.info.FrameDurationUs + " s=" + v.info.FrameSize + " b=" + v.info.Bitrate + " ud=" + voiceInfo.UserData); if (this.frontend.IsChannelJoined(channelId)) { this.frontend.SendVoicesInfo(this.buildVoicesInfo(new List <LocalVoice>() { v }, true), channelId, 0); // broadcast if joined } v.AudioGroup = this.GlobalAudioGroup; return(v); } else { return(null); } }
/// <summary> /// Removes local voice (outgoing audio stream). /// <param name="voice">Handler of outgoing stream to be removed.</param> /// </summary> public static void RemoveLocalVoice(Voice.LocalVoice voice) { // can be called from OnDestroy, check if still exists if (!destroyed) { instance.client.RemoveLocalVoice(voice); } }
public override void Service(LocalVoice localVoice) { while (this.reader.Read(buffer)) { var v = ((LocalVoiceFramed <T>)localVoice); var buf = v.PushDataBufferPool.AcquireOrCreate(); Array.Copy(buffer, buf, buffer.Length); v.PushDataAsync(buf); } }
// give user a chance to change MicrophoneDevice in Awake() void Start() { if (photonView.isMine) { var pvs = PhotonVoiceSettings.Instance; Application.RequestUserAuthorization(UserAuthorization.Microphone); // put required sample rate into audio source and encoder - both adjust it if needed Voice.IBufferReader <float> audioStream; int channels = 0; int sourceSamplingRate = 0; if (AudioClip == null) { if (Microphone.devices.Length < 1) { // Error already logged in PhotonVoiceNetwork.Awake() return; } var micDev = this.MicrophoneDevice != null ? this.MicrophoneDevice : PhotonVoiceNetwork.MicrophoneDevice; if (PhotonVoiceSettings.Instance.DebugInfo) { Debug.LogFormat("PUNVoice: Setting recorder's microphone device to {0}", micDev); } var mic = new MicWrapper(micDev, (int)pvs.SamplingRate); sourceSamplingRate = mic.SourceSamplingRate; channels = mic.Channels; audioStream = mic; } else { audioStream = new AudioClipWrapper(AudioClip); sourceSamplingRate = AudioClip.frequency; channels = AudioClip.channels; if (this.LoopAudioClip) { ((AudioClipWrapper)audioStream).Loop = true; } } Voice.VoiceInfo voiceInfo = Voice.VoiceInfo.CreateAudioOpus(pvs.SamplingRate, sourceSamplingRate, channels, pvs.FrameDuration, pvs.Bitrate, photonView.viewID); this.voice = createLocalVoice(voiceInfo, audioStream); this.VoiceDetector.On = PhotonVoiceSettings.Instance.VoiceDetection; this.VoiceDetector.Threshold = PhotonVoiceSettings.Instance.VoiceDetectionThreshold; if (this.voice != Voice.LocalVoiceAudio.Dummy) { this.voice.Transmit = PhotonVoiceSettings.Instance.AutoTransmit; } else if (PhotonVoiceSettings.Instance.AutoTransmit) { Debug.LogWarning("PUNVoice: Cannot Transmit."); } sendVoiceCreatedMessage(voiceInfo); } }
/// <summary> /// Removes local voice (outgoing audio stream). /// <param name="voice">Handler of outgoing stream to be removed.</param> /// </summary> public void RemoveLocalVoice(LocalVoice v) { localVoices.Remove(v); if (this.State == LoadBalancing.ClientState.Joined) { this.sendVoiceRemove(new List <LocalVoice> { v }); } this.DebugReturn(DebugLevel.INFO, "[PV] Local voice #" + v.id + " removed"); }
/// <summary> /// Removes local voice (outgoing data stream). /// <param name="voice">Handler of outgoing stream to be removed.</param> /// </summary> internal void RemoveLocalVoice(LocalVoice voice) { this.localVoices.Remove(voice.id); this.localVoicesPerChannel[voice.channelId].Remove(voice); if (this.frontend.IsChannelJoined(voice.channelId)) { this.frontend.SendVoiceRemove(voice, voice.channelId, 0); } voice.Dispose(); this.frontend.LogInfo(voice.LogPrefix + " removed"); }
// Supposed to be called once at voice initialization. // Otherwise recreate native object (instead of adding 'set callback' method to native interface) public void SetCallback(Action <float[]> callback, Voice.LocalVoice localVoice) { if (handle != IntPtr.Zero) { Dispose(); } this.pushCallback = callback; this.localVoice = (Voice.LocalVoiceFramed <float>)localVoice; handle = Photon_Audio_In_CreatePusher(instanceCnt, deviceID, nativePushCallback); instancePerHandle.Add(instanceCnt++, this); }
public override void Service(LocalVoice localVoice) { var v = ((LocalVoiceFramed <T>)localVoice); T[] buf = v.PushDataBufferPool.AcquireOrCreate(); while (this.reader.Read(buf)) { v.PushDataAsync(buf); buf = v.PushDataBufferPool.AcquireOrCreate(); } // release unused buffer v.PushDataBufferPool.Release(buf, buf.Length); }
// Supposed to be called once at voice initialization. // Otherwise recreate native object (instead of adding 'set callback' method to native interface) public void SetCallback(Action <short[]> callback, Voice.LocalVoice localVoice) { if (handle != IntPtr.Zero) { Dispose(); } this.pushCallback = callback; this.localVoice = (Voice.LocalVoiceFramed <short>)localVoice; pushRef = push; // use default playback device handle = Photon_Audio_In_Create(SystemMode.SINGLE_CHANNEL_AEC, deviceID, -1, pushRef, true, true, true, true); // defaults in original ms sample: false, true, false, false }
/// <summary> /// Creates new local voice (outgoing audio stream). /// </summary> /// <param name="audioStream">Object providing audio data for the outgoing stream.</param> /// <param name="voiceInfo">Outgoing audio stream parameters (should be set according to Opus encoder restrictions).</param> /// <returns>Outgoing stream handler.</returns> /// <remarks> /// audioStream.SamplingRate and voiceInfo.SamplingRate may do not match. Automatic resampling will occur in this case. /// </remarks> public LocalVoice CreateLocalVoice(IAudioStream audioStream, VoiceInfo voiceInfo) { // id assigned starting from 1 and up to 255 byte newId = 0; // non-zero if successfully assigned if (voiceIdCnt == 255) { // try to reuse id var ids = new bool[256]; foreach (var v in localVoices) { ids[v.id] = true; } // ids[0] is not used for (byte id = 1; id != 0 /* < 256 */; id++) { if (!ids[id]) { newId = id; break; } } } else { voiceIdCnt++; newId = voiceIdCnt; } if (newId != 0) { var v = new LocalVoice(this, newId, audioStream, voiceInfo.UserData, voiceInfo.SamplingRate, voiceInfo.Channels, voiceInfo.Bitrate, voiceInfo.EncoderDelay); localVoices.Add(v); this.DebugReturn(DebugLevel.INFO, "[PV] Local voice #" + v.id + " added: src_f=" + audioStream.SamplingRate + " enc_f=" + v.InputSamplingRate + " ch=" + v.InputChannels + " d=" + v.EncoderDelay + " s=" + v.frameSize + " b=" + v.Bitrate + " ud=" + voiceInfo.UserData); if (this.State == LoadBalancing.ClientState.Joined) { this.sendVoicesInfo(0, new List <LocalVoice>() { v }); // broadcast if joined } v.AudioGroup = this.globalAudioGroup; return(v); } else { return(null); } }
/// <summary> /// Removes local voice (outgoing audio stream). /// <param name="voice">Handler of outgoing stream to be removed.</param> /// </summary> public void RemoveLocalVoice(LocalVoice voice) { this.localVoices.Remove(voice.id); this.localVoicesPerChannel[voice.channelId].Remove(voice); if (this.frontend.IsChannelJoined(voice.channelId)) { var content = this.buildVoiceRemoveMessage(new List <LocalVoice>() { voice }); this.frontend.SendVoiceRemove(content, voice.channelId); } this.frontend.DebugReturn(DebugLevel.INFO, "[PV] Local voice #" + voice.id + " at channel " + this.channelStr(voice.channelId) + " removed"); }
public static IEncoder Create(VoiceInfo i, LocalVoice localVoice) { if (localVoice.GetType() == typeof(LocalVoiceAudioFloat)) { return(new EncoderFloat(i)); } else if (localVoice.GetType() == typeof(LocalVoiceAudioShort)) { return(new EncoderShort(i)); } else { throw new UnsupportedCodecException(i.Codec, localVoice); } }
internal static IEncoder CreateDefaultEncoder(VoiceInfo info, LocalVoice localVoice) { switch (info.Codec) { case Codec.AudioOpus: return(OpusCodec.EncoderFactory.Create(info, localVoice)); #if PHOTON_VOICE_VIDEO_ENABLE case Codec.VideoVP8: return(new VPxCodec.Encoder(info)); #endif default: throw new UnsupportedCodecException(info.Codec, localVoice); } }
public void SendVoiceRemove(LocalVoice voice, int channelId, int targetPlayerId) { object content = voiceClient.buildVoiceRemoveMessage(voice); var opt = new LoadBalancing.RaiseEventOptions(); opt.SequenceChannel = (byte)channelId; if (targetPlayerId != 0) { opt.TargetActors = new int[] { targetPlayerId }; } lock (sendLock) { this.OpRaiseEvent(VoiceEventCode.GetCode(opt.SequenceChannel), content, true, opt); } }
/// <summary>Iterates through copy of all local voices list of given channel.</summary> public IEnumerable <LocalVoice> LocalVoicesInChannel(int channelId) { List <LocalVoice> channelVoices; if (this.localVoicesPerChannel.TryGetValue(channelId, out channelVoices)) { var res = new LocalVoice[channelVoices.Count]; channelVoices.CopyTo(res, 0); return(res); } else { return(new LocalVoice[0]); } }
public void SendFrame(object content, int channelId, LocalVoice localVoice) { var opt = new LoadBalancing.RaiseEventOptions(); opt.SequenceChannel = (byte)channelId; if (localVoice.DebugEchoMode) { opt.Receivers = LoadBalancing.ReceiverGroup.All; } opt.InterestGroup = localVoice.Group; lock (sendLock) { this.OpRaiseEvent((byte)VoiceEventCode.GetCode(opt.SequenceChannel), content, localVoice.Reliable, opt); } this.loadBalancingPeer.SendOutgoingCommands(); }
public void SetDebugEchoMode(LocalVoice v) { if (this.State == LoadBalancing.ClientState.Joined) { if (v.DebugEchoMode) { SendVoicesInfo(new List <LocalVoice>() { v }, v.channelId, this.LocalPlayer.ID); } else { SendVoiceRemove(v, v.channelId, this.LocalPlayer.ID); } } }
public override void Service(Voice.LocalVoice localVoice) { var v = ((Voice.LocalVoiceFramed <short>)localVoice); short[] buf = v.PushDataBufferPool.AcquireOrCreate(); while (this.reader.Read(buffer)) { for (int i = 0; i < buf.Length; i++) { buf[i] = (short)(buffer[i] * (float)short.MaxValue); } v.PushDataAsync(buf); buf = v.PushDataBufferPool.AcquireOrCreate(); } // release unused buffer v.PushDataBufferPool.Release(buf, buf.Length); }
IEnumerator Start() { yield return(Application.RequestUserAuthorization(UserAuthorization.Microphone)); if (!Application.HasUserAuthorization(UserAuthorization.Microphone)) { yield break; } if (photonView.isMine) { switch (this.TypeConvert) { case SampleTypeConv.Short: forceShort = true; Debug.LogFormat("PUNVoice: Type Convertion set to Short. Audio samples will be converted if source samples type differs."); break; case SampleTypeConv.ShortAuto: var speex = gameObject.GetComponent <SpeexDSP>(); if (speex != null && speex.Active) { if (PhotonVoiceSettings.Instance.DebugInfo) { Debug.LogFormat("PUNVoice: Type Convertion set to ShortAuto. SpeexDSP found. Audio samples will be converted if source samples type differs."); } forceShort = true; } break; } this.voice = createLocalVoiceAudioAndSource(); this.VoiceDetector.On = PhotonVoiceSettings.Instance.VoiceDetection; this.VoiceDetector.Threshold = PhotonVoiceSettings.Instance.VoiceDetectionThreshold; if (this.voice != Voice.LocalVoiceAudio.Dummy) { this.voice.Transmit = PhotonVoiceSettings.Instance.AutoTransmit; } else if (PhotonVoiceSettings.Instance.AutoTransmit) { Debug.LogWarning("PUNVoice: Cannot Transmit."); } sendPhotonVoiceCreatedMessage(); } }
// give user a chance to change MicrophoneDevice in Awake() void Start() { if (photonView.isMine) { var pvs = PhotonVoiceSettings.Instance; if (!this.microphoneDeviceSet) { this.MicrophoneDevice = PhotonVoiceNetwork.MicrophoneDevice; } Application.RequestUserAuthorization(UserAuthorization.Microphone); // put required sample rate into audio source and encoder - both adjust it if needed Voice.IAudioStream audioStream; int channels = 0; if (AudioClip == null) { if (PhotonVoiceSettings.Instance.DebugInfo) { Debug.Log("PUNVoice: Setting recorder's microphone device to " + this.MicrophoneDevice); } var mic = new MicWrapper(this.MicrophoneDevice, (int)pvs.SamplingRate); this.microphoneDeviceUsed = true; channels = mic.Channels; audioStream = mic; } else { audioStream = new AudioClipWrapper(AudioClip); channels = AudioClip.channels; if (this.LoopAudioClip) { ((AudioClipWrapper)audioStream).Loop = true; } } Voice.VoiceInfo voiceInfo = new Voice.VoiceInfo((int)pvs.SamplingRate, channels, (int)pvs.Delay, pvs.Bitrate, photonView.viewID); this.voice = PhotonVoiceNetwork.CreateLocalVoice(audioStream, voiceInfo); this.VoiceDetector.On = PhotonVoiceSettings.Instance.VoiceDetection; this.VoiceDetector.Threshold = PhotonVoiceSettings.Instance.VoiceDetectionThreshold; } }
void addVoice(byte newId, int channelId, LocalVoice v) { localVoices[newId] = v; List <LocalVoice> voiceList; if (!localVoicesPerChannel.TryGetValue(channelId, out voiceList)) { voiceList = new List <LocalVoice>(); localVoicesPerChannel[channelId] = voiceList; } voiceList.Add(v); if (this.frontend.IsChannelJoined(channelId)) { this.frontend.SendVoicesInfo(new List <LocalVoice>() { v }, channelId, 0); // broadcast if joined } v.Group = this.GlobalGroup; }
private LocalVoice createLocalVoice(VoiceInfo voiceInfo, int channelId, IEncoder encoder, Func <byte, int, LocalVoice> voiceFactory) { if (channelId == ChannelAuto) { channelId = this.frontend.AssignChannel(voiceInfo); } var newId = getNewVoiceId(); if (newId != 0) { LocalVoice v = voiceFactory(newId, channelId); if (v != null) { addVoice(newId, channelId, v); this.frontend.LogInfo(v.LogPrefix + " added enc: " + v.info.ToString()); return(v); } } return(null); }
private void InitialVoiceSetUp() { var pvs = PhotonVoiceSettings.Instance; Application.RequestUserAuthorization(UserAuthorization.Microphone); // put required sample rate into audio source and encoder - both adjust it if needed Voice.IAudioStream audioStream; int channels = 0; if (AudioClip == null) { var micDev = microphoneDevice ?? PhotonVoiceNetwork.MicrophoneDevice ?? (Microphone.devices.Any() ? Microphone.devices.First() : null); microphoneDevice = micDev; if (PhotonVoiceSettings.Instance.DebugInfo) { Debug.LogFormat("PUNVoice: Setting recorder's microphone device to {0}", micDev); } var mic = new MicWrapper(micDev, (int)pvs.SamplingRate); channels = mic.Channels; audioStream = mic; } else { audioStream = new AudioClipWrapper(AudioClip); channels = AudioClip.channels; if (LoopAudioClip) { ((AudioClipWrapper)audioStream).Loop = true; } } Voice.VoiceInfo voiceInfo = new Voice.VoiceInfo((int)pvs.SamplingRate, channels, (int)pvs.Delay, pvs.Bitrate, photonView.viewID); voice = PhotonVoiceNetwork.CreateLocalVoice(audioStream, voiceInfo); VoiceDetector.On = PhotonVoiceSettings.Instance.VoiceDetection; VoiceDetector.Threshold = PhotonVoiceSettings.Instance.VoiceDetectionThreshold; MicrophoneDevice = microphoneDevice; }
// Message sent by PhotonVoiceRecorder void PhotonVoiceCreated(Voice.LocalVoice localVoice) { if (!Active) { return; } if (localVoice.Info.Channels != 1) { throw new Exception("SpeexProcessor: only mono audio signals supported."); } if (!(localVoice is Voice.LocalVoiceAudioShort)) { throw new Exception("SpeexProcessor: only short audio voice supported (Set PhotonVoiceRecorder.TypeConvert option)."); } var v = (Voice.LocalVoiceAudioShort)localVoice; // can't access the AudioSettings properties in InitAEC if it's called from not main thread var playChannelCount = new Dictionary <AudioSpeakerMode, int>() { { AudioSpeakerMode.Raw, 0 }, { AudioSpeakerMode.Mono, 1 }, { AudioSpeakerMode.Stereo, 2 }, { AudioSpeakerMode.Quad, 4 }, { AudioSpeakerMode.Surround, 5 }, { AudioSpeakerMode.Mode5point1, 6 }, { AudioSpeakerMode.Mode7point1, 8 }, { AudioSpeakerMode.Prologic, 0 }, }[AudioSettings.speakerMode]; int playBufSize; int playBufNum; AudioSettings.GetDSPBufferSize(out playBufSize, out playBufNum); proc = new Voice.SpeexProcessor(new Logger(), () => (long)(AudioSettings.dspTime * 1000), v.FrameSize, localVoice.Info.SourceSamplingRate, localVoice.Info.Channels, AudioSettings.outputSampleRate, playChannelCount, playBufSize); v.AddPreProcessor(proc); UpdateProcProps(); Debug.Log("SpeexDSP initialized."); }
public void SendVoiceRemove(LocalVoice voice, int channelId, int targetPlayerId) { object content = voiceClient.buildVoiceRemoveMessage(voice); var sendOpt = new SendOptions() { Reliability = true, Channel = (byte)channelId }; var opt = new LoadBalancing.RaiseEventOptions(); if (targetPlayerId != 0) { opt.TargetActors = new int[] { targetPlayerId }; } lock (sendLock) { if (voice.DebugEchoMode) { opt.Receivers = ReceiverGroup.All; } this.OpRaiseEvent(VoiceEventCode.GetCode(channelId), content, opt, sendOpt); } }