Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

415 řádky
12 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #pragma once
  13. //
  14. // The audio stream interface
  15. // ——————————————————————————
  16. // Stream, mix, and apply audio effects.
  17. //
  18. #include <cmath> // std::floor
  19. #include <functional> // std::function
  20. #include <limits> // std::numeric_limits
  21. #include <memory> // std::shared_ptr
  22. #include <type_traits> // std::is_same_v
  23. #include <unordered_set> // std::unordered_set
  24. #include "../math/interp.h" // lol::interp::lanczos
  25. namespace lol::audio
  26. {
  27. class sample
  28. {
  29. public:
  30. // Convert samples between different types (float, int16_t, uint8_t, …)
  31. template<typename FROM, typename TO>
  32. static inline TO convert(FROM x)
  33. {
  34. constexpr auto from_fp = std::is_floating_point_v<FROM>;
  35. constexpr auto to_fp = std::is_floating_point_v<TO>;
  36. if constexpr (std::is_same_v<FROM, TO> || (from_fp && to_fp))
  37. {
  38. // If types are the same, or both floating point, no conversion is needed
  39. return TO(x);
  40. }
  41. else if constexpr (from_fp)
  42. {
  43. // From floating point to integer:
  44. // - change range from -1…1 to 0…1
  45. // - multiply by the size of the integer range
  46. // - add min, round down, and clamp to min…max
  47. FROM constexpr min(std::numeric_limits<TO>::min());
  48. FROM constexpr max(std::numeric_limits<TO>::max());
  49. x = (max - min + 1) / 2 * (x + 1);
  50. return TO(std::max(min, std::min(max, std::floor(x + min))));
  51. }
  52. else if constexpr (to_fp)
  53. {
  54. // From integer to floating point:
  55. // - compute (x - min) / (max - min)
  56. // - change range from 0…1 to -1…1
  57. TO constexpr min(std::numeric_limits<FROM>::min());
  58. TO constexpr max(std::numeric_limits<FROM>::max());
  59. return 2 / (max - min) * (TO(x) - min) - 1;
  60. }
  61. else
  62. {
  63. // When converting between integer types, we first convert to an unsigned
  64. // type of same size as source (e.g. int16_t → uint16_t) to ensure that all
  65. // operations will happen modulo n (not guaranteed with signed types).
  66. // The next step is to shift right (drop bits) or promote left (multiply by
  67. // a magic constant such as 0x1010101 or 0x10001). This happens using the
  68. // UBIG type, which is an unsigned integer type at least as large as FROM
  69. // and TO.
  70. // Finally, we convert back to signed (e.g. uint16_t → int16_t) if necessary.
  71. using UFROM = std::make_unsigned_t<FROM>;
  72. using UTO = std::make_unsigned_t<TO>;
  73. using UBIG = std::conditional_t<(sizeof(FROM) > sizeof(TO)), UFROM, UTO>;
  74. UBIG constexpr mul = std::numeric_limits<UBIG>::max() / std::numeric_limits<UFROM>::max();
  75. UBIG constexpr div = UBIG(1) << 8 * (sizeof(UBIG) - sizeof(UTO));
  76. auto tmp = UFROM(UFROM(x) - UFROM(std::numeric_limits<FROM>::min())) * mul / div;
  77. return TO(tmp + UTO(std::numeric_limits<TO>::min()));
  78. }
  79. }
  80. // Saturated addition for samples
  81. template<typename T>
  82. static inline T sadd(T x, T y)
  83. {
  84. if constexpr (std::is_floating_point_v<T>)
  85. {
  86. // No saturation for floating point types
  87. return x + y;
  88. }
  89. else if constexpr (sizeof(T) <= 4)
  90. {
  91. // For integer types up to 32-bit, do the computation with a larger type
  92. using BIG = std::conditional_t<sizeof(T) == 1, int16_t,
  93. std::conditional_t<sizeof(T) == 2, int32_t,
  94. std::conditional_t<sizeof(T) == 4, int64_t, void>>>;
  95. BIG constexpr min = std::numeric_limits<T>::min();
  96. BIG constexpr max = std::numeric_limits<T>::max();
  97. BIG constexpr zero = (min + max + 1) >> 1;
  98. return T(std::max(min, std::min(max, BIG(BIG(x) + BIG(y) - zero))));
  99. }
  100. else if constexpr (std::is_unsigned_v<T>)
  101. {
  102. // Unsigned saturated add for 64-bit and larger: clamp according to overflow
  103. T constexpr zero = T(1) << 8 * sizeof(T) - 1;
  104. T constexpr minus_one = zero - T(1);
  105. T ret = x + y;
  106. return ret >= x ? std::max(zero, ret) - zero : std::min(minus_one, ret) + zero;
  107. }
  108. else
  109. {
  110. // Signed saturated add for 64-bit and larger: if signs differ, no overflow
  111. // occurred, just return the sum of the arguments; otherwise, clamp according
  112. // to the arguments sign.
  113. using U = std::make_unsigned_t<T>;
  114. U constexpr umax = U(std::numeric_limits<T>::max());
  115. U constexpr umin = U(std::numeric_limits<T>::min());
  116. U ret = U(x) + U(y);
  117. return T(x ^ y) < 0 ? T(ret) : x >= 0 ? T(std::min(ret, umax)) : T(std::max(ret, umin));
  118. }
  119. }
  120. // Clipping for samples
  121. template<typename T>
  122. static inline T clip(T x)
  123. {
  124. if constexpr (std::is_floating_point_v<T>)
  125. {
  126. return std::min(T(1), std::max(T(-1), x));
  127. }
  128. else
  129. {
  130. // Clipping is only relevant for floating point types
  131. return x;
  132. }
  133. }
  134. template<typename T>
  135. static inline T softclip(T x)
  136. {
  137. if constexpr (std::is_floating_point_v<T>)
  138. {
  139. return std::tanh(x);
  140. }
  141. else
  142. {
  143. // Clipping is only relevant for floating point types
  144. return x;
  145. }
  146. }
  147. };
  148. template<typename T>
  149. class stream
  150. {
  151. public:
  152. using sample_type = T;
  153. virtual size_t get(T* buf, size_t frames) = 0;
  154. inline size_t channels() const { return m_channels; }
  155. inline int frequency() const { return m_frequency; }
  156. inline size_t frame_size() const { return channels() * sizeof(T); }
  157. virtual ~stream() = default;
  158. protected:
  159. stream(size_t channels, int frequency)
  160. : m_channels(channels),
  161. m_frequency(frequency)
  162. {}
  163. size_t m_channels;
  164. int m_frequency;
  165. };
  166. template<typename T>
  167. class generator : public stream<T>
  168. {
  169. public:
  170. generator(std::function<size_t(T*, size_t)> get, size_t channels, int frequency)
  171. : stream<T>(channels, frequency),
  172. m_get(get)
  173. {}
  174. virtual size_t get(T* buf, size_t frames) override
  175. {
  176. return m_get(buf, frames);
  177. }
  178. protected:
  179. std::function<size_t(T*, size_t)> m_get;
  180. };
  181. template<typename T>
  182. class mixer : public stream<T>
  183. {
  184. public:
  185. mixer(size_t channels, int frequency)
  186. : stream<T>(channels, frequency)
  187. {}
  188. void add(std::shared_ptr<stream<T>> s)
  189. {
  190. // FIXME: check the channel count!
  191. m_streams.insert(s);
  192. }
  193. void remove(std::shared_ptr<stream<T>> s)
  194. {
  195. m_streams.erase(s);
  196. }
  197. virtual size_t get(T *buf, size_t frames) override
  198. {
  199. std::vector<std::vector<T>> buffers;
  200. size_t const samples = frames * this->channels();
  201. for (auto s : m_streams)
  202. {
  203. buffers.push_back(std::vector<T>(samples));
  204. s->get(buffers.back().data(), frames);
  205. }
  206. for (size_t n = 0; n < samples; ++n)
  207. {
  208. T x = T(0);
  209. for (auto const &b : buffers)
  210. x = sample::sadd(x, b[n]);
  211. buf[n] = x;
  212. }
  213. return frames;
  214. }
  215. protected:
  216. std::unordered_set<std::shared_ptr<stream<T>>> m_streams;
  217. };
  218. template<typename T, typename T0>
  219. class converter : public stream<T>
  220. {
  221. public:
  222. converter(std::shared_ptr<stream<T0>> s)
  223. : stream<T>(s->channels(), s->frequency()),
  224. m_in(s)
  225. {}
  226. virtual size_t get(T *buf, size_t frames) override
  227. {
  228. if constexpr (std::is_same_v<T0, T>)
  229. return m_in->get(buf, frames);
  230. size_t samples = frames * this->channels();
  231. std::vector<T0> tmp(samples);
  232. m_in->get(tmp.data(), frames);
  233. for (size_t n = 0; n < samples; ++n)
  234. buf[n] = sample::convert<T0, T>(tmp[n]);
  235. return frames;
  236. }
  237. protected:
  238. std::shared_ptr<stream<T0>> m_in;
  239. };
  240. template<typename T>
  241. class mapper : public stream<T>
  242. {
  243. public:
  244. mapper(std::shared_ptr<stream<T>> s, size_t channels)
  245. : stream<T>(channels, s->frequency()),
  246. m_in(s)
  247. {}
  248. virtual size_t get(T *buf, size_t frames) override
  249. {
  250. if (this->channels() == m_in->channels())
  251. return m_in->get(buf, frames);
  252. std::vector<T> tmp(frames * m_in->channels());
  253. m_in->get(tmp.data(), frames);
  254. // FIXME: we need to build a better matrix than this, also maybe the mapper
  255. // constructor needs an option to preserve energy or not?
  256. std::vector<T> matrix(m_in->channels() * this->channels(), T(1));
  257. for (size_t f = 0; f < frames; ++f)
  258. {
  259. for (size_t out_ch = 0; out_ch < this->channels(); ++out_ch)
  260. {
  261. T x(0);
  262. for (size_t in_ch = 0; in_ch < m_in->channels(); ++in_ch)
  263. x = sample::sadd(x, tmp[f * m_in->channels() + in_ch] * matrix[out_ch * m_in->channels() + in_ch]);
  264. buf[f * this->channels() + out_ch] = x;
  265. }
  266. }
  267. return frames;
  268. }
  269. protected:
  270. std::shared_ptr<stream<T>> m_in;
  271. };
  272. template<typename T>
  273. class resampler : public stream<T>
  274. {
  275. public:
  276. resampler(std::shared_ptr<stream<T>> s, int frequency)
  277. : stream<T>(s->channels(), frequency),
  278. m_in(s),
  279. m_pos(0)
  280. {}
  281. virtual size_t get(T *buf, size_t frames) override
  282. {
  283. size_t const channels = this->channels();
  284. size_t const in_rate = m_in->frequency();
  285. size_t const out_rate = this->frequency();
  286. if (in_rate == out_rate)
  287. return m_in->get(buf, frames);
  288. for (size_t n = 0; n < frames; ++n, m_pos += in_rate)
  289. {
  290. // Fill internal buffer if we don’t have enough data
  291. while (m_cache.size() / channels < m_pos / out_rate + m_lanczos.size())
  292. {
  293. // Remove obsolete frames on the left
  294. size_t todelete = std::min(m_pos / out_rate, m_cache.size() / channels);
  295. std::vector<T>(m_cache.begin() + todelete * channels, m_cache.end()).swap(m_cache);
  296. m_pos -= todelete * out_rate;
  297. // Add new frames to the right
  298. size_t offset = m_cache.size();
  299. m_cache.resize(offset + frames * channels);
  300. m_in->get(&m_cache[offset], frames);
  301. }
  302. size_t n0 = m_pos / out_rate;
  303. float alpha = float(m_pos % out_rate) / out_rate;
  304. for (size_t ch = 0; ch < channels; ++ch)
  305. *buf++ = m_lanczos.get(&m_cache[n0 * channels + ch], channels, alpha);
  306. }
  307. return frames;
  308. }
  309. protected:
  310. std::shared_ptr<stream<T>> m_in;
  311. interp::lanczos<T> m_lanczos;
  312. std::vector<T> m_cache;
  313. size_t m_pos;
  314. };
  315. template<typename T>
  316. static inline auto make_generator(std::function<size_t(T*, size_t)> f, size_t channels, int frequency)
  317. {
  318. return std::make_shared<generator<T>>(f, channels, frequency);
  319. }
  320. template<typename F>
  321. static inline auto make_generator(F f, size_t channels, int frequency)
  322. {
  323. return make_generator(std::function(f), channels, frequency);
  324. }
  325. template<typename T, typename S0, typename T0 = typename S0::sample_type>
  326. static inline auto make_converter(std::shared_ptr<S0> s)
  327. {
  328. return std::make_shared<converter<T, T0>>(std::shared_ptr<stream<T0>>(s));
  329. }
  330. template<typename S, typename T = typename S::sample_type>
  331. static inline auto make_mapper(std::shared_ptr<S> s, size_t channels)
  332. {
  333. return std::make_shared<mapper<T>>(std::shared_ptr<stream<T>>(s), channels);
  334. }
  335. template<typename S, typename T = typename S::sample_type>
  336. static inline auto make_resampler(std::shared_ptr<S> s, int frequency)
  337. {
  338. return std::make_shared<resampler<T>>(std::shared_ptr<stream<T>>(s), frequency);
  339. }
  340. template<typename T, typename S0, typename T0 = typename S0::sample_type>
  341. static inline auto make_adapter(std::shared_ptr<S0> s, size_t channels, int frequency)
  342. {
  343. return make_resampler(make_mapper(make_converter<T>(s), channels), frequency);
  344. }
  345. } // namespace lol::audio