TimeConverterFactory.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. namespace Bmp.Core.FFMpeg.CsCorePorts;
  2. /// <summary>
  3. /// Provides <see cref="TimeConverter"/>s for converting raw time values (e.g. bytes, samples,...) to a <see cref="TimeSpan"/> and back.
  4. /// </summary>
  5. public sealed class TimeConverterFactory
  6. {
  7. // ReSharper disable once InconsistentNaming
  8. private readonly static TimeConverterFactory _instance = new TimeConverterFactory();
  9. /// <summary>
  10. /// Gets the default instance of the factory.
  11. /// </summary>
  12. public static TimeConverterFactory Instance
  13. {
  14. get { return _instance; }
  15. }
  16. private readonly Dictionary<Type, TimeConverter> _timeConverters;
  17. private readonly Dictionary<Type, CacheItem> _cache;
  18. private TimeConverterFactory()
  19. {
  20. _timeConverters = new Dictionary<Type, TimeConverter>();
  21. _cache = new Dictionary<Type, CacheItem>();
  22. RegisterTimeConverterForSourceType<IWaveSource>(TimeConverter.WaveSourceTimeConverter);
  23. RegisterTimeConverterForSourceType<ISampleSource>(TimeConverter.SampleSourceTimeConverter);
  24. }
  25. /// <summary>
  26. /// Registers a new <see cref="TimeConverter"/> for a specific source type.
  27. /// </summary>
  28. /// <param name="timeConverter">The <see cref="TimeConverter"/> to register.</param>
  29. /// <typeparam name="TSource">The source type.</typeparam>
  30. /// <exception cref="ArgumentNullException">timeConverter is null.</exception>
  31. /// <exception cref="ArgumentException">There is already a <see cref="TimeConverter"/> registered for the specified <typeparamref name="TSource"/>.</exception>
  32. /// <remarks>The <see cref="TimeConverterFactory"/> class uses the source type to find choose the best <see cref="TimeConverter"/> for an <see cref="IAudioSource"/>. For more information, see <see cref="GetTimeConverterForSourceType"/>.</remarks>
  33. public void RegisterTimeConverterForSourceType<TSource>(TimeConverter timeConverter)
  34. where TSource : IAudioSource
  35. {
  36. if (timeConverter == null)
  37. throw new ArgumentNullException("timeConverter");
  38. var type = typeof(TSource);
  39. if (_timeConverters.ContainsKey(type))
  40. throw new ArgumentException("A timeconverter for the same source type got already registered.");
  41. _timeConverters.Add(type, timeConverter);
  42. }
  43. /// <summary>
  44. /// Unregisters a previously registered <see cref="TimeConverter"/>.
  45. /// </summary>
  46. /// <typeparam name="TSource">The source type, that got passed to the <see cref="RegisterTimeConverterForSourceType{TSource}"/> method previously.</typeparam>
  47. /// <exception cref="ArgumentException">The specified source type could not be found.</exception>
  48. public void UnregisterTimeConverter<TSource>()
  49. where TSource : IAudioSource
  50. {
  51. var type = typeof(TSource);
  52. if (!_timeConverters.ContainsKey(type))
  53. throw new ArgumentException("There is no timeconverter registered for the specified source type.");
  54. _timeConverters.Remove(type);
  55. }
  56. /// <summary>
  57. /// Gets the <see cref="TimeConverter"/> for the specified <paramref name="source"/>.
  58. /// </summary>
  59. /// <param name="source">The <see cref="IAudioSource"/> object to get the <see cref="TimeConverter"/> for.</param>
  60. /// <typeparam name="TSource">The type of the <paramref name="source"/>.</typeparam>
  61. /// <returns>The best <see cref="TimeConverter"/> for the specified <paramref name="source"/>.</returns>
  62. /// <exception cref="ArgumentNullException">The specified <paramref name="source"/> is null.</exception>
  63. /// <exception cref="ArgumentException">
  64. /// Specified type is no AudioSource.;type
  65. /// or
  66. /// No registered time converter for the specified source type was found.
  67. /// or
  68. /// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the <see cref="TimeConverterAttribute"/>.
  69. /// </exception>
  70. /// <remarks>
  71. /// The <see cref="GetTimeConverterForSource{TSource}(TSource)"/> chooses the best <see cref="TimeConverter"/> for the specified <paramref name="source"/>.
  72. /// If there is no <see cref="TimeConverterAttribute"/> applied to the <see cref="IAudioSource"/> object (the <paramref name="source"/>), it looks up the inheritance hierarchy (interfaces included) of the <see cref="IAudioSource"/> object
  73. /// and searches for all registered source types. If there is a match it returns the associated <see cref="TimeConverter"/>. If there are more or less than one match BUT no <see cref="TimeConverterAttribute"/>
  74. /// it throws an exception.</remarks>
  75. public TimeConverter GetTimeConverterForSource<TSource>(TSource source) where TSource : class, IAudioSource
  76. {
  77. if (source == null)
  78. throw new ArgumentNullException("source");
  79. return GetTimeConverterForSourceType(source.GetType());
  80. }
  81. /// <summary>
  82. /// Gets the <see cref="TimeConverter"/> for the specified source type.
  83. /// </summary>
  84. /// <typeparam name="TSource">The type of the source.</typeparam>
  85. /// <returns>The best <see cref="TimeConverter"/> for the specified source type.</returns>
  86. /// <exception cref="ArgumentException">
  87. /// Specified type is no AudioSource.;type
  88. /// or
  89. /// No registered time converter for the specified source type was found.
  90. /// or
  91. /// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the <see cref="TimeConverterAttribute"/>.
  92. /// </exception>
  93. /// <remarks>
  94. /// The <see cref="GetTimeConverterForSource{TSource}()"/> chooses the best <see cref="TimeConverter"/> for the specified source type.
  95. /// If there is no <see cref="TimeConverterAttribute"/> applied to the <see cref="IAudioSource"/> object, it looks up the inheritance hierarchy (interfaces included) of the <see cref="IAudioSource"/> object
  96. /// and searches for all registered source types. If there is a match it returns the associated <see cref="TimeConverter"/>. If there are more or less than one match BUT no <see cref="TimeConverterAttribute"/>
  97. /// it throws an exception.</remarks>
  98. public TimeConverter GetTimeConverterForSource<TSource>()
  99. where TSource : IAudioSource
  100. {
  101. return GetTimeConverterForSourceType(typeof(TSource));
  102. }
  103. /// <summary>
  104. /// Gets the <see cref="TimeConverter"/> for the specified <paramref name="sourceType"/>.
  105. /// </summary>
  106. /// <param name="sourceType">The <see cref="Type"/> to get the associated <see cref="TimeConverter"/> for.</param>
  107. /// <returns>The best <see cref="TimeConverter"/> for the specified <paramref name="sourceType"/>.</returns>
  108. /// <exception cref="ArgumentException">
  109. /// Specified type is no AudioSource.;type
  110. /// or
  111. /// No registered time converter for the specified source type was found.
  112. /// or
  113. /// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the <see cref="TimeConverterAttribute"/>.
  114. /// </exception>
  115. /// <remarks>
  116. /// The <see cref="GetTimeConverterForSourceType(Type)"/> chooses the best <see cref="TimeConverter"/> for the specified <paramref name="sourceType"/>.
  117. /// If there is no <see cref="TimeConverterAttribute"/> applied to the <see cref="IAudioSource"/> object (the <paramref name="sourceType"/>), it looks up the inheritance hierarchy (interfaces included) of the <see cref="IAudioSource"/> object
  118. /// and searches for all registered source types. If there is a match it returns the associated <see cref="TimeConverter"/>. If there are more or less than one match BUT no <see cref="TimeConverterAttribute"/>
  119. /// it throws an exception.</remarks>
  120. public TimeConverter GetTimeConverterForSourceType(Type sourceType)
  121. {
  122. if (sourceType == null)
  123. throw new ArgumentNullException("sourceType");
  124. if (!typeof(IAudioSource).IsAssignableFrom(sourceType))
  125. throw new ArgumentException("Specified type is no AudioSource.", "sourceType");
  126. //we may got it already in the cache
  127. if (_cache.ContainsKey(sourceType))
  128. return _cache[sourceType].GetTimeConverter();
  129. //nope, there is nothing in the cache
  130. //search for a TimeConverterAttribute
  131. var attribute =
  132. sourceType.GetCustomAttributes(typeof(TimeConverterAttribute), false).FirstOrDefault() as
  133. TimeConverterAttribute;
  134. TimeConverter timeConverter = null;
  135. try
  136. {
  137. if (attribute == null)
  138. {
  139. //there is no attribute
  140. //search for base types
  141. var baseTypes = GetTypes(sourceType).Where(x => _timeConverters.ContainsKey(x)).ToArray();
  142. //we've got a match
  143. if (baseTypes.Length == 1)
  144. {
  145. timeConverter = _timeConverters[baseTypes.First()];
  146. return timeConverter;
  147. }
  148. //we've got no match
  149. if (baseTypes.Length == 0)
  150. throw new ArgumentException(
  151. "No registered time converter for the specified source type was found.");
  152. //else baseTypes.Length > 1
  153. throw new ArgumentException(
  154. "Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the TimeConverterAttribute.");
  155. }
  156. var timeConverterType = attribute.TimeConverterType;
  157. timeConverter = (TimeConverter)Activator.CreateInstance(timeConverterType, attribute.Args);
  158. return timeConverter;
  159. }
  160. finally
  161. {
  162. //add the result to the cache
  163. if (timeConverter != null)
  164. {
  165. CacheItem cacheItem;
  166. if (attribute == null)
  167. cacheItem = new CacheItem { CreateNewInstance = false, TimeConverter = timeConverter };
  168. else
  169. cacheItem = new CacheItem
  170. {
  171. CreateNewInstance = attribute.ForceNewInstance,
  172. TimeConverterAttribute = attribute,
  173. TimeConverter = attribute.ForceNewInstance ? null : timeConverter
  174. };
  175. _cache[sourceType] = cacheItem;
  176. }
  177. }
  178. }
  179. /// <summary>
  180. /// Clears the internal cache.
  181. /// </summary>
  182. public void ClearCache()
  183. {
  184. _cache.Clear();
  185. }
  186. private IEnumerable<Type> GetTypes(Type type)
  187. {
  188. //copied from dadhi see http://stackoverflow.com/questions/1823655/given-a-c-sharp-type-get-its-base-classes-and-implemented-interfaces
  189. return type.BaseType == typeof(object)
  190. ? type.GetInterfaces()
  191. : Enumerable
  192. .Repeat(type.BaseType, 1)
  193. .Concat(type.GetInterfaces())
  194. .Concat(GetTypes(type.BaseType))
  195. .Distinct();
  196. }
  197. private class CacheItem
  198. {
  199. public TimeConverter TimeConverter { get; set; }
  200. public TimeConverterAttribute TimeConverterAttribute { get; set; }
  201. public bool CreateNewInstance { get; set; }
  202. public TimeConverter GetTimeConverter()
  203. {
  204. if (CreateNewInstance)
  205. return (TimeConverter)Activator.CreateInstance(TimeConverterAttribute.TimeConverterType, TimeConverterAttribute.Args);
  206. return TimeConverter;
  207. }
  208. }
  209. }