allafplay.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. /*
  2. * OpenAL LAF Playback Example
  3. *
  4. * Copyright (c) 2024 by Chris Robinson <[email protected]>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. /* This file contains an example for playback of Limitless Audio Format files.
  25. *
  26. * Some current shortcomings:
  27. *
  28. * - 256 track limit. Could be made higher, but making it too flexible would
  29. * necessitate more micro-allocations.
  30. *
  31. * - "Objects" mode only supports sample rates that are a multiple of 48. Since
  32. * positions are specified as samples in extra channels/tracks, and 3*16
  33. * samples are needed per track to specify the full set of positions, and
  34. * each chunk is exactly one second long, other sample rates would result in
  35. * the positions being split across chunks, causing the source playback
  36. * offset to go out of sync with the offset used to look up the current
  37. * spatial positions. Fixing this will require slightly more work to update
  38. * and synchronize the spatial position arrays against the playback offset.
  39. *
  40. * - Updates are specified as fast as the app can detect and react to the
  41. * reported source offset (that in turn depends on how often OpenAL renders).
  42. * This can cause some positions to be a touch late and lose some granular
  43. * temporal movement. In practice, this should probably be good enough for
  44. * most use-cases. Fixing this would need either a new extension to queue
  45. * position changes to apply when needed, or use a separate loopback device
  46. * to render with and control the number of samples rendered between updates
  47. * (with a second device to do the actual playback).
  48. *
  49. * - The LAF documentation doesn't prohibit object position tracks from being
  50. * separated with audio tracks in between, or from being the first tracks
  51. * followed by the audio tracks. It's not known if this is intended to be
  52. * allowed, but it's not supported. Object position tracks must be last.
  53. *
  54. * Some remaining issues:
  55. *
  56. * - There are bursts of static on some channels. This doesn't appear to be a
  57. * parsing error since the bursts last less than the chunk size, and it never
  58. * loses sync with the remaining chunks. Might be an encoding error with the
  59. * files tested.
  60. *
  61. * - Positions are specified in left-handed coordinates, despite the LAF
  62. * documentation saying it's right-handed. Might be an encoding error with
  63. * the files tested, or might be a misunderstanding about which is which. How
  64. * to proceed may depend on how wide-spread this issue ends up being, but for
  65. * now, they're treated as left-handed here.
  66. *
  67. * - The LAF documentation doesn't specify the range or direction for the
  68. * channels' X and Y axis rotation in Channels mode. Presumably X rotation
  69. * (elevation) goes from -pi/2...+pi/2 and Y rotation (azimuth) goes from
  70. * either -pi...+pi or 0...pi*2, but the direction of movement isn't
  71. * specified. Currently positive azimuth moves from center rightward and
  72. * positive elevation moves from head-level upward.
  73. */
  74. #include <algorithm>
  75. #include <array>
  76. #include <cassert>
  77. #include <cstdint>
  78. #include <fstream>
  79. #include <memory>
  80. #include <numeric>
  81. #include <string>
  82. #include <string_view>
  83. #include <thread>
  84. #include <type_traits>
  85. #include <vector>
  86. #include "AL/alc.h"
  87. #include "AL/al.h"
  88. #include "AL/alext.h"
  89. #include "albit.h"
  90. #include "almalloc.h"
  91. #include "alnumeric.h"
  92. #include "alspan.h"
  93. #include "alstring.h"
  94. #include "common/alhelpers.h"
  95. #include "filesystem.h"
  96. #include "fmt/core.h"
  97. #include "fmt/std.h"
  98. #include "win_main_utf8.h"
  99. namespace {
  100. /* Filter object functions */
  101. auto alGenFilters = LPALGENFILTERS{};
  102. auto alDeleteFilters = LPALDELETEFILTERS{};
  103. auto alIsFilter = LPALISFILTER{};
  104. auto alFilteri = LPALFILTERI{};
  105. auto alFilteriv = LPALFILTERIV{};
  106. auto alFilterf = LPALFILTERF{};
  107. auto alFilterfv = LPALFILTERFV{};
  108. auto alGetFilteri = LPALGETFILTERI{};
  109. auto alGetFilteriv = LPALGETFILTERIV{};
  110. auto alGetFilterf = LPALGETFILTERF{};
  111. auto alGetFilterfv = LPALGETFILTERFV{};
  112. /* Effect object functions */
  113. auto alGenEffects = LPALGENEFFECTS{};
  114. auto alDeleteEffects = LPALDELETEEFFECTS{};
  115. auto alIsEffect = LPALISEFFECT{};
  116. auto alEffecti = LPALEFFECTI{};
  117. auto alEffectiv = LPALEFFECTIV{};
  118. auto alEffectf = LPALEFFECTF{};
  119. auto alEffectfv = LPALEFFECTFV{};
  120. auto alGetEffecti = LPALGETEFFECTI{};
  121. auto alGetEffectiv = LPALGETEFFECTIV{};
  122. auto alGetEffectf = LPALGETEFFECTF{};
  123. auto alGetEffectfv = LPALGETEFFECTFV{};
  124. /* Auxiliary Effect Slot object functions */
  125. auto alGenAuxiliaryEffectSlots = LPALGENAUXILIARYEFFECTSLOTS{};
  126. auto alDeleteAuxiliaryEffectSlots = LPALDELETEAUXILIARYEFFECTSLOTS{};
  127. auto alIsAuxiliaryEffectSlot = LPALISAUXILIARYEFFECTSLOT{};
  128. auto alAuxiliaryEffectSloti = LPALAUXILIARYEFFECTSLOTI{};
  129. auto alAuxiliaryEffectSlotiv = LPALAUXILIARYEFFECTSLOTIV{};
  130. auto alAuxiliaryEffectSlotf = LPALAUXILIARYEFFECTSLOTF{};
  131. auto alAuxiliaryEffectSlotfv = LPALAUXILIARYEFFECTSLOTFV{};
  132. auto alGetAuxiliaryEffectSloti = LPALGETAUXILIARYEFFECTSLOTI{};
  133. auto alGetAuxiliaryEffectSlotiv = LPALGETAUXILIARYEFFECTSLOTIV{};
  134. auto alGetAuxiliaryEffectSlotf = LPALGETAUXILIARYEFFECTSLOTF{};
  135. auto alGetAuxiliaryEffectSlotfv = LPALGETAUXILIARYEFFECTSLOTFV{};
  136. auto MuteFilterID = ALuint{};
  137. auto LowFrequencyEffectID = ALuint{};
  138. auto LfeSlotID = ALuint{};
  139. using namespace std::string_view_literals;
  140. [[noreturn]]
  141. void do_assert(const char *message, int linenum, const char *filename, const char *funcname)
  142. {
  143. auto errstr = fmt::format("{}:{}: {}: {}", filename, linenum, funcname, message);
  144. throw std::runtime_error{errstr};
  145. }
  146. #define MyAssert(cond) do { \
  147. if(!(cond)) UNLIKELY \
  148. do_assert("Assertion '" #cond "' failed", __LINE__, __FILE__, \
  149. std::data(__func__)); \
  150. } while(0)
  151. enum class Quality : std::uint8_t {
  152. s8, s16, f32, s24
  153. };
  154. enum class Mode : bool {
  155. Channels, Objects
  156. };
  157. auto GetQualityName(Quality quality) noexcept -> std::string_view
  158. {
  159. switch(quality)
  160. {
  161. case Quality::s8: return "8-bit int"sv;
  162. case Quality::s16: return "16-bit int"sv;
  163. case Quality::f32: return "32-bit float"sv;
  164. case Quality::s24: return "24-bit int"sv;
  165. }
  166. return "<unknown>"sv;
  167. }
  168. auto GetModeName(Mode mode) noexcept -> std::string_view
  169. {
  170. switch(mode)
  171. {
  172. case Mode::Channels: return "channels"sv;
  173. case Mode::Objects: return "objects"sv;
  174. }
  175. return "<unknown>"sv;
  176. }
  177. auto BytesFromQuality(Quality quality) noexcept -> size_t
  178. {
  179. switch(quality)
  180. {
  181. case Quality::s8: return 1;
  182. case Quality::s16: return 2;
  183. case Quality::f32: return 4;
  184. case Quality::s24: return 3;
  185. }
  186. return 4;
  187. }
  188. auto BufferBytesFromQuality(Quality quality) noexcept -> size_t
  189. {
  190. switch(quality)
  191. {
  192. case Quality::s8: return 1;
  193. case Quality::s16: return 2;
  194. case Quality::f32: return 4;
  195. /* 24-bit samples are converted to 32-bit for OpenAL. */
  196. case Quality::s24: return 4;
  197. }
  198. return 4;
  199. }
  200. /* Helper class for reading little-endian samples on big-endian targets, or
  201. * convert 24-bit samples.
  202. */
  203. template<Quality Q>
  204. struct SampleReader;
  205. template<>
  206. struct SampleReader<Quality::s8> {
  207. using src_t = int8_t;
  208. using dst_t = int8_t;
  209. [[nodiscard]] static
  210. auto read(const src_t &in) noexcept -> dst_t { return in; }
  211. };
  212. template<>
  213. struct SampleReader<Quality::s16> {
  214. using src_t = int16_t;
  215. using dst_t = int16_t;
  216. [[nodiscard]] static
  217. auto read(const src_t &in) noexcept -> dst_t
  218. {
  219. if constexpr(al::endian::native == al::endian::little)
  220. return in;
  221. else
  222. return al::byteswap(in);
  223. }
  224. };
  225. template<>
  226. struct SampleReader<Quality::f32> {
  227. /* 32-bit float samples are read as 32-bit integer on big-endian systems,
  228. * so that they can be byteswapped before being reinterpreted as float.
  229. */
  230. using src_t = std::conditional_t<al::endian::native==al::endian::little, float,uint32_t>;
  231. using dst_t = float;
  232. [[nodiscard]] static
  233. auto read(const src_t &in) noexcept -> dst_t
  234. {
  235. if constexpr(al::endian::native == al::endian::little)
  236. return in;
  237. else
  238. return al::bit_cast<dst_t>(al::byteswap(static_cast<uint32_t>(in)));
  239. }
  240. };
  241. template<>
  242. struct SampleReader<Quality::s24> {
  243. /* 24-bit samples are converted to 32-bit integer. */
  244. using src_t = std::array<uint8_t,3>;
  245. using dst_t = int32_t;
  246. [[nodiscard]] static
  247. auto read(const src_t &in) noexcept -> dst_t
  248. {
  249. return static_cast<int32_t>((uint32_t{in[0]}<<8) | (uint32_t{in[1]}<<16)
  250. | (uint32_t{in[2]}<<24));
  251. }
  252. };
  253. /* Each track with position data consists of a set of 3 samples per 16 audio
  254. * channels, resulting in a full set of positions being specified over 48
  255. * sample frames.
  256. */
  257. constexpr auto FramesPerPos = 48_uz;
  258. struct Channel {
  259. ALuint mSource{};
  260. std::array<ALuint,2> mBuffers{};
  261. float mAzimuth{};
  262. float mElevation{};
  263. bool mIsLfe{};
  264. Channel() = default;
  265. Channel(const Channel&) = delete;
  266. Channel(Channel&& rhs)
  267. : mSource{rhs.mSource}, mBuffers{rhs.mBuffers}, mAzimuth{rhs.mAzimuth}
  268. , mElevation{rhs.mElevation}, mIsLfe{rhs.mIsLfe}
  269. {
  270. rhs.mSource = 0;
  271. rhs.mBuffers.fill(0);
  272. }
  273. ~Channel()
  274. {
  275. if(mSource) alDeleteSources(1, &mSource);
  276. if(mBuffers[0]) alDeleteBuffers(ALsizei(mBuffers.size()), mBuffers.data());
  277. }
  278. auto operator=(const Channel&) -> Channel& = delete;
  279. auto operator=(Channel&& rhs) -> Channel&
  280. {
  281. std::swap(mSource, rhs.mSource);
  282. std::swap(mBuffers, rhs.mBuffers);
  283. std::swap(mAzimuth, rhs.mAzimuth);
  284. std::swap(mElevation, rhs.mElevation);
  285. std::swap(mIsLfe, rhs.mIsLfe);
  286. return *this;
  287. }
  288. };
  289. struct LafStream {
  290. std::filebuf mInFile;
  291. Quality mQuality{};
  292. Mode mMode{};
  293. uint32_t mNumTracks{};
  294. uint32_t mSampleRate{};
  295. ALenum mALFormat{};
  296. uint64_t mSampleCount{};
  297. uint64_t mCurrentSample{};
  298. std::array<uint8_t,32> mEnabledTracks{};
  299. uint32_t mNumEnabled{};
  300. std::vector<char> mSampleChunk;
  301. al::span<char> mSampleLine;
  302. std::vector<Channel> mChannels;
  303. std::vector<std::vector<float>> mPosTracks;
  304. LafStream() = default;
  305. LafStream(const LafStream&) = delete;
  306. ~LafStream() = default;
  307. auto operator=(const LafStream&) -> LafStream& = delete;
  308. [[nodiscard]]
  309. auto readChunk() -> uint32_t;
  310. void convertSamples(const al::span<char> samples) const;
  311. void convertPositions(const al::span<float> dst, const al::span<const char> src) const;
  312. template<Quality Q>
  313. void copySamples(char *dst, const char *src, size_t idx, size_t count) const;
  314. [[nodiscard]]
  315. auto prepareTrack(size_t trackidx, size_t count) -> al::span<char>;
  316. [[nodiscard]]
  317. auto isAtEnd() const noexcept -> bool { return mCurrentSample >= mSampleCount; }
  318. };
  319. auto LafStream::readChunk() -> uint32_t
  320. {
  321. mEnabledTracks.fill(0);
  322. mInFile.sgetn(reinterpret_cast<char*>(mEnabledTracks.data()), (mNumTracks+7_z)>>3);
  323. mNumEnabled = std::accumulate(mEnabledTracks.cbegin(), mEnabledTracks.cend(), 0u,
  324. [](const unsigned int val, const uint8_t in)
  325. { return val + unsigned(al::popcount(unsigned(in))); });
  326. /* Make sure enable bits aren't set for non-existent tracks. */
  327. if(mEnabledTracks[((mNumTracks+7_uz)>>3) - 1] >= (1u<<(mNumTracks&7)))
  328. throw std::runtime_error{"Invalid channel enable bits"};
  329. /* Each chunk is exactly one second long, with samples interleaved for each
  330. * enabled track. The last chunk may be shorter if there isn't enough time
  331. * remaining for a full second.
  332. */
  333. const auto numsamples = std::min(uint64_t{mSampleRate}, mSampleCount-mCurrentSample);
  334. const auto toread = std::streamsize(numsamples * BytesFromQuality(mQuality) * mNumEnabled);
  335. if(mInFile.sgetn(mSampleChunk.data(), toread) != toread)
  336. throw std::runtime_error{"Failed to read sample chunk"};
  337. std::fill(mSampleChunk.begin()+toread, mSampleChunk.end(), char{});
  338. mCurrentSample += numsamples;
  339. return static_cast<uint32_t>(numsamples);
  340. }
  341. void LafStream::convertSamples(const al::span<char> samples) const
  342. {
  343. /* OpenAL uses unsigned 8-bit samples (0...255), so signed 8-bit samples
  344. * (-128...+127) need conversion. The other formats are fine.
  345. */
  346. if(mQuality == Quality::s8)
  347. std::transform(samples.begin(), samples.end(), samples.begin(),
  348. [](const char sample) noexcept { return char(sample^0x80); });
  349. }
  350. void LafStream::convertPositions(const al::span<float> dst, const al::span<const char> src) const
  351. {
  352. switch(mQuality)
  353. {
  354. case Quality::s8:
  355. std::transform(src.begin(), src.end(), dst.begin(),
  356. [](const int8_t in) { return float(in) / 127.0f; });
  357. break;
  358. case Quality::s16:
  359. {
  360. auto i16src = al::span{reinterpret_cast<const int16_t*>(src.data()),
  361. src.size()/sizeof(int16_t)};
  362. std::transform(i16src.begin(), i16src.end(), dst.begin(),
  363. [](const int16_t in) { return float(in) / 32767.0f; });
  364. }
  365. break;
  366. case Quality::f32:
  367. {
  368. auto f32src = al::span{reinterpret_cast<const float*>(src.data()),
  369. src.size()/sizeof(float)};
  370. std::copy(f32src.begin(), f32src.end(), dst.begin());
  371. }
  372. break;
  373. case Quality::s24:
  374. {
  375. /* 24-bit samples are converted to 32-bit in copySamples. */
  376. auto i32src = al::span{reinterpret_cast<const int32_t*>(src.data()),
  377. src.size()/sizeof(int32_t)};
  378. std::transform(i32src.begin(), i32src.end(), dst.begin(),
  379. [](const int32_t in) { return float(in>>8) / 8388607.0f; });
  380. }
  381. break;
  382. }
  383. }
  384. template<Quality Q>
  385. void LafStream::copySamples(char *dst, const char *src, const size_t idx, const size_t count) const
  386. {
  387. using reader_t = SampleReader<Q>;
  388. using src_t = typename reader_t::src_t;
  389. using dst_t = typename reader_t::dst_t;
  390. const auto step = size_t{mNumEnabled};
  391. assert(idx < step);
  392. auto input = al::span{reinterpret_cast<const src_t*>(src), count*step};
  393. auto output = al::span{reinterpret_cast<dst_t*>(dst), count};
  394. auto inptr = input.begin();
  395. std::generate_n(output.begin(), output.size(), [&inptr,idx,step]
  396. {
  397. auto ret = reader_t::read(inptr[idx]);
  398. inptr += ptrdiff_t(step);
  399. return ret;
  400. });
  401. }
  402. auto LafStream::prepareTrack(const size_t trackidx, const size_t count) -> al::span<char>
  403. {
  404. const auto todo = std::min(size_t{mSampleRate}, count);
  405. if((mEnabledTracks[trackidx>>3] & (1_uz<<(trackidx&7))))
  406. {
  407. /* If the track is enabled, get the real index (skipping disabled
  408. * tracks), and deinterlace it into the mono line.
  409. */
  410. const auto idx = [this,trackidx]() -> unsigned int
  411. {
  412. const auto bits = al::span{mEnabledTracks}.first(trackidx>>3);
  413. const auto res = std::accumulate(bits.begin(), bits.end(), 0u,
  414. [](const unsigned int val, const uint8_t in)
  415. { return val + unsigned(al::popcount(unsigned(in))); });
  416. return unsigned(al::popcount(mEnabledTracks[trackidx>>3] & ((1u<<(trackidx&7))-1)))
  417. + res;
  418. }();
  419. switch(mQuality)
  420. {
  421. case Quality::s8:
  422. copySamples<Quality::s8>(mSampleLine.data(), mSampleChunk.data(), idx, todo);
  423. break;
  424. case Quality::s16:
  425. copySamples<Quality::s16>(mSampleLine.data(), mSampleChunk.data(), idx, todo);
  426. break;
  427. case Quality::f32:
  428. copySamples<Quality::f32>(mSampleLine.data(), mSampleChunk.data(), idx, todo);
  429. break;
  430. case Quality::s24:
  431. copySamples<Quality::s24>(mSampleLine.data(), mSampleChunk.data(), idx, todo);
  432. break;
  433. }
  434. }
  435. else
  436. {
  437. /* If the track is disabled, provide silence. */
  438. std::fill_n(mSampleLine.begin(), mSampleLine.size(), char{});
  439. }
  440. return mSampleLine.first(todo * BufferBytesFromQuality(mQuality));
  441. }
  442. auto LoadLAF(const fs::path &fname) -> std::unique_ptr<LafStream>
  443. {
  444. auto laf = std::make_unique<LafStream>();
  445. if(!laf->mInFile.open(fname, std::ios_base::binary | std::ios_base::in))
  446. throw std::runtime_error{"Could not open file"};
  447. auto marker = std::array<char,9>{};
  448. if(laf->mInFile.sgetn(marker.data(), marker.size()) != marker.size())
  449. throw std::runtime_error{"Failed to read file marker"};
  450. if(std::string_view{marker.data(), marker.size()} != "LIMITLESS"sv)
  451. throw std::runtime_error{"Not an LAF file"};
  452. auto header = std::array<char,10>{};
  453. if(laf->mInFile.sgetn(header.data(), header.size()) != header.size())
  454. throw std::runtime_error{"Failed to read header"};
  455. while(std::string_view{header.data(), 4} != "HEAD"sv)
  456. {
  457. auto headview = std::string_view{header.data(), header.size()};
  458. auto hiter = header.begin();
  459. if(const auto hpos = std::min(headview.find("HEAD"sv), headview.size());
  460. hpos < headview.size())
  461. {
  462. /* Found the HEAD marker. Copy what was read of the header to the
  463. * front, fill in the rest of the header, and continue loading.
  464. */
  465. hiter = std::copy(header.begin()+hpos, header.end(), hiter);
  466. }
  467. else if(al::ends_with(headview, "HEA"sv))
  468. {
  469. /* Found what might be the HEAD marker at the end. Copy it to the
  470. * front, refill the header, and check again.
  471. */
  472. hiter = std::copy_n(header.end()-3, 3, hiter);
  473. }
  474. else if(al::ends_with(headview, "HE"sv))
  475. hiter = std::copy_n(header.end()-2, 2, hiter);
  476. else if(headview.back() == 'H')
  477. hiter = std::copy_n(header.end()-1, 1, hiter);
  478. const auto toread = std::distance(hiter, header.end());
  479. if(laf->mInFile.sgetn(al::to_address(hiter), toread) != toread)
  480. throw std::runtime_error{"Failed to read header"};
  481. }
  482. laf->mQuality = [stype=int{header[4]}] {
  483. if(stype == 0) return Quality::s8;
  484. if(stype == 1) return Quality::s16;
  485. if(stype == 2) return Quality::f32;
  486. if(stype == 3) return Quality::s24;
  487. throw std::runtime_error{fmt::format("Invalid quality type: {}", stype)};
  488. }();
  489. laf->mMode = [mode=int{header[5]}] {
  490. if(mode == 0) return Mode::Channels;
  491. if(mode == 1) return Mode::Objects;
  492. throw std::runtime_error{fmt::format("Invalid mode: {}", mode)};
  493. }();
  494. laf->mNumTracks = [input=al::span{header}.subspan<6,4>()] {
  495. return uint32_t{uint8_t(input[0])} | (uint32_t{uint8_t(input[1])}<<8u)
  496. | (uint32_t{uint8_t(input[2])}<<16u) | (uint32_t{uint8_t(input[3])}<<24u);
  497. }();
  498. fmt::println("Filename: {}", fname.string());
  499. fmt::println(" quality: {}", GetQualityName(laf->mQuality));
  500. fmt::println(" mode: {}", GetModeName(laf->mMode));
  501. fmt::println(" track count: {}", laf->mNumTracks);
  502. if(laf->mNumTracks == 0)
  503. throw std::runtime_error{"No tracks"};
  504. if(laf->mNumTracks > 256)
  505. throw std::runtime_error{fmt::format("Too many tracks: {}", laf->mNumTracks)};
  506. auto chandata = std::vector<char>(laf->mNumTracks*9_uz);
  507. auto headersize = std::streamsize(chandata.size());
  508. if(laf->mInFile.sgetn(chandata.data(), headersize) != headersize)
  509. throw std::runtime_error{"Failed to read channel header data"};
  510. if(laf->mMode == Mode::Channels)
  511. laf->mChannels.reserve(laf->mNumTracks);
  512. else
  513. {
  514. if(laf->mNumTracks < 2)
  515. throw std::runtime_error{"Not enough tracks"};
  516. auto numchans = uint32_t{laf->mNumTracks - 1};
  517. auto numpostracks = uint32_t{1};
  518. while(numpostracks*16 < numchans)
  519. {
  520. --numchans;
  521. ++numpostracks;
  522. }
  523. laf->mChannels.reserve(numchans);
  524. laf->mPosTracks.reserve(numpostracks);
  525. }
  526. for(uint32_t i{0};i < laf->mNumTracks;++i)
  527. {
  528. static constexpr auto read_float = [](al::span<char,4> input)
  529. {
  530. const auto value = uint32_t{uint8_t(input[0])} | (uint32_t{uint8_t(input[1])}<<8u)
  531. | (uint32_t{uint8_t(input[2])}<<16u) | (uint32_t{uint8_t(input[3])}<<24u);
  532. return al::bit_cast<float>(value);
  533. };
  534. auto chan = al::span{chandata}.subspan(i*9_uz, 9);
  535. auto x_axis = read_float(chan.first<4>());
  536. auto y_axis = read_float(chan.subspan<4,4>());
  537. auto lfe_flag = int{chan[8]};
  538. fmt::println("Track {}: E={:f}, A={:f} (LFE: {})", i, x_axis, y_axis, lfe_flag);
  539. if(x_axis != x_axis && y_axis == 0.0)
  540. {
  541. MyAssert(laf->mMode == Mode::Objects);
  542. MyAssert(i != 0);
  543. laf->mPosTracks.emplace_back();
  544. }
  545. else
  546. {
  547. MyAssert(laf->mPosTracks.empty());
  548. MyAssert(std::isfinite(x_axis) && std::isfinite(y_axis));
  549. auto &channel = laf->mChannels.emplace_back();
  550. channel.mAzimuth = y_axis;
  551. channel.mElevation = x_axis;
  552. channel.mIsLfe = lfe_flag != 0;
  553. }
  554. }
  555. fmt::println("Channels: {}", laf->mChannels.size());
  556. /* For "objects" mode, ensure there's enough tracks with position data to
  557. * handle the audio channels.
  558. */
  559. if(laf->mMode == Mode::Objects)
  560. MyAssert(((laf->mChannels.size()-1)>>4) == laf->mPosTracks.size()-1);
  561. auto footer = std::array<char,12>{};
  562. if(laf->mInFile.sgetn(footer.data(), footer.size()) != footer.size())
  563. throw std::runtime_error{"Failed to read sample header data"};
  564. laf->mSampleRate = [input=al::span{footer}.first<4>()] {
  565. return uint32_t{uint8_t(input[0])} | (uint32_t{uint8_t(input[1])}<<8u)
  566. | (uint32_t{uint8_t(input[2])}<<16u) | (uint32_t{uint8_t(input[3])}<<24u);
  567. }();
  568. laf->mSampleCount = [input=al::span{footer}.last<8>()] {
  569. return uint64_t{uint8_t(input[0])} | (uint64_t{uint8_t(input[1])}<<8)
  570. | (uint64_t{uint8_t(input[2])}<<16u) | (uint64_t{uint8_t(input[3])}<<24u)
  571. | (uint64_t{uint8_t(input[4])}<<32u) | (uint64_t{uint8_t(input[5])}<<40u)
  572. | (uint64_t{uint8_t(input[6])}<<48u) | (uint64_t{uint8_t(input[7])}<<56u);
  573. }();
  574. fmt::println("Sample rate: {}", laf->mSampleRate);
  575. fmt::println("Length: {} samples ({:.2f} sec)", laf->mSampleCount,
  576. static_cast<double>(laf->mSampleCount)/static_cast<double>(laf->mSampleRate));
  577. /* Position vectors get split across the PCM chunks if the sample rate
  578. * isn't a multiple of 48. Each PCM chunk is exactly one second (the sample
  579. * rate in sample frames). Each track with position data consists of a set
  580. * of 3 samples for 16 audio channels, resuling in 48 sample frames for a
  581. * full set of positions. Extra logic will be needed to manage the position
  582. * frame offset separate from each chunk.
  583. */
  584. MyAssert(laf->mMode == Mode::Channels || (laf->mSampleRate%FramesPerPos) == 0);
  585. for(size_t i{0};i < laf->mPosTracks.size();++i)
  586. laf->mPosTracks[i].resize(laf->mSampleRate*2_uz, 0.0f);
  587. laf->mSampleChunk.resize(laf->mSampleRate*BytesFromQuality(laf->mQuality)*laf->mNumTracks
  588. + laf->mSampleRate*BufferBytesFromQuality(laf->mQuality));
  589. laf->mSampleLine = al::span{laf->mSampleChunk}.last(laf->mSampleRate
  590. * BufferBytesFromQuality(laf->mQuality));
  591. return laf;
  592. }
  593. void PlayLAF(std::string_view fname)
  594. try {
  595. auto laf = LoadLAF(fs::u8path(fname));
  596. switch(laf->mQuality)
  597. {
  598. case Quality::s8:
  599. laf->mALFormat = AL_FORMAT_MONO8;
  600. break;
  601. case Quality::s16:
  602. laf->mALFormat = AL_FORMAT_MONO16;
  603. break;
  604. case Quality::f32:
  605. if(alIsExtensionPresent("AL_EXT_FLOAT32"))
  606. laf->mALFormat = AL_FORMAT_MONO_FLOAT32;
  607. break;
  608. case Quality::s24:
  609. laf->mALFormat = alGetEnumValue("AL_FORMAT_MONO32");
  610. if(!laf->mALFormat || laf->mALFormat == -1)
  611. laf->mALFormat = alGetEnumValue("AL_FORMAT_MONO_I32");
  612. break;
  613. }
  614. if(!laf->mALFormat || laf->mALFormat == -1)
  615. throw std::runtime_error{fmt::format("No supported format for {} samples",
  616. GetQualityName(laf->mQuality))};
  617. static constexpr auto alloc_channel = [](Channel &channel)
  618. {
  619. alGenSources(1, &channel.mSource);
  620. alGenBuffers(ALsizei(channel.mBuffers.size()), channel.mBuffers.data());
  621. /* Disable distance attenuation, and make sure the source stays locked
  622. * relative to the listener.
  623. */
  624. alSourcef(channel.mSource, AL_ROLLOFF_FACTOR, 0.0f);
  625. alSourcei(channel.mSource, AL_SOURCE_RELATIVE, AL_TRUE);
  626. /* FIXME: Is the Y rotation/azimuth clockwise or counter-clockwise?
  627. * Does +azimuth move a front sound right or left?
  628. */
  629. const auto x = std::sin(channel.mAzimuth) * std::cos(channel.mElevation);
  630. const auto y = std::sin(channel.mElevation);
  631. const auto z = -std::cos(channel.mAzimuth) * std::cos(channel.mElevation);
  632. alSource3f(channel.mSource, AL_POSITION, x, y, z);
  633. if(channel.mIsLfe)
  634. {
  635. if(LfeSlotID)
  636. {
  637. /* For LFE, silence the direct/dry path and connect the LFE aux
  638. * slot on send 0.
  639. */
  640. alSourcei(channel.mSource, AL_DIRECT_FILTER, ALint(MuteFilterID));
  641. alSource3i(channel.mSource, AL_AUXILIARY_SEND_FILTER, ALint(LfeSlotID), 0,
  642. AL_FILTER_NULL);
  643. }
  644. else
  645. {
  646. /* If AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT isn't available,
  647. * silence LFE channels since they may not be appropriate to
  648. * play normally.
  649. */
  650. alSourcef(channel.mSource, AL_GAIN, 0.0f);
  651. }
  652. }
  653. if(auto err=alGetError())
  654. throw std::runtime_error{fmt::format("OpenAL error: {}", alGetString(err))};
  655. };
  656. std::for_each(laf->mChannels.begin(), laf->mChannels.end(), alloc_channel);
  657. while(!laf->isAtEnd())
  658. {
  659. auto state = ALenum{};
  660. auto offset = ALint{};
  661. auto processed = ALint{};
  662. /* All sources are played in sync, so they'll all be at the same offset
  663. * with the same state and number of processed buffers. Query the back
  664. * source just in case the previous update ran really late and missed
  665. * updating only some sources on time (in which case, the latter ones
  666. * will underrun, which this will detect and restart them all as
  667. * needed).
  668. */
  669. alGetSourcei(laf->mChannels.back().mSource, AL_BUFFERS_PROCESSED, &processed);
  670. alGetSourcei(laf->mChannels.back().mSource, AL_SAMPLE_OFFSET, &offset);
  671. alGetSourcei(laf->mChannels.back().mSource, AL_SOURCE_STATE, &state);
  672. if(state == AL_PLAYING || state == AL_PAUSED)
  673. {
  674. if(!laf->mPosTracks.empty())
  675. {
  676. alcSuspendContext(alcGetCurrentContext());
  677. for(size_t i{0};i < laf->mChannels.size();++i)
  678. {
  679. const auto trackidx = i>>4;
  680. const auto posoffset = unsigned(offset)/FramesPerPos*16_uz + (i&15);
  681. const auto x = laf->mPosTracks[trackidx][posoffset*3 + 0];
  682. const auto y = laf->mPosTracks[trackidx][posoffset*3 + 1];
  683. const auto z = laf->mPosTracks[trackidx][posoffset*3 + 2];
  684. /* Contrary to the docs, the position is left-handed and
  685. * needs to be converted to right-handed.
  686. */
  687. alSource3f(laf->mChannels[i].mSource, AL_POSITION, x, y, -z);
  688. }
  689. alcProcessContext(alcGetCurrentContext());
  690. }
  691. if(processed > 0)
  692. {
  693. const auto numsamples = laf->readChunk();
  694. for(size_t i{0};i < laf->mChannels.size();++i)
  695. {
  696. const auto samples = laf->prepareTrack(i, numsamples);
  697. laf->convertSamples(samples);
  698. auto bufid = ALuint{};
  699. alSourceUnqueueBuffers(laf->mChannels[i].mSource, 1, &bufid);
  700. alBufferData(bufid, laf->mALFormat, samples.data(), ALsizei(samples.size()),
  701. ALsizei(laf->mSampleRate));
  702. alSourceQueueBuffers(laf->mChannels[i].mSource, 1, &bufid);
  703. }
  704. for(size_t i{0};i < laf->mPosTracks.size();++i)
  705. {
  706. std::copy(laf->mPosTracks[i].begin() + ptrdiff_t(laf->mSampleRate),
  707. laf->mPosTracks[i].end(), laf->mPosTracks[i].begin());
  708. const auto positions = laf->prepareTrack(laf->mChannels.size()+i, numsamples);
  709. laf->convertPositions(al::span{laf->mPosTracks[i]}.last(laf->mSampleRate),
  710. positions);
  711. }
  712. }
  713. else
  714. std::this_thread::sleep_for(std::chrono::milliseconds{10});
  715. }
  716. else if(state == AL_STOPPED)
  717. {
  718. auto sources = std::array<ALuint,256>{};
  719. for(size_t i{0};i < laf->mChannels.size();++i)
  720. sources[i] = laf->mChannels[i].mSource;
  721. alSourcePlayv(ALsizei(laf->mChannels.size()), sources.data());
  722. }
  723. else if(state == AL_INITIAL)
  724. {
  725. auto sources = std::array<ALuint,256>{};
  726. auto numsamples = laf->readChunk();
  727. for(size_t i{0};i < laf->mChannels.size();++i)
  728. {
  729. const auto samples = laf->prepareTrack(i, numsamples);
  730. laf->convertSamples(samples);
  731. alBufferData(laf->mChannels[i].mBuffers[0], laf->mALFormat, samples.data(),
  732. ALsizei(samples.size()), ALsizei(laf->mSampleRate));
  733. }
  734. for(size_t i{0};i < laf->mPosTracks.size();++i)
  735. {
  736. const auto positions = laf->prepareTrack(laf->mChannels.size()+i, numsamples);
  737. laf->convertPositions(al::span{laf->mPosTracks[i]}.first(laf->mSampleRate),
  738. positions);
  739. }
  740. numsamples = laf->readChunk();
  741. for(size_t i{0};i < laf->mChannels.size();++i)
  742. {
  743. const auto samples = laf->prepareTrack(i, numsamples);
  744. laf->convertSamples(samples);
  745. alBufferData(laf->mChannels[i].mBuffers[1], laf->mALFormat, samples.data(),
  746. ALsizei(samples.size()), ALsizei(laf->mSampleRate));
  747. alSourceQueueBuffers(laf->mChannels[i].mSource,
  748. ALsizei(laf->mChannels[i].mBuffers.size()), laf->mChannels[i].mBuffers.data());
  749. sources[i] = laf->mChannels[i].mSource;
  750. }
  751. for(size_t i{0};i < laf->mPosTracks.size();++i)
  752. {
  753. const auto positions = laf->prepareTrack(laf->mChannels.size()+i, numsamples);
  754. laf->convertPositions(al::span{laf->mPosTracks[i]}.last(laf->mSampleRate),
  755. positions);
  756. }
  757. if(!laf->mPosTracks.empty())
  758. {
  759. for(size_t i{0};i < laf->mChannels.size();++i)
  760. {
  761. const auto trackidx = i>>4;
  762. const auto x = laf->mPosTracks[trackidx][(i&15)*3 + 0];
  763. const auto y = laf->mPosTracks[trackidx][(i&15)*3 + 1];
  764. const auto z = laf->mPosTracks[trackidx][(i&15)*3 + 2];
  765. alSource3f(laf->mChannels[i].mSource, AL_POSITION, x, y, -z);
  766. }
  767. }
  768. alSourcePlayv(ALsizei(laf->mChannels.size()), sources.data());
  769. }
  770. else
  771. break;
  772. }
  773. auto state = ALenum{};
  774. auto offset = ALint{};
  775. alGetSourcei(laf->mChannels.back().mSource, AL_SAMPLE_OFFSET, &offset);
  776. alGetSourcei(laf->mChannels.back().mSource, AL_SOURCE_STATE, &state);
  777. while(alGetError() == AL_NO_ERROR && state == AL_PLAYING)
  778. {
  779. if(!laf->mPosTracks.empty())
  780. {
  781. alcSuspendContext(alcGetCurrentContext());
  782. for(size_t i{0};i < laf->mChannels.size();++i)
  783. {
  784. const auto trackidx = i>>4;
  785. const auto posoffset = unsigned(offset)/FramesPerPos*16_uz + (i&15);
  786. const auto x = laf->mPosTracks[trackidx][posoffset*3 + 0];
  787. const auto y = laf->mPosTracks[trackidx][posoffset*3 + 1];
  788. const auto z = laf->mPosTracks[trackidx][posoffset*3 + 2];
  789. alSource3f(laf->mChannels[i].mSource, AL_POSITION, x, y, -z);
  790. }
  791. alcProcessContext(alcGetCurrentContext());
  792. }
  793. std::this_thread::sleep_for(std::chrono::milliseconds{10});
  794. alGetSourcei(laf->mChannels.back().mSource, AL_SAMPLE_OFFSET, &offset);
  795. alGetSourcei(laf->mChannels.back().mSource, AL_SOURCE_STATE, &state);
  796. }
  797. }
  798. catch(std::exception& e) {
  799. fmt::println(stderr, "Error playing {}:\n {}", fname, e.what());
  800. }
  801. auto main(al::span<std::string_view> args) -> int
  802. {
  803. /* Print out usage if no arguments were specified */
  804. if(args.size() < 2)
  805. {
  806. fmt::println(stderr, "Usage: {} [-device <name>] <filenames...>\n", args[0]);
  807. return 1;
  808. }
  809. args = args.subspan(1);
  810. if(InitAL(args) != 0)
  811. throw std::runtime_error{"Failed to initialize OpenAL"};
  812. /* A simple RAII container for automating OpenAL shutdown. */
  813. struct AudioManager {
  814. AudioManager() = default;
  815. AudioManager(const AudioManager&) = delete;
  816. auto operator=(const AudioManager&) -> AudioManager& = delete;
  817. ~AudioManager()
  818. {
  819. if(LfeSlotID)
  820. {
  821. alDeleteAuxiliaryEffectSlots(1, &LfeSlotID);
  822. alDeleteEffects(1, &LowFrequencyEffectID);
  823. alDeleteFilters(1, &MuteFilterID);
  824. }
  825. CloseAL();
  826. }
  827. };
  828. AudioManager almgr;
  829. if(auto *device = alcGetContextsDevice(alcGetCurrentContext());
  830. alcIsExtensionPresent(device, "ALC_EXT_EFX")
  831. && alcIsExtensionPresent(device, "ALC_EXT_DEDICATED"))
  832. {
  833. #define LOAD_PROC(x) do { \
  834. x = reinterpret_cast<decltype(x)>(alGetProcAddress(#x)); \
  835. if(!x) fmt::println(stderr, "Failed to find function '{}'\n", #x##sv);\
  836. } while(0)
  837. LOAD_PROC(alGenFilters);
  838. LOAD_PROC(alDeleteFilters);
  839. LOAD_PROC(alIsFilter);
  840. LOAD_PROC(alFilterf);
  841. LOAD_PROC(alFilterfv);
  842. LOAD_PROC(alFilteri);
  843. LOAD_PROC(alFilteriv);
  844. LOAD_PROC(alGetFilterf);
  845. LOAD_PROC(alGetFilterfv);
  846. LOAD_PROC(alGetFilteri);
  847. LOAD_PROC(alGetFilteriv);
  848. LOAD_PROC(alGenEffects);
  849. LOAD_PROC(alDeleteEffects);
  850. LOAD_PROC(alIsEffect);
  851. LOAD_PROC(alEffectf);
  852. LOAD_PROC(alEffectfv);
  853. LOAD_PROC(alEffecti);
  854. LOAD_PROC(alEffectiv);
  855. LOAD_PROC(alGetEffectf);
  856. LOAD_PROC(alGetEffectfv);
  857. LOAD_PROC(alGetEffecti);
  858. LOAD_PROC(alGetEffectiv);
  859. LOAD_PROC(alGenAuxiliaryEffectSlots);
  860. LOAD_PROC(alDeleteAuxiliaryEffectSlots);
  861. LOAD_PROC(alIsAuxiliaryEffectSlot);
  862. LOAD_PROC(alAuxiliaryEffectSlotf);
  863. LOAD_PROC(alAuxiliaryEffectSlotfv);
  864. LOAD_PROC(alAuxiliaryEffectSloti);
  865. LOAD_PROC(alAuxiliaryEffectSlotiv);
  866. LOAD_PROC(alGetAuxiliaryEffectSlotf);
  867. LOAD_PROC(alGetAuxiliaryEffectSlotfv);
  868. LOAD_PROC(alGetAuxiliaryEffectSloti);
  869. LOAD_PROC(alGetAuxiliaryEffectSlotiv);
  870. #undef LOAD_PROC
  871. alGenFilters(1, &MuteFilterID);
  872. alFilteri(MuteFilterID, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
  873. alFilterf(MuteFilterID, AL_LOWPASS_GAIN, 0.0f);
  874. MyAssert(alGetError() == AL_NO_ERROR);
  875. alGenEffects(1, &LowFrequencyEffectID);
  876. alEffecti(LowFrequencyEffectID, AL_EFFECT_TYPE, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT);
  877. MyAssert(alGetError() == AL_NO_ERROR);
  878. alGenAuxiliaryEffectSlots(1, &LfeSlotID);
  879. alAuxiliaryEffectSloti(LfeSlotID, AL_EFFECTSLOT_EFFECT, ALint(LowFrequencyEffectID));
  880. MyAssert(alGetError() == AL_NO_ERROR);
  881. }
  882. std::for_each(args.begin(), args.end(), PlayLAF);
  883. return 0;
  884. }
  885. } // namespace
  886. int main(int argc, char **argv)
  887. {
  888. MyAssert(argc >= 0);
  889. auto args = std::vector<std::string_view>(static_cast<unsigned int>(argc));
  890. std::copy_n(argv, args.size(), args.begin());
  891. return main(al::span{args});
  892. }