namespace Bmp.Core.FFMpeg.CsCorePorts;
///
/// Provides s for converting raw time values (e.g. bytes, samples,...) to a and back.
///
public sealed class TimeConverterFactory
{
// ReSharper disable once InconsistentNaming
private readonly static TimeConverterFactory _instance = new TimeConverterFactory();
///
/// Gets the default instance of the factory.
///
public static TimeConverterFactory Instance
{
get { return _instance; }
}
private readonly Dictionary _timeConverters;
private readonly Dictionary _cache;
private TimeConverterFactory()
{
_timeConverters = new Dictionary();
_cache = new Dictionary();
RegisterTimeConverterForSourceType(TimeConverter.WaveSourceTimeConverter);
RegisterTimeConverterForSourceType(TimeConverter.SampleSourceTimeConverter);
}
///
/// Registers a new for a specific source type.
///
/// The to register.
/// The source type.
/// timeConverter is null.
/// There is already a registered for the specified .
/// The class uses the source type to find choose the best for an . For more information, see .
public void RegisterTimeConverterForSourceType(TimeConverter timeConverter)
where TSource : IAudioSource
{
if (timeConverter == null)
throw new ArgumentNullException("timeConverter");
var type = typeof(TSource);
if (_timeConverters.ContainsKey(type))
throw new ArgumentException("A timeconverter for the same source type got already registered.");
_timeConverters.Add(type, timeConverter);
}
///
/// Unregisters a previously registered .
///
/// The source type, that got passed to the method previously.
/// The specified source type could not be found.
public void UnregisterTimeConverter()
where TSource : IAudioSource
{
var type = typeof(TSource);
if (!_timeConverters.ContainsKey(type))
throw new ArgumentException("There is no timeconverter registered for the specified source type.");
_timeConverters.Remove(type);
}
///
/// Gets the for the specified .
///
/// The object to get the for.
/// The type of the .
/// The best for the specified .
/// The specified is null.
///
/// Specified type is no AudioSource.;type
/// or
/// No registered time converter for the specified source type was found.
/// or
/// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the .
///
///
/// The chooses the best for the specified .
/// If there is no applied to the object (the ), it looks up the inheritance hierarchy (interfaces included) of the object
/// and searches for all registered source types. If there is a match it returns the associated . If there are more or less than one match BUT no
/// it throws an exception.
public TimeConverter GetTimeConverterForSource(TSource source) where TSource : class, IAudioSource
{
if (source == null)
throw new ArgumentNullException("source");
return GetTimeConverterForSourceType(source.GetType());
}
///
/// Gets the for the specified source type.
///
/// The type of the source.
/// The best for the specified source type.
///
/// Specified type is no AudioSource.;type
/// or
/// No registered time converter for the specified source type was found.
/// or
/// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the .
///
///
/// The chooses the best for the specified source type.
/// If there is no applied to the object, it looks up the inheritance hierarchy (interfaces included) of the object
/// and searches for all registered source types. If there is a match it returns the associated . If there are more or less than one match BUT no
/// it throws an exception.
public TimeConverter GetTimeConverterForSource()
where TSource : IAudioSource
{
return GetTimeConverterForSourceType(typeof(TSource));
}
///
/// Gets the for the specified .
///
/// The to get the associated for.
/// The best for the specified .
///
/// Specified type is no AudioSource.;type
/// or
/// No registered time converter for the specified source type was found.
/// or
/// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the .
///
///
/// The chooses the best for the specified .
/// If there is no applied to the object (the ), it looks up the inheritance hierarchy (interfaces included) of the object
/// and searches for all registered source types. If there is a match it returns the associated . If there are more or less than one match BUT no
/// it throws an exception.
public TimeConverter GetTimeConverterForSourceType(Type sourceType)
{
if (sourceType == null)
throw new ArgumentNullException("sourceType");
if (!typeof(IAudioSource).IsAssignableFrom(sourceType))
throw new ArgumentException("Specified type is no AudioSource.", "sourceType");
//we may got it already in the cache
if (_cache.ContainsKey(sourceType))
return _cache[sourceType].GetTimeConverter();
//nope, there is nothing in the cache
//search for a TimeConverterAttribute
var attribute =
sourceType.GetCustomAttributes(typeof(TimeConverterAttribute), false).FirstOrDefault() as
TimeConverterAttribute;
TimeConverter timeConverter = null;
try
{
if (attribute == null)
{
//there is no attribute
//search for base types
var baseTypes = GetTypes(sourceType).Where(x => _timeConverters.ContainsKey(x)).ToArray();
//we've got a match
if (baseTypes.Length == 1)
{
timeConverter = _timeConverters[baseTypes.First()];
return timeConverter;
}
//we've got no match
if (baseTypes.Length == 0)
throw new ArgumentException(
"No registered time converter for the specified source type was found.");
//else baseTypes.Length > 1
throw new ArgumentException(
"Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the TimeConverterAttribute.");
}
var timeConverterType = attribute.TimeConverterType;
timeConverter = (TimeConverter)Activator.CreateInstance(timeConverterType, attribute.Args);
return timeConverter;
}
finally
{
//add the result to the cache
if (timeConverter != null)
{
CacheItem cacheItem;
if (attribute == null)
cacheItem = new CacheItem { CreateNewInstance = false, TimeConverter = timeConverter };
else
cacheItem = new CacheItem
{
CreateNewInstance = attribute.ForceNewInstance,
TimeConverterAttribute = attribute,
TimeConverter = attribute.ForceNewInstance ? null : timeConverter
};
_cache[sourceType] = cacheItem;
}
}
}
///
/// Clears the internal cache.
///
public void ClearCache()
{
_cache.Clear();
}
private IEnumerable GetTypes(Type type)
{
//copied from dadhi see http://stackoverflow.com/questions/1823655/given-a-c-sharp-type-get-its-base-classes-and-implemented-interfaces
return type.BaseType == typeof(object)
? type.GetInterfaces()
: Enumerable
.Repeat(type.BaseType, 1)
.Concat(type.GetInterfaces())
.Concat(GetTypes(type.BaseType))
.Distinct();
}
private class CacheItem
{
public TimeConverter TimeConverter { get; set; }
public TimeConverterAttribute TimeConverterAttribute { get; set; }
public bool CreateNewInstance { get; set; }
public TimeConverter GetTimeConverter()
{
if (CreateNewInstance)
return (TimeConverter)Activator.CreateInstance(TimeConverterAttribute.TimeConverterType, TimeConverterAttribute.Args);
return TimeConverter;
}
}
}