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();
}
}
}