using System; namespace Photon.Voice { public interface IResettable { void Reset(); } /// Audio Source interface. public interface IAudioDesc : IDisposable { /// Sampling rate of the audio signal (in Hz). int SamplingRate { get; } /// Number of channels in the audio signal. int Channels { get; } /// If not null, audio object is in invalid state. string Error { get; } } // Trivial implementation. Used to build erroneous source. public class AudioDesc : IAudioDesc { public AudioDesc(int samplingRate, int channels, string error) { SamplingRate = samplingRate; Channels = channels; Error = error; } public int SamplingRate { get; private set; } public int Channels { get; private set; } public string Error { get; private set; } public void Dispose() { } } /// Audio Reader interface. /// Opposed to an IAudioPusher (which will push its audio data whenever it is ready), /// an IAudioReader will deliver audio data when it is "pulled" (it's Read function is called). public interface IAudioReader : IDataReader, IAudioDesc { } /// Audio Pusher interface. /// Opposed to an IAudioReader (which will deliver audio data when it is "pulled"), /// an IAudioPusher will push its audio data whenever it is ready, public interface IAudioPusher : IAudioDesc { /// Set the callback function used for pushing data. /// Callback function to use. /// Buffer factory used to create the buffer that is pushed to the callback void SetCallback(Action callback, ObjectFactory bufferFactory); } /// Interface for an outgoing audio stream. /// A LocalVoice always brings a LevelMeter and a VoiceDetector, which you can access using this interface. public interface ILocalVoiceAudio { /// The VoiceDetector in use. /// Use it to enable or disable voice detector and set its parameters. AudioUtil.IVoiceDetector VoiceDetector { get; } /// The LevelMeter utility in use. AudioUtil.ILevelMeter LevelMeter { get; } /// If true, voice detector calibration is in progress. bool VoiceDetectorCalibrating { get; } /// /// Trigger voice detector calibration process. /// /// While calibrating, keep silence. Voice detector sets threshold based on measured backgroud noise level. /// Duration of calibration (in milliseconds). /// Called when calibration is complete. Parameter is new threshold value. void VoiceDetectorCalibrate(int durationMs, Action onCalibrated = null); } /// The type of samples used for audio processing. public enum AudioSampleType { Source, Short, Float, } /// Outgoing audio stream. abstract public class LocalVoiceAudio : LocalVoiceFramed, ILocalVoiceAudio { /// Create a new LocalVoiceAudio{T} instance. /// The VoiceClient to use for this outgoing stream. /// Numeric ID for this voice. /// Encoder to use for this voice. /// Outgoing stream parameters. /// Audio source parameters. /// Voice transport channel ID to use for this voice. /// The new LocalVoiceAudio{T} instance. public static LocalVoiceAudio Create(VoiceClient voiceClient, byte voiceId, IEncoder encoder, VoiceInfo voiceInfo, IAudioDesc audioSourceDesc, int channelId) { if (typeof(T) == typeof(float)) { return new LocalVoiceAudioFloat(voiceClient, encoder, voiceId, voiceInfo, audioSourceDesc, channelId) as LocalVoiceAudio; } else if (typeof(T) == typeof(short)) { return new LocalVoiceAudioShort(voiceClient, encoder, voiceId, voiceInfo, audioSourceDesc, channelId) as LocalVoiceAudio; } else { throw new UnsupportedSampleTypeException(typeof(T)); } } public virtual AudioUtil.IVoiceDetector VoiceDetector { get { return voiceDetector; } } protected AudioUtil.VoiceDetector voiceDetector; protected AudioUtil.VoiceDetectorCalibration voiceDetectorCalibration; public virtual AudioUtil.ILevelMeter LevelMeter { get { return levelMeter; } } protected AudioUtil.LevelMeter levelMeter; /// Trigger voice detector calibration process. /// While calibrating, keep silence. Voice detector sets threshold basing on measured backgroud noise level. /// Duration of calibration in milliseconds. /// Called when calibration is complete. Parameter is new threshold value. public void VoiceDetectorCalibrate(int durationMs, Action onCalibrated = null) { voiceDetectorCalibration.Calibrate(durationMs, onCalibrated); } /// True if the VoiceDetector is currently calibrating. public bool VoiceDetectorCalibrating { get { return voiceDetectorCalibration.IsCalibrating; } } protected int channels; protected bool resampleSource; internal LocalVoiceAudio(VoiceClient voiceClient, IEncoder encoder, byte id, VoiceInfo voiceInfo, IAudioDesc audioSourceDesc, int channelId) : base(voiceClient, encoder, id, voiceInfo, channelId, voiceInfo.SamplingRate != 0 ? voiceInfo.FrameSize * audioSourceDesc.SamplingRate / voiceInfo.SamplingRate : voiceInfo.FrameSize ) { this.channels = voiceInfo.Channels; if (audioSourceDesc.SamplingRate != voiceInfo.SamplingRate) { this.resampleSource = true; this.voiceClient.logger.LogWarning("[PV] Local voice #" + this.id + " audio source frequency " + audioSourceDesc.SamplingRate + " and encoder sampling rate " + voiceInfo.SamplingRate + " do not match. Resampling will occur before encoding."); } } protected void initBuiltinProcessors() { if (this.resampleSource) { AddPostProcessor(new AudioUtil.Resampler(this.info.FrameSize, channels)); } this.voiceDetectorCalibration = new AudioUtil.VoiceDetectorCalibration(voiceDetector, levelMeter, this.info.SamplingRate, (int)this.channels); AddPostProcessor(levelMeter, voiceDetectorCalibration, voiceDetector); // level meter and calibration should be processed even if no signal detected } } /// Dummy LocalVoiceAudio /// For testing, this LocalVoiceAudio implementation features a and a public class LocalVoiceAudioDummy : LocalVoice, ILocalVoiceAudio { private AudioUtil.VoiceDetectorDummy voiceDetector; private AudioUtil.LevelMeterDummy levelMeter; public AudioUtil.IVoiceDetector VoiceDetector { get { return voiceDetector; } } public AudioUtil.ILevelMeter LevelMeter { get { return levelMeter; } } public bool VoiceDetectorCalibrating { get { return false; } } public void VoiceDetectorCalibrate(int durationMs, Action onCalibrated = null) { } public LocalVoiceAudioDummy() { voiceDetector = new AudioUtil.VoiceDetectorDummy(); levelMeter = new AudioUtil.LevelMeterDummy(); } /// A Dummy LocalVoiceAudio instance. public static LocalVoiceAudioDummy Dummy = new LocalVoiceAudioDummy(); } /// Specialization of for float audio public class LocalVoiceAudioFloat : LocalVoiceAudio { internal LocalVoiceAudioFloat(VoiceClient voiceClient, IEncoder encoder, byte id, VoiceInfo voiceInfo, IAudioDesc audioSourceDesc, int channelId) : base(voiceClient, encoder, id, voiceInfo, audioSourceDesc, channelId) { // these 2 processors go after resampler this.levelMeter = new AudioUtil.LevelMeterFloat(this.info.SamplingRate, this.info.Channels); this.voiceDetector = new AudioUtil.VoiceDetectorFloat(this.info.SamplingRate, this.info.Channels); initBuiltinProcessors(); } } /// Specialization of for short audio public class LocalVoiceAudioShort : LocalVoiceAudio { internal LocalVoiceAudioShort(VoiceClient voiceClient, IEncoder encoder, byte id, VoiceInfo voiceInfo, IAudioDesc audioSourceDesc, int channelId) : base(voiceClient, encoder, id, voiceInfo,audioSourceDesc, channelId) { // these 2 processors go after resampler this.levelMeter = new AudioUtil.LevelMeterShort(this.info.SamplingRate, this.info.Channels); //1/2 sec this.voiceDetector = new AudioUtil.VoiceDetectorShort(this.info.SamplingRate, this.info.Channels); initBuiltinProcessors(); } } }