sound_world_al.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #if CROWN_SOUND_OPENAL
  7. #include "core/containers/array.inl"
  8. #include "core/filesystem/file.h"
  9. #include "core/math/constants.h"
  10. #include "core/math/matrix4x4.inl"
  11. #include "core/strings/string_id.inl"
  12. #include "core/math/vector3.inl"
  13. #include "core/memory/temp_allocator.inl"
  14. #include "device/log.h"
  15. #include "resource/resource_manager.h"
  16. #include "resource/sound_resource.inl"
  17. #include "resource/sound_ogg.h"
  18. #include "world/sound_world.h"
  19. #include <AL/al.h>
  20. #include <AL/alc.h>
  21. #include <AL/alext.h>
  22. #define STB_VORBIS_HEADER_ONLY
  23. #define STB_VORBIS_NO_PULLDATA_API
  24. #include <stb_vorbis.c>
  25. LOG_SYSTEM(SOUND, "sound")
  26. namespace crown
  27. {
  28. #if CROWN_DEBUG
  29. static const char *al_error_to_string(ALenum error)
  30. {
  31. switch (error) {
  32. case AL_INVALID_ENUM: return "AL_INVALID_ENUM";
  33. case AL_INVALID_VALUE: return "AL_INVALID_VALUE";
  34. case AL_INVALID_OPERATION: return "AL_INVALID_OPERATION";
  35. case AL_OUT_OF_MEMORY: return "AL_OUT_OF_MEMORY";
  36. default: return "UNKNOWN_AL_ERROR";
  37. }
  38. }
  39. #define AL_CHECK(function) \
  40. function; \
  41. do \
  42. { \
  43. ALenum error; \
  44. CE_ASSERT((error = alGetError()) == AL_NO_ERROR \
  45. , "alGetError: %s" \
  46. , al_error_to_string(error) \
  47. ); \
  48. } while (0)
  49. #else
  50. #define AL_CHECK(function) function
  51. #endif // if CROWN_DEBUG
  52. struct SoundInstance
  53. {
  54. SoundInstanceId _id;
  55. const SoundResource *_resource;
  56. StringId64 _name;
  57. bool _loop;
  58. f32 _volume;
  59. StringId32 _group;
  60. f32 _range;
  61. u32 _flags;
  62. ALuint _buffer[SOUND_MAX_BUFFERS];
  63. u32 _num_buffers;
  64. ALenum _format;
  65. ALuint _source;
  66. u32 _block_samples; ///< Number of samples in each block per channel.
  67. u32 _block_size; ///< Size of each block of samples in bytes.
  68. void *_stream_data; ///< Total memory to allow streaming.
  69. u8 *_stream_decoded; ///< Current block of decoded audio samples.
  70. u8 *_stream_encoded; ///< Current block of encoded streaming data.
  71. u32 _stream_pos; ///< Size of encoded data.
  72. File *_stream; ///< Streaming data source.
  73. // Vorbis-specific.
  74. stb_vorbis_alloc *_vorbis_alloc;
  75. unsigned char *_vorbis_headers;
  76. stb_vorbis *_vorbis;
  77. void create(const SoundResource *sr, File *stream, bool loop, f32 range, u32 flags, const Vector3 &pos, StringId32 group)
  78. {
  79. _resource = sr;
  80. _loop = loop;
  81. _stream_data = NULL;
  82. _stream = stream;
  83. _vorbis = NULL;
  84. _volume = 1.0f;
  85. _group = group;
  86. _flags = flags;
  87. // Create source.
  88. AL_CHECK(alGenSources(1, &_source));
  89. CE_ASSERT(alIsSource(_source), "alGenSources: error");
  90. bool is_2d = !(flags & PlaySoundFlags::ENABLE_ATTENUATION);
  91. AL_CHECK(alSourcef(_source, AL_REFERENCE_DISTANCE, 1.0f));
  92. AL_CHECK(alSourcef(_source, AL_ROLLOFF_FACTOR, is_2d ? 0.0f : 1.0f));
  93. AL_CHECK(alSourcei(_source, AL_SOURCE_RELATIVE, is_2d ? AL_TRUE : AL_FALSE));
  94. AL_CHECK(alSourcef(_source, AL_PITCH, 1.0f));
  95. set_position(pos);
  96. _range = range;
  97. switch (sr->bit_depth) {
  98. case 8: _format = sr->channels > 1 ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8; break;
  99. case 16: _format = sr->channels > 1 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; break;
  100. case 32: _format = sr->channels > 1 ? AL_FORMAT_STEREO_FLOAT32 : AL_FORMAT_MONO_FLOAT32; break;
  101. default: CE_FATAL("Number of bits per sample not supported."); break;
  102. }
  103. // Generate buffers.
  104. _block_samples = sr->sample_rate * SOUND_BUFFER_MS / 1000;
  105. _block_size = (sr->bit_depth / 8) * _block_samples * sr->channels;
  106. _num_buffers = SOUND_MAX_BUFFERS;
  107. if (sr->stream_format == StreamFormat::NONE) {
  108. // If there's no stream, use a single buffer and
  109. // make AL handle the looping machinery for us.
  110. _num_buffers = 1;
  111. AL_CHECK(alSourcei(_source, AL_LOOPING, (loop ? AL_TRUE : AL_FALSE)));
  112. }
  113. AL_CHECK(alGenBuffers(_num_buffers, &_buffer[0]));
  114. fill_buffers();
  115. }
  116. // Fill buffers with pre-decoded samples.
  117. void fill_buffers()
  118. {
  119. const u8 *pcm_data = sound_resource::pcm_data(_resource);
  120. u32 bs = _block_size;
  121. if (_num_buffers == 1)
  122. bs = _resource->pcm_size;
  123. for (u32 i = 0, p = 0; i < _num_buffers && p != _resource->pcm_size; ++i) {
  124. const u32 num = min(bs, _resource->pcm_size - p);
  125. AL_CHECK(alBufferData(_buffer[i], _format, &pcm_data[p], num, _resource->sample_rate));
  126. AL_CHECK(alSourceQueueBuffers(_source, 1, &_buffer[i]));
  127. p += num;
  128. }
  129. }
  130. void create(ResourceManager *rm, StringId64 name, bool loop, f32 range, u32 flags, const Vector3 &pos, StringId32 group)
  131. {
  132. const SoundResource *sr = (SoundResource *)rm->get(RESOURCE_TYPE_SOUND, name);
  133. File *stream = NULL;
  134. if (sr->stream_format != StreamFormat::NONE)
  135. stream = rm->open_stream(RESOURCE_TYPE_SOUND, name);
  136. _name = name;
  137. return create(sr, stream, loop, range, flags, pos, group);
  138. }
  139. /// Decodes a block of samples of size BLOCK_MS or less, and feeds it to AL.
  140. /// Returns the number of samples that have been decoded.
  141. u32 decode_samples()
  142. {
  143. if (!_stream)
  144. return 0;
  145. if (_resource->stream_format == StreamFormat::OGG) {
  146. const OggStreamMetadata *ogg = (OggStreamMetadata *)sound_resource::stream_metadata(_resource);
  147. int used;
  148. int skip_n = 0;
  149. // Open the stream.
  150. if (_stream_data == NULL) {
  151. const u32 stream_mem_size = 0
  152. + sizeof(stb_vorbis_alloc) + alignof(stb_vorbis_alloc)
  153. + ogg->alloc_buffer_size
  154. + ogg->headers_size
  155. + ogg->max_frame_size
  156. + _block_size*2 + alignof(f32)
  157. ;
  158. _stream_data = default_allocator().allocate(stream_mem_size);
  159. _vorbis_alloc = (stb_vorbis_alloc *)memory::align_top(_stream_data, alignof(stb_vorbis_alloc));
  160. _vorbis_alloc->alloc_buffer = (char *)&_vorbis_alloc[1];
  161. _vorbis_alloc->alloc_buffer_length_in_bytes = ogg->alloc_buffer_size;
  162. _vorbis_headers = (unsigned char *)&_vorbis_alloc[1] + ogg->alloc_buffer_size;
  163. _stream_encoded = (unsigned char *)_vorbis_headers + ogg->headers_size;
  164. _stream_decoded = (u8 *)memory::align_top(_stream_encoded + ogg->max_frame_size, alignof(f32));
  165. _stream_pos = 0;
  166. _stream->read(_vorbis_headers, ogg->headers_size);
  167. skip_n = ogg->num_samples_skip;
  168. }
  169. if (_vorbis == NULL) {
  170. int error;
  171. _vorbis = stb_vorbis_open_pushdata(_vorbis_headers, ogg->headers_size, &used, &error, _vorbis_alloc);
  172. CE_ENSURE(error == VORBIS__no_error);
  173. CE_ENSURE(_vorbis != NULL);
  174. CE_ENSURE(used == ogg->headers_size);
  175. }
  176. // Decode _block_size samples or less.
  177. f32 *dec = (f32 *)_stream_decoded;
  178. u32 tot_n = 0;
  179. while (tot_n < _block_samples) {
  180. float **output;
  181. int n;
  182. int q = ogg->max_frame_size;
  183. retry:
  184. if (q > int(_stream->size() - _stream_pos))
  185. q = _stream->size() - _stream_pos;
  186. if ((int)_stream_pos < q)
  187. _stream_pos += _stream->read(&_stream_encoded[_stream_pos], q - _stream_pos);
  188. used = stb_vorbis_decode_frame_pushdata(_vorbis
  189. , _stream_encoded
  190. , _stream_pos
  191. , NULL
  192. , &output
  193. , &n
  194. );
  195. if (used == 0) {
  196. if (_stream->end_of_file()) {
  197. if (_loop) {
  198. stb_vorbis_close(_vorbis);
  199. _vorbis = NULL;
  200. _stream->seek(ogg->headers_size);
  201. _stream_pos = 0;
  202. }
  203. break;
  204. }
  205. goto retry; // Need more data.
  206. }
  207. _stream_pos = q - used;
  208. memmove(_stream_encoded, &_stream_encoded[used], _stream_pos);
  209. if (n == 0)
  210. continue; // Seek/error recovery.
  211. // Skip unwanted samples.
  212. if (skip_n >= n) {
  213. skip_n -= n;
  214. continue;
  215. } else {
  216. n -= skip_n;
  217. }
  218. for (int i = 0; i < n; ++i) {
  219. *dec++ = output[0][i + skip_n];
  220. *dec++ = output[1][i + skip_n];
  221. }
  222. skip_n = 0;
  223. tot_n += n;
  224. }
  225. return tot_n;
  226. } else {
  227. CE_FATAL("Unknown stream format");
  228. return 0;
  229. }
  230. }
  231. void update()
  232. {
  233. ALint processed;
  234. AL_CHECK(alGetSourcei(_source, AL_BUFFERS_PROCESSED, &processed));
  235. while (processed > 0) {
  236. ALuint buffer;
  237. AL_CHECK(alSourceUnqueueBuffers(_source, 1, &buffer));
  238. --processed;
  239. // Decode a block of samples and enqueue it.
  240. u32 n = decode_samples();
  241. if (n > 0) {
  242. const u32 size = n * _resource->channels * _resource->bit_depth / 8;
  243. AL_CHECK(alBufferData(buffer, _format, _stream_decoded, size, _resource->sample_rate));
  244. AL_CHECK(alSourceQueueBuffers(_source, 1, &buffer));
  245. }
  246. }
  247. ALint state;
  248. AL_CHECK(alGetSourcei(_source, AL_SOURCE_STATE, &state));
  249. if (state != AL_PLAYING && state != AL_PAUSED) {
  250. // Either the source underrun or no buffers were enqueued.
  251. ALint queued;
  252. AL_CHECK(alGetSourcei(_source, AL_BUFFERS_QUEUED, &queued));
  253. if (queued == 0)
  254. return; // Finished.
  255. // Underrun, restart playback.
  256. AL_CHECK(alSourcePlay(_source));
  257. }
  258. }
  259. void destroy(ResourceManager *rm)
  260. {
  261. stop();
  262. AL_CHECK(alSourcei(_source, AL_BUFFER, 0));
  263. AL_CHECK(alDeleteBuffers(_num_buffers, &_buffer[0]));
  264. AL_CHECK(alDeleteSources(1, &_source));
  265. // Deallocate streaming memory.
  266. stb_vorbis_close(_vorbis);
  267. default_allocator().deallocate(_stream_data);
  268. if (_stream != NULL)
  269. rm->close_stream(_stream);
  270. }
  271. void reload(ResourceManager *rm)
  272. {
  273. destroy(rm);
  274. create(rm, _name, _loop, _range, _flags, position(), _group);
  275. }
  276. void pause()
  277. {
  278. AL_CHECK(alSourcePause(_source));
  279. }
  280. void resume()
  281. {
  282. AL_CHECK(alSourcePlay(_source));
  283. }
  284. void stop()
  285. {
  286. AL_CHECK(alSourceStop(_source));
  287. AL_CHECK(alSourceRewind(_source)); // Workaround
  288. ALint processed;
  289. AL_CHECK(alGetSourcei(_source, AL_BUFFERS_PROCESSED, &processed));
  290. if (processed > 0) {
  291. ALuint removed;
  292. AL_CHECK(alSourceUnqueueBuffers(_source, 1, &removed));
  293. }
  294. }
  295. bool is_playing()
  296. {
  297. ALint state;
  298. AL_CHECK(alGetSourcei(_source, AL_SOURCE_STATE, &state));
  299. return state == AL_PLAYING;
  300. }
  301. bool finished()
  302. {
  303. ALint state;
  304. AL_CHECK(alGetSourcei(_source, AL_SOURCE_STATE, &state));
  305. return state != AL_PLAYING && state != AL_PAUSED;
  306. }
  307. Vector3 position()
  308. {
  309. ALfloat pos[3];
  310. AL_CHECK(alGetSourcefv(_source, AL_POSITION, pos));
  311. return { pos[0], pos[1], pos[2] };
  312. }
  313. void set_position(const Vector3 &pos)
  314. {
  315. AL_CHECK(alSourcefv(_source, AL_POSITION, to_float_ptr(pos)));
  316. }
  317. };
  318. #define MAX_OBJECTS 1024
  319. #define INDEX_MASK 0xffff
  320. #define NEW_OBJECT_ID_ADD 0x10000
  321. struct SoundWorldAL : public SoundWorld
  322. {
  323. struct Index
  324. {
  325. SoundInstanceId id;
  326. u16 index;
  327. u16 next;
  328. };
  329. struct SoundGroup
  330. {
  331. StringId32 name;
  332. f32 volume;
  333. };
  334. u32 _marker;
  335. Allocator *_allocator;
  336. ResourceManager *_resource_manager;
  337. u32 _num_objects;
  338. SoundInstance _playing_sounds[MAX_OBJECTS];
  339. Index _indices[MAX_OBJECTS];
  340. u16 _freelist_enqueue;
  341. u16 _freelist_dequeue;
  342. Matrix4x4 _listener_pose;
  343. Array<SoundGroup> _groups;
  344. SoundWorldAL(Allocator &a, ResourceManager &rm)
  345. : _marker(SOUND_WORLD_MARKER)
  346. , _allocator(&a)
  347. , _resource_manager(&rm)
  348. , _groups(a)
  349. {
  350. _num_objects = 0;
  351. for (u32 i = 0; i < MAX_OBJECTS; ++i) {
  352. _indices[i].id = i;
  353. _indices[i].next = i + 1;
  354. }
  355. _freelist_dequeue = 0;
  356. _freelist_enqueue = MAX_OBJECTS - 1;
  357. set_listener_pose(MATRIX4X4_IDENTITY);
  358. }
  359. SoundWorldAL(const SoundWorldAL &) = delete;
  360. SoundWorldAL &operator=(const SoundWorldAL &) = delete;
  361. virtual ~SoundWorldAL()
  362. {
  363. for (u32 i = 0; i < _num_objects; ++i) {
  364. SoundInstance &inst = lookup(_playing_sounds[i]._id);
  365. inst.destroy(_resource_manager);
  366. }
  367. _marker = 0;
  368. }
  369. bool has(SoundInstanceId id)
  370. {
  371. const Index &in = _indices[id & INDEX_MASK];
  372. return in.id == id && in.index != UINT16_MAX;
  373. }
  374. SoundInstance &lookup(SoundInstanceId id)
  375. {
  376. return _playing_sounds[_indices[id & INDEX_MASK].index];
  377. }
  378. SoundInstanceId add()
  379. {
  380. Index &in = _indices[_freelist_dequeue];
  381. _freelist_dequeue = in.next;
  382. in.id += NEW_OBJECT_ID_ADD;
  383. in.index = _num_objects++;
  384. SoundInstance &o = _playing_sounds[in.index];
  385. o._id = in.id;
  386. return o._id;
  387. }
  388. void remove(SoundInstanceId id)
  389. {
  390. Index &in = _indices[id & INDEX_MASK];
  391. SoundInstance &o = _playing_sounds[in.index];
  392. o = _playing_sounds[--_num_objects];
  393. _indices[o._id & INDEX_MASK].index = in.index;
  394. in.index = UINT16_MAX;
  395. _indices[_freelist_enqueue].next = id & INDEX_MASK;
  396. _freelist_enqueue = id & INDEX_MASK;
  397. }
  398. SoundInstanceId play(StringId64 name
  399. , bool loop
  400. , f32 volume
  401. , f32 range
  402. , u32 flags
  403. , const Vector3 &pos
  404. , StringId32 group
  405. )
  406. {
  407. // Create sound group if it does not exist.
  408. u32 i, n;
  409. for (i = 0, n = array::size(_groups); i < n; ++i) {
  410. if (_groups[i].name == group && group != StringId32(0u))
  411. break;
  412. }
  413. if (i == n)
  414. array::push_back(_groups, { group, 1.0f });
  415. // Spawn sound instance.
  416. SoundInstanceId id = add();
  417. SoundInstance &inst = lookup(id);
  418. inst.create(_resource_manager, name, loop, range, flags, pos, group);
  419. set_sound_volumes(1, &id, &volume);
  420. if (distance_squared(translation(_listener_pose), inst.position()) > inst._range*inst._range) {
  421. AL_CHECK(alSourcef(inst._source, AL_GAIN, 0.0f));
  422. }
  423. AL_CHECK(alSourcePlay(inst._source));
  424. return id;
  425. }
  426. void stop(SoundInstanceId id)
  427. {
  428. SoundInstance &si = lookup(id);
  429. si.destroy(_resource_manager);
  430. remove(id);
  431. }
  432. bool is_playing(SoundInstanceId id)
  433. {
  434. return has(id) && lookup(id).is_playing();
  435. }
  436. void stop_all()
  437. {
  438. for (u32 i = 0; i < _num_objects; ++i) {
  439. _playing_sounds[i].stop();
  440. }
  441. }
  442. void pause_all()
  443. {
  444. for (u32 i = 0; i < _num_objects; ++i) {
  445. _playing_sounds[i].pause();
  446. }
  447. }
  448. void resume_all()
  449. {
  450. for (u32 i = 0; i < _num_objects; ++i) {
  451. _playing_sounds[i].resume();
  452. }
  453. }
  454. void set_sound_positions(u32 num, const SoundInstanceId *ids, const Vector3 *positions)
  455. {
  456. for (u32 i = 0; i < num; ++i) {
  457. lookup(ids[i]).set_position(positions[i]);
  458. }
  459. }
  460. void set_sound_ranges(u32 num, const SoundInstanceId *ids, const f32 *ranges)
  461. {
  462. for (u32 i = 0; i < num; ++i) {
  463. lookup(ids[i])._range = ranges[i];
  464. }
  465. }
  466. void set_sound_volumes(u32 num, const SoundInstanceId *ids, const f32 *volumes)
  467. {
  468. for (u32 i = 0; i < num; i++) {
  469. SoundInstance &inst = lookup(ids[i]);
  470. f32 volume = clamp(volumes[i], 0.0f, 1.0f);
  471. f32 group_volume = 1.0f;
  472. // Find group's volume.
  473. for (u32 i = 0; i < array::size(_groups); ++i) {
  474. if (_groups[i].name == inst._group) {
  475. group_volume = _groups[i].volume;
  476. break;
  477. }
  478. }
  479. AL_CHECK(alSourcef(inst._source, AL_GAIN, volume * group_volume));
  480. inst._volume = volume;
  481. }
  482. }
  483. void reload_sounds(const SoundResource *old_sr, const SoundResource *new_sr)
  484. {
  485. CE_UNUSED(new_sr);
  486. for (u32 i = 0; i < _num_objects; ++i) {
  487. if (_playing_sounds[i]._resource == old_sr) {
  488. _playing_sounds[i].reload(_resource_manager);
  489. }
  490. }
  491. }
  492. void set_listener_pose(const Matrix4x4 &pose)
  493. {
  494. const Vector3 pos = translation(pose);
  495. const Vector3 up = z(pose);
  496. const Vector3 at = y(pose);
  497. AL_CHECK(alListener3f(AL_POSITION, pos.x, pos.y, pos.z));
  498. // AL_CHECK(alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z));
  499. const ALfloat orientation[] = { at.x, at.y, at.z, up.x, up.y, up.z };
  500. AL_CHECK(alListenerfv(AL_ORIENTATION, orientation));
  501. _listener_pose = pose;
  502. }
  503. void set_group_volume(StringId32 group, f32 volume)
  504. {
  505. u32 i, n;
  506. for (i = 0, n = array::size(_groups); i < n; ++i) {
  507. if (_groups[i].name == group) {
  508. _groups[i].volume = volume;
  509. break;
  510. }
  511. }
  512. if (i == n)
  513. array::push_back(_groups, { group, volume });
  514. for (u32 i = 0; i < _num_objects; ++i) {
  515. SoundInstance &inst = _playing_sounds[i];
  516. set_sound_volumes(1, &inst._id, &inst._volume);
  517. }
  518. }
  519. void update()
  520. {
  521. TempAllocator256 alloc;
  522. Array<SoundInstanceId> to_delete(alloc);
  523. Vector3 listener_pos = translation(_listener_pose);
  524. // Update instances with new samples.
  525. for (u32 i = 0; i < _num_objects; ++i) {
  526. SoundInstance &inst = _playing_sounds[i];
  527. inst.update();
  528. if (inst.finished()) {
  529. array::push_back(to_delete, inst._id);
  530. } else {
  531. if (distance_squared(listener_pos, inst.position()) > inst._range*inst._range) {
  532. AL_CHECK(alSourcef(inst._source, AL_GAIN, 0.0f));
  533. } else {
  534. set_sound_volumes(1, &inst._id, &inst._volume);
  535. }
  536. }
  537. }
  538. // Destroy instances which finished playing.
  539. for (u32 i = 0; i < array::size(to_delete); ++i) {
  540. stop(to_delete[i]);
  541. }
  542. }
  543. };
  544. namespace sound_world_al
  545. {
  546. static ALCdevice *s_al_device;
  547. static ALCcontext *s_al_context;
  548. void init()
  549. {
  550. s_al_device = alcOpenDevice(NULL);
  551. if (s_al_device == NULL) {
  552. logw(SOUND, "Failed to open audio device");
  553. return;
  554. }
  555. s_al_context = alcCreateContext(s_al_device, NULL);
  556. CE_ASSERT(s_al_context, "alcCreateContext: error");
  557. AL_CHECK(alcMakeContextCurrent(s_al_context));
  558. #if CROWN_DEBUG && !CROWN_DEVELOPMENT
  559. logi(SOUND, "OpenAL Vendor : %s", alGetString(AL_VENDOR));
  560. logi(SOUND, "OpenAL Version : %s", alGetString(AL_VERSION));
  561. logi(SOUND, "OpenAL Renderer : %s", alGetString(AL_RENDERER));
  562. #endif
  563. AL_CHECK(alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED));
  564. AL_CHECK(alDopplerFactor(1.0f));
  565. AL_CHECK(alDopplerVelocity(343.0f));
  566. }
  567. void shutdown()
  568. {
  569. alcDestroyContext(s_al_context);
  570. alcCloseDevice(s_al_device);
  571. }
  572. SoundWorld *create(Allocator &a, ResourceManager &rm)
  573. {
  574. return s_al_device != NULL
  575. ? CE_NEW(a, SoundWorldAL)(a, rm)
  576. : NULL
  577. ;
  578. }
  579. void destroy(Allocator &a, SoundWorld &sw)
  580. {
  581. CE_DELETE(a, &sw);
  582. }
  583. } // namespace sound_world_al
  584. } // namespace crown
  585. #endif // if CROWN_SOUND_OPENAL