/* Copyright (C) 2014 DaikonForge */ namespace DaikonForge.VoIP { using UnityEngine; using System.Collections.Generic; public abstract class VoiceControllerBase : MonoBehaviour { /// /// Gets whether this voice controller belongs to the local client or not /// public abstract bool IsLocal { get; } /// /// Gets the audio input device attached to this voice controller /// public AudioInputDeviceBase AudioInputDevice { get { return this.microphone; } } /// /// Gets the audio output device attached to this voice controller /// public IAudioPlayer AudioOutputDevice { get { return this.speaker; } } /// /// If true, play back received audio even if this belongs to the local client /// public bool DebugAudio = true; /// /// If true, voice controller won't decode and play back received frames /// public bool Mute = false; protected AudioInputDeviceBase microphone; protected IAudioPlayer speaker; protected IAudioCodec codec; protected ulong nextFrameIndex = 0; protected ulong nextExpectedIndex = 0; protected virtual void Awake() { codec = GetCodec(); microphone = GetComponent(); speaker = GetComponent( typeof( IAudioPlayer ) ) as IAudioPlayer; if( microphone == null ) { Debug.LogWarning( "No audio input component attached to speaker", this ); return; } if( speaker == null ) { Debug.LogWarning("No audio output component attached to speaker", this); return; } if( IsLocal ) { microphone.OnAudioBufferReady += this.OnMicrophoneDataReady; microphone.OnRecordingStart += this.RecordingStart; microphone.OnNoRecordingPerimission += this.NoRecordingPermission; microphone.OnRecordingEnd += this.RecordingEnd; //microphone.StartRecording(); } } protected virtual void OnDestroy() { if( IsLocal && microphone != null ) { microphone.OnAudioBufferReady -= this.OnMicrophoneDataReady; microphone.OnRecordingStart -= this.RecordingStart; microphone.OnNoRecordingPerimission -= this.NoRecordingPermission; microphone.OnRecordingEnd -= this.RecordingEnd; microphone.StopRecording(); } } protected virtual void RecordingStart() { nextFrameIndex = 0; } protected virtual void NoRecordingPermission() { } protected virtual void RecordingEnd(int length) { } /// /// Called when a frame of audio is encoded and ready to send /// protected virtual void OnAudioDataEncoded( VoicePacketWrapper encodedFrame ) { // TODO: send audio over network } /// /// Create a new codec /// protected virtual IAudioCodec GetCodec() { AudioUtils.FrequencyProvider = new SpeexCodec.FrequencyProvider(); return new SpeexCodec(VBR, Quality); } private static int _quality = 4; public static int Quality { get { return _quality; } set { _quality = value; } } private static bool _vbr = false; public static bool VBR { get { return _vbr; } set { _vbr = value; } } /// /// If you need to skip receiving a frame, you should call this function so it can advance the next expected index counter /// protected void SkipFrame() { nextExpectedIndex++; } float startTime = 0; public float RecordeStartTime() { startTime = Time.time; return startTime; } public float CalcEndTime() { return Time.time - startTime; } /// /// Decode and play back received audio data /// protected virtual void ReceiveAudioData( VoicePacketWrapper encodedFrame ) { if( !IsLocal || DebugAudio ) { // discard old samples if (encodedFrame.Index < nextExpectedIndex) return; // voice controller is muted - don't bother decoding or buffering audio data if( Mute ) { nextExpectedIndex = encodedFrame.Index + 1; return; } //speaker.SetSampleRate( encodedFrame.Frequency * 1000 ); // some frames were lost, generate filler data for them // unless the speaker isn't playing any sound, in which case filler data will only delay the stream further // OR unless nextExpectedIndex is zero, implying that we haven't received any frames yet if( nextExpectedIndex != 0 && encodedFrame.Index != nextExpectedIndex && speaker.PlayingSound ) { int numMissingFrames = (int)( encodedFrame.Index - nextExpectedIndex ); for( int i = 0; i < numMissingFrames; i++ ) { BigArray filler = codec.GenerateMissingFrame( encodedFrame.Frequency ); speaker.BufferAudio( filler ); } } BigArray decoded = codec.DecodeFrame( encodedFrame ); speaker.BufferAudio( decoded ); nextExpectedIndex = encodedFrame.Index + 1; } } /// /// Called when new audio is available from the microphone /// protected virtual void OnMicrophoneDataReady( BigArray newData, int frequency ) { if( !IsLocal ) return; codec.OnAudioAvailable( newData ); VoicePacketWrapper? enc = codec.GetNextEncodedFrame( frequency ); while( enc.HasValue ) { // assign index VoicePacketWrapper packet = enc.Value; packet.Index = nextFrameIndex++; enc = packet; OnAudioDataEncoded( enc.Value ); enc = codec.GetNextEncodedFrame( frequency ); } } protected virtual void OnRecordingFinish() { } } }