rhino/demo/c/dr_libs/old/dr_audio.h
Go to the documentation of this file.
1 // Audio playback, recording and mixing. Public domain. See "unlicense" statement at the end of this file.
2 // dr_audio - v0.0 (unversioned) - Release Date TBD
3 //
4 // David Reid - mackron@gmail.com
5 
6 // !!!!! THIS IS WORK IN PROGRESS !!!!!
7 
8 // USAGE
9 //
10 // dr_audio is a single-file library. To use it, do something like the following in one .c file.
11 // #define DR_AUDIO_IMPLEMENTATION
12 // #include "dr_audio.h"
13 //
14 // You can then #include this file in other parts of the program as you would with any other header file.
15 //
16 // dr_audio supports loading and decoding of WAV, FLAC and Vorbis streams via dr_wav, dr_flac and stb_vorbis respectively. To enable
17 // these all you need to do is #include "dr_audio.h" _after_ #include "dr_wav.h", #include "dr_flac.h" and #include "stb_vorbis.c" in
18 // the implementation file, like so:
19 //
20 // #define DR_WAV_IMPLEMENTATION
21 // #include "dr_wav.h"
22 //
23 // #define DR_FLAC_IMPLEMENTATION
24 // #include "dr_flac.h"
25 //
26 // #define STB_VORBIS_IMPLEMENTATION
27 // #include "stb_vorbis.c"
28 //
29 // #define DR_AUDIO_IMPLEMENTATION
30 // #include "dr_audio.h"
31 //
32 // dr_wav, dr_flac and stb_vorbis are entirely optional, and dr_audio will automatically detect the ones that are available without
33 // any additional intervention on your part.
34 //
35 //
36 // dr_audio has a layered API with different levels of flexibility vs simplicity. An example of the high level API follows:
37 //
38 // dra_device* pDevice;
39 // dra_result result = dra_device_create(NULL, dra_device_type_playback, &pDevice);
40 // if (result != DRA_RESULT_SUCCESS) {
41 // return -1;
42 // }
43 //
44 // dra_voice* pVoice;
45 // result = dra_voice_create_from_file(pDevice, "my_song.flac", &pVoice);
46 // if (result != DRA_RESULT_SUCCESS) {
47 // return -1;
48 // }
49 //
50 // dra_voice_play(pVoice, DR_FALSE);
51 //
52 // ...
53 //
54 // dra_voice_delete(pVoice);
55 // dra_device_delete(pDevice);
56 //
57 //
58 // An example of the low level API:
59 //
60 // dra_context context;
61 // dra_result result = dra_context_init(&context); // Initializes the backend (DirectSound/ALSA)
62 // if (result != DRA_RESULT_SUCCESS) {
63 // return -1;
64 // }
65 //
66 // unsigned int deviceID = 0; // Default device
67 // unsigned int channels = 2; // Stereo
68 // unsigned int sampleRate = 48000;
69 // unsigned int latencyInMilliseconds = 0; // 0 will default to DR_AUDIO_DEFAULT_LATENCY.
70 // dra_device device;
71 // result = dra_device_init_ex(&context, dra_device_type_playback, deviceID, channels, sampleRate, latencyInMilliseconds, &device);
72 // if (result != DRA_RESULT_SUCCESS) {
73 // return -1;
74 // }
75 //
76 // dra_voice* pVoice;
77 // dra_result result = dra_voice_create(pDevice, dra_format_f32, channels, sampleRate, voiceBufferSizeInBytes, pVoiceSampleData, &pVoice);
78 // if (result != DRA_RESULT_SUCCESS) {
79 // return -1;
80 // }
81 //
82 // ...
83 //
84 // // Sometime later you may need to update the data inside the voice's internal buffer... It's your job to handle
85 // // synchronization - have fun! Hint: use playback events at fixed locations to know when a region of the buffer
86 // // is available for updating.
87 // float* pVoiceData = (float*)dra_voice_get_buffer_ptr_by_sample(pVoice, sampleOffset);
88 // if (pVoiceData == NULL) {
89 // return -1;
90 // }
91 //
92 // memcpy(pVoiceData, pNewVoiceData, sizeof(float) * samplesToCopy);
93 //
94 // ...
95 //
96 // dra_voice_delete(pVoice);
97 // dra_device_uninit(pDevice);
98 // dra_context_uninit(pContext);
99 //
100 // In the above example the voice and device are configured to use the same number of channels and sample rate, however they are
101 // allowed to differ, in which case dr_audio will automatically convert the data. Note that sample rate conversion is currently
102 // very low quality.
103 //
104 // To handle streaming buffers, you can attach a callback that's fired when a voice's playback position reaches a certain point.
105 // Usually you would set this to the middle and end of the buffer, filling the previous half with new data. Use the
106 // dra_voice_add_playback_event() API for this.
107 //
108 // The playback position of a voice can be retrieved and set with dra_voice_get_playback_position() and dra_voice_set_playback_position()
109 // respctively. The playback is specified in samples. dra_voice_get_playback_position() will always return a value which is a multiple
110 // of the channel count. dra_voice_set_playback_position() will round the specified sample index to a multiple of the channel count.
111 //
112 //
113 // dr_audio has support for submixing which basically allows you to control volume (and in the future, effects) for groups of sounds
114 // which would typically be organized into categories. An abvious example would be in games where you may want to have separate volume
115 // controls for music, voices, special effects, etc. To do submixing, all you need to do is create a mixer. There is a master mixer
116 // associated with every device, and all newly created mixers are a child of the master mixer, by default:
117 //
118 // dra_mixer* pMusicMixer;
119 // dra_result result = dra_mixer_create(pDevice, &pMusicMixer);
120 // if (result != DRA_RESULT_SUCCESS) {
121 // return -1;
122 // }
123 //
124 // // At this point pMusicMixer is a child of the device's master mixer. To change the hierarchy, just do something like this:
125 // dra_mixer_attach_submixer(pSomeOtherMixer, pMusicMixer);
126 //
127 // // A voice is attached to the master mixer by default, but you can attach it to a different mixer like this:
128 // dra_mixer_attach_voice(pMusicMixer, pMyMusicVoice);
129 //
130 // // Control the volume of the mixer...
131 // dra_mixer_set_volume(pMusicMixer, 0.5f); // <-- The volume is linear, so this is half volume.
132 //
133 //
134 //
135 // dr_audio includes an abstraction for audio decoding. Built-in support is included for WAV, FLAC and Vorbis streams:
136 //
137 // dra_decoder decoder;
138 // if (dra_decoder_open_file(&decoder, filePath) != DRA_RESULT_SUCCESS) {
139 // return -1;
140 // }
141 //
142 // dr_uint64 samplesRead = dra_decoder_read_f32(&decoder, samplesToRead, pSamples);
143 // update_my_voice_data(pVoice, pSamples, samplesRead);
144 //
145 // dra_decoder_close(&decoder);
146 //
147 // Decoders can be opened/initialized from files, a block of memory, or application-defined callbacks.
148 //
149 //
150 //
151 // OPTIONS
152 // #define these options before including this file.
153 //
154 // #define DR_AUDIO_NO_DSOUND
155 // Disables the DirectSound backend. Note that this is the only backend for the Windows platform.
156 //
157 // #define DR_AUDIO_NO_ALSA
158 // Disables the ALSA backend. Note that this is the only backend for the Linux platform.
159 //
160 // #define DR_AUDIO_NO_STDIO
161 // Disables any functions that use stdio, such as dra_sound_create_from_file().
162 
163 
164 #ifndef dr_audio2_h
165 #define dr_audio2_h
166 
167 #ifdef __cplusplus
168 extern "C" {
169 #endif
170 
171 #include <stddef.h>
172 
173 #ifndef DR_SIZED_TYPES_DEFINED
174 #define DR_SIZED_TYPES_DEFINED
175 #if defined(_MSC_VER) && _MSC_VER < 1600
176 typedef signed char dr_int8;
177 typedef unsigned char dr_uint8;
178 typedef signed short dr_int16;
179 typedef unsigned short dr_uint16;
180 typedef signed int dr_int32;
181 typedef unsigned int dr_uint32;
182 typedef signed __int64 dr_int64;
183 typedef unsigned __int64 dr_uint64;
184 #else
185 #include <stdint.h>
186 typedef int8_t dr_int8;
187 typedef uint8_t dr_uint8;
188 typedef int16_t dr_int16;
189 typedef uint16_t dr_uint16;
190 typedef int32_t dr_int32;
191 typedef uint32_t dr_uint32;
192 typedef int64_t dr_int64;
193 typedef uint64_t dr_uint64;
194 #endif
197 #define DR_TRUE 1
198 #define DR_FALSE 0
199 #endif
200 
201 #ifndef DR_AUDIO_MAX_CHANNEL_COUNT
202 #define DR_AUDIO_MAX_CHANNEL_COUNT 16
203 #endif
204 
205 #ifndef DR_AUDIO_MAX_EVENT_COUNT
206 #define DR_AUDIO_MAX_EVENT_COUNT 16
207 #endif
208 
209 #define DR_AUDIO_EVENT_ID_STOP 0xFFFFFFFFFFFFFFFFULL
210 #define DR_AUDIO_EVENT_ID_PLAY 0xFFFFFFFFFFFFFFFEULL
211 
212 typedef int dra_result;
213 #define DRA_RESULT_SUCCESS 0
214 #define DRA_RESULT_UNKNOWN_ERROR -1
215 #define DRA_RESULT_INVALID_ARGS -2
216 #define DRA_RESULT_OUT_OF_MEMORY -3
217 #define DRA_RESULT_FAILED_TO_OPEN_FILE -4
218 #define DRA_RESULT_NO_BACKEND -1024
219 #define DRA_RESULT_NO_BACKEND_DEVICE -1025
220 #define DRA_RESULT_NO_DECODER -1026
221 #define DRA_FAILED(result) ((result) != 0)
222 #define DRA_SUCCEEDED(result) ((result) == 0)
223 
224 #define DRA_MIXER_FLAG_PAUSED (1 << 0)
225 
226 typedef enum
227 {
231 
232 typedef enum
233 {
240 } dra_format;
241 
242 typedef enum
243 {
247 
248 // dra_thread_event_type is used internally for thread management.
249 typedef enum
250 {
255 
256 typedef struct dra_backend dra_backend;
258 
259 typedef struct dra_context dra_context;
260 typedef struct dra_device dra_device;
261 typedef struct dra_mixer dra_mixer;
262 typedef struct dra_voice dra_voice;
263 
264 typedef void* dra_thread;
265 typedef void* dra_mutex;
266 typedef void* dra_semaphore;
267 
268 typedef void (* dra_event_proc) (dr_uint64 eventID, void* pUserData);
269 typedef void (* dra_samples_processed_proc)(dra_device* pDevice, const size_t sampleCount, const float* pSamples, void* pUserData);
270 
271 typedef struct
272 {
273  dr_uint64 id;
274  void* pUserData;
275  dr_uint64 sampleIndex;
276  dra_event_proc proc;
277  dra_voice* pVoice;
278  dr_bool32 hasBeenSignaled;
279 } dra__event;
280 
281 typedef struct
282 {
283  size_t firstEvent;
284  size_t eventCount;
285  size_t eventBufferSize;
286  dra__event* pEvents;
287  dra_mutex lock;
289 
290 struct dra_context
291 {
293 };
294 
295 struct dra_device
296 {
297  // The context that created and owns this device.
299 
300  // The backend device. This is used to connect the cross-platform front-end with the backend.
302 
303  // The main mutex for handling thread-safety.
305 
306  // The main thread. For playback devices, this is the thread that waits for fragments to finish processing an then
307  // mixes and pushes new audio data to the hardware buffer for playback.
309 
310  // The semaphore used by the main thread to determine whether or not an event is waiting to be processed.
312 
313  // The event type of the most recent event.
315 
316  // TODO: Make these booleans flags.
317 
318  // Whether or not the device owns the context. This basically just means whether or not the device was created with a null
319  // context and needs to delete the context itself when the device is deleted.
321 
322  // Whether or not the device is being closed. This is used by the thread to determine if it needs to terminate. When
323  // dra_device_close() is called, this flag will be set and threadEventSem released. The thread will then see this as it's
324  // signal to terminate.
326 
327  // Whether or not the device is currently playing. When at least one voice is playing, this will be DR_TRUE. When there
328  // are no voices playing, this will be set to DR_FALSE and the background thread will sit dormant until another voice
329  // starts playing or the device is closed.
331 
332  // Whether or not the device should stop on the next fragment. This is used for stopping playback of devices that
333  // have no voice's playing.
335 
336 
337  // The master mixer. This is the one and only top-level mixer.
339 
340  // The number of voices currently being played. This is used to determine whether or not the device should be placed
341  // into a dormant state when nothing is being played.
342  size_t playingVoicesCount;
343 
344 
345  // When a playback event is scheduled it is added to this queue. Events are not posted straight away, but are instead
346  // placed in a queue for processing later at specific times to ensure the event is posted AFTER the device has actually
347  // played the sample the event is set for.
349 
350  // The function to call when a segment of samples has been processed. This is only really needed for capture devices,
351  // but can also be used to keep track of all of the audio data that's passed through a playback device. This is called
352  // as the read pointer moves passed each segment and again for the leftover partial segment that'll occur when the
353  // device is stopped.
356 
357 
358  // The number of channels being used by the device.
359  unsigned int channels;
360 
361  // The sample rate in seconds.
362  unsigned int sampleRate;
363 };
364 
365 struct dra_mixer
366 {
367  // The device that created and owns this mixer.
369 
370  // Whether or not the mixer is paused.
372 
373 
374  // The parent mixer.
376 
377  // The first child mixer.
379 
380  // The last child mixer.
382 
383  // The next sibling mixer.
385 
386  // The previous sibling mixer.
388 
389 
390  // The first voice attached to the mixer.
392 
393  // The last voice attached to the mixer.
395 
396 
397  // The volume of the buffer as a linear scale. A value of 0.5 means the sound is at half volume. There is no hard
398  // limit on the volume, however going beyond 1 may introduce clipping.
399  float linearVolume;
400 
401 
402  // Mixers output the results of the final mix into a buffer referred to as the staging buffer. A parent mixer will
403  // use the staging buffer when it mixes the results of it's submixers. This is an offset of pData.
404  float* pStagingBuffer;
405 
406  // A pointer to the buffer containing the sample data of the buffer currently being mixed, as floating point values.
407  // This is an offset of pData.
408  float* pNextSamplesToMix;
409 
410 
411  // The sample data for pStagingBuffer and pNextSamplesToMix.
412  float pData[1];
413 };
414 
415 struct dra_voice
416 {
417  // The device that created and owns this voice.
419 
420  // The mixer the voice is attached to. Should never be null. The mixer doesn't "own" the voice - the voice
421  // is simply attached to it.
422  dra_mixer* pMixer;
423 
424 
425  // The next voice in the linked list of voices attached to the mixer.
427 
428  // The previous voice in the linked list of voices attached to the mixer.
430 
431 
432  // The format of the audio data contained within this voice.
434 
435  // The number of channels.
436  unsigned int channels;
437 
438  // The sample rate in seconds.
439  unsigned int sampleRate;
440 
441 
442  // The volume of the voice as a linear scale. A value of 0.5 means the sound is at half volume. There is no hard
443  // limit on the volume, however going beyond 1 may introduce clipping.
444  float linearVolume;
445 
446 
447 
448  // Whether or not the voice is currently playing.
450 
451  // Whether or not the voice is currently looping. Whether or not the voice is looping is determined by the last
452  // call to dra_voice_play().
454 
455 
456  // The total number of frames in the voice.
458 
459  // The current read position, in frames. An important detail with this is that it's based on the sample rate of the
460  // device, not the voice.
462 
463 
464  // A buffer for storing converted frames. This is used by dra_voice__next_frame(). As frames are converted to
465  // floats, that are placed into this buffer.
467 
468 
469  // Data for sample rate conversion. Different SRC algorithms will use different data, which will be stored in their
470  // own structure.
471  struct
472  {
473  // The sample rate conversion algorithm to use.
475 
476  union
477  {
478  struct
479  {
483  } linear;
484  } data;
485  } src;
486 
487 
488  // The number of playback notification events. This does not include the stop and play events.
489  size_t playbackEventCount;
490 
491  // A pointer to the list of playback events.
493 
494  // The event to call when the voice has stopped playing, either naturally or explicitly with dra_voice_stop().
496 
497  // The event to call when the voice starts playing.
499 
500 
501  // Application defined user data.
502  void* pUserData;
503 
504 
505  // The size of the buffer in bytes.
506  size_t sizeInBytes;
507 
508  // The actual buffer containing the raw audio data in it's native format. At mix time the data will be converted
509  // to floats.
510  dr_uint8 pData[1];
511 };
512 
513 
514 // dra_context_init()
516 void dra_context_uninit(dra_context* pContext);
517 
518 // Helper for allocating and initializing a context. If you want to do your own memory management or just want to put the context
519 // object on the stack, just use dra_context_init() instead.
521 void dra_context_delete(dra_context* pContext);
522 
523 
524 // dra_device_init_ex()
525 //
526 // If deviceID is 0 the default device for the given type is used.
527 // format can be dra_format_default which is dra_format_s32.
528 // If channels is set to 0, defaults 2 channels (stereo).
529 // If sampleRate is set to 0, defaults to 48000.
530 // If latency is 0, defaults to 50 milliseconds. See notes about latency above.
531 //
532 // Concerning the DirectSound backend (From MSDN):
533 // Note that if your application is playing sounds as well as capturing them, capture buffer creation can fail when
534 // the format of the capture buffer is not the same as that of the primary buffer. The reason is that some cards have
535 // only a single clock and cannot support capture and playback at two different frequencies.
536 //
537 // This means you will need to keep the channels and sample rate consistent across playback and capture devices when
538 // using the DirectSound backend.
539 dra_result dra_device_init_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device* pDevice);
541 void dra_device_uninit(dra_device* pDevice);
542 
543 // Helper for allocating and initializing a device object.
544 dra_result dra_device_create_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device** ppDevice);
546 void dra_device_delete(dra_device* pDevice);
547 
548 // Starts a capture device.
549 //
550 // Do not call this on a playback device - this is managed by dr_audio. This will fail for playback devices.
552 
553 // Stops a capture device.
554 //
555 // Do not call this on a playback device - this is managed by dr_audio. This will fail for playback devices.
557 
558 // Sets the function to call when a segment of samples have been processed by the device (either captured
559 // or played back). Use this to keep track of the audio data that's passed to a playback device or from a
560 // capture device.
562 
563 
564 
565 
566 // dra_mixer_create()
567 dra_result dra_mixer_create(dra_device* pDevice, dra_mixer** ppMixer);
568 
569 // dra_mixer_delete()
570 //
571 // Deleting a mixer will detach any attached voices and sub-mixers and attach them to the master mixer. It is
572 // up to the application to manage the allocation of sub-mixers and voices. Typically you'll want to delete
573 // child mixers and voices before deleting a mixer.
574 void dra_mixer_delete(dra_mixer* pMixer);
575 
576 // dra_mixer_attach_submixer()
577 void dra_mixer_attach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer);
578 
579 // dra_mixer_detach_submixer()
580 void dra_mixer_detach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer);
581 
582 // dra_mixer_detach_all_submixers()
584 
585 // dra_mixer_attach_voice()
586 void dra_mixer_attach_voice(dra_mixer* pMixer, dra_voice* pVoice);
587 
588 // dra_mixer_detach_voice()
589 void dra_mixer_detach_voice(dra_mixer* pMixer, dra_voice* pVoice);
590 
591 // dra_mixer_detach_all_voices()
593 
594 // dra_voice_set_volume()
595 void dra_mixer_set_volume(dra_mixer* pMixer, float linearVolume);
596 
597 // dra_voice_get_volume()
598 float dra_mixer_get_volume(dra_mixer* pMixer);
599 
600 // Mixes the next number of frameCount and moves the playback position appropriately.
601 //
602 // pMixer [in] The mixer.
603 // frameCount [in] The number of frames to mix.
604 //
605 // Returns the number of frames actually mixed.
606 //
607 // The return value is used to determine whether or not there's anything left to mix in the future. When there are
608 // no samples left to mix, the device can be put into a dormant state to prevent unnecessary processing.
609 //
610 // Mixed samples will be placed in pMixer->pStagingBuffer.
611 size_t dra_mixer_mix_next_frames(dra_mixer* pMixer, size_t frameCount);
612 
613 
614 // Non-recursively counts the number of voices that are attached to the given mixer.
616 
617 // Recursively counts the number of voices that are attached to the given mixer.
619 
620 // Non-recursively gathers all of the voices that are currently attached to the given mixer.
621 size_t dra_mixer_gather_attached_voices(dra_mixer* pMixer, dra_voice** ppVoicesOut);
622 
623 // Recursively gathers all of the voices that are currently attached to the given mixer.
624 size_t dra_mixer_gather_attached_voices_recursive(dra_mixer* pMixer, dra_voice** ppVoicesOut);
625 
626 
627 // Marks the given mixer as paused.
628 void dra_mixer_pause(dra_mixer* pMixer);
629 
630 // Unmarks the given mixer as paused.
631 void dra_mixer_resume(dra_mixer* pMixer);
632 
633 // Determines whether or not the given mixer is paused.
635 
636 
637 // dra_voice_create()
638 dra_result dra_voice_create(dra_device* pDevice, dra_format format, unsigned int channels, unsigned int sampleRate, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice);
639 dra_result dra_voice_create_compatible(dra_device* pDevice, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice);
640 
641 // dra_voice_delete()
642 void dra_voice_delete(dra_voice* pVoice);
643 
644 // dra_voice_play()
645 //
646 // If the mixer the voice is attached to is not playing, the voice will be marked as playing, but won't actually play anything until
647 // the mixer is started again.
648 void dra_voice_play(dra_voice* pVoice, dr_bool32 loop);
649 
650 // dra_voice_stop()
651 void dra_voice_stop(dra_voice* pVoice);
652 
653 // dra_voice_is_playing()
655 
656 // dra_voice_is_looping()
658 
659 
660 // dra_voice_set_volume()
661 void dra_voice_set_volume(dra_voice* pVoice, float linearVolume);
662 
663 // dra_voice_get_volume()
664 float dra_voice_get_volume(dra_voice* pVoice);
665 
666 
667 void dra_voice_set_on_stop(dra_voice* pVoice, dra_event_proc proc, void* pUserData);
668 void dra_voice_set_on_play(dra_voice* pVoice, dra_event_proc proc, void* pUserData);
669 dr_bool32 dra_voice_add_playback_event(dra_voice* pVoice, dr_uint64 sampleIndex, dr_uint64 eventID, dra_event_proc proc, void* pUserData);
671 
672 // dra_voice_get_playback_position()
674 
675 // dra_voice_set_playback_position()
676 void dra_voice_set_playback_position(dra_voice* pVoice, dr_uint64 sampleIndex);
677 
678 
679 // dra_voice_get_buffer_ptr_by_sample()
681 
682 // dra_voice_write_silence()
683 void dra_voice_write_silence(dra_voice* pVoice, dr_uint64 sampleOffset, dr_uint64 sampleCount);
684 
685 
687 
688 // Frees memory that was allocated internally by dr_audio.
689 void dra_free(void* p);
690 
691 // Retrieves the number of bits per sample based on the given format.
692 unsigned int dra_get_bits_per_sample_by_format(dra_format format);
693 
694 // Retrieves the number of bytes per sample based on the given format.
696 
697 
699 
700 typedef enum
701 {
705 
706 typedef size_t (* dra_decoder_on_read_proc) (void* pUserData, void* pDataOut, size_t bytesToRead);
707 typedef dr_bool32 (* dra_decoder_on_seek_proc) (void* pUserData, int offset, dra_seek_origin origin);
708 
709 typedef void (* dra_decoder_on_delete_proc) (void* pBackendDecoder);
710 typedef dr_uint64 (* dra_decoder_on_read_samples_proc) (void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut);
711 typedef dr_bool32 (* dra_decoder_on_seek_samples_proc) (void* pBackendDecoder, dr_uint64 sample);
712 
713 typedef struct
714 {
715  const dr_uint8* data;
716  size_t dataSize;
717  size_t currentReadPos;
719 
720 typedef struct
721 {
722  unsigned int channels;
723  unsigned int sampleRate;
724  dr_uint64 totalSampleCount; // <-- Can be 0.
725 
728  void* pUserData;
729 
730  void* pBackendDecoder;
732  dra_decoder_on_read_samples_proc onReadSamples;
733  dra_decoder_on_seek_samples_proc onSeekSamples;
734 
735  // A hack to enable decoding from memory without mallocing the user data.
736  dra__memory_stream memoryStream;
737 } dra_decoder;
738 
739 // dra_decoder_open()
741 float* dra_decoder_open_and_decode_f32(dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount);
742 
743 dra_result dra_decoder_open_memory(dra_decoder* pDecoder, const void* pData, size_t dataSize);
744 float* dra_decoder_open_and_decode_memory_f32(const void* pData, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount);
745 
746 #ifndef DR_AUDIO_NO_STDIO
747 dra_result dra_decoder_open_file(dra_decoder* pDecoder, const char* filePath);
748 float* dra_decoder_open_and_decode_file_f32(const char* filePath, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount);
749 #endif
750 
751 // dra_decoder_close()
752 void dra_decoder_close(dra_decoder* pDecoder);
753 
754 // dra_decoder_read_f32()
755 dr_uint64 dra_decoder_read_f32(dra_decoder* pDecoder, dr_uint64 samplesToRead, float* pSamplesOut);
756 
757 // dra_decoder_seek_to_sample()
759 
760 
762 
763 #ifndef DR_AUDIO_NO_STDIO
764 // Creates a voice from a file.
765 dra_result dra_voice_create_from_file(dra_device* pDevice, const char* filePath, dra_voice** ppVoice);
766 #endif
767 
768 
770 //
771 // This section is for the sound world APIs. These are high-level APIs that sit directly on top of the main API.
773 typedef struct dra_sound dra_sound;
775 
776 typedef void (* dra_sound_on_delete_proc) (dra_sound* pSound);
777 typedef dr_uint64 (* dra_sound_on_read_proc) (dra_sound* pSound, dr_uint64 samplesToRead, void* pSamplesOut);
778 typedef dr_bool32 (* dra_sound_on_seek_proc) (dra_sound* pSound, dr_uint64 sample);
779 
780 struct dra_sound_desc
781 {
782  // The format of the sound.
784 
785  // The number of channels in the audio data.
786  unsigned int channels;
787 
788  // The sample rate of the audio data.
789  unsigned int sampleRate;
790 
791 
792  // The size of the audio data in bytes. If this is 0 it is assumed the data will be streamed.
793  size_t dataSize;
794 
795  // A pointer to the audio data. If this is null it is assumed the audio data is streamed.
796  void* pData;
797 
798 
799  // A pointer to the function to call when the sound object is deleted. This is used to give the application an
800  // opportunity to do any clean up, such as closing decoders or whatnot.
802 
803  // A pointer to the function to call when dr_audio needs to request a chunk of audio data. This is only used when
804  // streaming data.
806 
807  // A pointer to the function to call when dr_audio needs to seek the audio data. This is only used when streaming
808  // data.
810 
811  // A pointer to some application defined user data that can be associated with the sound.
812  void* pUserData;
813 };
814 
815 struct dra_sound_world
816 {
817  // The playback device.
819 
820  // Whether or not the world owns the playback device. When this is set to DR_TRUE, it will be deleted when the world is deleted.
822 };
823 
824 struct dra_sound
825 {
826  // The world that owns this sound.
828 
829  // The voice object for emitting audio out of the device.
830  dra_voice* pVoice;
831 
832  // The descriptor of the sound that was used to initialize the sound.
834 
835  // Whether or not the sound is looping.
837 
838  // Whether or not the sound should be stopped at the end of the chunk that's currently playing.
840 
841  // Application defined user data.
842  void* pUserData;
843 };
844 
845 // dra_sound_world_create()
846 //
847 // The playback device can be null, in which case a default one will be created.
849 
850 // dra_sound_world_delete()
851 //
852 // This will delete every sound this world owns.
854 
855 // dra_sound_world_play_inline()
856 //
857 // pMixer [in, optional] The mixer to attach the sound to. Can null, in which case it's attached to the master mixer.
859 
860 // Plays an inlined sound in 3D space.
861 //
862 // This is a placeholder function. 3D position is not yet implemented.
863 void dra_sound_world_play_inline_3f(dra_sound_world* pWorld, dra_sound_desc* pDesc, dra_mixer* pMixer, float xPos, float yPos, float zPos);
864 
865 // Stops playing every sound.
866 //
867 // This will stop all voices that are attached to the world's playback deviecs, including those that are not attached to a dra_sound object.
869 
870 
871 // Sets the position of the listener for 3D effects.
872 //
873 // This is placeholder.
874 void dra_sound_world_set_listener_position(dra_sound_world* pWorld, float xPos, float yPos, float zPos);
875 
876 // Sets the orientation of the listener for 3D effects.
877 //
878 // This is placeholder.
879 void dra_sound_world_set_listener_orientation(dra_sound_world* pWorld, float xForward, float yForward, float zForward, float xUp, float yUp, float zUp);
880 
881 
882 
883 
884 // dra_sound_create()
885 //
886 // The datails in "desc" can be accessed from the returned object directly.
887 //
888 // This uses the pUserData member of the internal voice. Do not overwrite this. Instead, use the pUserData member of
889 // the returned dra_sound object.
891 
892 #ifndef DR_AUDIO_NO_STDIO
893 // dra_sound_create_from_file()
894 //
895 // This will hold a handle to the file for the life of the sound.
896 dra_sound* dra_sound_create_from_file(dra_sound_world* pWorld, const char* filePath);
897 #endif
898 
899 // dra_sound_delete()
900 void dra_sound_delete(dra_sound* pSound);
901 
902 
903 // dra_sound_play()
904 void dra_sound_play(dra_sound* pSound, dr_bool32 loop);
905 
906 // dra_sound_stop()
907 void dra_sound_stop(dra_sound* pSound);
908 
909 
910 // Attaches the given sound to the given mixer.
911 //
912 // Setting pMixer to null will detach the sound from the mixer it is currently attached to and attach it
913 // to the master mixer.
914 void dra_sound_attach_to_mixer(dra_sound* pSound, dra_mixer* pMixer);
915 
916 
917 // dra_sound_set_on_stop()
918 void dra_sound_set_on_stop(dra_sound* pSound, dra_event_proc proc, void* pUserData);
919 
920 // dra_sound_set_on_play()
921 void dra_sound_set_on_play(dra_sound* pSound, dra_event_proc proc, void* pUserData);
922 
923 
924 #ifdef __cplusplus
925 }
926 #endif
927 #endif //dr_audio2_h
928 
929 
930 
932 //
933 // IMPLEMENTATION
934 //
936 #ifdef DR_AUDIO_IMPLEMENTATION
937 #include <stdlib.h>
938 #include <math.h>
939 #include <assert.h>
940 #include <stdio.h> // For good old printf debugging. Delete later.
941 
942 #ifdef _MSC_VER
943 #define DR_AUDIO_INLINE static __forceinline
944 #else
945 #define DR_AUDIO_INLINE static inline
946 #endif
947 
948 #define DR_AUDIO_DEFAULT_CHANNEL_COUNT 2
949 #define DR_AUDIO_DEFAULT_SAMPLE_RATE 48000
950 #define DR_AUDIO_DEFAULT_LATENCY 100 // Milliseconds. TODO: Test this with very low values. DirectSound appears to not signal the fragment events when it's too small. With values of about 20 it sounds crackly.
951 #define DR_AUDIO_DEFAULT_FRAGMENT_COUNT 3 // The hardware buffer is divided up into latency-sized blocks. This controls that number. Must be at least 2.
952 
953 #define DR_AUDIO_BACKEND_TYPE_NULL 0
954 #define DR_AUDIO_BACKEND_TYPE_DSOUND 1
955 #define DR_AUDIO_BACKEND_TYPE_ALSA 2
956 
957 #ifdef dr_wav_h
958 #define DR_AUDIO_HAS_WAV
959  #ifndef DR_WAV_NO_STDIO
960  #define DR_AUDIO_HAS_WAV_STDIO
961  #endif
962 #endif
963 #ifdef dr_flac_h
964 #define DR_AUDIO_HAS_FLAC
965  #ifndef DR_FLAC_NO_STDIO
966  #define DR_AUDIO_HAS_FLAC_STDIO
967  #endif
968 #endif
969 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
970 #define DR_AUDIO_HAS_VORBIS
971  #ifndef STB_VORBIS_NO_STDIO
972  #define DR_AUDIO_HAS_VORBIS_STDIO
973  #endif
974 #endif
975 
976 #if defined(DR_AUDIO_HAS_WAV) || \
977  defined(DR_AUDIO_HAS_FLAC) || \
978  defined(DR_AUDIO_HAS_VORBIS)
979 #define DR_AUDIO_HAS_EXTERNAL_DECODER
980 #endif
981 
982 
983 // Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
984 DR_AUDIO_INLINE unsigned int dra_next_power_of_2(unsigned int x)
985 {
986  x--;
987  x |= x >> 1;
988  x |= x >> 2;
989  x |= x >> 4;
990  x |= x >> 8;
991  x |= x >> 16;
992  x++;
993 
994  return x;
995 }
996 
997 DR_AUDIO_INLINE unsigned int dra_prev_power_of_2(unsigned int x)
998 {
999  return dra_next_power_of_2(x) >> 1;
1000 }
1001 
1002 
1003 DR_AUDIO_INLINE float dra_mixf(float x, float y, float a)
1004 {
1005  return x*(1-a) + y*a;
1006 }
1007 
1008 
1010 //
1011 // Platform Specific
1012 //
1014 
1015 // Every backend struct should begin with these properties.
1016 struct dra_backend
1017 {
1018 #define DR_AUDIO_BASE_BACKEND_ATTRIBS \
1019  unsigned int type; \
1020 
1021  DR_AUDIO_BASE_BACKEND_ATTRIBS
1022 };
1023 
1024 // Every backend device struct should begin with these properties.
1025 struct dra_backend_device
1026 {
1027 #define DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS \
1028  dra_backend* pBackend; \
1029  dra_device_type type; \
1030  unsigned int channels; \
1031  unsigned int sampleRate; \
1032  unsigned int fragmentCount; \
1033  unsigned int samplesPerFragment; \
1034 
1035  DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS
1036 };
1037 
1038 
1039 
1040 
1041 // Compile-time platform detection and #includes.
1042 #ifdef _WIN32
1043 #include <windows.h>
1044 
1046 typedef DWORD (* dra_thread_entry_proc)(LPVOID pData);
1047 
1048 dra_thread dra_thread_create(dra_thread_entry_proc entryProc, void* pData)
1049 {
1050  return (dra_thread)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)entryProc, pData, 0, NULL);
1051 }
1052 
1053 void dra_thread_delete(dra_thread thread)
1054 {
1055  CloseHandle((HANDLE)thread);
1056 }
1057 
1058 void dra_thread_wait(dra_thread thread)
1059 {
1060  WaitForSingleObject((HANDLE)thread, INFINITE);
1061 }
1062 
1063 
1064 dra_mutex dra_mutex_create()
1065 {
1066  return (dra_mutex)CreateEventA(NULL, FALSE, TRUE, NULL);
1067 }
1068 
1069 void dra_mutex_delete(dra_mutex mutex)
1070 {
1071  CloseHandle((HANDLE)mutex);
1072 }
1073 
1074 void dra_mutex_lock(dra_mutex mutex)
1075 {
1076  WaitForSingleObject((HANDLE)mutex, INFINITE);
1077 }
1078 
1079 void dra_mutex_unlock(dra_mutex mutex)
1080 {
1081  SetEvent((HANDLE)mutex);
1082 }
1083 
1084 
1085 dra_semaphore dra_semaphore_create(int initialValue)
1086 {
1087  return (void*)CreateSemaphoreA(NULL, initialValue, LONG_MAX, NULL);
1088 }
1089 
1090 void dra_semaphore_delete(dra_semaphore semaphore)
1091 {
1092  CloseHandle((HANDLE)semaphore);
1093 }
1094 
1095 dr_bool32 dra_semaphore_wait(dra_semaphore semaphore)
1096 {
1097  return WaitForSingleObject((HANDLE)semaphore, INFINITE) == WAIT_OBJECT_0;
1098 }
1099 
1100 dr_bool32 dra_semaphore_release(dra_semaphore semaphore)
1101 {
1102  return ReleaseSemaphore((HANDLE)semaphore, 1, NULL) != 0;
1103 }
1104 
1105 
1106 
1108 #ifndef DR_AUDIO_NO_DSOUND
1109 #define DR_AUDIO_ENABLE_DSOUND
1110 #include <dsound.h>
1111 #include <mmreg.h> // WAVEFORMATEX
1112 
1113 GUID DR_AUDIO_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
1114 
1115 static GUID _g_draGUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
1116 static GUID _g_draGUID_IID_IDirectSoundCaptureBuffer8 = {0x00990df4, 0x0dbb, 0x4872, {0x83, 0x3e, 0x6d, 0x30, 0x3e, 0x80, 0xae, 0xb6}};
1117 static GUID _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
1118 
1119 #ifdef __cplusplus
1120 static GUID g_draGUID_IID_DirectSoundNotify = _g_draGUID_IID_DirectSoundNotify;
1121 static GUID g_draGUID_IID_IDirectSoundCaptureBuffer8 = _g_draGUID_IID_IDirectSoundCaptureBuffer8;
1122 //static GUID g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1123 #else
1124 static GUID* g_draGUID_IID_DirectSoundNotify = &_g_draGUID_IID_DirectSoundNotify;
1125 static GUID* g_draGUID_IID_IDirectSoundCaptureBuffer8 = &_g_draGUID_IID_IDirectSoundCaptureBuffer8;
1126 //static GUID* g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = &_g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1127 #endif
1128 
1129 typedef HRESULT (WINAPI * pDirectSoundCreate8Proc)(LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS8, LPUNKNOWN pUnkOuter);
1130 typedef HRESULT (WINAPI * pDirectSoundEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
1131 typedef HRESULT (WINAPI * pDirectSoundCaptureCreate8Proc)(LPCGUID pcGuidDevice, LPDIRECTSOUNDCAPTURE8 *ppDSC8, LPUNKNOWN pUnkOuter);
1132 typedef HRESULT (WINAPI * pDirectSoundCaptureEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
1133 
1134 typedef struct
1135 {
1136  DR_AUDIO_BASE_BACKEND_ATTRIBS
1137 
1138  // A handle to the dsound DLL for doing run-time linking.
1139  HMODULE hDSoundDLL;
1140 
1141  pDirectSoundCreate8Proc pDirectSoundCreate8;
1142  pDirectSoundEnumerateAProc pDirectSoundEnumerateA;
1143  pDirectSoundCaptureCreate8Proc pDirectSoundCaptureCreate8;
1144  pDirectSoundCaptureEnumerateAProc pDirectSoundCaptureEnumerateA;
1145 } dra_backend_dsound;
1146 
1147 typedef struct
1148 {
1149  DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS
1150 
1151 
1152  // The main device object for use with DirectSound.
1153  LPDIRECTSOUND8 pDS;
1154 
1155  // The DirectSound "primary buffer". It's basically just representing the connection between us and the hardware device.
1156  LPDIRECTSOUNDBUFFER pDSPrimaryBuffer;
1157 
1158  // The DirectSound "secondary buffer". This is where the actual audio data will be written to by dr_audio when it's time
1159  // to play back some audio through the speakers. This represents the hardware buffer.
1160  LPDIRECTSOUNDBUFFER pDSSecondaryBuffer;
1161 
1162 
1163  // The main capture device object for use with DirectSound. This is only used by capture devices and is created by DirectSoundCaptureCreate8().
1164  LPDIRECTSOUNDCAPTURE8 pDSCapture;
1165 
1166  // The capture buffer. This is where captured audio data will be placed. This is only used by capture devices.
1167  LPDIRECTSOUNDCAPTUREBUFFER8 pDSCaptureBuffer;
1168 
1169 
1170  // The notification object used by DirectSound to notify dr_audio that it's ready for the next fragment of audio data.
1171  LPDIRECTSOUNDNOTIFY pDSNotify;
1172 
1173  // Notification events for each fragment.
1174  HANDLE pNotifyEvents[DR_AUDIO_DEFAULT_FRAGMENT_COUNT];
1175 
1176  // The event the main playback thread will wait on to determine whether or not the playback loop should terminate.
1177  HANDLE hStopEvent;
1178 
1179  // The index of the fragment that is currently being played.
1180  unsigned int currentFragmentIndex;
1181 
1182  // The address of the mapped fragment. This is set with IDirectSoundBuffer8::Lock() and passed to IDriectSoundBuffer8::Unlock().
1183  void* pLockPtr;
1184 
1185  // The size of the locked buffer. This is set with IDirectSoundBuffer8::Lock() and passed to IDriectSoundBuffer8::Unlock().
1186  DWORD lockSize;
1187 
1188 } dra_backend_device_dsound;
1189 
1190 typedef struct
1191 {
1192  unsigned int deviceID;
1193  unsigned int counter;
1194  const GUID* pGuid;
1195 } dra_dsound__device_enum_data;
1196 
1197 static BOOL CALLBACK dra_dsound__get_device_guid_by_id__callback(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
1198 {
1199  (void)lpcstrDescription;
1200  (void)lpcstrModule;
1201 
1202  dra_dsound__device_enum_data* pData = (dra_dsound__device_enum_data*)lpContext;
1203  assert(pData != NULL);
1204 
1205  if (pData->counter == pData->deviceID) {
1206  pData->pGuid = lpGuid;
1207  return DR_FALSE;
1208  }
1209 
1210  pData->counter += 1;
1211  return DR_TRUE;
1212 }
1213 
1214 const GUID* dra_dsound__get_playback_device_guid_by_id(dra_backend* pBackend, unsigned int deviceID)
1215 {
1216  // From MSDN:
1217  //
1218  // The first device enumerated is always called the Primary Sound Driver, and the lpGUID parameter of the callback is
1219  // NULL. This device represents the preferred output device set by the user in Control Panel.
1220  if (deviceID == 0) {
1221  return NULL;
1222  }
1223 
1224  dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
1225  if (pBackendDS == NULL) {
1226  return NULL;
1227  }
1228 
1229  // The device ID is treated as the device index. The actual ID for use by DirectSound is a GUID. We use DirectSoundEnumerateA()
1230  // iterate over each device. This function is usually only going to be used during initialization time so it won't be a performance
1231  // issue to not cache these.
1232  dra_dsound__device_enum_data data = {0};
1233  data.deviceID = deviceID;
1234  pBackendDS->pDirectSoundEnumerateA(dra_dsound__get_device_guid_by_id__callback, &data);
1235 
1236  return data.pGuid;
1237 }
1238 
1239 const GUID* dra_dsound__get_capture_device_guid_by_id(dra_backend* pBackend, unsigned int deviceID)
1240 {
1241  // From MSDN:
1242  //
1243  // The first device enumerated is always called the Primary Sound Driver, and the lpGUID parameter of the callback is
1244  // NULL. This device represents the preferred output device set by the user in Control Panel.
1245  if (deviceID == 0) {
1246  return NULL;
1247  }
1248 
1249  dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
1250  if (pBackendDS == NULL) {
1251  return NULL;
1252  }
1253 
1254  // The device ID is treated as the device index. The actual ID for use by DirectSound is a GUID. We use DirectSoundEnumerateA()
1255  // iterate over each device. This function is usually only going to be used during initialization time so it won't be a performance
1256  // issue to not cache these.
1257  dra_dsound__device_enum_data data = {0};
1258  data.deviceID = deviceID;
1259  pBackendDS->pDirectSoundCaptureEnumerateA(dra_dsound__get_device_guid_by_id__callback, &data);
1260 
1261  return data.pGuid;
1262 }
1263 
1264 dra_backend* dra_backend_create_dsound()
1265 {
1266  dra_backend_dsound* pBackendDS = (dra_backend_dsound*)calloc(1, sizeof(*pBackendDS)); // <-- Note the calloc() - makes it easier to handle the on_error goto.
1267  if (pBackendDS == NULL) {
1268  return NULL;
1269  }
1270 
1271  pBackendDS->type = DR_AUDIO_BACKEND_TYPE_DSOUND;
1272 
1273  pBackendDS->hDSoundDLL = LoadLibraryW(L"dsound.dll");
1274  if (pBackendDS->hDSoundDLL == NULL) {
1275  goto on_error;
1276  }
1277 
1278  pBackendDS->pDirectSoundCreate8 = (pDirectSoundCreate8Proc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundCreate8");
1279  if (pBackendDS->pDirectSoundCreate8 == NULL){
1280  goto on_error;
1281  }
1282 
1283  pBackendDS->pDirectSoundEnumerateA = (pDirectSoundEnumerateAProc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundEnumerateA");
1284  if (pBackendDS->pDirectSoundEnumerateA == NULL){
1285  goto on_error;
1286  }
1287 
1288  pBackendDS->pDirectSoundCaptureCreate8 = (pDirectSoundCaptureCreate8Proc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundCaptureCreate8");
1289  if (pBackendDS->pDirectSoundCaptureCreate8 == NULL){
1290  goto on_error;
1291  }
1292 
1293  pBackendDS->pDirectSoundCaptureEnumerateA = (pDirectSoundCaptureEnumerateAProc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundCaptureEnumerateA");
1294  if (pBackendDS->pDirectSoundCaptureEnumerateA == NULL){
1295  goto on_error;
1296  }
1297 
1298 
1299  return (dra_backend*)pBackendDS;
1300 
1301 on_error:
1302  if (pBackendDS != NULL) {
1303  if (pBackendDS->hDSoundDLL != NULL) {
1304  FreeLibrary(pBackendDS->hDSoundDLL);
1305  }
1306 
1307  free(pBackendDS);
1308  }
1309 
1310  return NULL;
1311 }
1312 
1313 void dra_backend_delete_dsound(dra_backend* pBackend)
1314 {
1315  dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
1316  if (pBackendDS == NULL) {
1317  return;
1318  }
1319 
1320  if (pBackendDS->hDSoundDLL != NULL) {
1321  FreeLibrary(pBackendDS->hDSoundDLL);
1322  }
1323 
1324  free(pBackendDS);
1325 }
1326 
1327 void dra_backend_device_close_dsound(dra_backend_device* pDevice)
1328 {
1329  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
1330  if (pDeviceDS == NULL) {
1331  return;
1332  }
1333 
1334  if (pDeviceDS->pDSNotify) IDirectSoundNotify_Release(pDeviceDS->pDSNotify);
1335 
1336  if (pDevice->type == dra_device_type_playback) {
1337  if (pDeviceDS->pDSSecondaryBuffer) IDirectSoundBuffer_Release(pDeviceDS->pDSSecondaryBuffer);
1338  if (pDeviceDS->pDSPrimaryBuffer) IDirectSoundBuffer_Release(pDeviceDS->pDSPrimaryBuffer);
1339  if (pDeviceDS->pDS) IDirectSound_Release(pDeviceDS->pDS);
1340  } else {
1341  if (pDeviceDS->pDSCaptureBuffer) IDirectSoundCaptureBuffer_Release(pDeviceDS->pDSCaptureBuffer);
1342  if (pDeviceDS->pDSCapture) IDirectSoundCapture_Release(pDeviceDS->pDSCapture);
1343  }
1344 
1345 
1346  for (int i = 0; i < DR_AUDIO_DEFAULT_FRAGMENT_COUNT; ++i) {
1347  CloseHandle(pDeviceDS->pNotifyEvents[i]);
1348  }
1349 
1350  if (pDeviceDS->hStopEvent != NULL) {
1351  CloseHandle(pDeviceDS->hStopEvent);
1352  }
1353 
1354  free(pDeviceDS);
1355 }
1356 
1357 dra_backend_device* dra_backend_device_open_playback_dsound(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
1358 {
1359  // These are declared at the top to stop compilations errors on GCC about goto statements skipping over variable initialization.
1360  HRESULT hr;
1361  WAVEFORMATEXTENSIBLE* actualFormat;
1362  unsigned int sampleRateInMilliseconds;
1363  unsigned int proposedFramesPerFragment;
1364  unsigned int framesPerFragment;
1365  size_t fragmentSize;
1366  size_t hardwareBufferSize;
1367 
1368  dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
1369  if (pBackendDS == NULL) {
1370  return NULL;
1371  }
1372 
1373  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)calloc(1, sizeof(*pDeviceDS));
1374  if (pDeviceDS == NULL) {
1375  goto on_error;
1376  }
1377 
1378  if (channels == 0) {
1379  channels = DR_AUDIO_DEFAULT_CHANNEL_COUNT;
1380  }
1381 
1382  pDeviceDS->pBackend = pBackend;
1383  pDeviceDS->type = dra_device_type_playback;
1384  pDeviceDS->channels = channels;
1385  pDeviceDS->sampleRate = sampleRate;
1386 
1387  hr = pBackendDS->pDirectSoundCreate8(dra_dsound__get_playback_device_guid_by_id(pBackend, deviceID), &pDeviceDS->pDS, NULL);
1388  if (FAILED(hr)) {
1389  goto on_error;
1390  }
1391 
1392  // The cooperative level must be set before doing anything else.
1393  hr = IDirectSound_SetCooperativeLevel(pDeviceDS->pDS, GetForegroundWindow(), DSSCL_PRIORITY);
1394  if (FAILED(hr)) {
1395  goto on_error;
1396  }
1397 
1398 
1399  // The primary buffer is basically just the connection to the hardware.
1400  DSBUFFERDESC descDSPrimary;
1401  memset(&descDSPrimary, 0, sizeof(DSBUFFERDESC));
1402  descDSPrimary.dwSize = sizeof(DSBUFFERDESC);
1403  descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
1404 
1405  hr = IDirectSound_CreateSoundBuffer(pDeviceDS->pDS, &descDSPrimary, &pDeviceDS->pDSPrimaryBuffer, NULL);
1406  if (FAILED(hr)) {
1407  goto on_error;
1408  }
1409 
1410 
1411  // If the channel count is 0 then we need to use the default. From MSDN:
1412  //
1413  // The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
1414  // supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
1415  // and compare the result with the format that was requested with the SetFormat method.
1416  WAVEFORMATEXTENSIBLE wf;
1417  memset(&wf, 0, sizeof(wf));
1418  wf.Format.cbSize = sizeof(wf);
1419  wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1420  wf.Format.nChannels = (WORD)channels;
1421  wf.Format.nSamplesPerSec = (DWORD)sampleRate;
1422  wf.Format.wBitsPerSample = sizeof(float)*8;
1423  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
1424  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
1425  wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
1426  wf.dwChannelMask = 0;
1427  wf.SubFormat = _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1428  if (channels > 2) {
1429  wf.dwChannelMask = ~(((DWORD)-1) << channels);
1430  }
1431 
1432  hr = IDirectSoundBuffer_SetFormat(pDeviceDS->pDSPrimaryBuffer, (WAVEFORMATEX*)&wf);
1433  if (FAILED(hr)) {
1434  goto on_error;
1435  }
1436 
1437 
1438  // Get the ACTUAL properties of the buffer. This is silly API design...
1439  DWORD requiredSize;
1440  hr = IDirectSoundBuffer_GetFormat(pDeviceDS->pDSPrimaryBuffer, NULL, 0, &requiredSize);
1441  if (FAILED(hr)) {
1442  goto on_error;
1443  }
1444 
1445  char rawdata[1024];
1446  actualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
1447  hr = IDirectSoundBuffer_GetFormat(pDeviceDS->pDSPrimaryBuffer, (WAVEFORMATEX*)actualFormat, requiredSize, NULL);
1448  if (FAILED(hr)) {
1449  goto on_error;
1450  }
1451 
1452  pDeviceDS->channels = actualFormat->Format.nChannels;
1453  pDeviceDS->sampleRate = actualFormat->Format.nSamplesPerSec;
1454 
1455  // DirectSound always has the same number of fragments.
1456  pDeviceDS->fragmentCount = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
1457 
1458 
1459  // The secondary buffer is the buffer where the real audio data will be written to and used by the hardware device. It's
1460  // size is based on the latency, sample rate and channels.
1461  //
1462  // The format of the secondary buffer should exactly match the primary buffer as to avoid unnecessary data conversions.
1463  sampleRateInMilliseconds = pDeviceDS->sampleRate / 1000;
1464  if (sampleRateInMilliseconds == 0) {
1465  sampleRateInMilliseconds = 1;
1466  }
1467 
1468  // The size of a fragment is sized such that the number of frames contained within it is a multiple of 2. The reason for
1469  // this is to keep it consistent with the ALSA backend.
1470  proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
1471  framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
1472  if (framesPerFragment == 0) {
1473  framesPerFragment = 2;
1474  }
1475 
1476  pDeviceDS->samplesPerFragment = framesPerFragment * pDeviceDS->channels;
1477 
1478  fragmentSize = pDeviceDS->samplesPerFragment * sizeof(float);
1479  hardwareBufferSize = fragmentSize * pDeviceDS->fragmentCount;
1480  assert(hardwareBufferSize > 0); // <-- If you've triggered this is means you've got something set to 0. You haven't been setting that latency to 0 have you?! That's not allowed!
1481 
1482  // Meaning of dwFlags (from MSDN):
1483  //
1484  // DSBCAPS_CTRLPOSITIONNOTIFY
1485  // The buffer has position notification capability.
1486  //
1487  // DSBCAPS_GLOBALFOCUS
1488  // With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
1489  // another application, even if the new application uses DirectSound.
1490  //
1491  // DSBCAPS_GETCURRENTPOSITION2
1492  // In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
1493  // sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
1494  // application can get a more accurate play cursor.
1495  DSBUFFERDESC descDS;
1496  memset(&descDS, 0, sizeof(DSBUFFERDESC));
1497  descDS.dwSize = sizeof(DSBUFFERDESC);
1498  descDS.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1499  descDS.dwBufferBytes = (DWORD)hardwareBufferSize;
1500  descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
1501  hr = IDirectSound_CreateSoundBuffer(pDeviceDS->pDS, &descDS, &pDeviceDS->pDSSecondaryBuffer, NULL);
1502  if (FAILED(hr)) {
1503  goto on_error;
1504  }
1505 
1506 
1507  // As DirectSound is playing back the hardware buffer it needs to notify dr_audio when it's ready for new data. This is done
1508  // through a notification object which we retrieve from the secondary buffer.
1509  hr = IDirectSoundBuffer8_QueryInterface(pDeviceDS->pDSSecondaryBuffer, g_draGUID_IID_DirectSoundNotify, (void**)&pDeviceDS->pDSNotify);
1510  if (FAILED(hr)) {
1511  goto on_error;
1512  }
1513 
1514  DSBPOSITIONNOTIFY notifyPoints[DR_AUDIO_DEFAULT_FRAGMENT_COUNT]; // One notification event for each fragment.
1515  for (int i = 0; i < DR_AUDIO_DEFAULT_FRAGMENT_COUNT; ++i)
1516  {
1517  pDeviceDS->pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
1518  if (pDeviceDS->pNotifyEvents[i] == NULL) {
1519  goto on_error;
1520  }
1521 
1522  notifyPoints[i].dwOffset = (DWORD)(i * fragmentSize); // <-- This is in bytes.
1523  notifyPoints[i].hEventNotify = pDeviceDS->pNotifyEvents[i];
1524  }
1525 
1526  hr = IDirectSoundNotify_SetNotificationPositions(pDeviceDS->pDSNotify, DR_AUDIO_DEFAULT_FRAGMENT_COUNT, notifyPoints);
1527  if (FAILED(hr)) {
1528  goto on_error;
1529  }
1530 
1531 
1532 
1533  // The termination event is used to determine when the playback thread should be terminated. The playback thread
1534  // will wait on this event in addition to the notification events in it's main loop.
1535  pDeviceDS->hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1536  if (pDeviceDS->hStopEvent == NULL) {
1537  goto on_error;
1538  }
1539 
1540  return (dra_backend_device*)pDeviceDS;
1541 
1542 on_error:
1543  dra_backend_device_close_dsound((dra_backend_device*)pDeviceDS);
1544  return NULL;
1545 }
1546 
1547 dra_backend_device* dra_backend_device_open_capture_dsound(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
1548 {
1549  (void)latencyInMilliseconds;
1550 
1551  HRESULT hr;
1552  unsigned int sampleRateInMilliseconds;
1553  unsigned int proposedFramesPerFragment;
1554  unsigned int framesPerFragment;
1555  size_t fragmentSize;
1556  size_t hardwareBufferSize;
1557 
1558  dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
1559  if (pBackendDS == NULL) {
1560  return NULL;
1561  }
1562 
1563  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)calloc(1, sizeof(*pDeviceDS));
1564  if (pDeviceDS == NULL) {
1565  goto on_error;
1566  }
1567 
1568  if (channels == 0) {
1569  channels = DR_AUDIO_DEFAULT_CHANNEL_COUNT;
1570  }
1571 
1572  pDeviceDS->pBackend = pBackend;
1573  pDeviceDS->type = dra_device_type_capture;
1574  pDeviceDS->channels = channels;
1575  pDeviceDS->sampleRate = sampleRate;
1576 
1577  hr = pBackendDS->pDirectSoundCaptureCreate8(dra_dsound__get_capture_device_guid_by_id(pBackend, deviceID), &pDeviceDS->pDSCapture, NULL);
1578  if (FAILED(hr)) {
1579  goto on_error;
1580  }
1581 
1582  pDeviceDS->fragmentCount = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
1583 
1584  // The secondary buffer is the buffer where the real audio data will be written to and used by the hardware device. It's
1585  // size is based on the latency, sample rate and channels.
1586  //
1587  // The format of the secondary buffer should exactly match the primary buffer as to avoid unnecessary data conversions.
1588  sampleRateInMilliseconds = pDeviceDS->sampleRate / 1000;
1589  if (sampleRateInMilliseconds == 0) {
1590  sampleRateInMilliseconds = 1;
1591  }
1592 
1593  // The size of a fragment is sized such that the number of frames contained within it is a multiple of 2. The reason for
1594  // this is to keep it consistent with the ALSA backend.
1595  proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
1596  framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
1597  if (framesPerFragment == 0) {
1598  framesPerFragment = 2;
1599  }
1600 
1601  pDeviceDS->samplesPerFragment = framesPerFragment * pDeviceDS->channels;
1602 
1603  fragmentSize = pDeviceDS->samplesPerFragment * sizeof(float);
1604  hardwareBufferSize = fragmentSize * pDeviceDS->fragmentCount;
1605  assert(hardwareBufferSize > 0); // <-- If you've triggered this is means you've got something set to 0. You haven't been setting that latency to 0 have you?! That's not allowed!
1606 
1607 
1608  WAVEFORMATEXTENSIBLE wf;
1609  memset(&wf, 0, sizeof(wf));
1610  wf.Format.cbSize = sizeof(wf);
1611  wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1612  wf.Format.nChannels = (WORD)channels;
1613  wf.Format.nSamplesPerSec = (DWORD)sampleRate;
1614  wf.Format.wBitsPerSample = sizeof(float)*8;
1615  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
1616  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
1617  wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
1618  wf.dwChannelMask = 0;
1619  wf.SubFormat = _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1620  if (channels > 2) {
1621  wf.dwChannelMask = ~(((DWORD)-1) << channels);
1622  }
1623 
1624  DSCBUFFERDESC descDS;
1625  memset(&descDS, 0, sizeof(descDS));
1626  descDS.dwSize = sizeof(descDS);
1627  descDS.dwFlags = 0;
1628  descDS.dwBufferBytes = (DWORD)hardwareBufferSize;
1629  descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
1630 
1631  LPDIRECTSOUNDCAPTUREBUFFER pDSCB_Temp;
1632  hr = IDirectSoundCapture_CreateCaptureBuffer(pDeviceDS->pDSCapture, &descDS, &pDSCB_Temp, NULL);
1633  if (FAILED(hr)) {
1634  goto on_error;
1635  }
1636 
1637  hr = IDirectSoundCapture_QueryInterface(pDSCB_Temp, g_draGUID_IID_IDirectSoundCaptureBuffer8, (LPVOID*)&pDeviceDS->pDSCaptureBuffer);
1638  IDirectSoundCaptureBuffer_Release(pDSCB_Temp);
1639  if (FAILED(hr)) {
1640  goto on_error; // Failed to retrieve the DirectSoundCaptureBuffer8 interface.
1641  }
1642 
1643 
1644  // As DirectSound is playing back the hardware buffer it needs to notify dr_audio when it's ready for new data. This is done
1645  // through a notification object which we retrieve from the secondary buffer.
1646  hr = IDirectSoundCaptureBuffer8_QueryInterface(pDeviceDS->pDSCaptureBuffer, g_draGUID_IID_DirectSoundNotify, (void**)&pDeviceDS->pDSNotify);
1647  if (FAILED(hr)) {
1648  goto on_error;
1649  }
1650 
1651  DSBPOSITIONNOTIFY notifyPoints[DR_AUDIO_DEFAULT_FRAGMENT_COUNT]; // One notification event for each fragment.
1652  for (int i = 0; i < DR_AUDIO_DEFAULT_FRAGMENT_COUNT; ++i)
1653  {
1654  pDeviceDS->pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
1655  if (pDeviceDS->pNotifyEvents[i] == NULL) {
1656  goto on_error;
1657  }
1658 
1659  notifyPoints[i].dwOffset = (DWORD)(((i+1) * fragmentSize) % hardwareBufferSize); // <-- This is in bytes.
1660  notifyPoints[i].hEventNotify = pDeviceDS->pNotifyEvents[i];
1661  }
1662 
1663  hr = IDirectSoundNotify_SetNotificationPositions(pDeviceDS->pDSNotify, DR_AUDIO_DEFAULT_FRAGMENT_COUNT, notifyPoints);
1664  if (FAILED(hr)) {
1665  goto on_error;
1666  }
1667 
1668 
1669  // The termination event is used to determine when the capture thread should be terminated. This thread
1670  // will wait on this event in addition to the notification events in it's main loop.
1671  pDeviceDS->hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1672  if (pDeviceDS->hStopEvent == NULL) {
1673  goto on_error;
1674  }
1675 
1676  return (dra_backend_device*)pDeviceDS;
1677 
1678 on_error:
1679  dra_backend_device_close_dsound((dra_backend_device*)pDeviceDS);
1680  return NULL;
1681 }
1682 
1683 dra_backend_device* dra_backend_device_open_dsound(dra_backend* pBackend, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
1684 {
1685  if (type == dra_device_type_playback) {
1686  return dra_backend_device_open_playback_dsound(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
1687  } else {
1688  return dra_backend_device_open_capture_dsound(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
1689  }
1690 }
1691 
1692 void dra_backend_device_play(dra_backend_device* pDevice)
1693 {
1694  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
1695  if (pDeviceDS == NULL) {
1696  return;
1697  }
1698 
1699  if (pDevice->type == dra_device_type_playback) {
1700  IDirectSoundBuffer_Play(pDeviceDS->pDSSecondaryBuffer, 0, 0, DSBPLAY_LOOPING);
1701  } else {
1702  IDirectSoundCaptureBuffer8_Start(pDeviceDS->pDSCaptureBuffer, DSCBSTART_LOOPING);
1703  }
1704 }
1705 
1706 void dra_backend_device_stop(dra_backend_device* pDevice)
1707 {
1708  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
1709  if (pDeviceDS == NULL) {
1710  return;
1711  }
1712 
1713  if (pDevice->type == dra_device_type_playback) {
1714  // Don't do anything if the buffer is not already playing.
1715  DWORD status;
1716  IDirectSoundBuffer_GetStatus(pDeviceDS->pDSSecondaryBuffer, &status);
1717  if ((status & DSBSTATUS_PLAYING) == 0) {
1718  return; // The buffer is already stopped.
1719  }
1720 
1721  // Stop the playback straight away to ensure output to the hardware device is stopped as soon as possible.
1722  IDirectSoundBuffer_Stop(pDeviceDS->pDSSecondaryBuffer);
1723  IDirectSoundBuffer_SetCurrentPosition(pDeviceDS->pDSSecondaryBuffer, 0);
1724  } else {
1725  // Don't do anything if the buffer is not already playing.
1726  DWORD status;
1727  IDirectSoundCaptureBuffer8_GetStatus(pDeviceDS->pDSCaptureBuffer, &status);
1728  if ((status & DSBSTATUS_PLAYING) == 0) {
1729  return; // The buffer is already stopped.
1730  }
1731 
1732  // Stop capture straight away to ensure output to the hardware device is stopped as soon as possible.
1733  //IDirectSoundCaptureBuffer8_Stop(pDeviceDS->pDSCaptureBuffer); // <-- There's actually a typo in my version of dsound.h which trigger's a compilation error here. The call below is safe, albeit slightly less intuitive.
1734  IDirectSoundCaptureBuffer_Stop(pDeviceDS->pDSCaptureBuffer);
1735  }
1736 
1737  // Now we just need to make dra_backend_device_play() return which in the case of DirectSound we do by
1738  // simply signaling the stop event.
1739  SetEvent(pDeviceDS->hStopEvent);
1740 }
1741 
1742 dr_bool32 dra_backend_device_wait(dra_backend_device* pDevice) // <-- Returns DR_TRUE if the function has returned because it needs more data; DR_FALSE if the device has been stopped or an error has occured.
1743 {
1744  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
1745  if (pDeviceDS == NULL) {
1746  return DR_FALSE;
1747  }
1748 
1749  unsigned int eventCount = DR_AUDIO_DEFAULT_FRAGMENT_COUNT + 1;
1750  HANDLE eventHandles[DR_AUDIO_DEFAULT_FRAGMENT_COUNT + 1]; // +1 for the stop event.
1751  memcpy(eventHandles, pDeviceDS->pNotifyEvents, sizeof(HANDLE) * DR_AUDIO_DEFAULT_FRAGMENT_COUNT);
1752  eventHandles[DR_AUDIO_DEFAULT_FRAGMENT_COUNT] = pDeviceDS->hStopEvent;
1753 
1754  DWORD rc = WaitForMultipleObjects(DR_AUDIO_DEFAULT_FRAGMENT_COUNT + 1, eventHandles, FALSE, INFINITE);
1755  if (rc >= WAIT_OBJECT_0 && rc < eventCount)
1756  {
1757  unsigned int eventIndex = rc - WAIT_OBJECT_0;
1758  HANDLE hEvent = eventHandles[eventIndex];
1759 
1760  // Has the device been stopped? If so, need to return DR_FALSE.
1761  if (hEvent == pDeviceDS->hStopEvent) {
1762  return DR_FALSE;
1763  }
1764 
1765  // If we get here it means the event that's been signaled represents a fragment.
1766  pDeviceDS->currentFragmentIndex = eventIndex;
1767  return DR_TRUE;
1768  }
1769 
1770  return DR_FALSE;
1771 }
1772 
1773 void* dra_backend_device_map_next_fragment(dra_backend_device* pDevice, size_t* pSamplesInFragmentOut)
1774 {
1775  assert(pSamplesInFragmentOut != NULL);
1776 
1777  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
1778  if (pDeviceDS == NULL) {
1779  return NULL;
1780  }
1781 
1782  if (pDeviceDS->pLockPtr != NULL) {
1783  return NULL; // A fragment is already mapped. Can only have a single fragment mapped at a time.
1784  }
1785 
1786  if (pDevice->type == dra_device_type_playback) {
1787  // If the device is not currently playing, we just return the first fragment. Otherwise we return the fragment that's sitting just past the
1788  // one that's currently playing.
1789  DWORD dwOffset = 0;
1790  DWORD dwBytes = pDeviceDS->samplesPerFragment * sizeof(float);
1791 
1792  DWORD status;
1793  IDirectSoundBuffer_GetStatus(pDeviceDS->pDSSecondaryBuffer, &status);
1794  if ((status & DSBSTATUS_PLAYING) != 0) {
1795  dwOffset = (((pDeviceDS->currentFragmentIndex + 1) % pDeviceDS->fragmentCount) * pDeviceDS->samplesPerFragment) * sizeof(float);
1796  }
1797 
1798  HRESULT hr = IDirectSoundBuffer_Lock(pDeviceDS->pDSSecondaryBuffer, dwOffset, dwBytes, &pDeviceDS->pLockPtr, &pDeviceDS->lockSize, NULL, NULL, 0);
1799  if (FAILED(hr)) {
1800  return NULL;
1801  }
1802  } else {
1803  DWORD dwOffset = (pDeviceDS->currentFragmentIndex * pDeviceDS->samplesPerFragment) * sizeof(float);
1804  DWORD dwBytes = pDeviceDS->samplesPerFragment * sizeof(float);
1805 
1806  HRESULT hr = IDirectSoundCaptureBuffer8_Lock(pDeviceDS->pDSCaptureBuffer, dwOffset, dwBytes, &pDeviceDS->pLockPtr, &pDeviceDS->lockSize, NULL, NULL, 0);
1807  if (FAILED(hr)) {
1808  return NULL;
1809  }
1810  }
1811 
1812  *pSamplesInFragmentOut = pDeviceDS->samplesPerFragment;
1813  return pDeviceDS->pLockPtr;
1814 }
1815 
1816 void dra_backend_device_unmap_next_fragment(dra_backend_device* pDevice)
1817 {
1818  dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
1819  if (pDeviceDS == NULL) {
1820  return;
1821  }
1822 
1823  if (pDeviceDS->pLockPtr == NULL) {
1824  return; // Nothing is mapped.
1825  }
1826 
1827  if (pDevice->type == dra_device_type_playback) {
1828  IDirectSoundBuffer_Unlock(pDeviceDS->pDSSecondaryBuffer, pDeviceDS->pLockPtr, pDeviceDS->lockSize, NULL, 0);
1829  } else {
1830  IDirectSoundCaptureBuffer8_Unlock(pDeviceDS->pDSCaptureBuffer, pDeviceDS->pLockPtr, pDeviceDS->lockSize, NULL, 0);
1831  }
1832 
1833  pDeviceDS->pLockPtr = NULL;
1834  pDeviceDS->lockSize = 0;
1835 }
1836 #endif // DR_AUDIO_NO_SOUND
1837 #endif // _WIN32
1838 
1839 #ifdef __linux__
1840 #include <unistd.h>
1841 #include <sys/syscall.h>
1842 #include <sys/types.h>
1843 #include <pthread.h>
1844 #include <fcntl.h>
1845 #include <semaphore.h>
1846 
1848 typedef void* (* dra_thread_entry_proc)(void* pData);
1849 
1850 dra_thread dra_thread_create(dra_thread_entry_proc entryProc, void* pData)
1851 {
1852  pthread_t thread;
1853  if (pthread_create(&thread, NULL, entryProc, pData) != 0) {
1854  return NULL;
1855  }
1856 
1857  return (dra_thread)thread;
1858 }
1859 
1860 void dra_thread_delete(dra_thread thread)
1861 {
1862  (void)thread;
1863 }
1864 
1865 void dra_thread_wait(dra_thread thread)
1866 {
1867  pthread_join((pthread_t)thread, NULL);
1868 }
1869 
1870 
1871 dra_mutex dra_mutex_create()
1872 {
1873  // The pthread_mutex_t object is not a void* castable handle type. Just create it on the heap and be done with it.
1874  pthread_mutex_t* mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
1875  if (pthread_mutex_init(mutex, NULL) != 0) {
1876  free(mutex);
1877  mutex = NULL;
1878  }
1879 
1880  return mutex;
1881 }
1882 
1883 void dra_mutex_delete(dra_mutex mutex)
1884 {
1885  pthread_mutex_destroy((pthread_mutex_t*)mutex);
1886 }
1887 
1888 void dra_mutex_lock(dra_mutex mutex)
1889 {
1890  pthread_mutex_lock((pthread_mutex_t*)mutex);
1891 }
1892 
1893 void dra_mutex_unlock(dra_mutex mutex)
1894 {
1895  pthread_mutex_unlock((pthread_mutex_t*)mutex);
1896 }
1897 
1898 
1899 dra_semaphore dra_semaphore_create(int initialValue)
1900 {
1901  sem_t* semaphore = (sem_t*)malloc(sizeof(sem_t));
1902  if (sem_init(semaphore, 0, (unsigned int)initialValue) == -1) {
1903  free(semaphore);
1904  semaphore = NULL;
1905  }
1906 
1907  return (dra_semaphore)semaphore;
1908 }
1909 
1910 void dra_semaphore_delete(dra_semaphore semaphore)
1911 {
1912  sem_close((sem_t*)semaphore);
1913 }
1914 
1915 dr_bool32 dra_semaphore_wait(dra_semaphore semaphore)
1916 {
1917  return sem_wait((sem_t*)semaphore) != -1;
1918 }
1919 
1920 dr_bool32 dra_semaphore_release(dra_semaphore semaphore)
1921 {
1922  return sem_post((sem_t*)semaphore) != -1;
1923 }
1924 
1925 
1927 
1928 #ifndef DR_AUDIO_NO_ALSA
1929 #define DR_AUDIO_ENABLE_ALSA
1930 #include <alsa/asoundlib.h>
1931 
1932 typedef struct
1933 {
1934  DR_AUDIO_BASE_BACKEND_ATTRIBS
1935 
1936  int unused;
1937 } dra_backend_alsa;
1938 
1939 typedef struct
1940 {
1941  DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS
1942 
1943  // The ALSA device handle.
1944  snd_pcm_t* deviceALSA;
1945 
1946  // Whether or not the device is currently playing.
1947  dr_bool32 isPlaying;
1948 
1949  // Whether or not the intermediary buffer is mapped.
1950  dr_bool32 isBufferMapped;
1951 
1952  // The intermediary buffer where audio data is written before being submitted to the device.
1953  float* pIntermediaryBuffer;
1954 } dra_backend_device_alsa;
1955 
1956 
1957 static dr_bool32 dra_alsa__get_device_name_by_id(dra_backend* pBackend, unsigned int deviceID, char* deviceNameOut)
1958 {
1959  assert(pBackend != NULL);
1960  assert(deviceNameOut != NULL);
1961 
1962  deviceNameOut[0] = '\0'; // Safety.
1963 
1964  if (deviceID == 0) {
1965  strcpy(deviceNameOut, "default");
1966  return DR_TRUE;
1967  }
1968 
1969 
1970  unsigned int iDevice = 0;
1971 
1972  char** deviceHints;
1973  if (snd_device_name_hint(-1, "pcm", (void***)&deviceHints) < 0) {
1974  //printf("Failed to iterate devices.");
1975  return -1;
1976  }
1977 
1978  char** nextDeviceHint = deviceHints;
1979  while (*nextDeviceHint != NULL && iDevice < deviceID) {
1980  nextDeviceHint += 1;
1981  iDevice += 1;
1982  }
1983 
1984  dr_bool32 result = DR_FALSE;
1985  if (iDevice == deviceID) {
1986  strcpy(deviceNameOut, snd_device_name_get_hint(*nextDeviceHint, "NAME"));
1987  result = DR_TRUE;
1988  }
1989 
1990  snd_device_name_free_hint((void**)deviceHints);
1991 
1992 
1993  return result;
1994 }
1995 
1996 
1997 dra_backend* dra_backend_create_alsa()
1998 {
1999  dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)calloc(1, sizeof(*pBackendALSA)); // <-- Note the calloc() - makes it easier to handle the on_error goto.
2000  if (pBackendALSA == NULL) {
2001  return NULL;
2002  }
2003 
2004  pBackendALSA->type = DR_AUDIO_BACKEND_TYPE_ALSA;
2005 
2006 
2007  return (dra_backend*)pBackendALSA;
2008 
2009 #if 0
2010 on_error:
2011  if (pBackendALSA != NULL) {
2012  free(pBackendALSA);
2013  }
2014 
2015  return NULL;
2016 #endif
2017 }
2018 
2019 void dra_backend_delete_alsa(dra_backend* pBackend)
2020 {
2021  dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)pBackend;
2022  if (pBackendALSA == NULL) {
2023  return;
2024  }
2025 
2026  free(pBackend);
2027 }
2028 
2029 
2030 void dra_backend_device_close_alsa(dra_backend_device* pDevice)
2031 {
2032  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
2033  if (pDeviceALSA == NULL) {
2034  return;
2035  }
2036 
2037  if (pDeviceALSA->deviceALSA != NULL) {
2038  snd_pcm_close(pDeviceALSA->deviceALSA);
2039  }
2040 
2041  free(pDeviceALSA->pIntermediaryBuffer);
2042  free(pDeviceALSA);
2043 }
2044 
2045 dra_backend_device* dra_backend_device_open_playback_alsa(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
2046 {
2047  unsigned int periods;
2048  int dir;
2049  size_t sampleRateInMilliseconds;
2050  unsigned int proposedFramesPerFragment;
2051  unsigned int framesPerFragment;
2052  snd_pcm_sw_params_t* pSWParams;
2053 
2054  dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)pBackend;
2055  if (pBackendALSA == NULL) {
2056  return NULL;
2057  }
2058 
2059  snd_pcm_hw_params_t* pHWParams = NULL;
2060 
2061  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)calloc(1, sizeof(*pDeviceALSA));
2062  if (pDeviceALSA == NULL) {
2063  goto on_error;
2064  }
2065 
2066  pDeviceALSA->pBackend = pBackend;
2067  pDeviceALSA->type = dra_device_type_playback;
2068  pDeviceALSA->channels = channels;
2069  pDeviceALSA->sampleRate = sampleRate;
2070 
2071  char deviceName[1024];
2072  if (!dra_alsa__get_device_name_by_id(pBackend, deviceID, deviceName)) { // <-- This will return "default" if deviceID is 0.
2073  goto on_error;
2074  }
2075 
2076  if (snd_pcm_open(&pDeviceALSA->deviceALSA, deviceName, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
2077  goto on_error;
2078  }
2079 
2080 
2081  if (snd_pcm_hw_params_malloc(&pHWParams) < 0) {
2082  goto on_error;
2083  }
2084 
2085  if (snd_pcm_hw_params_any(pDeviceALSA->deviceALSA, pHWParams) < 0) {
2086  goto on_error;
2087  }
2088 
2089  if (snd_pcm_hw_params_set_access(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
2090  goto on_error;
2091  }
2092 
2093  if (snd_pcm_hw_params_set_format(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_FORMAT_FLOAT_LE) < 0) {
2094  goto on_error;
2095  }
2096 
2097 
2098  if (snd_pcm_hw_params_set_rate_near(pDeviceALSA->deviceALSA, pHWParams, &sampleRate, 0) < 0) {
2099  goto on_error;
2100  }
2101 
2102  if (snd_pcm_hw_params_set_channels_near(pDeviceALSA->deviceALSA, pHWParams, &channels) < 0) {
2103  goto on_error;
2104  }
2105 
2106  pDeviceALSA->sampleRate = sampleRate;
2107  pDeviceALSA->channels = channels;
2108 
2109  periods = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
2110  dir = 1;
2111  if (snd_pcm_hw_params_set_periods_near(pDeviceALSA->deviceALSA, pHWParams, &periods, &dir) < 0) {
2112  //printf("Failed to set periods.\n");
2113  goto on_error;
2114  }
2115 
2116  pDeviceALSA->fragmentCount = periods;
2117 
2118  //printf("Periods: %d | Direction: %d\n", periods, dir);
2119 
2120 
2121  sampleRateInMilliseconds = pDeviceALSA->sampleRate / 1000;
2122  if (sampleRateInMilliseconds == 0) {
2123  sampleRateInMilliseconds = 1;
2124  }
2125 
2126 
2127  // According to the ALSA documentation, the value passed to snd_pcm_sw_params_set_avail_min() must be a power
2128  // of 2 on some hardware. The value passed to this function is the size in frames of a fragment. Thus, to be
2129  // as robust as possible the size of the hardware buffer should be sized based on the size of a closest power-
2130  // of-two fragment.
2131  //
2132  // To calculate the size of a fragment, the first step is to determine the initial proposed size. From that
2133  // it is dropped to the previous power of two. The reason for this is that, based on admittedly very basic
2134  // testing, ALSA seems to have good latency characteristics, and less latency is always preferable.
2135  proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
2136  framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
2137  if (framesPerFragment == 0) {
2138  framesPerFragment = 2;
2139  }
2140 
2141  pDeviceALSA->samplesPerFragment = framesPerFragment * pDeviceALSA->channels;
2142 
2143  if (snd_pcm_hw_params_set_buffer_size(pDeviceALSA->deviceALSA, pHWParams, framesPerFragment * pDeviceALSA->fragmentCount) < 0) {
2144  //printf("Failed to set buffer size.\n");
2145  goto on_error;
2146  }
2147 
2148 
2149  if (snd_pcm_hw_params(pDeviceALSA->deviceALSA, pHWParams) < 0) {
2150  goto on_error;
2151  }
2152 
2153  snd_pcm_hw_params_free(pHWParams);
2154 
2155 
2156 
2157  // Software params. There needs to be at least fragmentSize bytes in the hardware buffer before playing it, and there needs
2158  // be fragmentSize bytes available after every wait.
2159  pSWParams = NULL;
2160  if (snd_pcm_sw_params_malloc(&pSWParams) < 0) {
2161  goto on_error;
2162  }
2163 
2164  if (snd_pcm_sw_params_current(pDeviceALSA->deviceALSA, pSWParams) != 0) {
2165  goto on_error;
2166  }
2167 
2168  if (snd_pcm_sw_params_set_start_threshold(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
2169  goto on_error;
2170  }
2171  if (snd_pcm_sw_params_set_avail_min(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
2172  goto on_error;
2173  }
2174 
2175  if (snd_pcm_sw_params(pDeviceALSA->deviceALSA, pSWParams) != 0) {
2176  goto on_error;
2177  }
2178  snd_pcm_sw_params_free(pSWParams);
2179 
2180 
2181  // The intermediary buffer that will be used for mapping/unmapping.
2182  pDeviceALSA->isBufferMapped = DR_FALSE;
2183  pDeviceALSA->pIntermediaryBuffer = (float*)malloc(pDeviceALSA->samplesPerFragment * sizeof(float));
2184  if (pDeviceALSA->pIntermediaryBuffer == NULL) {
2185  goto on_error;
2186  }
2187 
2188  return (dra_backend_device*)pDeviceALSA;
2189 
2190 on_error:
2191  if (pHWParams) {
2192  snd_pcm_hw_params_free(pHWParams);
2193  }
2194 
2195  if (pDeviceALSA != NULL) {
2196  if (pDeviceALSA->deviceALSA != NULL) {
2197  snd_pcm_close(pDeviceALSA->deviceALSA);
2198  }
2199 
2200  free(pDeviceALSA->pIntermediaryBuffer);
2201  free(pDeviceALSA);
2202  }
2203 
2204  return NULL;
2205 }
2206 
2207 dra_backend_device* dra_backend_device_open_capture_alsa(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
2208 {
2209  unsigned int periods;
2210  int dir;
2211  size_t sampleRateInMilliseconds;
2212  unsigned int proposedFramesPerFragment;
2213  unsigned int framesPerFragment;
2214  snd_pcm_sw_params_t* pSWParams;
2215 
2216  dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)pBackend;
2217  if (pBackendALSA == NULL) {
2218  return NULL;
2219  }
2220 
2221  snd_pcm_hw_params_t* pHWParams = NULL;
2222 
2223  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)calloc(1, sizeof(*pDeviceALSA));
2224  if (pDeviceALSA == NULL) {
2225  goto on_error;
2226  }
2227 
2228  pDeviceALSA->pBackend = pBackend;
2229  pDeviceALSA->type = dra_device_type_capture;
2230  pDeviceALSA->channels = channels;
2231  pDeviceALSA->sampleRate = sampleRate;
2232 
2233  char deviceName[1024];
2234  if (!dra_alsa__get_device_name_by_id(pBackend, deviceID, deviceName)) { // <-- This will return "default" if deviceID is 0.
2235  goto on_error;
2236  }
2237 
2238  if (snd_pcm_open(&pDeviceALSA->deviceALSA, deviceName, SND_PCM_STREAM_CAPTURE, 0) < 0) {
2239  goto on_error;
2240  }
2241 
2242 
2243  if (snd_pcm_hw_params_malloc(&pHWParams) < 0) {
2244  goto on_error;
2245  }
2246 
2247  if (snd_pcm_hw_params_any(pDeviceALSA->deviceALSA, pHWParams) < 0) {
2248  goto on_error;
2249  }
2250 
2251  if (snd_pcm_hw_params_set_access(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
2252  goto on_error;
2253  }
2254 
2255  if (snd_pcm_hw_params_set_format(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_FORMAT_FLOAT_LE) < 0) {
2256  goto on_error;
2257  }
2258 
2259 
2260  if (snd_pcm_hw_params_set_rate_near(pDeviceALSA->deviceALSA, pHWParams, &sampleRate, 0) < 0) {
2261  goto on_error;
2262  }
2263 
2264  if (snd_pcm_hw_params_set_channels_near(pDeviceALSA->deviceALSA, pHWParams, &channels) < 0) {
2265  goto on_error;
2266  }
2267 
2268  pDeviceALSA->sampleRate = sampleRate;
2269  pDeviceALSA->channels = channels;
2270 
2271  periods = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
2272  dir = 1;
2273  if (snd_pcm_hw_params_set_periods_near(pDeviceALSA->deviceALSA, pHWParams, &periods, &dir) < 0) {
2274  //printf("Failed to set periods.\n");
2275  goto on_error;
2276  }
2277 
2278  pDeviceALSA->fragmentCount = periods;
2279 
2280  //printf("Periods: %d | Direction: %d\n", periods, dir);
2281 
2282 
2283  sampleRateInMilliseconds = pDeviceALSA->sampleRate / 1000;
2284  if (sampleRateInMilliseconds == 0) {
2285  sampleRateInMilliseconds = 1;
2286  }
2287 
2288 
2289  // According to the ALSA documentation, the value passed to snd_pcm_sw_params_set_avail_min() must be a power
2290  // of 2 on some hardware. The value passed to this function is the size in frames of a fragment. Thus, to be
2291  // as robust as possible the size of the hardware buffer should be sized based on the size of a closest power-
2292  // of-two fragment.
2293  //
2294  // To calculate the size of a fragment, the first step is to determine the initial proposed size. From that
2295  // it is dropped to the previous power of two. The reason for this is that, based on admittedly very basic
2296  // testing, ALSA seems to have good latency characteristics, and less latency is always preferable.
2297  proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
2298  framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
2299  if (framesPerFragment == 0) {
2300  framesPerFragment = 2;
2301  }
2302 
2303  pDeviceALSA->samplesPerFragment = framesPerFragment * pDeviceALSA->channels;
2304 
2305  if (snd_pcm_hw_params_set_buffer_size(pDeviceALSA->deviceALSA, pHWParams, framesPerFragment * pDeviceALSA->fragmentCount) < 0) {
2306  //printf("Failed to set buffer size.\n");
2307  goto on_error;
2308  }
2309 
2310 
2311  if (snd_pcm_hw_params(pDeviceALSA->deviceALSA, pHWParams) < 0) {
2312  goto on_error;
2313  }
2314 
2315  snd_pcm_hw_params_free(pHWParams);
2316 
2317 
2318 
2319  // Software params. There needs to be at least fragmentSize bytes in the hardware buffer before playing it, and there needs
2320  // be fragmentSize bytes available after every wait.
2321  pSWParams = NULL;
2322  if (snd_pcm_sw_params_malloc(&pSWParams) < 0) {
2323  goto on_error;
2324  }
2325 
2326  if (snd_pcm_sw_params_current(pDeviceALSA->deviceALSA, pSWParams) != 0) {
2327  goto on_error;
2328  }
2329 
2330  if (snd_pcm_sw_params_set_start_threshold(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
2331  goto on_error;
2332  }
2333  if (snd_pcm_sw_params_set_avail_min(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
2334  goto on_error;
2335  }
2336 
2337  if (snd_pcm_sw_params(pDeviceALSA->deviceALSA, pSWParams) != 0) {
2338  goto on_error;
2339  }
2340  snd_pcm_sw_params_free(pSWParams);
2341 
2342  // The intermediary buffer that will be used for mapping/unmapping.
2343  pDeviceALSA->isBufferMapped = DR_FALSE;
2344  pDeviceALSA->pIntermediaryBuffer = (float*)malloc(pDeviceALSA->samplesPerFragment * sizeof(float));
2345  if (pDeviceALSA->pIntermediaryBuffer == NULL) {
2346  goto on_error;
2347  }
2348 
2349  return (dra_backend_device*)pDeviceALSA;
2350 
2351 on_error:
2352  dra_backend_device_close_alsa((dra_backend_device*)pDeviceALSA);
2353  return NULL;
2354 }
2355 
2356 dra_backend_device* dra_backend_device_open_alsa(dra_backend* pBackend, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
2357 {
2358  if (type == dra_device_type_playback) {
2359  return dra_backend_device_open_playback_alsa(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
2360  } else {
2361  return dra_backend_device_open_capture_alsa(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
2362  }
2363 }
2364 
2365 
2366 void dra_backend_device_play(dra_backend_device* pDevice)
2367 {
2368  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
2369  if (pDeviceALSA == NULL) {
2370  return;
2371  }
2372 
2373  snd_pcm_prepare(pDeviceALSA->deviceALSA);
2374  pDeviceALSA->isPlaying = DR_TRUE;
2375 }
2376 
2377 void dra_backend_device_stop(dra_backend_device* pDevice)
2378 {
2379  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
2380  if (pDeviceALSA == NULL) {
2381  return;
2382  }
2383 
2384  snd_pcm_drop(pDeviceALSA->deviceALSA);
2385  pDeviceALSA->isPlaying = DR_FALSE;
2386 }
2387 
2388 dr_bool32 dra_backend_device_wait(dra_backend_device* pDevice)
2389 {
2390  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
2391  if (pDeviceALSA == NULL) {
2392  return DR_FALSE;
2393  }
2394 
2395  if (!pDeviceALSA->isPlaying) {
2396  return DR_FALSE;
2397  }
2398 
2399  if (pDevice->type == dra_device_type_playback) {
2400  int result = snd_pcm_wait(pDeviceALSA->deviceALSA, -1);
2401  if (result > 0) {
2402  return DR_TRUE;
2403  }
2404 
2405  if (result == -EPIPE) {
2406  // xrun. Prepare the device again and just return DR_TRUE.
2407  snd_pcm_prepare(pDeviceALSA->deviceALSA);
2408  return DR_TRUE;
2409  }
2410  } else {
2411  snd_pcm_uframes_t frameCount = pDeviceALSA->samplesPerFragment / pDeviceALSA->channels;
2412  snd_pcm_sframes_t framesRead = snd_pcm_readi(pDeviceALSA->deviceALSA, pDeviceALSA->pIntermediaryBuffer, frameCount);
2413  if (framesRead > 0) {
2414  return DR_TRUE;
2415  }
2416 
2417  if (framesRead == -EPIPE) {
2418  // xrun. Prepare the device again and just return DR_TRUE.
2419  snd_pcm_prepare(pDeviceALSA->deviceALSA);
2420  return DR_TRUE;
2421  }
2422  }
2423 
2424  return DR_FALSE;
2425 }
2426 
2427 void* dra_backend_device_map_next_fragment(dra_backend_device* pDevice, size_t* pSamplesInFragmentOut)
2428 {
2429  assert(pSamplesInFragmentOut != NULL);
2430 
2431  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
2432  if (pDeviceALSA == NULL) {
2433  return NULL;
2434  }
2435 
2436  if (pDeviceALSA->isBufferMapped) {
2437  return NULL; // A fragment is already mapped. Can only have a single fragment mapped at a time.
2438  }
2439 
2440  //if (pDeviceALSA->type == dra_device_type_capture) {
2441  // snd_pcm_readi(pDeviceALSA->deviceALSA, pDeviceALSA->pIntermediaryBuffer, pDeviceALSA->samplesPerFragment / pDeviceALSA->channels);
2442  //}
2443 
2444  *pSamplesInFragmentOut = pDeviceALSA->samplesPerFragment;
2445  return pDeviceALSA->pIntermediaryBuffer;
2446 }
2447 
2448 void dra_backend_device_unmap_next_fragment(dra_backend_device* pDevice)
2449 {
2450  dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
2451  if (pDeviceALSA == NULL) {
2452  return;
2453  }
2454 
2455  if (pDeviceALSA->isBufferMapped) {
2456  return; // Nothing is mapped.
2457  }
2458 
2459  // Unammping is when the data is written to the device.
2460  if (pDeviceALSA->type == dra_device_type_playback) {
2461  snd_pcm_writei(pDeviceALSA->deviceALSA, pDeviceALSA->pIntermediaryBuffer, pDeviceALSA->samplesPerFragment / pDeviceALSA->channels);
2462  }
2463 }
2464 #endif // DR_AUDIO_NO_ALSA
2465 #endif // __linux__
2466 
2467 
2468 void dra_thread_wait_and_delete(dra_thread thread)
2469 {
2470  dra_thread_wait(thread);
2471  dra_thread_delete(thread);
2472 }
2473 
2474 
2475 dra_backend* dra_backend_create()
2476 {
2477  dra_backend* pBackend = NULL;
2478 
2479 #ifdef DR_AUDIO_ENABLE_DSOUND
2480  pBackend = dra_backend_create_dsound();
2481  if (pBackend != NULL) {
2482  return pBackend;
2483  }
2484 #endif
2485 
2486 #ifdef DR_AUDIO_ENABLE_ALSA
2487  pBackend = dra_backend_create_alsa();
2488  if (pBackend != NULL) {
2489  return pBackend;
2490  }
2491 #endif
2492 
2493  // If we get here it means we couldn't find a backend. Default to a NULL backend? Returning NULL makes it clearer that an error occured.
2494  return NULL;
2495 }
2496 
2497 void dra_backend_delete(dra_backend* pBackend)
2498 {
2499  if (pBackend == NULL) {
2500  return;
2501  }
2502 
2503 #ifdef DR_AUDIO_ENABLE_DSOUND
2504  if (pBackend->type == DR_AUDIO_BACKEND_TYPE_DSOUND) {
2505  dra_backend_delete_dsound(pBackend);
2506  return;
2507  }
2508 #endif
2509 
2510 #ifdef DR_AUDIO_ENABLE_ALSA
2511  if (pBackend->type == DR_AUDIO_BACKEND_TYPE_ALSA) {
2512  dra_backend_delete_alsa(pBackend);
2513  return;
2514  }
2515 #endif
2516 
2517  // Should never get here. If this assert is triggered it means you haven't plugged in the API in the list above.
2518  assert(DR_FALSE);
2519 }
2520 
2521 
2522 dra_backend_device* dra_backend_device_open(dra_backend* pBackend, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
2523 {
2524  if (pBackend == NULL) {
2525  return NULL;
2526  }
2527 
2528 #ifdef DR_AUDIO_ENABLE_DSOUND
2529  if (pBackend->type == DR_AUDIO_BACKEND_TYPE_DSOUND) {
2530  return dra_backend_device_open_dsound(pBackend, type, deviceID, channels, sampleRate, latencyInMilliseconds);
2531  }
2532 #endif
2533 
2534 #ifdef DR_AUDIO_ENABLE_ALSA
2535  if (pBackend->type == DR_AUDIO_BACKEND_TYPE_ALSA) {
2536  return dra_backend_device_open_alsa(pBackend, type, deviceID, channels, sampleRate, latencyInMilliseconds);
2537  }
2538 #endif
2539 
2540 
2541  // Should never get here. If this assert is triggered it means you haven't plugged in the API in the list above.
2542  assert(DR_FALSE);
2543  return NULL;
2544 }
2545 
2546 void dra_backend_device_close(dra_backend_device* pDevice)
2547 {
2548  if (pDevice == NULL) {
2549  return;
2550  }
2551 
2552  assert(pDevice->pBackend != NULL);
2553 
2554 #ifdef DR_AUDIO_ENABLE_DSOUND
2555  if (pDevice->pBackend->type == DR_AUDIO_BACKEND_TYPE_DSOUND) {
2556  dra_backend_device_close_dsound(pDevice);
2557  return;
2558  }
2559 #endif
2560 
2561 #ifdef DR_AUDIO_ENABLE_ALSA
2562  if (pDevice->pBackend->type == DR_AUDIO_BACKEND_TYPE_ALSA) {
2563  dra_backend_device_close_alsa(pDevice);
2564  return;
2565  }
2566 #endif
2567 }
2568 
2569 
2570 
2572 //
2573 // Cross Platform
2574 //
2576 
2577 // Reads the next frame.
2578 //
2579 // Frames are retrieved with respect to the device the voice is attached to. What this basically means is
2580 // that any data conversions will be done within this function.
2581 //
2582 // The return value is a pointer to the voice containing the converted samples, always as floating point.
2583 //
2584 // If the voice is in the same format as the device (floating point, same sample rate and channels), then
2585 // this function will be on a fast path and will return almost immediately with a pointer that points to
2586 // the voice's actual data without any data conversion.
2587 //
2588 // If an error occurs, null is returned. Null will be returned if the end of the voice's buffer is reached
2589 // and it's non-looping. This will not return NULL if the voice is looping - it will just loop back to the
2590 // start as one would expect.
2591 //
2592 // This function is not thread safe, but can be called from multiple threads if you do your own
2593 // synchronization. Just keep in mind that the return value may point to the voice's actual internal data.
2594 float* dra_voice__next_frame(dra_voice* pVoice);
2595 
2596 // dra_voice__next_frames()
2597 size_t dra_voice__next_frames(dra_voice* pVoice, size_t frameCount, float* pSamplesOut);
2598 
2599 
2601 {
2602  if (pContext == NULL) return DRA_RESULT_INVALID_ARGS;
2603  memset(pContext, 0, sizeof(*pContext));
2604 
2605  // We need a backend first.
2606  pContext->pBackend = dra_backend_create();
2607  if (pContext->pBackend == NULL) {
2608  return DRA_RESULT_NO_BACKEND; // Failed to create a backend.
2609  }
2610 
2611  return DRA_RESULT_SUCCESS;
2612 }
2613 
2614 void dra_context_uninit(dra_context* pContext)
2615 {
2616  if (pContext == NULL || pContext->pBackend == NULL) return;
2617  dra_backend_delete(pContext->pBackend);
2618 }
2619 
2621 {
2622  if (ppContext == NULL) return DRA_RESULT_INVALID_ARGS;
2623  *ppContext = NULL;
2624 
2625  dra_context* pContext = (dra_context*)malloc(sizeof(*pContext));
2626  if (pContext == NULL) {
2627  return DRA_RESULT_OUT_OF_MEMORY;
2628  }
2629 
2630  dra_result result = dra_context_init(pContext);
2631  if (result != DRA_RESULT_SUCCESS) {
2632  free(pContext);
2633  return result;
2634  }
2635 
2636  *ppContext = pContext;
2637  return DRA_RESULT_SUCCESS;
2638 }
2639 
2640 void dra_context_delete(dra_context* pContext)
2641 {
2642  if (pContext == NULL) return;
2643 
2644  dra_context_uninit(pContext);
2645  free(pContext);
2646 }
2647 
2648 
2649 void dra_event_queue__schedule_event(dra__event_queue* pQueue, dra__event* pEvent)
2650 {
2651  if (pQueue == NULL || pEvent == NULL) {
2652  return;
2653  }
2654 
2655  dra_mutex_lock(pQueue->lock);
2656  {
2657  if (pQueue->eventCount == pQueue->eventBufferSize)
2658  {
2659  // Ran out of room. Resize.
2660  size_t newEventBufferSize = (pQueue->eventBufferSize == 0) ? 16 : pQueue->eventBufferSize*2;
2661  dra__event* pNewEvents = (dra__event*)malloc(newEventBufferSize * sizeof(*pNewEvents));
2662  if (pNewEvents == NULL) {
2663  return;
2664  }
2665 
2666  for (size_t i = 0; i < pQueue->eventCount; ++i) {
2667  pQueue->pEvents[i] = pQueue->pEvents[(pQueue->firstEvent + i) % pQueue->eventBufferSize];
2668  }
2669 
2670  pQueue->firstEvent = 0;
2671  pQueue->eventBufferSize = newEventBufferSize;
2672  pQueue->pEvents = pNewEvents;
2673  }
2674 
2675  assert(pQueue->eventCount < pQueue->eventBufferSize);
2676 
2677  pQueue->pEvents[(pQueue->firstEvent + pQueue->eventCount) % pQueue->eventBufferSize] = *pEvent;
2678  pQueue->eventCount += 1;
2679  }
2680  dra_mutex_unlock(pQueue->lock);
2681 }
2682 
2683 void dra_event_queue__cancel_events_of_voice(dra__event_queue* pQueue, dra_voice* pVoice)
2684 {
2685  if (pQueue == NULL || pVoice == NULL) {
2686  return;
2687  }
2688 
2689  dra_mutex_lock(pQueue->lock);
2690  {
2691  // We don't actually remove anything from the queue, but instead zero out the event's data.
2692  for (size_t i = 0; i < pQueue->eventCount; ++i) {
2693  dra__event* pEvent = &pQueue->pEvents[(pQueue->firstEvent + i) % pQueue->eventBufferSize];
2694  if (pEvent->pVoice == pVoice) {
2695  pEvent->pVoice = NULL;
2696  pEvent->proc = NULL;
2697  }
2698  }
2699  }
2700  dra_mutex_unlock(pQueue->lock);
2701 }
2702 
2703 dr_bool32 dra_event_queue__next_event(dra__event_queue* pQueue, dra__event* pEventOut)
2704 {
2705  if (pQueue == NULL || pEventOut == NULL) {
2706  return DR_FALSE;
2707  }
2708 
2709  dr_bool32 result = DR_FALSE;
2710  dra_mutex_lock(pQueue->lock);
2711  {
2712  if (pQueue->eventCount > 0) {
2713  *pEventOut = pQueue->pEvents[pQueue->firstEvent];
2714  pQueue->firstEvent = (pQueue->firstEvent + 1) % pQueue->eventBufferSize;
2715  pQueue->eventCount -= 1;
2716  result = DR_TRUE;
2717  }
2718  }
2719  dra_mutex_unlock(pQueue->lock);
2720 
2721  return result;
2722 }
2723 
2724 void dra_event_queue__post_events(dra__event_queue* pQueue)
2725 {
2726  if (pQueue == NULL) {
2727  return;
2728  }
2729 
2730  dra__event nextEvent;
2731  while (dra_event_queue__next_event(pQueue, &nextEvent)) {
2732  if (nextEvent.proc) {
2733  nextEvent.proc(nextEvent.id, nextEvent.pUserData);
2734  }
2735  }
2736 }
2737 
2738 
2739 void dra_device__post_event(dra_device* pDevice, dra_thread_event_type type)
2740 {
2741  assert(pDevice != NULL);
2742 
2743  pDevice->nextThreadEventType = type;
2744  dra_semaphore_release(pDevice->threadEventSem);
2745 }
2746 
2747 void dra_device__lock(dra_device* pDevice)
2748 {
2749  assert(pDevice != NULL);
2750  dra_mutex_lock(pDevice->mutex);
2751 }
2752 
2753 void dra_device__unlock(dra_device* pDevice)
2754 {
2755  assert(pDevice != NULL);
2756  dra_mutex_unlock(pDevice->mutex);
2757 }
2758 
2759 dr_bool32 dra_device__is_playing_nolock(dra_device* pDevice)
2760 {
2761  assert(pDevice != NULL);
2762  return pDevice->isPlaying;
2763 }
2764 
2765 dr_bool32 dra_device__mix_next_fragment(dra_device* pDevice)
2766 {
2767  assert(pDevice != NULL);
2768 
2769  size_t samplesInFragment;
2770  void* pSampleData = dra_backend_device_map_next_fragment(pDevice->pBackendDevice, &samplesInFragment);
2771  if (pSampleData == NULL) {
2772  dra_backend_device_stop(pDevice->pBackendDevice);
2773  return DR_FALSE;
2774  }
2775 
2776  size_t framesInFragment = samplesInFragment / pDevice->channels;
2777  size_t framesMixed = dra_mixer_mix_next_frames(pDevice->pMasterMixer, framesInFragment);
2778 
2779  memcpy(pSampleData, pDevice->pMasterMixer->pStagingBuffer, (size_t)samplesInFragment * sizeof(float));
2780 
2781  if (pDevice->onSamplesProcessed) {
2782  pDevice->onSamplesProcessed(pDevice, framesMixed * pDevice->channels, (const float*)pSampleData, pDevice->pUserDataForOnSamplesProcessed);
2783  }
2784 
2785  dra_backend_device_unmap_next_fragment(pDevice->pBackendDevice);
2786 
2787  if (framesMixed == 0) {
2788  pDevice->stopOnNextFragment = DR_TRUE;
2789  }
2790 
2791  //printf("Mixed next fragment into %p\n", pSampleData);
2792  return DR_TRUE;
2793 }
2794 
2795 void dra_device__play(dra_device* pDevice)
2796 {
2797  assert(pDevice != NULL);
2798 
2799  dra_device__lock(pDevice);
2800  {
2801  // Don't do anything if the device is already playing.
2802  if (!dra_device__is_playing_nolock(pDevice))
2803  {
2804  assert(pDevice->pBackendDevice->type == dra_device_type_capture || pDevice->playingVoicesCount > 0);
2805 
2806  dra_device__post_event(pDevice, dra_thread_event_type_play);
2807  pDevice->isPlaying = DR_TRUE;
2808  pDevice->stopOnNextFragment = DR_FALSE;
2809  }
2810  }
2811  dra_device__unlock(pDevice);
2812 }
2813 
2814 void dra_device__stop(dra_device* pDevice)
2815 {
2816  assert(pDevice != NULL);
2817 
2818  dra_device__lock(pDevice);
2819  {
2820  // Don't do anything if the device is already stopped.
2821  if (dra_device__is_playing_nolock(pDevice))
2822  {
2823  //assert(pDevice->playingVoicesCount == 0);
2824 
2825  dra_backend_device_stop(pDevice->pBackendDevice);
2826  pDevice->isPlaying = DR_FALSE;
2827  }
2828  }
2829  dra_device__unlock(pDevice);
2830 }
2831 
2832 void dra_device__voice_playback_count_inc(dra_device* pDevice)
2833 {
2834  assert(pDevice != NULL);
2835 
2836  dra_device__lock(pDevice);
2837  {
2838  pDevice->playingVoicesCount += 1;
2839  pDevice->stopOnNextFragment = DR_FALSE;
2840  }
2841  dra_device__unlock(pDevice);
2842 }
2843 
2844 void dra_device__voice_playback_count_dec(dra_device* pDevice)
2845 {
2846  dra_device__lock(pDevice);
2847  {
2848  pDevice->playingVoicesCount -= 1;
2849  }
2850  dra_device__unlock(pDevice);
2851 }
2852 
2853 // The entry point signature is slightly different depending on whether or not we're using Win32 or POSIX threads.
2854 #ifdef _WIN32
2855 DWORD dra_device__thread_proc(LPVOID pData)
2856 #else
2857 void* dra_device__thread_proc(void* pData)
2858 #endif
2859 {
2860  dra_device* pDevice = (dra_device*)pData;
2861  assert(pDevice != NULL);
2862 
2863  // The thread is always open for the life of the device. The loop below will only terminate when a terminate message is received.
2864  for (;;)
2865  {
2866  // Wait for an event...
2867  dra_semaphore_wait(pDevice->threadEventSem);
2868 
2870  //printf("Terminated!\n");
2871  break;
2872  }
2873 
2875  {
2876  if (pDevice->pBackendDevice->type == dra_device_type_playback) {
2877  // The backend device needs to start playing, but we first need to ensure it has an initial chunk of data available.
2878  dra_device__mix_next_fragment(pDevice);
2879  }
2880 
2881  // Start playing the backend device only after the initial fragment has been mixed, and only if it's a playback device.
2882  dra_backend_device_play(pDevice->pBackendDevice);
2883 
2884  // There could be "play" events needing to be posted.
2885  dra_event_queue__post_events(&pDevice->eventQueue);
2886 
2887 
2888  // Wait for the device to request more data...
2889  while (dra_backend_device_wait(pDevice->pBackendDevice)) {
2890  dra_event_queue__post_events(&pDevice->eventQueue);
2891 
2892  if (pDevice->stopOnNextFragment) {
2893  dra_device__stop(pDevice); // <-- Don't break from the loop here. Instead have dra_backend_device_wait() return naturally from the stop notification.
2894  } else {
2895  if (pDevice->pBackendDevice->type == dra_device_type_playback) {
2896  dra_device__mix_next_fragment(pDevice);
2897  } else {
2898  size_t sampleCount;
2899  void* pSampleData = dra_backend_device_map_next_fragment(pDevice->pBackendDevice, &sampleCount);
2900  if (pSampleData != NULL) {
2901  if (pDevice->onSamplesProcessed) {
2902  pDevice->onSamplesProcessed(pDevice, sampleCount, (const float*)pSampleData, pDevice->pUserDataForOnSamplesProcessed);
2903  }
2904 
2905  dra_backend_device_unmap_next_fragment(pDevice->pBackendDevice);
2906  }
2907  }
2908  }
2909  }
2910 
2911  // There could be some events needing to be posted.
2912  dra_event_queue__post_events(&pDevice->eventQueue);
2913  //printf("Stopped!\n");
2914 
2915  // Don't fall through.
2916  continue;
2917  }
2918  }
2919 
2920  return 0;
2921 }
2922 
2923 dra_result dra_device_init_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device* pDevice)
2924 {
2925  if (pDevice == NULL) return DRA_RESULT_INVALID_ARGS;
2926 
2927  dr_bool32 ownsContext = DR_FALSE;
2928  if (pContext == NULL) {
2929  pContext = (dra_context*)malloc(sizeof(*pContext));
2930  if (pContext == NULL) {
2931  return DRA_RESULT_OUT_OF_MEMORY;
2932  }
2933 
2934  dra_result result = dra_context_init(pContext);
2935  if (result != DRA_RESULT_SUCCESS) {
2936  return result;
2937  }
2938 
2939  ownsContext = DR_TRUE;
2940  }
2941 
2942  if (sampleRate == 0) sampleRate = DR_AUDIO_DEFAULT_SAMPLE_RATE;
2943  if (latencyInMilliseconds == 0) latencyInMilliseconds = DR_AUDIO_DEFAULT_LATENCY;
2944 
2945 
2946  dra_result result = DRA_RESULT_SUCCESS;
2947 
2948  memset(pDevice, 0, sizeof(*pDevice));
2949  pDevice->pContext = pContext;
2950  pDevice->ownsContext = ownsContext;
2951 
2952  pDevice->pBackendDevice = dra_backend_device_open(pContext->pBackend, type, deviceID, channels, sampleRate, latencyInMilliseconds);
2953  if (pDevice->pBackendDevice == NULL) {
2955  goto on_error;
2956  }
2957 
2958  pDevice->channels = pDevice->pBackendDevice->channels;
2959  pDevice->sampleRate = pDevice->pBackendDevice->sampleRate;
2960 
2961 
2962  pDevice->mutex = dra_mutex_create();
2963  if (pDevice->mutex == NULL) {
2964  result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_mutex_create().
2965  goto on_error;
2966  }
2967 
2968  pDevice->threadEventSem = dra_semaphore_create(0);
2969  if (pDevice->threadEventSem == NULL) {
2970  result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_semaphore_create().
2971  goto on_error;
2972  }
2973 
2974  result = dra_mixer_create(pDevice, &pDevice->pMasterMixer);
2975  if (result != DRA_RESULT_SUCCESS) {
2976  goto on_error;
2977  }
2978 
2979 
2980  pDevice->eventQueue.lock = dra_mutex_create();
2981  if (pDevice->eventQueue.lock == NULL) {
2982  result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_mutex_create().
2983  goto on_error;
2984  }
2985 
2986 
2987  // Create the thread last to ensure the device is in a valid state as soon as the entry procedure is run.
2988  pDevice->thread = dra_thread_create(dra_device__thread_proc, pDevice);
2989  if (pDevice->thread == NULL) {
2990  result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_thread_create().
2991  goto on_error;
2992  }
2993 
2994  return result;
2995 
2996 on_error:
2997  if (pDevice != NULL) {
2998  if (pDevice->pMasterMixer != NULL) dra_mixer_delete(pDevice->pMasterMixer);
2999  if (pDevice->pBackendDevice != NULL) dra_backend_device_close(pDevice->pBackendDevice);
3000  if (pDevice->threadEventSem != NULL) dra_semaphore_delete(pDevice->threadEventSem);
3001  if (pDevice->mutex != NULL) dra_mutex_delete(pDevice->mutex);
3002 
3003  if (pDevice->ownsContext) {
3004  dra_context_uninit(pDevice->pContext);
3005  free(pDevice->pContext);
3006  }
3007  }
3008 
3009  return result;
3010 }
3011 
3013 {
3014  return dra_device_init_ex(pContext, type, 0, 0, DR_AUDIO_DEFAULT_SAMPLE_RATE, DR_AUDIO_DEFAULT_LATENCY, pDevice);
3015 }
3016 
3017 void dra_device_uninit(dra_device* pDevice)
3018 {
3019  if (pDevice == NULL || pDevice->pContext == NULL) return;
3020 
3021  // Mark the device as closed in order to prevent other threads from doing work after closing.
3022  dra_device__lock(pDevice);
3023  {
3024  pDevice->isClosed = DR_TRUE;
3025  }
3026  dra_device__unlock(pDevice);
3027 
3028  // Stop playback before doing anything else.
3029  dra_device__stop(pDevice);
3030 
3031  // The background thread needs to be terminated at this point.
3032  dra_device__post_event(pDevice, dra_thread_event_type_terminate);
3033  dra_thread_wait_and_delete(pDevice->thread);
3034 
3035 
3036  // At this point the device is marked as closed which should prevent voice's and mixers from being created and deleted. We now need
3037  // to delete the master mixer which in turn will delete all of the attached voices and submixers.
3038  if (pDevice->pMasterMixer != NULL) {
3039  dra_mixer_delete(pDevice->pMasterMixer);
3040  }
3041 
3042 
3043  if (pDevice->pBackendDevice != NULL) {
3044  dra_backend_device_close(pDevice->pBackendDevice);
3045  }
3046 
3047  if (pDevice->threadEventSem != NULL) {
3048  dra_semaphore_delete(pDevice->threadEventSem);
3049  }
3050 
3051  if (pDevice->mutex != NULL) {
3052  dra_mutex_delete(pDevice->mutex);
3053  }
3054 
3055 
3056  if (pDevice->eventQueue.pEvents) {
3057  free(pDevice->eventQueue.pEvents);
3058  }
3059 
3060 
3061  if (pDevice->ownsContext) {
3062  dra_context_uninit(pDevice->pContext);
3063  free(pDevice->pContext);
3064  }
3065 }
3066 
3067 
3068 dra_result dra_device_create_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device** ppDevice)
3069 {
3070  if (ppDevice == NULL) return DRA_RESULT_INVALID_ARGS;
3071  *ppDevice = NULL;
3072 
3073  dra_device* pDevice = (dra_device*)malloc(sizeof(*pDevice));
3074  if (pDevice == NULL) {
3075  return DRA_RESULT_OUT_OF_MEMORY;
3076  }
3077 
3078  dra_result result = dra_device_init_ex(pContext, type, deviceID, channels, sampleRate, latencyInMilliseconds, pDevice);
3079  if (result != DRA_RESULT_SUCCESS) {
3080  free(pDevice);
3081  return result;
3082  }
3083 
3084  *ppDevice = pDevice;
3085  return DRA_RESULT_SUCCESS;
3086 }
3087 
3089 {
3090  return dra_device_create_ex(pContext, type, 0, 0, DR_AUDIO_DEFAULT_SAMPLE_RATE, DR_AUDIO_DEFAULT_LATENCY, ppDevice);
3091 }
3092 
3093 void dra_device_delete(dra_device* pDevice)
3094 {
3095  if (pDevice == NULL) return;
3096 
3097  dra_device_uninit(pDevice);
3098  free(pDevice);
3099 }
3100 
3102 {
3103  if (pDevice == NULL || pDevice->pBackendDevice->type == dra_device_type_playback) return DRA_RESULT_INVALID_ARGS;
3104 
3105  dra_device__play(pDevice);
3106  return DRA_RESULT_SUCCESS;
3107 }
3108 
3110 {
3111  if (pDevice == NULL || pDevice->pBackendDevice->type == dra_device_type_playback) return DRA_RESULT_INVALID_ARGS;
3112 
3113  dra_device__stop(pDevice);
3114  return DRA_RESULT_SUCCESS;
3115 }
3116 
3118 {
3119  if (pDevice == NULL) return;
3120  pDevice->onSamplesProcessed = proc;
3121  pDevice->pUserDataForOnSamplesProcessed = pUserData;
3122 }
3123 
3124 
3125 dra_result dra_mixer_create(dra_device* pDevice, dra_mixer** ppMixer)
3126 {
3127  if (ppMixer == NULL) return DRA_RESULT_INVALID_ARGS;
3128  *ppMixer = NULL;
3129 
3130  if (pDevice == NULL) return DRA_RESULT_INVALID_ARGS;
3131 
3132 
3133  // There needs to be two blocks of memory at the end of the mixer - one for the staging buffer and another for the buffer that
3134  // will store the float32 samples of the voice currently being mixed.
3135  size_t extraDataSize = (size_t)pDevice->pBackendDevice->samplesPerFragment * sizeof(float) * 2;
3136  dra_mixer* pMixer = (dra_mixer*)calloc(1, sizeof(*pMixer) + extraDataSize);
3137  if (pMixer == NULL) {
3138  return DRA_RESULT_OUT_OF_MEMORY;
3139  }
3140 
3141  pMixer->pDevice = pDevice;
3142  pMixer->linearVolume = 1;
3143 
3144  pMixer->pStagingBuffer = pMixer->pData;
3145  pMixer->pNextSamplesToMix = pMixer->pStagingBuffer + pDevice->pBackendDevice->samplesPerFragment;
3146 
3147  // Attach the mixer to the master mixer by default. If the master mixer is null it means we're creating the master mixer itself.
3148  if (pDevice->pMasterMixer != NULL) {
3149  dra_mixer_attach_submixer(pDevice->pMasterMixer, pMixer);
3150  }
3151 
3152  *ppMixer = pMixer;
3153  return DRA_RESULT_SUCCESS;
3154 }
3155 
3156 void dra_mixer_delete(dra_mixer* pMixer)
3157 {
3158  if (pMixer == NULL) return;
3159 
3162 
3163  if (pMixer->pParentMixer != NULL) {
3164  dra_mixer_detach_submixer(pMixer->pParentMixer, pMixer);
3165  }
3166 
3167  free(pMixer);
3168 }
3169 
3170 void dra_mixer_attach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer)
3171 {
3172  if (pMixer == NULL || pSubmixer == NULL) {
3173  return;
3174  }
3175 
3176  if (pSubmixer->pParentMixer != NULL) {
3177  dra_mixer_detach_submixer(pSubmixer->pParentMixer, pSubmixer);
3178  }
3179 
3180 
3181  pSubmixer->pParentMixer = pMixer;
3182 
3183  if (pMixer->pFirstChildMixer == NULL) {
3184  pMixer->pFirstChildMixer = pSubmixer;
3185  pMixer->pLastChildMixer = pSubmixer;
3186  return;
3187  }
3188 
3189  assert(pMixer->pLastChildMixer != NULL);
3190  pMixer->pLastChildMixer->pNextSiblingMixer = pSubmixer;
3191  pSubmixer->pPrevSiblingMixer = pMixer->pLastChildMixer;
3192  pSubmixer->pNextSiblingMixer = NULL;
3193  pMixer->pLastChildMixer = pSubmixer;
3194 }
3195 
3196 void dra_mixer_detach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer)
3197 {
3198  if (pMixer == NULL || pSubmixer == NULL) {
3199  return;
3200  }
3201 
3202  if (pSubmixer->pParentMixer != pMixer) {
3203  return; // Doesn't have the same parent.
3204  }
3205 
3206 
3207  // Detach from parent.
3208  if (pSubmixer->pParentMixer->pFirstChildMixer == pSubmixer) {
3209  pSubmixer->pParentMixer->pFirstChildMixer = pSubmixer->pNextSiblingMixer;
3210  }
3211  if (pSubmixer->pParentMixer->pLastChildMixer == pSubmixer) {
3212  pSubmixer->pParentMixer->pLastChildMixer = pSubmixer->pPrevSiblingMixer;
3213  }
3214 
3215  pSubmixer->pParentMixer = NULL;
3216 
3217 
3218  // Detach from siblings.
3219  if (pSubmixer->pPrevSiblingMixer) {
3220  pSubmixer->pPrevSiblingMixer->pNextSiblingMixer = pSubmixer->pNextSiblingMixer;
3221  }
3222  if (pSubmixer->pNextSiblingMixer) {
3223  pSubmixer->pNextSiblingMixer->pPrevSiblingMixer = pSubmixer->pPrevSiblingMixer;
3224  }
3225 
3226  pSubmixer->pNextSiblingMixer = NULL;
3227  pSubmixer->pPrevSiblingMixer = NULL;
3228 }
3229 
3231 {
3232  if (pMixer == NULL) {
3233  return;
3234  }
3235 
3236  while (pMixer->pFirstChildMixer != NULL) {
3238  }
3239 }
3240 
3241 void dra_mixer_attach_voice(dra_mixer* pMixer, dra_voice* pVoice)
3242 {
3243  if (pMixer == NULL || pVoice == NULL) {
3244  return;
3245  }
3246 
3247  if (pVoice->pMixer != NULL) {
3248  dra_mixer_detach_voice(pVoice->pMixer, pVoice);
3249  }
3250 
3251  pVoice->pMixer = pMixer;
3252 
3253  if (pMixer->pFirstVoice == NULL) {
3254  pMixer->pFirstVoice = pVoice;
3255  pMixer->pLastVoice = pVoice;
3256  return;
3257  }
3258 
3259  // Attach the voice to the end of the list.
3260  pVoice->pPrevVoice = pMixer->pLastVoice;
3261  pVoice->pNextVoice = NULL;
3262 
3263  pMixer->pLastVoice->pNextVoice = pVoice;
3264  pMixer->pLastVoice = pVoice;
3265 }
3266 
3267 void dra_mixer_detach_voice(dra_mixer* pMixer, dra_voice* pVoice)
3268 {
3269  if (pMixer == NULL || pVoice == NULL) {
3270  return;
3271  }
3272 
3273 
3274  // Detach from mixer.
3275  if (pMixer->pFirstVoice == pVoice) {
3276  pMixer->pFirstVoice = pMixer->pFirstVoice->pNextVoice;
3277  }
3278  if (pMixer->pLastVoice == pVoice) {
3279  pMixer->pLastVoice = pMixer->pLastVoice->pPrevVoice;
3280  }
3281 
3282  pVoice->pMixer = NULL;
3283 
3284 
3285  // Remove from list.
3286  if (pVoice->pNextVoice) {
3287  pVoice->pNextVoice->pPrevVoice = pVoice->pPrevVoice;
3288  }
3289  if (pVoice->pPrevVoice) {
3290  pVoice->pPrevVoice->pNextVoice = pVoice->pNextVoice;
3291  }
3292 
3293  pVoice->pNextVoice = NULL;
3294  pVoice->pPrevVoice = NULL;
3295 
3296 }
3297 
3299 {
3300  if (pMixer == NULL) {
3301  return;
3302  }
3303 
3304  while (pMixer->pFirstVoice) {
3305  dra_mixer_detach_voice(pMixer, pMixer->pFirstVoice);
3306  }
3307 }
3308 
3309 void dra_mixer_set_volume(dra_mixer* pMixer, float linearVolume)
3310 {
3311  if (pMixer == NULL) {
3312  return;
3313  }
3314 
3315  pMixer->linearVolume = linearVolume;
3316 }
3317 
3318 float dra_mixer_get_volume(dra_mixer* pMixer)
3319 {
3320  if (pMixer == NULL) {
3321  return 0;
3322  }
3323 
3324  return pMixer->linearVolume;
3325 }
3326 
3327 size_t dra_mixer_mix_next_frames(dra_mixer* pMixer, size_t frameCount)
3328 {
3329  if (pMixer == NULL) {
3330  return 0;
3331  }
3332 
3333  if (pMixer->pFirstVoice == NULL && pMixer->pFirstChildMixer == NULL) {
3334  return 0;
3335  }
3336 
3337  if (dra_mixer_is_paused(pMixer)) {
3338  return 0;
3339  }
3340 
3341 
3342  size_t framesMixed = 0;
3343 
3344  // Mixing works by simply adding together the sample data of each voice and submixer. We just start at 0 and then
3345  // just accumulate each one.
3346  memset(pMixer->pStagingBuffer, 0, frameCount * pMixer->pDevice->channels * sizeof(float));
3347 
3348  // Voices first. Doesn't really matter if we do voices or submixers first.
3349  for (dra_voice* pVoice = pMixer->pFirstVoice; pVoice != NULL; pVoice = pVoice->pNextVoice)
3350  {
3351  if (pVoice->isPlaying) {
3352  size_t framesJustRead = dra_voice__next_frames(pVoice, frameCount, pMixer->pNextSamplesToMix);
3353  for (size_t i = 0; i < framesJustRead * pMixer->pDevice->channels; ++i) {
3354  pMixer->pStagingBuffer[i] += (pMixer->pNextSamplesToMix[i] * pVoice->linearVolume);
3355  }
3356 
3357  // Has the end of the voice's buffer been reached?
3358  if (framesJustRead < frameCount)
3359  {
3360  // We'll get here if the end of the voice's buffer has been reached. The voice needs to be forcefully stopped to
3361  // ensure the device is aware of it and is able to put itself into a dormant state if necessary. Also note that
3362  // the playback position is moved back to start. The rationale for this is that it's a little bit more useful than
3363  // just leaving the playback position sitting on the end. Also it allows an application to restart playback with
3364  // a single call to dra_voice_play() without having to explicitly set the playback position.
3365  pVoice->currentReadPos = 0;
3366  dra_voice_stop(pVoice);
3367  }
3368 
3369  if (framesMixed < framesJustRead) {
3370  framesMixed = framesJustRead;
3371  }
3372  }
3373  }
3374 
3375  // Submixers.
3376  for (dra_mixer* pSubmixer = pMixer->pFirstChildMixer; pSubmixer != NULL; pSubmixer = pSubmixer->pNextSiblingMixer)
3377  {
3378  size_t framesJustMixed = dra_mixer_mix_next_frames(pSubmixer, frameCount);
3379  for (size_t i = 0; i < framesJustMixed * pMixer->pDevice->channels; ++i) {
3380  pMixer->pStagingBuffer[i] += pSubmixer->pStagingBuffer[i];
3381  }
3382 
3383  if (framesMixed < framesJustMixed) {
3384  framesMixed = framesJustMixed;
3385  }
3386  }
3387 
3388 
3389  // At this point the mixer's effects and volume need to be applied to each sample.
3390  size_t samplesMixed = framesMixed * pMixer->pDevice->channels;
3391  for (size_t i = 0; i < samplesMixed; ++i) {
3392  pMixer->pStagingBuffer[i] *= pMixer->linearVolume;
3393  }
3394 
3395 
3396  // Finally we need to ensure every samples is clamped to -1 to 1. There are two easy ways to do this: clamp or normalize. For now I'm just
3397  // clamping to keep it simple, but it might be valuable to make this configurable.
3398  for (size_t i = 0; i < framesMixed * pMixer->pDevice->channels; ++i)
3399  {
3400  // TODO: Investigate using SSE here (MINPS/MAXPS)
3401  // TODO: Investigate if the backends clamp the samples themselves, thus making this redundant.
3402  if (pMixer->pStagingBuffer[i] < -1) {
3403  pMixer->pStagingBuffer[i] = -1;
3404  } else if (pMixer->pStagingBuffer[i] > 1) {
3405  pMixer->pStagingBuffer[i] = 1;
3406  }
3407  }
3408 
3409  return framesMixed;
3410 }
3411 
3413 {
3414  if (pMixer == NULL) {
3415  return 0;
3416  }
3417 
3418  size_t count = 0;
3419  for (dra_voice* pVoice = pMixer->pFirstVoice; pVoice != NULL; pVoice = pVoice->pNextVoice) {
3420  count += 1;
3421  }
3422 
3423  return count;
3424 }
3425 
3427 {
3428  if (pMixer == NULL) {
3429  return 0;
3430  }
3431 
3432  size_t count = dra_mixer_count_attached_voices(pMixer);
3433 
3434  // Children.
3435  for (dra_mixer* pChildMixer = pMixer->pFirstChildMixer; pChildMixer != NULL; pChildMixer = pChildMixer->pNextSiblingMixer) {
3437  }
3438 
3439  return count;
3440 }
3441 
3442 size_t dra_mixer_gather_attached_voices(dra_mixer* pMixer, dra_voice** ppVoicesOut)
3443 {
3444  if (pMixer == NULL) {
3445  return 0;
3446  }
3447 
3448  if (ppVoicesOut == NULL) {
3449  return dra_mixer_count_attached_voices(pMixer);
3450  }
3451 
3452  size_t count = 0;
3453  for (dra_voice* pVoice = pMixer->pFirstVoice; pVoice != NULL; pVoice = pVoice->pNextVoice) {
3454  ppVoicesOut[count] = pVoice;
3455  count += 1;
3456  }
3457 
3458  return count;
3459 }
3460 
3461 size_t dra_mixer_gather_attached_voices_recursive(dra_mixer* pMixer, dra_voice** ppVoicesOut)
3462 {
3463  if (pMixer == NULL) {
3464  return 0;
3465  }
3466 
3467  if (ppVoicesOut == NULL) {
3469  }
3470 
3471  size_t count = dra_mixer_gather_attached_voices(pMixer, ppVoicesOut);
3472 
3473  // Children.
3474  for (dra_mixer* pChildMixer = pMixer->pFirstChildMixer; pChildMixer != NULL; pChildMixer = pChildMixer->pNextSiblingMixer) {
3475  count += dra_mixer_gather_attached_voices_recursive(pChildMixer, ppVoicesOut + count);
3476  }
3477 
3478  return count;
3479 }
3480 
3481 
3482 void dra_mixer_pause(dra_mixer* pMixer)
3483 {
3484  if (pMixer == NULL) return;
3485  pMixer->flags |= DRA_MIXER_FLAG_PAUSED;
3486 }
3487 
3488 void dra_mixer_resume(dra_mixer* pMixer)
3489 {
3490  if (pMixer == NULL) return;
3491  pMixer->flags &= ~DRA_MIXER_FLAG_PAUSED;
3492 
3493  // When the mixer was paused it may have resulted in no audio being played which means dr_audio will have stopped the device
3494  // to save CPU usage. We need to make sure we wake up the device.
3495  dra_device__play(pMixer->pDevice);
3496 }
3497 
3499 {
3500  if (pMixer == NULL) return DR_FALSE;
3501  return (pMixer->flags & DRA_MIXER_FLAG_PAUSED) != 0;
3502 }
3503 
3504 
3505 
3506 dra_result dra_voice_create(dra_device* pDevice, dra_format format, unsigned int channels, unsigned int sampleRate, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice)
3507 {
3508  if (ppVoice == NULL) return DRA_RESULT_INVALID_ARGS;
3509  *ppVoice = NULL;
3510 
3511  if (pDevice == NULL || sizeInBytes == 0) return DRA_RESULT_INVALID_ARGS;
3512 
3513 
3514  // The number of bytes must be a multiple of the size of a frame.
3515  size_t bytesPerSample = dra_get_bytes_per_sample_by_format(format);
3516  if ((sizeInBytes % (bytesPerSample * channels)) != 0) {
3517  return DRA_RESULT_INVALID_ARGS;
3518  }
3519 
3520 
3521  dra_voice* pVoice = (dra_voice*)calloc(1, sizeof(*pVoice) + sizeInBytes);
3522  if (pVoice == NULL) {
3523  return DRA_RESULT_OUT_OF_MEMORY;
3524  }
3525 
3526  pVoice->pDevice = pDevice;
3527  pVoice->pMixer = NULL;
3528  pVoice->format = format;
3529  pVoice->channels = channels;
3530  pVoice->sampleRate = sampleRate;
3531  pVoice->linearVolume = 1;
3532  pVoice->isPlaying = DR_FALSE;
3533  pVoice->isLooping = DR_FALSE;
3534  pVoice->frameCount = sizeInBytes / (bytesPerSample * channels);
3535  pVoice->currentReadPos = 0;
3536  pVoice->sizeInBytes = sizeInBytes;
3537  pVoice->pNextVoice = NULL;
3538  pVoice->pPrevVoice = NULL;
3539 
3540  if (pInitialData != NULL) {
3541  memcpy(pVoice->pData, pInitialData, sizeInBytes);
3542  } else {
3543  //memset(pVoice->pData, 0, sizeInBytes); // <-- This is already zeroed by the calloc() above, but leaving this comment here for emphasis.
3544  }
3545 
3546 
3547  // Sample rate conversion.
3548  if (sampleRate == pDevice->sampleRate) {
3550  } else {
3552  }
3553 
3554 
3555  // Attach the voice to the master mixer by default.
3556  if (pDevice->pMasterMixer != NULL) {
3557  dra_mixer_attach_voice(pDevice->pMasterMixer, pVoice);
3558  }
3559 
3560  *ppVoice = pVoice;
3561  return DRA_RESULT_SUCCESS;
3562 }
3563 
3564 dra_result dra_voice_create_compatible(dra_device* pDevice, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice)
3565 {
3566  return dra_voice_create(pDevice, dra_format_f32, pDevice->channels, pDevice->sampleRate, sizeInBytes, pInitialData, ppVoice);
3567 }
3568 
3569 void dra_voice_delete(dra_voice* pVoice)
3570 {
3571  if (pVoice == NULL) return;
3572 
3573  // The voice needs to be stopped...
3574  dra_voice_stop(pVoice);
3575 
3576  // ... and all pending events need to be cancelled to ensure the application isn't notified of an event of a deleted voice.
3577  dra_event_queue__cancel_events_of_voice(&pVoice->pDevice->eventQueue, pVoice);
3578 
3579  if (pVoice->pMixer != NULL) {
3580  dra_mixer_detach_voice(pVoice->pMixer, pVoice);
3581  }
3582 
3583  free(pVoice);
3584 }
3585 
3586 void dra_voice_play(dra_voice* pVoice, dr_bool32 loop)
3587 {
3588  if (pVoice == NULL) {
3589  return;
3590  }
3591 
3592  if (!dra_voice_is_playing(pVoice)) {
3593  dra_device__voice_playback_count_inc(pVoice->pDevice);
3594  } else {
3595  if (dra_voice_is_looping(pVoice) == loop) {
3596  return; // Nothing has changed - don't need to do anything.
3597  }
3598  }
3599 
3600  pVoice->isPlaying = DR_TRUE;
3601  pVoice->isLooping = loop;
3602 
3603  dra_event_queue__schedule_event(&pVoice->pDevice->eventQueue, &pVoice->playEvent);
3604 
3605  // When playing a voice we need to ensure the backend device is playing.
3606  dra_device__play(pVoice->pDevice);
3607 }
3608 
3609 void dra_voice_stop(dra_voice* pVoice)
3610 {
3611  if (pVoice == NULL) {
3612  return;
3613  }
3614 
3615  if (!dra_voice_is_playing(pVoice)) {
3616  return; // The voice is already stopped.
3617  }
3618 
3619  dra_device__voice_playback_count_dec(pVoice->pDevice);
3620 
3621  pVoice->isPlaying = DR_FALSE;
3622  pVoice->isLooping = DR_FALSE;
3623 
3624  dra_event_queue__schedule_event(&pVoice->pDevice->eventQueue, &pVoice->stopEvent);
3625 }
3626 
3628 {
3629  if (pVoice == NULL) {
3630  return DR_FALSE;
3631  }
3632 
3633  return pVoice->isPlaying;
3634 }
3635 
3637 {
3638  if (pVoice == NULL) {
3639  return DR_FALSE;
3640  }
3641 
3642  return pVoice->isLooping;
3643 }
3644 
3645 
3646 void dra_voice_set_volume(dra_voice* pVoice, float linearVolume)
3647 {
3648  if (pVoice == NULL) {
3649  return;
3650  }
3651 
3652  pVoice->linearVolume = linearVolume;
3653 }
3654 
3655 float dra_voice_get_volume(dra_voice* pVoice)
3656 {
3657  if (pVoice == NULL) {
3658  return 0;
3659  }
3660 
3661  return pVoice->linearVolume;
3662 }
3663 
3664 
3665 void dra_f32_to_f32(float* pOut, const float* pIn, size_t sampleCount)
3666 {
3667  memcpy(pOut, pIn, sampleCount * sizeof(float));
3668 }
3669 
3670 void dra_s32_to_f32(float* pOut, const dr_int32* pIn, size_t sampleCount)
3671 {
3672  // TODO: Try SSE-ifying this.
3673  for (size_t i = 0; i < sampleCount; ++i) {
3674  pOut[i] = pIn[i] / 2147483648.0f;
3675  }
3676 }
3677 
3678 void dra_s24_to_f32(float* pOut, const dr_uint8* pIn, size_t sampleCount)
3679 {
3680  // TODO: Try SSE-ifying this.
3681  for (size_t i = 0; i < sampleCount; ++i) {
3682  dr_uint8 s0 = pIn[i*3 + 0];
3683  dr_uint8 s1 = pIn[i*3 + 1];
3684  dr_uint8 s2 = pIn[i*3 + 2];
3685 
3686  dr_int32 sample32 = (dr_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
3687  pOut[i] = sample32 / 2147483648.0f;
3688  }
3689 }
3690 
3691 void dra_s16_to_f32(float* pOut, const dr_int16* pIn, size_t sampleCount)
3692 {
3693  // TODO: Try SSE-ifying this.
3694  for (size_t i = 0; i < sampleCount; ++i) {
3695  pOut[i] = pIn[i] / 32768.0f;
3696  }
3697 }
3698 
3699 void dra_u8_to_f32(float* pOut, const dr_uint8* pIn, size_t sampleCount)
3700 {
3701  // TODO: Try SSE-ifying this.
3702  for (size_t i = 0; i < sampleCount; ++i) {
3703  pOut[i] = (pIn[i] / 127.5f) - 1;
3704  }
3705 }
3706 
3707 
3708 // Generic sample format conversion function. To add basic, unoptimized support for a new format, just add it to this function.
3709 // Format-specific optimizations need to be implemented specifically for each format, at a higher level.
3710 void dra_to_f32(float* pOut, const void* pIn, size_t sampleCount, dra_format format)
3711 {
3712  switch (format)
3713  {
3714  case dra_format_f32:
3715  {
3716  dra_f32_to_f32(pOut, (float*)pIn, sampleCount);
3717  } break;
3718 
3719  case dra_format_s32:
3720  {
3721  dra_s32_to_f32(pOut, (dr_int32*)pIn, sampleCount);
3722  } break;
3723 
3724  case dra_format_s24:
3725  {
3726  dra_s24_to_f32(pOut, (dr_uint8*)pIn, sampleCount);
3727  } break;
3728 
3729  case dra_format_s16:
3730  {
3731  dra_s16_to_f32(pOut, (dr_int16*)pIn, sampleCount);
3732  } break;
3733 
3734  case dra_format_u8:
3735  {
3736  dra_u8_to_f32(pOut, (dr_uint8*)pIn, sampleCount);
3737  } break;
3738 
3739  default: break; // Unknown or unsupported format.
3740  }
3741 }
3742 
3743 
3744 // Notes on channel shuffling.
3745 //
3746 // Channels are shuffled frame-by-frame by first normalizing everything to floats. Then, a shuffling function is called to
3747 // shuffle the channels in a particular way depending on the destination and source channel assignments.
3748 void dra_shuffle_channels__generic_inc(float* pOut, const float* pIn, unsigned int channelsOut, unsigned int channelsIn)
3749 {
3750  // This is the generic function for taking a frame with a smaller number of channels and expanding it to a frame with
3751  // a greater number of channels. This just copies the first channelsIn samples to the output and silences the remaing
3752  // channels.
3753  assert(channelsOut > channelsIn);
3754 
3755  for (unsigned int i = 0; i < channelsIn; ++i) {
3756  pOut[i] = pIn[i];
3757  }
3758 
3759  // Silence the left over.
3760  for (unsigned int i = channelsIn; i < channelsOut; ++i) {
3761  pOut[i] = 0;
3762  }
3763 }
3764 
3765 void dra_shuffle_channels__generic_dec(float* pOut, const float* pIn, unsigned int channelsOut, unsigned int channelsIn)
3766 {
3767  // This is the opposite of dra_shuffle_channels__generic_inc() - it decreases the number of channels in the input stream
3768  // by simply stripping the excess channels.
3769  assert(channelsOut < channelsIn);
3770  (void)channelsIn;
3771 
3772  // Just copy the first channelsOut.
3773  for (unsigned int i = 0; i < channelsOut; ++i) {
3774  pOut[i] = pIn[i];
3775  }
3776 }
3777 
3778 void dra_shuffle_channels(float* pOut, const float* pIn, unsigned int channelsOut, unsigned int channelsIn)
3779 {
3780  assert(channelsOut != 0);
3781  assert(channelsIn != 0);
3782 
3783  if (channelsOut == channelsIn) {
3784  for (unsigned int i = 0; i < channelsOut; ++i) {
3785  pOut[i] = pIn[i];
3786  }
3787  } else {
3788  switch (channelsIn)
3789  {
3790  case 1:
3791  {
3792  // Mono input. This is a simple case - just copy the value of the mono channel to every output channel.
3793  for (unsigned int i = 0; i < channelsOut; ++i) {
3794  pOut[i] = pIn[0];
3795  }
3796  } break;
3797 
3798  case 2:
3799  {
3800  // Stereo input.
3801  if (channelsOut == 1)
3802  {
3803  // For mono output, just average.
3804  pOut[0] = (pIn[0] + pIn[1]) * 0.5f;
3805  }
3806  else
3807  {
3808  // TODO: Do a specialized implementation for all major formats, in particluar 5.1.
3809  dra_shuffle_channels__generic_inc(pOut, pIn, channelsOut, channelsIn);
3810  }
3811  } break;
3812 
3813  default:
3814  {
3815  if (channelsOut == 1)
3816  {
3817  // For mono output, just average each sample.
3818  float total = 0;
3819  for (unsigned int i = 0; i < channelsIn; ++i) {
3820  total += pIn[i];
3821  }
3822 
3823  pOut[0] = total / channelsIn;
3824  }
3825  else
3826  {
3827  if (channelsOut > channelsIn) {
3828  dra_shuffle_channels__generic_inc(pOut, pIn, channelsOut, channelsIn);
3829  } else {
3830  dra_shuffle_channels__generic_dec(pOut, pIn, channelsOut, channelsIn);
3831  }
3832  }
3833  } break;
3834  }
3835  }
3836 }
3837 
3838 float dra_voice__get_sample_rate_factor(dra_voice* pVoice)
3839 {
3840  if (pVoice == NULL) {
3841  return 1;
3842  }
3843 
3844  return pVoice->pDevice->sampleRate / (float)pVoice->sampleRate;
3845 }
3846 
3847 void dra_voice__unsignal_playback_events(dra_voice* pVoice)
3848 {
3849  // This function will be called when the voice has looped back to the start. In this case the playback notification events need
3850  // to be marked as unsignaled so that they're able to be fired again.
3851  for (size_t i = 0; i < pVoice->playbackEventCount; ++i) {
3853  }
3854 }
3855 
3856 float* dra_voice__next_frame(dra_voice* pVoice)
3857 {
3858  if (pVoice == NULL) {
3859  return NULL;
3860  }
3861 
3862 
3863  if (pVoice->format == dra_format_f32 && pVoice->sampleRate == pVoice->pDevice->sampleRate && pVoice->channels == pVoice->pDevice->channels)
3864  {
3865  // Fast path.
3866  if (!pVoice->isLooping && pVoice->currentReadPos == pVoice->frameCount) {
3867  return NULL; // At the end of a non-looping voice.
3868  }
3869 
3870  float* pOut = (float*)pVoice->pData + (pVoice->currentReadPos * pVoice->channels);
3871 
3872  pVoice->currentReadPos += 1;
3873  if (pVoice->currentReadPos == pVoice->frameCount && pVoice->isLooping) {
3874  pVoice->currentReadPos = 0;
3875  dra_voice__unsignal_playback_events(pVoice);
3876  }
3877 
3878  return pOut;
3879  }
3880  else
3881  {
3882  size_t bytesPerSample = dra_get_bytes_per_sample_by_format(pVoice->format);
3883 
3884  if (pVoice->sampleRate == pVoice->pDevice->sampleRate)
3885  {
3886  // Same sample rate. This path isn't ideal, but it's not too bad since there is no need for sample rate conversion.
3887  if (!pVoice->isLooping && pVoice->currentReadPos == pVoice->frameCount) {
3888  return NULL; // At the end of a non-looping voice.
3889  }
3890 
3891  float* pOut = pVoice->convertedFrame;
3892 
3893  unsigned int channelsIn = pVoice->channels;
3894  unsigned int channelsOut = pVoice->pDevice->channels;
3895  float tempFrame[DR_AUDIO_MAX_CHANNEL_COUNT];
3896  dr_uint64 sampleOffset = pVoice->currentReadPos * channelsIn;
3897 
3898  // The conversion is done differently depending on the format of the voice.
3899  if (pVoice->format == dra_format_f32) {
3900  dra_shuffle_channels(pOut, (float*)pVoice->pData + sampleOffset, channelsOut, channelsIn);
3901  } else {
3902  sampleOffset = pVoice->currentReadPos * (channelsIn * bytesPerSample);
3903  dra_to_f32(tempFrame, (dr_uint8*)pVoice->pData + sampleOffset, channelsIn, pVoice->format);
3904  dra_shuffle_channels(pOut, tempFrame, channelsOut, channelsIn);
3905  }
3906 
3907  pVoice->currentReadPos += 1;
3908  if (pVoice->currentReadPos == pVoice->frameCount && pVoice->isLooping) {
3909  pVoice->currentReadPos = 0;
3910  dra_voice__unsignal_playback_events(pVoice);
3911  }
3912 
3913  return pOut;
3914  }
3915  else
3916  {
3917  // Different sample rate. This is the truly slow path.
3918  unsigned int sampleRateIn = pVoice->sampleRate;
3919  unsigned int sampleRateOut = pVoice->pDevice->sampleRate;
3920  unsigned int channelsIn = pVoice->channels;
3921  unsigned int channelsOut = pVoice->pDevice->channels;
3922 
3923  float factor = (float)sampleRateOut / (float)sampleRateIn;
3924  float invfactor = 1 / factor;
3925 
3926  if (!pVoice->isLooping && pVoice->currentReadPos >= (pVoice->frameCount * factor)) {
3927  return NULL; // At the end of a non-looping voice.
3928  }
3929 
3930  float* pOut = pVoice->convertedFrame;
3931 
3932  if (pVoice->src.algorithm == dra_src_algorithm_linear) {
3933  // Linear filtering.
3934  float timeIn = pVoice->currentReadPos * invfactor;
3935  dr_uint64 prevFrameIndexIn = (dr_uint64)(timeIn);
3936  dr_uint64 nextFrameIndexIn = prevFrameIndexIn + 1;
3937  if (nextFrameIndexIn >= pVoice->frameCount) {
3938  nextFrameIndexIn = pVoice->frameCount-1;
3939  }
3940 
3941  if (prevFrameIndexIn != pVoice->src.data.linear.prevFrameIndex)
3942  {
3943  dr_uint64 sampleOffset = prevFrameIndexIn * (channelsIn * bytesPerSample);
3944  dra_to_f32(pVoice->src.data.linear.prevFrame, (dr_uint8*)pVoice->pData + sampleOffset, channelsIn, pVoice->format);
3945 
3946  sampleOffset = nextFrameIndexIn * (channelsIn * bytesPerSample);
3947  dra_to_f32(pVoice->src.data.linear.nextFrame, (dr_uint8*)pVoice->pData + sampleOffset, channelsIn, pVoice->format);
3948 
3949  pVoice->src.data.linear.prevFrameIndex = prevFrameIndexIn;
3950  }
3951 
3952  float alpha = timeIn - prevFrameIndexIn;
3953  float frame[DR_AUDIO_MAX_CHANNEL_COUNT];
3954  for (unsigned int i = 0; i < pVoice->channels; ++i) {
3955  frame[i] = dra_mixf(pVoice->src.data.linear.prevFrame[i], pVoice->src.data.linear.nextFrame[i], alpha);
3956  }
3957 
3958  dra_shuffle_channels(pOut, frame, channelsOut, channelsIn);
3959  }
3960 
3961 
3962  pVoice->currentReadPos += 1;
3963  if (pVoice->currentReadPos >= (pVoice->frameCount * factor) && pVoice->isLooping) {
3964  pVoice->currentReadPos = 0;
3965  dra_voice__unsignal_playback_events(pVoice);
3966  }
3967 
3968  return pOut;
3969  }
3970  }
3971 }
3972 
3973 size_t dra_voice__next_frames(dra_voice* pVoice, size_t frameCount, float* pSamplesOut)
3974 {
3975  // TODO: Check for the fast path and do a bulk copy rather than frame-by-frame. Don't forget playback event handling.
3976 
3977  size_t framesRead = 0;
3978 
3979  dr_uint64 prevReadPosLocal = pVoice->currentReadPos * pVoice->channels;
3980 
3981  float* pNextFrame = NULL;
3982  while ((framesRead < frameCount) && (pNextFrame = dra_voice__next_frame(pVoice)) != NULL) {
3983  memcpy(pSamplesOut, pNextFrame, pVoice->pDevice->channels * sizeof(float));
3984  pSamplesOut += pVoice->pDevice->channels;
3985  framesRead += 1;
3986  }
3987 
3988  float sampleRateFactor = dra_voice__get_sample_rate_factor(pVoice);
3989  dr_uint64 totalSampleCount = (dr_uint64)((pVoice->frameCount * pVoice->channels) * sampleRateFactor);
3990 
3991  // Now we need to check if we've got past any notification events and post events for them if so.
3992  dr_uint64 currentReadPosLocal = (prevReadPosLocal + (framesRead * pVoice->channels)) % totalSampleCount;
3993  for (size_t i = 0; i < pVoice->playbackEventCount; ++i) {
3994  dra__event* pEvent = &pVoice->playbackEvents[i];
3995  if (!pEvent->hasBeenSignaled && pEvent->sampleIndex*sampleRateFactor <= currentReadPosLocal) {
3996  dra_event_queue__schedule_event(&pVoice->pDevice->eventQueue, pEvent); // <-- TODO: Check that this really needs to be scheduled. Can probably call it directly and avoid a mutex lock/unlock.
3997  pEvent->hasBeenSignaled = DR_TRUE;
3998  }
3999  }
4000 
4001  return framesRead;
4002 }
4003 
4004 
4005 void dra_voice_set_on_stop(dra_voice* pVoice, dra_event_proc proc, void* pUserData)
4006 {
4007  if (pVoice == NULL) {
4008  return;
4009  }
4010 
4012  pVoice->stopEvent.pUserData = pUserData;
4013  pVoice->stopEvent.sampleIndex = 0;
4014  pVoice->stopEvent.proc = proc;
4015  pVoice->stopEvent.pVoice = pVoice;
4016 }
4017 
4018 void dra_voice_set_on_play(dra_voice* pVoice, dra_event_proc proc, void* pUserData)
4019 {
4020  if (pVoice == NULL) {
4021  return;
4022  }
4023 
4025  pVoice->playEvent.pUserData = pUserData;
4026  pVoice->playEvent.sampleIndex = 0;
4027  pVoice->playEvent.proc = proc;
4028  pVoice->playEvent.pVoice = pVoice;
4029 }
4030 
4031 dr_bool32 dra_voice_add_playback_event(dra_voice* pVoice, dr_uint64 sampleIndex, dr_uint64 eventID, dra_event_proc proc, void* pUserData)
4032 {
4033  if (pVoice == NULL) {
4034  return DR_FALSE;
4035  }
4036 
4038  return DR_FALSE;
4039  }
4040 
4041  pVoice->playbackEvents[pVoice->playbackEventCount].id = eventID;
4042  pVoice->playbackEvents[pVoice->playbackEventCount].pUserData = pUserData;
4043  pVoice->playbackEvents[pVoice->playbackEventCount].sampleIndex = sampleIndex;
4044  pVoice->playbackEvents[pVoice->playbackEventCount].proc = proc;
4045  pVoice->playbackEvents[pVoice->playbackEventCount].pVoice = pVoice;
4046 
4047  pVoice->playbackEventCount += 1;
4048  return DR_TRUE;
4049 }
4050 
4052 {
4053  if (pVoice == NULL) {
4054  return;
4055  }
4056 
4057  for (size_t i = 0; i < pVoice->playbackEventCount; /* DO NOTHING */) {
4058  if (pVoice->playbackEvents[i].id == eventID) {
4059  memmove(&pVoice->playbackEvents[i], &pVoice->playbackEvents[i + 1], (pVoice->playbackEventCount - (i+1)) * sizeof(dra__event));
4060  pVoice->playbackEventCount -= 1;
4061  } else {
4062  i += 1;
4063  }
4064  }
4065 }
4066 
4067 
4069 {
4070  if (pVoice == NULL) {
4071  return 0;
4072  }
4073 
4074  return (dr_uint64)((pVoice->currentReadPos * pVoice->channels) / dra_voice__get_sample_rate_factor(pVoice));
4075 }
4076 
4077 void dra_voice_set_playback_position(dra_voice* pVoice, dr_uint64 sampleIndex)
4078 {
4079  if (pVoice == NULL) {
4080  return;
4081  }
4082 
4083  // When setting the playback position it's important to consider sample-rate conversion. Sample rate conversion will often depend on
4084  // previous and next frames in order to calculate the next frame. Therefore, depending on the type of SRC we're using, we'll need to
4085  // seek a few frames earlier and then re-fill the delay-line buffer used for a particular SRC algorithm.
4086  dr_uint64 localFramePos = sampleIndex / pVoice->channels;
4087  pVoice->currentReadPos = (dr_uint64)(localFramePos * dra_voice__get_sample_rate_factor(pVoice));
4088 
4089  if (pVoice->sampleRate != pVoice->pDevice->sampleRate) {
4090  if (pVoice->src.algorithm == dra_src_algorithm_linear) {
4091  // Linear filtering just requires the previous frame. However, this is handled at mixing time for linear SRC so all we need to
4092  // do is ensure the mixing function is aware that the previous frame need to be re-read. This is done by simply resetting the
4093  // variable the mixer uses to determine whether or not the previous frame needs to be re-read.
4094  pVoice->src.data.linear.prevFrameIndex = 0;
4095  }
4096  }
4097 
4098  // TODO: Normalize the hasBeenSignaled properties of events.
4099 }
4100 
4101 
4103 {
4104  if (pVoice == NULL) {
4105  return NULL;
4106  }
4107 
4108  dr_uint64 totalSampleCount = pVoice->frameCount * pVoice->channels;
4109  if (sample > totalSampleCount) {
4110  return NULL;
4111  }
4112 
4113  return pVoice->pData + (sample * dra_get_bytes_per_sample_by_format(pVoice->format));
4114 }
4115 
4116 void dra_voice_write_silence(dra_voice* pVoice, dr_uint64 sampleOffset, dr_uint64 sampleCount)
4117 {
4118  void* pData = dra_voice_get_buffer_ptr_by_sample(pVoice, sampleOffset);
4119  if (pData == NULL) {
4120  return;
4121  }
4122 
4123  dr_uint64 totalSamplesRemaining = (pVoice->frameCount * pVoice->channels) - sampleOffset;
4124  if (sampleCount > totalSamplesRemaining) {
4125  sampleCount = totalSamplesRemaining;
4126  }
4127 
4128  memset(pData, 0, (size_t)(sampleCount * dra_get_bytes_per_sample_by_format(pVoice->format)));
4129 }
4130 
4131 
4132 
4133 
4134 
4135 
4137 
4138 void dra_free(void* p)
4139 {
4140  free(p);
4141 }
4142 
4143 unsigned int dra_get_bits_per_sample_by_format(dra_format format)
4144 {
4145  unsigned int lookup[] = {
4146  8, // dra_format_u8
4147  16, // dra_format_s16
4148  24, // dra_format_s24
4149  32, // dra_format_s32
4150  32 // dra_format_f32
4151  };
4152 
4153  return lookup[format];
4154 }
4155 
4157 {
4158  return dra_get_bits_per_sample_by_format(format) / 8;
4159 }
4160 
4161 
4163 
4164 #ifndef DR_AUDIO_NO_STDIO
4165 static FILE* dra__fopen(const char* filePath)
4166 {
4167  FILE* pFile;
4168 #ifdef _MSC_VER
4169  if (fopen_s(&pFile, filePath, "rb") != 0) {
4170  return NULL;
4171  }
4172 #else
4173  pFile = fopen(filePath, "rb");
4174  if (pFile == NULL) {
4175  return NULL;
4176  }
4177 #endif
4178 
4179  return (FILE*)pFile;
4180 }
4181 #endif //DR_AUDIO_NO_STDIO
4182 
4183 
4185 
4186 #ifdef DR_AUDIO_HAS_WAV
4187 size_t dra_decoder_on_read__wav(void* pUserData, void* pDataOut, size_t bytesToRead)
4188 {
4189  dra_decoder* pDecoder = (dra_decoder*)pUserData;
4190  assert(pDecoder != NULL);
4191  assert(pDecoder->onRead != NULL);
4192 
4193  return pDecoder->onRead(pDecoder->pUserData, pDataOut, bytesToRead);
4194 }
4195 drwav_bool32 dra_decoder_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
4196 {
4197  dra_decoder* pDecoder = (dra_decoder*)pUserData;
4198  assert(pDecoder != NULL);
4199  assert(pDecoder->onSeek != NULL);
4200 
4201  return pDecoder->onSeek(pDecoder->pUserData, offset, (origin == drwav_seek_origin_start) ? dra_seek_origin_start : dra_seek_origin_current);
4202 }
4203 
4204 void dra_decoder_on_delete__wav(void* pBackendDecoder)
4205 {
4206  drwav* pWav = (drwav*)pBackendDecoder;
4207  assert(pWav != NULL);
4208 
4209  drwav_close(pWav);
4210 }
4211 
4212 dr_uint64 dra_decoder_on_read_samples__wav(void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
4213 {
4214  drwav* pWav = (drwav*)pBackendDecoder;
4215  assert(pWav != NULL);
4216 
4217  return drwav_read_f32(pWav, samplesToRead, pSamplesOut);
4218 }
4219 
4220 dr_bool32 dra_decoder_on_seek_samples__wav(void* pBackendDecoder, dr_uint64 sample)
4221 {
4222  drwav* pWav = (drwav*)pBackendDecoder;
4223  assert(pWav != NULL);
4224 
4225  return drwav_seek_to_sample(pWav, sample);
4226 }
4227 
4228 
4229 void dra_decoder_init__wav(dra_decoder* pDecoder, drwav* pWav)
4230 {
4231  assert(pDecoder != NULL);
4232  assert(pWav != NULL);
4233 
4234  pDecoder->channels = pWav->channels;
4235  pDecoder->sampleRate = pWav->sampleRate;
4236  pDecoder->totalSampleCount = pWav->totalSampleCount;
4237 
4238  pDecoder->pBackendDecoder = pWav;
4239  pDecoder->onDelete = dra_decoder_on_delete__wav;
4240  pDecoder->onReadSamples = dra_decoder_on_read_samples__wav;
4241  pDecoder->onSeekSamples = dra_decoder_on_seek_samples__wav;
4242 }
4243 
4244 dr_bool32 dra_decoder_open__wav(dra_decoder* pDecoder)
4245 {
4246  drwav* pWav = drwav_open(dra_decoder_on_read__wav, dra_decoder_on_seek__wav, pDecoder);
4247  if (pWav == NULL) {
4248  return DR_FALSE;
4249  }
4250 
4251  dra_decoder_init__wav(pDecoder, pWav);
4252  return DR_TRUE;
4253 }
4254 
4255 dr_bool32 dra_decoder_open_memory__wav(dra_decoder* pDecoder, const void* pData, size_t dataSize)
4256 {
4257  drwav* pWav = drwav_open_memory(pData, dataSize);
4258  if (pWav == NULL) {
4259  return DR_FALSE;
4260  }
4261 
4262  dra_decoder_init__wav(pDecoder, pWav);
4263  return DR_TRUE;
4264 }
4265 
4266 #ifdef DR_AUDIO_HAS_WAV_STDIO
4267 dr_bool32 dra_decoder_open_file__wav(dra_decoder* pDecoder, const char* filePath)
4268 {
4269  drwav* pWav = drwav_open_file(filePath);
4270  if (pWav == NULL) {
4271  return DR_FALSE;
4272  }
4273 
4274  dra_decoder_init__wav(pDecoder, pWav);
4275  return DR_TRUE;
4276 }
4277 #endif
4278 #endif //WAV
4279 
4280 #ifdef DR_AUDIO_HAS_FLAC
4281 size_t dra_decoder_on_read__flac(void* pUserData, void* pDataOut, size_t bytesToRead)
4282 {
4283  dra_decoder* pDecoder = (dra_decoder*)pUserData;
4284  assert(pDecoder != NULL);
4285  assert(pDecoder->onRead != NULL);
4286 
4287  return pDecoder->onRead(pDecoder->pUserData, pDataOut, bytesToRead);
4288 }
4289 drflac_bool32 dra_decoder_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
4290 {
4291  dra_decoder* pDecoder = (dra_decoder*)pUserData;
4292  assert(pDecoder != NULL);
4293  assert(pDecoder->onSeek != NULL);
4294 
4295  return pDecoder->onSeek(pDecoder->pUserData, offset, (origin == drflac_seek_origin_start) ? dra_seek_origin_start : dra_seek_origin_current);
4296 }
4297 
4298 void dra_decoder_on_delete__flac(void* pBackendDecoder)
4299 {
4300  drflac* pFlac = (drflac*)pBackendDecoder;
4301  assert(pFlac != NULL);
4302 
4303  drflac_close(pFlac);
4304 }
4305 
4306 dr_uint64 dra_decoder_on_read_samples__flac(void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
4307 {
4308  drflac* pFlac = (drflac*)pBackendDecoder;
4309  assert(pFlac != NULL);
4310 
4311  dr_uint64 samplesRead = drflac_read_s32(pFlac, samplesToRead, (dr_int32*)pSamplesOut);
4312 
4313  dra_s32_to_f32(pSamplesOut, (dr_int32*)pSamplesOut, (size_t)samplesRead);
4314  return samplesRead;
4315 }
4316 
4317 dr_bool32 dra_decoder_on_seek_samples__flac(void* pBackendDecoder, dr_uint64 sample)
4318 {
4319  drflac* pFlac = (drflac*)pBackendDecoder;
4320  assert(pFlac != NULL);
4321 
4322  return drflac_seek_to_sample(pFlac, sample);
4323 }
4324 
4325 
4326 void dra_decoder_init__flac(dra_decoder* pDecoder, drflac* pFlac)
4327 {
4328  assert(pDecoder != NULL);
4329  assert(pFlac != NULL);
4330 
4331  pDecoder->channels = pFlac->channels;
4332  pDecoder->sampleRate = pFlac->sampleRate;
4333  pDecoder->totalSampleCount = pFlac->totalSampleCount;
4334 
4335  pDecoder->pBackendDecoder = pFlac;
4336  pDecoder->onDelete = dra_decoder_on_delete__flac;
4337  pDecoder->onReadSamples = dra_decoder_on_read_samples__flac;
4338  pDecoder->onSeekSamples = dra_decoder_on_seek_samples__flac;
4339 }
4340 
4341 dr_bool32 dra_decoder_open__flac(dra_decoder* pDecoder)
4342 {
4343  drflac* pFlac = drflac_open(dra_decoder_on_read__flac, dra_decoder_on_seek__flac, pDecoder);
4344  if (pFlac == NULL) {
4345  return DR_FALSE;
4346  }
4347 
4348  dra_decoder_init__flac(pDecoder, pFlac);
4349  return DR_TRUE;
4350 }
4351 
4352 dr_bool32 dra_decoder_open_memory__flac(dra_decoder* pDecoder, const void* pData, size_t dataSize)
4353 {
4354  drflac* pFlac = drflac_open_memory(pData, dataSize);
4355  if (pFlac == NULL) {
4356  return DR_FALSE;
4357  }
4358 
4359  dra_decoder_init__flac(pDecoder, pFlac);
4360  return DR_TRUE;
4361 }
4362 
4363 #ifdef DR_AUDIO_HAS_FLAC_STDIO
4364 dr_bool32 dra_decoder_open_file__flac(dra_decoder* pDecoder, const char* filePath)
4365 {
4366  drflac* pFlac = drflac_open_file(filePath);
4367  if (pFlac == NULL) {
4368  return DR_FALSE;
4369  }
4370 
4371  dra_decoder_init__flac(pDecoder, pFlac);
4372  return DR_TRUE;
4373 }
4374 #endif
4375 #endif //FLAC
4376 
4377 #ifdef DR_AUDIO_HAS_VORBIS
4378 void dra_decoder_on_delete__vorbis(void* pBackendDecoder)
4379 {
4380  stb_vorbis* pVorbis = (stb_vorbis*)pBackendDecoder;
4381  assert(pVorbis != NULL);
4382 
4383  stb_vorbis_close(pVorbis);
4384 }
4385 
4386 dr_uint64 dra_decoder_on_read_samples__vorbis(void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
4387 {
4388  stb_vorbis* pVorbis = (stb_vorbis*)pBackendDecoder;
4389  assert(pVorbis != NULL);
4390 
4391  stb_vorbis_info info = stb_vorbis_get_info(pVorbis);
4392  return (dr_uint64)stb_vorbis_get_samples_float_interleaved(pVorbis, info.channels, pSamplesOut, (int)samplesToRead) * info.channels;
4393 }
4394 
4395 dr_bool32 dra_decoder_on_seek_samples__vorbis(void* pBackendDecoder, dr_uint64 sample)
4396 {
4397  stb_vorbis* pVorbis = (stb_vorbis*)pBackendDecoder;
4398  assert(pVorbis != NULL);
4399 
4400  return stb_vorbis_seek(pVorbis, (unsigned int)sample) != 0;
4401 }
4402 
4403 
4404 void dra_decoder_init__vorbis(dra_decoder* pDecoder, stb_vorbis* pVorbis)
4405 {
4406  assert(pDecoder != NULL);
4407  assert(pVorbis != NULL);
4408 
4409  stb_vorbis_info info = stb_vorbis_get_info(pVorbis);
4410 
4411  pDecoder->channels = info.channels;
4412  pDecoder->sampleRate = info.sample_rate;
4414 
4415  pDecoder->pBackendDecoder = pVorbis;
4416  pDecoder->onDelete = dra_decoder_on_delete__vorbis;
4417  pDecoder->onReadSamples = dra_decoder_on_read_samples__vorbis;
4418  pDecoder->onSeekSamples = dra_decoder_on_seek_samples__vorbis;
4419 }
4420 
4421 dr_bool32 dra_decoder_open__vorbis(dra_decoder* pDecoder)
4422 {
4423  // TODO: Add support for the push API.
4424 
4425  // Not currently supporting callback based decoding.
4426  (void)pDecoder;
4427  return DR_FALSE;
4428 }
4429 
4430 dr_bool32 dra_decoder_open_memory__vorbis(dra_decoder* pDecoder, const void* pData, size_t dataSize)
4431 {
4432  stb_vorbis* pVorbis = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
4433  if (pVorbis == NULL) {
4434  return DR_FALSE;
4435  }
4436 
4437  dra_decoder_init__vorbis(pDecoder, pVorbis);
4438  return DR_TRUE;
4439 }
4440 
4441 #ifdef DR_AUDIO_HAS_VORBIS_STDIO
4442 dr_bool32 dra_decoder_open_file__vorbis(dra_decoder* pDecoder, const char* filePath)
4443 {
4444  stb_vorbis* pVorbis = stb_vorbis_open_filename(filePath, NULL, NULL);
4445  if (pVorbis == NULL) {
4446  return DR_FALSE;
4447  }
4448 
4449  dra_decoder_init__vorbis(pDecoder, pVorbis);
4450  return DR_TRUE;
4451 }
4452 #endif
4453 #endif //Vorbis
4454 
4456 {
4457  if (pDecoder == NULL) return DRA_RESULT_INVALID_ARGS;
4458  memset(pDecoder, 0, sizeof(*pDecoder));
4459 
4460  if (onRead == NULL || onSeek == NULL) return DRA_RESULT_INVALID_ARGS;
4461 
4462  pDecoder->onRead = onRead;
4463  pDecoder->onSeek = onSeek;
4464  pDecoder->pUserData = pUserData;
4465 
4466 #ifdef DR_AUDIO_HAS_WAV_STDIO
4467  if (dra_decoder_open__wav(pDecoder)) {
4468  return DRA_RESULT_SUCCESS;
4469  }
4470  onSeek(pUserData, 0, dra_seek_origin_start);
4471 #endif
4472 #ifdef DR_AUDIO_HAS_FLAC_STDIO
4473  if (dra_decoder_open__flac(pDecoder)) {
4474  return DRA_RESULT_SUCCESS;
4475  }
4476  onSeek(pUserData, 0, dra_seek_origin_start);
4477 #endif
4478 #ifdef DR_AUDIO_HAS_VORBIS_STDIO
4479  if (dra_decoder_open__vorbis(pDecoder)) {
4480  return DRA_RESULT_SUCCESS;
4481  }
4482  onSeek(pUserData, 0, dra_seek_origin_start);
4483 #endif
4484 
4485  // If we get here it means we were unable to open a decoder.
4486  return DRA_RESULT_NO_DECODER;
4487 }
4488 
4489 
4490 size_t dra_decoder__on_read_memory(void* pUserData, void* pDataOut, size_t bytesToRead)
4491 {
4492  dra__memory_stream* memoryStream = (dra__memory_stream*)pUserData;
4493  assert(memoryStream != NULL);
4494  assert(memoryStream->dataSize >= memoryStream->currentReadPos);
4495 
4496  size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
4497  if (bytesToRead > bytesRemaining) {
4498  bytesToRead = bytesRemaining;
4499  }
4500 
4501  if (bytesToRead > 0) {
4502  memcpy(pDataOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
4503  memoryStream->currentReadPos += bytesToRead;
4504  }
4505 
4506  return bytesToRead;
4507 }
4508 dr_bool32 dra_decoder__on_seek_memory(void* pUserData, int offset, dra_seek_origin origin)
4509 {
4510  dra__memory_stream* memoryStream = (dra__memory_stream*)pUserData;
4511  assert(memoryStream != NULL);
4512  assert(offset > 0 || (offset == 0 && origin == dra_seek_origin_start));
4513 
4514  if (origin == dra_seek_origin_current) {
4515  if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
4516  memoryStream->currentReadPos += offset;
4517  } else {
4518  memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
4519  }
4520  } else {
4521  if ((dr_uint32)offset <= memoryStream->dataSize) {
4522  memoryStream->currentReadPos = offset;
4523  } else {
4524  memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
4525  }
4526  }
4527 
4528  return DR_TRUE;
4529 }
4530 
4531 dra_result dra_decoder_open_memory(dra_decoder* pDecoder, const void* pData, size_t dataSize)
4532 {
4533  if (pDecoder == NULL) return DRA_RESULT_INVALID_ARGS;
4534  memset(pDecoder, 0, sizeof(*pDecoder));
4535 
4536  if (pData == NULL || dataSize == 0) return DRA_RESULT_INVALID_ARGS;
4537 
4538  // Prefer the backend's native APIs.
4539 #if defined(DR_AUDIO_HAS_WAV)
4540  if (dra_decoder_open_memory__wav(pDecoder, pData, dataSize)) {
4541  return DRA_RESULT_SUCCESS;
4542  }
4543 #endif
4544 #if defined(DR_AUDIO_HAS_FLAC)
4545  if (dra_decoder_open_memory__flac(pDecoder, pData, dataSize)) {
4546  return DRA_RESULT_SUCCESS;
4547  }
4548 #endif
4549 #if defined(DR_AUDIO_HAS_VORBIS)
4550  if (dra_decoder_open_memory__vorbis(pDecoder, pData, dataSize)) {
4551  return DRA_RESULT_SUCCESS;
4552  }
4553 #endif
4554 
4555  // If we get here it means the backend does not have a native memory loader so we'll need to it ourselves.
4556  dra__memory_stream memoryStream;
4557  memoryStream.data = (const unsigned char*)pData;
4558  memoryStream.dataSize = dataSize;
4559  memoryStream.currentReadPos = 0;
4560  dra_result result = dra_decoder_open(pDecoder, dra_decoder__on_read_memory, dra_decoder__on_seek_memory, &memoryStream);
4561  if (result != DRA_RESULT_SUCCESS) {
4562  return result;
4563  }
4564 
4565  pDecoder->memoryStream = memoryStream;
4566  pDecoder->pUserData = &pDecoder->memoryStream;
4567 
4568  return DRA_RESULT_SUCCESS;
4569 }
4570 
4571 
4572 #ifndef DR_AUDIO_NO_STDIO
4573 size_t dra_decoder__on_read_stdio(void* pUserData, void* pDataOut, size_t bytesToRead)
4574 {
4575  return fread(pDataOut, 1, bytesToRead, (FILE*)pUserData);
4576 }
4577 dr_bool32 dra_decoder__on_seek_stdio(void* pUserData, int offset, dra_seek_origin origin)
4578 {
4579  return fseek((FILE*)pUserData, offset, (origin == dra_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
4580 }
4581 
4582 dra_result dra_decoder_open_file(dra_decoder* pDecoder, const char* filePath)
4583 {
4584  if (pDecoder == NULL) return DRA_RESULT_INVALID_ARGS;
4585  memset(pDecoder, 0, sizeof(*pDecoder));
4586 
4587  if (filePath == NULL) return DR_FALSE;
4588 
4589  // When opening a decoder from a file it's preferrable to use the backend's native file IO APIs if it has them.
4590 #if defined(DR_AUDIO_HAS_WAV) && defined(DR_AUDIO_HAS_WAV_STDIO)
4591  if (dra_decoder_open_file__wav(pDecoder, filePath)) {
4592  return DRA_RESULT_SUCCESS;
4593  }
4594 #endif
4595 #if defined(DR_AUDIO_HAS_FLAC) && defined(DR_AUDIO_HAS_FLAC_STDIO)
4596  if (dra_decoder_open_file__flac(pDecoder, filePath)) {
4597  return DRA_RESULT_SUCCESS;
4598  }
4599 #endif
4600 #if defined(DR_AUDIO_HAS_VORBIS) && defined(DR_AUDIO_HAS_VORBIS_STDIO)
4601  if (dra_decoder_open_file__vorbis(pDecoder, filePath)) {
4602  return DRA_RESULT_SUCCESS;
4603  }
4604 #endif
4605 
4606  // If we get here it means we were unable to open the decoder using any of the backends' native file IO
4607  // APIs. In this case we fall back to a generic method.
4608  FILE* pFile = dra__fopen(filePath);
4609  if (pFile == NULL) {
4611  }
4612 
4613  dra_result result = dra_decoder_open(pDecoder, dra_decoder__on_read_stdio, dra_decoder__on_seek_stdio, pFile);
4614  if (result != DRA_RESULT_SUCCESS) {
4615  fclose(pFile);
4616  return result;
4617  }
4618 
4619  return DRA_RESULT_SUCCESS;
4620 }
4621 #endif
4622 
4623 void dra_decoder_close(dra_decoder* pDecoder)
4624 {
4625  if (pDecoder == NULL) {
4626  return;
4627  }
4628 
4629  if (pDecoder->onDelete) {
4630  pDecoder->onDelete(pDecoder->pBackendDecoder);
4631  }
4632 
4633 #ifndef DR_AUDIO_NO_STDIO
4634  if (pDecoder->onRead == dra_decoder__on_read_stdio) {
4635  fclose((FILE*)pDecoder->pUserData);
4636  }
4637 #endif
4638 }
4639 
4640 dr_uint64 dra_decoder_read_f32(dra_decoder* pDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
4641 {
4642  if (pDecoder == NULL || pSamplesOut == NULL) {
4643  return 0;
4644  }
4645 
4646  return pDecoder->onReadSamples(pDecoder->pBackendDecoder, samplesToRead, pSamplesOut);
4647 }
4648 
4650 {
4651  if (pDecoder == NULL) {
4652  return DR_FALSE;
4653  }
4654 
4655  return pDecoder->onSeekSamples(pDecoder->pBackendDecoder, sample);
4656 }
4657 
4658 
4659 float* dra_decoder__full_decode_and_close(dra_decoder* pDecoder, unsigned int* channelsOut, unsigned int* sampleRateOut, dr_uint64* totalSampleCountOut)
4660 {
4661  assert(pDecoder != NULL);
4662 
4663  float* pSampleData = NULL;
4664  dr_uint64 totalSampleCount = pDecoder->totalSampleCount;
4665 
4666  if (totalSampleCount == 0)
4667  {
4668  float buffer[4096];
4669 
4670  size_t sampleDataBufferSize = sizeof(buffer);
4671  pSampleData = (float*)malloc(sampleDataBufferSize);
4672  if (pSampleData == NULL) {
4673  goto on_error;
4674  }
4675 
4676  dr_uint64 samplesRead;
4677  while ((samplesRead = (dr_uint64)dra_decoder_read_f32(pDecoder, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0)
4678  {
4679  if (((totalSampleCount + samplesRead) * sizeof(float)) > sampleDataBufferSize) {
4680  sampleDataBufferSize *= 2;
4681  float* pNewSampleData = (float*)realloc(pSampleData, sampleDataBufferSize);
4682  if (pNewSampleData == NULL) {
4683  free(pSampleData);
4684  goto on_error;
4685  }
4686 
4687  pSampleData = pNewSampleData;
4688  }
4689 
4690  memcpy(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(float)));
4691  totalSampleCount += samplesRead;
4692  }
4693 
4694  // At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to
4695  // protect those ears from random noise!
4696  memset(pSampleData + totalSampleCount, 0, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(float)));
4697  }
4698  else
4699  {
4700  dr_uint64 dataSize = totalSampleCount * sizeof(float);
4701  if (dataSize > SIZE_MAX) {
4702  goto on_error; // The decoded data is too big.
4703  }
4704 
4705  pSampleData = (float*)malloc((size_t)dataSize); // <-- Safe cast as per the check above.
4706  if (pSampleData == NULL) {
4707  goto on_error;
4708  }
4709 
4710  dr_uint64 samplesDecoded = dra_decoder_read_f32(pDecoder, pDecoder->totalSampleCount, pSampleData);
4711  if (samplesDecoded != pDecoder->totalSampleCount) {
4712  free(pSampleData);
4713  goto on_error; // Something went wrong when decoding the FLAC stream.
4714  }
4715  }
4716 
4717 
4718  if (channelsOut) *channelsOut = pDecoder->channels;
4719  if (sampleRateOut) *sampleRateOut = pDecoder->sampleRate;
4720  if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount;
4721 
4722  dra_decoder_close(pDecoder);
4723  return pSampleData;
4724 
4725 on_error:
4726  dra_decoder_close(pDecoder);
4727  return NULL;
4728 }
4729 
4730 float* dra_decoder_open_and_decode_f32(dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount)
4731 {
4732  // Safety.
4733  if (channels) *channels = 0;
4734  if (sampleRate) *sampleRate = 0;
4735  if (totalSampleCount) *totalSampleCount = 0;
4736 
4738  dra_result result = dra_decoder_open(&decoder, onRead, onSeek, pUserData);
4739  if (result != DRA_RESULT_SUCCESS) {
4740  return NULL;
4741  }
4742 
4743  return dra_decoder__full_decode_and_close(&decoder, channels, sampleRate, totalSampleCount);
4744 }
4745 
4746 float* dra_decoder_open_and_decode_memory_f32(const void* pData, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount)
4747 {
4748  // Safety.
4749  if (channels) *channels = 0;
4750  if (sampleRate) *sampleRate = 0;
4751  if (totalSampleCount) *totalSampleCount = 0;
4752 
4754  dra_result result = dra_decoder_open_memory(&decoder, pData, dataSize);
4755  if (result != DRA_RESULT_SUCCESS) {
4756  return NULL;
4757  }
4758 
4759  return dra_decoder__full_decode_and_close(&decoder, channels, sampleRate, totalSampleCount);
4760 }
4761 
4762 #ifndef DR_AUDIO_NO_STDIO
4763 float* dra_decoder_open_and_decode_file_f32(const char* filePath, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount)
4764 {
4765  // Safety.
4766  if (channels) *channels = 0;
4767  if (sampleRate) *sampleRate = 0;
4768  if (totalSampleCount) *totalSampleCount = 0;
4769 
4771  dra_result result = dra_decoder_open_file(&decoder, filePath);
4772  if (result != DRA_RESULT_SUCCESS) {
4773  return NULL;
4774  }
4775 
4776  return dra_decoder__full_decode_and_close(&decoder, channels, sampleRate, totalSampleCount);
4777 }
4778 #endif
4779 
4780 
4781 
4783 
4784 #ifndef DR_AUDIO_NO_STDIO
4785 dra_result dra_voice_create_from_file(dra_device* pDevice, const char* filePath, dra_voice** ppVoice)
4786 {
4787  if (pDevice == NULL || filePath == NULL) return DRA_RESULT_INVALID_ARGS;
4788 
4789  unsigned int channels;
4790  unsigned int sampleRate;
4791  dr_uint64 totalSampleCount;
4792  float* pSampleData = dra_decoder_open_and_decode_file_f32(filePath, &channels, &sampleRate, &totalSampleCount);
4793  if (pSampleData == NULL) {
4794  return DRA_RESULT_UNKNOWN_ERROR;
4795  }
4796 
4797  dra_result result = dra_voice_create(pDevice, dra_format_f32, channels, sampleRate, (size_t)totalSampleCount * sizeof(float), pSampleData, ppVoice);
4798 
4799  free(pSampleData);
4800  return result;
4801 }
4802 #endif
4803 
4804 
4806 
4808 {
4809  dra_sound_world* pWorld = (dra_sound_world*)calloc(1, sizeof(*pWorld));
4810  if (pWorld == NULL) {
4811  goto on_error;
4812  }
4813 
4814  pWorld->pPlaybackDevice = pPlaybackDevice;
4815  if (pWorld->pPlaybackDevice == NULL) {
4817  if (result != DRA_RESULT_SUCCESS) {
4818  return NULL;
4819  }
4820 
4821  pWorld->ownsPlaybackDevice = DR_TRUE;
4822  }
4823 
4824 
4825 
4826 
4827  return pWorld;
4828 
4829 
4830 on_error:
4831  dra_sound_world_delete(pWorld);
4832  return NULL;
4833 }
4834 
4836 {
4837  if (pWorld == NULL) {
4838  return;
4839  }
4840 
4841  if (pWorld->ownsPlaybackDevice) {
4843  }
4844 
4845  free(pWorld);
4846 }
4847 
4848 
4849 void dra_sound_world__on_inline_sound_stop(dr_uint64 eventID, void* pUserData)
4850 {
4851  (void)eventID;
4852 
4853  dra_sound* pSound = (dra_sound*)pUserData;
4854  assert(pSound != NULL);
4855 
4856  dra_sound_delete(pSound);
4857 }
4858 
4860 {
4861  if (pWorld == NULL || pDesc == NULL) {
4862  return;
4863  }
4864 
4865  // An inline sound is just like any other, except it never loops and it's deleted automatically when it stops playing. Therefore what
4866  // we need to do is attach an event handler to the voice's stop callback which is where the sound will be deleted.
4867  dra_sound* pSound = dra_sound_create(pWorld, pDesc);
4868  if (pSound == NULL) {
4869  return;
4870  }
4871 
4872  if (pMixer != NULL) {
4873  dra_sound_attach_to_mixer(pSound, pMixer);
4874  }
4875 
4876  dra_voice_set_on_stop(pSound->pVoice, dra_sound_world__on_inline_sound_stop, pSound);
4877  dra_sound_play(pSound, DR_FALSE);
4878 }
4879 
4880 void dra_sound_world_play_inline_3f(dra_sound_world* pWorld, dra_sound_desc* pDesc, dra_mixer* pMixer, float xPos, float yPos, float zPos)
4881 {
4882  if (pWorld == NULL || pDesc == NULL) {
4883  return;
4884  }
4885 
4886  // TODO: Implement 3D positioning once the effects framework is in.
4887  (void)xPos;
4888  (void)yPos;
4889  (void)zPos;
4890  dra_sound_world_play_inline(pWorld, pDesc, pMixer);
4891 }
4892 
4894 {
4895  if (pWorld == NULL) {
4896  return;
4897  }
4898 
4899  // When sounds are stopped the on_stop event handler will be fired. It is possible for the implementation of this event handler to
4900  // delete the sound, so we'll first need to gather the sounds into a separate list.
4902  if (voiceCount == 0) {
4903  return;
4904  }
4905 
4906  dra_voice** ppVoices = (dra_voice**)malloc(voiceCount * sizeof(*ppVoices));
4907  if (ppVoices == NULL) {
4908  return;
4909  }
4910 
4912  assert(voiceCount != 0);
4913 
4914  for (size_t iVoice = 0; iVoice < voiceCount; ++iVoice) {
4915  dra_voice_stop(ppVoices[iVoice]);
4916  }
4917 
4918  free(ppVoices);
4919 }
4920 
4921 void dra_sound_world_set_listener_position(dra_sound_world* pWorld, float xPos, float yPos, float zPos)
4922 {
4923  if (pWorld == NULL) {
4924  return;
4925  }
4926 
4927  // TODO: Implement me.
4928  (void)xPos;
4929  (void)yPos;
4930  (void)zPos;
4931 }
4932 
4933 void dra_sound_world_set_listener_orientation(dra_sound_world* pWorld, float xForward, float yForward, float zForward, float xUp, float yUp, float zUp)
4934 {
4935  if (pWorld == NULL) {
4936  return;
4937  }
4938 
4939  // TODO: Implement me.
4940  (void)xForward;
4941  (void)yForward;
4942  (void)zForward;
4943  (void)xUp;
4944  (void)yUp;
4945  (void)zUp;
4946 }
4947 
4948 
4949 dr_bool32 dra_sound__is_streaming(dra_sound* pSound)
4950 {
4951  assert(pSound != NULL);
4952  return pSound->desc.dataSize == 0 || pSound->desc.pData == NULL;
4953 }
4954 
4955 dr_bool32 dra_sound__read_next_chunk(dra_sound* pSound, dr_uint64 outputSampleOffset)
4956 {
4957  assert(pSound != NULL);
4958  if (pSound->desc.onRead == NULL) {
4959  return DR_FALSE;
4960  }
4961 
4962  dr_uint64 chunkSizeInSamples = (pSound->pVoice->frameCount * pSound->pVoice->channels) / 2;
4963  assert(chunkSizeInSamples > 0);
4964 
4965  dr_uint64 samplesRead = pSound->desc.onRead(pSound, chunkSizeInSamples, dra_voice_get_buffer_ptr_by_sample(pSound->pVoice, outputSampleOffset));
4966  if (samplesRead == 0 && !pSound->isLooping) {
4967  dra_voice_write_silence(pSound->pVoice, outputSampleOffset, chunkSizeInSamples);
4968  return DR_FALSE; // Ran out of samples in a non-looping buffer.
4969  }
4970 
4971  if (samplesRead == chunkSizeInSamples) {
4972  return DR_TRUE;
4973  }
4974 
4975  assert(samplesRead > 0);
4976  assert(samplesRead < chunkSizeInSamples);
4977 
4978  // Ran out of samples. If the sound is not looping it simply means the end of the data has been reached. The remaining samples need
4979  // to be zeroed out to create silence.
4980  if (!pSound->isLooping) {
4981  dra_voice_write_silence(pSound->pVoice, outputSampleOffset + samplesRead, chunkSizeInSamples - samplesRead);
4982  return DR_TRUE;
4983  }
4984 
4985  // At this point the sound will not be looping. We want to continuously loop back to the start and keep reading samples until the
4986  // chunk is filled.
4987  while (samplesRead < chunkSizeInSamples) {
4988  if (!pSound->desc.onSeek(pSound, 0)) {
4989  return DR_FALSE;
4990  }
4991 
4992  dr_uint64 samplesRemaining = chunkSizeInSamples - samplesRead;
4993  dr_uint64 samplesJustRead = pSound->desc.onRead(pSound, samplesRemaining, dra_voice_get_buffer_ptr_by_sample(pSound->pVoice, outputSampleOffset + samplesRead));
4994  if (samplesJustRead == 0) {
4995  return DR_FALSE;
4996  }
4997 
4998  samplesRead += samplesJustRead;
4999  }
5000 
5001  return DR_TRUE;
5002 }
5003 
5004 void dra_sound__on_read_next_chunk(dr_uint64 eventID, void* pUserData)
5005 {
5006  dra_sound* pSound = (dra_sound*)pUserData;
5007  assert(pSound != NULL);
5008 
5009  if (pSound->stopOnNextChunk) {
5010  pSound->stopOnNextChunk = DR_FALSE;
5011  dra_sound_stop(pSound);
5012  return;
5013  }
5014 
5015  // The event ID is the index of the sample to write to.
5016  dr_uint64 sampleOffset = eventID;
5017  if (!dra_sound__read_next_chunk(pSound, sampleOffset)) {
5018  pSound->stopOnNextChunk = DR_TRUE;
5019  }
5020 }
5021 
5022 
5024 {
5025  if (pWorld == NULL) {
5026  return NULL;
5027  }
5028 
5029  dr_bool32 isStreaming = DR_FALSE;
5030  dra_result result = DRA_RESULT_SUCCESS;
5031 
5032  dra_sound* pSound = (dra_sound*)calloc(1, sizeof(*pSound));
5033  if (pSound == NULL) {
5034  result = DRA_RESULT_OUT_OF_MEMORY;
5035  goto on_error;
5036  }
5037 
5038  pSound->pWorld = pWorld;
5039  pSound->desc = *pDesc;
5040 
5041  isStreaming = dra_sound__is_streaming(pSound);
5042  if (!isStreaming) {
5043  result = dra_voice_create(pWorld->pPlaybackDevice, pDesc->format, pDesc->channels, pDesc->sampleRate, pDesc->dataSize, pDesc->pData, &pSound->pVoice);
5044  } else {
5045  size_t streamingBufferSize = (pDesc->sampleRate * pDesc->channels) * 2; // 2 seconds total, 1 second chunks. Keep total an even number and a multiple of the channel count.
5046  result = dra_voice_create(pWorld->pPlaybackDevice, pDesc->format, pDesc->channels, pDesc->sampleRate, streamingBufferSize * dra_get_bytes_per_sample_by_format(pDesc->format), NULL, &pSound->pVoice);
5047 
5048  // Streaming buffers require 2 playback events. As one is being played, the other is filled. The event ID is set to the sample
5049  // index of the next chunk that needs updating and is used in determining where to place new data.
5050  dra_voice_add_playback_event(pSound->pVoice, 0, streamingBufferSize/2, dra_sound__on_read_next_chunk, pSound);
5051  dra_voice_add_playback_event(pSound->pVoice, streamingBufferSize/2, 0, dra_sound__on_read_next_chunk, pSound);
5052  }
5053 
5054  if (result != DRA_RESULT_SUCCESS) {
5055  goto on_error;
5056  }
5057 
5058  pSound->pVoice->pUserData = pSound;
5059 
5060 
5061  // Streaming buffers need to have an initial chunk of data loaded before returning. This ensures the internal buffer contains valid audio data in
5062  // preparation for being played for the first time.
5063  if (isStreaming) {
5064  if (!dra_sound__read_next_chunk(pSound, 0)) {
5065  goto on_error;
5066  }
5067  }
5068 
5069  return pSound;
5070 
5071 on_error:
5072  dra_sound_delete(pSound);
5073  return NULL;
5074 }
5075 
5076 
5077 
5078 void dra_sound__on_delete_decoder(dra_sound* pSound)
5079 {
5080  dra_decoder* pDecoder = (dra_decoder*)pSound->desc.pUserData;
5081  assert(pDecoder != NULL);
5082 
5083  dra_decoder_close(pDecoder);
5084  free(pDecoder);
5085 }
5086 
5087 dr_uint64 dra_sound__on_read_decoder(dra_sound* pSound, dr_uint64 samplesToRead, void* pSamplesOut)
5088 {
5089  dra_decoder* pDecoder = (dra_decoder*)pSound->desc.pUserData;
5090  assert(pDecoder != NULL);
5091 
5092  return dra_decoder_read_f32(pDecoder, samplesToRead, (float*)pSamplesOut);
5093 }
5094 
5095 dr_bool32 dra_sound__on_seek_decoder(dra_sound* pSound, dr_uint64 sample)
5096 {
5097  dra_decoder* pDecoder = (dra_decoder*)pSound->desc.pUserData;
5098  assert(pDecoder != NULL);
5099 
5100  return dra_decoder_seek_to_sample(pDecoder, sample);
5101 }
5102 
5103 #ifndef DR_AUDIO_NO_STDIO
5104 dra_sound* dra_sound_create_from_file(dra_sound_world* pWorld, const char* filePath)
5105 {
5106  if (pWorld == NULL || filePath == NULL) {
5107  return NULL;
5108  }
5109 
5110  dra_decoder* pDecoder = (dra_decoder*)malloc(sizeof(*pDecoder));
5111  if (pDecoder == NULL) {
5112  return NULL;
5113  }
5114 
5115  dra_result result = dra_decoder_open_file(pDecoder, filePath);
5116  if (result != DRA_RESULT_SUCCESS) {
5117  free(pDecoder);
5118  return NULL;
5119  }
5120 
5121  dra_sound_desc desc;
5122  desc.format = dra_format_f32;
5123  desc.channels = pDecoder->channels;
5124  desc.sampleRate = pDecoder->sampleRate;
5125  desc.dataSize = 0;
5126  desc.pData = NULL;
5127  desc.onDelete = dra_sound__on_delete_decoder;
5128  desc.onRead = dra_sound__on_read_decoder;
5129  desc.onSeek = dra_sound__on_seek_decoder;
5130  desc.pUserData = pDecoder;
5131 
5132  dra_sound* pSound = dra_sound_create(pWorld, &desc);
5133 
5134  // After creating the sound, the audio data of a non-streaming voice can be deleted.
5135  if (desc.pData != NULL) {
5136  free(desc.pData);
5137  }
5138 
5139  return pSound;
5140 }
5141 #endif
5142 
5143 void dra_sound_delete(dra_sound* pSound)
5144 {
5145  if (pSound == NULL) {
5146  return;
5147  }
5148 
5149  if (pSound->pVoice != NULL) {
5150  dra_voice_delete(pSound->pVoice);
5151  }
5152 
5153  if (pSound->desc.onDelete) {
5154  pSound->desc.onDelete(pSound);
5155  }
5156 
5157  free(pSound);
5158 }
5159 
5160 
5161 void dra_sound_play(dra_sound* pSound, dr_bool32 loop)
5162 {
5163  if (pSound == NULL) {
5164  return;
5165  }
5166 
5167  // The voice is always set to loop for streaming sounds.
5168  if (dra_sound__is_streaming(pSound)) {
5169  dra_voice_play(pSound->pVoice, DR_TRUE);
5170  } else {
5171  dra_voice_play(pSound->pVoice, loop);
5172  }
5173 
5174  pSound->isLooping = loop;
5175 }
5176 
5177 void dra_sound_stop(dra_sound* pSound)
5178 {
5179  if (pSound == NULL) {
5180  return;
5181  }
5182 
5183  dra_voice_stop(pSound->pVoice);
5184 }
5185 
5186 
5187 void dra_sound_attach_to_mixer(dra_sound* pSound, dra_mixer* pMixer)
5188 {
5189  if (pSound == NULL) {
5190  return;
5191  }
5192 
5193  if (pMixer == NULL) {
5194  pMixer = pSound->pWorld->pPlaybackDevice->pMasterMixer;
5195  }
5196 
5197  dra_mixer_attach_voice(pMixer, pSound->pVoice);
5198 }
5199 
5200 
5201 void dra_sound_set_on_stop(dra_sound* pSound, dra_event_proc proc, void* pUserData)
5202 {
5203  dra_voice_set_on_stop(pSound->pVoice, proc, pUserData);
5204 }
5205 
5206 void dra_sound_set_on_play(dra_sound* pSound, dra_event_proc proc, void* pUserData)
5207 {
5208  dra_voice_set_on_play(pSound->pVoice, proc, pUserData);
5209 }
5210 
5211 #endif //DR_AUDIO_IMPLEMENTATION
5212 
5213 
5214 // TODO
5215 //
5216 // - Forward declare every backend function and document them.
5217 // - Add support for the push API in stb_vorbis.
5218 
5219 // DEVELOPMENT NOTES AND BRAINSTORMING
5220 //
5221 // This is just random brainstorming and is likely very out of date and often just outright incorrect.
5222 //
5223 //
5224 // Latency
5225 //
5226 // When a device is created it'll create a "hardware buffer" which is basically the buffer that the hardware
5227 // device will read from when it needs to play audio. The hardware buffer is divided into two halves. As the
5228 // buffer plays, it moves the playback pointer forward through the buffer and loops. When it hits the half
5229 // way point it notifies the application that it needs more data to continue playing. Once one half starts
5230 // playing the data within it is committed and cannot be changed. The size of each half determines the latency
5231 // of the device.
5232 //
5233 // It sounds tempting to set this to something small like 1ms, but making it too small will
5234 // increase the chance that the CPU won't be able to keep filling it with fresh data. In addition it will
5235 // incrase overall CPU usage because operating system's scheduler will need to wake up the thread more often.
5236 // Increasing the latency will increase memory usage and make playback of new sound sources take longer to
5237 // begin playing. For example, if the latency was set to something like 1 second, a sound effect in a game
5238 // may take up to a whole second to start playing. A balance needs to be made when setting the latency, and
5239 // it can be configured when the device is created.
5240 //
5241 // (mention the fragments system to help avoiding the CPU running out of time to fill new audio data)
5242 //
5243 //
5244 // Mixing
5245 //
5246 // Mixing is done via dra_mixer objects. Buffers can be attached to a mixer, but not more than one at a time.
5247 // By default buffers are attached to a master mixer. Effects like volume can be applied on a per-buffer and
5248 // per-mixer basis.
5249 //
5250 // Mixers can be chained together in a hierarchial manner. Child mixers will be mixed first with the result
5251 // then passed on to higher level mixing. The master mixer is always the top level mixer.
5252 //
5253 // An obvious use case for mixers in games is to have separate volume controls for different categories of
5254 // sounds, such as music, voices and sounds effects. Another example may be in music production where you may
5255 // want to have separate mixers for vocal tracks, percussion tracks, etc.
5256 //
5257 // A mixer can be thought of as a complex buffer - it can be played/stopped/paused and have effects such as
5258 // volume apllied to it. All of this affects all attached buffers and sub-mixers. You can, for example, pause
5259 // every buffer attached to the mixer by simply pausing the mixer. This is an efficient and easy way to pause
5260 // a group of audio buffers at the same time, such as when the user hits the pause button in a game.
5261 //
5262 // Every device includes a master mixer which is the one that buffers are automatically attached to. This one
5263 // is intentionally hidden from the public facing API in order to keep it simpler. For basic audio playback
5264 // using the master mixer will work just fine, however for more complex sound interactions you'll want to use
5265 // your own mixers. Mixers, like buffers, are attached to the master mixer by default
5266 //
5267 //
5268 // Thread Safety
5269 //
5270 // Everything in dr_audio should be thread-safe.
5271 //
5272 // Backends are implemented as if they are always run from a single thread. It's up to the cross-platform
5273 // section to ensure thread-safety. This is an important property because if each backend is responsible for
5274 // their own thread safety it increases the likelyhood of subtle backend-specific bugs.
5275 //
5276 // Every device has their own thread for asynchronous playback. This thread is created when the device is
5277 // created, and deleted when the device is deleted.
5278 //
5279 // (Note edge cases when thread-safety may be an issue)
5280 
5281 
5282 /*
5283 This is free and unencumbered software released into the public domain.
5284 
5285 Anyone is free to copy, modify, publish, use, compile, sell, or
5286 distribute this software, either in source code form or as a compiled
5287 binary, for any purpose, commercial or non-commercial, and by any
5288 means.
5289 
5290 In jurisdictions that recognize copyright laws, the author or authors
5291 of this software dedicate any and all copyright interest in the
5292 software to the public domain. We make this dedication for the benefit
5293 of the public at large and to the detriment of our heirs and
5294 successors. We intend this dedication to be an overt act of
5295 relinquishment in perpetuity of all present and future rights to this
5296 software under copyright law.
5297 
5298 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
5299 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
5300 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
5301 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
5302 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
5303 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
5304 OTHER DEALINGS IN THE SOFTWARE.
5305 
5306 For more information, please refer to <http://unlicense.org/>
5307 */
drwav_seek_origin
drwav_seek_origin
Definition: porcupine/demo/c/dr_libs/dr_wav.h:268
dra_voice::playbackEvents
dra__event playbackEvents[DR_AUDIO_MAX_EVENT_COUNT]
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:492
dra_sound_desc::onSeek
dra_sound_on_seek_proc onSeek
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:809
dra_decoder::pUserData
void * pUserData
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:728
dra_sound_stop
void dra_sound_stop(dra_sound *pSound)
dra_voice_stop
void dra_voice_stop(dra_voice *pVoice)
dra_decoder::onReadSamples
dra_decoder_on_read_samples_proc onReadSamples
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:732
dra_mixer::pStagingBuffer
float * pStagingBuffer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:404
dra_voice_is_looping
dr_bool32 dra_voice_is_looping(dra_voice *pVoice)
dra_mixer::linearVolume
float linearVolume
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:399
dra_sound::desc
dra_sound_desc desc
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:833
dra_thread_event_type_terminate
@ dra_thread_event_type_terminate
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:252
dra_sound_desc::pData
void * pData
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:796
DR_AUDIO_EVENT_ID_PLAY
#define DR_AUDIO_EVENT_ID_PLAY
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:210
dra_mixer::pLastChildMixer
dra_mixer * pLastChildMixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:381
dra_format
dra_format
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:232
dra_device_delete
void dra_device_delete(dra_device *pDevice)
dra__event_queue::lock
dra_mutex lock
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:287
dra_sound_create
dra_sound * dra_sound_create(dra_sound_world *pWorld, dra_sound_desc *pDesc)
dra_mixer_mix_next_frames
size_t dra_mixer_mix_next_frames(dra_mixer *pMixer, size_t frameCount)
dra_voice::linearVolume
float linearVolume
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:444
dra_thread_event_type_none
@ dra_thread_event_type_none
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:251
drwav_bool32
drwav_uint32 drwav_bool32
Definition: porcupine/demo/c/dr_libs/dr_wav.h:163
dra_voice_remove_playback_event
void dra_voice_remove_playback_event(dra_voice *pVoice, dr_uint64 eventID)
dra_sound_on_delete_proc
void(* dra_sound_on_delete_proc)(dra_sound *pSound)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:776
dra_sound_desc::sampleRate
unsigned int sampleRate
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:789
dra_voice_create
dra_result dra_voice_create(dra_device *pDevice, dra_format format, unsigned int channels, unsigned int sampleRate, size_t sizeInBytes, const void *pInitialData, dra_voice **ppVoice)
dra_voice::nextFrame
float nextFrame[DR_AUDIO_MAX_CHANNEL_COUNT]
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:482
DR_FALSE
#define DR_FALSE
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:198
stb_vorbis_get_info
stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:4280
dr_uint32
uint32_t dr_uint32
Definition: porcupine/demo/c/dr_libs/old/dr.h:65
dra_device_uninit
void dra_device_uninit(dra_device *pDevice)
dra_mixer_detach_submixer
void dra_mixer_detach_submixer(dra_mixer *pMixer, dra_mixer *pSubmixer)
dra_sound_desc::onDelete
dra_sound_on_delete_proc onDelete
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:801
dra_context_uninit
void dra_context_uninit(dra_context *pContext)
dra_sound_world_play_inline
void dra_sound_world_play_inline(dra_sound_world *pWorld, dra_sound_desc *pDesc, dra_mixer *pMixer)
dra_samples_processed_proc
void(* dra_samples_processed_proc)(dra_device *pDevice, const size_t sampleCount, const float *pSamples, void *pUserData)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:269
dra_voice_play
void dra_voice_play(dra_voice *pVoice, dr_bool32 loop)
NULL
#define NULL
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/speex_resampler/thirdparty/resample.c:92
dra_sound_desc::onRead
dra_sound_on_read_proc onRead
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:805
dra__event_queue
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:281
dra_device::eventQueue
dra__event_queue eventQueue
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:348
dra_voice::currentReadPos
dr_uint64 currentReadPos
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:461
stb_vorbis_seek
int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:4893
drflac
Definition: porcupine/demo/c/dr_libs/dr_flac.h:688
dra_decoder_close
void dra_decoder_close(dra_decoder *pDecoder)
dra_get_bytes_per_sample_by_format
unsigned int dra_get_bytes_per_sample_by_format(dra_format format)
drflac::channels
drflac_uint8 channels
Definition: porcupine/demo/c/dr_libs/dr_flac.h:707
dra_device
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:295
dra_voice::format
dra_format format
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:433
dra__event_queue::pEvents
dra__event * pEvents
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:286
dr_bool8
dr_int8 dr_bool8
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:195
dr_int8
int8_t dr_int8
Definition: porcupine/demo/c/dr_libs/old/dr.h:60
dra_sound
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:824
dra_device::isPlaying
dr_bool32 isPlaying
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:330
dra_sound_desc
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:780
drwav_seek_origin_start
@ drwav_seek_origin_start
Definition: porcupine/demo/c/dr_libs/dr_wav.h:270
dra_backend_device
struct dra_backend_device dra_backend_device
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:257
dra_mutex
void * dra_mutex
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:265
dra_device::mutex
dra_mutex mutex
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:304
dra_device::isClosed
dr_bool32 isClosed
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:325
DR_AUDIO_MAX_EVENT_COUNT
#define DR_AUDIO_MAX_EVENT_COUNT
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:206
dra_device::pContext
dra_context * pContext
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:298
dra_device_type_capture
@ dra_device_type_capture
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:229
dra_sound_world
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:815
dra_decoder::pBackendDecoder
void * pBackendDecoder
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:730
dra__event_queue::firstEvent
size_t firstEvent
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:283
dra_mixer_count_attached_voices
size_t dra_mixer_count_attached_voices(dra_mixer *pMixer)
dra_backend
struct dra_backend dra_backend
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:256
stb_vorbis_info
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:124
dr_int16
int16_t dr_int16
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:188
dra_src_algorithm
dra_src_algorithm
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:242
dra__event::pUserData
void * pUserData
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:274
FALSE
#define FALSE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:642
dra_src_algorithm
dra_src_algorithm
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:242
dra_decoder_on_delete_proc
void(* dra_decoder_on_delete_proc)(void *pBackendDecoder)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:709
dra__event::hasBeenSignaled
dr_bool32 hasBeenSignaled
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:278
decoder
ma_decoder decoder
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/tests/test_deviceio/ma_test_deviceio.c:61
dra_sound_set_on_play
void dra_sound_set_on_play(dra_sound *pSound, dra_event_proc proc, void *pUserData)
dra_mixer_pause
void dra_mixer_pause(dra_mixer *pMixer)
dra_sound::stopOnNextChunk
dr_bool32 stopOnNextChunk
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:839
dra_voice_set_on_stop
void dra_voice_set_on_stop(dra_voice *pVoice, dra_event_proc proc, void *pUserData)
dra_voice_is_playing
dr_bool32 dra_voice_is_playing(dra_voice *pVoice)
dra_seek_origin_current
@ dra_seek_origin_current
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:703
dra_voice_write_silence
void dra_voice_write_silence(dra_voice *pVoice, dr_uint64 sampleOffset, dr_uint64 sampleCount)
DRA_RESULT_SUCCESS
#define DRA_RESULT_SUCCESS
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:213
dr_int16
int16_t dr_int16
Definition: porcupine/demo/c/dr_libs/old/dr.h:62
dra_format_f32
@ dra_format_f32
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:238
dra_voice::pData
dr_uint8 pData[1]
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:510
dra_sound_world_set_listener_orientation
void dra_sound_world_set_listener_orientation(dra_sound_world *pWorld, float xForward, float yForward, float zForward, float xUp, float yUp, float zUp)
dra_mixer_detach_all_voices
void dra_mixer_detach_all_voices(dra_mixer *pMixer)
dra_sound::pUserData
void * pUserData
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:842
dra_sound_world::ownsPlaybackDevice
dr_bool32 ownsPlaybackDevice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:821
dra_sound_world::pPlaybackDevice
dra_device * pPlaybackDevice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:818
dra_src_algorithm_none
@ dra_src_algorithm_none
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:244
dra_mixer_gather_attached_voices_recursive
size_t dra_mixer_gather_attached_voices_recursive(dra_mixer *pMixer, dra_voice **ppVoicesOut)
stb_vorbis_open_filename
stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:5057
dra_sound_set_on_stop
void dra_sound_set_on_stop(dra_sound *pSound, dra_event_proc proc, void *pUserData)
dra_voice::pNextVoice
dra_voice * pNextVoice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:426
dra_mixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:365
dra_mixer::pNextSamplesToMix
float * pNextSamplesToMix
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:408
dra_format
dra_format
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:232
stb_vorbis_stream_length_in_samples
unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:4920
dra_device::thread
dra_thread thread
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:308
dra_sound_desc::format
dra_format format
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:783
dr_uint16
uint16_t dr_uint16
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:189
dra_seek_origin
dra_seek_origin
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:700
dr_int8
int8_t dr_int8
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:186
dr_int64
int64_t dr_int64
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:192
drwav::sampleRate
drwav_uint32 sampleRate
Definition: porcupine/demo/c/dr_libs/dr_wav.h:831
dra_thread_event_type
dra_thread_event_type
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:249
dra_mixer::pFirstVoice
dra_voice * pFirstVoice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:391
dra_voice_get_volume
float dra_voice_get_volume(dra_voice *pVoice)
dra_get_bits_per_sample_by_format
unsigned int dra_get_bits_per_sample_by_format(dra_format format)
dra__event_queue::eventBufferSize
size_t eventBufferSize
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:285
dra_sound::isLooping
dr_bool32 isLooping
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:836
dra_decoder
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:720
dra_device::playingVoicesCount
size_t playingVoicesCount
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:342
dra_sound_world_delete
void dra_sound_world_delete(dra_sound_world *pWorld)
dra_voice::pMixer
dra_mixer * pMixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:422
dra_decoder::channels
unsigned int channels
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:722
dra_mixer_is_paused
dr_bool32 dra_mixer_is_paused(dra_mixer *pMixer)
DRA_RESULT_NO_DECODER
#define DRA_RESULT_NO_DECODER
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:220
dra_sound_create_from_file
dra_sound * dra_sound_create_from_file(dra_sound_world *pWorld, const char *filePath)
drflac_open_memory
DRFLAC_API drflac * drflac_open_memory(const void *pData, size_t dataSize, const drflac_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:56697
dra_mixer_resume
void dra_mixer_resume(dra_mixer *pMixer)
dra_mixer_detach_voice
void dra_mixer_detach_voice(dra_mixer *pMixer, dra_voice *pVoice)
dra_semaphore
void * dra_semaphore
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:266
dra_voice::convertedFrame
float convertedFrame[DR_AUDIO_MAX_CHANNEL_COUNT]
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:466
stb_vorbis_open_memory
stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:5073
dra__event::sampleIndex
dr_uint64 sampleIndex
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:275
dra_device_init_ex
dra_result dra_device_init_ex(dra_context *pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device *pDevice)
DR_TRUE
#define DR_TRUE
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:197
stb_vorbis_get_samples_float_interleaved
int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:5399
dra__memory_stream::dataSize
size_t dataSize
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:716
dra_decoder_open_and_decode_f32
float * dra_decoder_open_and_decode_f32(dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void *pUserData, unsigned int *channels, unsigned int *sampleRate, dr_uint64 *totalSampleCount)
dra_format_s24
@ dra_format_s24
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:236
dra_voice::isLooping
dr_bool32 isLooping
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:453
dra_sound_desc::channels
unsigned int channels
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:786
dra_decoder_on_read_proc
size_t(* dra_decoder_on_read_proc)(void *pUserData, void *pDataOut, size_t bytesToRead)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:706
DR_AUDIO_MAX_CHANNEL_COUNT
#define DR_AUDIO_MAX_CHANNEL_COUNT
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:202
dra_format_u8
@ dra_format_u8
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:234
dra_decoder_read_f32
dr_uint64 dra_decoder_read_f32(dra_decoder *pDecoder, dr_uint64 samplesToRead, float *pSamplesOut)
dra_voice::stopEvent
dra__event stopEvent
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:495
drflac_open
DRFLAC_API drflac * drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:56747
dra_device_stop
dra_result dra_device_stop(dra_device *pDevice)
drflac_seek_origin
drflac_seek_origin
Definition: porcupine/demo/c/dr_libs/dr_flac.h:380
dra_event_proc
void(* dra_event_proc)(dr_uint64 eventID, void *pUserData)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:268
TRUE
#define TRUE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:641
dra_decoder::onDelete
dra_decoder_on_delete_proc onDelete
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:731
dra_voice::prevFrame
float prevFrame[DR_AUDIO_MAX_CHANNEL_COUNT]
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:481
drflac_seek_origin_start
@ drflac_seek_origin_start
Definition: porcupine/demo/c/dr_libs/dr_flac.h:382
dra_mixer_attach_voice
void dra_mixer_attach_voice(dra_mixer *pMixer, dra_voice *pVoice)
dra_decoder_open_file
dra_result dra_decoder_open_file(dra_decoder *pDecoder, const char *filePath)
dra_voice_set_volume
void dra_voice_set_volume(dra_voice *pVoice, float linearVolume)
dra_thread
void * dra_thread
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:264
dra_context_init
dra_result dra_context_init(dra_context *pContext)
dra_voice::src
struct dra_voice::@12 src
dra_mixer_gather_attached_voices
size_t dra_mixer_gather_attached_voices(dra_mixer *pMixer, dra_voice **ppVoicesOut)
dr_bool32
dr_uint32 dr_bool32
Definition: porcupine/demo/c/dr_libs/old/dr.h:70
dra_mixer_detach_all_submixers
void dra_mixer_detach_all_submixers(dra_mixer *pMixer)
dra_decoder_open
dra_result dra_decoder_open(dra_decoder *pDecoder, dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void *pUserData)
dra_voice_get_buffer_ptr_by_sample
void * dra_voice_get_buffer_ptr_by_sample(dra_voice *pVoice, dr_uint64 sample)
dra_sound_world_create
dra_sound_world * dra_sound_world_create(dra_device *pPlaybackDevice)
dra_voice::pPrevVoice
dra_voice * pPrevVoice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:429
count
size_t count
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/tests/test_common/ma_test_common.c:31
dr_uint64
uint64_t dr_uint64
Definition: porcupine/demo/c/dr_libs/old/dr.h:67
dra_decoder::onRead
dra_decoder_on_read_proc onRead
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:726
dr_uint8
uint8_t dr_uint8
Definition: porcupine/demo/c/dr_libs/old/dr.h:61
dra_device::pMasterMixer
dra_mixer * pMasterMixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:338
dra_device_create_ex
dra_result dra_device_create_ex(dra_context *pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device **ppDevice)
dra_decoder_open_and_decode_file_f32
float * dra_decoder_open_and_decode_file_f32(const char *filePath, unsigned int *channels, unsigned int *sampleRate, dr_uint64 *totalSampleCount)
DRA_MIXER_FLAG_PAUSED
#define DRA_MIXER_FLAG_PAUSED
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:224
drflac_open_file
DRFLAC_API drflac * drflac_open_file(const char *pFileName, const drflac_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:56601
dra_decoder::onSeekSamples
dra_decoder_on_seek_samples_proc onSeekSamples
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:733
dra_mixer_count_attached_voices_recursive
size_t dra_mixer_count_attached_voices_recursive(dra_mixer *pMixer)
DRA_RESULT_INVALID_ARGS
#define DRA_RESULT_INVALID_ARGS
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:215
dra_mixer_delete
void dra_mixer_delete(dra_mixer *pMixer)
dra_decoder::totalSampleCount
dr_uint64 totalSampleCount
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:724
dra_decoder::onSeek
dra_decoder_on_seek_proc onSeek
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:727
dra_mixer_set_volume
void dra_mixer_set_volume(dra_mixer *pMixer, float linearVolume)
dra_voice_delete
void dra_voice_delete(dra_voice *pVoice)
dra_device::pBackendDevice
dra_backend_device * pBackendDevice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:301
dra_sound_world_play_inline_3f
void dra_sound_world_play_inline_3f(dra_sound_world *pWorld, dra_sound_desc *pDesc, dra_mixer *pMixer, float xPos, float yPos, float zPos)
dra_mutex
void * dra_mutex
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:265
dra_voice::pUserData
void * pUserData
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:502
dra_seek_origin_start
@ dra_seek_origin_start
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:702
dra_device_type_playback
@ dra_device_type_playback
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:228
dra_semaphore
void * dra_semaphore
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:266
dra_voice::pDevice
dra_device * pDevice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:418
DRA_RESULT_OUT_OF_MEMORY
#define DRA_RESULT_OUT_OF_MEMORY
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:216
dra_voice::data
union dra_voice::@12::@13 data
dr_uint8
uint8_t dr_uint8
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:187
DRA_RESULT_NO_BACKEND
#define DRA_RESULT_NO_BACKEND
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:218
drflac_bool32
drflac_uint32 drflac_bool32
Definition: porcupine/demo/c/dr_libs/dr_flac.h:270
dra_format_s16
@ dra_format_s16
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:235
dra_sound_world_stop_all_sounds
void dra_sound_world_stop_all_sounds(dra_sound_world *pWorld)
L
#define L
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:5102
dra_decoder_open_memory
dra_result dra_decoder_open_memory(dra_decoder *pDecoder, const void *pData, size_t dataSize)
dra_format_s32
@ dra_format_s32
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:237
dra_thread_event_type
dra_thread_event_type
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:249
dra_device_start
dra_result dra_device_start(dra_device *pDevice)
stb_vorbis
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:768
dra_sound::pVoice
dra_voice * pVoice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:830
dra__memory_stream
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:713
dra_voice_create_compatible
dra_result dra_voice_create_compatible(dra_device *pDevice, size_t sizeInBytes, const void *pInitialData, dra_voice **ppVoice)
dra_free
void dra_free(void *p)
dra_voice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:415
DRA_RESULT_UNKNOWN_ERROR
#define DRA_RESULT_UNKNOWN_ERROR
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:214
dra_voice_get_playback_position
dr_uint64 dra_voice_get_playback_position(dra_voice *pVoice)
dra_sound_desc::dataSize
size_t dataSize
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:793
dra_voice_set_playback_position
void dra_voice_set_playback_position(dra_voice *pVoice, dr_uint64 sampleIndex)
dra_seek_origin
dra_seek_origin
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:700
dra_sound_play
void dra_sound_play(dra_sound *pSound, dr_bool32 loop)
drwav::channels
drwav_uint16 channels
Definition: porcupine/demo/c/dr_libs/dr_wav.h:834
dra_context_create
dra_result dra_context_create(dra_context **ppContext)
dra_device_type
dra_device_type
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:226
python.pvrecorder.CALLBACK
CALLBACK
Definition: porcupine/demo/c/pvrecorder/sdk/python/pvrecorder.py:17
dra_mixer_attach_submixer
void dra_mixer_attach_submixer(dra_mixer *pMixer, dra_mixer *pSubmixer)
dra_mixer::pDevice
dra_device * pDevice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:368
stb_vorbis_info::sample_rate
unsigned int sample_rate
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:126
dra_result
int dra_result
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:212
dra_sound_on_seek_proc
dr_bool32(* dra_sound_on_seek_proc)(dra_sound *pSound, dr_uint64 sample)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:778
dra_context::pBackend
dra_backend * pBackend
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:292
dra_mixer_create
dra_result dra_mixer_create(dra_device *pDevice, dra_mixer **ppMixer)
dra_mixer::pPrevSiblingMixer
dra_mixer * pPrevSiblingMixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:387
dra_device_set_samples_processed_callback
void dra_device_set_samples_processed_callback(dra_device *pDevice, dra_samples_processed_proc proc, void *pUserData)
dra_device::onSamplesProcessed
dra_samples_processed_proc onSamplesProcessed
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:354
dra_decoder::memoryStream
dra__memory_stream memoryStream
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:736
dra_mixer::pNextSiblingMixer
dra_mixer * pNextSiblingMixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:384
dra_device::nextThreadEventType
dra_thread_event_type nextThreadEventType
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:314
dra_voice::sizeInBytes
size_t sizeInBytes
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:506
dra_decoder_on_read_samples_proc
dr_uint64(* dra_decoder_on_read_samples_proc)(void *pBackendDecoder, dr_uint64 samplesToRead, float *pSamplesOut)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:710
dr_int32
int32_t dr_int32
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:190
dra_src_algorithm_linear
@ dra_src_algorithm_linear
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:245
drwav
Definition: porcupine/demo/c/dr_libs/dr_wav.h:805
stb_vorbis_info::channels
int channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:127
dra_mixer_get_volume
float dra_mixer_get_volume(dra_mixer *pMixer)
dra_voice::sampleRate
unsigned int sampleRate
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:439
dr_int32
int32_t dr_int32
Definition: porcupine/demo/c/dr_libs/old/dr.h:64
dra_voice::linear
struct dra_voice::@12::@13::@14 linear
dra_mixer::pLastVoice
dra_voice * pLastVoice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:394
dra_voice_add_playback_event
dr_bool32 dra_voice_add_playback_event(dra_voice *pVoice, dr_uint64 sampleIndex, dr_uint64 eventID, dra_event_proc proc, void *pUserData)
DR_AUDIO_EVENT_ID_STOP
#define DR_AUDIO_EVENT_ID_STOP
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:209
dra_decoder_seek_to_sample
dr_bool32 dra_decoder_seek_to_sample(dra_decoder *pDecoder, dr_uint64 sample)
dr_uint32
uint32_t dr_uint32
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:191
dra__event::id
dr_uint64 id
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:273
dra_context
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:290
stb_vorbis_close
void stb_vorbis_close(stb_vorbis *f)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:4246
dra_mixer::flags
dr_uint32 flags
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:371
dra_device::threadEventSem
dra_semaphore threadEventSem
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:311
dr_uint64
uint64_t dr_uint64
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:193
dra_voice::algorithm
dra_src_algorithm algorithm
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:474
dra_result
int dra_result
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:212
drflac_close
DRFLAC_API void drflac_close(drflac *pFlac)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:56763
dra_mixer::pParentMixer
dra_mixer * pParentMixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:375
dra_sound::pWorld
dra_sound_world * pWorld
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:827
dra_voice::prevFrameIndex
dr_uint64 prevFrameIndex
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:480
dra_sound_delete
void dra_sound_delete(dra_sound *pSound)
dra_device::pUserDataForOnSamplesProcessed
void * pUserDataForOnSamplesProcessed
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:355
dra__event::proc
dra_event_proc proc
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:276
drflac::sampleRate
drflac_uint32 sampleRate
Definition: porcupine/demo/c/dr_libs/dr_flac.h:701
dra__memory_stream::currentReadPos
size_t currentReadPos
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:717
dra_format_default
@ dra_format_default
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:239
dra_sound_attach_to_mixer
void dra_sound_attach_to_mixer(dra_sound *pSound, dra_mixer *pMixer)
dra__event::pVoice
dra_voice * pVoice
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:277
dra_voice::isPlaying
dr_bool32 isPlaying
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:449
dra_voice_set_on_play
void dra_voice_set_on_play(dra_voice *pVoice, dra_event_proc proc, void *pUserData)
dra_voice::playEvent
dra__event playEvent
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:498
dra_device_type
dra_device_type
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:226
dra_voice_create_from_file
dra_result dra_voice_create_from_file(dra_device *pDevice, const char *filePath, dra_voice **ppVoice)
dra__event_queue::eventCount
size_t eventCount
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:284
dra_decoder_on_seek_samples_proc
dr_bool32(* dra_decoder_on_seek_samples_proc)(void *pBackendDecoder, dr_uint64 sample)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:711
dra_decoder_open_and_decode_memory_f32
float * dra_decoder_open_and_decode_memory_f32(const void *pData, size_t dataSize, unsigned int *channels, unsigned int *sampleRate, dr_uint64 *totalSampleCount)
dra_device_create
dra_result dra_device_create(dra_context *pContext, dra_device_type type, dra_device **ppDevice)
DRA_RESULT_NO_BACKEND_DEVICE
#define DRA_RESULT_NO_BACKEND_DEVICE
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:219
DRA_RESULT_FAILED_TO_OPEN_FILE
#define DRA_RESULT_FAILED_TO_OPEN_FILE
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:217
assert.h
dra_decoder_on_seek_proc
dr_bool32(* dra_decoder_on_seek_proc)(void *pUserData, int offset, dra_seek_origin origin)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:707
dra_decoder::sampleRate
unsigned int sampleRate
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:723
dra_sound_world_set_listener_position
void dra_sound_world_set_listener_position(dra_sound_world *pWorld, float xPos, float yPos, float zPos)
dra_voice::frameCount
dr_uint64 frameCount
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:457
dra_device::ownsContext
dr_bool32 ownsContext
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:320
dra_sound_desc::pUserData
void * pUserData
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:812
dra_device_init
dra_result dra_device_init(dra_context *pContext, dra_device_type type, dra_device *pDevice)
dra_voice::playbackEventCount
size_t playbackEventCount
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:489
dra_thread_event_type_play
@ dra_thread_event_type_play
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:253
dra_context_delete
void dra_context_delete(dra_context *pContext)
dra_thread
void * dra_thread
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:264
dra_mixer::pFirstChildMixer
dra_mixer * pFirstChildMixer
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:378
dra_device::channels
unsigned int channels
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:359
dra_sound_on_read_proc
dr_uint64(* dra_sound_on_read_proc)(dra_sound *pSound, dr_uint64 samplesToRead, void *pSamplesOut)
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:777
dra__memory_stream::data
const dr_uint8 * data
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:715
dra_device::stopOnNextFragment
dr_bool32 stopOnNextFragment
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:334
dra__event
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:271
dr_bool32
dr_int32 dr_bool32
Definition: rhino/demo/c/dr_libs/old/dr_audio.h:196
dra_voice::channels
unsigned int channels
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:436
dra_device::sampleRate
unsigned int sampleRate
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:362
dra_mixer::pData
float pData[1]
Definition: porcupine/demo/c/dr_libs/old/dr_audio.h:412


picovoice_driver
Author(s):
autogenerated on Fri Apr 1 2022 02:13:49