pvrecorder/src/miniaudio/research/miniaudio_engine.h
Go to the documentation of this file.
1 /* !!! THIS FILE WILL BE MERGED INTO miniaudio.h WHEN COMPLETE !!! */
2 
3 /*
4 EXPERIMENTAL
5 ============
6 Everything in this file is experimental and subject to change. Some stuff isn't yet implemented, in particular spatialization. I've noted some ideas that are
7 basically straight off the top of my head - many of these are probably outright wrong or just generally bad ideas.
8 
9 Very simple APIs for spatialization are declared by not yet implemented. They're just placeholders to give myself an idea on some of the API design.
10 
11 The idea is that you have an `ma_engine` object - one per listener. Decoupled from that is the `ma_resource_manager` object. You can have one `ma_resource_manager`
12 object to many `ma_engine` objects. This will allow you to share resources between each listener. The `ma_engine` is responsible for the playback of audio from a
13 list of data sources. The `ma_resource_manager` is responsible for the actual loading, caching and unloading of those data sources. This decoupling is
14 something that I'm really liking right now and will likely stay in place for the final version.
15 
16 You create "sounds" from the engine which represent a sound/voice in the world. You first need to create a sound, and then you need to start it. Sounds do not
17 start by default. You can use `ma_engine_play_sound()` to "fire and forget" sounds.
18 
19 Sounds can be allocated to groups called `ma_sound_group`. This is how you can support submixing and is one way you could achieve the kinds of groupings you see
20 in games for things like SFX, Music and Voices. Unlike sounds, groups are started by default. When you stop a group, all sounds within that group will be
21 stopped atomically. When the group is started again, all sounds attached to the group will also be started, so long as the sound is also marked as started.
22 
23 The creation and deletion of sounds and groups should be thread safe.
24 
25 The engine runs on top of a node graph, and sounds and groups are just nodes within that graph. The output of a sound can be attached to the input of any node
26 on the graph. To apply an effect to a sound or group, attach it's output to the input of an effect node. See the Routing Infrastructure section below for
27 details on this.
28 
29 The best resource to use when understanding the API is the function declarations for `ma_engine`. I expect you should be able to figure it out! :)
30 */
31 #ifndef miniaudio_engine_h
32 #define miniaudio_engine_h
33 
34 #ifdef __cplusplus
35 extern "C" {
36 #endif
37 
38 /*
39 Memory Allocation Types
40 =======================
41 When allocating memory you may want to optimize your custom allocators based on what it is miniaudio is actually allocating. Normally the context in which you
42 are using the allocator is enough to optimize allocations, however there are high-level APIs that perform many different types of allocations and it can be
43 useful to be told exactly what it being allocated so you can optimize your allocations appropriately.
44 */
45 #define MA_ALLOCATION_TYPE_GENERAL 0x00000001 /* A general memory allocation. */
46 #define MA_ALLOCATION_TYPE_CONTEXT 0x00000002 /* A ma_context allocation. */
47 #define MA_ALLOCATION_TYPE_DEVICE 0x00000003 /* A ma_device allocation. */
48 #define MA_ALLOCATION_TYPE_DECODER 0x00000004 /* A ma_decoder allocation. */
49 #define MA_ALLOCATION_TYPE_AUDIO_BUFFER 0x00000005 /* A ma_audio_buffer allocation. */
50 #define MA_ALLOCATION_TYPE_ENCODED_BUFFER 0x00000006 /* Allocation for encoded audio data containing the raw file data of a sound file. */
51 #define MA_ALLOCATION_TYPE_DECODED_BUFFER 0x00000007 /* Allocation for decoded audio data from a sound file. */
52 #define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER_NODE 0x00000010 /* A ma_resource_manager_data_buffer_node object. */
53 #define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER 0x00000011 /* A ma_resource_manager_data_buffer_node object. */
54 #define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_STREAM 0x00000012 /* A ma_resource_manager_data_stream object. */
55 #define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_SOURCE 0x00000013 /* A ma_resource_manager_data_source object. */
56 
57 
58 /*
59 Paged Audio Buffer
60 ==================
61 A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It
62 can be used for cases where audio data is streamed in asynchronously while allowing data to be read
63 at the same time.
64 
65 This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across
66 simultaneously across different threads, however only one thread at a time can append, and only one
67 thread at a time can read and seek.
68 */
71 {
75 };
76 
77 typedef struct
78 {
79  ma_format format;
80  ma_uint32 channels;
81  ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */
82  MA_ATOMIC ma_paged_audio_buffer_page* pTail; /* Never null. Initially set to &head. */
84 
90 MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage);
93 MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks);
94 
95 
96 typedef struct
97 {
98  ma_paged_audio_buffer_data* pData; /* Must not be null. */
100 
102 
103 
104 typedef struct
105 {
107  ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */
108  ma_paged_audio_buffer_page* pCurrent;
109  ma_uint64 relativeCursor; /* Relative to the current page. */
110  ma_uint64 absoluteCursor;
112 
115 MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */
119 
120 
121 /*
122 Resource Management
123 ===================
124 Many programs will want to manage sound resources for things such as reference counting and streaming. This is supported by miniaudio via the
125 `ma_resource_manager` API.
126 
127 The resource manager is mainly responsible for the following:
128 
129  1) Loading of sound files into memory with reference counting.
130  2) Streaming of sound data
131 
132 When loading a sound file, the resource manager will give you back a data source compatible object called `ma_resource_manager_data_source`. This object can be
133 passed into any `ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you specify whether or not you want the sound to
134 be fully loaded into memory (and optionally pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want the data to be
135 loaded asynchronously.
136 
137 The example below is how you can initialize a resource manager using it's default configuration:
138 
139  ```c
140  ma_resource_manager_config config;
141  ma_resource_manager resourceManager;
142 
143  config = ma_resource_manager_config_init();
144  result = ma_resource_manager_init(&config, &resourceManager);
145  if (result != MA_SUCCESS) {
146  ma_device_uninit(&device);
147  printf("Failed to initialize the resource manager.");
148  return -1;
149  }
150  ```
151 
152 You can configure the format, channels and sample rate of the decoded audio data. By default it will use the file's native data format, but you can configure
153 it to use a consistent format. This is useful for offloading the cost of data conversion to load time rather than dynamically converting a mixing time. To do
154 this, you configure the decoded format, channels and sample rate like the code below:
155 
156  ```c
157  config = ma_resource_manager_config_init();
158  config.decodedFormat = device.playback.format;
159  config.decodedChannels = device.playback.channels;
160  config.decodedSampleRate = device.sampleRate;
161  ```
162 
163 In the code above, the resource manager will be configured so that any decoded audio data will be pre-converted at load time to the device's native data
164 format. If instead you used defaults and the data format of the file did not match the device's data format, you would need to convert the data at mixing time
165 which may be prohibitive in high-performance and large scale scenarios like games.
166 
167 Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the decoding of a page, a job will be posted to a queue which
168 will then be processed by a job thread. By default there will be only one job thread running, but this can be configured, like so:
169 
170  ```c
171  config = ma_resource_manager_config_init();
172  config.jobThreadCount = MY_JOB_THREAD_COUNT;
173  ```
174 
175 By default job threads are managed internally by the resource manager, however you can also self-manage your job threads if, for example, you want to integrate
176 the job processing into your existing job infrastructure, or if you simply don't like the way the resource manager does it. To do this, just set the job thread
177 count to 0 and process jobs manually. To process jobs, you first need to retrieve a job using `ma_resource_manager_next_job()` and then process it using
178 `ma_resource_manager_process_job()`:
179 
180  ```c
181  config = ma_resource_manager_config_init();
182  config.jobThreadCount = 0; // Don't manage any job threads internally.
183  config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking.
184 
185  // ... Initialize your custom job threads ...
186 
187  void my_custom_job_thread(...)
188  {
189  for (;;) {
190  ma_job job;
191  ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
192  if (result != MA_SUCCESS) {
193  if (result == MA_NOT_DATA_AVAILABLE) {
194  // No jobs are available. Keep going. Will only get this if the resource manager was initialized
195  // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
196  continue;
197  } else if (result == MA_CANCELLED) {
198  // MA_JOB_QUIT was posted. Exit.
199  break;
200  } else {
201  // Some other error occurred.
202  break;
203  }
204  }
205 
206  ma_resource_manager_process_job(pMyResourceManager, &job);
207  }
208  }
209  ```
210 
211 In the example above, the MA_JOB_QUIT event is the used as the termination indicator. You can instead use whatever variable you would like to terminate the
212 thread. The call to `ma_resource_manager_next_job()` is blocking by default, by can be configured to be non-blocking by initializing the resource manager
213 with the MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING configuration flag.
214 
215 When loading a file, it's sometimes convenient to be able to customize how files are opened and read. This can be done by setting `pVFS` member of the
216 resource manager's config:
217 
218  ```c
219  // Initialize your custom VFS object. See documentation for VFS for information on how to do this.
220  my_custom_vfs vfs = my_custom_vfs_init();
221 
222  config = ma_resource_manager_config_init();
223  config.pVFS = &vfs;
224  ```
225 
226 If you do not specify a custom VFS, the resource manager will use the operating system's normal file operations. This is default.
227 
228 To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When loading a sound you need to specify the file path and
229 options for how the sounds should be loaded. By default a sound will be loaded synchronously. The returned data source is owned by the caller which means the
230 caller is responsible for the allocation and freeing of the data source. Below is an example for initializing a data source:
231 
232  ```c
233  ma_resource_manager_data_source dataSource;
234  ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource);
235  if (result != MA_SUCCESS) {
236  // Error.
237  }
238 
239  // ...
240 
241  // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call
242  // the `ma_data_source_read_pcm_frames()` like you would with any normal data source.
243  result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead);
244  if (result != MA_SUCCESS) {
245  // Failed to read PCM frames.
246  }
247 
248  // ...
249 
250  ma_resource_manager_data_source_uninit(pResourceManager, &dataSource);
251  ```
252 
253 The `flags` parameter specifies how you want to perform loading of the sound file. It can be a combination of the following flags:
254 
255  ```
256  MA_DATA_SOURCE_STREAM
257  MA_DATA_SOURCE_DECODE
258  MA_DATA_SOURCE_ASYNC
259  ```
260 
261 When no flags are specified (set to 0), the sound will be fully loaded into memory, but not decoded, meaning the raw file data will be stored in memory, and
262 then dynamically decoded when `ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in memory, use the
263 `MA_DATA_SOURCE_DECODE` flag. By default, the sound file will be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after
264 the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You can instead load the sound asynchronously using the
265 `MA_DATA_SOURCE_ASYNC` flag. This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be returned by
266 `ma_data_source_read_pcm_frames()` until some data is available. When no data is available because the asynchronous decoding hasn't caught up, MA_BUSY will be
267 returned by `ma_data_source_read_pcm_frames()`.
268 
269 For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you can instead stream audio data which you can do by specifying
270 the `MA_DATA_SOURCE_STREAM` flag. When streaming, data will be decoded in 1 second pages. When a new page needs to be decoded, a job will be posted to the job
271 queue and then subsequently processed in a job thread.
272 
273 When loading asynchronously, it can be useful to poll whether or not loading has finished. Use `ma_resource_manager_data_source_result()` to determine this.
274 For in-memory sounds, this will return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, `MA_BUSY` will be returned.
275 Otherwise, some other error code will be returned if the sound failed to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page
276 has been decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` will be returned. Otherwise, some other error code
277 will be returned if the sound failed to load.
278 
279 For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means multiple calls to `ma_resource_manager_data_source_init()`
280 with the same file path will result in the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be matched up with a
281 call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful for a program to register self-managed raw audio data and associate it with a
282 file path. Use `ma_resource_manager_register_decoded_data()`, `ma_resource_manager_register_encoded_data()` and `ma_resource_manager_unregister_data()` to do
283 this. `ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed decoded audio data in the specified data format with
284 the specified name. Likewise, `ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed encoded audio data (the raw
285 file data) with the specified name. Note that these names need not be actual file paths. When `ma_resource_manager_data_source_init()` is called (without the
286 `MA_DATA_SOURCE_STREAM` flag), the resource manager will look for these explicitly registered data buffers and, if found, will use it as the backing data for
287 the data source. Note that the resource manager does *not* make a copy of this data so it is up to the caller to ensure the pointer stays valid for it's
288 lifetime. Use `ma_resource_manager_unregister_data()` to unregister the self-managed data. It does not make sense to use the `MA_DATA_SOURCE_STREAM` flag with
289 a self-managed data pointer. When `MA_DATA_SOURCE_STREAM` is specified, it will try loading the file data through the VFS.
290 
291 
292 Resource Manager Implementation Details
293 ---------------------------------------
294 Resources are managed in two main ways:
295 
296  1) By storing the entire sound inside an in-memory buffer (referred to as a data buffer - `ma_resource_manager_data_buffer_node`)
297  2) By streaming audio data on the fly (referred to as a data stream - `ma_resource_manager_data_stream`)
298 
299 A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or data stream, depending on whether or not the data source was
300 initialized with the `MA_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a `ma_resource_manager_data_stream` object. Otherwise it will use a
301 `ma_resource_manager_data_buffer_node` object.
302 
303 Another major feature of the resource manager is the ability to asynchronously decode audio files. This relieves the audio thread of time-consuming decoding
304 which can negatively affect scalability due to the audio thread needing to complete it's work extremely quickly to avoid glitching. Asynchronous decoding is
305 achieved through a job system. There is a central multi-producer, multi-consumer, lock-free, fixed-capacity job queue. When some asynchronous work needs to be
306 done, a job is posted to the queue which is then read by a job thread. The number of job threads can be configured for improved scalability, and job threads
307 can all run in parallel without needing to worry about the order of execution (how this is achieved is explained below).
308 
309 When a sound is being loaded asynchronously, playback can begin before the sound has been fully decoded. This enables the application to start playback of the
310 sound quickly, while at the same time allowing to resource manager to keep loading in the background. Since there may be less threads than the number of sounds
311 being loaded at a given time, a simple scheduling system is used to keep decoding time fair. The resource manager solves this by splitting decoding into chunks
312 called pages. By default, each page is 1 second long. When a page has been decoded, the a new job will be posted to start decoding the next page. By dividing
313 up decoding into pages, an individual sound shouldn't ever delay every other sound from having their first page decoded. Of course, when loading many sounds at
314 the same time, there will always be an amount of time required to process jobs in the queue so in heavy load situations there will still be some delay. To
315 determine if a data source is ready to have some frames read, use `ma_resource_manager_data_source_get_available_frames()`. This will return the number of
316 frames available starting from the current position.
317 
318 
319 Data Buffers
320 ------------
321 When the `MA_DATA_SOURCE_FLAG_STREAM` flag is not specified at initialization time, the resource manager will try to load the data into an in-memory data
322 buffer. Before doing so, however, it will first check if the specified file has already been loaded. If so, it will increment a reference counter and just use
323 the already loaded data. This saves both time and memory. A binary search tree (BST) is used for storing data buffers as it has good balance between efficiency
324 and simplicity. The key of the BST is a 64-bit hash of the file path that was passed into `ma_resource_manager_data_source_init()`. The advantage of using a
325 hash is that it saves memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST due to the random nature of the hash.
326 The disadvantage is that file names are case-sensitive. If this is an issue, you should normalize your file names to upper- or lower-case before initializing
327 your data sources.
328 
329 When a sound file has not already been loaded and the `MA_DATA_SOURCE_ASYNC` is not specified, the file will be decoded synchronously by the calling thread.
330 There are two options for controlling how the audio is stored in the data buffer - encoded or decoded. When the `MA_DATA_SOURCE_DECODE` option is not
331 specified, the raw file data will be stored in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is a very simple
332 and standard process of simply adding an item to the BST, allocating a block of memory and then decoding (if `MA_DATA_SOURCE_DECODE` is specified).
333 
334 When the `MA_DATA_SOURCE_ASYNC` flag is specified, loading of the data buffer is done asynchronously. In this case, a job is posted to the queue to start
335 loading and then the function instantly returns, setting an internal result code to `MA_BUSY`. This result code is returned when the program calls
336 `ma_resource_manager_data_source_result()`. When decoding has fully completed, `MA_RESULT` will be returned. This can be used to know if loading has fully
337 completed.
338 
339 When loading asynchronously, a single job is posted to the queue of the type `MA_JOB_LOAD_DATA_BUFFER`. This involves making a copy of the file path and
340 associating it with job. When the job is processed by the job thread, it will first load the file using the VFS associated with the resource manager. When
341 using a custom VFS, it's important that it be completely thread-safe because it will be used from one or more job threads at the same time. Individual files
342 should only ever be accessed by one thread at a time, however. After opening the file via the VFS, the job will determine whether or not the file is being
343 decoded. If not, it simply allocates a block of memory and loads the raw file contents into it and returns. On the other hand, when the file is being decoded,
344 it will first allocate a decoder on the heap and initialize it. Then it will check if the length of the file is known. If so it will allocate a block of memory
345 to store the decoded output and initialize it to silence. If the size is unknown, it will allocate room for one page. After memory has been allocated, the
346 first page will be decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the completion event will be signalled and
347 loading is now complete. If, however, there is store more to decode, a job with the code `MA_JOB_PAGE_DATA_BUFFER` is posted. This job will decode the next
348 page and perform the same process if it reaches the end. If there is more to decode, the job will post another `MA_JOB_PAGE_DATA_BUFFER` job which will keep on
349 happening until the sound has been fully decoded. For sounds of an unknown length, the buffer will be dynamically expanded as necessary, and then shrunk with a
350 final realloc() when the end of the file has been reached.
351 
352 
353 Data Streams
354 ------------
355 Data streams only ever store two pages worth of data for each sound. They are most useful for large sounds like music tracks in games which would consume too
356 much memory if fully decoded in memory. Only two pages of audio data are stored in memory at a time for each data stream. After every frame from a page has
357 been read, a job will be posted to load the next page which is done from the VFS.
358 
359 For data streams, the `MA_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or not initialization of the data source waits until the two pages have been
360 decoded. When unset, `ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise it will return immediately.
361 
362 When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, `MA_BUSY` will be returned if there are no frames available.
363 If there are some frames available, but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames read will be less than
364 the number requested. Due to the asymchronous nature of data streams, seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY`
365 will be returned when trying to read frames.
366 
367 When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed, a job is posted to load the next page. This will be posted
368 from the same thread that called `ma_resource_manager_data_source_read_pcm_frames()` which should be lock-free.
369 
370 Data streams are uninitialized by posting a job to the queue, but the function won't return until that job has been processed. The reason for this is that the
371 caller owns the data stream object and therefore we need to ensure everything completes before handing back control to the caller. Also, if the data stream is
372 uninitialized while pages are in the middle of decoding, they must complete before destroying any underlying object and the job system handles this cleanly.
373 
374 
375 Job Queue
376 ---------
377 The resource manager uses a job queue which is multi-producer, multi-consumer, lock-free and fixed-capacity. The lock-free property of the queue is achieved
378 using the algorithm described by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors. In
379 order for this to work, only a fixed number of jobs can be allocated and inserted into the queue which is done through a lock-free data structure for
380 allocating an index into a fixed sized array, with reference counting for mitigation of the ABA problem. The reference count is 32-bit.
381 
382 For many types of jobs it's important that they execute in a specific order. In these cases, jobs are executed serially. The way in which each type of job
383 handles this is specific to the job type. For the resource manager, serial execution of jobs is only required on a per-object basis (per data buffer or per
384 data stream). Each of these objects stores an execution counter. When a job is posted it is associated with an execution counter. When the job is processed, it
385 checks if the execution counter of the job equals the execution counter of the owning object and if so, processes the job. If the counters are not equal, the
386 job will be posted back onto the job queue for later processing. When the job finishes processing the execution order of the main object is incremented. This
387 system means the no matter how many job threads are executing, decoding of an individual sound will always get processed serially. The advantage to having
388 multiple threads comes into play when loading multiple sounds at the time time.
389 */
390 MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains);
391 MA_API void ma_apply_volume_factor_per_channel_f32(float* pFramesOut, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains);
392 
395 
396 
397 typedef struct
398 {
399  ma_uint32 channels;
400  ma_uint32 smoothTimeInFrames;
402 
403 MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames);
404 
405 
406 typedef struct
407 {
409  ma_uint32 t;
410  float* pOldGains;
411  float* pNewGains;
412 
413  /* Memory management. */
414  void* _pHeap;
415  ma_bool32 _ownsHeap;
416 } ma_gainer;
417 
418 MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes);
419 MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer);
420 MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer);
421 MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks);
422 MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
423 MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain);
424 MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains);
425 
426 
427 /*
428 Routing Infrastructure
429 ======================
430 miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a
431 node whose outputs are attached to inputs of another node, thereby creating a graph. There are
432 different types of nodes, with each node in the graph processing input data to produce output,
433 which is then fed through the chain. Each node in the graph can apply their own custom effects. At
434 the end of the graph is an endpoint which represents the end of the chain and is where the final
435 output is ultimately extracted from.
436 
437 Each node has a number of input buses and a number of output buses. An output bus from a node is
438 attached to an input bus of another. Multiple nodes can connect their output buses to another
439 node's input bus, in which case their outputs will be mixed before processing by the node.
440 
441 Each input bus must be configured to accept the same number of channels, but input buses and output
442 buses can each have different channel counts, in which case miniaudio will automatically convert
443 the input data to the output channel count before processing. The number of channels of an output
444 bus of one node must match the channel count of the input bus it's attached to. The channel counts
445 cannot be changed after the node has been initialized. If you attempt to attach an output bus to
446 an input bus with a different channel count, attachment will fail.
447 
448 To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a
449 container around the entire graph. The `ma_node_graph` object is required for some thread-safety
450 issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's
451 standard config/init system:
452 
453  ```c
454  ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount);
455 
456  result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks.
457  if (result != MA_SUCCESS) {
458  // Failed to initialize node graph.
459  }
460  ```
461 
462 When you initialize the node graph, you're specifying the channel count of the endpoint. The
463 endpoint is a special node which has one input bus and one output bus, both of which have the
464 same channel count, which is specified in the config. Any nodes that connect directly to the
465 endpoint must be configured such that their output buses have the same channel count. When you read
466 audio data from the node graph, it'll have the channel count you specified in the config. To read
467 data from the graph:
468 
469  ```c
470  ma_uint32 framesRead;
471  result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead);
472  if (result != MA_SUCCESS) {
473  // Failed to read data from the node graph.
474  }
475  ```
476 
477 When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
478 data from it's input attachments, which in turn recusively pull in data from their inputs, and so
479 on. At the very base of the graph there will be some kind of data source node which will have zero
480 inputs and will instead read directly from a data source. The base nodes don't literally need to
481 read from a `ma_data_source` object, but they will always have some kind of underlying object that
482 sources some kind of audio. The `ma_data_source_node` node can be used to read from a
483 `ma_data_source`. Data is always in floating-point format and in the number of channels you
484 specified when the graph was initialized. The sample rate is defined by the underlying data sources.
485 It's up to you to ensure they use a consistent and appropraite sample rate.
486 
487 The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
488 miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
489 node which reads directly from a data source (`ma_data_source_node`) which is an example of one
490 of the stock nodes that comes with miniaudio:
491 
492  ```c
493  ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource, isLooping);
494 
495  ma_data_source_node dataSourceNode;
496  result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode);
497  if (result != MA_SUCCESS) {
498  // Failed to create data source node.
499  }
500  ```
501 
502 The data source node will use the output channel count to determine the channel count of the output
503 bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data
504 source). The data source must output to floating-point (ma_format_f32) or else an error will be
505 returned from `ma_data_source_node_init()`.
506 
507 By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`:
508 
509  ```c
510  result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0);
511  if (result != MA_SUCCESS) {
512  // Failed to attach node.
513  }
514  ```
515 
516 The code above connects the data source node directly to the endpoint. Since the data source node
517 has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single
518 input bus which means the input bus index will also always be 0.
519 
520 To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use
521 `ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to
522 another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll
523 deal with it for you.
524 
525 Less frequently you may want to create a specialized node. This will be a node where you implement
526 your own processing callback to apply a custom effect of some kind. This is similar to initalizing
527 one of the stock node types, only this time you need to specify a pointer to a vtable containing a
528 pointer to the processing function and the number of input and output buses. Example:
529 
530  ```c
531  static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
532  {
533  // Do some processing of ppFramesIn (one stream of audio data per input bus)
534  const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0.
535  const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1.
536  float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0.
537 
538  // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each
539  // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers
540  // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames
541  // your node consumed and `pFrameCountOut` should be set the number of output frames that
542  // were produced.
543  //
544  // You should process as many frames as you can. If your effect consumes input frames at the
545  // same rate as output frames (always the case, unless you're doing resampling), you need
546  // only look at `ppFramesOut` and process that exact number of frames. If you're doing
547  // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut`
548  // properly.
549  }
550 
551  static ma_node_vtable my_custom_node_vtable =
552  {
553  my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing.
554  NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
555  2, // 2 input buses.
556  1, // 1 output bus.
557  0 // Default flags.
558  };
559 
560  ...
561 
562  // Each bus needs to have a channel count specified. To do this you need to specify the channel
563  // counts in an array and then pass that into the node config.
564  ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable.
565  ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable.
566 
567  inputChannels[0] = channelsIn;
568  inputChannels[1] = channelsIn;
569  outputChannels[0] = channelsOut;
570 
571  ma_node_config nodeConfig = ma_node_config_init();
572  nodeConfig.vtable = &my_custom_node_vtable;
573  nodeConfig.pInputChannels = inputChannels;
574  nodeConfig.pOutputChannels = outputChannels;
575 
576  ma_node_base node;
577  result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node);
578  if (result != MA_SUCCESS) {
579  // Failed to initialize node.
580  }
581  ```
582 
583 When initializing a custom node, as in the code above, you'll normally just place your vtable in
584 static space. The number of input and output buses are specified as part of the vtable. If you need
585 a variable number of buses on a per-node bases, the vtable should have the relevant bus count set
586 to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config:
587 
588  ```c
589  static ma_node_vtable my_custom_node_vtable =
590  {
591  my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing.
592  NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
593  MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis.
594  1, // 1 output bus.
595  0 // Default flags.
596  };
597 
598  ...
599 
600  ma_node_config nodeConfig = ma_node_config_init();
601  nodeConfig.vtable = &my_custom_node_vtable;
602  nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here.
603  nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array.
604  nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array.
605  ```
606 
607 In the above example it's important to never set the `inputBusCount` and `outputBusCount` members
608 to anything other than their defaults if the vtable specifies an explicit count. They can only be
609 set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count.
610 
611 Most often you'll want to create a structure to encapsulate your node with some extra data. You
612 need to make sure the `ma_node_base` object is your first member of the structure:
613 
614  ```c
615  typedef struct
616  {
617  ma_node_base base; // <-- Make sure this is always the first member.
618  float someCustomData;
619  } my_custom_node;
620  ```
621 
622 By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the
623 graph just like any other node.
624 
625 In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the
626 number of channels for each bus is what was specified by the config when the node was initialized
627 with `ma_node_init()`. In addition, all attachments to each of the input buses will have been
628 pre-mixed by miniaudio. The config allows you to specify different channel counts for each
629 individual input and output bus. It's up to the effect to handle it appropriate, and if it can't,
630 return an error in it's initialization routine.
631 
632 Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable
633 and include the following:
634 
635  +-----------------------------------------+---------------------------------------------------+
636  | Flag Name | Description |
637  +-----------------------------------------+---------------------------------------------------+
638  | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio |
639  | | processing, but are instead used for tracking |
640  | | time, handling events, etc. Also used by the |
641  | | internal endpoint node. It reads directly from |
642  | | the input bus to the output bus. Nodes with this |
643  | | flag must have exactly 1 input bus and 1 output |
644  | | bus, and both buses must have the same channel |
645  | | counts. |
646  +-----------------------------------------+---------------------------------------------------+
647  | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even |
648  | | when no data is available to be read from input |
649  | | attachments. This is useful for effects like |
650  | | echos where there will be a tail of audio data |
651  | | that still needs to be processed even when the |
652  | | original data sources have reached their ends. |
653  +-----------------------------------------+---------------------------------------------------+
654  | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with |
655  | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this |
656  | | is set, the `ppFramesIn` parameter of the |
657  | | processing callback will be set to NULL when |
658  | | there are no input frames are available. When |
659  | | this is unset, silence will be posted to the |
660  | | processing callback. |
661  +-----------------------------------------+---------------------------------------------------+
662  | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output |
663  | | frames are processed at different rates. You |
664  | | should set this for any nodes that perform |
665  | | resampling. |
666  +-----------------------------------------+---------------------------------------------------+
667 
668 
669 If you need to make a copy of an audio stream for effect processing you can use a splitter node
670 called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses.
671 You can use it like this:
672 
673  ```c
674  ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut);
675 
676  ma_splitter_node splitterNode;
677  result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
678  if (result != MA_SUCCESS) {
679  // Failed to create node.
680  }
681 
682  // Attach your output buses to two different input buses (can be on two different nodes).
683  ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint.
684  ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node.
685  ```
686 
687 The volume of an output bus can be configured on a per-bus basis:
688 
689  ```c
690  ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);
691  ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);
692  ```
693 
694 In the code above we're using the splitter node from before and changing the volume of each of the
695 copied streams.
696 
697 You can start, stop and mute a node with the following:
698 
699  ```c
700  ma_node_set_state(&splitterNode, ma_node_state_started); // The default state.
701  ma_node_set_state(&splitterNode, ma_node_state_stopped);
702  ma_node_set_state(&splitterNode, ma_node_state_muted);
703  ```
704 
705 By default the node is in a started state, but since it won't be connected to anything won't
706 actually be invoked by the node graph until it's connected. When you stop a node, data will not be
707 read from any of it's input connections. You can use this property to stop a group of sounds
708 atomically.
709 
710 You can configure the initial state of a node in it's config:
711 
712  ```c
713  nodeConfig.initialState = ma_node_state_stopped;
714  ```
715 
716 Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member
717 which is the config to use with the base node. This is where the initial state can be configured
718 for specialized nodes:
719 
720  ```c
721  dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped;
722  ```
723 
724 When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not
725 modify the `vtable` member of the `nodeConfig` object.
726 
727 
728 Timing
729 ------
730 The node graph supports starting and stopping nodes at scheduled times. This is especially useful
731 for data source nodes where you want to get the node set up, but only start playback at a specific
732 time. There are two clocks: local and global.
733 
734 A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can
735 only be done based on the global clock because the local clock will not be running while the node
736 is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the
737 other hand, the local clock only advances when the node's processing callback is fired, and is
738 advanced based on the output frame count.
739 
740 To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with
741 `ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline.
742 Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time,
743 and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the
744 audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these
745 outside of the node processing callbacks which are always run on the audio thread.
746 
747 There is basic support for scheduling the starting and stopping of nodes. You can only schedule one
748 start and one stop at a time. This is mainly intended for putting nodes into a started or stopped
749 state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited
750 to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks
751 of several milliseconds. The following APIs can be used for scheduling node states:
752 
753  ```c
754  ma_node_set_state_time()
755  ma_node_get_state_time()
756  ```
757 
758 The time is absolute and must be based on the global clock. An example is below:
759 
760  ```c
761  ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second.
762  ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds.
763  ```
764 
765 An example for changing the state using a relative time.
766 
767  ```c
768  ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph));
769  ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph));
770  ```
771 
772 Note that due to the nature of multi-threading the times may not be 100% exact. If this is an
773 issue, consider scheduling state changes from within a processing callback. An idea might be to
774 have some kind of passthrough trigger node that is used specifically for tracking time and handling
775 events.
776 
777 
778 
779 Thread Safety and Locking
780 -------------------------
781 When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's
782 expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so
783 without the use of any locks. This section discusses the implementation used by miniaudio and goes
784 over some of the compromises employed by miniaudio to achieve this goal. Note that the current
785 implementation may not be ideal - feedback and critiques are most welcome.
786 
787 The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected
788 to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the
789 implementation, but are crafted in a way such that such locking is not required when reading audio
790 data from the graph. Locking in these areas are achieved by means of spinlocks.
791 
792 The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact
793 that a node can be uninitialized, and it's memory potentially freed, while in the middle of being
794 processed on the audio thread. There are times when the audio thread will be referencing a node,
795 which means the uninitialization process of a node needs to make sure it delays returning until the
796 audio thread is finished so that control is not handed back to the caller thereby giving them a
797 chance to free the node's memory.
798 
799 When the audio thread is processing a node, it does so by reading from each of the output buses of
800 the node. In order for a node to process data for one of it's output buses, it needs to read from
801 each of it's input buses, and so on an so forth. It follows that once all output buses of a node
802 are detached, the node as a whole will be disconnected and no further processing will occur unless
803 it's output buses are reattached, which won't be happening when the node is being uninitialized.
804 By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can
805 simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By
806 doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output
807 nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean
808 up.
809 
810 With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as
811 it takes to process the output bus being detached. This will happen if it's called at just the
812 wrong moment where the audio thread has just iterated it and has just started processing. The
813 caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which
814 includes the cost of recursively processing it's inputs. This is the biggest compromise made with
815 the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes
816 earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching
817 higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
818 detachments, detach starting from the lowest level nodes and work your way towards the final
819 endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
820 running, detachment will be fast and detachment in any order will be the same. The reason nodes
821 need to wait for their input attachments to complete is due to the potential for desyncs between
822 data sources. If the node was to terminate processing mid way through processing it's inputs,
823 there's a chance that some of the underlying data sources will have been read, but then others not.
824 That will then result in a potential desynchronization when detaching and reattaching higher-level
825 nodes. A possible solution to this is to have an option when detaching to terminate processing
826 before processing all input attachments which should be fairly simple.
827 
828 Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
829 locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
830 for each input bus and output bus. When an output bus is connected to an input bus, both the output
831 bus and input bus is locked. This locking is specifically for attaching and detaching across
832 different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
833 unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
834 considering that iterating over attachments must not break as a result of attaching or detaching a
835 node while iteration is occuring.
836 
837 Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
838 bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
839 each item in the list is and output bus. We have some intentional (and convenient) restrictions on
840 what can done with the linked list in order to simplify the implementation. First of all, whenever
841 something needs to iterate over the list, it must do so in a forward direction. Backwards iteration
842 is not supported. Also, items can only be added to the start of the list.
843 
844 The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer
845 to the next item in the list, and another to the previous item. A pointer to the previous item is
846 only required for fast detachment of the node - it is never used in iteration. This is an
847 important property because it means from the perspective of iteration, attaching and detaching of
848 an item can be done with a single atomic assignment. This is exploited by both the attachment and
849 detachment process. When attaching the node, the first thing that is done is the setting of the
850 local "next" and "previous" pointers of the node. After that, the item is "attached" to the list
851 by simply performing an atomic exchange with the head pointer. After that, the node is "attached"
852 to the list from the perspective of iteration. Even though the "previous" pointer of the next item
853 hasn't yet been set, from the perspective of iteration it's been attached because iteration will
854 only be happening in a forward direction which means the "previous" pointer won't actually ever get
855 used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and
856 `ma_node_detach_output_bus()` for the implementation of this mechanism.
857 */
858 
859 
860 /* Must never exceed 254. */
861 #ifndef MA_MAX_NODE_BUS_COUNT
862 #define MA_MAX_NODE_BUS_COUNT 254
863 #endif
864 
865 /* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */
866 #ifndef MA_MAX_NODE_LOCAL_BUS_COUNT
867 #define MA_MAX_NODE_LOCAL_BUS_COUNT 2
868 #endif
869 
870 /* Use this when the bus count is determined by the node instance rather than the vtable. */
871 #define MA_NODE_BUS_COUNT_UNKNOWN 255
872 
874 typedef void ma_node;
875 
876 
877 /* Node flags. */
878 #define MA_NODE_FLAG_PASSTHROUGH 0x00000001
879 #define MA_NODE_FLAG_CONTINUOUS_PROCESSING 0x00000002
880 #define MA_NODE_FLAG_ALLOW_NULL_INPUT 0x00000004
881 #define MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES 0x00000008
882 
883 
884 /* The playback state of a node. Either started or stopped. */
885 typedef enum
886 {
889 } ma_node_state;
890 
891 
892 typedef struct
893 {
894  /*
895  Extended processing callback. This callback is used for effects that process input and output
896  at different rates (i.e. they perform resampling). This is similar to the simple version, only
897  they take two sepate frame counts: one for input, and one for output.
898 
899  On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas
900  `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
901 
902  On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set
903  `pFrameCountIn` to the number of input frames that were consumed.
904  */
905  void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
906 
907  /*
908  A callback for retrieving the number of a input frames that are required to output the
909  specified number of output frames. You would only want to implement this when the node performs
910  resampling. This is optional, even for nodes that perform resampling, but it does offer a
911  small reduction in latency as it allows miniaudio to calculate the exact number of input frames
912  to read at a time instead of having to estimate.
913  */
914  ma_uint32 (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount);
915 
916  /*
917  The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
918  parameters of the callbacks above.
919  */
920  ma_uint8 inputBusCount;
921 
922  /*
923  The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`
924  parameters of the callbacks above.
925  */
926  ma_uint8 outputBusCount;
927 
928  /*
929  Flags describing characteristics of the node. This is currently just a placeholder for some
930  ideas for later on.
931  */
932  ma_uint32 flags;
934 
935 typedef struct
936 {
937  const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */
938  ma_node_state initialState; /* Defaults to ma_node_state_started. */
939  ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
940  ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
941  const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
942  const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
944 
946 
947 
948 /*
949 A node has multiple output buses. An output bus is attached to an input bus as an item in a linked
950 list. Think of the input bus as a linked list, with the output bus being an item in that list.
951 */
952 #define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */
953 
955 struct ma_node_output_bus
956 {
957  /* Immutable. */
958  ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */
959  ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */
960  ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
961 
962  /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */
963  MA_ATOMIC ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */
964  MA_ATOMIC ma_uint8 flags; /* Some state flags for tracking the read state of the output buffer. */
965  MA_ATOMIC ma_uint16 refCount; /* Reference count for some thread-safety when detaching. */
966  MA_ATOMIC ma_bool8 isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */
967  MA_ATOMIC ma_spinlock lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
968  MA_ATOMIC float volume; /* Linear. */
969  MA_ATOMIC ma_node_output_bus* pNext; /* If null, it's the tail node or detached. */
970  MA_ATOMIC ma_node_output_bus* pPrev; /* If null, it's the head node or detached. */
971  MA_ATOMIC ma_node* pInputNode; /* The node that this output bus is attached to. Required for detaching. */
972 };
973 
974 /*
975 A node has multiple input buses. The output buses of a node are connecting to the input busses of
976 another. An input bus is essentially just a linked list of output buses.
977 */
979 struct ma_node_input_bus
980 {
981  /* Mutable via multiple threads. */
982  ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */
983  MA_ATOMIC ma_uint16 nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */
984  MA_ATOMIC ma_spinlock lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
985 
986  /* Set once at startup. */
987  ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
988 };
989 
990 
991 typedef struct ma_node_base ma_node_base;
992 struct ma_node_base
993 {
994  /* These variables are set once at startup. */
995  ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
996  const ma_node_vtable* vtable;
997  float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */
998  ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
999 
1000  /* These variables are read and written only from the audio thread. */
1004 
1005  /* These variables are read and written between different threads. */
1006  MA_ATOMIC ma_node_state state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
1007  MA_ATOMIC ma_uint64 stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */
1008  MA_ATOMIC ma_uint64 localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */
1013 
1014  /* Memory management. */
1017  void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */
1018  ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
1019 };
1020 
1021 MA_API ma_result ma_node_get_heap_size(const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
1022 MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
1023 MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
1024 MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
1028 MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex);
1029 MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex);
1030 MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex);
1033 MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume);
1034 MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex);
1040 MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd);
1043 
1044 
1045 typedef struct
1046 {
1047  ma_uint32 channels;
1049 
1051 
1052 
1053 struct ma_node_graph
1054 {
1055  /* Immutable. */
1056  ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */
1057 
1058  /* Read and written by multiple threads. */
1060 };
1061 
1062 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
1063 MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks);
1065 MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead);
1069 
1070 
1071 
1072 /* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */
1073 typedef struct
1074 {
1075  ma_node_config nodeConfig;
1076  ma_data_source* pDataSource;
1077  ma_bool32 looping; /* Can be changed after initialization with ma_data_source_node_set_looping(). */
1079 
1081 
1082 
1083 typedef struct
1084 {
1085  ma_node_base base;
1086  ma_data_source* pDataSource;
1087  MA_ATOMIC ma_bool32 looping; /* This can be modified and read across different threads. Must be used atomically. */
1089 
1090 MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode);
1091 MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks);
1094 
1095 
1096 /* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */
1097 typedef struct
1098 {
1099  ma_node_config nodeConfig;
1101 
1103 
1104 
1105 typedef struct
1106 {
1107  ma_node_base base;
1109 
1110 MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode);
1111 MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks);
1112 
1113 
1114 
1115 
1116 /*
1117 Resource Manager Data Source Flags
1118 ==================================
1119 The flags below are used for controlling how the resource manager should handle the loading and caching of data sources.
1120 */
1121 #define MA_DATA_SOURCE_FLAG_STREAM 0x00000001 /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */
1122 #define MA_DATA_SOURCE_FLAG_DECODE 0x00000002 /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */
1123 #define MA_DATA_SOURCE_FLAG_ASYNC 0x00000004 /* When set, the resource manager will load the data source asynchronously. */
1124 #define MA_DATA_SOURCE_FLAG_WAIT_INIT 0x00000008 /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */
1125 
1126 
1132 
1133 
1134 
1135 #ifndef MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY
1136 #define MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY 1024
1137 #endif
1138 
1139 #define MA_JOB_QUIT 0x00000000
1140 #define MA_JOB_LOAD_DATA_BUFFER_NODE 0x00000001
1141 #define MA_JOB_FREE_DATA_BUFFER_NODE 0x00000002
1142 #define MA_JOB_PAGE_DATA_BUFFER_NODE 0x00000003
1143 #define MA_JOB_LOAD_DATA_BUFFER 0x00000004
1144 #define MA_JOB_FREE_DATA_BUFFER 0x00000005
1145 #define MA_JOB_LOAD_DATA_STREAM 0x00000006
1146 #define MA_JOB_FREE_DATA_STREAM 0x00000007
1147 #define MA_JOB_PAGE_DATA_STREAM 0x00000008
1148 #define MA_JOB_SEEK_DATA_STREAM 0x00000009
1149 #define MA_JOB_CUSTOM 0x00000100 /* Number your custom job codes as (MA_JOB_CUSTOM + 0), (MA_JOB_CUSTOM + 1), etc. */
1150 
1151 
1152 /*
1153 The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used
1154 as the insertion point for an object.
1155 
1156 Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
1157 
1158 The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits:
1159 
1160  +-----------------+-----------------+
1161  | 32 Bits | 32 Bits |
1162  +-----------------+-----------------+
1163  | Reference Count | Slot Index |
1164  +-----------------+-----------------+
1165 */
1166 typedef struct
1167 {
1168  struct
1169  {
1170  MA_ATOMIC ma_uint32 bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */
1172  ma_uint32 slots[MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY]; /* 32 bits for reference counting for ABA mitigation. */
1173  ma_uint32 count; /* Allocation count. */
1175 
1179 
1180 
1181 
1182 /*
1183 Fence
1184 =====
1185 This locks while the counter is larger than 0. Counter can be incremented and decremented by any
1186 thread, but care needs to be taken when waiting. It is possible for one thread to acquire the
1187 fence just as another thread returns from ma_fence_wait().
1188 
1189 The idea behind a fence is to allow you to wait for a group of operations to complete. When an
1190 operation starts, the counter is incremented which locks the fence. When the operation completes,
1191 the fence will be released which decrements the counter. ma_fence_wait() will block until the
1192 counter hits zero.
1193 */
1194 typedef struct
1195 {
1196  ma_event e;
1197  ma_uint32 counter;
1198 } ma_fence;
1199 
1201 MA_API void ma_fence_uninit(ma_fence* pFence);
1202 MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */
1203 MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */
1204 MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */
1205 
1206 
1207 
1208 /*
1209 Notification callback for asynchronous operations.
1210 */
1212 
1213 typedef struct
1214 {
1215  void (* onSignal)(ma_async_notification* pNotification);
1217 
1219 
1220 
1221 /*
1222 Simple polling notification.
1223 
1224 This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled()
1225 */
1226 typedef struct
1227 {
1229  ma_bool32 signalled;
1231 
1234 
1235 
1236 /*
1237 Event Notification
1238 
1239 This notification signals an event internally on the MA_NOTIFICATION_COMPLETE and MA_NOTIFICATION_FAILED codes. All other codes are ignored.
1240 */
1241 typedef struct
1242 {
1244  ma_event e;
1246 
1251 
1252 
1253 typedef struct
1254 {
1255  ma_async_notification* pNotification;
1256  ma_fence* pFence;
1258 
1259 typedef struct
1260 {
1261  ma_pipeline_stage_notification init; /* Initialization of the decoder. */
1262  ma_pipeline_stage_notification done; /* Decoding fully completed. */
1264 
1266 
1267 
1268 typedef struct
1269 {
1270  union
1271  {
1272  struct
1273  {
1274  ma_uint16 code;
1275  ma_uint16 slot;
1276  ma_uint32 refcount;
1277  } breakup;
1278  ma_uint64 allocation;
1279  } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */
1280  ma_uint64 next; /* refcount + slot for the next item. Does not include the job code. */
1281  ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */
1282 
1283  union
1284  {
1285  /* Resource Managemer Jobs */
1286  struct
1287  {
1288  ma_resource_manager_data_buffer_node* pDataBufferNode;
1289  char* pFilePath;
1290  wchar_t* pFilePathW;
1291  ma_bool32 decode; /* When set to true, the data buffer will be decoded. Otherwise it'll be encoded and will use a decoder for the connector. */
1292  ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
1293  ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_PAGE_DATA_BUFFER_NODE when decoding. */
1294  ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */
1295  ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */
1296  } loadDataBufferNode;
1297  struct
1298  {
1299  ma_resource_manager_data_buffer_node* pDataBufferNode;
1300  ma_async_notification* pDoneNotification;
1301  ma_fence* pDoneFence;
1302  } freeDataBufferNode;
1303  struct
1304  {
1305  ma_resource_manager_data_buffer_node* pDataBufferNode;
1306  ma_decoder* pDecoder;
1307  ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
1308  ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */
1309  } pageDataBufferNode;
1310 
1311  struct
1312  {
1313  ma_resource_manager_data_buffer* pDataBuffer;
1314  ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
1315  ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
1316  ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
1317  ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */
1318  } loadDataBuffer;
1319  struct
1320  {
1321  ma_resource_manager_data_buffer* pDataBuffer;
1322  ma_async_notification* pDoneNotification;
1323  ma_fence* pDoneFence;
1324  } freeDataBuffer;
1325 
1326  struct
1327  {
1328  ma_resource_manager_data_stream* pDataStream;
1329  char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */
1330  wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */
1331  ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */
1332  ma_fence* pInitFence;
1333  } loadDataStream;
1334  struct
1335  {
1336  ma_resource_manager_data_stream* pDataStream;
1337  ma_async_notification* pDoneNotification;
1338  ma_fence* pDoneFence;
1339  } freeDataStream;
1340  struct
1341  {
1342  ma_resource_manager_data_stream* pDataStream;
1343  ma_uint32 pageIndex; /* The index of the page to decode into. */
1344  } pageDataStream;
1345  struct
1346  {
1347  ma_resource_manager_data_stream* pDataStream;
1348  ma_uint64 frameIndex;
1349  } seekDataStream;
1350 
1351  /* Others. */
1352  struct
1353  {
1354  ma_uintptr data0;
1355  ma_uintptr data1;
1356  } custom;
1357  };
1358 } ma_job;
1359 
1361 
1362 
1363 /*
1364 When set, ma_job_queue_next() will not wait and no semaphore will be signaled in
1365 ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available.
1366 
1367 This flag should always be used for platforms that do not support multithreading.
1368 */
1369 #define MA_JOB_QUEUE_FLAG_NON_BLOCKING 0x00000001
1370 
1371 typedef struct
1372 {
1373  ma_uint32 flags; /* Flags passed in at initialization time. */
1374  ma_uint64 head; /* The first item in the list. Required for removing from the top of the list. */
1375  ma_uint64 tail; /* The last item in the list. Required for appending to the end of the list. */
1376  ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */
1377  ma_slot_allocator allocator;
1379 } ma_job_queue;
1380 
1383 MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob);
1384 MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */
1385 
1386 
1387 /* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */
1388 #ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
1389 #define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64
1390 #endif
1391 
1392 /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */
1393 #define MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING 0x00000001
1394 
1395 /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */
1396 #define MA_RESOURCE_MANAGER_FLAG_NO_THREADING 0x00000002
1397 
1398 
1399 typedef enum
1400 {
1401  ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */
1402  ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */
1403  ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */
1404  ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */
1406 
1407 typedef struct
1408 {
1409  MA_ATOMIC ma_resource_manager_data_supply_type type; /* Read and written from different threads so needs to be accessed atomically. */
1410  union
1411  {
1412  struct
1413  {
1414  const void* pData;
1415  size_t sizeInBytes;
1416  } encoded;
1417  struct
1418  {
1419  const void* pData;
1420  ma_uint64 totalFrameCount;
1421  ma_uint64 decodedFrameCount;
1422  ma_format format;
1423  ma_uint32 channels;
1424  ma_uint32 sampleRate;
1425  } decoded;
1426  struct
1427  {
1429  ma_uint64 decodedFrameCount;
1430  ma_uint32 sampleRate;
1431  } decodedPaged;
1432  };
1434 
1436 {
1437  ma_uint32 hashedName32; /* The hashed name. This is the key. */
1439  MA_ATOMIC ma_result result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */
1440  ma_uint32 executionCounter; /* For allocating execution orders for jobs. */
1441  ma_uint32 executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
1442  ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */
1447 };
1448 
1450 {
1451  ma_data_source_base ds; /* Base data source. A data buffer is a data source. */
1452  ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */
1453  ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */
1454  ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */
1455  ma_uint32 executionCounter; /* For allocating execution orders for jobs. */
1456  ma_uint32 executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
1457  ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */
1458  ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */
1459  MA_ATOMIC ma_result result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */
1460  MA_ATOMIC ma_bool32 isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */
1461  ma_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */
1462  union
1463  {
1464  ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */
1465  ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */
1466  ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */
1467  } connector; /* Connects this object to the node's data supply. */
1468 };
1469 
1471 {
1472  ma_data_source_base ds; /* Base data source. A data stream is a data source. */
1473  ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */
1474  ma_uint32 flags; /* The flags that were passed used to initialize the stream. */
1475  ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */
1476  ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_FREE_DATA_STREAM. */
1477  ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_LOAD_DATA_STREAM. */
1478  ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */
1479  ma_uint64 absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */
1480  ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */
1481  ma_uint32 executionCounter; /* For allocating execution orders for jobs. */
1482  ma_uint32 executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
1483 
1484  /* Written by the public API, read by the job thread. */
1485  MA_ATOMIC ma_bool32 isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */
1486 
1487  /* Written by the job thread, read by the public API. */
1488  void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */
1489  MA_ATOMIC ma_uint32 pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */
1490 
1491  /* Written and read by both the public API and the job thread. These must be atomic. */
1492  MA_ATOMIC ma_result result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */
1493  MA_ATOMIC ma_bool32 isDecoderAtEnd; /* Whether or not the decoder has reached the end. */
1494  MA_ATOMIC ma_bool32 isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */
1495  MA_ATOMIC ma_bool32 seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */
1496 };
1497 
1499 {
1500  union
1501  {
1504  }; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */
1505 
1506  ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */
1507  ma_uint32 executionCounter; /* For allocating execution orders for jobs. */
1508  ma_uint32 executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
1509 };
1510 
1511 typedef struct
1512 {
1513  ma_allocation_callbacks allocationCallbacks;
1514  ma_log* pLog;
1515  ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */
1516  ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */
1517  ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */
1518  ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */
1519  ma_uint32 flags;
1520  ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */
1521  ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;
1522  ma_uint32 customDecodingBackendCount;
1523  void* pCustomDecodingBackendUserData;
1525 
1527 
1528 struct ma_resource_manager
1529 {
1531  ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */
1532  ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */
1533  ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */
1534  ma_job_queue jobQueue; /* Lock-free multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */
1535  ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */
1536  ma_log log; /* Only used if no log was specified in the config. */
1537 };
1538 
1539 /* Init. */
1541 MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager);
1543 
1544 /* Registration. */
1545 MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags);
1546 MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags);
1547 MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */
1548 MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
1549 MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */
1550 MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes);
1551 MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath);
1552 MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath);
1553 MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName);
1554 MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName);
1555 
1556 /* Data Buffers. */
1557 MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
1558 MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
1570 
1571 /* Data Streams. */
1572 MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
1573 MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
1577 MA_API ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount);
1586 
1587 /* Data Sources. */
1588 MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
1589 MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
1594 MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount);
1603 
1604 /* Job management. */
1606 MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */
1609 MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */
1610 
1611 
1612 /*
1613 Engine
1614 ======
1615 The `ma_engine` API is a high-level API for audio playback. Internally it contains sounds (`ma_sound`) with resources managed via a resource manager
1616 (`ma_resource_manager`).
1617 
1618 Within the world there is the concept of a "listener". Each `ma_engine` instances has a single listener, but you can instantiate multiple `ma_engine` instances
1619 if you need more than one listener. In this case you will want to share a resource manager which you can do by initializing one manually and passing it into
1620 `ma_engine_config`. Using this method will require your application to manage groups and sounds on a per `ma_engine` basis.
1621 */
1622 typedef struct ma_engine ma_engine;
1623 typedef struct ma_sound ma_sound;
1624 
1625 
1626 /* Stereo panner. */
1627 typedef enum
1628 {
1629  ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */
1630  ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */
1631 } ma_pan_mode;
1632 
1633 typedef struct
1634 {
1635  ma_format format;
1636  ma_uint32 channels;
1637  ma_pan_mode mode;
1638  float pan;
1640 
1642 
1643 
1644 typedef struct
1645 {
1646  ma_format format;
1647  ma_uint32 channels;
1648  ma_pan_mode mode;
1649  float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */
1650 } ma_panner;
1651 
1652 MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner);
1653 MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1654 MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode);
1655 MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan);
1656 
1657 
1658 
1659 /* Fader. */
1660 typedef struct
1661 {
1662  ma_format format;
1663  ma_uint32 channels;
1664  ma_uint32 sampleRate;
1665 } ma_fader_config;
1666 
1668 
1669 typedef struct
1670 {
1672  float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */
1673  float volumeEnd;
1674  ma_uint64 lengthInFrames; /* The total length of the fade. */
1675  ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */
1676 } ma_fader;
1677 
1678 MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader);
1679 MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1680 MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
1681 MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames);
1683 
1684 
1685 
1686 /* Spatializer. */
1687 typedef struct
1688 {
1689  float x;
1690  float y;
1691  float z;
1692 } ma_vec3f;
1693 
1694 typedef enum
1695 {
1696  ma_attenuation_model_none, /* No distance attenuation and no spatialization. */
1697  ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */
1698  ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */
1699  ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */
1701 
1702 typedef enum
1703 {
1706 } ma_positioning;
1707 
1708 typedef enum
1709 {
1712 } ma_handedness;
1713 
1714 
1715 typedef struct
1716 {
1717  ma_uint32 channelsOut;
1718  ma_channel* pChannelMapOut;
1719  ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */
1720  float coneInnerAngleInRadians;
1721  float coneOuterAngleInRadians;
1722  float coneOuterGain;
1723  float speedOfSound;
1724  ma_vec3f worldUp;
1726 
1728 
1729 
1730 typedef struct
1731 {
1733  ma_vec3f position; /* The absolute position of the listener. */
1734  ma_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */
1735  ma_vec3f velocity;
1736 
1737  /* Memory management. */
1738  void* _pHeap;
1739  ma_bool32 _ownsHeap;
1741 
1745 MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks);
1747 MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
1748 MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
1749 MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z);
1751 MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z);
1753 MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z);
1757 MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z);
1759 
1760 
1761 typedef struct
1762 {
1763  ma_uint32 channelsIn;
1764  ma_uint32 channelsOut;
1765  ma_channel* pChannelMapIn;
1766  ma_attenuation_model attenuationModel;
1767  ma_positioning positioning;
1768  ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */
1769  float minGain;
1770  float maxGain;
1771  float minDistance;
1772  float maxDistance;
1773  float rolloff;
1774  float coneInnerAngleInRadians;
1775  float coneOuterAngleInRadians;
1776  float coneOuterGain;
1777  float dopplerFactor; /* Set to 0 to disable doppler effect. This will run on a fast path. */
1778  ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
1780 
1782 
1783 
1784 typedef struct
1785 {
1787  ma_vec3f position;
1788  ma_vec3f direction;
1789  ma_vec3f velocity; /* For doppler effect. */
1790  float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */
1791  ma_gainer gainer; /* For smooth gain transitions. */
1792  float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */
1793 
1794  /* Memory management. */
1795  void* _pHeap;
1796  ma_bool32 _ownsHeap;
1797 } ma_spatializer;
1798 
1799 MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes);
1800 MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer);
1801 MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer);
1802 MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks);
1803 MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1808 MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning);
1810 MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff);
1811 MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer);
1812 MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain);
1813 MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer);
1814 MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain);
1815 MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer);
1816 MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance);
1817 MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer);
1818 MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance);
1819 MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer);
1820 MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
1821 MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
1822 MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor);
1823 MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer);
1824 MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z);
1826 MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z);
1828 MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z);
1830 
1831 
1832 
1833 /* Sound flags. */
1834 #define MA_SOUND_FLAG_STREAM MA_DATA_SOURCE_FLAG_STREAM /* 0x00000001 */
1835 #define MA_SOUND_FLAG_DECODE MA_DATA_SOURCE_FLAG_DECODE /* 0x00000002 */
1836 #define MA_SOUND_FLAG_ASYNC MA_DATA_SOURCE_FLAG_ASYNC /* 0x00000004 */
1837 #define MA_SOUND_FLAG_WAIT_INIT MA_DATA_SOURCE_FLAG_WAIT_INIT /* 0x00000008 */
1838 #define MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT 0x00000010 /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
1839 #define MA_SOUND_FLAG_NO_PITCH 0x00000020 /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
1840 #define MA_SOUND_FLAG_NO_SPATIALIZATION 0x00000040 /* Disable spatialization. */
1841 
1842 #ifndef MA_ENGINE_MAX_LISTENERS
1843 #define MA_ENGINE_MAX_LISTENERS 4
1844 #endif
1845 
1846 #define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1)
1847 
1848 typedef enum
1849 {
1853 
1854 typedef struct
1855 {
1856  ma_engine* pEngine;
1857  ma_engine_node_type type;
1858  ma_uint32 channelsIn;
1859  ma_uint32 channelsOut;
1860  ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */
1861  ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */
1862  ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */
1863  ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */
1865 
1867 
1868 
1869 /* Base node object for both ma_sound and ma_sound_group. */
1870 typedef struct
1871 {
1872  ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */
1873  ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */
1874  ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */
1875  ma_fader fader;
1876  ma_resampler resampler; /* For pitch shift. May change this to ma_linear_resampler later. */
1877  ma_spatializer spatializer;
1878  ma_panner panner;
1879  MA_ATOMIC float pitch;
1880  float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */
1881  float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */
1882  MA_ATOMIC ma_bool8 isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */
1883  MA_ATOMIC ma_bool8 isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */
1884  MA_ATOMIC ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */
1885 
1886  /* Memory management. */
1887  ma_bool8 _ownsHeap;
1888  void* _pHeap;
1889 } ma_engine_node;
1890 
1891 MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes);
1892 MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode);
1893 MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode);
1894 MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks);
1895 
1896 
1897 typedef struct
1898 {
1899  const char* pFilePath; /* Set this to load from the resource manager. */
1900  const wchar_t* pFilePathW; /* Set this to load from the resource manager. */
1901  ma_data_source* pDataSource; /* Set this to load from an existing data source. */
1902  ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */
1903  ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */
1904  ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */
1905  ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. */
1906  ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */
1907  ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */
1908 } ma_sound_config;
1909 
1911 
1912 struct ma_sound
1913 {
1914  ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */
1916  ma_uint64 seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */
1917  MA_ATOMIC ma_bool8 isLooping; /* False by default. */
1920 
1921  /*
1922  We're declaring a resource manager data source object here to save us a malloc when loading a
1923  sound via the resource manager, which I *think* will be the most common scenario.
1924  */
1925 #ifndef MA_NO_RESOURCE_MANAGER
1927 #endif
1928 };
1929 
1930 /* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */
1932 struct ma_sound_inlined
1933 {
1934  ma_sound sound;
1937 };
1938 
1939 /* A sound group is just a sound. */
1942 
1944 
1945 
1946 typedef struct
1947 {
1948  ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */
1949  ma_context* pContext;
1950  ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */
1951  ma_log* pLog; /* When set to NULL, will use the context's log. */
1952  ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
1953  ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */
1954  ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */
1955  ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/
1956  ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */
1957  ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
1958  ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
1959  ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */
1960  ma_allocation_callbacks allocationCallbacks;
1961  ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */
1962  ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */
1964 
1966 
1967 
1968 struct ma_engine
1969 {
1970  ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */
1972  ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
1973  ma_log* pLog;
1979  ma_mutex inlinedSoundLock; /* For synchronizing access so the inlined sound list. */
1980  ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */
1981  MA_ATOMIC ma_uint32 inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */
1982  ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */
1983 };
1984 
1985 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine);
1986 MA_API void ma_engine_uninit(ma_engine* pEngine);
1987 MA_API void ma_engine_data_callback(ma_engine* pEngine, void* pOutput, const void* pInput, ma_uint32 frameCount);
1992 MA_API ma_uint64 ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime);
1995 
1998 MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume);
1999 MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB);
2000 
2002 MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ);
2003 MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
2004 MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex);
2005 MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
2006 MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex);
2007 MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
2008 MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex);
2009 MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
2010 MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
2011 MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
2012 MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex);
2013 
2014 MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */
2015 
2016 
2017 #ifndef MA_NO_RESOURCE_MANAGER
2018 MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);
2019 MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);
2020 MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);
2021 #endif
2023 MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound);
2024 MA_API void ma_sound_uninit(ma_sound* pSound);
2029 MA_API ma_result ma_sound_set_volume(ma_sound* pSound, float volume);
2030 MA_API ma_result ma_sound_set_gain_db(ma_sound* pSound, float gainDB);
2031 MA_API void ma_sound_set_pan(ma_sound* pSound, float pan);
2032 MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode);
2033 MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch);
2035 MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint8 listenerIndex);
2037 MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z);
2039 MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z);
2041 MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z);
2043 MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel);
2045 MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning);
2047 MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff);
2048 MA_API float ma_sound_get_rolloff(const ma_sound* pSound);
2049 MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain);
2050 MA_API float ma_sound_get_min_gain(const ma_sound* pSound);
2051 MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain);
2052 MA_API float ma_sound_get_max_gain(const ma_sound* pSound);
2053 MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance);
2054 MA_API float ma_sound_get_min_distance(const ma_sound* pSound);
2055 MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance);
2056 MA_API float ma_sound_get_max_distance(const ma_sound* pSound);
2057 MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
2058 MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
2059 MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor);
2060 MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound);
2061 MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
2062 MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
2064 MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
2065 MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
2066 MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
2067 MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
2070 MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool8 isLooping);
2072 MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound);
2073 MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */
2074 MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
2077 
2078 MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup);
2086 MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan);
2088 MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch);
2092 MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z);
2094 MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z);
2096 MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z);
2102 MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff);
2103 MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup);
2104 MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain);
2106 MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain);
2108 MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance);
2110 MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance);
2112 MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
2113 MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
2114 MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor);
2116 MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
2117 MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
2119 MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
2120 MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
2121 MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
2122 MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
2125 
2126 
2127 
2128 /*
2129 Biquad Node
2130 */
2131 typedef struct
2132 {
2133  ma_node_config nodeConfig;
2134  ma_biquad_config biquad;
2136 
2137 MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2);
2138 
2139 
2140 typedef struct
2141 {
2142  ma_node_base baseNode;
2143  ma_biquad biquad;
2144 } ma_biquad_node;
2145 
2146 MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode);
2148 MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2149 
2150 
2151 /*
2152 Low Pass Filter Node
2153 */
2154 typedef struct
2155 {
2156  ma_node_config nodeConfig;
2157  ma_lpf_config lpf;
2159 
2160 MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2161 
2162 
2163 typedef struct
2164 {
2165  ma_node_base baseNode;
2166  ma_lpf lpf;
2167 } ma_lpf_node;
2168 
2169 MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode);
2171 MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2172 
2173 
2174 /*
2175 High Pass Filter Node
2176 */
2177 typedef struct
2178 {
2179  ma_node_config nodeConfig;
2180  ma_hpf_config hpf;
2182 
2183 MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2184 
2185 
2186 typedef struct
2187 {
2188  ma_node_base baseNode;
2189  ma_hpf hpf;
2190 } ma_hpf_node;
2191 
2192 MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode);
2194 MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2195 
2196 
2197 /*
2198 Band Pass Filter Node
2199 */
2200 typedef struct
2201 {
2202  ma_node_config nodeConfig;
2203  ma_bpf_config bpf;
2205 
2206 MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2207 
2208 
2209 typedef struct
2210 {
2211  ma_node_base baseNode;
2212  ma_bpf bpf;
2213 } ma_bpf_node;
2214 
2215 MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode);
2217 MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2218 
2219 
2220 /*
2221 Notching Filter Node
2222 */
2223 typedef struct
2224 {
2225  ma_node_config nodeConfig;
2226  ma_notch_config notch;
2228 
2229 MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
2230 
2231 
2232 typedef struct
2233 {
2234  ma_node_base baseNode;
2235  ma_notch2 notch;
2236 } ma_notch_node;
2237 
2238 MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode);
2240 MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2241 
2242 
2243 /*
2244 Peaking Filter Node
2245 */
2246 typedef struct
2247 {
2248  ma_node_config nodeConfig;
2249  ma_peak_config peak;
2251 
2252 MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
2253 
2254 
2255 typedef struct
2256 {
2257  ma_node_base baseNode;
2258  ma_peak2 peak;
2259 } ma_peak_node;
2260 
2261 MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode);
2263 MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2264 
2265 
2266 /*
2267 Low Shelf Filter Node
2268 */
2269 typedef struct
2270 {
2271  ma_node_config nodeConfig;
2272  ma_loshelf_config loshelf;
2274 
2275 MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
2276 
2277 
2278 typedef struct
2279 {
2280  ma_node_base baseNode;
2281  ma_loshelf2 loshelf;
2282 } ma_loshelf_node;
2283 
2284 MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode);
2286 MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2287 
2288 
2289 /*
2290 High Shelf Filter Node
2291 */
2292 typedef struct
2293 {
2294  ma_node_config nodeConfig;
2295  ma_hishelf_config hishelf;
2297 
2298 MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
2299 
2300 
2301 typedef struct
2302 {
2303  ma_node_base baseNode;
2304  ma_hishelf2 hishelf;
2305 } ma_hishelf_node;
2306 
2307 MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode);
2309 MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
2310 
2311 
2312 
2313 /*
2314 Delay
2315 */
2316 typedef struct
2317 {
2318  ma_uint32 channels;
2319  ma_uint32 sampleRate;
2320  ma_uint32 delayInFrames;
2321  ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */
2322  float wet; /* 0..1. Default = 1. */
2323  float dry; /* 0..1. Default = 1. */
2324  float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */
2325 } ma_delay_config;
2326 
2327 MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
2328 
2329 
2330 typedef struct
2331 {
2333  ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */
2334  ma_uint32 bufferSizeInFrames; /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */
2335  float* pBuffer;
2336 } ma_delay;
2337 
2338 MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay);
2339 MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks);
2340 MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount);
2341 MA_API void ma_delay_set_wet(ma_delay* pDelay, float value);
2342 MA_API float ma_delay_get_wet(const ma_delay* pDelay);
2343 MA_API void ma_delay_set_dry(ma_delay* pDelay, float value);
2344 MA_API float ma_delay_get_dry(const ma_delay* pDelay);
2345 MA_API void ma_delay_set_decay(ma_delay* pDelay, float value);
2346 MA_API float ma_delay_get_decay(const ma_delay* pDelay);
2347 
2348 
2349 
2350 typedef struct
2351 {
2352  ma_node_config nodeConfig;
2353  ma_delay_config delay;
2355 
2356 MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
2357 
2358 
2359 typedef struct
2360 {
2361  ma_node_base baseNode;
2362  ma_delay delay;
2363 } ma_delay_node;
2364 
2365 MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode);
2366 MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks);
2367 MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value);
2368 MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode);
2369 MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value);
2370 MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode);
2371 MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value);
2372 MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode);
2373 
2374 
2375 #ifdef __cplusplus
2376 }
2377 #endif
2378 #endif /* miniaudio_engine_h */
2379 
2380 
2381 #if defined(MA_IMPLEMENTATION) || defined(MINIAUDIO_IMPLEMENTATION)
2382 
2384 {
2385  if (pData == NULL) {
2386  return MA_INVALID_ARGS;
2387  }
2388 
2389  MA_ZERO_OBJECT(pData);
2390 
2391  pData->format = format;
2392  pData->channels = channels;
2393  pData->pTail = &pData->head;
2394 
2395  return MA_SUCCESS;
2396 }
2397 
2399 {
2401 
2402  if (pData == NULL) {
2403  return;
2404  }
2405 
2406  /* All pages need to be freed. */
2408  while (pPage != NULL) {
2410 
2411  ma_free(pPage, pAllocationCallbacks);
2412  pPage = pNext;
2413  }
2414 }
2415 
2417 {
2418  if (pData == NULL) {
2419  return NULL;
2420  }
2421 
2422  return &pData->head;
2423 }
2424 
2426 {
2427  if (pData == NULL) {
2428  return NULL;
2429  }
2430 
2431  return pData->pTail;
2432 }
2433 
2435 {
2437 
2438  if (pLength == NULL) {
2439  return MA_INVALID_ARGS;
2440  }
2441 
2442  *pLength = 0;
2443 
2444  if (pData == NULL) {
2445  return MA_INVALID_ARGS;
2446  }
2447 
2448  /* Calculate the length from the linked list. */
2449  for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) {
2450  *pLength += pPage->sizeInFrames;
2451  }
2452 
2453  return MA_SUCCESS;
2454 }
2455 
2456 MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage)
2457 {
2459  ma_uint64 allocationSize;
2460 
2461  if (ppPage == NULL) {
2462  return MA_INVALID_ARGS;
2463  }
2464 
2465  *ppPage = NULL;
2466 
2467  if (pData == NULL) {
2468  return MA_INVALID_ARGS;
2469  }
2470 
2471  allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels));
2472  if (allocationSize > MA_SIZE_MAX) {
2473  return MA_OUT_OF_MEMORY; /* Too big. */
2474  }
2475 
2476  pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */
2477  if (pPage == NULL) {
2478  return MA_OUT_OF_MEMORY;
2479  }
2480 
2481  pPage->pNext = NULL;
2482  pPage->sizeInFrames = pageSizeInFrames;
2483 
2484  if (pInitialData != NULL) {
2485  ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);
2486  }
2487 
2488  *ppPage = pPage;
2489 
2490  return MA_SUCCESS;
2491 }
2492 
2494 {
2495  if (pData == NULL || pPage == NULL) {
2496  return MA_INVALID_ARGS;
2497  }
2498 
2499  /* It's assumed the page is not attached to the list. */
2500  ma_free(pPage, pAllocationCallbacks);
2501 
2502  return MA_SUCCESS;
2503 }
2504 
2506 {
2507  if (pData == NULL || pPage == NULL) {
2508  return MA_INVALID_ARGS;
2509  }
2510 
2511  /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */
2512 
2513  /* First thing to do is update the tail. */
2514  for (;;) {
2516  ma_paged_audio_buffer_page* pNewTail = pPage;
2517 
2518  if (c89atomic_compare_exchange_weak_ptr((void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {
2519  /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */
2520  c89atomic_exchange_ptr(&pOldTail->pNext, pPage);
2521  break; /* Done. */
2522  }
2523  }
2524 
2525  return MA_SUCCESS;
2526 }
2527 
2528 MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks)
2529 {
2530  ma_result result;
2532 
2533  result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);
2534  if (result != MA_SUCCESS) {
2535  return result;
2536  }
2537 
2538  return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */
2539 }
2540 
2541 
2543 {
2545 
2547  config.pData = pData;
2548 
2549  return config;
2550 }
2551 
2552 
2553 static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
2554 {
2555  return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
2556 }
2557 
2558 static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
2559 {
2560  return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);
2561 }
2562 
2563 static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
2564 {
2565  ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;
2566 
2567  *pFormat = pPagedAudioBuffer->pData->format;
2568  *pChannels = pPagedAudioBuffer->pData->channels;
2569  *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
2570 
2571  return MA_SUCCESS;
2572 }
2573 
2574 static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
2575 {
2577 }
2578 
2579 static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
2580 {
2582 }
2583 
2584 static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =
2585 {
2586  ma_paged_audio_buffer__data_source_on_read,
2587  ma_paged_audio_buffer__data_source_on_seek,
2588  NULL, /* onMap */
2589  NULL, /* onUnmap */
2590  ma_paged_audio_buffer__data_source_on_get_data_format,
2591  ma_paged_audio_buffer__data_source_on_get_cursor,
2592  ma_paged_audio_buffer__data_source_on_get_length
2593 };
2594 
2596 {
2597  ma_result result;
2598  ma_data_source_config dataSourceConfig;
2599 
2600  if (pPagedAudioBuffer == NULL) {
2601  return MA_INVALID_ARGS;
2602  }
2603 
2604  MA_ZERO_OBJECT(pPagedAudioBuffer);
2605 
2606  /* A config is required for the format and channel count. */
2607  if (pConfig == NULL) {
2608  return MA_INVALID_ARGS;
2609  }
2610 
2611  if (pConfig->pData == NULL) {
2612  return MA_INVALID_ARGS; /* No underlying data specified. */
2613  }
2614 
2615  dataSourceConfig = ma_data_source_config_init();
2616  dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;
2617 
2618  result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);
2619  if (result != MA_SUCCESS) {
2620  return result;
2621  }
2622 
2623  pPagedAudioBuffer->pData = pConfig->pData;
2624  pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData);
2625  pPagedAudioBuffer->relativeCursor = 0;
2626  pPagedAudioBuffer->absoluteCursor = 0;
2627 
2628  return MA_SUCCESS;
2629 }
2630 
2632 {
2633  if (pPagedAudioBuffer == NULL) {
2634  return;
2635  }
2636 
2637  /* Nothing to do. The data needs to be deleted separately. */
2638 }
2639 
2640 MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
2641 {
2642  ma_result result = MA_SUCCESS;
2643  ma_uint64 totalFramesRead = 0;
2644  ma_format format;
2645  ma_uint32 channels;
2646 
2647  if (pPagedAudioBuffer == NULL) {
2648  return MA_INVALID_ARGS;
2649  }
2650 
2651  format = pPagedAudioBuffer->pData->format;
2652  channels = pPagedAudioBuffer->pData->channels;
2653 
2654  while (totalFramesRead < frameCount) {
2655  /* Read from the current page. The buffer should never be in a state where this is NULL. */
2656  ma_uint64 framesRemainingInCurrentPage;
2657  ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;
2658  ma_uint64 framesToReadThisIteration;
2659 
2660  MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);
2661 
2662  framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;
2663 
2664  framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);
2665  ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels);
2666  totalFramesRead += framesToReadThisIteration;
2667 
2668  pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;
2669  pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;
2670 
2671  /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */
2672  MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);
2673 
2674  if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {
2675  /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */
2677  if (pNext == NULL) {
2678  result = MA_AT_END;
2679  break; /* We've reached the end. */
2680  } else {
2681  pPagedAudioBuffer->pCurrent = pNext;
2682  pPagedAudioBuffer->relativeCursor = 0;
2683  }
2684  }
2685  }
2686 
2687  if (pFramesRead != NULL) {
2688  *pFramesRead = totalFramesRead;
2689  }
2690 
2691  return result;
2692 }
2693 
2695 {
2696  if (pPagedAudioBuffer == NULL) {
2697  return MA_INVALID_ARGS;
2698  }
2699 
2700  if (frameIndex == pPagedAudioBuffer->absoluteCursor) {
2701  return MA_SUCCESS; /* Nothing to do. */
2702  }
2703 
2704  if (frameIndex < pPagedAudioBuffer->absoluteCursor) {
2705  /* Moving backwards. Need to move the cursor back to the start, and then move forward. */
2706  pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);
2707  pPagedAudioBuffer->absoluteCursor = 0;
2708  pPagedAudioBuffer->relativeCursor = 0;
2709 
2710  /* Fall through to the forward seeking section below. */
2711  }
2712 
2713  if (frameIndex > pPagedAudioBuffer->absoluteCursor) {
2714  /* Moving forward. */
2716  ma_uint64 runningCursor = 0;
2717 
2719  ma_uint64 pageRangeBeg = runningCursor;
2720  ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;
2721 
2722  if (frameIndex >= pageRangeBeg) {
2723  if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */
2724  /* We found the page. */
2725  pPagedAudioBuffer->pCurrent = pPage;
2726  pPagedAudioBuffer->absoluteCursor = frameIndex;
2727  pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;
2728  return MA_SUCCESS;
2729  }
2730  }
2731 
2732  runningCursor = pageRangeEnd;
2733  }
2734 
2735  /* Getting here means we tried seeking too far forward. Don't change any state. */
2736  return MA_BAD_SEEK;
2737  }
2738 
2739  return MA_SUCCESS;
2740 }
2741 
2743 {
2744  if (pCursor == NULL) {
2745  return MA_INVALID_ARGS;
2746  }
2747 
2748  *pCursor = 0; /* Safety. */
2749 
2750  if (pPagedAudioBuffer == NULL) {
2751  return MA_INVALID_ARGS;
2752  }
2753 
2754  *pCursor = pPagedAudioBuffer->absoluteCursor;
2755 
2756  return MA_SUCCESS;
2757 }
2758 
2760 {
2761  return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);
2762 }
2763 
2764 
2765 
2767 {
2768  size_t bytesPerSample[ma_format_count] = {
2769  0, /* ma_format_unknown */
2770  sizeof(ma_int16), /* ma_format_u8 */
2771  sizeof(ma_int32), /* ma_format_s16 */
2772  sizeof(ma_int64), /* ma_format_s24 */
2773  sizeof(ma_int64), /* ma_format_s32 */
2774  sizeof(float) /* ma_format_f32 */
2775  };
2776 
2777  return bytesPerSample[format];
2778 }
2779 
2781 {
2782  return ma_get_accumulation_bytes_per_sample(format) * channels;
2783 }
2784 
2785 
2786 
2787 MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames)
2788 {
2790 
2792  config.channels = channels;
2793  config.smoothTimeInFrames = smoothTimeInFrames;
2794 
2795  return config;
2796 }
2797 
2798 
2799 typedef struct
2800 {
2801  size_t sizeInBytes;
2802  size_t oldGainsOffset;
2803  size_t newGainsOffset;
2804 } ma_gainer_heap_layout;
2805 
2806 static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout)
2807 {
2808  MA_ASSERT(pHeapLayout != NULL);
2809 
2810  MA_ZERO_OBJECT(pHeapLayout);
2811 
2812  if (pConfig == NULL) {
2813  return MA_INVALID_ARGS;
2814  }
2815 
2816  if (pConfig->channels == 0) {
2817  return MA_INVALID_ARGS;
2818  }
2819 
2820  pHeapLayout->sizeInBytes = 0;
2821 
2822  /* Old gains. */
2823  pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes;
2824  pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
2825 
2826  /* New gains. */
2827  pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes;
2828  pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
2829 
2830  /* Alignment. */
2831  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
2832 
2833  return MA_SUCCESS;
2834 }
2835 
2836 
2837 MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes)
2838 {
2839  ma_result result;
2840  ma_gainer_heap_layout heapLayout;
2841 
2842  if (pHeapSizeInBytes == NULL) {
2843  return MA_INVALID_ARGS;
2844  }
2845 
2846  *pHeapSizeInBytes = 0;
2847 
2848  result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
2849  if (result != MA_SUCCESS) {
2850  return MA_INVALID_ARGS;
2851  }
2852 
2853  *pHeapSizeInBytes = heapLayout.sizeInBytes;
2854 
2855  return MA_SUCCESS;
2856 }
2857 
2858 
2859 MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer)
2860 {
2861  ma_result result;
2862  ma_gainer_heap_layout heapLayout;
2863  ma_uint32 iChannel;
2864 
2865  if (pGainer == NULL) {
2866  return MA_INVALID_ARGS;
2867  }
2868 
2869  MA_ZERO_OBJECT(pGainer);
2870 
2871  if (pConfig == NULL || pHeap == NULL) {
2872  return MA_INVALID_ARGS;
2873  }
2874 
2875  result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
2876  if (result != MA_SUCCESS) {
2877  return result;
2878  }
2879 
2880  pGainer->_pHeap = pHeap;
2881  pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset);
2882  pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset);
2883 
2884  pGainer->config = *pConfig;
2885  pGainer->t = (ma_uint32)-1; /* No interpolation by default. */
2886 
2887  for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
2888  pGainer->pOldGains[iChannel] = 1;
2889  pGainer->pNewGains[iChannel] = 1;
2890  }
2891 
2892  return MA_SUCCESS;
2893 }
2894 
2895 MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer)
2896 {
2897  ma_result result;
2898  size_t heapSizeInBytes;
2899  void* pHeap;
2900 
2901  result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes);
2902  if (result != MA_SUCCESS) {
2903  return result; /* Failed to retrieve the size of the heap allocation. */
2904  }
2905 
2906  if (heapSizeInBytes > 0) {
2907  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
2908  if (pHeap == NULL) {
2909  return MA_OUT_OF_MEMORY;
2910  }
2911  } else {
2912  pHeap = NULL;
2913  }
2914 
2915  result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer);
2916  if (result != MA_SUCCESS) {
2917  return result;
2918  }
2919 
2920  pGainer->_ownsHeap = MA_TRUE;
2921  return MA_SUCCESS;
2922 }
2923 
2924 MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks)
2925 {
2926  if (pGainer == NULL) {
2927  return;
2928  }
2929 
2930  if (pGainer->_pHeap != NULL && pGainer->_ownsHeap) {
2931  ma_free(pGainer->_pHeap, pAllocationCallbacks);
2932  }
2933 }
2934 
2935 static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel)
2936 {
2937  float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
2938  return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a);
2939 }
2940 
2941 MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
2942 {
2943  ma_uint64 iFrame;
2944  ma_uint32 iChannel;
2945  float* pFramesOutF32 = (float*)pFramesOut;
2946  const float* pFramesInF32 = (const float*)pFramesIn;
2947 
2948  if (pGainer == NULL) {
2949  return MA_INVALID_ARGS;
2950  }
2951 
2952  if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
2953  /* Fast path. No gain calculation required. */
2954  ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);
2955 
2956  /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
2957  if (pGainer->t == (ma_uint32)-1) {
2958  pGainer->t = pGainer->config.smoothTimeInFrames;
2959  }
2960  } else {
2961  /* Slow path. Need to interpolate the gain for each channel individually. */
2962 
2963  /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
2964  if (pFramesOut != NULL && pFramesIn != NULL) {
2965  float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
2966  float d = 1.0f / pGainer->config.smoothTimeInFrames;
2967  ma_uint32 channelCount = pGainer->config.channels;
2968 
2969  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
2970  for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
2971  pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a);
2972  }
2973 
2974  pFramesOutF32 += channelCount;
2975  pFramesInF32 += channelCount;
2976 
2977  a += d;
2978  if (a > 1) {
2979  a = 1;
2980  }
2981  }
2982  }
2983 
2984  pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);
2985 
2986  #if 0 /* Reference implementation. */
2987  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
2988  /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
2989  if (pFramesOut != NULL && pFramesIn != NULL) {
2990  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
2991  pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel);
2992  }
2993  }
2994 
2995  /* Move interpolation time forward, but don't go beyond our smoothing time. */
2996  pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
2997  }
2998  #endif
2999  }
3000 
3001  return MA_SUCCESS;
3002 }
3003 
3004 static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel)
3005 {
3006  pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel);
3007  pGainer->pNewGains[iChannel] = newGain;
3008 }
3009 
3010 static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer)
3011 {
3012  if (pGainer->t == (ma_uint32)-1) {
3013  pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */
3014  } else {
3015  pGainer->t = 0;
3016  }
3017 }
3018 
3019 MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain)
3020 {
3021  ma_uint32 iChannel;
3022 
3023  if (pGainer == NULL) {
3024  return MA_INVALID_ARGS;
3025  }
3026 
3027  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
3028  ma_gainer_set_gain_by_index(pGainer, newGain, iChannel);
3029  }
3030 
3031  /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */
3032  ma_gainer_reset_smoothing_time(pGainer);
3033 
3034  return MA_SUCCESS;
3035 }
3036 
3037 MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains)
3038 {
3039  ma_uint32 iChannel;
3040 
3041  if (pGainer == NULL || pNewGains == NULL) {
3042  return MA_INVALID_ARGS;
3043  }
3044 
3045  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
3046  ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel);
3047  }
3048 
3049  /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */
3050  ma_gainer_reset_smoothing_time(pGainer);
3051 
3052  return MA_SUCCESS;
3053 }
3054 
3055 
3056 
3057 
3058 /* 10ms @ 48K = 480. Must never exceed 65535. */
3059 #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
3060 #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
3061 #endif
3062 
3063 
3064 static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime);
3065 
3066 
3067 MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
3068 {
3069  #ifndef MA_NO_GENERATION
3070  {
3071  ma_waveform_config waveformConfig;
3073 
3074  waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);
3075  ma_waveform_init(&waveformConfig, &waveform);
3076  ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount);
3077  }
3078  #else
3079  {
3080  (void)pFramesOut;
3081  (void)frameCount;
3082  (void)format;
3083  (void)channels;
3084  (void)sampleRate;
3085  #if defined(MA_DEBUG_OUTPUT)
3086  {
3087  #warning ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.
3088  }
3089  #endif
3090  }
3091  #endif
3092 }
3093 
3094 
3095 
3096 static MA_INLINE ma_int16 ma_float_to_fixed_16(float x)
3097 {
3098  return (ma_int16)(x * (1 << 8));
3099 }
3100 
3101 
3102 static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume)
3103 {
3104  return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8);
3105 }
3106 
3107 static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume)
3108 {
3109  return (ma_int32)((x * volume) >> 8);
3110 }
3111 
3112 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume)
3113 {
3114  return (ma_int64)((x * volume) >> 8);
3115 }
3116 
3117 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume)
3118 {
3119  return (ma_int64)((x * volume) >> 8);
3120 }
3121 
3122 static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume)
3123 {
3124  return x * volume;
3125 }
3126 
3127 
3128 
3129 
3130 static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable)
3131 {
3132  ma_uint32 iChannelIn;
3133  ma_uint32 iChannelOut;
3134 
3135  if (pShuffleTable == NULL || channelCountIn == 0 || channelCountIn > MA_MAX_CHANNELS || channelCountOut == 0 || channelCountOut > MA_MAX_CHANNELS) {
3136  return MA_INVALID_ARGS;
3137  }
3138 
3139  /*
3140  When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the
3141  input channel has more than one occurance of a channel position, the second one will be ignored.
3142  */
3143  for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {
3144  ma_channel channelOut;
3145 
3146  /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */
3147  pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL;
3148 
3149  channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut);
3150  for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) {
3151  ma_channel channelIn;
3152 
3153  channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn);
3154  if (channelOut == channelIn) {
3155  pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
3156  break;
3157  }
3158 
3159  /*
3160  Getting here means the channels don't exactly match, but we are going to support some
3161  relaxed matching for practicality. If, for example, there are two stereo channel maps,
3162  but one uses front left/right and the other uses side left/right, it makes logical
3163  sense to just map these. The way we'll do it is we'll check if there is a logical
3164  corresponding mapping, and if so, apply it, but we will *not* break from the loop,
3165  thereby giving the loop a chance to find an exact match later which will take priority.
3166  */
3167  switch (channelOut)
3168  {
3169  /* Left channels. */
3170  case MA_CHANNEL_FRONT_LEFT:
3171  case MA_CHANNEL_SIDE_LEFT:
3172  {
3173  switch (channelIn) {
3174  case MA_CHANNEL_FRONT_LEFT:
3175  case MA_CHANNEL_SIDE_LEFT:
3176  {
3177  pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
3178  } break;
3179  }
3180  } break;
3181 
3182  /* Right channels. */
3184  case MA_CHANNEL_SIDE_RIGHT:
3185  {
3186  switch (channelIn) {
3188  case MA_CHANNEL_SIDE_RIGHT:
3189  {
3190  pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
3191  } break;
3192  }
3193  } break;
3194 
3195  default: break;
3196  }
3197  }
3198  }
3199 
3200  return MA_SUCCESS;
3201 }
3202 
3203 static ma_result ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)
3204 {
3205  ma_uint64 iFrame;
3206  ma_uint32 iChannelOut;
3207 
3208  if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || channelsOut > MA_MAX_CHANNELS || pShuffleTable == NULL) {
3209  return MA_INVALID_ARGS;
3210  }
3211 
3212  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
3213  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
3214  ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
3215  if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
3216  pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
3217  } else {
3218  pFramesOut[iChannelOut] = 0;
3219  }
3220  }
3221 
3222  pFramesOut += channelsOut;
3223  pFramesIn += channelsIn;
3224  }
3225 
3226  return MA_SUCCESS;
3227 }
3228 
3229 static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount)
3230 {
3231  ma_uint64 iFrame;
3232  ma_uint32 iChannelIn;
3233  ma_uint32 accumulationCount;
3234 
3235  if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) {
3236  return MA_INVALID_ARGS;
3237  }
3238 
3239  /* In this case the output stream needs to be the average of all channels, ignoring NONE. */
3240 
3241  /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */
3242  accumulationCount = 0;
3243  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
3244  if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) {
3245  accumulationCount += 1;
3246  }
3247  }
3248 
3249  if (accumulationCount > 0) { /* <-- Prevent a division by zero. */
3250  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
3251  float accumulation = 0;
3252 
3253  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
3254  ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
3255  if (channelIn != MA_CHANNEL_NONE) {
3256  accumulation += pFramesIn[iChannelIn];
3257  }
3258  }
3259 
3260  pFramesOut[0] = accumulation / accumulationCount;
3261  pFramesOut += 1;
3262  pFramesIn += channelsIn;
3263  }
3264  } else {
3265  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1);
3266  }
3267 
3268  return MA_SUCCESS;
3269 }
3270 
3271 static ma_result ma_channel_map_apply_mono_in_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint64 frameCount)
3272 {
3273  ma_uint64 iFrame;
3274  ma_uint32 iChannelOut;
3275 
3276  if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) {
3277  return MA_INVALID_ARGS;
3278  }
3279 
3280  /* In this case we just copy the mono channel to each of the output channels, ignoring NONE. */
3281  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
3282  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
3283  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
3284  if (channelOut != MA_CHANNEL_NONE) {
3285  pFramesOut[iChannelOut] = pFramesIn[0];
3286  }
3287  }
3288 
3289  pFramesOut += channelsOut;
3290  pFramesIn += 1;
3291  }
3292 
3293  return MA_SUCCESS;
3294 }
3295 
3296 static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode)
3297 {
3298  ma_bool32 passthrough = MA_FALSE;
3299 
3300  if (channelsOut == channelsIn) {
3301  if (pChannelMapOut == pChannelMapIn) {
3302  passthrough = MA_TRUE;
3303  } else {
3304  if (ma_channel_map_equal(channelsOut, pChannelMapOut, pChannelMapIn)) {
3305  passthrough = MA_TRUE;
3306  }
3307  }
3308  }
3309 
3310  /* Optimized Path: Passthrough */
3311  if (passthrough) {
3312  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut);
3313  return;
3314  }
3315 
3316  /* Special Path: Mono Output. */
3317  if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) {
3318  ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount);
3319  return;
3320  }
3321 
3322  /* Special Path: Mono Input. */
3323  if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) {
3324  ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount);
3325  return;
3326  }
3327 
3328 
3329  if (channelsOut <= MA_MAX_CHANNELS) {
3330  ma_result result;
3331 
3332  if (mode == ma_channel_mix_mode_simple) {
3333  ma_channel shuffleTable[MA_MAX_CHANNELS];
3334 
3335  result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable);
3336  if (result != MA_SUCCESS) {
3337  return;
3338  }
3339 
3340  result = ma_channel_map_apply_shuffle_table_f32(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable);
3341  if (result != MA_SUCCESS) {
3342  return;
3343  }
3344  } else {
3345  ma_uint32 iFrame;
3346  ma_uint32 iChannelOut;
3347  ma_uint32 iChannelIn;
3348  float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */
3349 
3350  /*
3351  If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to
3352  fall back to a slower path because otherwise we'll run out of stack space.
3353  */
3354  if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) {
3355  /* Pre-compute weights. */
3356  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
3357  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
3358  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
3359  ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
3360  weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
3361  }
3362  }
3363 
3364  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
3365  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
3366  float accumulation = 0;
3367 
3368  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
3369  accumulation += pFramesIn[iChannelIn] * weights[iChannelOut][iChannelIn];
3370  }
3371 
3372  pFramesOut[iChannelOut] = accumulation;
3373  }
3374 
3375  pFramesOut += channelsOut;
3376  pFramesIn += channelsIn;
3377  }
3378  } else {
3379  /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */
3380  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
3381  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
3382  float accumulation = 0;
3383  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
3384 
3385  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
3386  ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
3387  accumulation += pFramesIn[iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
3388  }
3389 
3390  pFramesOut[iChannelOut] = accumulation;
3391  }
3392 
3393  pFramesOut += channelsOut;
3394  pFramesIn += channelsIn;
3395  }
3396  }
3397  }
3398  } else {
3399  /* Fall back to silence. If you hit this, what are you doing with so many channels?! */
3400  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut);
3401  }
3402 }
3403 
3404 
3405 
3406 MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains)
3407 {
3408  ma_uint64 iFrame;
3409 
3410  if (channels == 2) {
3411  /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */
3412  }
3413 
3414  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
3415  ma_uint32 iChannel;
3416  for (iChannel = 0; iChannel < channels; iChannel += 1) {
3417  pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel];
3418  }
3419  }
3420 }
3421 
3422 MA_API void ma_apply_volume_factor_per_channel_f32(float* pFramesOut, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains)
3423 {
3424  ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOut, pFramesOut, frameCount, channels, pChannelGains);
3425 }
3426 
3427 
3428 /* Not used right now, but leaving here for reference. */
3429 #if 0
3430 static ma_result ma_mix_pcm_frames_u8(ma_int16* pDst, const ma_uint8* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
3431 {
3432  ma_uint64 iSample;
3433  ma_uint64 sampleCount;
3434 
3435  if (pDst == NULL || pSrc == NULL || channels == 0) {
3436  return MA_INVALID_ARGS;
3437  }
3438 
3439  if (volume == 0) {
3440  return MA_SUCCESS; /* No changes if the volume is 0. */
3441  }
3442 
3443  sampleCount = frameCount * channels;
3444 
3445  if (volume == 1) {
3446  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3447  pDst[iSample] += ma_pcm_sample_u8_to_s16_no_scale(pSrc[iSample]);
3448  }
3449  } else {
3450  ma_int16 volumeFixed = ma_float_to_fixed_16(volume);
3451  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3452  pDst[iSample] += ma_apply_volume_unclipped_u8(ma_pcm_sample_u8_to_s16_no_scale(pSrc[iSample]), volumeFixed);
3453  }
3454  }
3455 
3456  return MA_SUCCESS;
3457 }
3458 
3459 static ma_result ma_mix_pcm_frames_s16(ma_int32* pDst, const ma_int16* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
3460 {
3461  ma_uint64 iSample;
3462  ma_uint64 sampleCount;
3463 
3464  if (pDst == NULL || pSrc == NULL || channels == 0) {
3465  return MA_INVALID_ARGS;
3466  }
3467 
3468  if (volume == 0) {
3469  return MA_SUCCESS; /* No changes if the volume is 0. */
3470  }
3471 
3472  sampleCount = frameCount * channels;
3473 
3474  if (volume == 1) {
3475  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3476  pDst[iSample] += pSrc[iSample];
3477  }
3478  } else {
3479  ma_int16 volumeFixed = ma_float_to_fixed_16(volume);
3480  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3481  pDst[iSample] += ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed);
3482  }
3483  }
3484 
3485  return MA_SUCCESS;
3486 }
3487 
3488 static ma_result ma_mix_pcm_frames_s24(ma_int64* pDst, const ma_uint8* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
3489 {
3490  ma_uint64 iSample;
3491  ma_uint64 sampleCount;
3492 
3493  if (pDst == NULL || pSrc == NULL || channels == 0) {
3494  return MA_INVALID_ARGS;
3495  }
3496 
3497  if (volume == 0) {
3498  return MA_SUCCESS; /* No changes if the volume is 0. */
3499  }
3500 
3501  sampleCount = frameCount * channels;
3502 
3503  if (volume == 1) {
3504  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3505  pDst[iSample] += ma_pcm_sample_s24_to_s32_no_scale(&pSrc[iSample*3]);
3506  }
3507  } else {
3508  ma_int16 volumeFixed = ma_float_to_fixed_16(volume);
3509  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3510  pDst[iSample] += ma_apply_volume_unclipped_s24(ma_pcm_sample_s24_to_s32_no_scale(&pSrc[iSample*3]), volumeFixed);
3511  }
3512  }
3513 
3514  return MA_SUCCESS;
3515 }
3516 
3517 static ma_result ma_mix_pcm_frames_s32(ma_int64* pDst, const ma_int32* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
3518 {
3519  ma_uint64 iSample;
3520  ma_uint64 sampleCount;
3521 
3522  if (pDst == NULL || pSrc == NULL || channels == 0) {
3523  return MA_INVALID_ARGS;
3524  }
3525 
3526  if (volume == 0) {
3527  return MA_SUCCESS; /* No changes if the volume is 0. */
3528  }
3529 
3530 
3531  sampleCount = frameCount * channels;
3532 
3533  if (volume == 1) {
3534  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3535  pDst[iSample] += pSrc[iSample];
3536  }
3537  } else {
3538  ma_int16 volumeFixed = ma_float_to_fixed_16(volume);
3539  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3540  pDst[iSample] += ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed);
3541  }
3542  }
3543 
3544  return MA_SUCCESS;
3545 }
3546 #endif
3547 
3548 static ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
3549 {
3550  ma_uint64 iSample;
3551  ma_uint64 sampleCount;
3552 
3553  if (pDst == NULL || pSrc == NULL || channels == 0) {
3554  return MA_INVALID_ARGS;
3555  }
3556 
3557  if (volume == 0) {
3558  return MA_SUCCESS; /* No changes if the volume is 0. */
3559  }
3560 
3561  sampleCount = frameCount * channels;
3562 
3563  if (volume == 1) {
3564  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3565  pDst[iSample] += pSrc[iSample];
3566  }
3567  } else {
3568  for (iSample = 0; iSample < sampleCount; iSample += 1) {
3569  pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
3570  }
3571  }
3572 
3573  return MA_SUCCESS;
3574 }
3575 
3576 #if 0
3577 static ma_result ma_mix_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume)
3578 {
3579  ma_result result;
3580 
3581  switch (format)
3582  {
3583  case ma_format_u8: result = ma_mix_pcm_frames_u8( (ma_int16*)pDst, (const ma_uint8*)pSrc, frameCount, channels, volume); break;
3584  case ma_format_s16: result = ma_mix_pcm_frames_s16((ma_int32*)pDst, (const ma_int16*)pSrc, frameCount, channels, volume); break;
3585  case ma_format_s24: result = ma_mix_pcm_frames_s24((ma_int64*)pDst, (const ma_uint8*)pSrc, frameCount, channels, volume); break;
3586  case ma_format_s32: result = ma_mix_pcm_frames_s32((ma_int64*)pDst, (const ma_int32*)pSrc, frameCount, channels, volume); break;
3587  case ma_format_f32: result = ma_mix_pcm_frames_f32(( float*)pDst, (const float*)pSrc, frameCount, channels, volume); break;
3588  default: return MA_INVALID_ARGS; /* Unknown format. */
3589  }
3590 
3591  return result;
3592 }
3593 #endif
3594 
3595 
3596 
3598 {
3600 
3602  config.channels = channels;
3603 
3604  return config;
3605 }
3606 
3607 
3608 static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool8 isReading)
3609 {
3610  MA_ASSERT(pNodeGraph != NULL);
3611  c89atomic_exchange_8(&pNodeGraph->isReading, isReading);
3612 }
3613 
3614 #if 0
3615 static ma_bool8 ma_node_graph_is_reading(ma_node_graph* pNodeGraph)
3616 {
3617  MA_ASSERT(pNodeGraph != NULL);
3618  return c89atomic_load_8(&pNodeGraph->isReading);
3619 }
3620 #endif
3621 
3622 
3623 static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
3624 {
3625  MA_ASSERT(pNode != NULL);
3628 
3629  /* Input channel count needs to be the same as the output channel count. */
3631 
3632  /* We don't need to do anything here because it's a passthrough. */
3633  (void)pNode;
3634  (void)ppFramesIn;
3635  (void)pFrameCountIn;
3636  (void)ppFramesOut;
3637  (void)pFrameCountOut;
3638 
3639 #if 0
3640  /* The data has already been mixed. We just need to move it to the output buffer. */
3641  if (ppFramesIn != NULL) {
3642  ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0));
3643  }
3644 #endif
3645 }
3646 
3647 static ma_node_vtable g_node_graph_endpoint_vtable =
3648 {
3649  ma_node_graph_endpoint_process_pcm_frames,
3650  NULL, /* onGetRequiredInputFrameCount */
3651  1, /* 1 input bus. */
3652  1, /* 1 output bus. */
3653  MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */
3654 };
3655 
3656 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph)
3657 {
3658  ma_result result;
3659  ma_node_config endpointConfig;
3660 
3661  if (pNodeGraph == NULL) {
3662  return MA_INVALID_ARGS;
3663  }
3664 
3665  MA_ZERO_OBJECT(pNodeGraph);
3666 
3667  endpointConfig = ma_node_config_init();
3668  endpointConfig.vtable = &g_node_graph_endpoint_vtable;
3669  endpointConfig.pInputChannels = &pConfig->channels;
3670  endpointConfig.pOutputChannels = &pConfig->channels;
3671 
3672  result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint);
3673  if (result != MA_SUCCESS) {
3674  return result;
3675  }
3676 
3677  return MA_SUCCESS;
3678 }
3679 
3680 MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks)
3681 {
3682  if (pNodeGraph == NULL) {
3683  return;
3684  }
3685 
3686  ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
3687 }
3688 
3690 {
3691  if (pNodeGraph == NULL) {
3692  return NULL;
3693  }
3694 
3695  return &pNodeGraph->endpoint;
3696 }
3697 
3698 MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
3699 {
3700  ma_result result = MA_SUCCESS;
3701  ma_uint32 totalFramesRead;
3702  ma_uint32 channels;
3703 
3704  if (pFramesRead != NULL) {
3705  *pFramesRead = 0; /* Safety. */
3706  }
3707 
3708  if (pNodeGraph == NULL) {
3709  return MA_INVALID_ARGS;
3710  }
3711 
3712  channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
3713 
3714 
3715  /* We'll be nice and try to do a full read of all frameCount frames. */
3716  totalFramesRead = 0;
3717  while (totalFramesRead < frameCount) {
3718  ma_uint32 framesJustRead;
3719  ma_uint32 framesToRead = frameCount - totalFramesRead;
3720 
3721  ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
3722  {
3723  result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));
3724  }
3725  ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
3726 
3727  totalFramesRead += framesJustRead;
3728 
3729  if (result != MA_SUCCESS) {
3730  break;
3731  }
3732 
3733  /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
3734  if (framesJustRead == 0) {
3735  break;
3736  }
3737  }
3738 
3739  /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */
3740  if (totalFramesRead < frameCount) {
3741  ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);
3742  }
3743 
3744  if (pFramesRead != NULL) {
3745  *pFramesRead = totalFramesRead;
3746  }
3747 
3748  return result;
3749 }
3750 
3752 {
3753  if (pNodeGraph == NULL) {
3754  return 0;
3755  }
3756 
3757  return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
3758 }
3759 
3761 {
3762  if (pNodeGraph == NULL) {
3763  return 0;
3764  }
3765 
3766  return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */
3767 }
3768 
3770 {
3771  if (pNodeGraph == NULL) {
3772  return MA_INVALID_ARGS;
3773  }
3774 
3775  return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */
3776 }
3777 
3778 
3779 
3780 static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)
3781 {
3782  MA_ASSERT(pOutputBus != NULL);
3783  MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);
3784  MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));
3785  MA_ASSERT(channels < 256);
3786 
3787  MA_ZERO_OBJECT(pOutputBus);
3788 
3789  if (channels == 0) {
3790  return MA_INVALID_ARGS;
3791  }
3792 
3793  pOutputBus->pNode = pNode;
3794  pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;
3795  pOutputBus->channels = (ma_uint8)channels;
3796  pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */
3797  pOutputBus->volume = 1;
3798 
3799  return MA_SUCCESS;
3800 }
3801 
3802 static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)
3803 {
3804  ma_spinlock_lock(&pOutputBus->lock);
3805 }
3806 
3807 static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)
3808 {
3809  ma_spinlock_unlock(&pOutputBus->lock);
3810 }
3811 
3812 
3813 static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)
3814 {
3815  return pOutputBus->channels;
3816 }
3817 
3818 
3819 static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)
3820 {
3821  if (hasRead) {
3823  } else {
3825  }
3826 }
3827 
3828 static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)
3829 {
3830  return (c89atomic_load_8(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;
3831 }
3832 
3833 
3834 static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool8 isAttached)
3835 {
3836  c89atomic_exchange_8(&pOutputBus->isAttached, isAttached);
3837 }
3838 
3839 static ma_bool8 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)
3840 {
3841  return c89atomic_load_8(&pOutputBus->isAttached);
3842 }
3843 
3844 
3845 static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)
3846 {
3847  MA_ASSERT(pOutputBus != NULL);
3848 
3849  if (volume < 0.0f) {
3850  volume = 0.0f;
3851  }
3852 
3853  c89atomic_exchange_f32(&pOutputBus->volume, volume);
3854 
3855  return MA_SUCCESS;
3856 }
3857 
3858 static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)
3859 {
3860  return c89atomic_load_f32((float*)&pOutputBus->volume);
3861 }
3862 
3863 
3864 static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
3865 {
3866  MA_ASSERT(pInputBus != NULL);
3867  MA_ASSERT(channels < 256);
3868 
3869  MA_ZERO_OBJECT(pInputBus);
3870 
3871  if (channels == 0) {
3872  return MA_INVALID_ARGS;
3873  }
3874 
3875  pInputBus->channels = (ma_uint8)channels;
3876 
3877  return MA_SUCCESS;
3878 }
3879 
3880 static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
3881 {
3882  ma_spinlock_lock(&pInputBus->lock);
3883 }
3884 
3885 static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
3886 {
3887  ma_spinlock_unlock(&pInputBus->lock);
3888 }
3889 
3890 
3891 static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)
3892 {
3893  c89atomic_fetch_add_16(&pInputBus->nextCounter, 1);
3894 }
3895 
3896 static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)
3897 {
3898  c89atomic_fetch_sub_16(&pInputBus->nextCounter, 1);
3899 }
3900 
3901 static ma_uint16 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)
3902 {
3903  return c89atomic_load_16(&pInputBus->nextCounter);
3904 }
3905 
3906 
3907 static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)
3908 {
3909  return pInputBus->channels;
3910 }
3911 
3912 
3913 static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
3914 {
3915  MA_ASSERT(pInputBus != NULL);
3916  MA_ASSERT(pOutputBus != NULL);
3917 
3918  /*
3919  Mark the output bus as detached first. This will prevent future iterations on the audio thread
3920  from iterating this output bus.
3921  */
3922  ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);
3923 
3924  /*
3925  We cannot use the output bus lock here since it'll be getting used at a higher level, but we do
3926  still need to use the input bus lock since we'll be updating pointers on two different output
3927  buses. The same rules apply here as the attaching case. Although we're using a lock here, we're
3928  *not* using a lock when iterating over the list in the audio thread. We therefore need to craft
3929  this in a way such that the iteration on the audio thread doesn't break.
3930 
3931  The the first thing to do is swap out the "next" pointer of the previous output bus with the
3932  new "next" output bus. This is the operation that matters for iteration on the audio thread.
3933  After that, the previous pointer on the new "next" pointer needs to be updated, after which
3934  point the linked list will be in a good state.
3935  */
3936  ma_node_input_bus_lock(pInputBus);
3937  {
3938  ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev);
3939  ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext);
3940 
3941  if (pOldPrev != NULL) {
3942  c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */
3943  }
3944  if (pOldNext != NULL) {
3945  c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */
3946  }
3947  }
3948  ma_node_input_bus_unlock(pInputBus);
3949 
3950  /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */
3951  c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */
3952  c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */
3953  pOutputBus->pInputNode = NULL;
3954  pOutputBus->inputNodeInputBusIndex = 0;
3955 
3956 
3957  /*
3958  For thread-safety reasons, we don't want to be returning from this straight away. We need to
3959  wait for the audio thread to finish with the output bus. There's two things we need to wait
3960  for. The first is the part that selects the next output bus in the list, and the other is the
3961  part that reads from the output bus. Basically all we're doing is waiting for the input bus
3962  to stop referencing the output bus.
3963 
3964  We're doing this part last because we want the section above to run while the audio thread
3965  is finishing up with the output bus, just for efficiency reasons. We marked the output bus as
3966  detached right at the top of this function which is going to prevent the audio thread from
3967  iterating the output bus again.
3968  */
3969 
3970  /* Part 1: Wait for the current iteration to complete. */
3971  while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {
3972  ma_yield();
3973  }
3974 
3975  /* Part 2: Wait for any reads to complete. */
3976  while (c89atomic_load_16(&pOutputBus->refCount) > 0) {
3977  ma_yield();
3978  }
3979 
3980  /*
3981  At this point we're done detaching and we can be guaranteed that the audio thread is not going
3982  to attempt to reference this output bus again (until attached again).
3983  */
3984 }
3985 
3986 #if 0 /* Not used at the moment, but leaving here in case I need it later. */
3987 static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
3988 {
3989  MA_ASSERT(pInputBus != NULL);
3990  MA_ASSERT(pOutputBus != NULL);
3991 
3992  ma_node_output_bus_lock(pOutputBus);
3993  {
3994  ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
3995  }
3996  ma_node_output_bus_unlock(pOutputBus);
3997 }
3998 #endif
3999 
4000 static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)
4001 {
4002  MA_ASSERT(pInputBus != NULL);
4003  MA_ASSERT(pOutputBus != NULL);
4004 
4005  ma_node_output_bus_lock(pOutputBus);
4006  {
4007  ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode);
4008 
4009  /* Detach from any existing attachment first if necessary. */
4010  if (pOldInputNode != NULL) {
4011  ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
4012  }
4013 
4014  /*
4015  At this point we can be sure the output bus is not attached to anything. The linked list in the
4016  old input bus has been updated so that pOutputBus will not get iterated again.
4017  */
4018  pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */
4019  pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* As above. */
4020 
4021  /*
4022  Now we need to attach the output bus to the linked list. This involves updating two pointers on
4023  two different output buses so I'm going to go ahead and keep this simple and just use a lock.
4024  There are ways to do this without a lock, but it's just too hard to maintain for it's value.
4025 
4026  Although we're locking here, it's important to remember that we're *not* locking when iterating
4027  and reading audio data since that'll be running on the audio thread. As a result we need to be
4028  careful how we craft this so that we don't break iteration. What we're going to do is always
4029  attach the new item so that it becomes the first item in the list. That way, as we're iterating
4030  we won't break any links in the list and iteration will continue safely. The detaching case will
4031  also be crafted in a way as to not break list iteration. It's important to remember to use
4032  atomic exchanges here since no locking is happening on the audio thread during iteration.
4033  */
4034  ma_node_input_bus_lock(pInputBus);
4035  {
4036  ma_node_output_bus* pNewPrev = &pInputBus->head;
4038 
4039  /* Update the local output bus. */
4040  c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);
4041  c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);
4042 
4043  /* Update the other output buses to point back to the local output bus. */
4044  c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */
4045 
4046  /* Do the previous pointer last. This is only used for detachment. */
4047  if (pNewNext != NULL) {
4048  c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus);
4049  }
4050  }
4051  ma_node_input_bus_unlock(pInputBus);
4052 
4053  /*
4054  Mark the node as attached last. This is used to controlling whether or the output bus will be
4055  iterated on the audio thread. Mainly required for detachment purposes.
4056  */
4057  ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);
4058  }
4059  ma_node_output_bus_unlock(pOutputBus);
4060 }
4061 
4062 static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
4063 {
4064  ma_node_output_bus* pNext;
4065 
4066  MA_ASSERT(pInputBus != NULL);
4067 
4068  if (pOutputBus == NULL) {
4069  return NULL;
4070  }
4071 
4072  ma_node_input_bus_next_begin(pInputBus);
4073  {
4074  pNext = pOutputBus;
4075  for (;;) {
4076  pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext);
4077  if (pNext == NULL) {
4078  break; /* Reached the end. */
4079  }
4080 
4081  if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {
4082  continue; /* The node is not attached. Keep checking. */
4083  }
4084 
4085  /* The next node has been selected. */
4086  break;
4087  }
4088 
4089  /* We need to increment the reference count of the selected node. */
4090  if (pNext != NULL) {
4091  c89atomic_fetch_add_16(&pNext->refCount, 1);
4092  }
4093 
4094  /* The previous node is no longer being referenced. */
4095  c89atomic_fetch_sub_16(&pOutputBus->refCount, 1);
4096  }
4097  ma_node_input_bus_next_end(pInputBus);
4098 
4099  return pNext;
4100 }
4101 
4102 static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
4103 {
4104  return ma_node_input_bus_next(pInputBus, &pInputBus->head);
4105 }
4106 
4107 
4108 
4109 static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
4110 {
4111  ma_result result = MA_SUCCESS;
4112  ma_node_output_bus* pOutputBus;
4113  ma_node_output_bus* pFirst;
4114  ma_uint32 inputChannels;
4115 
4116  /*
4117  This will be called from the audio thread which means we can't be doing any locking. Basically,
4118  this function will not perfom any locking, whereas attaching and detaching will, but crafted in
4119  such a way that we don't need to perform any locking here. The important thing to remember is
4120  to always iterate in a forward direction.
4121 
4122  In order to process any data we need to first read from all input buses. That's where this
4123  function comes in. This iterates over each of the attachments and accumulates/mixes them. We
4124  also convert the channels to the nodes output channel count before mixing. We want to do this
4125  channel conversion so that the caller of this function can invoke the processing callback
4126  without having to do it themselves.
4127 
4128  When we iterate over each of the attachments on the input bus, we need to read as much data as
4129  we can from each of them so that we don't end up with holes between each of the attachments. To
4130  do this, we need to read from each attachment in a loop and read as many frames as we can, up
4131  to `frameCount`.
4132  */
4133  MA_ASSERT(pInputNode != NULL);
4134  MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */
4135 
4136  *pFramesRead = 0; /* Safety. */
4137 
4138  inputChannels = ma_node_input_bus_get_channels(pInputBus);
4139 
4140  /*
4141  We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They
4142  are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()
4143  once per iteration, however we have an optimization to checks whether or not it's the first item in
4144  the list. We therefore need to store a pointer to the first item rather than repeatedly calling
4145  ma_node_input_bus_first(). It's safe to keep hold of this point, so long as we don't dereference it
4146  after calling ma_node_input_bus_next(), which we won't be.
4147  */
4148  pFirst = ma_node_input_bus_first(pInputBus);
4149  if (pFirst == NULL) {
4150  return MA_SUCCESS; /* No attachments. Read nothing. */
4151  }
4152 
4153  for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
4154  ma_uint32 framesProcessed = 0;
4155 
4156  MA_ASSERT(pOutputBus->pNode != NULL);
4157 
4158  if (pFramesOut != NULL) {
4159  /* Read. */
4160  float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
4161  ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;
4162  float volume = ma_node_output_bus_get_volume(pOutputBus);
4163 
4164  while (framesProcessed < frameCount) {
4165  float* pRunningFramesOut;
4166  ma_uint32 framesToRead;
4167  ma_uint32 framesJustRead;
4168 
4169  framesToRead = frameCount - framesProcessed;
4170  if (framesToRead > tempCapInFrames) {
4171  framesToRead = tempCapInFrames;
4172  }
4173 
4174  pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
4175 
4176  if (pOutputBus == pFirst) {
4177  /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */
4178  result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
4179  if (result == MA_SUCCESS || result == MA_AT_END) {
4180  /* Apply volume, if necessary. */
4181  if (volume != 1) {
4182  ma_apply_volume_factor_f32(pRunningFramesOut, framesJustRead * inputChannels, volume);
4183  }
4184  }
4185  } else {
4186  /* Slow path. Not the first attachment. Mixing required. */
4187  result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);
4188  if (result == MA_SUCCESS || result == MA_AT_END) {
4189  /* Apply volume, if necessary. */
4190  if (volume != 1) {
4191  ma_apply_volume_factor_f32(temp, framesJustRead * inputChannels, volume);
4192  }
4193 
4194  ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);
4195  }
4196  }
4197 
4198  framesProcessed += framesJustRead;
4199 
4200  /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */
4201  if (result != MA_SUCCESS) {
4202  break;
4203  }
4204 
4205  /* If we didn't read anything, abort so we don't get stuck in a loop. */
4206  if (framesJustRead == 0) {
4207  break;
4208  }
4209  }
4210 
4211  /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */
4212  if (pOutputBus == pFirst && framesProcessed < frameCount) {
4213  ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);
4214  }
4215  } else {
4216  /* Seek. */
4217  ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);
4218  }
4219  }
4220 
4221  /* In this path we always "process" the entire amount. */
4222  *pFramesRead = frameCount;
4223 
4224  return result;
4225 }
4226 
4227 
4229 {
4231 
4233  config.initialState = ma_node_state_started; /* Nodes are started by default. */
4234  config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
4235  config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
4236 
4237  return config;
4238 }
4239 
4240 
4241 
4242 static ma_result ma_node_detach_full(ma_node* pNode);
4243 
4244 static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex)
4245 {
4246  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4247  ma_uint32 iInputBus;
4248  float* pBasePtr;
4249 
4250  MA_ASSERT(pNodeBase != NULL);
4251 
4252  /* Input data is stored at the front of the buffer. */
4253  pBasePtr = pNodeBase->pCachedData;
4254  for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) {
4255  pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
4256  }
4257 
4258  return pBasePtr;
4259 }
4260 
4261 static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex)
4262 {
4263  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4264  ma_uint32 iInputBus;
4265  ma_uint32 iOutputBus;
4266  float* pBasePtr;
4267 
4268  MA_ASSERT(pNodeBase != NULL);
4269 
4270  /* Cached output data starts after the input data. */
4271  pBasePtr = pNodeBase->pCachedData;
4272  for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
4273  pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
4274  }
4275 
4276  for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) {
4277  pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]);
4278  }
4279 
4280  return pBasePtr;
4281 }
4282 
4283 
4284 typedef struct
4285 {
4286  size_t sizeInBytes;
4287  size_t inputBusOffset;
4288  size_t outputBusOffset;
4289  size_t cachedDataOffset;
4290  ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */
4291  ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */
4292 } ma_node_heap_layout;
4293 
4294 static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)
4295 {
4296  ma_uint32 inputBusCount;
4297  ma_uint32 outputBusCount;
4298 
4299  MA_ASSERT(pConfig != NULL);
4300  MA_ASSERT(pInputBusCount != NULL);
4301  MA_ASSERT(pOutputBusCount != NULL);
4302 
4303  /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */
4304  if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
4305  inputBusCount = pConfig->inputBusCount;
4306  } else {
4307  inputBusCount = pConfig->vtable->inputBusCount;
4308 
4309  if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {
4310  return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
4311  }
4312  }
4313 
4314  if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
4315  outputBusCount = pConfig->outputBusCount;
4316  } else {
4317  outputBusCount = pConfig->vtable->outputBusCount;
4318 
4319  if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {
4320  return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
4321  }
4322  }
4323 
4324  /* Bus counts must be within limits. */
4325  if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {
4326  return MA_INVALID_ARGS;
4327  }
4328 
4329 
4330  /* We must have channel counts for each bus. */
4331  if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {
4332  return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */
4333  }
4334 
4335 
4336  /* Some special rules for passthrough nodes. */
4337  if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
4338  if (pConfig->vtable->inputBusCount != 1 || pConfig->vtable->outputBusCount != 1) {
4339  return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 input bus and 1 output bus. */
4340  }
4341 
4342  if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) {
4343  return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */
4344  }
4345  }
4346 
4347 
4348  *pInputBusCount = inputBusCount;
4349  *pOutputBusCount = outputBusCount;
4350 
4351  return MA_SUCCESS;
4352 }
4353 
4354 static ma_result ma_node_get_heap_layout(const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)
4355 {
4356  ma_result result;
4357  ma_uint32 inputBusCount;
4358  ma_uint32 outputBusCount;
4359 
4360  MA_ASSERT(pHeapLayout != NULL);
4361 
4362  MA_ZERO_OBJECT(pHeapLayout);
4363 
4364  if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
4365  return MA_INVALID_ARGS;
4366  }
4367 
4368  result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);
4369  if (result != MA_SUCCESS) {
4370  return result;
4371  }
4372 
4373  pHeapLayout->sizeInBytes = 0;
4374 
4375  /* Input buses. */
4376  if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
4377  pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;
4378  pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount);
4379  } else {
4380  pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */
4381  }
4382 
4383  /* Output buses. */
4384  if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
4385  pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;
4386  pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount);
4387  } else {
4388  pHeapLayout->outputBusOffset = MA_SIZE_MAX;
4389  }
4390 
4391  /*
4392  Cached audio data.
4393 
4394  We need to allocate memory for a caching both input and output data. We have an optimization
4395  where no caching is necessary for specific conditions:
4396 
4397  - The node has 0 inputs and 1 output.
4398 
4399  When a node meets the above conditions, no cache is allocated.
4400 
4401  The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by
4402  allocating too much, but at the same time we want it be large enough so that enough frames can
4403  be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For
4404  now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile
4405  time. It might also be worth investigating whether or not this can be configured at run time.
4406  */
4407  if (inputBusCount == 0 && outputBusCount == 1) {
4408  /* Fast path. No cache needed. */
4409  pHeapLayout->cachedDataOffset = MA_SIZE_MAX;
4410  } else {
4411  /* Slow path. Cache needed. */
4412  size_t cachedDataSizeInBytes = 0;
4413  ma_uint32 iBus;
4414 
4415  MA_ASSERT(MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS <= 0xFFFF); /* Clamped to 16 bits. */
4416 
4417  for (iBus = 0; iBus < inputBusCount; iBus += 1) {
4418  cachedDataSizeInBytes += MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
4419  }
4420 
4421  for (iBus = 0; iBus < outputBusCount; iBus += 1) {
4422  cachedDataSizeInBytes += MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
4423  }
4424 
4425  pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
4426  pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes);
4427  }
4428 
4429 
4430  /*
4431  Not technically part of the heap, but we can output the input and output bus counts so we can
4432  avoid a redundant call to ma_node_translate_bus_counts().
4433  */
4434  pHeapLayout->inputBusCount = inputBusCount;
4435  pHeapLayout->outputBusCount = outputBusCount;
4436 
4437  return MA_SUCCESS;
4438 }
4439 
4440 MA_API ma_result ma_node_get_heap_size(const ma_node_config* pConfig, size_t* pHeapSizeInBytes)
4441 {
4442  ma_result result;
4443  ma_node_heap_layout heapLayout;
4444 
4445  if (pHeapSizeInBytes == NULL) {
4446  return MA_INVALID_ARGS;
4447  }
4448 
4449  *pHeapSizeInBytes = 0;
4450 
4451  result = ma_node_get_heap_layout(pConfig, &heapLayout);
4452  if (result != MA_SUCCESS) {
4453  return result;
4454  }
4455 
4456  *pHeapSizeInBytes = heapLayout.sizeInBytes;
4457 
4458  return MA_SUCCESS;
4459 }
4460 
4461 MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)
4462 {
4463  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4464  ma_result result;
4465  ma_node_heap_layout heapLayout;
4466  ma_uint32 iInputBus;
4467  ma_uint32 iOutputBus;
4468 
4469  if (pNodeBase == NULL) {
4470  return MA_INVALID_ARGS;
4471  }
4472 
4473  MA_ZERO_OBJECT(pNodeBase);
4474 
4475  result = ma_node_get_heap_layout(pConfig, &heapLayout);
4476  if (result != MA_SUCCESS) {
4477  return result;
4478  }
4479 
4480  pNodeBase->_pHeap = pHeap;
4481  pNodeBase->pNodeGraph = pNodeGraph;
4482  pNodeBase->vtable = pConfig->vtable;
4483  pNodeBase->state = pConfig->initialState;
4484  pNodeBase->stateTimes[ma_node_state_started] = 0;
4485  pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */
4486  pNodeBase->inputBusCount = heapLayout.inputBusCount;
4487  pNodeBase->outputBusCount = heapLayout.outputBusCount;
4488 
4489  if (heapLayout.inputBusOffset != MA_SIZE_MAX) {
4490  pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
4491  } else {
4492  pNodeBase->pInputBuses = pNodeBase->_inputBuses;
4493  }
4494 
4495  if (heapLayout.outputBusOffset != MA_SIZE_MAX) {
4496  pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
4497  } else {
4498  pNodeBase->pOutputBuses = pNodeBase->_outputBuses;
4499  }
4500 
4501  if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
4502  pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
4503  pNodeBase->cachedDataCapInFramesPerBus = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
4504  } else {
4505  pNodeBase->pCachedData = NULL;
4506  }
4507 
4508 
4509 
4510  /* We need to run an initialization step for each input and output bus. */
4511  for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
4512  result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
4513  if (result != MA_SUCCESS) {
4514  return result;
4515  }
4516  }
4517 
4518  for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
4519  result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);
4520  if (result != MA_SUCCESS) {
4521  return result;
4522  }
4523  }
4524 
4525 
4526  /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */
4527  if (pNodeBase->pCachedData != NULL) {
4528  ma_uint32 iBus;
4529 
4530  #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */
4531  /* For safety we'll go ahead and default the buffer to silence. */
4532  for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
4533  ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]));
4534  }
4535  for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
4536  ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]));
4537  }
4538  #else
4539  /* For debugging. Default to a sine wave. */
4540  for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
4541  ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000);
4542  }
4543  for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
4544  ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000);
4545  }
4546  #endif
4547  }
4548 
4549  return MA_SUCCESS;
4550 }
4551 
4552 MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
4553 {
4554  ma_result result;
4555  size_t heapSizeInBytes;
4556  void* pHeap;
4557 
4558  result = ma_node_get_heap_size(pConfig, &heapSizeInBytes);
4559  if (result != MA_SUCCESS) {
4560  return result;
4561  }
4562 
4563  if (heapSizeInBytes > 0) {
4564  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
4565  if (pHeap == NULL) {
4566  return MA_OUT_OF_MEMORY;
4567  }
4568  } else {
4569  pHeap = NULL;
4570  }
4571 
4572  result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
4573  if (result != MA_SUCCESS) {
4574  ma_free(pHeap, pAllocationCallbacks);
4575  return result;
4576  }
4577 
4578  ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
4579  return MA_SUCCESS;
4580 }
4581 
4582 MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
4583 {
4584  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4585 
4586  if (pNodeBase == NULL) {
4587  return;
4588  }
4589 
4590  /*
4591  The first thing we need to do is fully detach the node. This will detach all inputs and
4592  outputs. We need to do this first because it will sever the connection with the node graph and
4593  allow us to complete uninitialization without needing to worry about thread-safety with the
4594  audio thread. The detachment process will wait for any local processing of the node to finish.
4595  */
4596  ma_node_detach_full(pNode);
4597 
4598  /*
4599  At this point the node should be completely unreferenced by the node graph and we can finish up
4600  the uninitialization process without needing to worry about thread-safety.
4601  */
4602  if (pNodeBase->_pHeap != NULL && pNodeBase->_ownsHeap) {
4603  ma_free(pNodeBase->_pHeap, pAllocationCallbacks);
4604  pNodeBase->_pHeap;
4605  }
4606 }
4607 
4609 {
4610  if (pNode == NULL) {
4611  return NULL;
4612  }
4613 
4614  return ((const ma_node_base*)pNode)->pNodeGraph;
4615 }
4616 
4618 {
4619  if (pNode == NULL) {
4620  return 0;
4621  }
4622 
4623  return ((ma_node_base*)pNode)->inputBusCount;
4624 }
4625 
4627 {
4628  if (pNode == NULL) {
4629  return 0;
4630  }
4631 
4632  return ((ma_node_base*)pNode)->outputBusCount;
4633 }
4634 
4635 
4636 MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)
4637 {
4638  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
4639 
4640  if (pNode == NULL) {
4641  return 0;
4642  }
4643 
4644  if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {
4645  return 0; /* Invalid bus index. */
4646  }
4647 
4648  return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);
4649 }
4650 
4651 MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)
4652 {
4653  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
4654 
4655  if (pNode == NULL) {
4656  return 0;
4657  }
4658 
4659  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
4660  return 0; /* Invalid bus index. */
4661  }
4662 
4663  return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);
4664 }
4665 
4666 
4667 static ma_result ma_node_detach_full(ma_node* pNode)
4668 {
4669  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4670  ma_uint32 iInputBus;
4671 
4672  if (pNodeBase == NULL) {
4673  return MA_INVALID_ARGS;
4674  }
4675 
4676  /*
4677  Make sure the node is completely detached first. This will not return until the output bus is
4678  guaranteed to no longer be referenced by the audio thread.
4679  */
4681 
4682  /*
4683  At this point all output buses will have been detached from the graph and we can be guaranteed
4684  that none of it's input nodes will be getting processed by the graph. We can detach these
4685  without needing to worry about the audio thread touching them.
4686  */
4687  for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
4688  ma_node_input_bus* pInputBus;
4689  ma_node_output_bus* pOutputBus;
4690 
4691  pInputBus = &pNodeBase->pInputBuses[iInputBus];
4692 
4693  /*
4694  This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those
4695  functions are specifically for the audio thread. We'll instead just manually iterate using standard
4696  linked list logic. We don't need to worry about the audio thread referencing these because the step
4697  above severed the connection to the graph.
4698  */
4699  for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) {
4700  ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */
4701  }
4702  }
4703 
4704  return MA_SUCCESS;
4705 }
4706 
4708 {
4709  ma_result result = MA_SUCCESS;
4710  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4711  ma_node_base* pInputNodeBase;
4712 
4713  if (pNode == NULL) {
4714  return MA_INVALID_ARGS;
4715  }
4716 
4717  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
4718  return MA_INVALID_ARGS; /* Invalid output bus index. */
4719  }
4720 
4721  /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */
4722  ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
4723  {
4724  pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
4725  if (pInputNodeBase != NULL) {
4726  ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);
4727  }
4728  }
4729  ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);
4730 
4731  return result;
4732 }
4733 
4735 {
4736  ma_uint32 iOutputBus;
4737 
4738  if (pNode == NULL) {
4739  return MA_INVALID_ARGS;
4740  }
4741 
4742  for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {
4743  ma_node_detach_output_bus(pNode, iOutputBus);
4744  }
4745 
4746  return MA_SUCCESS;
4747 }
4748 
4749 MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)
4750 {
4751  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4752  ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;
4753 
4754  if (pNodeBase == NULL || pOtherNodeBase == NULL) {
4755  return MA_INVALID_ARGS;
4756  }
4757 
4758  if (pNodeBase == pOtherNodeBase) {
4759  return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */
4760  }
4761 
4762  if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) {
4763  return MA_INVALID_OPERATION; /* Invalid bus index. */
4764  }
4765 
4766  /* The output channel count of the output node must be the same as the input channel count of the input node. */
4767  if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) {
4768  return MA_INVALID_OPERATION; /* Channel count is incompatible. */
4769  }
4770 
4771  /* This will deal with detaching if the output bus is already attached to something. */
4772  ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex);
4773 
4774  return MA_SUCCESS;
4775 }
4776 
4777 MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume)
4778 {
4779  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4780 
4781  if (pNodeBase == NULL) {
4782  return MA_INVALID_ARGS;
4783  }
4784 
4785  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
4786  return MA_INVALID_ARGS; /* Invalid bus index. */
4787  }
4788 
4789  return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume);
4790 }
4791 
4792 MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex)
4793 {
4794  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
4795 
4796  if (pNodeBase == NULL) {
4797  return 0;
4798  }
4799 
4800  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
4801  return 0; /* Invalid bus index. */
4802  }
4803 
4804  return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]);
4805 }
4806 
4808 {
4809  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4810 
4811  if (pNodeBase == NULL) {
4812  return MA_INVALID_ARGS;
4813  }
4814 
4815  c89atomic_exchange_i32(&pNodeBase->state, state);
4816 
4817  return MA_SUCCESS;
4818 }
4819 
4821 {
4822  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
4823 
4824  if (pNodeBase == NULL) {
4825  return ma_node_state_stopped;
4826  }
4827 
4828  return (ma_node_state)c89atomic_load_i32(&pNodeBase->state);
4829 }
4830 
4832 {
4833  if (pNode == NULL) {
4834  return MA_INVALID_ARGS;
4835  }
4836 
4837  /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
4838  if (state != ma_node_state_started && state != ma_node_state_stopped) {
4839  return MA_INVALID_ARGS;
4840  }
4841 
4842  c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime);
4843 
4844  return MA_SUCCESS;
4845 }
4846 
4848 {
4849  if (pNode == NULL) {
4850  return 0;
4851  }
4852 
4853  /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
4854  if (state != ma_node_state_started && state != ma_node_state_stopped) {
4855  return 0;
4856  }
4857 
4858  return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]);
4859 }
4860 
4862 {
4863  if (pNode == NULL) {
4864  return ma_node_state_stopped;
4865  }
4866 
4867  return ma_node_get_state_by_time_range(pNode, globalTime, globalTime);
4868 }
4869 
4870 MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
4871 {
4872  ma_node_state state;
4873 
4874  if (pNode == NULL) {
4875  return ma_node_state_stopped;
4876  }
4877 
4878  state = ma_node_get_state(pNode);
4879 
4880  /* An explicitly stopped node is always stopped. */
4881  if (state == ma_node_state_stopped) {
4882  return ma_node_state_stopped;
4883  }
4884 
4885  /*
4886  Getting here means the node is marked as started, but it may still not be truly started due to
4887  it's start time not having been reached yet. Also, the stop time may have also been reached in
4888  which case it'll be considered stopped.
4889  */
4890  if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) {
4891  return ma_node_state_stopped; /* Start time has not yet been reached. */
4892  }
4893 
4894  if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) {
4895  return ma_node_state_stopped; /* Stop time has been reached. */
4896  }
4897 
4898  /* Getting here means the node is marked as started and is within it's start/stop times. */
4899  return ma_node_state_started;
4900 }
4901 
4903 {
4904  if (pNode == NULL) {
4905  return 0;
4906  }
4907 
4908  return c89atomic_load_64(&((ma_node_base*)pNode)->localTime);
4909 }
4910 
4912 {
4913  if (pNode == NULL) {
4914  return MA_INVALID_ARGS;
4915  }
4916 
4917  c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime);
4918 
4919  return MA_SUCCESS;
4920 }
4921 
4922 
4923 
4924 static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
4925 {
4926  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4927 
4928  MA_ASSERT(pNode != NULL);
4929 
4930  if (pNodeBase->vtable->onProcess) {
4931  pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
4932  }
4933 }
4934 
4935 static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
4936 {
4937  ma_node_base* pNodeBase = (ma_node_base*)pNode;
4938  ma_result result = MA_SUCCESS;
4939  ma_uint32 iInputBus;
4940  ma_uint32 iOutputBus;
4941  ma_uint32 inputBusCount;
4942  ma_uint32 outputBusCount;
4943  ma_uint32 totalFramesRead = 0;
4944  float* ppFramesIn[MA_MAX_NODE_BUS_COUNT];
4945  float* ppFramesOut[MA_MAX_NODE_BUS_COUNT];
4946  ma_uint64 globalTimeBeg;
4947  ma_uint64 globalTimeEnd;
4948  ma_uint64 startTime;
4949  ma_uint64 stopTime;
4950  ma_uint32 timeOffsetBeg;
4951  ma_uint32 timeOffsetEnd;
4952  ma_uint32 frameCountIn;
4953  ma_uint32 frameCountOut;
4954 
4955  /*
4956  pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and
4957  expected that the number of frames read may be different to that requested. Therefore, the caller
4958  must look at this value to correctly determine how many frames were read.
4959  */
4960  MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */
4961  if (pFramesRead == NULL) {
4962  return MA_INVALID_ARGS;
4963  }
4964 
4965  *pFramesRead = 0; /* Safety. */
4966 
4967  if (pNodeBase == NULL) {
4968  return MA_INVALID_ARGS;
4969  }
4970 
4971  if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) {
4972  return MA_INVALID_ARGS; /* Invalid output bus index. */
4973  }
4974 
4975  /* Don't do anything if we're in a stopped state. */
4976  if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) {
4977  return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */
4978  }
4979 
4980 
4981  globalTimeBeg = globalTime;
4982  globalTimeEnd = globalTime + frameCount;
4983  startTime = ma_node_get_state_time(pNode, ma_node_state_started);
4984  stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped);
4985 
4986  /*
4987  At this point we know that we are inside our start/stop times. However, we may need to adjust
4988  our frame count and output pointer to accomodate since we could be straddling the time period
4989  that this function is getting called for.
4990 
4991  It's possible (and likely) that the start time does not line up with the output buffer. We
4992  therefore need to offset it by a number of frames to accomodate. The same thing applies for
4993  the stop time.
4994  */
4995  timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0;
4996  timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0;
4997 
4998  /* Trim based on the start offset. We need to silence the start of the buffer. */
4999  if (timeOffsetBeg > 0) {
5000  ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
5001  pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);
5002  frameCount -= timeOffsetBeg;
5003  }
5004 
5005  /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */
5006  if (timeOffsetEnd > 0) {
5007  frameCount -= timeOffsetEnd;
5008  }
5009 
5010 
5011  /* We run on different paths depending on the bus counts. */
5012  inputBusCount = ma_node_get_input_bus_count(pNode);
5013  outputBusCount = ma_node_get_output_bus_count(pNode);
5014 
5015  /*
5016  Run a simplified path when there are no inputs and one output. In this case there's nothing to
5017  actually read and we can go straight to output. This is a very common scenario because the vast
5018  majority of data source nodes will use this setup so this optimization I think is worthwhile.
5019  */
5020  if (inputBusCount == 0 && outputBusCount == 1) {
5021  /* Fast path. No need to read from input and no need for any caching. */
5022  frameCountIn = 0;
5023  frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */
5024 
5025  /* Don't do anything if our read counter is ahead of the node graph. That means we're */
5026  ppFramesOut[0] = pFramesOut;
5027  ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
5028  totalFramesRead = frameCountOut;
5029  } else {
5030  /* Slow path. Need to read input data. */
5031  if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
5032  /*
5033  Fast path. We're running a passthrough. We need to read directly into the output buffer, but
5034  still fire the callback so that event handling and trigger nodes can do their thing. Since
5035  it's a passthrough there's no need for any kind of caching logic.
5036  */
5037  MA_ASSERT(outputBusCount == inputBusCount);
5038  MA_ASSERT(outputBusCount == 1);
5039  MA_ASSERT(outputBusIndex == 0);
5040 
5041  /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */
5042  ppFramesOut[0] = pFramesOut;
5043  ppFramesIn[0] = ppFramesOut[0];
5044 
5045  result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime);
5046  if (result == MA_SUCCESS) {
5047  /* Even though it's a passthrough, we still need to fire the callback. */
5048  frameCountIn = totalFramesRead;
5049  frameCountOut = totalFramesRead;
5050 
5051  if (totalFramesRead > 0) {
5052  ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */
5053  }
5054 
5055  /*
5056  A passthrough should never have modified the input and output frame counts. If you're
5057  triggering these assers you need to fix your processing callback.
5058  */
5059  MA_ASSERT(frameCountIn == totalFramesRead);
5060  MA_ASSERT(frameCountOut == totalFramesRead);
5061  }
5062  } else {
5063  /* Slow path. Need to do caching. */
5064  ma_uint32 framesToProcessIn;
5065  ma_uint32 framesToProcessOut;
5066  ma_bool32 consumeNullInput = MA_FALSE;
5067 
5068  /*
5069  We use frameCount as a basis for the number of frames to read since that's what's being
5070  requested, however we still need to clamp it to whatever can fit in the cache.
5071 
5072  This will also be used as the basis for determining how many input frames to read. This is
5073  not ideal because it can result in too many input frames being read which introduces latency.
5074  To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount
5075  which is used as hint to miniaudio as to how many input frames it needs to read at a time. This
5076  callback is completely optional, and if it's not set, miniaudio will assume `frameCount`.
5077 
5078  This function will be called multiple times for each period of time, once for each output node.
5079  We cannot read from each input node each time this function is called. Instead we need to check
5080  whether or not this is first output bus to be read from for this time period, and if so, read
5081  from our input data.
5082 
5083  To determine whether or not we're ready to read data, we check a flag. There will be one flag
5084  for each output. When the flag is set, it means data has been read previously and that we're
5085  ready to advance time forward for our input nodes by reading fresh data.
5086  */
5087  framesToProcessOut = frameCount;
5088  if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) {
5089  framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus;
5090  }
5091 
5092  framesToProcessIn = frameCount;
5093  if (pNodeBase->vtable->onGetRequiredInputFrameCount) {
5094  framesToProcessIn = pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut);
5095  }
5096  if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) {
5097  framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus;
5098  }
5099 
5100 
5101  MA_ASSERT(framesToProcessIn <= 0xFFFF);
5102  MA_ASSERT(framesToProcessOut <= 0xFFFF);
5103 
5104  if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) {
5105  /* Getting here means we need to do another round of processing. */
5106  pNodeBase->cachedFrameCountOut = 0;
5107 
5108  /*
5109  We need to prepare our output frame pointers for processing. In the same iteration we need
5110  to mark every output bus as unread so that future calls to this function for different buses
5111  for the current time period don't pull in data when they should instead be reading from cache.
5112  */
5113  for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) {
5114  ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */
5115  ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus);
5116  }
5117 
5118  /* We only need to read from input buses if there isn't already some data in the cache. */
5119  if (pNodeBase->cachedFrameCountIn == 0) {
5120  ma_uint32 maxFramesReadIn = 0;
5121 
5122  /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */
5123  for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
5124  ma_uint32 framesRead;
5125 
5126  /* The first thing to do is get the offset within our bulk allocation to store this input data. */
5127  ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);
5128 
5129  /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */
5130  result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);
5131  if (result != MA_SUCCESS) {
5132  /* It doesn't really matter if we fail because we'll just fill with silence. */
5133  framesRead = 0; /* Just for safety, but I don't think it's really needed. */
5134  }
5135 
5136  /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */
5137  /* Any leftover frames need to silenced for safety. */
5138  if (framesRead < framesToProcessIn) {
5139  ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus));
5140  }
5141 
5142  maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);
5143  }
5144 
5145  /* This was a fresh load of input data so reset our consumption counter. */
5146  pNodeBase->consumedFrameCountIn = 0;
5147 
5148  /*
5149  We don't want to keep processing if there's nothing to process, so set the number of cached
5150  input frames to the maximum number we read from each attachment (the lesser will be padded
5151  with silence). If we didn't read anything, this will be set to 0 and the entire buffer will
5152  have been assigned to silence. This being equal to 0 is an important property for us because
5153  it allows us to detect when NULL can be passed into the processing callback for the input
5154  buffer for the purpose of continuous processing.
5155  */
5156  pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;
5157  } else {
5158  /* We don't need to read anything, but we do need to prepare our input frame pointers. */
5159  for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
5160  ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));
5161  }
5162  }
5163 
5164  /*
5165  At this point we have our input data so now we need to do some processing. Sneaky little
5166  optimization here - we can set the pointer to the output buffer for this output bus so
5167  that the final copy into the output buffer is done directly by onProcess().
5168  */
5169  if (pFramesOut != NULL) {
5170  ppFramesOut[outputBusIndex] = pFramesOut;
5171  }
5172 
5173 
5174  /* Give the processing function the entire capacity of the output buffer. */
5175  frameCountOut = framesToProcessOut;
5176 
5177  /*
5178  We need to treat nodes with continuous processing a little differently. For these ones,
5179  we always want to fire the callback with the requested number of frames, regardless of
5180  pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass
5181  in NULL for the input buffer to the callback.
5182  */
5183  if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {
5184  /* We're using continuous processing. Make sure we specify the whole frame count at all times. */
5185  frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer. */
5186 
5187  if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {
5188  consumeNullInput = MA_TRUE;
5189  } else {
5190  consumeNullInput = MA_FALSE;
5191  }
5192  } else {
5193  frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */
5194  consumeNullInput = MA_FALSE;
5195  }
5196 
5197  /*
5198  Process data slightly differently depending on whether or not we're consuming NULL
5199  input (checked just above).
5200  */
5201  if (consumeNullInput) {
5202  ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
5203  } else {
5204  /*
5205  We want to skip processing if there's no input data, but we can only do that safely if
5206  we know that there is no chance of any output frames being produced. If continuous
5207  processing is being used, this won't be a problem because the input frame count will
5208  always be non-0. However, if continuous processing is *not* enabled and input and output
5209  data is processed at different rates, we still need to process that last input frame
5210  because there could be a few excess output frames needing to be produced from cached
5211  data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for
5212  determining whether or not we need to process the node even when there are no input
5213  frames available right now.
5214  */
5215  if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
5216  ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */
5217  }
5218  }
5219 
5220  /*
5221  Thanks to our sneaky optimization above we don't need to do any data copying directly into
5222  the output buffer - the onProcess() callback just did that for us. We do, however, need to
5223  apply the number of input and output frames that were processed. Note that due to continuous
5224  processing above, we need to do explicit checks here. If we just consumed a NULL input
5225  buffer it means that no actual input data was processed from the internal buffers and we
5226  don't want to be modifying any counters.
5227  */
5228  if (consumeNullInput == MA_FALSE) {
5229  pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;
5230  pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn;
5231  }
5232 
5233  /* The cached output frame count is always equal to what we just read. */
5234  pNodeBase->cachedFrameCountOut = (ma_uint16)frameCountOut;
5235  } else {
5236  /*
5237  We're not needing to read anything from the input buffer so just read directly from our
5238  already-processed data.
5239  */
5240  if (pFramesOut != NULL) {
5241  ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex));
5242  }
5243  }
5244 
5245  /* The number of frames read is always equal to the number of cached output frames. */
5246  totalFramesRead = pNodeBase->cachedFrameCountOut;
5247 
5248  /* Now that we've read the data, make sure our read flag is set. */
5249  ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE);
5250  }
5251  }
5252 
5253  /* Advance our local time forward. */
5254  c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead);
5255 
5256  *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */
5257  return result;
5258 }
5259 
5260 
5261 
5262 
5263 /* Data source node. */
5265 {
5267 
5269  config.nodeConfig = ma_node_config_init();
5270  config.pDataSource = pDataSource;
5271  config.looping = looping;
5272 
5273  return config;
5274 }
5275 
5276 
5277 static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
5278 {
5279  ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode;
5280  ma_format format;
5281  ma_uint32 channels;
5282  ma_uint32 frameCount;
5283  ma_uint64 framesRead = 0;
5284 
5285  MA_ASSERT(pDataSourceNode != NULL);
5286  MA_ASSERT(pDataSourceNode->pDataSource != NULL);
5287  MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0);
5288  MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1);
5289 
5290  /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */
5291  (void)ppFramesIn;
5292  (void)pFrameCountIn;
5293 
5294  frameCount = *pFrameCountOut;
5295 
5296  if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */
5297  /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */
5298  MA_ASSERT(format == ma_format_f32);
5299  (void)format; /* Just to silence some static analysis tools. */
5300 
5301  ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead, c89atomic_load_32(&pDataSourceNode->looping));
5302  }
5303 
5304  *pFrameCountOut = (ma_uint32)framesRead;
5305 }
5306 
5307 static ma_node_vtable g_ma_data_source_node_vtable =
5308 {
5309  ma_data_source_node_process_pcm_frames,
5310  NULL, /* onGetRequiredInputFrameCount */
5311  0, /* 0 input buses. */
5312  1, /* 1 output bus. */
5313  0
5314 };
5315 
5316 MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode)
5317 {
5318  ma_result result;
5319  ma_format format; /* For validating the format, which must be ma_format_f32. */
5320  ma_uint32 channels; /* For specifying the channel count of the output bus. */
5321  ma_node_config baseConfig;
5322 
5323  if (pDataSourceNode == NULL) {
5324  return MA_INVALID_ARGS;
5325  }
5326 
5327  MA_ZERO_OBJECT(pDataSourceNode);
5328 
5329  if (pConfig == NULL) {
5330  return MA_INVALID_ARGS;
5331  }
5332 
5333  result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL); /* Don't care about sample rate. This will check pDataSource for NULL. */
5334  if (result != MA_SUCCESS) {
5335  return result;
5336  }
5337 
5338  MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */
5339  if (format != ma_format_f32) {
5340  return MA_INVALID_ARGS; /* Invalid format. */
5341  }
5342 
5343  /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */
5344  baseConfig = pConfig->nodeConfig;
5345  baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */
5346 
5347  /*
5348  The channel count is defined by the data source. It is invalid for the caller to manually set
5349  the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the
5350  channel count pointer to NULL which is how it must remain. If you trigger any of these asserts
5351  it means you're explicitly setting the channel count. Instead, configure the output channel
5352  count of your data source to be the necessary channel count.
5353  */
5354  if (baseConfig.pOutputChannels != NULL) {
5355  return MA_INVALID_ARGS;
5356  }
5357 
5358  baseConfig.pOutputChannels = &channels;
5359 
5360  result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base);
5361  if (result != MA_SUCCESS) {
5362  return result;
5363  }
5364 
5365  pDataSourceNode->pDataSource = pConfig->pDataSource;
5366  pDataSourceNode->looping = pConfig->looping;
5367 
5368  return MA_SUCCESS;
5369 }
5370 
5371 MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks)
5372 {
5373  ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks);
5374 }
5375 
5377 {
5378  if (pDataSourceNode == NULL) {
5379  return MA_INVALID_ARGS;
5380  }
5381 
5382  c89atomic_exchange_32(&pDataSourceNode->looping, looping);
5383 
5384  return MA_SUCCESS;
5385 }
5386 
5388 {
5389  if (pDataSourceNode == NULL) {
5390  return MA_FALSE;
5391  }
5392 
5393  return c89atomic_load_32(&pDataSourceNode->looping);
5394 }
5395 
5396 
5397 
5398 /* Splitter Node. */
5400 {
5402  ma_uint32 inputChannels[1];
5403  ma_uint32 outputChannels[2];
5404 
5405  /* Same channel count between inputs and outputs are required for splitters. */
5406  inputChannels[0] = channels;
5407  outputChannels[0] = channels;
5408  outputChannels[1] = channels;
5409 
5411  config.nodeConfig = ma_node_config_init();
5412  config.nodeConfig.pInputChannels = inputChannels;
5413  config.nodeConfig.pOutputChannels = outputChannels;
5414 
5415  return config;
5416 }
5417 
5418 
5419 static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
5420 {
5421  ma_node_base* pNodeBase = (ma_node_base*)pNode;
5422  ma_uint32 iOutputBus;
5423  ma_uint32 channels;
5424 
5425  MA_ASSERT(pNodeBase != NULL);
5426  MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1);
5427  MA_ASSERT(ma_node_get_output_bus_count(pNodeBase) >= 2);
5428 
5429  /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */
5430  (void)pFrameCountIn;
5431 
5432  /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */
5433  channels = ma_node_get_input_channels(pNodeBase, 0);
5434 
5435  /* Splitting is just copying the first input bus and copying it over to each output bus. */
5436  for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
5437  ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels);
5438  }
5439 }
5440 
5441 static ma_node_vtable g_ma_splitter_node_vtable =
5442 {
5443  ma_splitter_node_process_pcm_frames,
5444  NULL, /* onGetRequiredInputFrameCount */
5445  1, /* 1 input bus. */
5446  2, /* 2 output buses. */
5447  0
5448 };
5449 
5450 MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode)
5451 {
5452  ma_result result;
5453  ma_node_config baseConfig;
5454 
5455  if (pSplitterNode == NULL) {
5456  return MA_INVALID_ARGS;
5457  }
5458 
5459  MA_ZERO_OBJECT(pSplitterNode);
5460 
5461  if (pConfig == NULL) {
5462  return MA_INVALID_ARGS;
5463  }
5464 
5465  if (pConfig->nodeConfig.pInputChannels == NULL || pConfig->nodeConfig.pOutputChannels == NULL) {
5466  return MA_INVALID_ARGS; /* No channel counts specified. */
5467  }
5468 
5469  /* Splitters require the same number of channels between inputs and outputs. */
5470  if (pConfig->nodeConfig.pInputChannels[0] != pConfig->nodeConfig.pOutputChannels[0]) {
5471  return MA_INVALID_ARGS;
5472  }
5473 
5474  baseConfig = pConfig->nodeConfig;
5475  baseConfig.vtable = &g_ma_splitter_node_vtable;
5476 
5477  result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base);
5478  if (result != MA_SUCCESS) {
5479  return result; /* Failed to initialize the base node. */
5480  }
5481 
5482  return MA_SUCCESS;
5483 }
5484 
5485 MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks)
5486 {
5487  ma_node_uninit(pSplitterNode, pAllocationCallbacks);
5488 }
5489 
5490 
5491 
5492 
5493 
5494 #ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS
5495 #define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000
5496 #endif
5497 
5498 
5499 static ma_uint32 ma_ffs_32(ma_uint32 x)
5500 {
5501  ma_uint32 i;
5502 
5503  /* Just a naive implementation just to get things working for now. Will optimize this later. */
5504  for (i = 0; i < 32; i += 1) {
5505  if ((x & (1 << i)) != 0) {
5506  return i;
5507  }
5508  }
5509 
5510  return i;
5511 }
5512 
5513 
5514 
5515 #if 0
5516 static void ma_accumulate_and_clip_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)
5517 {
5518  ma_uint64 iSample;
5519 
5520  MA_ASSERT(pDst != NULL);
5521  MA_ASSERT(pSrc != NULL);
5522 
5523  for (iSample = 0; iSample < count; iSample += 1) {
5524  pDst[iSample] = ma_clip_u8(ma_pcm_sample_u8_to_s16_no_scale(pDst[iSample]) + pSrc[iSample]);
5525  }
5526 }
5527 
5528 static void ma_accumulate_and_clip_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count)
5529 {
5530  ma_uint64 iSample;
5531 
5532  MA_ASSERT(pDst != NULL);
5533  MA_ASSERT(pSrc != NULL);
5534 
5535  for (iSample = 0; iSample < count; iSample += 1) {
5536  pDst[iSample] = ma_clip_s16(pDst[iSample] + pSrc[iSample]);
5537  }
5538 }
5539 
5540 static void ma_accumulate_and_clip_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count)
5541 {
5542  ma_uint64 iSample;
5543 
5544  MA_ASSERT(pDst != NULL);
5545  MA_ASSERT(pSrc != NULL);
5546 
5547  for (iSample = 0; iSample < count; iSample += 1) {
5548  ma_int64 s = ma_clip_s24(ma_pcm_sample_s24_to_s32_no_scale(&pDst[iSample*3]) + pSrc[iSample]);
5549  pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
5550  pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
5551  pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
5552  }
5553 }
5554 
5555 static void ma_accumulate_and_clip_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)
5556 {
5557  ma_uint64 iSample;
5558 
5559  MA_ASSERT(pDst != NULL);
5560  MA_ASSERT(pSrc != NULL);
5561 
5562  for (iSample = 0; iSample < count; iSample += 1) {
5563  pDst[iSample] = ma_clip_s32(pDst[iSample] + pSrc[iSample]);
5564  }
5565 }
5566 
5567 static void ma_accumulate_and_clip_f32(float* pDst, const float* pSrc, ma_uint64 count)
5568 {
5569  ma_uint64 iSample;
5570 
5571  MA_ASSERT(pDst != NULL);
5572  MA_ASSERT(pSrc != NULL);
5573 
5574  for (iSample = 0; iSample < count; iSample += 1) {
5575  pDst[iSample] = ma_clip_f32(pDst[iSample] + pSrc[iSample]);
5576  }
5577 }
5578 #endif
5579 
5580 
5581 static void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)
5582 {
5583  ma_uint64 iSample;
5584 
5585  MA_ASSERT(pDst != NULL);
5586  MA_ASSERT(pSrc != NULL);
5587 
5588  for (iSample = 0; iSample < count; iSample += 1) {
5589  pDst[iSample] = ma_clip_u8(pSrc[iSample]);
5590  }
5591 }
5592 
5593 static void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count)
5594 {
5595  ma_uint64 iSample;
5596 
5597  MA_ASSERT(pDst != NULL);
5598  MA_ASSERT(pSrc != NULL);
5599 
5600  for (iSample = 0; iSample < count; iSample += 1) {
5601  pDst[iSample] = ma_clip_s16(pSrc[iSample]);
5602  }
5603 }
5604 
5605 static void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count)
5606 {
5607  ma_uint64 iSample;
5608 
5609  MA_ASSERT(pDst != NULL);
5610  MA_ASSERT(pSrc != NULL);
5611 
5612  for (iSample = 0; iSample < count; iSample += 1) {
5613  ma_int64 s = ma_clip_s24(pSrc[iSample]);
5614  pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
5615  pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
5616  pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
5617  }
5618 }
5619 
5620 static void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)
5621 {
5622  ma_uint64 iSample;
5623 
5624  MA_ASSERT(pDst != NULL);
5625  MA_ASSERT(pSrc != NULL);
5626 
5627  for (iSample = 0; iSample < count; iSample += 1) {
5628  pDst[iSample] = ma_clip_s32(pSrc[iSample]);
5629  }
5630 }
5631 
5632 static void ma_clip_samples_f32_ex(float* pDst, const float* pSrc, ma_uint64 count)
5633 {
5634  ma_uint64 iSample;
5635 
5636  MA_ASSERT(pDst != NULL);
5637  MA_ASSERT(pSrc != NULL);
5638 
5639  for (iSample = 0; iSample < count; iSample += 1) {
5640  pDst[iSample] = ma_clip_f32(pSrc[iSample]);
5641  }
5642 }
5643 
5644 
5645 
5646 static void ma_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)
5647 {
5648  ma_uint64 iSample;
5649  ma_int16 volumeFixed;
5650 
5651  MA_ASSERT(pDst != NULL);
5652  MA_ASSERT(pSrc != NULL);
5653 
5654  volumeFixed = ma_float_to_fixed_16(volume);
5655 
5656  for (iSample = 0; iSample < count; iSample += 1) {
5657  pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));
5658  }
5659 }
5660 
5661 static void ma_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)
5662 {
5663  ma_uint64 iSample;
5664  ma_int16 volumeFixed;
5665 
5666  MA_ASSERT(pDst != NULL);
5667  MA_ASSERT(pSrc != NULL);
5668 
5669  volumeFixed = ma_float_to_fixed_16(volume);
5670 
5671  for (iSample = 0; iSample < count; iSample += 1) {
5672  pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));
5673  }
5674 }
5675 
5676 static void ma_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
5677 {
5678  ma_uint64 iSample;
5679  ma_int16 volumeFixed;
5680 
5681  MA_ASSERT(pDst != NULL);
5682  MA_ASSERT(pSrc != NULL);
5683 
5684  volumeFixed = ma_float_to_fixed_16(volume);
5685 
5686  for (iSample = 0; iSample < count; iSample += 1) {
5687  ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));
5688  pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
5689  pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
5690  pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
5691  }
5692 }
5693 
5694 static void ma_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
5695 {
5696  ma_uint64 iSample;
5697  ma_int16 volumeFixed;
5698 
5699  MA_ASSERT(pDst != NULL);
5700  MA_ASSERT(pSrc != NULL);
5701 
5702  volumeFixed = ma_float_to_fixed_16(volume);
5703 
5704  for (iSample = 0; iSample < count; iSample += 1) {
5705  pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed));
5706  }
5707 }
5708 
5709 static void ma_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)
5710 {
5711  ma_uint64 iSample;
5712 
5713  MA_ASSERT(pDst != NULL);
5714  MA_ASSERT(pSrc != NULL);
5715 
5716  /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */
5717 
5718  for (iSample = 0; iSample < count; iSample += 1) {
5719  pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume));
5720  }
5721 }
5722 
5723 static void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
5724 {
5725  ma_uint64 sampleCount;
5726 
5727  MA_ASSERT(pDst != NULL);
5728  MA_ASSERT(pSrc != NULL);
5729 
5730  sampleCount = frameCount * channels;
5731 
5732  switch (format) {
5733  case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break;
5734  case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break;
5735  case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break;
5736  case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break;
5737  case ma_format_f32: ma_clip_samples_f32_ex((float*)pDst, (const float*)pSrc, sampleCount); break;
5738 
5739  /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */
5740  case ma_format_unknown:
5741  case ma_format_count:
5742  break;
5743  }
5744 }
5745 
5746 static void ma_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume)
5747 {
5748  MA_ASSERT(pDst != NULL);
5749  MA_ASSERT(pSrc != NULL);
5750 
5751  if (volume == 1) {
5752  ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */
5753  } else if (volume == 0) {
5754  ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */
5755  } else {
5756  ma_uint64 sampleCount = frameCount * channels;
5757 
5758  switch (format) {
5759  case ma_format_u8: ma_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;
5760  case ma_format_s16: ma_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;
5761  case ma_format_s24: ma_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
5762  case ma_format_s32: ma_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
5763  case ma_format_f32: ma_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break;
5764 
5765  /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */
5766  case ma_format_unknown:
5767  case ma_format_count:
5768  break;
5769  }
5770  }
5771 }
5772 
5773 
5774 /* Not used at the moment, but leaving it here in case I want to use it again later. */
5775 #if 0
5776 static void ma_clipped_accumulate_u8(ma_uint8* pDst, const ma_uint8* pSrc, ma_uint64 sampleCount)
5777 {
5778  ma_uint64 iSample;
5779 
5780  MA_ASSERT(pDst != NULL);
5781  MA_ASSERT(pSrc != NULL);
5782 
5783  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5784  pDst[iSample] = ma_clip_u8(ma_pcm_sample_u8_to_s16_no_scale(pDst[iSample]) + ma_pcm_sample_u8_to_s16_no_scale(pSrc[iSample]));
5785  }
5786 }
5787 
5788 static void ma_clipped_accumulate_s16(ma_int16* pDst, const ma_int16* pSrc, ma_uint64 sampleCount)
5789 {
5790  ma_uint64 iSample;
5791 
5792  MA_ASSERT(pDst != NULL);
5793  MA_ASSERT(pSrc != NULL);
5794 
5795  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5796  pDst[iSample] = ma_clip_s16((ma_int32)pDst[iSample] + (ma_int32)pSrc[iSample]);
5797  }
5798 }
5799 
5800 static void ma_clipped_accumulate_s24(ma_uint8* pDst, const ma_uint8* pSrc, ma_uint64 sampleCount)
5801 {
5802  ma_uint64 iSample;
5803 
5804  MA_ASSERT(pDst != NULL);
5805  MA_ASSERT(pSrc != NULL);
5806 
5807  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5809  pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
5810  pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
5811  pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
5812  }
5813 }
5814 
5815 static void ma_clipped_accumulate_s32(ma_int32* pDst, const ma_int32* pSrc, ma_uint64 sampleCount)
5816 {
5817  ma_uint64 iSample;
5818 
5819  MA_ASSERT(pDst != NULL);
5820  MA_ASSERT(pSrc != NULL);
5821 
5822  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5823  pDst[iSample] = ma_clip_s32((ma_int64)pDst[iSample] + (ma_int64)pSrc[iSample]);
5824  }
5825 }
5826 
5827 static void ma_clipped_accumulate_f32(float* pDst, const float* pSrc, ma_uint64 sampleCount)
5828 {
5829  ma_uint64 iSample;
5830 
5831  MA_ASSERT(pDst != NULL);
5832  MA_ASSERT(pSrc != NULL);
5833 
5834  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5835  pDst[iSample] = ma_clip_f32(pDst[iSample] + pSrc[iSample]);
5836  }
5837 }
5838 
5839 static void ma_clipped_accumulate_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
5840 {
5841  ma_uint64 sampleCount;
5842 
5843  MA_ASSERT(pDst != NULL);
5844  MA_ASSERT(pSrc != NULL);
5845 
5846  sampleCount = frameCount * channels;
5847 
5848  switch (format) {
5849  case ma_format_u8: ma_clipped_accumulate_u8( (ma_uint8*)pDst, (const ma_uint8*)pSrc, sampleCount); break;
5850  case ma_format_s16: ma_clipped_accumulate_s16((ma_int16*)pDst, (const ma_int16*)pSrc, sampleCount); break;
5851  case ma_format_s24: ma_clipped_accumulate_s24((ma_uint8*)pDst, (const ma_uint8*)pSrc, sampleCount); break;
5852  case ma_format_s32: ma_clipped_accumulate_s32((ma_int32*)pDst, (const ma_int32*)pSrc, sampleCount); break;
5853  case ma_format_f32: ma_clipped_accumulate_f32(( float*)pDst, (const float*)pSrc, sampleCount); break;
5854 
5855  /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */
5856  case ma_format_unknown:
5857  case ma_format_count:
5858  break;
5859  }
5860 }
5861 #endif
5862 
5863 
5864 /* Not used right now, but leaving here for reference. */
5865 #if 0
5866 static void ma_unclipped_accumulate_u8(ma_int16* pDst, const ma_uint8* pSrc, ma_uint64 sampleCount)
5867 {
5868  ma_uint64 iSample;
5869 
5870  MA_ASSERT(pDst != NULL);
5871  MA_ASSERT(pSrc != NULL);
5872 
5873  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5874  pDst[iSample] = pDst[iSample] + ma_pcm_sample_u8_to_s16_no_scale(pSrc[iSample]);
5875  }
5876 }
5877 
5878 static void ma_unclipped_accumulate_s16(ma_int32* pDst, const ma_int16* pSrc, ma_uint64 sampleCount)
5879 {
5880  ma_uint64 iSample;
5881 
5882  MA_ASSERT(pDst != NULL);
5883  MA_ASSERT(pSrc != NULL);
5884 
5885  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5886  pDst[iSample] = (ma_int32)pDst[iSample] + (ma_int32)pSrc[iSample];
5887  }
5888 }
5889 
5890 static void ma_unclipped_accumulate_s24(ma_int64* pDst, const ma_uint8* pSrc, ma_uint64 sampleCount)
5891 {
5892  ma_uint64 iSample;
5893 
5894  MA_ASSERT(pDst != NULL);
5895  MA_ASSERT(pSrc != NULL);
5896 
5897  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5898  pDst[iSample] = pDst[iSample] + ma_pcm_sample_s24_to_s32_no_scale(&pSrc[iSample*3]);
5899  }
5900 }
5901 
5902 static void ma_unclipped_accumulate_s32(ma_int64* pDst, const ma_int32* pSrc, ma_uint64 sampleCount)
5903 {
5904  ma_uint64 iSample;
5905 
5906  MA_ASSERT(pDst != NULL);
5907  MA_ASSERT(pSrc != NULL);
5908 
5909  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5910  pDst[iSample] = (ma_int64)pDst[iSample] + (ma_int64)pSrc[iSample];
5911  }
5912 }
5913 
5914 static void ma_unclipped_accumulate_f32(float* pDst, const float* pSrc, ma_uint64 sampleCount)
5915 {
5916  ma_uint64 iSample;
5917 
5918  MA_ASSERT(pDst != NULL);
5919  MA_ASSERT(pSrc != NULL);
5920 
5921  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5922  pDst[iSample] = pDst[iSample] + pSrc[iSample];
5923  }
5924 }
5925 
5926 static void ma_unclipped_accumulate_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
5927 {
5928  ma_uint64 sampleCount;
5929 
5930  MA_ASSERT(pDst != NULL);
5931  MA_ASSERT(pSrc != NULL);
5932 
5933  sampleCount = frameCount * channels;
5934 
5935  switch (format) {
5936  case ma_format_u8: ma_unclipped_accumulate_u8( (ma_int16*)pDst, (const ma_uint8*)pSrc, sampleCount); break;
5937  case ma_format_s16: ma_unclipped_accumulate_s16((ma_int32*)pDst, (const ma_int16*)pSrc, sampleCount); break;
5938  case ma_format_s24: ma_unclipped_accumulate_s24((ma_int64*)pDst, (const ma_uint8*)pSrc, sampleCount); break;
5939  case ma_format_s32: ma_unclipped_accumulate_s32((ma_int64*)pDst, (const ma_int32*)pSrc, sampleCount); break;
5940  case ma_format_f32: ma_unclipped_accumulate_f32(( float*)pDst, (const float*)pSrc, sampleCount); break;
5941 
5942  /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */
5943  case ma_format_unknown:
5944  case ma_format_count:
5945  break;
5946  }
5947 }
5948 #endif
5949 
5950 
5951 /* Not used right now, but leaving here for reference. */
5952 #if 0
5953 static void ma_volume_and_accumulate_and_clip_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)
5954 {
5955  ma_uint64 iSample;
5956  ma_int16 volumeFixed;
5957 
5958  MA_ASSERT(pDst != NULL);
5959  MA_ASSERT(pSrc != NULL);
5960 
5961  volumeFixed = ma_float_to_fixed_16(volume);
5962 
5963  for (iSample = 0; iSample < count; iSample += 1) {
5964  pDst[iSample] = ma_clip_u8(ma_pcm_sample_u8_to_s16_no_scale(pDst[iSample]) + ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));
5965  }
5966 }
5967 
5968 static void ma_volume_and_accumulate_and_clip_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)
5969 {
5970  ma_uint64 iSample;
5971  ma_int16 volumeFixed;
5972 
5973  MA_ASSERT(pDst != NULL);
5974  MA_ASSERT(pSrc != NULL);
5975 
5976  volumeFixed = ma_float_to_fixed_16(volume);
5977 
5978  for (iSample = 0; iSample < count; iSample += 1) {
5979  pDst[iSample] = ma_clip_s16(pDst[iSample] + ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));
5980  }
5981 }
5982 
5983 static void ma_volume_and_accumulate_and_clip_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
5984 {
5985  ma_uint64 iSample;
5986  ma_int16 volumeFixed;
5987 
5988  MA_ASSERT(pDst != NULL);
5989  MA_ASSERT(pSrc != NULL);
5990 
5991  volumeFixed = ma_float_to_fixed_16(volume);
5992 
5993  for (iSample = 0; iSample < count; iSample += 1) {
5994  ma_int64 s = ma_clip_s24(ma_pcm_sample_s24_to_s32_no_scale(&pDst[iSample*3]) + ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));
5995  pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
5996  pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
5997  pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
5998  }
5999 }
6000 
6001 static void ma_volume_and_accumulate_and_clip_s32(ma_int32* dst, const ma_int64* src, ma_uint64 count, float volume)
6002 {
6003  ma_uint64 iSample;
6004  ma_int16 volumeFixed;
6005 
6006  MA_ASSERT(dst != NULL);
6007  MA_ASSERT(src != NULL);
6008 
6009  volumeFixed = ma_float_to_fixed_16(volume);
6010 
6011  for (iSample = 0; iSample < count; iSample += 1) {
6012  dst[iSample] = ma_clip_s32(dst[iSample] + ma_apply_volume_unclipped_s32(src[iSample], volumeFixed));
6013  }
6014 }
6015 
6016 static void ma_volume_and_accumulate_and_clip_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)
6017 {
6018  ma_uint64 iSample;
6019 
6020  MA_ASSERT(pDst != NULL);
6021  MA_ASSERT(pSrc != NULL);
6022 
6023  for (iSample = 0; iSample < count; iSample += 1) {
6024  pDst[iSample] = ma_clip_f32(pDst[iSample] + ma_apply_volume_unclipped_f32(pSrc[iSample], volume));
6025  }
6026 }
6027 
6028 static ma_result ma_volume_and_accumulate_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume)
6029 {
6030  ma_uint64 sampleCount;
6031 
6032  if (pDst == NULL || pSrc == NULL) {
6033  return MA_INVALID_ARGS;
6034  }
6035 
6036  /* The output buffer cannot be the same as the accumulation buffer. */
6037  if (pDst == pSrc) {
6038  return MA_INVALID_OPERATION;
6039  }
6040 
6041  /* No-op if there's no volume. */
6042  if (volume == 0) {
6043  return MA_SUCCESS;
6044  }
6045 
6046  sampleCount = frameCount * channels;
6047 
6048  /* No need for volume control if the volume is 1. */
6049  if (volume == 1) {
6050  switch (format) {
6051  case ma_format_u8: ma_accumulate_and_clip_u8( pDst, pSrc, sampleCount); break;
6052  case ma_format_s16: ma_accumulate_and_clip_s16(pDst, pSrc, sampleCount); break;
6053  case ma_format_s24: ma_accumulate_and_clip_s24(pDst, pSrc, sampleCount); break;
6054  case ma_format_s32: ma_accumulate_and_clip_s32(pDst, pSrc, sampleCount); break;
6055  case ma_format_f32: ma_accumulate_and_clip_f32(pDst, pSrc, sampleCount); break;
6056  default: return MA_INVALID_ARGS; /* Unknown format. */
6057  }
6058  } else {
6059  /* Getting here means the volume is not 0 nor 1. */
6060  MA_ASSERT(volume != 0 && volume != 1);
6061 
6062  switch (format) {
6063  case ma_format_u8: ma_volume_and_accumulate_and_clip_u8( pDst, pSrc, sampleCount, volume); break;
6064  case ma_format_s16: ma_volume_and_accumulate_and_clip_s16(pDst, pSrc, sampleCount, volume); break;
6065  case ma_format_s24: ma_volume_and_accumulate_and_clip_s24(pDst, pSrc, sampleCount, volume); break;
6066  case ma_format_s32: ma_volume_and_accumulate_and_clip_s32(pDst, pSrc, sampleCount, volume); break;
6067  case ma_format_f32: ma_volume_and_accumulate_and_clip_f32(pDst, pSrc, sampleCount, volume); break;
6068  default: return MA_INVALID_ARGS; /* Unknown format. */
6069  }
6070  }
6071 
6072  return MA_SUCCESS;
6073 }
6074 #endif
6075 
6076 
6077 /* Not used right now, but leaving here for reference. */
6078 #if 0
6079 static void ma_mix_accumulation_buffers_u8(ma_int16* pDst, const ma_int16* pSrc, ma_uint64 sampleCount, float volume)
6080 {
6081  ma_uint64 iSample;
6082  ma_int16 volumeFixed;
6083 
6084  MA_ASSERT(pDst != NULL);
6085  MA_ASSERT(pSrc != NULL);
6086 
6087  volumeFixed = ma_float_to_fixed_16(volume);
6088 
6089  for (iSample = 0; iSample < sampleCount; iSample += 1) {
6090  pDst[iSample] += ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed);
6091  }
6092 }
6093 
6094 static void ma_mix_accumulation_buffers_s16(ma_int32* pDst, const ma_int32* pSrc, ma_uint64 sampleCount, float volume)
6095 {
6096  ma_uint64 iSample;
6097  ma_int16 volumeFixed;
6098 
6099  MA_ASSERT(pDst != NULL);
6100  MA_ASSERT(pSrc != NULL);
6101 
6102  volumeFixed = ma_float_to_fixed_16(volume);
6103 
6104  for (iSample = 0; iSample < sampleCount; iSample += 1) {
6105  pDst[iSample] += ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed);
6106  }
6107 }
6108 
6109 static void ma_mix_accumulation_buffers_s24(ma_int64* pDst, const ma_int64* pSrc, ma_uint64 sampleCount, float volume)
6110 {
6111  ma_uint64 iSample;
6112  ma_int16 volumeFixed;
6113 
6114  MA_ASSERT(pDst != NULL);
6115  MA_ASSERT(pSrc != NULL);
6116 
6117  volumeFixed = ma_float_to_fixed_16(volume);
6118 
6119  for (iSample = 0; iSample < sampleCount; iSample += 1) {
6120  pDst[iSample] += ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed);
6121  }
6122 }
6123 
6124 static void ma_mix_accumulation_buffers_s32(ma_int64* pDst, const ma_int64* pSrc, ma_uint64 sampleCount, float volume)
6125 {
6126  ma_uint64 iSample;
6127  ma_int16 volumeFixed;
6128 
6129  MA_ASSERT(pDst != NULL);
6130  MA_ASSERT(pSrc != NULL);
6131 
6132  volumeFixed = ma_float_to_fixed_16(volume);
6133 
6134  for (iSample = 0; iSample < sampleCount; iSample += 1) {
6135  pDst[iSample] += ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed);
6136  }
6137 }
6138 
6139 static void ma_mix_accumulation_buffers_f32(float* pDst, const float* pSrc, ma_uint64 sampleCount, float volume)
6140 {
6141  ma_uint64 iSample;
6142 
6143  MA_ASSERT(pDst != NULL);
6144  MA_ASSERT(pSrc != NULL);
6145 
6146  for (iSample = 0; iSample < sampleCount; iSample += 1) {
6147  pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
6148  }
6149 }
6150 
6151 static void ma_mix_accumulation_buffers(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format formatIn, ma_uint32 channelsIn, float volume)
6152 {
6153  ma_uint64 sampleCount;
6154 
6155  MA_ASSERT(pDst != NULL);
6156  MA_ASSERT(pSrc != NULL);
6157 
6158  sampleCount = frameCount * channelsIn;
6159 
6160  switch (formatIn)
6161  {
6162  case ma_format_u8: ma_mix_accumulation_buffers_u8( (ma_int16*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;
6163  case ma_format_s16: ma_mix_accumulation_buffers_s16((ma_int32*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;
6164  case ma_format_s24: ma_mix_accumulation_buffers_s24((ma_int64*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
6165  case ma_format_s32: ma_mix_accumulation_buffers_s32((ma_int64*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
6166  case ma_format_f32: ma_mix_accumulation_buffers_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break;
6167  default: break;
6168  }
6169 }
6170 
6171 static void ma_mix_accumulation_buffers_ex(void* pDst, ma_format formatOut, ma_uint32 channelsOut, const void* pSrc, ma_format formatIn, ma_uint32 channelsIn, ma_uint64 frameCount, float volume)
6172 {
6173  if (formatOut == formatIn && channelsOut == channelsIn) {
6174  /* Fast path. No conversion required. */
6175  ma_mix_accumulation_buffers(pDst, pSrc, frameCount, formatIn, channelsIn, volume);
6176  } else {
6177  /* Slow path. Conversion required. The way we're going to do this is clip the input buffer, and then use existing mixing infrastructure to mix as if it were regular input. */
6178  ma_uint8 clippedSrcBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* formatIn, channelsIn */
6179  ma_uint32 clippedSrcBufferCapInFrames = sizeof(clippedSrcBuffer) / ma_get_bytes_per_frame(formatIn, channelsIn);
6180  ma_uint64 totalFramesProcessed = 0;
6181  /* */ void* pRunningDst = pDst;
6182  const void* pRunningSrc = pSrc;
6183 
6184  while (totalFramesProcessed < frameCount) {
6185  ma_uint64 framesToProcess = frameCount - totalFramesProcessed;
6186  if (framesToProcess > clippedSrcBufferCapInFrames) {
6187  framesToProcess = clippedSrcBufferCapInFrames;
6188  }
6189 
6190  /* Volume and clip. */
6191  ma_volume_and_clip_pcm_frames(clippedSrcBuffer, pRunningSrc, framesToProcess, formatIn, channelsIn, volume);
6192 
6193  /* Mix. */
6194  ma_mix_pcm_frames_ex(pRunningDst, formatOut, channelsOut, clippedSrcBuffer, formatIn, channelsIn, framesToProcess, 1);
6195 
6196  totalFramesProcessed += framesToProcess;
6197  pRunningDst = ma_offset_ptr(pRunningDst, framesToProcess * ma_get_accumulation_bytes_per_frame(formatOut, channelsOut));
6198  pRunningSrc = ma_offset_ptr(pRunningSrc, framesToProcess * ma_get_accumulation_bytes_per_frame(formatIn, channelsIn ));
6199  }
6200  }
6201 }
6202 #endif
6203 
6204 
6205 
6206 
6208 {
6209  if (pAllocator == NULL) {
6210  return MA_INVALID_ARGS;
6211  }
6212 
6213  MA_ZERO_OBJECT(pAllocator);
6214 
6215  return MA_SUCCESS;
6216 }
6217 
6219 {
6220  ma_uint32 capacity;
6221  ma_uint32 iAttempt;
6222  const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */
6223 
6224  if (pAllocator == NULL || pSlot == NULL) {
6225  return MA_INVALID_ARGS;
6226  }
6227 
6228  capacity = ma_countof(pAllocator->groups) * 32;
6229 
6230  for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) {
6231  /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */
6232  ma_uint32 iGroup;
6233  for (iGroup = 0; iGroup < ma_countof(pAllocator->groups); iGroup += 1) {
6234  /* CAS */
6235  for (;;) {
6236  ma_uint32 oldBitfield;
6237  ma_uint32 newBitfield;
6238  ma_uint32 bitOffset;
6239 
6240  oldBitfield = c89atomic_load_32(&pAllocator->groups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
6241 
6242  /* Fast check to see if anything is available. */
6243  if (oldBitfield == 0xFFFFFFFF) {
6244  break; /* No available bits in this bitfield. */
6245  }
6246 
6247  bitOffset = ma_ffs_32(~oldBitfield);
6248  MA_ASSERT(bitOffset < 32);
6249 
6250  newBitfield = oldBitfield | (1 << bitOffset);
6251 
6252  if (c89atomic_compare_and_swap_32(&pAllocator->groups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
6253  ma_uint32 slotIndex;
6254 
6255  /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */
6256  c89atomic_fetch_add_32(&pAllocator->count, 1);
6257 
6258  /* The slot index is required for constructing the output value. */
6259  slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */
6260 
6261  /* Increment the reference count before constructing the output value. */
6262  pAllocator->slots[slotIndex] += 1;
6263 
6264  /* Construct the output value. */
6265  *pSlot = ((ma_uint64)pAllocator->slots[slotIndex] << 32 | slotIndex);
6266 
6267  return MA_SUCCESS;
6268  }
6269  }
6270  }
6271 
6272  /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */
6273  if (pAllocator->count < capacity) {
6274  ma_yield();
6275  } else {
6276  return MA_OUT_OF_MEMORY;
6277  }
6278  }
6279 
6280  /* We couldn't find a slot within the maximum number of attempts. */
6281  return MA_OUT_OF_MEMORY;
6282 }
6283 
6285 {
6286  ma_uint32 iGroup;
6287  ma_uint32 iBit;
6288 
6289  if (pAllocator == NULL) {
6290  return MA_INVALID_ARGS;
6291  }
6292 
6293  iGroup = (slot & 0xFFFFFFFF) >> 5; /* slot / 32 */
6294  iBit = (slot & 0xFFFFFFFF) & 31; /* slot % 32 */
6295 
6296  if (iGroup >= ma_countof(pAllocator->groups)) {
6297  return MA_INVALID_ARGS;
6298  }
6299 
6300  MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */
6301 
6302  while (pAllocator->count > 0) {
6303  /* CAS */
6304  ma_uint32 oldBitfield;
6305  ma_uint32 newBitfield;
6306 
6307  oldBitfield = c89atomic_load_32(&pAllocator->groups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
6308  newBitfield = oldBitfield & ~(1 << iBit);
6309 
6310  if (c89atomic_compare_and_swap_32(&pAllocator->groups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
6311  c89atomic_fetch_sub_32(&pAllocator->count, 1);
6312  return MA_SUCCESS;
6313  }
6314  }
6315 
6316  /* Getting here means there are no allocations available for freeing. */
6317  return MA_INVALID_OPERATION;
6318 }
6319 
6320 
6321 
6322 #define MA_FENCE_COUNTER_MAX 0x7FFFFFFF
6323 
6325 {
6326  ma_result result;
6327 
6328  if (pFence == NULL) {
6329  return MA_INVALID_ARGS;
6330  }
6331 
6332  MA_ZERO_OBJECT(pFence);
6333  pFence->counter = 0;
6334 
6335  result = ma_event_init(&pFence->e);
6336  if (result != MA_SUCCESS) {
6337  return result;
6338  }
6339 
6340  return MA_SUCCESS;
6341 }
6342 
6343 MA_API void ma_fence_uninit(ma_fence* pFence)
6344 {
6345  if (pFence == NULL) {
6346  return;
6347  }
6348 
6349  ma_event_uninit(&pFence->e);
6350 
6351  MA_ZERO_OBJECT(pFence);
6352 }
6353 
6355 {
6356  if (pFence == NULL) {
6357  return MA_INVALID_ARGS;
6358  }
6359 
6360  for (;;) {
6361  ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
6362  ma_uint32 newCounter = oldCounter + 1;
6363 
6364  /* Make sure we're not about to exceed our maximum value. */
6365  if (newCounter > MA_FENCE_COUNTER_MAX) {
6367  return MA_OUT_OF_RANGE;
6368  }
6369 
6370  if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
6371  return MA_SUCCESS;
6372  } else {
6373  if (oldCounter == MA_FENCE_COUNTER_MAX) {
6375  return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */
6376  }
6377  }
6378  }
6379 
6380  /* Should never get here. */
6381  /*return MA_SUCCESS;*/
6382 }
6383 
6385 {
6386  if (pFence == NULL) {
6387  return MA_INVALID_ARGS;
6388  }
6389 
6390  for (;;) {
6391  ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
6392  ma_uint32 newCounter = oldCounter - 1;
6393 
6394  if (oldCounter == 0) {
6396  return MA_INVALID_OPERATION; /* Acquire/release mismatch. */
6397  }
6398 
6399  if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
6400  if (newCounter == 0) {
6401  ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */
6402  }
6403 
6404  return MA_SUCCESS;
6405  } else {
6406  if (oldCounter == 0) {
6408  return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */
6409  }
6410  }
6411  }
6412 
6413  /* Should never get here. */
6414  /*return MA_SUCCESS;*/
6415 }
6416 
6418 {
6419  if (pFence == NULL) {
6420  return MA_INVALID_ARGS;
6421  }
6422 
6423  for (;;) {
6424  ma_result result;
6425  ma_uint32 counter;
6426 
6427  counter = c89atomic_load_32(&pFence->counter);
6428  if (counter == 0) {
6429  /*
6430  Counter has hit zero. By the time we get here some other thread may have acquired the
6431  fence again, but that is where the caller needs to take care with how they se the fence.
6432  */
6433  return MA_SUCCESS;
6434  }
6435 
6436  /* Getting here means the counter is > 0. We'll need to wait for something to happen. */
6437  result = ma_event_wait(&pFence->e);
6438  if (result != MA_SUCCESS) {
6439  return result;
6440  }
6441  }
6442 
6443  /* Should never get here. */
6444  /*return MA_INVALID_OPERATION;*/
6445 }
6446 
6447 
6448 
6450 {
6451  ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;
6452 
6453  if (pNotification == NULL) {
6454  return MA_INVALID_ARGS;
6455  }
6456 
6457  if (pNotificationCallbacks->onSignal == NULL) {
6458  return MA_NOT_IMPLEMENTED;
6459  }
6460 
6461  pNotificationCallbacks->onSignal(pNotification);
6462  return MA_INVALID_ARGS;
6463 }
6464 
6465 
6466 static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification)
6467 {
6468  ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE;
6469 }
6470 
6472 {
6473  if (pNotificationPoll == NULL) {
6474  return MA_INVALID_ARGS;
6475  }
6476 
6477  pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal;
6478  pNotificationPoll->signalled = MA_FALSE;
6479 
6480  return MA_SUCCESS;
6481 }
6482 
6484 {
6485  if (pNotificationPoll == NULL) {
6486  return MA_FALSE;
6487  }
6488 
6489  return pNotificationPoll->signalled;
6490 }
6491 
6492 
6493 static void ma_async_notification_event__on_signal(ma_async_notification* pNotification)
6494 {
6496 }
6497 
6499 {
6500  ma_result result;
6501 
6502  if (pNotificationEvent == NULL) {
6503  return MA_INVALID_ARGS;
6504  }
6505 
6506  pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal;
6507 
6508  result = ma_event_init(&pNotificationEvent->e);
6509  if (result != MA_SUCCESS) {
6510  return result;
6511  }
6512 
6513  return MA_SUCCESS;
6514 }
6515 
6517 {
6518  if (pNotificationEvent == NULL) {
6519  return MA_INVALID_ARGS;
6520  }
6521 
6522  ma_event_uninit(&pNotificationEvent->e);
6523  return MA_SUCCESS;
6524 }
6525 
6527 {
6528  if (pNotificationEvent == NULL) {
6529  return MA_INVALID_ARGS;
6530  }
6531 
6532  return ma_event_wait(&pNotificationEvent->e);
6533 }
6534 
6536 {
6537  if (pNotificationEvent == NULL) {
6538  return MA_INVALID_ARGS;
6539  }
6540 
6541  return ma_event_signal(&pNotificationEvent->e);
6542 }
6543 
6544 
6545 
6547 {
6548  ma_pipeline_notifications notifications;
6549 
6550  MA_ZERO_OBJECT(&notifications);
6551 
6552  return notifications;
6553 }
6554 
6555 static void ma_pipeline_notifications_signal_all_notifications(const ma_pipeline_notifications* pPipelineNotifications)
6556 {
6557  if (pPipelineNotifications == NULL) {
6558  return;
6559  }
6560 
6561  if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }
6562  if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }
6563 }
6564 
6565 static void ma_pipeline_notifications_acquire_all_fences(const ma_pipeline_notifications* pPipelineNotifications)
6566 {
6567  if (pPipelineNotifications == NULL) {
6568  return;
6569  }
6570 
6571  if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }
6572  if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }
6573 }
6574 
6575 static void ma_pipeline_notifications_release_all_fences(const ma_pipeline_notifications* pPipelineNotifications)
6576 {
6577  if (pPipelineNotifications == NULL) {
6578  return;
6579  }
6580 
6581  if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }
6582  if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }
6583 }
6584 
6585 
6586 
6587 #define MA_JOB_ID_NONE ~((ma_uint64)0)
6588 #define MA_JOB_SLOT_NONE (ma_uint16)(~0)
6589 
6590 static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc)
6591 {
6592  return (ma_uint32)(toc >> 32);
6593 }
6594 
6595 static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc)
6596 {
6597  return (ma_uint16)(toc & 0x0000FFFF);
6598 }
6599 
6600 static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc)
6601 {
6602  return (ma_uint16)((toc & 0xFFFF0000) >> 16);
6603 }
6604 
6605 static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc)
6606 {
6607  return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc);
6608 }
6609 
6610 
6612 {
6613  ma_job job;
6614 
6615  MA_ZERO_OBJECT(&job);
6616  job.toc.breakup.code = code;
6617  job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */
6618  job.next = MA_JOB_ID_NONE;
6619 
6620  return job;
6621 }
6622 
6623 
6624 /*
6625 Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors
6626 */
6628 {
6629  if (pQueue == NULL) {
6630  return MA_INVALID_ARGS;
6631  }
6632 
6633  MA_ZERO_OBJECT(pQueue);
6634  pQueue->flags = flags;
6635 
6636  ma_slot_allocator_init(&pQueue->allocator); /* Will not fail. */
6637 
6638  /* We need a semaphore if we're running in synchronous mode. */
6639  if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
6640  ma_semaphore_init(0, &pQueue->sem);
6641  }
6642 
6643  /*
6644  Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is
6645  just a dummy item for giving us the first item in the list which is stored in the "next" member.
6646  */
6647  ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */
6648  pQueue->jobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;
6649  pQueue->tail = pQueue->head;
6650 
6651  return MA_SUCCESS;
6652 }
6653 
6655 {
6656  if (pQueue == NULL) {
6657  return MA_INVALID_ARGS;
6658  }
6659 
6660  /* All we need to do is uninitialize the semaphore. */
6661  if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
6662  ma_semaphore_uninit(&pQueue->sem);
6663  }
6664 
6665  return MA_SUCCESS;
6666 }
6667 
6669 {
6670  ma_result result;
6671  ma_uint64 slot;
6672  ma_uint64 tail;
6673  ma_uint64 next;
6674 
6675  if (pQueue == NULL || pJob == NULL) {
6676  return MA_INVALID_ARGS;
6677  }
6678 
6679  /* We need a new slot. */
6680  result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);
6681  if (result != MA_SUCCESS) {
6682  return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */
6683  }
6684 
6685  /* At this point we should have a slot to place the job. */
6686  MA_ASSERT(ma_job_extract_slot(slot) < MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY);
6687 
6688  /* We need to put the job into memory before we do anything. */
6689  pQueue->jobs[ma_job_extract_slot(slot)] = *pJob;
6690  pQueue->jobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */
6691  pQueue->jobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */
6692  pQueue->jobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */
6693 
6694  /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */
6695  for (;;) {
6696  tail = pQueue->tail;
6697  next = pQueue->jobs[ma_job_extract_slot(tail)].next;
6698 
6699  if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(pQueue->tail)) {
6700  if (ma_job_extract_slot(next) == 0xFFFF) {
6701  if (c89atomic_compare_and_swap_64(&pQueue->jobs[ma_job_extract_slot(tail)].next, next, slot) == next) {
6702  break;
6703  }
6704  } else {
6705  c89atomic_compare_and_swap_64(&pQueue->tail, tail, next);
6706  }
6707  }
6708  }
6709  c89atomic_compare_and_swap_64(&pQueue->tail, tail, slot);
6710 
6711 
6712  /* Signal the semaphore as the last step if we're using synchronous mode. */
6713  if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
6714  ma_semaphore_release(&pQueue->sem);
6715  }
6716 
6717  return MA_SUCCESS;
6718 }
6719 
6721 {
6722  ma_uint64 head;
6723  ma_uint64 tail;
6724  ma_uint64 next;
6725 
6726  if (pQueue == NULL || pJob == NULL) {
6727  return MA_INVALID_ARGS;
6728  }
6729 
6730  /* If we're running in synchronous mode we'll need to wait on a semaphore. */
6731  if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
6732  ma_semaphore_wait(&pQueue->sem);
6733  }
6734 
6735  /* Now we need to remove the root item from the list. This must be done without locking. */
6736  for (;;) {
6737  head = pQueue->head;
6738  tail = pQueue->tail;
6739  next = pQueue->jobs[ma_job_extract_slot(head)].next;
6740 
6741  if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(pQueue->head)) {
6742  if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(tail)) {
6743  if (ma_job_extract_slot(next) == 0xFFFF) {
6744  return MA_NO_DATA_AVAILABLE;
6745  }
6746  c89atomic_compare_and_swap_64(&pQueue->tail, tail, next);
6747  } else {
6748  *pJob = pQueue->jobs[ma_job_extract_slot(next)];
6749  if (c89atomic_compare_and_swap_64(&pQueue->head, head, next) == head) {
6750  break;
6751  }
6752  }
6753  }
6754  }
6755 
6756  ma_slot_allocator_free(&pQueue->allocator, head);
6757 
6758  /*
6759  If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We
6760  could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as
6761  possible.
6762  */
6763  if (pJob->toc.breakup.code == MA_JOB_QUIT) {
6764  ma_job_queue_post(pQueue, pJob);
6765  return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */
6766  }
6767 
6768  return MA_SUCCESS;
6769 }
6770 
6771 MA_API ma_result ma_job_queue_free(ma_job_queue* pQueue, ma_job* pJob)
6772 {
6773  if (pQueue == NULL || pJob == NULL) {
6774  return MA_INVALID_ARGS;
6775  }
6776 
6777  return ma_slot_allocator_free(&pQueue->allocator, ma_job_toc_to_allocation(pJob->toc.allocation));
6778 }
6779 
6780 
6781 
6782 
6783 
6784 #ifndef MA_DEFAULT_HASH_SEED
6785 #define MA_DEFAULT_HASH_SEED 42
6786 #endif
6787 
6788 /* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */
6789 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
6790  #pragma GCC diagnostic push
6791  #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
6792 #endif
6793 
6794 static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r)
6795 {
6796  return (x << r) | (x >> (32 - r));
6797 }
6798 
6799 static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i)
6800 {
6801  if (ma_is_little_endian()) {
6802  return blocks[i];
6803  } else {
6804  return ma_swap_endian_uint32(blocks[i]);
6805  }
6806 }
6807 
6808 static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h)
6809 {
6810  h ^= h >> 16;
6811  h *= 0x85ebca6b;
6812  h ^= h >> 13;
6813  h *= 0xc2b2ae35;
6814  h ^= h >> 16;
6815 
6816  return h;
6817 }
6818 
6819 static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed)
6820 {
6821  const ma_uint8* data = (const ma_uint8*)key;
6822  const ma_uint32* blocks;
6823  const ma_uint8* tail;
6824  const int nblocks = len / 4;
6825  ma_uint32 h1 = seed;
6826  ma_uint32 c1 = 0xcc9e2d51;
6827  ma_uint32 c2 = 0x1b873593;
6828  ma_uint32 k1;
6829  int i;
6830 
6831  blocks = (const ma_uint32 *)(data + nblocks*4);
6832 
6833  for(i = -nblocks; i; i++) {
6834  k1 = ma_hash_getblock(blocks,i);
6835 
6836  k1 *= c1;
6837  k1 = ma_rotl32(k1, 15);
6838  k1 *= c2;
6839 
6840  h1 ^= k1;
6841  h1 = ma_rotl32(h1, 13);
6842  h1 = h1*5 + 0xe6546b64;
6843  }
6844 
6845 
6846  tail = (const ma_uint8*)(data + nblocks*4);
6847 
6848  k1 = 0;
6849  switch(len & 3) {
6850  case 3: k1 ^= tail[2] << 16;
6851  case 2: k1 ^= tail[1] << 8;
6852  case 1: k1 ^= tail[0];
6853  k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
6854  };
6855 
6856 
6857  h1 ^= len;
6858  h1 = ma_hash_fmix32(h1);
6859 
6860  return h1;
6861 }
6862 
6863 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
6864  #pragma GCC diagnostic push
6865 #endif
6866 /* End MurmurHash3 */
6867 
6868 static ma_uint32 ma_hash_string_32(const char* str)
6869 {
6870  return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED);
6871 }
6872 
6873 static ma_uint32 ma_hash_string_w_32(const wchar_t* str)
6874 {
6875  return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED);
6876 }
6877 
6878 
6879 
6880 
6881 /*
6882 Basic BST Functions
6883 */
6884 static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode)
6885 {
6887 
6888  MA_ASSERT(pResourceManager != NULL);
6889  MA_ASSERT(ppDataBufferNode != NULL);
6890 
6891  pCurrentNode = pResourceManager->pRootDataBufferNode;
6892  while (pCurrentNode != NULL) {
6893  if (hashedName32 == pCurrentNode->hashedName32) {
6894  break; /* Found. */
6895  } else if (hashedName32 < pCurrentNode->hashedName32) {
6896  pCurrentNode = pCurrentNode->pChildLo;
6897  } else {
6898  pCurrentNode = pCurrentNode->pChildHi;
6899  }
6900  }
6901 
6902  *ppDataBufferNode = pCurrentNode;
6903 
6904  if (pCurrentNode == NULL) {
6905  return MA_DOES_NOT_EXIST;
6906  } else {
6907  return MA_SUCCESS;
6908  }
6909 }
6910 
6911 static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint)
6912 {
6913  ma_result result = MA_SUCCESS;
6915 
6916  MA_ASSERT(pResourceManager != NULL);
6917  MA_ASSERT(ppInsertPoint != NULL);
6918 
6919  *ppInsertPoint = NULL;
6920 
6921  if (pResourceManager->pRootDataBufferNode == NULL) {
6922  return MA_SUCCESS; /* No items. */
6923  }
6924 
6925  /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */
6926  pCurrentNode = pResourceManager->pRootDataBufferNode;
6927  while (pCurrentNode != NULL) {
6928  if (hashedName32 == pCurrentNode->hashedName32) {
6929  result = MA_ALREADY_EXISTS;
6930  break;
6931  } else {
6932  if (hashedName32 < pCurrentNode->hashedName32) {
6933  if (pCurrentNode->pChildLo == NULL) {
6934  result = MA_SUCCESS;
6935  break;
6936  } else {
6937  pCurrentNode = pCurrentNode->pChildLo;
6938  }
6939  } else {
6940  if (pCurrentNode->pChildHi == NULL) {
6941  result = MA_SUCCESS;
6942  break;
6943  } else {
6944  pCurrentNode = pCurrentNode->pChildHi;
6945  }
6946  }
6947  }
6948  }
6949 
6950  *ppInsertPoint = pCurrentNode;
6951  return result;
6952 }
6953 
6954 static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint)
6955 {
6956  MA_ASSERT(pResourceManager != NULL);
6957  MA_ASSERT(pDataBufferNode != NULL);
6958 
6959  /* The key must have been set before calling this function. */
6960  MA_ASSERT(pDataBufferNode->hashedName32 != 0);
6961 
6962  if (pInsertPoint == NULL) {
6963  /* It's the first node. */
6964  pResourceManager->pRootDataBufferNode = pDataBufferNode;
6965  } else {
6966  /* It's not the first node. It needs to be inserted. */
6967  if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) {
6968  MA_ASSERT(pInsertPoint->pChildLo == NULL);
6969  pInsertPoint->pChildLo = pDataBufferNode;
6970  } else {
6971  MA_ASSERT(pInsertPoint->pChildHi == NULL);
6972  pInsertPoint->pChildHi = pDataBufferNode;
6973  }
6974  }
6975 
6976  pDataBufferNode->pParent = pInsertPoint;
6977 
6978  return MA_SUCCESS;
6979 }
6980 
6981 #if 0 /* Unused for now. */
6982 static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
6983 {
6984  ma_result result;
6986 
6987  MA_ASSERT(pResourceManager != NULL);
6988  MA_ASSERT(pDataBufferNode != NULL);
6989 
6990  result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint);
6991  if (result != MA_SUCCESS) {
6992  return MA_INVALID_ARGS;
6993  }
6994 
6995  return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
6996 }
6997 #endif
6998 
6999 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode)
7000 {
7002 
7003  MA_ASSERT(pDataBufferNode != NULL);
7004 
7005  pCurrentNode = pDataBufferNode;
7006  while (pCurrentNode->pChildLo != NULL) {
7007  pCurrentNode = pCurrentNode->pChildLo;
7008  }
7009 
7010  return pCurrentNode;
7011 }
7012 
7013 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode)
7014 {
7016 
7017  MA_ASSERT(pDataBufferNode != NULL);
7018 
7019  pCurrentNode = pDataBufferNode;
7020  while (pCurrentNode->pChildHi != NULL) {
7021  pCurrentNode = pCurrentNode->pChildHi;
7022  }
7023 
7024  return pCurrentNode;
7025 }
7026 
7027 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode)
7028 {
7029  MA_ASSERT(pDataBufferNode != NULL);
7030  MA_ASSERT(pDataBufferNode->pChildHi != NULL);
7031 
7032  return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi);
7033 }
7034 
7035 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode)
7036 {
7037  MA_ASSERT(pDataBufferNode != NULL);
7038  MA_ASSERT(pDataBufferNode->pChildLo != NULL);
7039 
7040  return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo);
7041 }
7042 
7043 static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
7044 {
7045  MA_ASSERT(pResourceManager != NULL);
7046  MA_ASSERT(pDataBufferNode != NULL);
7047 
7048  if (pDataBufferNode->pChildLo == NULL) {
7049  if (pDataBufferNode->pChildHi == NULL) {
7050  /* Simple case - deleting a buffer with no children. */
7051  if (pDataBufferNode->pParent == NULL) {
7052  MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */
7053  pResourceManager->pRootDataBufferNode = NULL;
7054  } else {
7055  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
7056  pDataBufferNode->pParent->pChildLo = NULL;
7057  } else {
7058  pDataBufferNode->pParent->pChildHi = NULL;
7059  }
7060  }
7061  } else {
7062  /* Node has one child - pChildHi != NULL. */
7063  pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent;
7064 
7065  if (pDataBufferNode->pParent == NULL) {
7066  MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
7067  pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi;
7068  } else {
7069  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
7070  pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi;
7071  } else {
7072  pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi;
7073  }
7074  }
7075  }
7076  } else {
7077  if (pDataBufferNode->pChildHi == NULL) {
7078  /* Node has one child - pChildLo != NULL. */
7079  pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;
7080 
7081  if (pDataBufferNode->pParent == NULL) {
7082  MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
7083  pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;
7084  } else {
7085  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
7086  pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;
7087  } else {
7088  pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;
7089  }
7090  }
7091  } else {
7092  /* Complex case - deleting a node with two children. */
7093  ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;
7094 
7095  /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */
7096  pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);
7097  MA_ASSERT(pReplacementDataBufferNode != NULL);
7098 
7099  /*
7100  Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement
7101  node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The
7102  replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the
7103  replacement node and reinserting it into the same position as the deleted node.
7104  */
7105  MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */
7106  MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */
7107 
7108  if (pReplacementDataBufferNode->pChildHi == NULL) {
7109  if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
7110  pReplacementDataBufferNode->pParent->pChildLo = NULL;
7111  } else {
7112  pReplacementDataBufferNode->pParent->pChildHi = NULL;
7113  }
7114  } else {
7115  pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;
7116  if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
7117  pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;
7118  } else {
7119  pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;
7120  }
7121  }
7122 
7123 
7124  /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */
7125  if (pDataBufferNode->pParent != NULL) {
7126  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
7127  pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;
7128  } else {
7129  pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;
7130  }
7131  }
7132 
7133  /* Now need to update the replacement node's pointers. */
7134  pReplacementDataBufferNode->pParent = pDataBufferNode->pParent;
7135  pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;
7136  pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;
7137 
7138  /* Now the children of the replacement node need to have their parent pointers updated. */
7139  if (pReplacementDataBufferNode->pChildLo != NULL) {
7140  pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;
7141  }
7142  if (pReplacementDataBufferNode->pChildHi != NULL) {
7143  pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;
7144  }
7145 
7146  /* Now the root node needs to be updated. */
7147  if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {
7148  pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;
7149  }
7150  }
7151  }
7152 
7153  return MA_SUCCESS;
7154 }
7155 
7156 #if 0 /* Unused for now. */
7157 static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)
7158 {
7159  ma_result result;
7160  ma_resource_manager_data_buffer_node* pDataBufferNode;
7161 
7162  result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);
7163  if (result != MA_SUCCESS) {
7164  return result; /* Could not find the data buffer. */
7165  }
7166 
7167  return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);
7168 }
7169 #endif
7170 
7171 static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)
7172 {
7174 }
7175 
7176 static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType)
7177 {
7178  c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);
7179 }
7180 
7181 static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
7182 {
7183  ma_uint32 refCount;
7184 
7185  MA_ASSERT(pResourceManager != NULL);
7186  MA_ASSERT(pDataBufferNode != NULL);
7187 
7188  (void)pResourceManager;
7189 
7190  refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1;
7191 
7192  if (pNewRefCount != NULL) {
7193  *pNewRefCount = refCount;
7194  }
7195 
7196  return MA_SUCCESS;
7197 }
7198 
7199 static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
7200 {
7201  ma_uint32 refCount;
7202 
7203  MA_ASSERT(pResourceManager != NULL);
7204  MA_ASSERT(pDataBufferNode != NULL);
7205 
7206  (void)pResourceManager;
7207 
7208  refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;
7209 
7210  if (pNewRefCount != NULL) {
7211  *pNewRefCount = refCount;
7212  }
7213 
7214  return MA_SUCCESS;
7215 }
7216 
7217 static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
7218 {
7219  MA_ASSERT(pResourceManager != NULL);
7220  MA_ASSERT(pDataBufferNode != NULL);
7221 
7222  if (pDataBufferNode->isDataOwnedByResourceManager) {
7223  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {
7224  ma_free((void*)pDataBufferNode->data.encoded.pData, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_ENCODED_BUFFER*/);
7225  pDataBufferNode->data.encoded.pData = NULL;
7226  pDataBufferNode->data.encoded.sizeInBytes = 0;
7227  } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {
7228  ma_free((void*)pDataBufferNode->data.decoded.pData, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/);
7229  pDataBufferNode->data.decoded.pData = NULL;
7230  pDataBufferNode->data.decoded.totalFrameCount = 0;
7231  } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {
7232  ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.decodedPaged.data, &pResourceManager->config.allocationCallbacks);
7233  } else {
7234  /* Should never hit this if the node was successfully initialized. */
7235  MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);
7236  }
7237  }
7238 
7239  /* The data buffer itself needs to be freed. */
7240  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/);
7241 }
7242 
7243 static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)
7244 {
7245  MA_ASSERT(pDataBufferNode != NULL);
7246 
7247  return c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */
7248 }
7249 
7250 
7251 static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)
7252 {
7253  MA_ASSERT(pResourceManager != NULL);
7254 
7255  return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;
7256 }
7257 
7258 
7259 typedef struct
7260 {
7261  union
7262  {
7265  }; /* Must be the first member. */
7266  ma_resource_manager* pResourceManager;
7267 } ma_resource_manager_inline_notification;
7268 
7269 static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)
7270 {
7271  MA_ASSERT(pResourceManager != NULL);
7272  MA_ASSERT(pNotification != NULL);
7273 
7274  pNotification->pResourceManager = pResourceManager;
7275 
7276  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
7277  return ma_async_notification_event_init(&pNotification->e);
7278  } else {
7279  return ma_async_notification_poll_init(&pNotification->p);
7280  }
7281 }
7282 
7283 static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)
7284 {
7285  MA_ASSERT(pNotification != NULL);
7286 
7287  if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
7288  ma_async_notification_event_uninit(&pNotification->e);
7289  } else {
7290  /* No need to uninitialize a polling notification. */
7291  }
7292 }
7293 
7294 static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)
7295 {
7296  MA_ASSERT(pNotification != NULL);
7297 
7298  if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
7299  ma_async_notification_event_wait(&pNotification->e);
7300  } else {
7301  while (ma_async_notification_poll_is_signalled(&pNotification->p) == MA_FALSE) {
7302  ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);
7303  if (result == MA_NO_DATA_AVAILABLE || result == MA_JOB_QUIT) {
7304  break;
7305  }
7306  }
7307  }
7308 }
7309 
7310 static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)
7311 {
7312  ma_resource_manager_inline_notification_wait(pNotification);
7313  ma_resource_manager_inline_notification_uninit(pNotification);
7314 }
7315 
7316 
7317 static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)
7318 {
7319  MA_ASSERT(pResourceManager != NULL);
7320 
7321  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
7322  ma_mutex_lock(&pResourceManager->dataBufferBSTLock);
7323  } else {
7324  /* Threading not enabled. Do nothing. */
7325  }
7326 }
7327 
7328 static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)
7329 {
7330  MA_ASSERT(pResourceManager != NULL);
7331 
7332  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
7333  ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);
7334  } else {
7335  /* Threading not enabled. Do nothing. */
7336  }
7337 }
7338 
7339 static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)
7340 {
7341  ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
7342  MA_ASSERT(pResourceManager != NULL);
7343 
7344  for (;;) {
7345  ma_result result;
7346  ma_job job;
7347 
7348  result = ma_resource_manager_next_job(pResourceManager, &job);
7349  if (result != MA_SUCCESS) {
7350  break;
7351  }
7352 
7353  /* Terminate if we got a quit message. */
7354  if (job.toc.breakup.code == MA_JOB_QUIT) {
7355  break;
7356  }
7357 
7358  ma_resource_manager_process_job(pResourceManager, &job);
7359  }
7360 
7361  return (ma_thread_result)0;
7362 }
7363 
7364 
7366 {
7368 
7370  config.decodedFormat = ma_format_unknown;
7371  config.decodedChannels = 0;
7372  config.decodedSampleRate = 0;
7373  config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */
7374 
7375  return config;
7376 }
7377 
7378 
7380 {
7381  ma_result result;
7382  ma_uint32 jobQueueFlags;
7383  ma_uint32 iJobThread;
7384 
7385  if (pResourceManager == NULL) {
7386  return MA_INVALID_ARGS;
7387  }
7388 
7389  MA_ZERO_OBJECT(pResourceManager);
7390 
7391  if (pConfig == NULL) {
7392  return MA_INVALID_ARGS;
7393  }
7394 
7395  if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {
7396  return MA_INVALID_ARGS; /* Requesting too many job threads. */
7397  }
7398 
7399  pResourceManager->config = *pConfig;
7401 
7402  /* Get the log set up early so we can start using it as soon as possible. */
7403  if (pResourceManager->config.pLog == NULL) {
7404  result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);
7405  if (result == MA_SUCCESS) {
7406  pResourceManager->config.pLog = &pResourceManager->log;
7407  } else {
7408  pResourceManager->config.pLog = NULL; /* Logging is unavailable. */
7409  }
7410  }
7411 
7412  if (pResourceManager->config.pVFS == NULL) {
7413  result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);
7414  if (result != MA_SUCCESS) {
7415  return result; /* Failed to initialize the default file system. */
7416  }
7417 
7418  pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
7419  }
7420 
7421  /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */
7422  if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
7423  pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING;
7424 
7425  /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */
7426  if (pResourceManager->config.jobThreadCount > 0) {
7427  return MA_INVALID_ARGS;
7428  }
7429  }
7430 
7431  /* Job queue. */
7432  jobQueueFlags = 0;
7433  if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {
7434  if (pResourceManager->config.jobThreadCount > 0) {
7435  return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */
7436  }
7437 
7438  jobQueueFlags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;
7439  }
7440 
7441  result = ma_job_queue_init(jobQueueFlags, &pResourceManager->jobQueue);
7442  if (result != MA_SUCCESS) {
7443  return result;
7444  }
7445 
7446 
7447  /* Custom decoding backends. */
7448  if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
7449  size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
7450 
7451  pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
7452  if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
7453  ma_job_queue_uninit(&pResourceManager->jobQueue);
7454  return MA_OUT_OF_MEMORY;
7455  }
7456 
7457  MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
7458 
7459  pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount;
7461  }
7462 
7463 
7464 
7465  /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */
7466  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
7467  /* Data buffer lock. */
7468  result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);
7469  if (result != MA_SUCCESS) {
7470  ma_job_queue_uninit(&pResourceManager->jobQueue);
7471  return result;
7472  }
7473 
7474  /* Create the job threads last to ensure the threads has access to valid data. */
7475  for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
7476  result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);
7477  if (result != MA_SUCCESS) {
7478  ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
7479  ma_job_queue_uninit(&pResourceManager->jobQueue);
7480  return result;
7481  }
7482  }
7483  }
7484 
7485  return MA_SUCCESS;
7486 }
7487 
7488 
7489 static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)
7490 {
7491  MA_ASSERT(pResourceManager);
7492 
7493  /* If everything was done properly, there shouldn't be any active data buffers. */
7494  while (pResourceManager->pRootDataBufferNode != NULL) {
7495  ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
7496  ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
7497 
7498  /* The data buffer has been removed from the BST, so now we need to free it's data. */
7499  ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
7500  }
7501 }
7502 
7504 {
7505  ma_uint32 iJobThread;
7506 
7507  if (pResourceManager == NULL) {
7508  return;
7509  }
7510 
7511  /*
7512  Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the
7513  queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.
7514  */
7515  ma_resource_manager_post_job_quit(pResourceManager);
7516 
7517  /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */
7518  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
7519  for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
7520  ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);
7521  }
7522  }
7523 
7524  /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */
7525  ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);
7526 
7527  /* The job queue is no longer needed. */
7528  ma_job_queue_uninit(&pResourceManager->jobQueue);
7529 
7530  /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */
7531  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
7532  ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
7533  }
7534 
7535  ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);
7536 
7537  if (pResourceManager->config.pLog == &pResourceManager->log) {
7538  ma_log_uninit(&pResourceManager->log);
7539  }
7540 }
7541 
7543 {
7544  if (pResourceManager == NULL) {
7545  return NULL;
7546  }
7547 
7548  return pResourceManager->config.pLog;
7549 }
7550 
7551 
7552 static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)
7553 {
7555 
7556  config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
7557  config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
7558  config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables;
7559  config.customBackendCount = pResourceManager->config.customDecodingBackendCount;
7560  config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData;
7561 
7562  return config;
7563 }
7564 
7565 static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)
7566 {
7567  ma_result result;
7569 
7570  MA_ASSERT(pResourceManager != NULL);
7571  MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
7572  MA_ASSERT(pDecoder != NULL);
7573 
7574  config = ma_resource_manager__init_decoder_config(pResourceManager);
7575 
7576  if (pFilePath != NULL) {
7577  result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder);
7578  if (result != MA_SUCCESS) {
7579  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result));
7580  return result;
7581  }
7582  } else {
7583  result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder);
7584  if (result != MA_SUCCESS) {
7585  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result));
7586  return result;
7587  }
7588  }
7589 
7590  return MA_SUCCESS;
7591 }
7592 
7593 static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, ma_async_notification* pInitNotification, ma_fence* pInitFence)
7594 {
7595  ma_result result;
7596 
7597  MA_ASSERT(pDataBuffer != NULL);
7598  MA_ASSERT(pDataBuffer->isConnectorInitialized == MA_FALSE);
7599 
7600  /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */
7601  result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
7602  if (result != MA_SUCCESS && result != MA_BUSY) {
7603  return result; /* The data buffer is in an erroneous state. */
7604  }
7605 
7606  /*
7607  We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the
7608  "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use
7609  an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.
7610  */
7611  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
7612  {
7613  case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
7614  {
7616  config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager);
7617  result = ma_decoder_init_memory(pDataBuffer->pNode->data.encoded.pData, pDataBuffer->pNode->data.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder);
7618  } break;
7619 
7620  case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
7621  {
7623  config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.decoded.format, pDataBuffer->pNode->data.decoded.channels, pDataBuffer->pNode->data.decoded.totalFrameCount, pDataBuffer->pNode->data.decoded.pData, NULL);
7624  result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
7625  } break;
7626 
7627  case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
7628  {
7631  result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);
7632  } break;
7633 
7635  default:
7636  {
7637  /* Unknown data supply type. Should never happen. Need to post an error here. */
7638  return MA_INVALID_ARGS;
7639  };
7640  }
7641 
7642  /*
7643  Initialization of the connector is when we can fire the init notification. This will give the application access to
7644  the format/channels/rate of the data source.
7645  */
7646  if (result == MA_SUCCESS) {
7647  pDataBuffer->isConnectorInitialized = MA_TRUE;
7648 
7649  if (pInitNotification != NULL) {
7650  ma_async_notification_signal(pInitNotification);
7651  }
7652 
7653  if (pInitFence != NULL) {
7654  ma_fence_release(pInitFence);
7655  }
7656  }
7657 
7658  /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */
7659  return result;
7660 }
7661 
7662 static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer)
7663 {
7664  MA_ASSERT(pResourceManager != NULL);
7665  MA_ASSERT(pDataBuffer != NULL);
7666 
7667  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
7668  {
7669  case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
7670  {
7671  ma_decoder_uninit(&pDataBuffer->connector.decoder);
7672  } break;
7673 
7674  case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
7675  {
7676  ma_audio_buffer_uninit(&pDataBuffer->connector.buffer);
7677  } break;
7678 
7679  case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
7680  {
7682  } break;
7683 
7685  default:
7686  {
7687  /* Unknown data supply type. Should never happen. Need to post an error here. */
7688  return MA_INVALID_ARGS;
7689  };
7690  }
7691 
7692  return MA_SUCCESS;
7693 }
7694 
7695 static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode)
7696 {
7697  MA_ASSERT(pDataBufferNode != NULL);
7698  return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1);
7699 }
7700 
7701 static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer)
7702 {
7703  switch (pDataBuffer->pNode->data.type)
7704  {
7708 
7710  default:
7711  {
7712  ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n");
7713  return NULL;
7714  };
7715  };
7716 }
7717 
7718 static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW)
7719 {
7720  ma_result result;
7721  size_t dataSizeInBytes;
7722  void* pData;
7723 
7724  MA_ASSERT(pResourceManager != NULL);
7725  MA_ASSERT(pDataBufferNode != NULL);
7726  MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
7727 
7728  result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks, MA_ALLOCATION_TYPE_ENCODED_BUFFER);
7729  if (result != MA_SUCCESS) {
7730  if (pFilePath != NULL) {
7731  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result));
7732  } else {
7733  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result));
7734  }
7735 
7736  return result;
7737  }
7738 
7739  pDataBufferNode->data.encoded.pData = pData;
7740  pDataBufferNode->data.encoded.sizeInBytes = dataSizeInBytes;
7741  ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */
7742 
7743  return MA_SUCCESS;
7744 }
7745 
7746 static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder** ppDecoder)
7747 {
7748  ma_result result = MA_SUCCESS;
7749  ma_decoder* pDecoder;
7750  ma_uint64 totalFrameCount;
7751 
7752  MA_ASSERT(pResourceManager != NULL);
7753  MA_ASSERT(pDataBufferNode != NULL);
7754  MA_ASSERT(ppDecoder != NULL);
7755  MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
7756 
7757  *ppDecoder = NULL; /* For safety. */
7758 
7759  pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
7760  if (pDecoder == NULL) {
7761  return MA_OUT_OF_MEMORY;
7762  }
7763 
7764  result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder);
7765  if (result != MA_SUCCESS) {
7766  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
7767  return result;
7768  }
7769 
7770  /*
7771  At this point we have the decoder and we now need to initialize the data supply. This will
7772  be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap
7773  allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter
7774  is used when the length of a sound is unknown until a full decode has been performed.
7775  */
7776  totalFrameCount = ma_decoder_get_length_in_pcm_frames(pDecoder);
7777  if (totalFrameCount > 0) {
7778  /* It's a known length. The data supply is a regular decoded buffer. */
7779  ma_uint64 dataSizeInBytes;
7780  void* pData;
7781 
7782  dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
7783  if (dataSizeInBytes > MA_SIZE_MAX) {
7784  ma_decoder_uninit(pDecoder);
7785  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
7786  return MA_TOO_BIG;
7787  }
7788 
7789  pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/);
7790  if (pData == NULL) {
7791  ma_decoder_uninit(pDecoder);
7792  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
7793  return MA_OUT_OF_MEMORY;
7794  }
7795 
7796  /* The buffer needs to be initialized to silence in case the caller reads from it. */
7797  ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels);
7798 
7799  /* Data has been allocated and the data supply can now be initialized. */
7800  pDataBufferNode->data.decoded.pData = pData;
7801  pDataBufferNode->data.decoded.totalFrameCount = totalFrameCount;
7802  pDataBufferNode->data.decoded.format = pDecoder->outputFormat;
7803  pDataBufferNode->data.decoded.channels = pDecoder->outputChannels;
7804  pDataBufferNode->data.decoded.sampleRate = pDecoder->outputSampleRate;
7805  pDataBufferNode->data.decoded.decodedFrameCount = 0;
7806  ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */
7807  } else {
7808  /*
7809  It's an unknown length. The data supply is a paged decoded buffer. Setting this up is
7810  actually easier than the non-paged decoded buffer because we just need to initialize
7811  a ma_paged_audio_buffer object.
7812  */
7813  result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.decodedPaged.data);
7814  if (result != MA_SUCCESS) {
7815  ma_decoder_uninit(pDecoder);
7816  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
7817  return result;
7818  }
7819 
7820  pDataBufferNode->data.decodedPaged.sampleRate = pDecoder->outputSampleRate;
7821  pDataBufferNode->data.decodedPaged.decodedFrameCount = 0;
7822  ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */
7823  }
7824 
7825  *ppDecoder = pDecoder;
7826 
7827  return MA_SUCCESS;
7828 }
7829 
7830 static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder)
7831 {
7832  ma_result result = MA_SUCCESS;
7833  ma_uint64 pageSizeInFrames;
7834  ma_uint64 framesToTryReading;
7835  ma_uint64 framesRead;
7836 
7837  MA_ASSERT(pResourceManager != NULL);
7838  MA_ASSERT(pDataBufferNode != NULL);
7839  MA_ASSERT(pDecoder != NULL);
7840 
7841  /* We need to know the size of a page in frames to know how many frames to decode. */
7842  pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000);
7843  framesToTryReading = pageSizeInFrames;
7844 
7845  /*
7846  Here is where we do the decoding of the next page. We'll run a slightly different path depending
7847  on whether or not we're using a flat or paged buffer because the allocation of the page differs
7848  between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged
7849  buffer, we need to allocate a new page and attach it to the linked list.
7850  */
7851  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode))
7852  {
7854  {
7855  /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */
7856  void* pDst;
7857  ma_uint64 framesRemaining = pDataBufferNode->data.decoded.totalFrameCount - pDataBufferNode->data.decoded.decodedFrameCount;
7858  if (framesToTryReading > framesRemaining) {
7859  framesToTryReading = framesRemaining;
7860  }
7861 
7862  pDst = ma_offset_ptr(
7863  pDataBufferNode->data.decoded.pData,
7864  pDataBufferNode->data.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.decoded.format, pDataBufferNode->data.decoded.channels)
7865  );
7866  MA_ASSERT(pDst != NULL);
7867 
7868  framesRead = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading);
7869  if (framesRead > 0) {
7870  pDataBufferNode->data.decoded.decodedFrameCount += framesRead;
7871  }
7872  } break;
7873 
7875  {
7876  /* The destination buffer is a freshly allocated page. */
7878 
7879  result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage);
7880  if (result != MA_SUCCESS) {
7881  return result;
7882  }
7883 
7884  framesRead = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading);
7885  if (framesRead > 0) {
7886  pPage->sizeInFrames = framesRead;
7887 
7888  result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.decodedPaged.data, pPage);
7889  if (result == MA_SUCCESS) {
7890  pDataBufferNode->data.decodedPaged.decodedFrameCount += framesRead;
7891  } else {
7892  /* Failed to append the page. Just abort and set the status to MA_AT_END. */
7893  ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
7894  result = MA_AT_END;
7895  }
7896  } else {
7897  /* No frames were read. Free the page and just set the status to MA_AT_END. */
7898  ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
7899  result = MA_AT_END;
7900  }
7901  } break;
7902 
7905  default:
7906  {
7907  /* Unexpected data supply type. */
7908  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode));
7909  return MA_ERROR;
7910  };
7911  }
7912 
7913  if (result == MA_SUCCESS && framesRead == 0) {
7914  result = MA_AT_END;
7915  }
7916 
7917  return result;
7918 }
7919 
7920 static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode)
7921 {
7922  ma_result result = MA_SUCCESS;
7923  ma_bool32 nodeAlreadyExists = MA_FALSE;
7924  ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
7925  ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
7926 
7927  if (ppDataBufferNode != NULL) {
7928  *ppDataBufferNode = NULL; /* Safety. */
7929  }
7930 
7931  if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {
7932  return MA_INVALID_ARGS;
7933  }
7934 
7935  /* If we're specifying existing data, it must be valid. */
7936  if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {
7937  return MA_INVALID_ARGS;
7938  }
7939 
7940  /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */
7941  if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
7942  flags &= ~MA_DATA_SOURCE_FLAG_ASYNC;
7943  }
7944 
7945  if (hashedName32 == 0) {
7946  if (pFilePath != NULL) {
7947  hashedName32 = ma_hash_string_32(pFilePath);
7948  } else {
7949  hashedName32 = ma_hash_string_w_32(pFilePathW);
7950  }
7951  }
7952 
7953  /*
7954  Here is where we either increment the node's reference count or allocate a new one and add it
7955  to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is
7956  posted inside the critical section just in case the caller immediately uninitializes the node
7957  as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the
7958  node is not uninitialized before initialization.
7959  */
7960  ma_resource_manager_data_buffer_bst_lock(pResourceManager);
7961  {
7963 
7964  result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);
7965  if (result == MA_ALREADY_EXISTS) {
7966  /* The node already exists. We just need to increment the reference count. */
7967  pDataBufferNode = pInsertPoint;
7968 
7969  result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);
7970  if (result != MA_SUCCESS) {
7971  goto early_exit; /* Should never happen. Failed to increment the reference count. */
7972  }
7973 
7974  nodeAlreadyExists = MA_TRUE; /* This is used later on, outside of this critical section, to determine whether or not a synchronous load should happen now. */
7975  } else {
7976  /*
7977  The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This
7978  needs to be done inside the critical section to ensure an uninitialization of the node
7979  does not occur before initialization on another thread.
7980  */
7981  pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);
7982  if (pDataBufferNode == NULL) {
7983  result = MA_OUT_OF_MEMORY;
7984  goto early_exit;
7985  }
7986 
7987  MA_ZERO_OBJECT(pDataBufferNode);
7988  pDataBufferNode->hashedName32 = hashedName32;
7989  pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */
7990 
7991  if (pExistingData == NULL) {
7992  pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */
7993  pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */
7994  pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;
7995  } else {
7996  pDataBufferNode->data = *pExistingData;
7997  pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */
7998  pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;
7999  }
8000 
8001  result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
8002  if (result != MA_SUCCESS) {
8003  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/);
8004  goto early_exit; /* Should never happen. Failed to insert the data buffer into the BST. */
8005  }
8006 
8007  /*
8008  Here is where we'll post the job, but only if we're loading asynchronously. If we're
8009  loading synchronously we'll defer loading to a later stage, outside of the critical
8010  section.
8011  */
8012  if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0) {
8013  /* Loading asynchronously. Post the job. */
8014  ma_job job;
8015  char* pFilePathCopy = NULL;
8016  wchar_t* pFilePathWCopy = NULL;
8017 
8018  /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */
8019  if (pFilePath != NULL) {
8020  pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
8021  } else {
8022  pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
8023  }
8024 
8025  if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
8026  result = MA_OUT_OF_MEMORY;
8027  goto done;
8028  }
8029 
8030  if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
8031  ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
8032  }
8033 
8034  /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */
8035  if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
8036  if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
8037 
8038  /* We now have everything we need to post the job to the job thread. */
8040  job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
8041  job.loadDataBufferNode.pDataBufferNode = pDataBufferNode;
8042  job.loadDataBufferNode.pFilePath = pFilePathCopy;
8043  job.loadDataBufferNode.pFilePathW = pFilePathWCopy;
8044  job.loadDataBufferNode.decode = (flags & MA_DATA_SOURCE_FLAG_DECODE ) != 0;
8045  job.loadDataBufferNode.pInitNotification = ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : NULL;
8046  job.loadDataBufferNode.pDoneNotification = NULL;
8047  job.loadDataBufferNode.pInitFence = pInitFence;
8048  job.loadDataBufferNode.pDoneFence = pDoneFence;
8049 
8050  result = ma_resource_manager_post_job(pResourceManager, &job);
8051  if (result != MA_SUCCESS) {
8052  /* Failed to post job. Probably ran out of memory. */
8053  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result));
8054 
8055  /*
8056  Fences were acquired before posting the job, but since the job was not able to
8057  be posted, we need to make sure we release them so nothing gets stuck waiting.
8058  */
8059  if (pInitFence != NULL) { ma_fence_release(pInitFence); }
8060  if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
8061 
8062  ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
8063  ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
8064  goto done;
8065  }
8066  }
8067  }
8068  }
8069  ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
8070 
8071 early_exit:
8072  if (result != MA_SUCCESS) {
8073  return result;
8074  }
8075 
8076  /*
8077  If we're loading synchronously, we'll need to load everything now. When loading asynchronously,
8078  a job will have been posted inside the BST critical section so that an uninitialization can be
8079  allocated an appropriate execution order thereby preventing it from being uninitialized before
8080  the node is initialized by the decoding thread(s).
8081  */
8082  if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */
8083  if (pFilePath == NULL && pFilePathW == NULL) {
8084  /*
8085  If this path is hit, it means a buffer is being copied (i.e. initialized from only the
8086  hashed name), but that node has been freed in the meantime, probably from some other
8087  thread. This is an invalid operation.
8088  */
8089  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n");
8090  result = MA_INVALID_OPERATION;
8091  goto done;
8092  }
8093 
8094  if (pDataBufferNode->isDataOwnedByResourceManager) {
8095  if ((flags & MA_DATA_SOURCE_FLAG_ASYNC) == 0) {
8096  /* Loading synchronously. Load the sound in it's entirety here. */
8097  if ((flags & MA_DATA_SOURCE_FLAG_DECODE) == 0) {
8098  /* No decoding. This is the simple case - just store the file contents in memory. */
8099  result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);
8100  if (result != MA_SUCCESS) {
8101  goto done;
8102  }
8103  } else {
8104  /* Decoding. We do this the same way as we do when loading asynchronously. */
8105  ma_decoder* pDecoder;
8106  result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, &pDecoder);
8107  if (result != MA_SUCCESS) {
8108  goto done;
8109  }
8110 
8111  /* We have the decoder, now decode page by page just like we do when loading asynchronously. */
8112  for (;;) {
8113  /* Decode next page. */
8114  result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);
8115  if (result != MA_SUCCESS) {
8116  break; /* Will return MA_AT_END when the last page has been decoded. */
8117  }
8118  }
8119 
8120  /* Reaching the end needs to be considered successful. */
8121  if (result == MA_AT_END) {
8122  result = MA_SUCCESS;
8123  }
8124 
8125  /*
8126  At this point the data buffer is either fully decoded or some error occurred. Either
8127  way, the decoder is no longer necessary.
8128  */
8129  ma_decoder_uninit(pDecoder);
8130  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
8131  }
8132 
8133  /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */
8134  c89atomic_exchange_i32(&pDataBufferNode->result, result);
8135  } else {
8136  /* Loading asynchronously. We may need to wait for initialization. */
8137  if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
8138  ma_resource_manager_inline_notification_wait(&initNotification);
8139  }
8140  }
8141  } else {
8142  /* The data is not managed by the resource manager so there's nothing else to do. */
8143  MA_ASSERT(pExistingData != NULL);
8144  }
8145  }
8146 
8147 done:
8148  /* If we failed to initialize the data buffer we need to free it. */
8149  if (result != MA_SUCCESS) {
8150  if (nodeAlreadyExists == MA_FALSE) {
8151  ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
8152  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/);
8153  }
8154  }
8155 
8156  /*
8157  The init notification needs to be uninitialized. This will be used if the node does not already
8158  exist, and we've specified ASYNC | WAIT_INIT.
8159  */
8160  if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0) {
8161  if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
8162  ma_resource_manager_inline_notification_uninit(&initNotification);
8163  }
8164  }
8165 
8166  if (ppDataBufferNode != NULL) {
8167  *ppDataBufferNode = pDataBufferNode;
8168  }
8169 
8170  return result;
8171 }
8172 
8173 static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW)
8174 {
8175  ma_result result = MA_SUCCESS;
8176  ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */
8177  ma_uint32 hashedName32 = 0;
8178 
8179  if (pResourceManager == NULL) {
8180  return MA_INVALID_ARGS;
8181  }
8182 
8183  if (pDataBufferNode == NULL) {
8184  if (pName == NULL && pNameW == NULL) {
8185  return MA_INVALID_ARGS;
8186  }
8187 
8188  if (pName != NULL) {
8189  hashedName32 = ma_hash_string_32(pName);
8190  } else {
8191  hashedName32 = ma_hash_string_w_32(pNameW);
8192  }
8193  }
8194 
8195  /*
8196  The first thing to do is decrement the reference counter of the node. Then, if the reference
8197  count is zero, we need to free the node. If the node is still in the process of loading, we'll
8198  need to post a job to the job queue to free the node. Otherwise we'll just do it here.
8199  */
8200  ma_resource_manager_data_buffer_bst_lock(pResourceManager);
8201  {
8202  /* Might need to find the node. Must be done inside the critical section. */
8203  if (pDataBufferNode == NULL) {
8204  result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);
8205  if (result != MA_SUCCESS) {
8206  goto stage2; /* Couldn't find the node. */
8207  }
8208  }
8209 
8210  result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);
8211  if (result != MA_SUCCESS) {
8212  goto stage2; /* Should never happen. */
8213  }
8214 
8215  if (refCount == 0) {
8216  result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
8217  if (result != MA_SUCCESS) {
8218  goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */
8219  }
8220  }
8221  }
8222  ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
8223 
8224 stage2:
8225  if (result != MA_SUCCESS) {
8226  return result;
8227  }
8228 
8229  /*
8230  Here is where we need to free the node. We don't want to do this inside the critical section
8231  above because we want to keep that as small as possible for multi-threaded efficiency.
8232  */
8233  if (refCount == 0) {
8234  if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
8235  /* The sound is still loading. We need to delay the freeing of the node to a safe time. */
8236  ma_job job;
8237 
8238  /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */
8239  c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);
8240 
8242  job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
8243  job.freeDataBufferNode.pDataBufferNode = pDataBufferNode;
8244 
8245  result = ma_resource_manager_post_job(pResourceManager, &job);
8246  if (result != MA_SUCCESS) {
8247  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result));
8248  return result;
8249  }
8250 
8251  /* If we don't support threading, process the job queue here. */
8252  if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
8253  while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
8254  result = ma_resource_manager_process_next_job(pResourceManager);
8255  if (result == MA_NO_DATA_AVAILABLE || result == MA_JOB_QUIT) {
8256  result = MA_SUCCESS;
8257  break;
8258  }
8259  }
8260  } else {
8261  /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */
8262  }
8263  } else {
8264  /* The sound isn't loading so we can just free the node here. */
8265  ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
8266  }
8267  }
8268 
8269  return result;
8270 }
8271 
8272 
8273 
8274 static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)
8275 {
8276  MA_ASSERT(pDataBuffer != NULL);
8277  return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);
8278 }
8279 
8280 static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
8281 {
8282  return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
8283 }
8284 
8285 static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
8286 {
8288 }
8289 
8290 static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
8291 {
8292  return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate);
8293 }
8294 
8295 static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
8296 {
8298 }
8299 
8300 static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
8301 {
8303 }
8304 
8305 static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
8306 {
8307  ma_resource_manager_data_buffer_cb__read_pcm_frames,
8308  ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,
8309  NULL, /* onMap */
8310  NULL, /* onUnmap */
8311  ma_resource_manager_data_buffer_cb__get_data_format,
8312  ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,
8313  ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames
8314 };
8315 
8316 static ma_result ma_resource_manager_data_buffer_init_internal(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
8317 {
8318  ma_result result = MA_SUCCESS;
8319  ma_resource_manager_data_buffer_node* pDataBufferNode;
8320  ma_data_source_config dataSourceConfig;
8321  ma_bool32 async;
8322  ma_pipeline_notifications notifications;
8323 
8324  if (pNotifications != NULL) {
8325  notifications = *pNotifications;
8326  pNotifications = NULL; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */
8327  } else {
8328  MA_ZERO_OBJECT(&notifications);
8329  }
8330 
8331  if (pDataBuffer == NULL) {
8332  ma_pipeline_notifications_signal_all_notifications(&notifications);
8333  return MA_INVALID_ARGS;
8334  }
8335 
8336  MA_ZERO_OBJECT(pDataBuffer);
8337 
8338  /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */
8339  if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
8340  flags &= ~MA_DATA_SOURCE_FLAG_ASYNC;
8341  }
8342 
8343  async = (flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0;
8344 
8345  /*
8346  Fences need to be acquired before doing anything. These must be aquired and released outside of
8347  the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
8348  data buffer has completed initialization.
8349 
8350  When loading asynchronously, the node acquisition routine below will acquire the fences on this
8351  thread and then release them on the async thread when the operation is complete.
8352 
8353  These fences are always released at the "done" tag at the end of this function. They'll be
8354  acquired a second if loading asynchronously. This double acquisition system is just done to
8355  simplify code maintanence.
8356  */
8357  ma_pipeline_notifications_acquire_all_fences(&notifications);
8358  {
8359  /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */
8360  result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
8361  if (result != MA_SUCCESS) {
8362  ma_pipeline_notifications_signal_all_notifications(&notifications);
8363  goto done;
8364  }
8365 
8366  dataSourceConfig = ma_data_source_config_init();
8367  dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
8368 
8369  result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
8370  if (result != MA_SUCCESS) {
8371  ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
8372  ma_pipeline_notifications_signal_all_notifications(&notifications);
8373  goto done;
8374  }
8375 
8376  pDataBuffer->pResourceManager = pResourceManager;
8377  pDataBuffer->pNode = pDataBufferNode;
8378  pDataBuffer->flags = flags;
8379  pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
8380 
8381  /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
8382  if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
8383  /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
8384  result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, NULL, NULL);
8385  c89atomic_exchange_i32(&pDataBuffer->result, result);
8386 
8387  ma_pipeline_notifications_signal_all_notifications(&notifications);
8388  goto done;
8389  } else {
8390  /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */
8391  ma_job job;
8392  ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
8393 
8394  if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
8395  ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
8396  }
8397 
8398  /*
8399  The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
8400  worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other
8401  than MA_BUSY, it'll assume an error and fall through to an early exit.
8402  */
8403  c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
8404 
8405  /* Acquire fences a second time. These will be released by the async thread. */
8406  ma_pipeline_notifications_acquire_all_fences(&notifications);
8407 
8409  job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
8410  job.loadDataBuffer.pDataBuffer = pDataBuffer;
8411  job.loadDataBuffer.pInitNotification = ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
8412  job.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
8413  job.loadDataBuffer.pInitFence = notifications.init.pFence;
8414  job.loadDataBuffer.pDoneFence = notifications.done.pFence;
8415 
8416  result = ma_resource_manager_post_job(pResourceManager, &job);
8417  if (result != MA_SUCCESS) {
8418  /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */
8419  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result));
8420  c89atomic_exchange_i32(&pDataBuffer->result, result);
8421 
8422  /* Release the fences after the result has been set on the data buffer. */
8423  ma_pipeline_notifications_release_all_fences(&notifications);
8424  } else {
8425  if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
8426  ma_resource_manager_inline_notification_wait(&initNotification);
8427 
8428  if (notifications.init.pNotification != NULL) {
8430  }
8431 
8432  /* NOTE: Do not release the init fence here. It will have been done by the job. */
8433 
8434  /* Make sure we return an error if initialization failed on the async thread. */
8435  result = ma_resource_manager_data_buffer_result(pDataBuffer);
8436  if (result == MA_BUSY) {
8437  result = MA_SUCCESS;
8438  }
8439  }
8440  }
8441 
8442  if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
8443  ma_resource_manager_inline_notification_uninit(&initNotification);
8444  }
8445  }
8446 
8447  if (result != MA_SUCCESS) {
8448  ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
8449  goto done;
8450  }
8451  }
8452 done:
8453  ma_pipeline_notifications_release_all_fences(&notifications);
8454 
8455  return result;
8456 }
8457 
8458 MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
8459 {
8460  return ma_resource_manager_data_buffer_init_internal(pResourceManager, pFilePath, NULL, 0, flags, pNotifications, pDataBuffer);
8461 }
8462 
8463 MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
8464 {
8465  return ma_resource_manager_data_buffer_init_internal(pResourceManager, NULL, pFilePath, 0, flags, pNotifications, pDataBuffer);
8466 }
8467 
8469 {
8470  if (pExistingDataBuffer == NULL) {
8471  return MA_INVALID_ARGS;
8472  }
8473 
8474  MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */
8475 
8476  return ma_resource_manager_data_buffer_init_internal(pResourceManager, NULL, NULL, pExistingDataBuffer->pNode->hashedName32, pExistingDataBuffer->flags, NULL, pDataBuffer);
8477 }
8478 
8479 static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)
8480 {
8481  MA_ASSERT(pDataBuffer != NULL);
8482 
8483  /* The connector should be uninitialized first. */
8484  ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);
8485 
8486  /* With the connector uninitialized we can unacquire the node. */
8487  ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);
8488 
8489  /* The base data source needs to be uninitialized as well. */
8490  ma_data_source_uninit(&pDataBuffer->ds);
8491 
8492  return MA_SUCCESS;
8493 }
8494 
8496 {
8497  ma_result result;
8498 
8499  if (pDataBuffer == NULL) {
8500  return MA_INVALID_ARGS;
8501  }
8502 
8503  if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {
8504  /* The data buffer can be deleted synchronously. */
8505  return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
8506  } else {
8507  /*
8508  The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will
8509  be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event
8510  to get processed before returning.
8511  */
8512  ma_resource_manager_inline_notification notification;
8513  ma_job job;
8514 
8515  /*
8516  We need to mark the node as unavailable so we don't try reading from it anymore, but also to
8517  let the loading thread know that it needs to abort it's loading procedure.
8518  */
8520 
8521  result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, &notification);
8522  if (result != MA_SUCCESS) {
8523  return result; /* Failed to create the notification. This should rarely, if ever, happen. */
8524  }
8525 
8527  job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
8528  job.freeDataBuffer.pDataBuffer = pDataBuffer;
8529  job.freeDataBuffer.pDoneNotification = &notification;
8530  job.freeDataBuffer.pDoneFence = NULL;
8531 
8532  result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
8533  if (result != MA_SUCCESS) {
8534  ma_resource_manager_inline_notification_uninit(&notification);
8535  return result;
8536  }
8537 
8538  ma_resource_manager_inline_notification_wait_and_uninit(&notification);
8539  }
8540 
8541  return result;
8542 }
8543 
8545 {
8546  ma_result result = MA_SUCCESS;
8547  ma_uint64 framesRead = 0;
8548  ma_bool32 isLooping;
8549  ma_bool32 isDecodedBufferBusy = MA_FALSE;
8550 
8551  /* Safety. */
8552  if (pFramesRead != NULL) {
8553  *pFramesRead = 0;
8554  }
8555 
8556  /*
8557  We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after
8558  it's been uninitialized or is in the process of uninitializing.
8559  */
8560  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
8561 
8562  /* If the node is not initialized we need to abort with a busy code. */
8563  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
8564  return MA_BUSY; /* Still loading. */
8565  }
8566 
8567  if (pDataBuffer->seekToCursorOnNextRead) {
8568  pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
8569 
8570  result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
8571  if (result != MA_SUCCESS) {
8572  return result;
8573  }
8574  }
8575 
8576  result = ma_resource_manager_data_buffer_get_looping(pDataBuffer, &isLooping);
8577  if (result != MA_SUCCESS) {
8578  return result;
8579  }
8580 
8581  /*
8582  For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot
8583  exceed this amount. We'll read as much as we can, and then return MA_BUSY.
8584  */
8585  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {
8586  ma_uint64 availableFrames;
8587 
8588  isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
8589 
8590  if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
8591  /* Don't try reading more than the available frame count. */
8592  if (frameCount > availableFrames) {
8593  frameCount = availableFrames;
8594 
8595  /*
8596  If there's no frames available we want to set the status to MA_AT_END. The logic below
8597  will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
8598  is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
8599  is 0 because that'll result in a situation where it's possible MA_AT_END won't get
8600  returned.
8601  */
8602  if (frameCount == 0) {
8603  result = MA_AT_END;
8604  }
8605  } else {
8606  isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
8607  }
8608  }
8609  }
8610 
8611  /* Don't attempt to read anything if we've got no frames available. */
8612  if (frameCount > 0) {
8613  result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead, isLooping);
8614  }
8615 
8616  /*
8617  If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound
8618  as at the end and terminate decoding.
8619  */
8620  if (result == MA_AT_END) {
8621  if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
8622  result = MA_BUSY;
8623  }
8624  }
8625 
8626  if (isDecodedBufferBusy) {
8627  result = MA_BUSY;
8628  }
8629 
8630  if (pFramesRead != NULL) {
8631  *pFramesRead = framesRead;
8632  }
8633 
8634  return result;
8635 }
8636 
8638 {
8639  ma_result result;
8640 
8641  /* We cannot be using the data source after it's been uninitialized. */
8642  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
8643 
8644  /* If we haven't yet got a connector we need to abort. */
8645  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
8646  pDataBuffer->seekTargetInPCMFrames = frameIndex;
8647  pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
8648  return MA_BUSY; /* Still loading. */
8649  }
8650 
8651  result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);
8652  if (result != MA_SUCCESS) {
8653  return result;
8654  }
8655 
8656  pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */
8657  pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
8658 
8659  return MA_SUCCESS;
8660 }
8661 
8663 {
8664  /* We cannot be using the data source after it's been uninitialized. */
8665  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
8666 
8667  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
8668  {
8670  {
8671  return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate);
8672  };
8673 
8675  {
8676  *pFormat = pDataBuffer->pNode->data.decoded.format;
8677  *pChannels = pDataBuffer->pNode->data.decoded.channels;
8678  *pSampleRate = pDataBuffer->pNode->data.decoded.sampleRate;
8679  return MA_SUCCESS;
8680  };
8681 
8683  {
8684  *pFormat = pDataBuffer->pNode->data.decodedPaged.data.format;
8685  *pChannels = pDataBuffer->pNode->data.decodedPaged.data.channels;
8686  *pSampleRate = pDataBuffer->pNode->data.decodedPaged.sampleRate;
8687  return MA_SUCCESS;
8688  };
8689 
8691  {
8692  return MA_BUSY; /* Still loading. */
8693  };
8694 
8695  default:
8696  {
8697  /* Unknown supply type. Should never hit this. */
8698  return MA_INVALID_ARGS;
8699  }
8700  }
8701 }
8702 
8704 {
8705  /* We cannot be using the data source after it's been uninitialized. */
8706  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
8707 
8708  if (pDataBuffer == NULL || pCursor == NULL) {
8709  return MA_INVALID_ARGS;
8710  }
8711 
8712  *pCursor = 0;
8713 
8714  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
8715  {
8717  {
8718  return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor);
8719  };
8720 
8722  {
8723  return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor);
8724  };
8725 
8727  {
8729  };
8730 
8732  {
8733  return MA_BUSY;
8734  };
8735 
8736  default:
8737  {
8738  return MA_INVALID_ARGS;
8739  }
8740  }
8741 }
8742 
8744 {
8745  /* We cannot be using the data source after it's been uninitialized. */
8746  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
8747 
8748  if (pDataBuffer == NULL || pLength == NULL) {
8749  return MA_INVALID_ARGS;
8750  }
8751 
8752  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
8753  return MA_BUSY; /* Still loading. */
8754  }
8755 
8756  return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength);
8757 }
8758 
8760 {
8761  if (pDataBuffer == NULL) {
8762  return MA_INVALID_ARGS;
8763  }
8764 
8765  return c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */
8766 }
8767 
8769 {
8770  if (pDataBuffer == NULL) {
8771  return MA_INVALID_ARGS;
8772  }
8773 
8774  c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
8775 
8776  return MA_SUCCESS;
8777 }
8778 
8780 {
8781  if (pIsLooping == NULL) {
8782  return MA_INVALID_ARGS;
8783  }
8784 
8785  *pIsLooping = MA_FALSE;
8786 
8787  if (pDataBuffer == NULL) {
8788  return MA_INVALID_ARGS;
8789  }
8790 
8791  *pIsLooping = c89atomic_load_32((ma_bool32*)&pDataBuffer->isLooping);
8792 
8793  return MA_SUCCESS;
8794 }
8795 
8797 {
8798  if (pAvailableFrames == NULL) {
8799  return MA_INVALID_ARGS;
8800  }
8801 
8802  *pAvailableFrames = 0;
8803 
8804  if (pDataBuffer == NULL) {
8805  return MA_INVALID_ARGS;
8806  }
8807 
8808  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
8809  if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
8810  return MA_BUSY;
8811  } else {
8812  return MA_INVALID_OPERATION; /* No connector. */
8813  }
8814  }
8815 
8816  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
8817  {
8819  {
8820  return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames);
8821  };
8822 
8824  {
8825  return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames);
8826  };
8827 
8829  {
8830  ma_uint64 cursor;
8832 
8833  if (pDataBuffer->pNode->data.decodedPaged.decodedFrameCount > cursor) {
8834  *pAvailableFrames = pDataBuffer->pNode->data.decodedPaged.decodedFrameCount - cursor;
8835  } else {
8836  *pAvailableFrames = 0;
8837  }
8838 
8839  return MA_SUCCESS;
8840  };
8841 
8843  default:
8844  {
8845  /* Unknown supply type. Should never hit this. */
8846  return MA_INVALID_ARGS;
8847  }
8848  }
8849 }
8850 
8851 MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)
8852 {
8853  return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);
8854 }
8855 
8856 MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)
8857 {
8858  return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);
8859 }
8860 
8861 
8862 static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData)
8863 {
8864  return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);
8865 }
8866 
8867 static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
8868 {
8871  data.decoded.pData = pData;
8872  data.decoded.totalFrameCount = frameCount;
8873  data.decoded.format = format;
8874  data.decoded.channels = channels;
8875  data.decoded.sampleRate = sampleRate;
8876 
8877  return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
8878 }
8879 
8880 MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
8881 {
8882  return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate);
8883 }
8884 
8885 MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
8886 {
8887  return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate);
8888 }
8889 
8890 
8891 static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes)
8892 {
8895  data.encoded.pData = pData;
8896  data.encoded.sizeInBytes = sizeInBytes;
8897 
8898  return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
8899 }
8900 
8901 MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes)
8902 {
8903  return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes);
8904 }
8905 
8906 MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes)
8907 {
8908  return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes);
8909 }
8910 
8911 
8912 MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath)
8913 {
8914  return ma_resource_manager_unregister_data(pResourceManager, pFilePath);
8915 }
8916 
8917 MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath)
8918 {
8919  return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath);
8920 }
8921 
8922 MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName)
8923 {
8924  return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL);
8925 }
8926 
8927 MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName)
8928 {
8929  return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName);
8930 }
8931 
8932 
8933 static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream)
8934 {
8935  MA_ASSERT(pDataStream != NULL);
8936  return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1);
8937 }
8938 
8939 static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream)
8940 {
8941  MA_ASSERT(pDataStream != NULL);
8942  return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd);
8943 }
8944 
8945 static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream)
8946 {
8947  MA_ASSERT(pDataStream != NULL);
8948  return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter);
8949 }
8950 
8951 
8952 static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
8953 {
8954  return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead);
8955 }
8956 
8957 static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
8958 {
8960 }
8961 
8962 static ma_result ma_resource_manager_data_stream_cb__map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
8963 {
8964  return ma_resource_manager_data_stream_map((ma_resource_manager_data_stream*)pDataSource, ppFramesOut, pFrameCount);
8965 }
8966 
8967 static ma_result ma_resource_manager_data_stream_cb__unmap(ma_data_source* pDataSource, ma_uint64 frameCount)
8968 {
8970 }
8971 
8972 static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
8973 {
8974  return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate);
8975 }
8976 
8977 static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
8978 {
8980 }
8981 
8982 static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
8983 {
8985 }
8986 
8987 static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
8988 {
8989  ma_resource_manager_data_stream_cb__read_pcm_frames,
8990  ma_resource_manager_data_stream_cb__seek_to_pcm_frame,
8991  ma_resource_manager_data_stream_cb__map,
8992  ma_resource_manager_data_stream_cb__unmap,
8993  ma_resource_manager_data_stream_cb__get_data_format,
8994  ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames,
8995  ma_resource_manager_data_stream_cb__get_length_in_pcm_frames
8996 };
8997 
8998 static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
8999 {
9000  ma_result result;
9001  ma_data_source_config dataSourceConfig;
9002  char* pFilePathCopy = NULL;
9003  wchar_t* pFilePathWCopy = NULL;
9004  ma_job job;
9005  ma_bool32 waitBeforeReturning = MA_FALSE;
9006  ma_resource_manager_inline_notification waitNotification;
9007  ma_pipeline_notifications notifications;
9008 
9009  if (pNotifications != NULL) {
9010  notifications = *pNotifications;
9011  pNotifications = NULL; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */
9012  } else {
9013  MA_ZERO_OBJECT(&notifications);
9014  }
9015 
9016  if (pDataStream == NULL) {
9017  ma_pipeline_notifications_signal_all_notifications(&notifications);
9018  return MA_INVALID_ARGS;
9019  }
9020 
9021  MA_ZERO_OBJECT(pDataStream);
9022 
9023  dataSourceConfig = ma_data_source_config_init();
9024  dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;
9025 
9026  result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
9027  if (result != MA_SUCCESS) {
9028  ma_pipeline_notifications_signal_all_notifications(&notifications);
9029  return result;
9030  }
9031 
9032  pDataStream->pResourceManager = pResourceManager;
9033  pDataStream->flags = flags;
9034  pDataStream->result = MA_BUSY;
9035 
9036  if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL)) {
9037  ma_pipeline_notifications_signal_all_notifications(&notifications);
9038  return MA_INVALID_ARGS;
9039  }
9040 
9041  /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */
9042 
9043  /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */
9044  if (pFilePath != NULL) {
9045  pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
9046  } else {
9047  pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
9048  }
9049 
9050  if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
9051  ma_pipeline_notifications_signal_all_notifications(&notifications);
9052  return MA_OUT_OF_MEMORY;
9053  }
9054 
9055  /*
9056  We need to check for the presence of MA_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we
9057  can return immediately. Likewise, we'll also check for MA_DATA_SOURCE_FLAG_WAIT_INIT and do the same.
9058  */
9059  if ((flags & MA_DATA_SOURCE_FLAG_ASYNC) == 0 || (flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
9060  waitBeforeReturning = MA_TRUE;
9061  ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
9062  }
9063 
9064  ma_pipeline_notifications_acquire_all_fences(&notifications);
9065 
9066  /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */
9068  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
9069  job.loadDataStream.pDataStream = pDataStream;
9070  job.loadDataStream.pFilePath = pFilePathCopy;
9071  job.loadDataStream.pFilePathW = pFilePathWCopy;
9072  job.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
9073  job.loadDataStream.pInitFence = notifications.init.pFence;
9074  result = ma_resource_manager_post_job(pResourceManager, &job);
9075  if (result != MA_SUCCESS) {
9076  ma_pipeline_notifications_signal_all_notifications(&notifications);
9077  ma_pipeline_notifications_release_all_fences(&notifications);
9078 
9079  if (waitBeforeReturning) {
9080  ma_resource_manager_inline_notification_uninit(&waitNotification);
9081  }
9082 
9083  ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
9084  ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
9085  return result;
9086  }
9087 
9088  /* Wait if needed. */
9089  if (waitBeforeReturning) {
9090  ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
9091 
9092  if (notifications.init.pNotification != NULL) {
9094  }
9095 
9096  /* NOTE: Do not release pInitFence here. That will be done by the job. */
9097  }
9098 
9099  return MA_SUCCESS;
9100 }
9101 
9102 MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
9103 {
9104  return ma_resource_manager_data_stream_init_internal(pResourceManager, pFilePath, NULL, flags, pNotifications, pDataStream);
9105 }
9106 
9107 MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
9108 {
9109  return ma_resource_manager_data_stream_init_internal(pResourceManager, NULL, pFilePath, flags, pNotifications, pDataStream);
9110 }
9111 
9113 {
9114  ma_resource_manager_inline_notification freeEvent;
9115  ma_job job;
9116 
9117  if (pDataStream == NULL) {
9118  return MA_INVALID_ARGS;
9119  }
9120 
9121  /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */
9123 
9124  /*
9125  We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need
9126  to wait for it to complete before returning which means we need an event.
9127  */
9128  ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent);
9129 
9131  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
9132  job.freeDataStream.pDataStream = pDataStream;
9133  job.freeDataStream.pDoneNotification = &freeEvent;
9134  job.freeDataStream.pDoneFence = NULL;
9135  ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
9136 
9137  /* We need to wait for the job to finish processing before we return. */
9138  ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);
9139 
9140  return MA_SUCCESS;
9141 }
9142 
9143 
9144 static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream)
9145 {
9146  MA_ASSERT(pDataStream != NULL);
9147  MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
9148 
9149  return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000);
9150 }
9151 
9152 static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor)
9153 {
9154  MA_ASSERT(pDataStream != NULL);
9155  MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
9156  MA_ASSERT(pageIndex == 0 || pageIndex == 1);
9157 
9158  return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels));
9159 }
9160 
9161 static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex)
9162 {
9163  ma_bool32 isLooping;
9164  ma_uint64 pageSizeInFrames;
9165  ma_uint64 totalFramesReadForThisPage = 0;
9166  void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0);
9167 
9168  pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
9169 
9170  ma_resource_manager_data_stream_get_looping(pDataStream, &isLooping); /* Won't fail. */
9171 
9172  if (isLooping) {
9173  while (totalFramesReadForThisPage < pageSizeInFrames) {
9174  ma_uint64 framesRemaining;
9175  ma_uint64 framesRead;
9176 
9177  framesRemaining = pageSizeInFrames - totalFramesReadForThisPage;
9178  framesRead = ma_decoder_read_pcm_frames(&pDataStream->decoder, ma_offset_pcm_frames_ptr(pPageData, totalFramesReadForThisPage, pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels), framesRemaining);
9179  totalFramesReadForThisPage += framesRead;
9180 
9181  /* Loop back to the start if we reached the end. We'll also have a known length at this point as well. */
9182  if (framesRead < framesRemaining) {
9183  if (pDataStream->totalLengthInPCMFrames == 0) {
9185  }
9186 
9187  ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, 0);
9188  }
9189  }
9190  } else {
9191  totalFramesReadForThisPage = ma_decoder_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames);
9192  }
9193 
9194  if (totalFramesReadForThisPage < pageSizeInFrames) {
9196  }
9197 
9198  c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
9199  c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
9200 }
9201 
9202 static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
9203 {
9204  ma_uint32 iPage;
9205 
9206  MA_ASSERT(pDataStream != NULL);
9207 
9208  for (iPage = 0; iPage < 2; iPage += 1) {
9209  ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
9210  }
9211 }
9212 
9214 {
9215  ma_result result = MA_SUCCESS;
9216  ma_uint64 totalFramesProcessed;
9217  ma_format format;
9218  ma_uint32 channels;
9219 
9220  /* Safety. */
9221  if (pFramesRead != NULL) {
9222  *pFramesRead = 0;
9223  }
9224 
9225  /* We cannot be using the data source after it's been uninitialized. */
9227 
9228  if (pDataStream == NULL) {
9229  return MA_INVALID_ARGS;
9230  }
9231 
9232  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
9233  return MA_INVALID_OPERATION;
9234  }
9235 
9236  /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
9237  if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
9238  return MA_BUSY;
9239  }
9240 
9241  ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL);
9242 
9243  /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */
9244  totalFramesProcessed = 0;
9245  while (totalFramesProcessed < frameCount) {
9246  void* pMappedFrames;
9247  ma_uint64 mappedFrameCount;
9248 
9249  mappedFrameCount = frameCount - totalFramesProcessed;
9250  result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);
9251  if (result != MA_SUCCESS) {
9252  break;
9253  }
9254 
9255  /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */
9256  if (pFramesOut != NULL) {
9257  ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);
9258  }
9259 
9260  totalFramesProcessed += mappedFrameCount;
9261 
9262  result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);
9263  if (result != MA_SUCCESS) {
9264  break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */
9265  }
9266  }
9267 
9268  if (pFramesRead != NULL) {
9269  *pFramesRead = totalFramesProcessed;
9270  }
9271 
9272  return result;
9273 }
9274 
9276 {
9277  ma_uint64 framesAvailable;
9278  ma_uint64 frameCount = 0;
9279 
9280  /* We cannot be using the data source after it's been uninitialized. */
9282 
9283  if (pFrameCount != NULL) {
9284  frameCount = *pFrameCount;
9285  *pFrameCount = 0;
9286  }
9287  if (ppFramesOut != NULL) {
9288  *ppFramesOut = NULL;
9289  }
9290 
9291  if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
9292  return MA_INVALID_ARGS;
9293  }
9294 
9295  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
9296  return MA_INVALID_OPERATION;
9297  }
9298 
9299  /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
9300  if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
9301  return MA_BUSY;
9302  }
9303 
9304  /* If the page we're on is invalid it means we've caught up to the job thread. */
9305  if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
9306  framesAvailable = 0;
9307  } else {
9308  /*
9309  The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is
9310  that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.
9311  */
9312  ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
9313  MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
9314 
9315  framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
9316  }
9317 
9318  /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */
9319  if (framesAvailable == 0) {
9320  if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
9321  return MA_AT_END;
9322  } else {
9323  return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */
9324  }
9325  }
9326 
9327  MA_ASSERT(framesAvailable > 0);
9328 
9329  if (frameCount > framesAvailable) {
9330  frameCount = framesAvailable;
9331  }
9332 
9333  *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
9334  *pFrameCount = frameCount;
9335 
9336  return MA_SUCCESS;
9337 }
9338 
9339 static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor)
9340 {
9341  /* Loop if possible. */
9342  if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {
9343  absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;
9344  }
9345 
9346  c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);
9347 }
9348 
9350 {
9351  ma_uint32 newRelativeCursor;
9352  ma_uint32 pageSizeInFrames;
9353  ma_job job;
9354 
9355  /* We cannot be using the data source after it's been uninitialized. */
9357 
9358  if (pDataStream == NULL) {
9359  return MA_INVALID_ARGS;
9360  }
9361 
9362  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
9363  return MA_INVALID_OPERATION;
9364  }
9365 
9366  /* The frame count should always fit inside a 32-bit integer. */
9367  if (frameCount > 0xFFFFFFFF) {
9368  return MA_INVALID_ARGS;
9369  }
9370 
9371  pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
9372 
9373  /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */
9374  ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
9375 
9376  /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */
9377  newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
9378 
9379  /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */
9380  if (newRelativeCursor >= pageSizeInFrames) {
9381  newRelativeCursor -= pageSizeInFrames;
9382 
9383  /* Here is where we post the job start decoding. */
9385  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
9386  job.pageDataStream.pDataStream = pDataStream;
9387  job.pageDataStream.pageIndex = pDataStream->currentPageIndex;
9388 
9389  /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */
9390  c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
9391 
9392  /* Before posting the job we need to make sure we set some state. */
9393  pDataStream->relativeCursor = newRelativeCursor;
9394  pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
9395  return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
9396  } else {
9397  /* We haven't moved into a new page so we can just move the cursor forward. */
9398  pDataStream->relativeCursor = newRelativeCursor;
9399  return MA_SUCCESS;
9400  }
9401 }
9402 
9404 {
9405  ma_job job;
9406  ma_result streamResult;
9407 
9408  streamResult = ma_resource_manager_data_stream_result(pDataStream);
9409 
9410  /* We cannot be using the data source after it's been uninitialized. */
9411  MA_ASSERT(streamResult != MA_UNAVAILABLE);
9412 
9413  if (pDataStream == NULL) {
9414  return MA_INVALID_ARGS;
9415  }
9416 
9417  if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
9418  return MA_INVALID_OPERATION;
9419  }
9420 
9421  /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */
9422  c89atomic_fetch_add_32(&pDataStream->seekCounter, 1);
9423 
9424  /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */
9425  ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);
9426 
9427  /*
9428  We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public
9429  API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of
9430  the first page.
9431  */
9432  pDataStream->relativeCursor = 0;
9433  pDataStream->currentPageIndex = 0;
9434  c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);
9435  c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);
9436 
9437  /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */
9439 
9440  /*
9441  The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages
9442  are invalid and any content contained within them will be discarded and replaced with newly decoded data.
9443  */
9445  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
9446  job.seekDataStream.pDataStream = pDataStream;
9447  job.seekDataStream.frameIndex = frameIndex;
9448  return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
9449 }
9450 
9452 {
9453  /* We cannot be using the data source after it's been uninitialized. */
9455 
9456  if (pFormat != NULL) {
9457  *pFormat = ma_format_unknown;
9458  }
9459 
9460  if (pChannels != NULL) {
9461  *pChannels = 0;
9462  }
9463 
9464  if (pSampleRate != NULL) {
9465  *pSampleRate = 0;
9466  }
9467 
9468  if (pDataStream == NULL) {
9469  return MA_INVALID_ARGS;
9470  }
9471 
9472  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
9473  return MA_INVALID_OPERATION;
9474  }
9475 
9476  /*
9477  We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function
9478  such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.
9479  */
9480  return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate);
9481 }
9482 
9484 {
9485  if (pCursor == NULL) {
9486  return MA_INVALID_ARGS;
9487  }
9488 
9489  *pCursor = 0;
9490 
9491  /* We cannot be using the data source after it's been uninitialized. */
9493 
9494  if (pDataStream == NULL) {
9495  return MA_INVALID_ARGS;
9496  }
9497 
9498  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
9499  return MA_INVALID_OPERATION;
9500  }
9501 
9502  *pCursor = pDataStream->absoluteCursor;
9503 
9504  return MA_SUCCESS;
9505 }
9506 
9508 {
9509  ma_result streamResult;
9510 
9511  if (pLength == NULL) {
9512  return MA_INVALID_ARGS;
9513  }
9514 
9515  *pLength = 0;
9516 
9517  streamResult = ma_resource_manager_data_stream_result(pDataStream);
9518 
9519  /* We cannot be using the data source after it's been uninitialized. */
9520  MA_ASSERT(streamResult != MA_UNAVAILABLE);
9521 
9522  if (pDataStream == NULL) {
9523  return MA_INVALID_ARGS;
9524  }
9525 
9526  if (streamResult != MA_SUCCESS) {
9527  return streamResult;
9528  }
9529 
9530  /*
9531  We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we
9532  calculated when we initialized it on the job thread.
9533  */
9534  *pLength = pDataStream->totalLengthInPCMFrames;
9535  if (*pLength == 0) {
9536  return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */
9537  }
9538 
9539  return MA_SUCCESS;
9540 }
9541 
9543 {
9544  if (pDataStream == NULL) {
9545  return MA_INVALID_ARGS;
9546  }
9547 
9548  return c89atomic_load_i32(&pDataStream->result);
9549 }
9550 
9552 {
9553  if (pDataStream == NULL) {
9554  return MA_INVALID_ARGS;
9555  }
9556 
9557  c89atomic_exchange_32(&pDataStream->isLooping, isLooping);
9558 
9559  return MA_SUCCESS;
9560 }
9561 
9563 {
9564  if (pIsLooping == NULL) {
9565  return MA_INVALID_ARGS;
9566  }
9567 
9568  *pIsLooping = MA_FALSE;
9569 
9570  if (pDataStream == NULL) {
9571  return MA_INVALID_ARGS;
9572  }
9573 
9574  *pIsLooping = c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */
9575 
9576  return MA_SUCCESS;
9577 }
9578 
9580 {
9581  ma_uint32 pageIndex0;
9582  ma_uint32 pageIndex1;
9583  ma_uint32 relativeCursor;
9584  ma_uint64 availableFrames;
9585 
9586  if (pAvailableFrames == NULL) {
9587  return MA_INVALID_ARGS;
9588  }
9589 
9590  *pAvailableFrames = 0;
9591 
9592  if (pDataStream == NULL) {
9593  return MA_INVALID_ARGS;
9594  }
9595 
9596  pageIndex0 = pDataStream->currentPageIndex;
9597  pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01;
9598  relativeCursor = pDataStream->relativeCursor;
9599 
9600  availableFrames = 0;
9601  if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {
9602  availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;
9603  if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {
9604  availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);
9605  }
9606  }
9607 
9608  *pAvailableFrames = availableFrames;
9609  return MA_SUCCESS;
9610 }
9611 
9612 
9613 static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, ma_uint32 flags, ma_resource_manager_data_source* pDataSource)
9614 {
9615  if (pDataSource == NULL) {
9616  return MA_INVALID_ARGS;
9617  }
9618 
9619  MA_ZERO_OBJECT(pDataSource);
9620 
9621  if (pResourceManager == NULL) {
9622  return MA_INVALID_ARGS;
9623  }
9624 
9625  pDataSource->flags = flags;
9626 
9627  return MA_SUCCESS;
9628 }
9629 
9630 MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)
9631 {
9632  ma_result result;
9633 
9634  result = ma_resource_manager_data_source_preinit(pResourceManager, flags, pDataSource);
9635  if (result != MA_SUCCESS) {
9636  return result;
9637  }
9638 
9639  /* The data source itself is just a data stream or a data buffer. */
9640  if ((flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9641  return ma_resource_manager_data_stream_init(pResourceManager, pName, flags, pNotifications, &pDataSource->stream);
9642  } else {
9643  return ma_resource_manager_data_buffer_init(pResourceManager, pName, flags, pNotifications, &pDataSource->buffer);
9644  }
9645 }
9646 
9647 MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)
9648 {
9649  ma_result result;
9650 
9651  result = ma_resource_manager_data_source_preinit(pResourceManager, flags, pDataSource);
9652  if (result != MA_SUCCESS) {
9653  return result;
9654  }
9655 
9656  /* The data source itself is just a data stream or a data buffer. */
9657  if ((flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9658  return ma_resource_manager_data_stream_init_w(pResourceManager, pName, flags, pNotifications, &pDataSource->stream);
9659  } else {
9660  return ma_resource_manager_data_buffer_init_w(pResourceManager, pName, flags, pNotifications, &pDataSource->buffer);
9661  }
9662 }
9663 
9665 {
9666  ma_result result;
9667 
9668  if (pExistingDataSource == NULL) {
9669  return MA_INVALID_ARGS;
9670  }
9671 
9672  result = ma_resource_manager_data_source_preinit(pResourceManager, pExistingDataSource->flags, pDataSource);
9673  if (result != MA_SUCCESS) {
9674  return result;
9675  }
9676 
9677  /* Copying can only be done from data buffers. Streams cannot be copied. */
9678  if ((pExistingDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9679  return MA_INVALID_OPERATION;
9680  }
9681 
9682  return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->buffer, &pDataSource->buffer);
9683 }
9684 
9686 {
9687  if (pDataSource == NULL) {
9688  return MA_INVALID_ARGS;
9689  }
9690 
9691  /* All we need to is uninitialize the underlying data buffer or data stream. */
9692  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9693  return ma_resource_manager_data_stream_uninit(&pDataSource->stream);
9694  } else {
9695  return ma_resource_manager_data_buffer_uninit(&pDataSource->buffer);
9696  }
9697 }
9698 
9700 {
9701  /* Safety. */
9702  if (pFramesRead != NULL) {
9703  *pFramesRead = 0;
9704  }
9705 
9706  if (pDataSource == NULL) {
9707  return MA_INVALID_ARGS;
9708  }
9709 
9710  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9711  return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->stream, pFramesOut, frameCount, pFramesRead);
9712  } else {
9713  return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->buffer, pFramesOut, frameCount, pFramesRead);
9714  }
9715 }
9716 
9718 {
9719  if (pDataSource == NULL) {
9720  return MA_INVALID_ARGS;
9721  }
9722 
9723  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9724  return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->stream, frameIndex);
9725  } else {
9726  return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->buffer, frameIndex);
9727  }
9728 }
9729 
9731 {
9732  if (pDataSource == NULL) {
9733  return MA_INVALID_ARGS;
9734  }
9735 
9736  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9737  return ma_resource_manager_data_stream_map(&pDataSource->stream, ppFramesOut, pFrameCount);
9738  } else {
9739  return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
9740  }
9741 }
9742 
9744 {
9745  if (pDataSource == NULL) {
9746  return MA_INVALID_ARGS;
9747  }
9748 
9749  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9750  return ma_resource_manager_data_stream_unmap(&pDataSource->stream, frameCount);
9751  } else {
9752  return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
9753  }
9754 }
9755 
9757 {
9758  if (pDataSource == NULL) {
9759  return MA_INVALID_ARGS;
9760  }
9761 
9762  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9763  return ma_resource_manager_data_stream_get_data_format(&pDataSource->stream, pFormat, pChannels, pSampleRate);
9764  } else {
9765  return ma_resource_manager_data_buffer_get_data_format(&pDataSource->buffer, pFormat, pChannels, pSampleRate);
9766  }
9767 }
9768 
9770 {
9771  if (pDataSource == NULL) {
9772  return MA_INVALID_ARGS;
9773  }
9774 
9775  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9777  } else {
9779  }
9780 }
9781 
9783 {
9784  if (pDataSource == NULL) {
9785  return MA_INVALID_ARGS;
9786  }
9787 
9788  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9790  } else {
9792  }
9793 }
9794 
9796 {
9797  if (pDataSource == NULL) {
9798  return MA_INVALID_ARGS;
9799  }
9800 
9801  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9802  return ma_resource_manager_data_stream_result(&pDataSource->stream);
9803  } else {
9804  return ma_resource_manager_data_buffer_result(&pDataSource->buffer);
9805  }
9806 }
9807 
9809 {
9810  if (pDataSource == NULL) {
9811  return MA_INVALID_ARGS;
9812  }
9813 
9814  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9815  return ma_resource_manager_data_stream_set_looping(&pDataSource->stream, isLooping);
9816  } else {
9817  return ma_resource_manager_data_buffer_set_looping(&pDataSource->buffer, isLooping);
9818  }
9819 }
9820 
9822 {
9823  if (pDataSource == NULL || pIsLooping == NULL) {
9824  return MA_INVALID_ARGS;
9825  }
9826 
9827  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9828  return ma_resource_manager_data_stream_get_looping(&pDataSource->stream, pIsLooping);
9829  } else {
9830  return ma_resource_manager_data_buffer_get_looping(&pDataSource->buffer, pIsLooping);
9831  }
9832 }
9833 
9835 {
9836  if (pAvailableFrames == NULL) {
9837  return MA_INVALID_ARGS;
9838  }
9839 
9840  *pAvailableFrames = 0;
9841 
9842  if (pDataSource == NULL) {
9843  return MA_INVALID_ARGS;
9844  }
9845 
9846  if ((pDataSource->flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
9847  return ma_resource_manager_data_stream_get_available_frames(&pDataSource->stream, pAvailableFrames);
9848  } else {
9849  return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->buffer, pAvailableFrames);
9850  }
9851 }
9852 
9853 
9855 {
9856  if (pResourceManager == NULL) {
9857  return MA_INVALID_ARGS;
9858  }
9859 
9860  return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
9861 }
9862 
9864 {
9866  return ma_resource_manager_post_job(pResourceManager, &job);
9867 }
9868 
9870 {
9871  if (pResourceManager == NULL) {
9872  return MA_INVALID_ARGS;
9873  }
9874 
9875  return ma_job_queue_next(&pResourceManager->jobQueue, pJob);
9876 }
9877 
9878 
9879 static ma_result ma_resource_manager_process_job__load_data_buffer_node(ma_resource_manager* pResourceManager, ma_job* pJob)
9880 {
9881  ma_result result = MA_SUCCESS;
9882 
9883  MA_ASSERT(pResourceManager != NULL);
9884  MA_ASSERT(pJob != NULL);
9885  MA_ASSERT(pJob->loadDataBufferNode.pDataBufferNode != NULL);
9886  MA_ASSERT(pJob->loadDataBufferNode.pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */
9887 
9888  /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */
9889  if (ma_resource_manager_data_buffer_node_result(pJob->loadDataBufferNode.pDataBufferNode) != MA_BUSY) {
9890  result = ma_resource_manager_data_buffer_node_result(pJob->loadDataBufferNode.pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */
9891  goto done;
9892  }
9893 
9894  /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */
9895  if (pJob->order != pJob->loadDataBufferNode.pDataBufferNode->executionPointer) {
9896  return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_FREE_DATA_BUFFER job. */
9897  }
9898 
9899  /*
9900  We're ready to start loading. Essentially what we're doing here is initializing the data supply
9901  of the node. Once this is complete, data buffers can have their connectors initialized which
9902  will allow then to have audio data read from them.
9903 
9904  Note that when the data supply type has been moved away from "unknown", that is when other threads
9905  will determine that the node is available for data delivery and the data buffer connectors can be
9906  initialized. Therefore, it's important that it is set after the data supply has been initialized.
9907  */
9908  if (pJob->loadDataBufferNode.decode) {
9909  /*
9910  Decoding. This is the complex case because we're not going to be doing the entire decoding
9911  process here. Instead it's going to be split of multiple jobs and loaded in pages. The
9912  reason for this is to evenly distribute decoding time across multiple sounds, rather than
9913  having one huge sound hog all the available processing resources.
9914 
9915  The first thing we do is initialize a decoder. This is allocated on the heap and is passed
9916  around to the paging jobs. When the last paging job has completed it's processing, it'll
9917  free the decoder for us.
9918 
9919  This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job
9920  which is where the actual decoding work will be done. However, once this job is complete,
9921  the node will be in a state where data buffer connectors can be initialized.
9922  */
9923  ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */
9924  ma_job pageDataBufferNodeJob;
9925 
9926  /* Allocate the decoder by initializing a decoded data supply. */
9927  result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pJob->loadDataBufferNode.pDataBufferNode, pJob->loadDataBufferNode.pFilePath, pJob->loadDataBufferNode.pFilePathW, &pDecoder);
9928 
9929  /*
9930  Don't ever propagate an MA_BUSY result code or else the resource manager will think the
9931  node is just busy decoding rather than in an error state. This should never happen, but
9932  including this logic for safety just in case.
9933  */
9934  if (result == MA_BUSY) {
9935  result = MA_ERROR;
9936  }
9937 
9938  if (result != MA_SUCCESS) {
9939  if (pJob->loadDataBufferNode.pFilePath != NULL) {
9940  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->loadDataBufferNode.pFilePath, ma_result_description(result));
9941  } else {
9942  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->loadDataBufferNode.pFilePathW, ma_result_description(result));
9943  }
9944 
9945  goto done;
9946  }
9947 
9948  /*
9949  At this point the node's data supply is initialized and other threads can start initializing
9950  their data buffer connectors. However, no data will actually be available until we start to
9951  actually decode it. To do this, we need to post a paging job which is where the decoding
9952  work is done.
9953 
9954  Note that if an error occurred at an earlier point, this section will have been skipped.
9955  */
9956  pageDataBufferNodeJob = ma_job_init(MA_JOB_PAGE_DATA_BUFFER_NODE);
9957  pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pJob->loadDataBufferNode.pDataBufferNode);
9958  pageDataBufferNodeJob.pageDataBufferNode.pDataBufferNode = pJob->loadDataBufferNode.pDataBufferNode;
9959  pageDataBufferNodeJob.pageDataBufferNode.pDecoder = pDecoder;
9960  pageDataBufferNodeJob.pageDataBufferNode.pDoneNotification = pJob->loadDataBufferNode.pDoneNotification;
9961  pageDataBufferNodeJob.pageDataBufferNode.pDoneFence = pJob->loadDataBufferNode.pDoneFence;
9962 
9963  /* The job has been set up so it can now be posted. */
9964  result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
9965 
9966  /*
9967  When we get here, we want to make sure the result code is set to MA_BUSY. The reason for
9968  this is that the result will be copied over to the node's internal result variable. In
9969  this case, since the decoding is still in-progress, we need to make sure the result code
9970  is set to MA_BUSY.
9971  */
9972  if (result != MA_SUCCESS) {
9973  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_PAGE_DATA_BUFFER_NODE job. %d\n", ma_result_description(result));
9974  ma_decoder_uninit(pDecoder);
9975  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
9976  } else {
9977  result = MA_BUSY;
9978  }
9979  } else {
9980  /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */
9981  result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pJob->loadDataBufferNode.pDataBufferNode, pJob->loadDataBufferNode.pFilePath, pJob->loadDataBufferNode.pFilePathW);
9982  }
9983 
9984 
9985 done:
9986  /* File paths are no longer needed. */
9987  ma_free(pJob->loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
9988  ma_free(pJob->loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
9989 
9990  /*
9991  We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads
9992  are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY
9993  because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then
9994  immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any
9995  other error code would cause the buffer to look like it's in a state that it's not.
9996  */
9997  c89atomic_compare_and_swap_32(&pJob->loadDataBufferNode.pDataBufferNode->result, MA_BUSY, result);
9998 
9999  /* At this point initialization is complete and we can signal the notification if any. */
10000  if (pJob->loadDataBufferNode.pInitNotification != NULL) {
10001  ma_async_notification_signal(pJob->loadDataBufferNode.pInitNotification);
10002  }
10003  if (pJob->loadDataBufferNode.pInitFence != NULL) {
10004  ma_fence_release(pJob->loadDataBufferNode.pInitFence);
10005  }
10006 
10007  /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */
10008  if (result != MA_BUSY) {
10009  if (pJob->loadDataBufferNode.pDoneNotification != NULL) {
10010  ma_async_notification_signal(pJob->loadDataBufferNode.pDoneNotification);
10011  }
10012  if (pJob->loadDataBufferNode.pDoneFence != NULL) {
10013  ma_fence_release(pJob->loadDataBufferNode.pDoneFence);
10014  }
10015  }
10016 
10017  /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */
10018  c89atomic_fetch_add_32(&pJob->loadDataBufferNode.pDataBufferNode->executionPointer, 1);
10019  return result;
10020 }
10021 
10022 static ma_result ma_resource_manager_process_job__free_data_buffer_node(ma_resource_manager* pResourceManager, ma_job* pJob)
10023 {
10024  MA_ASSERT(pResourceManager != NULL);
10025  MA_ASSERT(pJob != NULL);
10026  MA_ASSERT(pJob->freeDataBufferNode.pDataBufferNode != NULL);
10027 
10028  if (pJob->order != pJob->freeDataBufferNode.pDataBufferNode->executionPointer) {
10029  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
10030  }
10031 
10032  ma_resource_manager_data_buffer_node_free(pResourceManager, pJob->freeDataBufferNode.pDataBufferNode);
10033 
10034  /* The event needs to be signalled last. */
10035  if (pJob->freeDataBufferNode.pDoneNotification != NULL) {
10036  ma_async_notification_signal(pJob->freeDataBufferNode.pDoneNotification);
10037  }
10038 
10039  if (pJob->freeDataBufferNode.pDoneFence != NULL) {
10040  ma_fence_release(pJob->freeDataBufferNode.pDoneFence);
10041  }
10042 
10043  c89atomic_fetch_add_32(&pJob->freeDataBufferNode.pDataBufferNode->executionPointer, 1);
10044  return MA_SUCCESS;
10045 }
10046 
10047 static ma_result ma_resource_manager_process_job__page_data_buffer_node(ma_resource_manager* pResourceManager, ma_job* pJob)
10048 {
10049  ma_result result = MA_SUCCESS;
10050 
10051  MA_ASSERT(pResourceManager != NULL);
10052  MA_ASSERT(pJob != NULL);
10053 
10054  /* Don't do any more decoding if the data buffer has started the uninitialization process. */
10055  if (ma_resource_manager_data_buffer_node_result(pJob->pageDataBufferNode.pDataBufferNode) != MA_BUSY) {
10056  result = ma_resource_manager_data_buffer_node_result(pJob->pageDataBufferNode.pDataBufferNode);
10057  goto done;
10058  }
10059 
10060  if (pJob->order != pJob->pageDataBufferNode.pDataBufferNode->executionPointer) {
10061  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
10062  }
10063 
10064  /* We're ready to decode the next page. */
10065  result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pJob->pageDataBufferNode.pDataBufferNode, pJob->pageDataBufferNode.pDecoder);
10066 
10067  /*
10068  If we have a success code by this point, we want to post another job. We're going to set the
10069  result back to MA_BUSY to make it clear that there's still more to load.
10070  */
10071  if (result == MA_SUCCESS) {
10072  ma_job newJob;
10073  newJob = *pJob; /* Everything is the same as the input job, except the execution order. */
10074  newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pJob->pageDataBufferNode.pDataBufferNode); /* We need a fresh execution order. */
10075 
10076  result = ma_resource_manager_post_job(pResourceManager, &newJob);
10077 
10078  /* Since the sound isn't yet fully decoded we want the status to be set to busy. */
10079  if (result == MA_SUCCESS) {
10080  result = MA_BUSY;
10081  }
10082  }
10083 
10084 done:
10085  /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */
10086  if (result != MA_BUSY) {
10087  ma_decoder_uninit(pJob->pageDataBufferNode.pDecoder);
10088  ma_free(pJob->pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
10089  }
10090 
10091  /* If we reached the end we need to treat it as successful. */
10092  if (result == MA_AT_END) {
10093  result = MA_SUCCESS;
10094  }
10095 
10096  /* Make sure we set the result of node in case some error occurred. */
10097  c89atomic_compare_and_swap_32(&pJob->pageDataBufferNode.pDataBufferNode->result, MA_BUSY, result);
10098 
10099  /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */
10100  if (result != MA_BUSY) {
10101  if (pJob->pageDataBufferNode.pDoneNotification != NULL) {
10102  ma_async_notification_signal(pJob->pageDataBufferNode.pDoneNotification);
10103  }
10104 
10105  if (pJob->pageDataBufferNode.pDoneFence != NULL) {
10106  ma_fence_release(pJob->pageDataBufferNode.pDoneFence);
10107  }
10108  }
10109 
10110  c89atomic_fetch_add_32(&pJob->pageDataBufferNode.pDataBufferNode->executionPointer, 1);
10111  return result;
10112 }
10113 
10114 
10115 static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_manager* pResourceManager, ma_job* pJob)
10116 {
10117  ma_result result = MA_SUCCESS;
10118 
10119  /*
10120  All we're doing here is checking if the node has finished loading. If not, we just re-post the job
10121  and keep waiting. Otherwise we increment the execution counter and set the buffer's result code.
10122  */
10123  MA_ASSERT(pResourceManager != NULL);
10124  MA_ASSERT(pJob != NULL);
10125  MA_ASSERT(pJob->loadDataBuffer.pDataBuffer != NULL);
10126 
10127  if (pJob->order != pJob->loadDataBuffer.pDataBuffer->executionPointer) {
10128  return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_FREE_DATA_BUFFER job. */
10129  }
10130 
10131  /*
10132  First thing we need to do is check whether or not the data buffer is getting deleted. If so we
10133  just abort, but making sure we increment the execution pointer.
10134  */
10136  result = ma_resource_manager_data_buffer_result(pJob->loadDataBuffer.pDataBuffer); /* The data buffer may be getting deleted before it's even been loaded. */
10137  goto done;
10138  }
10139 
10140  /* Try initializing the connector if we haven't already. */
10141  if (pJob->loadDataBuffer.pDataBuffer->isConnectorInitialized == MA_FALSE) {
10142  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pJob->loadDataBuffer.pDataBuffer->pNode) != ma_resource_manager_data_supply_type_unknown) {
10143  /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
10144  result = ma_resource_manager_data_buffer_init_connector(pJob->loadDataBuffer.pDataBuffer, pJob->loadDataBuffer.pInitNotification, pJob->loadDataBuffer.pInitFence);
10145  if (result != MA_SUCCESS) {
10146  ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result));
10147  goto done;
10148  }
10149  }
10150  } else {
10151  /* The connector is already initialized. Nothing to do here. */
10152  }
10153 
10154  /*
10155  If the data node is still loading, we need to repost the job and *not* increment the execution
10156  pointer (i.e. we need to not fall through to the "done" label).
10157  */
10158  result = ma_resource_manager_data_buffer_node_result(pJob->loadDataBuffer.pDataBuffer->pNode);
10159  if (result == MA_BUSY) {
10160  return ma_resource_manager_post_job(pResourceManager, pJob);
10161  }
10162 
10163 done:
10164  /* Only move away from a busy code so that we don't trash any existing error codes. */
10165  c89atomic_compare_and_swap_32(&pJob->loadDataBuffer.pDataBuffer->result, MA_BUSY, result);
10166 
10167  /* Only signal the other threads after the result has been set just for cleanliness sake. */
10168  if (pJob->loadDataBuffer.pDoneNotification != NULL) {
10169  ma_async_notification_signal(pJob->loadDataBuffer.pDoneNotification);
10170  }
10171  if (pJob->loadDataBuffer.pDoneFence != NULL) {
10172  ma_fence_release(pJob->loadDataBuffer.pDoneFence);
10173  }
10174 
10175  /*
10176  If at this point the data buffer has not had it's connector initialized, it means the
10177  notification event was never signalled which means we need to signal it here.
10178  */
10179  if (pJob->loadDataBuffer.pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) {
10180  if (pJob->loadDataBuffer.pInitNotification != NULL) {
10181  ma_async_notification_signal(pJob->loadDataBuffer.pInitNotification);
10182  }
10183  if (pJob->loadDataBuffer.pInitFence != NULL) {
10184  ma_fence_release(pJob->loadDataBuffer.pInitFence);
10185  }
10186  }
10187 
10188  c89atomic_fetch_add_32(&pJob->loadDataBuffer.pDataBuffer->executionPointer, 1);
10189  return result;
10190 }
10191 
10192 static ma_result ma_resource_manager_process_job__free_data_buffer(ma_resource_manager* pResourceManager, ma_job* pJob)
10193 {
10194  MA_ASSERT(pResourceManager != NULL);
10195  MA_ASSERT(pJob != NULL);
10196  MA_ASSERT(pJob->freeDataBuffer.pDataBuffer != NULL);
10197 
10198  if (pJob->order != pJob->freeDataBuffer.pDataBuffer->executionPointer) {
10199  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
10200  }
10201 
10202  ma_resource_manager_data_buffer_uninit_internal(pJob->freeDataBuffer.pDataBuffer);
10203 
10204  /* The event needs to be signalled last. */
10205  if (pJob->freeDataBuffer.pDoneNotification != NULL) {
10206  ma_async_notification_signal(pJob->freeDataBuffer.pDoneNotification);
10207  }
10208 
10209  if (pJob->freeDataBuffer.pDoneFence != NULL) {
10210  ma_fence_release(pJob->freeDataBuffer.pDoneFence);
10211  }
10212 
10213  c89atomic_fetch_add_32(&pJob->freeDataBuffer.pDataBuffer->executionPointer, 1);
10214  return MA_SUCCESS;
10215 }
10216 
10217 static ma_result ma_resource_manager_process_job__load_data_stream(ma_resource_manager* pResourceManager, ma_job* pJob)
10218 {
10219  ma_result result = MA_SUCCESS;
10220  ma_decoder_config decoderConfig;
10221  ma_uint32 pageBufferSizeInBytes;
10222  ma_resource_manager_data_stream* pDataStream;
10223 
10224  MA_ASSERT(pResourceManager != NULL);
10225  MA_ASSERT(pJob != NULL);
10226 
10227  pDataStream = pJob->loadDataStream.pDataStream;
10228 
10229  if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {
10230  result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */
10231  goto done;
10232  }
10233 
10234  if (pJob->order != pDataStream->executionPointer) {
10235  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
10236  }
10237 
10238  /* We need to initialize the decoder first so we can determine the size of the pages. */
10239  decoderConfig = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
10240  decoderConfig.allocationCallbacks = pResourceManager->config.allocationCallbacks;
10241 
10242  if (pJob->loadDataStream.pFilePath != NULL) {
10243  result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);
10244  } else {
10245  result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);
10246  }
10247  if (result != MA_SUCCESS) {
10248  goto done;
10249  }
10250 
10251  /* Retrieve the total length of the file before marking the decoder are loaded. */
10253 
10254  /*
10255  Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file
10256  and we don't want to have another thread trying to access the decoder while it's scanning.
10257  */
10258  pDataStream->isDecoderInitialized = MA_TRUE;
10259 
10260  /* We have the decoder so we can now initialize our page buffer. */
10261  pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);
10262 
10263  pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/);
10264  if (pDataStream->pPageData == NULL) {
10265  ma_decoder_uninit(&pDataStream->decoder);
10266  result = MA_OUT_OF_MEMORY;
10267  goto done;
10268  }
10269 
10270  /* We have our decoder and our page buffer, so now we need to fill our pages. */
10271  ma_resource_manager_data_stream_fill_pages(pDataStream);
10272 
10273  /* And now we're done. We want to make sure the result is MA_SUCCESS. */
10274  result = MA_SUCCESS;
10275 
10276 done:
10277  ma_free(pJob->loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
10278  ma_free(pJob->loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
10279 
10280  /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */
10281  c89atomic_compare_and_swap_32(&pDataStream->result, MA_BUSY, result);
10282 
10283  /* Only signal the other threads after the result has been set just for cleanliness sake. */
10284  if (pJob->loadDataStream.pInitNotification != NULL) {
10285  ma_async_notification_signal(pJob->loadDataStream.pInitNotification);
10286  }
10287  if (pJob->loadDataStream.pInitFence != NULL) {
10288  ma_fence_release(pJob->loadDataStream.pInitFence);
10289  }
10290 
10291  c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
10292  return result;
10293 }
10294 
10295 static ma_result ma_resource_manager_process_job__free_data_stream(ma_resource_manager* pResourceManager, ma_job* pJob)
10296 {
10297  ma_resource_manager_data_stream* pDataStream;
10298 
10299  MA_ASSERT(pResourceManager != NULL);
10300  MA_ASSERT(pJob != NULL);
10301 
10302  pDataStream = pJob->freeDataStream.pDataStream;
10303  MA_ASSERT(pDataStream != NULL);
10304 
10305  /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */
10307 
10308  if (pJob->order != pDataStream->executionPointer) {
10309  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
10310  }
10311 
10312  if (pDataStream->isDecoderInitialized) {
10313  ma_decoder_uninit(&pDataStream->decoder);
10314  }
10315 
10316  if (pDataStream->pPageData != NULL) {
10317  ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/);
10318  pDataStream->pPageData = NULL; /* Just in case... */
10319  }
10320 
10321  ma_data_source_uninit(&pDataStream->ds);
10322 
10323  /* The event needs to be signalled last. */
10324  if (pJob->freeDataStream.pDoneNotification != NULL) {
10325  ma_async_notification_signal(pJob->freeDataStream.pDoneNotification);
10326  }
10327  if (pJob->freeDataStream.pDoneFence != NULL) {
10328  ma_fence_release(pJob->freeDataStream.pDoneFence);
10329  }
10330 
10331  /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/
10332  return MA_SUCCESS;
10333 }
10334 
10335 static ma_result ma_resource_manager_process_job__page_data_stream(ma_resource_manager* pResourceManager, ma_job* pJob)
10336 {
10337  ma_result result = MA_SUCCESS;
10338  ma_resource_manager_data_stream* pDataStream;
10339 
10340  MA_ASSERT(pResourceManager != NULL);
10341  MA_ASSERT(pJob != NULL);
10342 
10343  pDataStream = pJob->pageDataStream.pDataStream;
10344  MA_ASSERT(pDataStream != NULL);
10345 
10346  /* For streams, the status should be MA_SUCCESS. */
10347  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
10348  result = MA_INVALID_OPERATION;
10349  goto done;
10350  }
10351 
10352  if (pJob->order != pDataStream->executionPointer) {
10353  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
10354  }
10355 
10356  ma_resource_manager_data_stream_fill_page(pDataStream, pJob->pageDataStream.pageIndex);
10357 
10358 done:
10359  c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
10360  return result;
10361 }
10362 
10363 static ma_result ma_resource_manager_process_job__seek_data_stream(ma_resource_manager* pResourceManager, ma_job* pJob)
10364 {
10365  ma_result result = MA_SUCCESS;
10366  ma_resource_manager_data_stream* pDataStream;
10367 
10368  MA_ASSERT(pResourceManager != NULL);
10369  MA_ASSERT(pJob != NULL);
10370 
10371  pDataStream = pJob->seekDataStream.pDataStream;
10372  MA_ASSERT(pDataStream != NULL);
10373 
10374  /* For streams the status should be MA_SUCCESS for this to do anything. */
10375  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) {
10376  result = MA_INVALID_OPERATION;
10377  goto done;
10378  }
10379 
10380  if (pJob->order != pDataStream->executionPointer) {
10381  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
10382  }
10383 
10384  /*
10385  With seeking we just assume both pages are invalid and the relative frame cursor at at position 0. This is basically exactly the same as loading, except
10386  instead of initializing the decoder, we seek to a frame.
10387  */
10388  ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->seekDataStream.frameIndex);
10389 
10390  /* After seeking we'll need to reload the pages. */
10391  ma_resource_manager_data_stream_fill_pages(pDataStream);
10392 
10393  /* We need to let the public API know that we're done seeking. */
10394  c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1);
10395 
10396 done:
10397  c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
10398  return result;
10399 }
10400 
10402 {
10403  if (pResourceManager == NULL || pJob == NULL) {
10404  return MA_INVALID_ARGS;
10405  }
10406 
10407  switch (pJob->toc.breakup.code)
10408  {
10409  /* Data Buffer Node */
10410  case MA_JOB_LOAD_DATA_BUFFER_NODE: return ma_resource_manager_process_job__load_data_buffer_node(pResourceManager, pJob);
10411  case MA_JOB_FREE_DATA_BUFFER_NODE: return ma_resource_manager_process_job__free_data_buffer_node(pResourceManager, pJob);
10412  case MA_JOB_PAGE_DATA_BUFFER_NODE: return ma_resource_manager_process_job__page_data_buffer_node(pResourceManager, pJob);
10413 
10414  /* Data Buffer */
10415  case MA_JOB_LOAD_DATA_BUFFER: return ma_resource_manager_process_job__load_data_buffer(pResourceManager, pJob);
10416  case MA_JOB_FREE_DATA_BUFFER: return ma_resource_manager_process_job__free_data_buffer(pResourceManager, pJob);
10417 
10418  /* Data Stream */
10419  case MA_JOB_LOAD_DATA_STREAM: return ma_resource_manager_process_job__load_data_stream(pResourceManager, pJob);
10420  case MA_JOB_FREE_DATA_STREAM: return ma_resource_manager_process_job__free_data_stream(pResourceManager, pJob);
10421  case MA_JOB_PAGE_DATA_STREAM: return ma_resource_manager_process_job__page_data_stream(pResourceManager, pJob);
10422  case MA_JOB_SEEK_DATA_STREAM: return ma_resource_manager_process_job__seek_data_stream(pResourceManager, pJob);
10423 
10424  default: break;
10425  }
10426 
10427  /* Getting here means we don't know what the job code is and cannot do anything with it. */
10428  return MA_INVALID_OPERATION;
10429 }
10430 
10432 {
10433  ma_result result;
10434  ma_job job;
10435 
10436  if (pResourceManager == NULL) {
10437  return MA_INVALID_ARGS;
10438  }
10439 
10440  /* This will return MA_CANCELLED if the next job is a quit job. */
10441  result = ma_resource_manager_next_job(pResourceManager, &job);
10442  if (result != MA_SUCCESS) {
10443  return result;
10444  }
10445 
10446  return ma_resource_manager_process_job(pResourceManager, &job);
10447 }
10448 
10449 
10450 
10451 
10452 
10454 {
10456 
10458  config.format = format;
10459  config.channels = channels;
10460  config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */
10461  config.pan = 0;
10462 
10463  return config;
10464 }
10465 
10466 
10467 MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner)
10468 {
10469  if (pPanner == NULL) {
10470  return MA_INVALID_ARGS;
10471  }
10472 
10473  MA_ZERO_OBJECT(pPanner);
10474 
10475  if (pConfig == NULL) {
10476  return MA_INVALID_ARGS;
10477  }
10478 
10479  pPanner->format = pConfig->format;
10480  pPanner->channels = pConfig->channels;
10481  pPanner->mode = pConfig->mode;
10482  pPanner->pan = pConfig->pan;
10483 
10484  return MA_SUCCESS;
10485 }
10486 
10487 static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
10488 {
10489  ma_uint64 iFrame;
10490 
10491  if (pan > 0) {
10492  float factor = 1.0f - pan;
10493  if (pFramesOut == pFramesIn) {
10494  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
10495  pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
10496  }
10497  } else {
10498  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
10499  pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
10500  pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1];
10501  }
10502  }
10503  } else {
10504  float factor = 1.0f + pan;
10505  if (pFramesOut == pFramesIn) {
10506  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
10507  pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
10508  }
10509  } else {
10510  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
10511  pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0];
10512  pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
10513  }
10514  }
10515  }
10516 }
10517 
10518 static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
10519 {
10520  if (pan == 0) {
10521  /* Fast path. No panning required. */
10522  if (pFramesOut == pFramesIn) {
10523  /* No-op */
10524  } else {
10525  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
10526  }
10527 
10528  return;
10529  }
10530 
10531  switch (format) {
10532  case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
10533 
10534  /* Unknown format. Just copy. */
10535  default:
10536  {
10537  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
10538  } break;
10539  }
10540 }
10541 
10542 
10543 static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
10544 {
10545  ma_uint64 iFrame;
10546 
10547  if (pan > 0) {
10548  float factorL0 = 1.0f - pan;
10549  float factorL1 = 0.0f + pan;
10550 
10551  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
10552  float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0);
10553  float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1];
10554 
10555  pFramesOut[iFrame*2 + 0] = sample0;
10556  pFramesOut[iFrame*2 + 1] = sample1;
10557  }
10558  } else {
10559  float factorR0 = 0.0f - pan;
10560  float factorR1 = 1.0f + pan;
10561 
10562  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
10563  float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0);
10564  float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1);
10565 
10566  pFramesOut[iFrame*2 + 0] = sample0;
10567  pFramesOut[iFrame*2 + 1] = sample1;
10568  }
10569  }
10570 }
10571 
10572 static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
10573 {
10574  if (pan == 0) {
10575  /* Fast path. No panning required. */
10576  if (pFramesOut == pFramesIn) {
10577  /* No-op */
10578  } else {
10579  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
10580  }
10581 
10582  return;
10583  }
10584 
10585  switch (format) {
10586  case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
10587 
10588  /* Unknown format. Just copy. */
10589  default:
10590  {
10591  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
10592  } break;
10593  }
10594 }
10595 
10596 MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
10597 {
10598  if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) {
10599  return MA_INVALID_ARGS;
10600  }
10601 
10602  if (pPanner->channels == 2) {
10603  /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */
10604  if (pPanner->mode == ma_pan_mode_balance) {
10605  ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
10606  } else {
10607  ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
10608  }
10609  } else {
10610  if (pPanner->channels == 1) {
10611  /* Panning has no effect on mono streams. */
10612  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
10613  } else {
10614  /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */
10615  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
10616  }
10617  }
10618 
10619  return MA_SUCCESS;
10620 }
10621 
10622 MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode)
10623 {
10624  if (pPanner == NULL) {
10625  return;
10626  }
10627 
10628  pPanner->mode = mode;
10629 }
10630 
10631 MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan)
10632 {
10633  if (pPanner == NULL) {
10634  return;
10635  }
10636 
10637  pPanner->pan = ma_clamp(pan, -1.0f, 1.0f);
10638 }
10639 
10640 
10641 
10642 
10644 {
10646 
10648  config.format = format;
10649  config.channels = channels;
10650  config.sampleRate = sampleRate;
10651 
10652  return config;
10653 }
10654 
10655 
10656 MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader)
10657 {
10658  if (pFader == NULL) {
10659  return MA_INVALID_ARGS;
10660  }
10661 
10662  MA_ZERO_OBJECT(pFader);
10663 
10664  if (pConfig == NULL) {
10665  return MA_INVALID_ARGS;
10666  }
10667 
10668  /* Only f32 is supported for now. */
10669  if (pConfig->format != ma_format_f32) {
10670  return MA_INVALID_ARGS;
10671  }
10672 
10673  pFader->config = *pConfig;
10674  pFader->volumeBeg = 1;
10675  pFader->volumeEnd = 1;
10676  pFader->lengthInFrames = 0;
10677  pFader->cursorInFrames = 0;
10678 
10679  return MA_SUCCESS;
10680 }
10681 
10682 MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
10683 {
10684  if (pFader == NULL) {
10685  return MA_INVALID_ARGS;
10686  }
10687 
10688  /* Optimized path if volumeBeg and volumeEnd are equal. */
10689  if (pFader->volumeBeg == pFader->volumeEnd) {
10690  if (pFader->volumeBeg == 1) {
10691  /* Straight copy. */
10692  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels);
10693  } else {
10694  /* Copy with volume. */
10695  ma_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
10696  }
10697  } else {
10698  /* Slower path. Volumes are different, so may need to do an interpolation. */
10699  if (pFader->cursorInFrames >= pFader->lengthInFrames) {
10700  /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */
10701  ma_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
10702  } else {
10703  /* Slow path. This is where we do the actual fading. */
10704  ma_uint64 iFrame;
10705  ma_uint32 iChannel;
10706 
10707  /* For now we only support f32. Support for other formats will be added later. */
10708  if (pFader->config.format == ma_format_f32) {
10709  const float* pFramesInF32 = (const float*)pFramesIn;
10710  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
10711 
10712  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
10713  float a = ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)pFader->lengthInFrames;
10714  float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a);
10715 
10716  for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) {
10717  pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume;
10718  }
10719  }
10720  } else {
10721  return MA_NOT_IMPLEMENTED;
10722  }
10723  }
10724  }
10725 
10726  pFader->cursorInFrames += frameCount;
10727 
10728  return MA_SUCCESS;
10729 }
10730 
10731 MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
10732 {
10733  if (pFader == NULL) {
10734  return;
10735  }
10736 
10737  if (pFormat != NULL) {
10738  *pFormat = pFader->config.format;
10739  }
10740 
10741  if (pChannels != NULL) {
10742  *pChannels = pFader->config.channels;
10743  }
10744 
10745  if (pSampleRate != NULL) {
10746  *pSampleRate = pFader->config.sampleRate;
10747  }
10748 }
10749 
10750 MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
10751 {
10752  if (pFader == NULL) {
10753  return;
10754  }
10755 
10756  /* If the volume is negative, use current volume. */
10757  if (volumeBeg < 0) {
10758  volumeBeg = ma_fader_get_current_volume(pFader);
10759  }
10760 
10761  pFader->volumeBeg = volumeBeg;
10762  pFader->volumeEnd = volumeEnd;
10763  pFader->lengthInFrames = lengthInFrames;
10764  pFader->cursorInFrames = 0; /* Reset cursor. */
10765 }
10766 
10768 {
10769  if (pFader == NULL) {
10770  return 0.0f;
10771  }
10772 
10773  /* The current volume depends on the position of the cursor. */
10774  if (pFader->cursorInFrames <= 0) {
10775  return pFader->volumeBeg;
10776  } else if (pFader->cursorInFrames >= pFader->lengthInFrames) {
10777  return pFader->volumeEnd;
10778  } else {
10779  /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */
10780  return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, pFader->cursorInFrames / (float)pFader->lengthInFrames);
10781  }
10782 }
10783 
10784 
10785 
10786 
10787 
10788 MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z)
10789 {
10790  ma_vec3f v;
10791 
10792  v.x = x;
10793  v.y = y;
10794  v.z = z;
10795 
10796  return v;
10797 }
10798 
10799 MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b)
10800 {
10801  return ma_vec3f_init_3f(
10802  a.x - b.x,
10803  a.y - b.y,
10804  a.z - b.z
10805  );
10806 }
10807 
10808 MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a)
10809 {
10810  return ma_vec3f_init_3f(
10811  -a.x,
10812  -a.y,
10813  -a.z
10814  );
10815 }
10816 
10817 MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b)
10818 {
10819  return a.x*b.x + a.y*b.y + a.z*b.z;
10820 }
10821 
10822 MA_API float ma_vec3f_len2(ma_vec3f v)
10823 {
10824  return ma_vec3f_dot(v, v);
10825 }
10826 
10827 MA_API float ma_vec3f_len(ma_vec3f v)
10828 {
10829  return (float)ma_sqrtd(ma_vec3f_len2(v));
10830 }
10831 
10832 MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b)
10833 {
10834  return ma_vec3f_len(ma_vec3f_sub(a, b));
10835 }
10836 
10837 MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v)
10838 {
10839  float f;
10840  float l = ma_vec3f_len(v);
10841  if (l == 0) {
10842  return ma_vec3f_init_3f(0, 0, 0);
10843  }
10844 
10845  f = 1 / l;
10846  v.x *= f;
10847  v.y *= f;
10848  v.z *= f;
10849 
10850  return v;
10851 }
10852 
10853 MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b)
10854 {
10855  return ma_vec3f_init_3f(
10856  a.y*b.z - a.z*b.y,
10857  a.z*b.x - a.x*b.z,
10858  a.x*b.y - a.y*b.x
10859  );
10860 }
10861 
10862 
10863 
10864 
10865 #ifndef MA_DEFAULT_SPEED_OF_SOUND
10866 #define MA_DEFAULT_SPEED_OF_SOUND 343.3f
10867 #endif
10868 
10869 /*
10870 These vectors represent the direction that speakers are facing from the center point. They're used
10871 for panning in the spatializer. Must be normalized.
10872 */
10873 static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = {
10874  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */
10875  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */
10876  {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */
10877  {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */
10878  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */
10879  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */
10880  {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */
10881  {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */
10882  {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */
10883  {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
10884  { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */
10885  {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */
10886  {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */
10887  { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */
10888  {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */
10889  { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */
10890  {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */
10891  {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */
10892  { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */
10893  {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */
10894  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */
10895  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */
10896  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */
10897  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */
10898  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */
10899  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */
10900  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */
10901  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */
10902  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */
10903  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */
10904  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */
10905  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */
10906  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */
10907  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */
10908  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */
10909  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */
10910  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */
10911  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */
10912  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */
10913  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */
10914  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */
10915  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */
10916  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */
10917  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */
10918  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */
10919  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */
10920  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */
10921  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */
10922  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */
10923  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */
10924  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */
10925  { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */
10926 };
10927 
10928 static ma_vec3f ma_get_channel_direction(ma_channel channel)
10929 {
10930  if (channel >= MA_CHANNEL_POSITION_COUNT) {
10931  return ma_vec3f_init_3f(0, 0, -1);
10932  } else {
10933  return g_maChannelDirections[channel];
10934  }
10935 }
10936 
10937 
10938 
10939 static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff)
10940 {
10941  if (minDistance >= maxDistance) {
10942  return 1; /* To avoid division by zero. Do not attenuate. */
10943  }
10944 
10945  return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance));
10946 }
10947 
10948 static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff)
10949 {
10950  if (minDistance >= maxDistance) {
10951  return 1; /* To avoid division by zero. Do not attenuate. */
10952  }
10953 
10954  return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance);
10955 }
10956 
10957 static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff)
10958 {
10959  if (minDistance >= maxDistance) {
10960  return 1; /* To avoid division by zero. Do not attenuate. */
10961  }
10962 
10963  return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff);
10964 }
10965 
10966 
10967 /*
10968 Dopper Effect calculation taken from the OpenAL spec, with two main differences:
10969 
10970  1) The source to listener vector will have already been calcualted at an earlier step so we can
10971  just use that directly. We need only the position of the source relative to the origin.
10972 
10973  2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight
10974  into the resampler directly.
10975 */
10976 static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor)
10977 {
10978  float len;
10979  float vls;
10980  float vss;
10981 
10982  len = ma_vec3f_len(relativePosition);
10983 
10984  /*
10985  There's a case where the position of the source will be right on top of the listener in which
10986  case the length will be 0 and we'll end up with a division by zero. We can just return a ratio
10987  of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary.
10988  */
10989  if (len == 0) {
10990  return 1.0;
10991  }
10992 
10993  vls = ma_vec3f_dot(relativePosition, listenVelocity) / len;
10994  vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len;
10995 
10996  vls = ma_min(vls, speedOfSound / dopplerFactor);
10997  vss = ma_min(vss, speedOfSound / dopplerFactor);
10998 
10999  return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss);
11000 }
11001 
11002 
11003 static void ma_get_default_channel_map_for_spatializer(ma_uint32 channelCount, ma_channel* pChannelMap)
11004 {
11005  /*
11006  Special case for stereo. Want to default the left and right speakers to side left and side
11007  right so that they're facing directly down the X axis rather than slightly forward. Not
11008  doing this will result in sounds being quieter when behind the listener. This might
11009  actually be good for some scenerios, but I don't think it's an appropriate default because
11010  it can be a bit unexpected.
11011  */
11012  if (channelCount == 2) {
11013  pChannelMap[0] = MA_CHANNEL_SIDE_LEFT;
11014  pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT;
11015  } else {
11017  }
11018 }
11019 
11020 
11022 {
11024 
11026  config.channelsOut = channelsOut;
11027  config.pChannelMapOut = NULL;
11028  config.handedness = ma_handedness_right;
11029  config.worldUp = ma_vec3f_init_3f(0, 1, 0);
11030  config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
11031  config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */
11032  config.coneOuterGain = 0;
11033  config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */
11034 
11035  return config;
11036 }
11037 
11038 
11039 typedef struct
11040 {
11041  size_t sizeInBytes;
11042  size_t channelMapOutOffset;
11043 } ma_spatializer_listener_heap_layout;
11044 
11045 static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout)
11046 {
11047  MA_ASSERT(pHeapLayout != NULL);
11048 
11049  MA_ZERO_OBJECT(pHeapLayout);
11050 
11051  if (pConfig == NULL) {
11052  return MA_INVALID_ARGS;
11053  }
11054 
11055  if (pConfig->channelsOut == 0) {
11056  return MA_INVALID_ARGS;
11057  }
11058 
11059  pHeapLayout->sizeInBytes = 0;
11060 
11061  /* Channel map. We always need this, even for passthroughs. */
11062  pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
11063  pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut);
11064 
11065  return MA_SUCCESS;
11066 }
11067 
11068 
11070 {
11071  ma_result result;
11072  ma_spatializer_listener_heap_layout heapLayout;
11073 
11074  if (pHeapSizeInBytes == NULL) {
11075  return MA_INVALID_ARGS;
11076  }
11077 
11078  *pHeapSizeInBytes = 0;
11079 
11080  result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
11081  if (result != MA_SUCCESS) {
11082  return result;
11083  }
11084 
11085  *pHeapSizeInBytes = heapLayout.sizeInBytes;
11086 
11087  return MA_SUCCESS;
11088 }
11089 
11091 {
11092  ma_result result;
11093  ma_spatializer_listener_heap_layout heapLayout;
11094 
11095  if (pListener == NULL) {
11096  return MA_INVALID_ARGS;
11097  }
11098 
11099  MA_ZERO_OBJECT(pListener);
11100 
11101  result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
11102  if (result != MA_SUCCESS) {
11103  return result;
11104  }
11105 
11106  pListener->_pHeap = pHeap;
11107  pListener->config = *pConfig;
11108  pListener->position = ma_vec3f_init_3f(0, 0, 0);
11109  pListener->direction = ma_vec3f_init_3f(0, 0, -1);
11110  pListener->velocity = ma_vec3f_init_3f(0, 0, 0);
11111 
11112  /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
11113  if (pListener->config.handedness == ma_handedness_left) {
11114  pListener->direction = ma_vec3f_neg(pListener->direction);
11115  }
11116 
11117 
11118  /* We must always have a valid channel map. */
11119  pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
11120 
11121  /* Use a slightly different default channel map for stereo. */
11122  if (pConfig->pChannelMapOut == NULL) {
11123  ma_get_default_channel_map_for_spatializer(pConfig->channelsOut, pListener->config.pChannelMapOut);
11124  } else {
11126  }
11127 
11128  return MA_SUCCESS;
11129 }
11130 
11132 {
11133  ma_result result;
11134  size_t heapSizeInBytes;
11135  void* pHeap;
11136 
11137  result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes);
11138  if (result != MA_SUCCESS) {
11139  return result;
11140  }
11141 
11142  if (heapSizeInBytes > 0) {
11143  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
11144  if (pHeap == NULL) {
11145  return MA_OUT_OF_MEMORY;
11146  }
11147  } else {
11148  pHeap = NULL;
11149  }
11150 
11151  result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener);
11152  if (result != MA_SUCCESS) {
11153  ma_free(pHeap, pAllocationCallbacks);
11154  return result;
11155  }
11156 
11157  pListener->_ownsHeap = MA_TRUE;
11158  return MA_SUCCESS;
11159 }
11160 
11161 MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks)
11162 {
11163  if (pListener == NULL) {
11164  return;
11165  }
11166 
11167  if (pListener->_pHeap != NULL && pListener->_ownsHeap) {
11168  ma_free(pListener->_pHeap, pAllocationCallbacks);
11169  }
11170 }
11171 
11173 {
11174  if (pListener == NULL) {
11175  return NULL;
11176  }
11177 
11178  return pListener->config.pChannelMapOut;
11179 }
11180 
11181 MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
11182 {
11183  if (pListener == NULL) {
11184  return;
11185  }
11186 
11187  pListener->config.coneInnerAngleInRadians = innerAngleInRadians;
11188  pListener->config.coneOuterAngleInRadians = outerAngleInRadians;
11189  pListener->config.coneOuterGain = outerGain;
11190 }
11191 
11192 MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
11193 {
11194  if (pListener == NULL) {
11195  return;
11196  }
11197 
11198  if (pInnerAngleInRadians != NULL) {
11199  *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians;
11200  }
11201 
11202  if (pOuterAngleInRadians != NULL) {
11203  *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians;
11204  }
11205 
11206  if (pOuterGain != NULL) {
11207  *pOuterGain = pListener->config.coneOuterGain;
11208  }
11209 }
11210 
11211 MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z)
11212 {
11213  if (pListener == NULL) {
11214  return;
11215  }
11216 
11217  pListener->position = ma_vec3f_init_3f(x, y, z);
11218 }
11219 
11221 {
11222  if (pListener == NULL) {
11223  return ma_vec3f_init_3f(0, 0, 0);
11224  }
11225 
11226  return pListener->position;
11227 }
11228 
11229 MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z)
11230 {
11231  if (pListener == NULL) {
11232  return;
11233  }
11234 
11235  pListener->direction = ma_vec3f_init_3f(x, y, z);
11236 }
11237 
11239 {
11240  if (pListener == NULL) {
11241  return ma_vec3f_init_3f(0, 0, -1);
11242  }
11243 
11244  return pListener->direction;
11245 }
11246 
11247 MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z)
11248 {
11249  if (pListener == NULL) {
11250  return;
11251  }
11252 
11253  pListener->velocity = ma_vec3f_init_3f(x, y, z);
11254 }
11255 
11257 {
11258  if (pListener == NULL) {
11259  return ma_vec3f_init_3f(0, 0, 0);
11260  }
11261 
11262  return pListener->velocity;
11263 }
11264 
11266 {
11267  if (pListener == NULL) {
11268  return;
11269  }
11270 
11271  pListener->config.speedOfSound = speedOfSound;
11272 }
11273 
11275 {
11276  if (pListener == NULL) {
11277  return 0;
11278  }
11279 
11280  return pListener->config.speedOfSound;
11281 }
11282 
11283 MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z)
11284 {
11285  if (pListener == NULL) {
11286  return;
11287  }
11288 
11289  pListener->config.worldUp = ma_vec3f_init_3f(x, y, z);
11290 }
11291 
11293 {
11294  if (pListener == NULL) {
11295  return ma_vec3f_init_3f(0, 1, 0);
11296  }
11297 
11298  return pListener->config.worldUp;
11299 }
11300 
11301 
11302 
11303 
11305 {
11307 
11309  config.channelsIn = channelsIn;
11310  config.channelsOut = channelsOut;
11311  config.pChannelMapIn = NULL;
11312  config.attenuationModel = ma_attenuation_model_inverse;
11313  config.positioning = ma_positioning_absolute;
11314  config.handedness = ma_handedness_right;
11315  config.minGain = 0;
11316  config.maxGain = 1;
11317  config.minDistance = 1;
11318  config.maxDistance = MA_FLT_MAX;
11319  config.rolloff = 1;
11320  config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
11321  config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */
11322  config.coneOuterGain = 0.0f;
11323  config.dopplerFactor = 1;
11324  config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */
11325 
11326  return config;
11327 }
11328 
11329 
11330 static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)
11331 {
11332  MA_ASSERT(pConfig != NULL);
11333  return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);
11334 }
11335 
11336 static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)
11337 {
11338  MA_ASSERT(pConfig != NULL);
11339 
11340  if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
11341  return MA_INVALID_ARGS;
11342  }
11343 
11344  return MA_SUCCESS;
11345 }
11346 
11347 typedef struct
11348 {
11349  size_t sizeInBytes;
11350  size_t channelMapInOffset;
11351  size_t newChannelGainsOffset;
11352  size_t gainerOffset;
11353 } ma_spatializer_heap_layout;
11354 
11355 static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)
11356 {
11357  ma_result result;
11358 
11359  MA_ASSERT(pHeapLayout != NULL);
11360 
11361  MA_ZERO_OBJECT(pHeapLayout);
11362 
11363  if (pConfig == NULL) {
11364  return MA_INVALID_ARGS;
11365  }
11366 
11367  result = ma_spatializer_validate_config(pConfig);
11368  if (result != MA_SUCCESS) {
11369  return result;
11370  }
11371 
11372  pHeapLayout->sizeInBytes = 0;
11373 
11374  /* Channel map. */
11375  pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */
11376  if (pConfig->pChannelMapIn != NULL) {
11377  pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
11378  pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);
11379  }
11380 
11381  /* New channel gains for output. */
11382  pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;
11383  pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);
11384 
11385  /* Gainer. */
11386  {
11387  size_t gainerHeapSizeInBytes;
11388  ma_gainer_config gainerConfig;
11389 
11390  gainerConfig = ma_spatializer_gainer_config_init(pConfig);
11391 
11392  result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);
11393  if (result != MA_SUCCESS) {
11394  return result;
11395  }
11396 
11397  pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
11398  pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);
11399  }
11400 
11401  return MA_SUCCESS;
11402 }
11403 
11404 MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)
11405 {
11406  ma_result result;
11407  ma_spatializer_heap_layout heapLayout;
11408 
11409  if (pHeapSizeInBytes == NULL) {
11410  return MA_INVALID_ARGS;
11411  }
11412 
11413  *pHeapSizeInBytes = 0; /* Safety. */
11414 
11415  result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
11416  if (result != MA_SUCCESS) {
11417  return result;
11418  }
11419 
11420  *pHeapSizeInBytes = heapLayout.sizeInBytes;
11421 
11422  return MA_SUCCESS;
11423 }
11424 
11425 
11426 MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer)
11427 {
11428  ma_result result;
11429  ma_spatializer_heap_layout heapLayout;
11430  ma_gainer_config gainerConfig;
11431 
11432  if (pSpatializer == NULL) {
11433  return MA_INVALID_ARGS;
11434  }
11435 
11436  MA_ZERO_OBJECT(pSpatializer);
11437 
11438  if (pConfig == NULL || pHeap == NULL) {
11439  return MA_INVALID_ARGS;
11440  }
11441 
11442  result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
11443  if (result != MA_SUCCESS) {
11444  return result;
11445  }
11446 
11447  pSpatializer->_pHeap = pHeap;
11448  pSpatializer->config = *pConfig;
11449  pSpatializer->position = ma_vec3f_init_3f(0, 0, 0);
11450  pSpatializer->direction = ma_vec3f_init_3f(0, 0, -1);
11451  pSpatializer->velocity = ma_vec3f_init_3f(0, 0, 0);
11452  pSpatializer->dopplerPitch = 1;
11453 
11454  /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
11455  if (pSpatializer->config.handedness == ma_handedness_left) {
11456  pSpatializer->direction = ma_vec3f_neg(pSpatializer->direction);
11457  }
11458 
11459  /* Channel map. This will be on the heap. */
11460  if (pConfig->pChannelMapIn != NULL) {
11461  pSpatializer->config.pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
11462  ma_channel_map_copy_or_default(pSpatializer->config.pChannelMapIn, pConfig->pChannelMapIn, pSpatializer->config.channelsIn);
11463  }
11464 
11465  /* New channel gains for output channels. */
11466  pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset);
11467 
11468  /* Gainer. */
11469  gainerConfig = ma_spatializer_gainer_config_init(pConfig);
11470 
11471  result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer);
11472  if (result != MA_SUCCESS) {
11473  return result; /* Failed to initialize the gainer. */
11474  }
11475 
11476  return MA_SUCCESS;
11477 }
11478 
11479 MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer)
11480 {
11481  ma_result result;
11482  size_t heapSizeInBytes;
11483  void* pHeap;
11484 
11485  /* We'll need a heap allocation to retrieve the size. */
11486  result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes);
11487  if (result != MA_SUCCESS) {
11488  return result;
11489  }
11490 
11491  if (heapSizeInBytes > 0) {
11492  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
11493  if (pHeap == NULL) {
11494  return MA_OUT_OF_MEMORY;
11495  }
11496  } else {
11497  pHeap = NULL;
11498  }
11499 
11500  result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer);
11501  if (result != MA_SUCCESS) {
11502  ma_free(pHeap, pAllocationCallbacks);
11503  return result;
11504  }
11505 
11506  pSpatializer->_ownsHeap = MA_TRUE;
11507  return MA_SUCCESS;
11508 }
11509 
11510 MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks)
11511 {
11512  if (pSpatializer == NULL) {
11513  return;
11514  }
11515 
11516  ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks);
11517 
11518  if (pSpatializer->_pHeap != NULL && pSpatializer->_ownsHeap) {
11519  ma_free(pSpatializer->_pHeap, pAllocationCallbacks);
11520  }
11521 }
11522 
11523 static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain)
11524 {
11525  /*
11526  Angular attenuation.
11527 
11528  Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
11529  this out for ourselves at the expense of possibly being inconsistent with other implementations.
11530 
11531  To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
11532  just need to get the direction from the source to the listener and then do a dot product against that and the
11533  direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
11534  angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
11535  the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
11536  */
11537  if (coneInnerAngleInRadians < 6.283185f) {
11538  float angularGain = 1;
11539  float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f);
11540  float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f);
11541  float d;
11542 
11543  d = ma_vec3f_dot(dirA, dirB);
11544 
11545  if (d > cutoffInner) {
11546  /* It's inside the inner angle. */
11547  angularGain = 1;
11548  } else {
11549  /* It's outside the inner angle. */
11550  if (d > cutoffOuter) {
11551  /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */
11552  angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter));
11553  } else {
11554  /* It's outside the outer angle. */
11555  angularGain = coneOuterGain;
11556  }
11557  }
11558 
11559  /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/
11560  return angularGain;
11561  } else {
11562  /* Inner angle is 360 degrees so no need to do any attenuation. */
11563  return 1;
11564  }
11565 }
11566 
11567 MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
11568 {
11569  ma_channel* pChannelMapIn = pSpatializer->config.pChannelMapIn;
11570  ma_channel* pChannelMapOut = pListener->config.pChannelMapOut;
11571 
11572  if (pSpatializer == NULL) {
11573  return MA_INVALID_ARGS;
11574  }
11575 
11576  /* If we're not spatializing we need to run an optimized path. */
11577  if (pSpatializer->config.attenuationModel == ma_attenuation_model_none) {
11578  /* No attenuation is required, but we'll need to do some channel conversion. */
11579  if (pSpatializer->config.channelsIn == pSpatializer->config.channelsOut) {
11580  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->config.channelsIn);
11581  } else {
11582  ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->config.channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->config.channelsIn, frameCount, ma_channel_mix_mode_rectangular); /* Safe casts to float* because f32 is the only supported format. */
11583  }
11584 
11585  /*
11586  We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is
11587  the correct thinking so might need to review this later.
11588  */
11589  pSpatializer->dopplerPitch = 1;
11590  } else {
11591  /*
11592  Let's first determine which listener the sound is closest to. Need to keep in mind that we
11593  might not have a world or any listeners, in which case we just spatializer based on the
11594  listener being positioned at the origin (0, 0, 0).
11595  */
11596  ma_vec3f relativePosNormalized;
11597  ma_vec3f relativePos; /* The position relative to the listener. */
11598  ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */
11599  ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */
11600  float speedOfSound;
11601  float distance = 0;
11602  float gain = 1;
11603  ma_uint32 iChannel;
11604  const ma_uint32 channelsOut = pSpatializer->config.channelsOut;
11605  const ma_uint32 channelsIn = pSpatializer->config.channelsIn;
11606 
11607  /*
11608  We'll need the listener velocity for doppler pitch calculations. The speed of sound is
11609  defined by the listener, so we'll grab that here too.
11610  */
11611  if (pListener != NULL) {
11612  listenerVel = pListener->velocity;
11613  speedOfSound = pListener->config.speedOfSound;
11614  } else {
11615  listenerVel = ma_vec3f_init_3f(0, 0, 0);
11616  speedOfSound = MA_DEFAULT_SPEED_OF_SOUND;
11617  }
11618 
11619  if (pListener == NULL || pSpatializer->config.positioning == ma_positioning_relative) {
11620  /* There's no listener or we're using relative positioning. */
11621  relativePos = pSpatializer->position;
11622  relativeDir = pSpatializer->direction;
11623  } else {
11624  /*
11625  We've found a listener and we're using absolute positioning. We need to transform the
11626  sound's position and direction so that it's relative to listener. Later on we'll use
11627  this for determining the factors to apply to each channel to apply the panning effect.
11628  */
11629  ma_vec3f v;
11630  ma_vec3f axisX;
11631  ma_vec3f axisY;
11632  ma_vec3f axisZ;
11633  float m[4][4];
11634 
11635  /*
11636  We need to calcualte the right vector from our forward and up vectors. This is done with
11637  a cross product.
11638  */
11639  axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */
11640  axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */
11641 
11642  /*
11643  The calculation of axisX above can result in a zero-length vector if the listener is
11644  looking straight up on the Y axis. We'll need to fall back to a +X in this case so that
11645  the calculations below don't fall apart. This is where a quaternion based listener and
11646  sound orientation would come in handy.
11647  */
11648  if (ma_vec3f_len2(axisX) == 0) {
11649  axisX = ma_vec3f_init_3f(1, 0, 0);
11650  }
11651 
11652  axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */
11653 
11654  /*
11655  We need to swap the X axis if we're left handed because otherwise the cross product above
11656  will have resulted in it pointing in the wrong direction (right handed was assumed in the
11657  cross products above).
11658  */
11659  if (pListener->config.handedness == ma_handedness_left) {
11660  axisX = ma_vec3f_neg(axisX);
11661  }
11662 
11663  /* Lookat. */
11664  m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position);
11665  m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position);
11666  m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), pListener->position);
11667  m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1;
11668 
11669  /*
11670  Multiply the lookat matrix by the spatializer position to transform it to listener
11671  space. This allows calculations to work based on the sound being relative to the
11672  origin which makes things simpler.
11673  */
11674  v = pSpatializer->position;
11675  relativePos.x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1;
11676  relativePos.y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1;
11677  relativePos.z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1;
11678 
11679  /*
11680  The direction of the sound needs to also be transformed so that it's relative to the
11681  rotation of the listener.
11682  */
11683  v = pSpatializer->direction;
11684  relativeDir.x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z;
11685  relativeDir.y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z;
11686  relativeDir.z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z;
11687  }
11688 
11689  distance = ma_vec3f_len(relativePos);
11690 
11691  /* We've gathered the data, so now we can apply some spatialization. */
11692  switch (pSpatializer->config.attenuationModel) {
11694  {
11695  gain = ma_attenuation_inverse(distance, pSpatializer->config.minDistance, pSpatializer->config.maxDistance, pSpatializer->config.rolloff);
11696  } break;
11698  {
11699  gain = ma_attenuation_linear(distance, pSpatializer->config.minDistance, pSpatializer->config.maxDistance, pSpatializer->config.rolloff);
11700  } break;
11702  {
11703  gain = ma_attenuation_exponential(distance, pSpatializer->config.minDistance, pSpatializer->config.maxDistance, pSpatializer->config.rolloff);
11704  } break;
11706  default:
11707  {
11708  gain = 1;
11709  } break;
11710  }
11711 
11712  /* Normalize the position. */
11713  if (distance > 0.001f) {
11714  float distanceInv = 1/distance;
11715  relativePosNormalized = relativePos;
11716  relativePosNormalized.x *= distanceInv;
11717  relativePosNormalized.y *= distanceInv;
11718  relativePosNormalized.z *= distanceInv;
11719  } else {
11720  distance = 0;
11721  relativePosNormalized = ma_vec3f_init_3f(0, 0, 0);
11722  }
11723 
11724  /*
11725  Angular attenuation.
11726 
11727  Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
11728  this out for ourselves at the expense of possibly being inconsistent with other implementations.
11729 
11730  To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
11731  just need to get the direction from the source to the listener and then do a dot product against that and the
11732  direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
11733  angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
11734  the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
11735  */
11736  if (distance > 0) {
11737  /* Source anglular gain. */
11738  gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), pSpatializer->config.coneInnerAngleInRadians, pSpatializer->config.coneOuterAngleInRadians, pSpatializer->config.coneOuterGain);
11739 
11740  /*
11741  We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that
11742  are positioned behind the listener. On default settings, this will have no effect.
11743  */
11744  if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) {
11745  ma_vec3f listenerDirection;
11746  float listenerInnerAngle;
11747  float listenerOuterAngle;
11748  float listenerOuterGain;
11749 
11750  if (pListener->config.handedness == ma_handedness_right) {
11751  listenerDirection = ma_vec3f_init_3f(0, 0, -1);
11752  } else {
11753  listenerDirection = ma_vec3f_init_3f(0, 0, +1);
11754  }
11755 
11756  listenerInnerAngle = pListener->config.coneInnerAngleInRadians;
11757  listenerOuterAngle = pListener->config.coneOuterAngleInRadians;
11758  listenerOuterGain = pListener->config.coneOuterGain;
11759 
11760  gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain);
11761  }
11762  } else {
11763  /* The sound is right on top of the listener. Don't do any angular attenuation. */
11764  }
11765 
11766 
11767  /* Clamp the gain. */
11768  gain = ma_clamp(gain, pSpatializer->config.minGain, pSpatializer->config.maxGain);
11769 
11770  /*
11771  Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for
11772  when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the
11773  gain to the final output.
11774  */
11775  /*printf("distance=%f; gain=%f\n", distance, gain);*/
11776 
11777  /* We must have a valid channel map here to ensure we spatialize properly. */
11778  MA_ASSERT(pChannelMapOut != NULL);
11779 
11780  /*
11781  We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being
11782  to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that
11783  the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and
11784  seeing how it goes. There might be better ways to do this.
11785 
11786  To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a
11787  direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will
11788  be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized
11789  position of the sound.
11790  */
11791  for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
11792  pSpatializer->pNewChannelGainsOut[iChannel] = gain;
11793  }
11794 
11795  /* Convert to our output channel count. */
11796  ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular);
11797 
11798  /*
11799  Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's
11800  relation to the direction of the channel.
11801  */
11802  if (distance > 0) {
11803  ma_vec3f unitPos = relativePos;
11804  float distanceInv = 1/distance;
11805  unitPos.x *= distanceInv;
11806  unitPos.y *= distanceInv;
11807  unitPos.z *= distanceInv;
11808 
11809  for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
11810  ma_channel channelOut;
11811  float d;
11812 
11813  channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel);
11814  if (ma_is_spatial_channel_position(channelOut)) {
11815  d = ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut));
11816  } else {
11817  d = 1; /* It's not a spatial channel so there's no real notion of direction. */
11818  }
11819 
11820  /*
11821  In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable.
11822  The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to
11823  0, panning will be most extreme and any sounds that are positioned on the opposite side of the
11824  speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it
11825  doesn't even remotely represent the real world at all because sounds that come from your right side
11826  are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at
11827  all, which is also not ideal. By setting it to something greater than 0, the spatialization effect
11828  becomes much less dramatic and a lot more bearable.
11829 
11830  Summary: 0 = more extreme panning; 1 = no panning.
11831  */
11832  float dMin = 0.2f; /* TODO: Consider making this configurable. */
11833 
11834  /*
11835  At this point, "d" will be positive if the sound is on the same side as the channel and negative if
11836  it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to
11837  calculate a panning value. The first is to simply convert it to 0..1, however this has a problem
11838  which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right
11839  in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like
11840  the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front
11841  of the listener. I would intuitively expect that to be played at full volume, or close to it.
11842 
11843  The second idea I think of is to only apply a reduction in gain when the sound is on the opposite
11844  side of the speaker. That is, reduce the gain only when the dot product is negative. The problem
11845  with this is that there will not be any attenuation as the sound sweeps around the 180 degrees
11846  where the dot product is positive. The idea with this option is that you leave the gain at 1 when
11847  the sound is being played on the same side as the speaker and then you just reduce the volume when
11848  the sound is on the other side.
11849 
11850  The summarize, I think the first option should give a better sense of spatialization, but the second
11851  option is better for preserving the sound's power.
11852 
11853  UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a
11854  bit better, but you can also hear the reduction in volume when it's right in front.
11855  */
11856  #if 1
11857  {
11858  /*
11859  Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power
11860  by being played at 0.5 gain.
11861  */
11862  d = (d + 1) * 0.5f; /* -1..1 to 0..1 */
11863  d = ma_max(d, dMin);
11864  pSpatializer->pNewChannelGainsOut[iChannel] *= d;
11865  }
11866  #else
11867  {
11868  /*
11869  Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more
11870  consistent, but comes at the expense of a worse sense of space and positioning.
11871  */
11872  if (d < 0) {
11873  d += 1; /* Move into the positive range. */
11874  d = ma_max(d, dMin);
11875  channelGainsOut[iChannel] *= d;
11876  }
11877  }
11878  #endif
11879  }
11880  } else {
11881  /* Assume the sound is right on top of us. Don't do any panning. */
11882  }
11883 
11884  /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */
11885  ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut);
11886  ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount);
11887 
11888  /*
11889  Before leaving we'll want to update our doppler pitch so that the caller can apply some
11890  pitch shifting if they desire. Note that we need to negate the relative position here
11891  because the doppler calculation needs to be source-to-listener, but ours is listener-to-
11892  source.
11893  */
11894  if (pSpatializer->config.dopplerFactor > 0) {
11895  pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(pListener->position, pSpatializer->position), pSpatializer->velocity, listenerVel, speedOfSound, pSpatializer->config.dopplerFactor);
11896  } else {
11897  pSpatializer->dopplerPitch = 1;
11898  }
11899  }
11900 
11901  return MA_SUCCESS;
11902 }
11903 
11905 {
11906  if (pSpatializer == NULL) {
11907  return 0;
11908  }
11909 
11910  return pSpatializer->config.channelsIn;
11911 }
11912 
11914 {
11915  if (pSpatializer == NULL) {
11916  return 0;
11917  }
11918 
11919  return pSpatializer->config.channelsOut;
11920 }
11921 
11923 {
11924  if (pSpatializer == NULL) {
11925  return;
11926  }
11927 
11928  pSpatializer->config.attenuationModel = attenuationModel;
11929 }
11930 
11932 {
11933  if (pSpatializer == NULL) {
11935  }
11936 
11937  return pSpatializer->config.attenuationModel;
11938 }
11939 
11940 MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning)
11941 {
11942  if (pSpatializer == NULL) {
11943  return;
11944  }
11945 
11946  pSpatializer->config.positioning = positioning;
11947 }
11948 
11950 {
11951  if (pSpatializer == NULL) {
11952  return ma_positioning_absolute;
11953  }
11954 
11955  return pSpatializer->config.positioning;
11956 }
11957 
11958 MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff)
11959 {
11960  if (pSpatializer == NULL) {
11961  return;
11962  }
11963 
11964  pSpatializer->config.rolloff = rolloff;
11965 }
11966 
11967 MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer)
11968 {
11969  if (pSpatializer == NULL) {
11970  return 0;
11971  }
11972 
11973  return pSpatializer->config.rolloff;
11974 }
11975 
11976 MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain)
11977 {
11978  if (pSpatializer == NULL) {
11979  return;
11980  }
11981 
11982  pSpatializer->config.minGain = minGain;
11983 }
11984 
11985 MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer)
11986 {
11987  if (pSpatializer == NULL) {
11988  return 0;
11989  }
11990 
11991  return pSpatializer->config.minGain;
11992 }
11993 
11994 MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain)
11995 {
11996  if (pSpatializer == NULL) {
11997  return;
11998  }
11999 
12000  pSpatializer->config.maxGain = maxGain;
12001 }
12002 
12003 MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer)
12004 {
12005  if (pSpatializer == NULL) {
12006  return 0;
12007  }
12008 
12009  return pSpatializer->config.maxGain;
12010 }
12011 
12012 MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance)
12013 {
12014  if (pSpatializer == NULL) {
12015  return;
12016  }
12017 
12018  pSpatializer->config.minDistance = minDistance;
12019 }
12020 
12021 MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer)
12022 {
12023  if (pSpatializer == NULL) {
12024  return 0;
12025  }
12026 
12027  return pSpatializer->config.minDistance;
12028 }
12029 
12030 MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance)
12031 {
12032  if (pSpatializer == NULL) {
12033  return;
12034  }
12035 
12036  pSpatializer->config.maxDistance = maxDistance;
12037 }
12038 
12039 MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer)
12040 {
12041  if (pSpatializer == NULL) {
12042  return 0;
12043  }
12044 
12045  return pSpatializer->config.maxDistance;
12046 }
12047 
12048 MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
12049 {
12050  if (pSpatializer == NULL) {
12051  return;
12052  }
12053 
12054  pSpatializer->config.coneInnerAngleInRadians = innerAngleInRadians;
12055  pSpatializer->config.coneOuterAngleInRadians = outerAngleInRadians;
12056  pSpatializer->config.coneOuterGain = outerGain;
12057 }
12058 
12059 MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
12060 {
12061  if (pSpatializer == NULL) {
12062  return;
12063  }
12064 
12065  if (pInnerAngleInRadians != NULL) {
12066  *pInnerAngleInRadians = pSpatializer->config.coneInnerAngleInRadians;
12067  }
12068 
12069  if (pOuterAngleInRadians != NULL) {
12070  *pOuterAngleInRadians = pSpatializer->config.coneOuterAngleInRadians;
12071  }
12072 
12073  if (pOuterGain != NULL) {
12074  *pOuterGain = pSpatializer->config.coneOuterGain;
12075  }
12076 }
12077 
12078 MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor)
12079 {
12080  if (pSpatializer == NULL) {
12081  return;
12082  }
12083 
12084  pSpatializer->config.dopplerFactor = dopplerFactor;
12085 }
12086 
12087 MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer)
12088 {
12089  if (pSpatializer == NULL) {
12090  return 1;
12091  }
12092 
12093  return pSpatializer->config.dopplerFactor;
12094 }
12095 
12096 MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z)
12097 {
12098  if (pSpatializer == NULL) {
12099  return;
12100  }
12101 
12102  pSpatializer->position = ma_vec3f_init_3f(x, y, z);
12103 }
12104 
12106 {
12107  if (pSpatializer == NULL) {
12108  return ma_vec3f_init_3f(0, 0, 0);
12109  }
12110 
12111  return pSpatializer->position;
12112 }
12113 
12114 MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z)
12115 {
12116  if (pSpatializer == NULL) {
12117  return;
12118  }
12119 
12120  pSpatializer->direction = ma_vec3f_init_3f(x, y, z);
12121 }
12122 
12124 {
12125  if (pSpatializer == NULL) {
12126  return ma_vec3f_init_3f(0, 0, -1);
12127  }
12128 
12129  return pSpatializer->direction;
12130 }
12131 
12132 MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z)
12133 {
12134  if (pSpatializer == NULL) {
12135  return;
12136  }
12137 
12138  pSpatializer->velocity = ma_vec3f_init_3f(x, y, z);
12139 }
12140 
12142 {
12143  if (pSpatializer == NULL) {
12144  return ma_vec3f_init_3f(0, 0, 0);
12145  }
12146 
12147  return pSpatializer->velocity;
12148 }
12149 
12150 
12151 
12152 
12153 /**************************************************************************************************************************************************************
12154 
12155 Engine
12156 
12157 **************************************************************************************************************************************************************/
12158 #define MA_SEEK_TARGET_NONE (~(ma_uint64)0)
12159 
12161 {
12163 
12165  config.pEngine = pEngine;
12166  config.type = type;
12167  config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0;
12168  config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0;
12169 
12170  return config;
12171 }
12172 
12173 
12174 static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode)
12175 {
12176  ma_bool32 isUpdateRequired = MA_FALSE;
12177  float newPitch;
12178 
12179  MA_ASSERT(pEngineNode != NULL);
12180 
12182 
12183  if (pEngineNode->oldPitch != newPitch) {
12184  pEngineNode->oldPitch = newPitch;
12185  isUpdateRequired = MA_TRUE;
12186  }
12187 
12188  if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) {
12189  pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch;
12190  isUpdateRequired = MA_TRUE;
12191  }
12192 
12193  if (isUpdateRequired) {
12194  float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine);
12195  ma_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch);
12196  }
12197 }
12198 
12199 static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode)
12200 {
12201  MA_ASSERT(pEngineNode != NULL);
12202 
12203  /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */
12205 }
12206 
12207 static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode)
12208 {
12209  MA_ASSERT(pEngineNode != NULL);
12210 
12212 }
12213 
12214 static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount)
12215 {
12216  if (ma_engine_node_is_pitching_enabled(pEngineNode)) {
12217  return ma_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount);
12218  } else {
12219  return outputFrameCount; /* No resampling, so 1:1. */
12220  }
12221 }
12222 
12223 static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
12224 {
12225  ma_uint32 frameCountIn;
12226  ma_uint32 frameCountOut;
12227  ma_uint32 totalFramesProcessedIn;
12228  ma_uint32 totalFramesProcessedOut;
12229  ma_uint32 channelsIn;
12230  ma_uint32 channelsOut;
12231  ma_bool32 isPitchingEnabled;
12232  ma_bool32 isFadingEnabled;
12233  ma_bool32 isSpatializationEnabled;
12234  ma_bool32 isPanningEnabled;
12235 
12236  frameCountIn = *pFrameCountIn;
12237  frameCountOut = *pFrameCountOut;
12238 
12239  channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer);
12240  channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer);
12241 
12242  totalFramesProcessedIn = 0;
12243  totalFramesProcessedOut = 0;
12244 
12245  isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode);
12246  isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1;
12247  isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode);
12248  isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1;
12249 
12250  /* Keep going while we've still got data available for processing. */
12251  while (totalFramesProcessedOut < frameCountOut) {
12252  /*
12253  We need to process in a specific order. We always do resampling first because it's likely
12254  we're going to be increasing the channel count after spatialization. Also, I want to do
12255  fading based on the output sample rate.
12256 
12257  We'll first read into a buffer from the resampler. Then we'll do all processing that
12258  operates on the on the input channel count. We'll then get the spatializer to output to
12259  the output buffer and then do all effects from that point directly in the output buffer
12260  in-place.
12261 
12262  Note that we're always running the resampler. If we try to be clever and skip resampling
12263  when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then
12264  away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler
12265  itself.
12266 
12267  There's a small optimization here that we'll utilize since it might be a fairly common
12268  case. When the input and output channel counts are the same, we'll read straight into the
12269  output buffer from the resampler and do everything in-place.
12270  */
12271  const float* pRunningFramesIn;
12272  float* pRunningFramesOut;
12273  float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */
12274  float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
12275  ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn;
12276  ma_uint32 framesAvailableIn;
12277  ma_uint32 framesAvailableOut;
12278  ma_uint32 framesJustProcessedIn;
12279  ma_uint32 framesJustProcessedOut;
12280  ma_bool32 isWorkingBufferValid = MA_FALSE;
12281 
12282  framesAvailableIn = frameCountIn - totalFramesProcessedIn;
12283  framesAvailableOut = frameCountOut - totalFramesProcessedOut;
12284 
12285  pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn);
12286  pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut);
12287 
12288  if (channelsIn == channelsOut) {
12289  /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */
12290  pWorkingBuffer = pRunningFramesOut;
12291  } else {
12292  /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */
12293  pWorkingBuffer = temp;
12294  if (framesAvailableOut > tempCapInFrames) {
12295  framesAvailableOut = tempCapInFrames;
12296  }
12297  }
12298 
12299  /* First is resampler. */
12300  if (isPitchingEnabled) {
12301  ma_uint64 resampleFrameCountIn = framesAvailableIn;
12302  ma_uint64 resampleFrameCountOut = framesAvailableOut;
12303 
12304  ma_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut);
12305  isWorkingBufferValid = MA_TRUE;
12306 
12307  framesJustProcessedIn = (ma_uint32)resampleFrameCountIn;
12308  framesJustProcessedOut = (ma_uint32)resampleFrameCountOut;
12309  } else {
12310  framesJustProcessedIn = framesAvailableIn;
12311  framesJustProcessedOut = framesAvailableOut;
12312  }
12313 
12314  /* Fading. */
12315  if (isFadingEnabled) {
12316  if (isWorkingBufferValid) {
12317  ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */
12318  } else {
12319  ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
12320  isWorkingBufferValid = MA_TRUE;
12321  }
12322  }
12323 
12324  /*
12325  If at this point we still haven't actually done anything with the working buffer we need
12326  to just read straight from the input buffer.
12327  */
12328  if (isWorkingBufferValid == MA_FALSE) {
12329  pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */
12330  }
12331 
12332  /* Spatialization. */
12333  if (isSpatializationEnabled) {
12334  ma_uint32 iListener;
12335 
12336  /*
12337  When determining the listener to use, we first check to see if the sound is pinned to a
12338  specific listener. If so, we use that. Otherwise we just use the closest listener.
12339  */
12340  if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) {
12341  iListener = pEngineNode->pinnedListenerIndex;
12342  } else {
12343  iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, pEngineNode->spatializer.position.x, pEngineNode->spatializer.position.y, pEngineNode->spatializer.position.z);
12344  }
12345 
12346  ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut);
12347  } else {
12348  /* No spatialization, but we still need to do channel conversion. */
12349  if (channelsIn == channelsOut) {
12350  /* No channel conversion required. Just copy straight to the output buffer. */
12351  ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut);
12352  } else {
12353  /* Channel conversion required. TODO: Add support for channel maps here. */
12354  ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple);
12355  }
12356  }
12357 
12358  /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */
12359 
12360  /* Panning. */
12361  if (isPanningEnabled) {
12362  ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */
12363  }
12364 
12365  /* We're done for this chunk. */
12366  totalFramesProcessedIn += framesJustProcessedIn;
12367  totalFramesProcessedOut += framesJustProcessedOut;
12368 
12369  /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */
12370  if (framesJustProcessedOut == 0) {
12371  break;
12372  }
12373  }
12374 
12375  /* At this point we're done processing. */
12376  *pFrameCountIn = totalFramesProcessedIn;
12377  *pFrameCountOut = totalFramesProcessedOut;
12378 }
12379 
12380 static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
12381 {
12382  /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
12383  ma_result result = MA_SUCCESS;
12384  ma_sound* pSound = (ma_sound*)pNode;
12385  ma_uint32 frameCount = *pFrameCountOut;
12386  ma_uint32 totalFramesRead = 0;
12387  ma_format dataSourceFormat;
12388  ma_uint32 dataSourceChannels;
12390  ma_uint32 tempCapInFrames;
12391 
12392  /* This is a data source node which means no input buses. */
12393  (void)ppFramesIn;
12394  (void)pFrameCountIn;
12395 
12396  /* If we're marked at the end we need to stop the sound and do nothing. */
12397  if (ma_sound_at_end(pSound)) {
12398  ma_sound_stop(pSound);
12399  *pFrameCountOut = 0;
12400  return;
12401  }
12402 
12403  /* If we're seeking, do so now before reading. */
12404  if (pSound->seekTarget != MA_SEEK_TARGET_NONE) {
12406 
12407  /* Any time-dependant effects need to have their times updated. */
12408  ma_node_set_time(pSound, pSound->seekTarget);
12409 
12410  pSound->seekTarget = MA_SEEK_TARGET_NONE;
12411  }
12412 
12413  /*
12414  We want to update the pitch once. For sounds, this can be either at the start or at the end. If
12415  we don't force this to only ever be updating once, we could end up in a situation where
12416  retrieving the required input frame count ends up being different to what we actually retrieve.
12417  What could happen is that the required input frame count is calculated, the pitch is update,
12418  and then this processing function is called resulting in a different number of input frames
12419  being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else
12420  you'll hit the aforementioned bug.
12421  */
12422  ma_engine_node_update_pitch_if_required(&pSound->engineNode);
12423 
12424  /*
12425  For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
12426  from the main engine.
12427  */
12428  result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL);
12429  if (result == MA_SUCCESS) {
12430  tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
12431 
12432  /* Keep reading until we've read as much as was requested or we reach the end of the data source. */
12433  while (totalFramesRead < frameCount) {
12434  ma_uint32 framesRemaining = frameCount - totalFramesRead;
12435  ma_uint32 framesToRead;
12436  ma_uint64 framesJustRead;
12437  ma_uint32 frameCountIn;
12438  ma_uint32 frameCountOut;
12439  const float* pRunningFramesIn;
12440  float* pRunningFramesOut;
12441 
12442  /*
12443  The first thing we need to do is read into the temporary buffer. We can calculate exactly
12444  how many input frames we'll need after resampling.
12445  */
12446  framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);
12447  if (framesToRead > tempCapInFrames) {
12448  framesToRead = tempCapInFrames;
12449  }
12450 
12451  result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead, ma_sound_is_looping(pSound));
12452 
12453  /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */
12454  if (result == MA_AT_END) {
12455  c89atomic_exchange_8(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */
12456  }
12457 
12458  pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));
12459 
12460  frameCountIn = (ma_uint32)framesJustRead;
12461  frameCountOut = framesRemaining;
12462 
12463  /* Convert if necessary. */
12464  if (dataSourceFormat == ma_format_f32) {
12465  /* Fast path. No data conversion necessary. */
12466  pRunningFramesIn = (float*)temp;
12467  ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
12468  } else {
12469  /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */
12470  float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */
12471  ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
12472 
12473  /* Now that we have our samples in f32 format we can process like normal. */
12474  pRunningFramesIn = tempf32;
12475  ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
12476  }
12477 
12478  /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */
12479  MA_ASSERT(frameCountIn == framesJustRead);
12480  totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
12481 
12482  if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
12483  break; /* Might have reached the end. */
12484  }
12485  }
12486  }
12487 
12488  *pFrameCountOut = totalFramesRead;
12489 }
12490 
12491 static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
12492 {
12493  /*
12494  Make sure the pitch is updated before trying to read anything. It's important that this is done
12495  only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that
12496  ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),
12497  and if another thread modifies the pitch just after that call it can result in a glitch due to
12498  the input rate changing.
12499  */
12500  ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
12501 
12502  /* For groups, the input data has already been read and we just need to apply the effect. */
12503  ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
12504 }
12505 
12506 static ma_uint32 ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount)
12507 {
12508  ma_uint64 result;
12509 
12510  /* Our pitch will affect this calculation. We need to update it. */
12511  ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
12512 
12513  result = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
12514  if (result > 0xFFFFFFFF) {
12515  result = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
12516  }
12517 
12518  return (ma_uint32)result;
12519 }
12520 
12521 
12522 static ma_node_vtable g_ma_engine_node_vtable__sound =
12523 {
12524  ma_engine_node_process_pcm_frames__sound,
12525  NULL, /* onGetRequiredInputFrameCount */
12526  0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
12527  1, /* Sounds have one output bus. */
12528  0 /* Default flags. */
12529 };
12530 
12531 static ma_node_vtable g_ma_engine_node_vtable__group =
12532 {
12533  ma_engine_node_process_pcm_frames__group,
12534  ma_engine_node_get_required_input_frame_count__group,
12535  1, /* Groups have one input bus. */
12536  1, /* Groups have one output bus. */
12537  MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
12538 };
12539 
12540 
12541 
12542 static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)
12543 {
12544  ma_node_config baseNodeConfig;
12545 
12546  if (pConfig->type == ma_engine_node_type_sound) {
12547  /* Sound. */
12548  baseNodeConfig = ma_node_config_init();
12549  baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound;
12550  baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */
12551  } else {
12552  /* Group. */
12553  baseNodeConfig = ma_node_config_init();
12554  baseNodeConfig.vtable = &g_ma_engine_node_vtable__group;
12555  baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */
12556  }
12557 
12558  return baseNodeConfig;
12559 }
12560 
12561 static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig)
12562 {
12563  return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]);
12564 }
12565 
12566 typedef struct
12567 {
12568  size_t sizeInBytes;
12569  size_t baseNodeOffset;
12570  size_t spatializerOffset;
12571 } ma_engine_node_heap_layout;
12572 
12573 static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout)
12574 {
12575  ma_result result;
12576  size_t tempHeapSize;
12577  ma_node_config baseNodeConfig;
12578  ma_spatializer_config spatializerConfig;
12579  ma_uint32 channelsIn;
12580  ma_uint32 channelsOut;
12581 
12582  MA_ASSERT(pHeapLayout);
12583 
12584  MA_ZERO_OBJECT(pHeapLayout);
12585 
12586  if (pConfig == NULL) {
12587  return MA_INVALID_ARGS;
12588  }
12589 
12590  if (pConfig->pEngine == NULL) {
12591  return MA_INVALID_ARGS; /* An engine must be specified. */
12592  }
12593 
12594  pHeapLayout->sizeInBytes = 0;
12595 
12596  channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
12597  channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
12598 
12599 
12600  /* Base node. */
12601  baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
12602  baseNodeConfig.pInputChannels = &channelsIn;
12603  baseNodeConfig.pOutputChannels = &channelsOut;
12604 
12605  result = ma_node_get_heap_size(&baseNodeConfig, &tempHeapSize);
12606  if (result != MA_SUCCESS) {
12607  return result; /* Failed to retrieve the size of the heap for the base node. */
12608  }
12609 
12610  pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;
12611  pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
12612 
12613 
12614  /* Spatializer. */
12615  spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
12616 
12617  result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize);
12618  if (result != MA_SUCCESS) {
12619  return result; /* Failed to retrieve the size of the heap for the spatializer. */
12620  }
12621 
12622  pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes;
12623  pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
12624 
12625 
12626  return MA_SUCCESS;
12627 }
12628 
12629 MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes)
12630 {
12631  ma_result result;
12632  ma_engine_node_heap_layout heapLayout;
12633 
12634  if (pHeapSizeInBytes == NULL) {
12635  return MA_INVALID_ARGS;
12636  }
12637 
12638  *pHeapSizeInBytes = 0;
12639 
12640  result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
12641  if (result != MA_SUCCESS) {
12642  return result;
12643  }
12644 
12645  *pHeapSizeInBytes = heapLayout.sizeInBytes;
12646 
12647  return MA_SUCCESS;
12648 }
12649 
12651 {
12652  ma_result result;
12653  ma_engine_node_heap_layout heapLayout;
12654  ma_node_config baseNodeConfig;
12655  ma_resampler_config resamplerConfig;
12656  ma_fader_config faderConfig;
12657  ma_spatializer_config spatializerConfig;
12658  ma_panner_config pannerConfig;
12659  ma_uint32 channelsIn;
12660  ma_uint32 channelsOut;
12661 
12662  if (pEngineNode == NULL) {
12663  return MA_INVALID_ARGS;
12664  }
12665 
12666  MA_ZERO_OBJECT(pEngineNode);
12667 
12668  result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
12669  if (result != MA_SUCCESS) {
12670  return result;
12671  }
12672 
12674  return MA_INVALID_ARGS; /* Invalid listener. */
12675  }
12676 
12677  pEngineNode->_pHeap = pHeap;
12678  pEngineNode->pEngine = pConfig->pEngine;
12679  pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine);
12680  pEngineNode->pitch = 1;
12681  pEngineNode->oldPitch = 1;
12682  pEngineNode->oldDopplerPitch = 1;
12683  pEngineNode->isPitchDisabled = pConfig->isPitchDisabled;
12684  pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled;
12685  pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex;
12686 
12687 
12688  channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
12689  channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
12690 
12691 
12692  /* Base node. */
12693  baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
12694  baseNodeConfig.pInputChannels = &channelsIn;
12695  baseNodeConfig.pOutputChannels = &channelsOut;
12696 
12697  result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);
12698  if (result != MA_SUCCESS) {
12699  goto error0;
12700  }
12701 
12702 
12703  /*
12704  We can now initialize the effects we need in order to implement the engine node. There's a
12705  defined order of operations here, mainly centered around when we convert our channels from the
12706  data source's native channel count to the engine's channel count. As a rule, we want to do as
12707  much computation as possible before spatialization because there's a chance that will increase
12708  the channel count, thereby increasing the amount of work needing to be done to process.
12709  */
12710 
12711  /* We'll always do resampling first. */
12712  resamplerConfig = ma_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine), ma_resample_algorithm_linear);
12713  resamplerConfig.linear.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */
12714 
12715  result = ma_resampler_init(&resamplerConfig, &pEngineNode->resampler);
12716  if (result != MA_SUCCESS) {
12717  goto error1;
12718  }
12719 
12720 
12721  /* After resampling will come the fader. */
12722  faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine));
12723 
12724  result = ma_fader_init(&faderConfig, &pEngineNode->fader);
12725  if (result != MA_SUCCESS) {
12726  goto error2;
12727  }
12728 
12729 
12730  /*
12731  Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to
12732  ensure channels counts link up correctly in the node graph.
12733  */
12734  spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
12735  spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames;
12736 
12737  result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer);
12738  if (result != MA_SUCCESS) {
12739  goto error2;
12740  }
12741 
12742 
12743  /*
12744  After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't
12745  be able to pan mono sounds.
12746  */
12747  pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]);
12748 
12749  result = ma_panner_init(&pannerConfig, &pEngineNode->panner);
12750  if (result != MA_SUCCESS) {
12751  goto error3;
12752  }
12753 
12754  return MA_SUCCESS;
12755 
12756 error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); /* <-- No need for allocation callbacks here because we use a preallocated heap. */
12757 error2: ma_resampler_uninit(&pEngineNode->resampler);
12758 error1: ma_node_uninit(&pEngineNode->baseNode, NULL); /* <-- No need for allocation callbacks here because we use a preallocated heap. */
12759 error0: return result;
12760 }
12761 
12762 MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
12763 {
12764  ma_result result;
12765  size_t heapSizeInBytes;
12766  void* pHeap;
12767 
12768  result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes);
12769  if (result != MA_SUCCESS) {
12770  return result;
12771  }
12772 
12773  if (heapSizeInBytes > 0) {
12774  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
12775  if (pHeap == NULL) {
12776  return MA_OUT_OF_MEMORY;
12777  }
12778  } else {
12779  pHeap = NULL;
12780  }
12781 
12782  result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode);
12783  if (result != MA_SUCCESS) {
12784  ma_free(pHeap, pAllocationCallbacks);
12785  return result;
12786  }
12787 
12788  pEngineNode->_ownsHeap = MA_TRUE;
12789  return MA_SUCCESS;
12790 }
12791 
12792 MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks)
12793 {
12794  /*
12795  The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we
12796  destroy anything that might be in the middle of being used by the processing function.
12797  */
12798  ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks);
12799 
12800  /* Now that the node has been uninitialized we can safely uninitialize the rest. */
12801  ma_spatializer_uninit(&pEngineNode->spatializer, NULL);
12802  ma_resampler_uninit(&pEngineNode->resampler);
12803 
12804  /* Free the heap last. */
12805  if (pEngineNode->_pHeap != NULL && pEngineNode->_ownsHeap) {
12806  ma_free(pEngineNode->_pHeap, pAllocationCallbacks);
12807  }
12808 }
12809 
12810 
12812 {
12814 
12816 
12817  return config;
12818 }
12819 
12821 {
12823 
12825 
12826  return config;
12827 }
12828 
12829 
12831 {
12833 
12835  config.listenerCount = 1; /* Always want at least one listener. */
12836 
12837  return config;
12838 }
12839 
12840 
12841 static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
12842 {
12843  ma_engine* pEngine = (ma_engine*)pDevice->pUserData;
12844 
12845  /*
12846  Experiment: Try processing a resource manager job if we're on the Emscripten build.
12847 
12848  This serves two purposes:
12849 
12850  1) It ensures jobs are actually processed at some point since we cannot guarantee that the
12851  caller is doing the right thing and calling ma_resource_manager_process_next_job(); and
12852 
12853  2) It's an attempt at working around an issue where processing jobs on the Emscripten main
12854  loop doesn't work as well as it should. When trying to load sounds without the `DECODE`
12855  flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time
12856  before the callback is processed. I think it's got something to do with the single-
12857  threaded nature of Web, but I'm not entirely sure.
12858  */
12859  #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)
12860  {
12861  if (pEngine->pResourceManager != NULL) {
12864  }
12865  }
12866  }
12867  #endif
12868 
12869  ma_engine_data_callback(pEngine, pFramesOut, pFramesIn, frameCount);
12870 }
12871 
12872 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)
12873 {
12874  ma_result result;
12875  ma_node_graph_config nodeGraphConfig;
12876  ma_engine_config engineConfig;
12877  ma_spatializer_listener_config listenerConfig;
12878  ma_uint32 iListener;
12879 
12880  if (pEngine != NULL) {
12881  MA_ZERO_OBJECT(pEngine);
12882  }
12883 
12884  /* The config is allowed to be NULL in which case we use defaults for everything. */
12885  if (pConfig != NULL) {
12886  engineConfig = *pConfig;
12887  } else {
12888  engineConfig = ma_engine_config_init();
12889  }
12890 
12891  pEngine->pResourceManager = engineConfig.pResourceManager;
12892  pEngine->pDevice = engineConfig.pDevice;
12894 
12895  /* If we don't have a device, we need one. */
12896  if (pEngine->pDevice == NULL) {
12897  ma_device_config deviceConfig;
12898 
12899  pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_CONTEXT*/);
12900  if (pEngine->pDevice == NULL) {
12901  return MA_OUT_OF_MEMORY;
12902  }
12903 
12905  deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID;
12906  deviceConfig.playback.format = ma_format_f32;
12907  deviceConfig.playback.channels = engineConfig.channels;
12908  deviceConfig.sampleRate = engineConfig.sampleRate;
12909  deviceConfig.dataCallback = ma_engine_data_callback_internal;
12910  deviceConfig.pUserData = pEngine;
12911  deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames;
12912  deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds;
12913  deviceConfig.noPreZeroedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */
12914  deviceConfig.noClip = MA_TRUE; /* The mixing engine will do clipping itself. */
12915 
12916  if (engineConfig.pContext == NULL) {
12917  ma_context_config contextConfig = ma_context_config_init();
12918  contextConfig.allocationCallbacks = pEngine->allocationCallbacks;
12919  contextConfig.pLog = engineConfig.pLog;
12920 
12921  /* If the engine config does not specify a log, use the resource manager's if we have one. */
12922  #ifndef MA_NO_RESOURCE_MANAGER
12923  {
12924  if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) {
12925  contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager);
12926  }
12927  }
12928  #endif
12929 
12930  result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice);
12931  } else {
12932  result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice);
12933  }
12934 
12935  if (result != MA_SUCCESS) {
12936  ma_free(pEngine->pDevice, &pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_CONTEXT*/);
12937  pEngine->pDevice = NULL;
12938  return result;
12939  }
12940 
12941  pEngine->ownsDevice = MA_TRUE;
12942  }
12943 
12944  /*
12945  The engine always uses either the log that was passed into the config, or the context's log. Either
12946  way, the engine never has ownership of the log.
12947  */
12948  if (engineConfig.pLog != NULL) {
12949  pEngine->pLog = engineConfig.pLog;
12950  } else {
12951  pEngine->pLog = ma_device_get_log(pEngine->pDevice);
12952  }
12953 
12954 
12955  /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */
12956  nodeGraphConfig = ma_node_graph_config_init(pEngine->pDevice->playback.channels);
12957 
12958  result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);
12959  if (result != MA_SUCCESS) {
12960  goto on_error_1;
12961  }
12962 
12963 
12964  /* We need at least one listener. */
12965  if (engineConfig.listenerCount == 0) {
12966  engineConfig.listenerCount = 1;
12967  }
12968 
12969  if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) {
12970  result = MA_INVALID_ARGS; /* Too many listeners. */
12971  goto on_error_1;
12972  }
12973 
12974  for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) {
12975  listenerConfig = ma_spatializer_listener_config_init(pEngine->pDevice->playback.channels);
12976 
12977  result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */
12978  if (result != MA_SUCCESS) {
12979  goto on_error_2;
12980  }
12981 
12982  pEngine->listenerCount += 1;
12983  }
12984 
12985 
12986  /* Gain smoothing for spatialized sounds. */
12987  pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;
12988  if (pEngine->gainSmoothTimeInFrames == 0) {
12989  ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;
12990  if (gainSmoothTimeInMilliseconds == 0) {
12991  gainSmoothTimeInMilliseconds = 8;
12992  }
12993 
12994  pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */
12995  }
12996 
12997 
12998  /* We need a resource manager. */
12999  #ifndef MA_NO_RESOURCE_MANAGER
13000  {
13001  if (pEngine->pResourceManager == NULL) {
13002  ma_resource_manager_config resourceManagerConfig;
13003 
13004  pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);
13005  if (pEngine->pResourceManager == NULL) {
13006  result = MA_OUT_OF_MEMORY;
13007  goto on_error_2;
13008  }
13009 
13010  resourceManagerConfig = ma_resource_manager_config_init();
13011  resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */
13012  resourceManagerConfig.decodedFormat = ma_format_f32;
13013  resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */
13014  resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);
13016  resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS;
13017 
13018  /* The Emscripten build cannot use threads. */
13019  #if defined(MA_EMSCRIPTEN)
13020  {
13021  resourceManagerConfig.jobThreadCount = 0;
13022  resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
13023  }
13024  #endif
13025 
13026  result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);
13027  if (result != MA_SUCCESS) {
13028  goto on_error_3;
13029  }
13030 
13031  pEngine->ownsResourceManager = MA_TRUE;
13032  }
13033  }
13034  #endif
13035 
13036  /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */
13037  ma_mutex_init(&pEngine->inlinedSoundLock);
13038  pEngine->pInlinedSoundHead = NULL;
13039 
13040  /* Start the engine if required. This should always be the last step. */
13041  if (engineConfig.noAutoStart == MA_FALSE) {
13042  result = ma_engine_start(pEngine);
13043  if (result != MA_SUCCESS) {
13044  goto on_error_4; /* Failed to start the engine. */
13045  }
13046  }
13047 
13048  return MA_SUCCESS;
13049 
13050 on_error_4:
13051  ma_mutex_uninit(&pEngine->inlinedSoundLock);
13052 #ifndef MA_NO_RESOURCE_MANAGER
13053 on_error_3:
13054  if (pEngine->ownsResourceManager) {
13055  ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
13056  }
13057 #endif /* MA_NO_RESOURCE_MANAGER */
13058 on_error_2:
13059  for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
13060  ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
13061  }
13062 
13063  ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
13064 on_error_1:
13065  if (pEngine->ownsDevice) {
13066  ma_device_uninit(pEngine->pDevice);
13067  ma_free(pEngine->pDevice, &pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_CONTEXT*/);
13068  }
13069 
13070  return result;
13071 }
13072 
13073 MA_API void ma_engine_uninit(ma_engine* pEngine)
13074 {
13075  if (pEngine == NULL) {
13076  return;
13077  }
13078 
13079  /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */
13080  if (pEngine->ownsDevice) {
13081  ma_device_uninit(pEngine->pDevice);
13082  ma_free(pEngine->pDevice, &pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_CONTEXT*/);
13083  } else {
13084  ma_device_stop(pEngine->pDevice);
13085  }
13086 
13087  /*
13088  All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case
13089  I want to do some kind of garbage collection later on.
13090  */
13091  ma_mutex_lock(&pEngine->inlinedSoundLock);
13092  {
13093  for (;;) {
13094  ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;
13095  if (pSoundToDelete == NULL) {
13096  break; /* Done. */
13097  }
13098 
13099  pEngine->pInlinedSoundHead = pSoundToDelete->pNext;
13100 
13101  ma_sound_uninit(&pSoundToDelete->sound);
13102  ma_free(pSoundToDelete, &pEngine->allocationCallbacks);
13103  }
13104  }
13105  ma_mutex_unlock(&pEngine->inlinedSoundLock);
13106  ma_mutex_uninit(&pEngine->inlinedSoundLock);
13107 
13108 
13109  /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */
13110  ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
13111 
13112  /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */
13113 #ifndef MA_NO_RESOURCE_MANAGER
13114  if (pEngine->ownsResourceManager) {
13116  ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
13117  }
13118 #endif
13119 }
13120 
13121 MA_API void ma_engine_data_callback(ma_engine* pEngine, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
13122 {
13123  (void)pFramesIn; /* Unused. */
13124 
13125  ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, NULL);
13126 }
13127 
13129 {
13130  if (pEngine == NULL) {
13131  return NULL;
13132  }
13133 
13134  return pEngine->pDevice;
13135 }
13136 
13138 {
13139  if (pEngine == NULL) {
13140  return NULL;
13141  }
13142 
13143  if (pEngine->pLog != NULL) {
13144  return pEngine->pLog;
13145  } else {
13146  return ma_device_get_log(ma_engine_get_device(pEngine));
13147  }
13148 }
13149 
13151 {
13152  return ma_node_graph_get_endpoint(&pEngine->nodeGraph);
13153 }
13154 
13156 {
13157  return ma_node_graph_get_time(&pEngine->nodeGraph);
13158 }
13159 
13161 {
13162  return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime);
13163 }
13164 
13166 {
13167  return ma_node_graph_get_channels(&pEngine->nodeGraph);
13168 }
13169 
13171 {
13172  if (pEngine == NULL) {
13173  return 0;
13174  }
13175 
13176  if (pEngine->pDevice != NULL) {
13177  return pEngine->pDevice->sampleRate;
13178  } else {
13179  return 0; /* No device. */
13180  }
13181 }
13182 
13183 
13185 {
13186  ma_result result;
13187 
13188  if (pEngine == NULL) {
13189  return MA_INVALID_ARGS;
13190  }
13191 
13192  result = ma_device_start(pEngine->pDevice);
13193  if (result != MA_SUCCESS) {
13194  return result;
13195  }
13196 
13197  return MA_SUCCESS;
13198 }
13199 
13201 {
13202  ma_result result;
13203 
13204  if (pEngine == NULL) {
13205  return MA_INVALID_ARGS;
13206  }
13207 
13208  result = ma_device_stop(pEngine->pDevice);
13209  if (result != MA_SUCCESS) {
13210  return result;
13211  }
13212 
13213  return MA_SUCCESS;
13214 }
13215 
13216 MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume)
13217 {
13218  if (pEngine == NULL) {
13219  return MA_INVALID_ARGS;
13220  }
13221 
13222  return ma_device_set_master_volume(pEngine->pDevice, volume);
13223 }
13224 
13225 MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB)
13226 {
13227  if (pEngine == NULL) {
13228  return MA_INVALID_ARGS;
13229  }
13230 
13231  return ma_device_set_master_gain_db(pEngine->pDevice, gainDB);
13232 }
13233 
13234 
13236 {
13237  if (pEngine == NULL) {
13238  return 0;
13239  }
13240 
13241  return pEngine->listenerCount;
13242 }
13243 
13244 MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
13245 {
13246  ma_uint32 iListener;
13247  ma_uint32 iListenerClosest;
13248  float closestLen2 = MA_FLT_MAX;
13249 
13250  if (pEngine == NULL || pEngine->listenerCount == 1) {
13251  return 0;
13252  }
13253 
13254  iListenerClosest = 0;
13255  for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
13256  float len2 = ma_vec3f_len2(ma_vec3f_sub(pEngine->listeners[iListener].position, ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ)));
13257  if (closestLen2 > len2) {
13258  closestLen2 = len2;
13259  iListenerClosest = iListener;
13260  }
13261  }
13262 
13263  MA_ASSERT(iListenerClosest < 255);
13264  return iListenerClosest;
13265 }
13266 
13267 MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
13268 {
13269  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13270  return;
13271  }
13272 
13273  ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z);
13274 }
13275 
13276 MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex)
13277 {
13278  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13279  return ma_vec3f_init_3f(0, 0, 0);
13280  }
13281 
13282  return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]);
13283 }
13284 
13285 MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
13286 {
13287  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13288  return;
13289  }
13290 
13291  ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z);
13292 }
13293 
13295 {
13296  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13297  return ma_vec3f_init_3f(0, 0, -1);
13298  }
13299 
13300  return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]);
13301 }
13302 
13303 MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
13304 {
13305  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13306  return;
13307  }
13308 
13309  ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z);
13310 }
13311 
13312 MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex)
13313 {
13314  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13315  return ma_vec3f_init_3f(0, 0, 0);
13316  }
13317 
13318  return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]);
13319 }
13320 
13321 MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
13322 {
13323  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13324  return;
13325  }
13326 
13327  ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain);
13328 }
13329 
13330 MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
13331 {
13332  if (pInnerAngleInRadians != NULL) {
13333  *pInnerAngleInRadians = 0;
13334  }
13335 
13336  if (pOuterAngleInRadians != NULL) {
13337  *pOuterAngleInRadians = 0;
13338  }
13339 
13340  if (pOuterGain != NULL) {
13341  *pOuterGain = 0;
13342  }
13343 
13344  ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
13345 }
13346 
13347 MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
13348 {
13349  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13350  return;
13351  }
13352 
13353  ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z);
13354 }
13355 
13356 MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex)
13357 {
13358  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
13359  return ma_vec3f_init_3f(0, 1, 0);
13360  }
13361 
13362  return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]);
13363 }
13364 
13365 
13366 #ifndef MA_NO_RESOURCE_MANAGER
13367 MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex)
13368 {
13369  ma_result result = MA_SUCCESS;
13370  ma_sound_inlined* pSound = NULL;
13371  ma_sound_inlined* pNextSound = NULL;
13372 
13373  if (pEngine == NULL || pFilePath == NULL) {
13374  return MA_INVALID_ARGS;
13375  }
13376 
13377  /* Attach to the endpoint node if nothing is specicied. */
13378  if (pNode == NULL) {
13379  pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);
13380  nodeInputBusIndex = 0;
13381  }
13382 
13383  /*
13384  We want to check if we can recycle an already-allocated inlined sound. Since this is just a
13385  helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep
13386  the implementation simple. Maybe this can be optimized later if there's enough demand, but
13387  if this function is being used it probably means the caller doesn't really care too much.
13388 
13389  What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise
13390  we just keep iterating. If we reach the end without finding a sound to recycle we just
13391  allocate a new one. This doesn't scale well for a massive number of sounds being played
13392  simultaneously as we don't ever actually free the sound objects. Some kind of garbage
13393  collection routine might be valuable for this which I'll think about.
13394  */
13395  ma_mutex_lock(&pEngine->inlinedSoundLock);
13396  {
13397  ma_uint32 soundFlags = 0;
13398 
13399  for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) {
13400  if (ma_sound_at_end(&pNextSound->sound)) {
13401  /*
13402  The sound is at the end which means it's available for recycling. All we need to do
13403  is uninitialize it and reinitialize it. All we're doing is recycling memory.
13404  */
13405  pSound = pNextSound;
13407  break;
13408  }
13409  }
13410 
13411  if (pSound != NULL) {
13412  /*
13413  We actually want to detach the sound from the list here. The reason is because we want the sound
13414  to be in a consistent state at the non-recycled case to simplify the logic below.
13415  */
13416  if (pEngine->pInlinedSoundHead == pSound) {
13417  pEngine->pInlinedSoundHead = pSound->pNext;
13418  }
13419 
13420  if (pSound->pPrev != NULL) {
13421  pSound->pPrev->pNext = pSound->pNext;
13422  }
13423  if (pSound->pNext != NULL) {
13424  pSound->pNext->pPrev = pSound->pPrev;
13425  }
13426 
13427  /* Now the previous sound needs to be uninitialized. */
13428  ma_sound_uninit(&pNextSound->sound);
13429  } else {
13430  /* No sound available for recycling. Allocate one now. */
13431  pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks);
13432  }
13433 
13434  if (pSound != NULL) { /* Safety check for the allocation above. */
13435  /*
13436  At this point we should have memory allocated for the inlined sound. We just need
13437  to initialize it like a normal sound now.
13438  */
13439  soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */
13440  soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */
13441  soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */
13442  soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */
13443 
13444  result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);
13445  if (result == MA_SUCCESS) {
13446  /* Now attach the sound to the graph. */
13447  result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);
13448  if (result == MA_SUCCESS) {
13449  /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */
13450  pSound->pNext = pEngine->pInlinedSoundHead;
13451  pSound->pPrev = NULL;
13452 
13453  pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */
13454  if (pSound->pNext != NULL) {
13455  pSound->pNext->pPrev = pSound;
13456  }
13457  } else {
13458  ma_free(pSound, &pEngine->allocationCallbacks);
13459  }
13460  } else {
13461  ma_free(pSound, &pEngine->allocationCallbacks);
13462  }
13463  } else {
13464  result = MA_OUT_OF_MEMORY;
13465  }
13466  }
13467  ma_mutex_unlock(&pEngine->inlinedSoundLock);
13468 
13469  if (result != MA_SUCCESS) {
13470  return result;
13471  }
13472 
13473  /* Finally we can start playing the sound. */
13474  result = ma_sound_start(&pSound->sound);
13475  if (result != MA_SUCCESS) {
13476  /* Failed to start the sound. We need to mark it for recycling and return an error. */
13478  return result;
13479  }
13480 
13482  return result;
13483 }
13484 
13485 MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
13486 {
13487  return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0);
13488 }
13489 #endif
13490 
13491 
13492 static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound)
13493 {
13494  if (pSound == NULL) {
13495  return MA_INVALID_ARGS;
13496  }
13497 
13498  MA_ZERO_OBJECT(pSound);
13499  pSound->seekTarget = MA_SEEK_TARGET_NONE;
13500 
13501  if (pEngine == NULL) {
13502  return MA_INVALID_ARGS;
13503  }
13504 
13505  return MA_SUCCESS;
13506 }
13507 
13508 static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
13509 {
13510  ma_result result;
13511  ma_engine_node_config engineNodeConfig;
13512  ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */
13513 
13514  /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */
13515  MA_ASSERT(pEngine != NULL);
13516  MA_ASSERT(pSound != NULL);
13517 
13518  if (pConfig == NULL) {
13519  return MA_INVALID_ARGS;
13520  }
13521 
13522  pSound->pDataSource = pConfig->pDataSource;
13523 
13524  if (pConfig->pDataSource != NULL) {
13526  } else {
13528  }
13529 
13530  /*
13531  Sounds are engine nodes. Before we can initialize this we need to determine the channel count.
13532  If we can't do this we need to abort. It's up to the caller to ensure they're using a data
13533  source that provides this information upfront.
13534  */
13535  engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags);
13536  engineNodeConfig.channelsIn = pConfig->channelsIn;
13537  engineNodeConfig.channelsOut = pConfig->channelsOut;
13538 
13539  /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */
13540  if (pConfig->pDataSource != NULL) {
13541  result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate);
13542  if (result != MA_SUCCESS) {
13543  return result; /* Failed to retrieve the channel count. */
13544  }
13545 
13546  if (engineNodeConfig.channelsIn == 0) {
13547  return MA_INVALID_OPERATION; /* Invalid channel count. */
13548  }
13549  }
13550 
13551 
13552  /* Getting here means we should have a valid channel count and we can initialize the engine node. */
13553  result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode);
13554  if (result != MA_SUCCESS) {
13555  return result;
13556  }
13557 
13558  /* If no attachment is specified, attach the sound straight to the endpoint. */
13559  if (pConfig->pInitialAttachment == NULL) {
13560  /* No group. Attach straight to the endpoint by default, unless the caller has requested that do not. */
13561  if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) {
13562  result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
13563  }
13564  } else {
13565  /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */
13566  result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex);
13567  }
13568 
13569  if (result != MA_SUCCESS) {
13571  return result;
13572  }
13573 
13574  return MA_SUCCESS;
13575 }
13576 
13577 #ifndef MA_NO_RESOURCE_MANAGER
13578 MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
13579 {
13580  ma_result result = MA_SUCCESS;
13581  ma_uint32 flags;
13583  ma_pipeline_notifications notifications;
13584 
13585  /*
13586  The engine requires knowledge of the channel count of the underlying data source before it can
13587  initialize the sound. Therefore, we need to make the resource manager wait until initialization
13588  of the underlying data source to be initialized so we can get access to the channel count. To
13589  do this, the MA_DATA_SOURCE_FLAG_WAIT_INIT is forced.
13590 
13591  Because we're initializing the data source before the sound, there's a chance the notification
13592  will get triggered before this function returns. This is OK, so long as the caller is aware of
13593  it and can avoid accessing the sound from within the notification.
13594  */
13595  flags = pConfig->flags | MA_DATA_SOURCE_FLAG_WAIT_INIT;
13596 
13598  if (pSound->pResourceManagerDataSource == NULL) {
13599  return MA_OUT_OF_MEMORY;
13600  }
13601 
13602  notifications = ma_pipeline_notifications_init();
13603  notifications.done.pFence = pConfig->pDoneFence;
13604 
13605  /*
13606  We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does
13607  not return prematurely before the sound has finished initializing.
13608  */
13609  if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }
13610  {
13611  if (pConfig->pFilePath != NULL) {
13612  result = ma_resource_manager_data_source_init(pEngine->pResourceManager, pConfig->pFilePath, flags, &notifications, pSound->pResourceManagerDataSource);
13613  } else {
13614  result = ma_resource_manager_data_source_init_w(pEngine->pResourceManager, pConfig->pFilePathW, flags, &notifications, pSound->pResourceManagerDataSource);
13615  }
13616 
13617  if (result != MA_SUCCESS) {
13618  goto done;
13619  }
13620 
13621  pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */
13622 
13623  /* We need to use a slightly customized version of the config so we'll need to make a copy. */
13624  config = *pConfig;
13625  config.pFilePath = NULL;
13626  config.pFilePathW = NULL;
13627  config.pDataSource = pSound->pResourceManagerDataSource;
13628 
13629  result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
13630  if (result != MA_SUCCESS) {
13633  MA_ZERO_OBJECT(pSound);
13634  goto done;
13635  }
13636  }
13637 done:
13638  if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }
13639  return result;
13640 }
13641 
13642 MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)
13643 {
13645  config.pFilePath = pFilePath;
13646  config.flags = flags;
13647  config.pInitialAttachment = pGroup;
13648  config.pDoneFence = pDoneFence;
13649  return ma_sound_init_ex(pEngine, &config, pSound);
13650 }
13651 
13652 MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)
13653 {
13655  config.pFilePathW = pFilePath;
13656  config.flags = flags;
13657  config.pInitialAttachment = pGroup;
13658  config.pDoneFence = pDoneFence;
13659  return ma_sound_init_ex(pEngine, &config, pSound);
13660 }
13661 
13662 MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)
13663 {
13664  ma_result result;
13666 
13667  result = ma_sound_preinit(pEngine, pSound);
13668  if (result != MA_SUCCESS) {
13669  return result;
13670  }
13671 
13672  if (pExistingSound == NULL) {
13673  return MA_INVALID_ARGS;
13674  }
13675 
13676  /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */
13677  if (pExistingSound->pResourceManagerDataSource == NULL) {
13678  return MA_INVALID_OPERATION;
13679  }
13680 
13681  /*
13682  We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream)
13683  the this will fail.
13684  */
13686  if (pSound->pResourceManagerDataSource == NULL) {
13687  return MA_OUT_OF_MEMORY;
13688  }
13689 
13691  if (result != MA_SUCCESS) {
13693  return result;
13694  }
13695 
13697  config.pDataSource = pSound->pResourceManagerDataSource;
13698  config.flags = flags;
13699  config.pInitialAttachment = pGroup;
13700 
13701  result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
13702  if (result != MA_SUCCESS) {
13705  MA_ZERO_OBJECT(pSound);
13706  return result;
13707  }
13708 
13709  return MA_SUCCESS;
13710 }
13711 #endif
13712 
13714 {
13716  config.pDataSource = pDataSource;
13717  config.flags = flags;
13718  config.pInitialAttachment = pGroup;
13719  return ma_sound_init_ex(pEngine, &config, pSound);
13720 }
13721 
13722 MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
13723 {
13724  ma_result result;
13725 
13726  result = ma_sound_preinit(pEngine, pSound);
13727  if (result != MA_SUCCESS) {
13728  return result;
13729  }
13730 
13731  if (pConfig == NULL) {
13732  return MA_INVALID_ARGS;
13733  }
13734 
13735  /* We need to load the sound differently depending on whether or not we're loading from a file. */
13736 #ifndef MA_NO_RESOURCE_MANAGER
13737  if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {
13738  return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);
13739  } else
13740 #endif
13741  {
13742  /*
13743  Getting here means we're not loading from a file. We may be loading from an already-initialized
13744  data source, or none at all. If we aren't specifying any data source, we'll be initializing the
13745  the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this
13746  for us, so no special treatment required here.
13747  */
13748  return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);
13749  }
13750 }
13751 
13752 MA_API void ma_sound_uninit(ma_sound* pSound)
13753 {
13754  if (pSound == NULL) {
13755  return;
13756  }
13757 
13758  /*
13759  Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done
13760  so which makes thread safety beyond this point trivial.
13761  */
13763 
13764  /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */
13765 #ifndef MA_NO_RESOURCE_MANAGER
13766  if (pSound->ownsDataSource) {
13769  pSound->pDataSource = NULL;
13770  }
13771 #else
13772  MA_ASSERT(pSound->ownsDataSource == MA_FALSE);
13773 #endif
13774 }
13775 
13777 {
13778  if (pSound == NULL) {
13779  return NULL;
13780  }
13781 
13782  return pSound->engineNode.pEngine;
13783 }
13784 
13786 {
13787  if (pSound == NULL) {
13788  return NULL;
13789  }
13790 
13791  return pSound->pDataSource;
13792 }
13793 
13795 {
13796  if (pSound == NULL) {
13797  return MA_INVALID_ARGS;
13798  }
13799 
13800  /* If the sound is already playing, do nothing. */
13801  if (ma_sound_is_playing(pSound)) {
13802  return MA_SUCCESS;
13803  }
13804 
13805  /* If the sound is at the end it means we want to start from the start again. */
13806  if (ma_sound_at_end(pSound)) {
13808  if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {
13809  return result; /* Failed to seek back to the start. */
13810  }
13811 
13812  /* Make sure we clear the end indicator. */
13813  c89atomic_exchange_8(&pSound->atEnd, MA_FALSE);
13814  }
13815 
13816  /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */
13818 
13819  return MA_SUCCESS;
13820 }
13821 
13823 {
13824  if (pSound == NULL) {
13825  return MA_INVALID_ARGS;
13826  }
13827 
13828  /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */
13830 
13831  return MA_SUCCESS;
13832 }
13833 
13834 MA_API ma_result ma_sound_set_volume(ma_sound* pSound, float volume)
13835 {
13836  if (pSound == NULL) {
13837  return MA_INVALID_ARGS;
13838  }
13839 
13840  /* The volume is controlled via the output bus. */
13841  ma_node_set_output_bus_volume(pSound, 0, volume);
13842 
13843  return MA_SUCCESS;
13844 }
13845 
13846 MA_API ma_result ma_sound_set_gain_db(ma_sound* pSound, float gainDB)
13847 {
13848  return ma_sound_set_volume(pSound, ma_gain_db_to_factor(gainDB));
13849 }
13850 
13851 MA_API void ma_sound_set_pan(ma_sound* pSound, float pan)
13852 {
13853  if (pSound == NULL) {
13854  return;
13855  }
13856 
13857  ma_panner_set_pan(&pSound->engineNode.panner, pan);
13858 }
13859 
13860 MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode)
13861 {
13862  if (pSound == NULL) {
13863  return;
13864  }
13865 
13866  ma_panner_set_mode(&pSound->engineNode.panner, panMode);
13867 }
13868 
13869 MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch)
13870 {
13871  if (pSound == NULL) {
13872  return;
13873  }
13874 
13876 }
13877 
13879 {
13880  if (pSound == NULL) {
13881  return;
13882  }
13883 
13885 }
13886 
13887 MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint8 listenerIndex)
13888 {
13889  if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) {
13890  return;
13891  }
13892 
13894 }
13895 
13897 {
13898  if (pSound == NULL) {
13900  }
13901 
13903 }
13904 
13905 MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z)
13906 {
13907  if (pSound == NULL) {
13908  return;
13909  }
13910 
13912 }
13913 
13915 {
13916  if (pSound == NULL) {
13917  return ma_vec3f_init_3f(0, 0, 0);
13918  }
13919 
13921 }
13922 
13923 MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z)
13924 {
13925  if (pSound == NULL) {
13926  return;
13927  }
13928 
13930 }
13931 
13933 {
13934  if (pSound == NULL) {
13935  return ma_vec3f_init_3f(0, 0, 0);
13936  }
13937 
13939 }
13940 
13941 MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z)
13942 {
13943  if (pSound == NULL) {
13944  return;
13945  }
13946 
13948 }
13949 
13951 {
13952  if (pSound == NULL) {
13953  return ma_vec3f_init_3f(0, 0, 0);
13954  }
13955 
13957 }
13958 
13960 {
13961  if (pSound == NULL) {
13962  return;
13963  }
13964 
13965  ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel);
13966 }
13967 
13969 {
13970  if (pSound == NULL) {
13972  }
13973 
13975 }
13976 
13977 MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning)
13978 {
13979  if (pSound == NULL) {
13980  return;
13981  }
13982 
13983  ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning);
13984 }
13985 
13987 {
13988  if (pSound == NULL) {
13989  return ma_positioning_absolute;
13990  }
13991 
13993 }
13994 
13995 MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff)
13996 {
13997  if (pSound == NULL) {
13998  return;
13999  }
14000 
14002 }
14003 
14004 MA_API float ma_sound_get_rolloff(const ma_sound* pSound)
14005 {
14006  if (pSound == NULL) {
14007  return 0;
14008  }
14009 
14011 }
14012 
14013 MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain)
14014 {
14015  if (pSound == NULL) {
14016  return;
14017  }
14018 
14020 }
14021 
14022 MA_API float ma_sound_get_min_gain(const ma_sound* pSound)
14023 {
14024  if (pSound == NULL) {
14025  return 0;
14026  }
14027 
14029 }
14030 
14031 MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain)
14032 {
14033  if (pSound == NULL) {
14034  return;
14035  }
14036 
14038 }
14039 
14040 MA_API float ma_sound_get_max_gain(const ma_sound* pSound)
14041 {
14042  if (pSound == NULL) {
14043  return 0;
14044  }
14045 
14047 }
14048 
14049 MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance)
14050 {
14051  if (pSound == NULL) {
14052  return;
14053  }
14054 
14056 }
14057 
14058 MA_API float ma_sound_get_min_distance(const ma_sound* pSound)
14059 {
14060  if (pSound == NULL) {
14061  return 0;
14062  }
14063 
14065 }
14066 
14067 MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance)
14068 {
14069  if (pSound == NULL) {
14070  return;
14071  }
14072 
14074 }
14075 
14076 MA_API float ma_sound_get_max_distance(const ma_sound* pSound)
14077 {
14078  if (pSound == NULL) {
14079  return 0;
14080  }
14081 
14083 }
14084 
14085 MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
14086 {
14087  if (pSound == NULL) {
14088  return;
14089  }
14090 
14091  ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain);
14092 }
14093 
14094 MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
14095 {
14096  if (pInnerAngleInRadians != NULL) {
14097  *pInnerAngleInRadians = 0;
14098  }
14099 
14100  if (pOuterAngleInRadians != NULL) {
14101  *pOuterAngleInRadians = 0;
14102  }
14103 
14104  if (pOuterGain != NULL) {
14105  *pOuterGain = 0;
14106  }
14107 
14108  ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
14109 }
14110 
14111 MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor)
14112 {
14113  if (pSound == NULL) {
14114  return;
14115  }
14116 
14117  ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor);
14118 }
14119 
14120 MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound)
14121 {
14122  if (pSound == NULL) {
14123  return 0;
14124  }
14125 
14127 }
14128 
14129 
14130 MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
14131 {
14132  if (pSound == NULL) {
14133  return;
14134  }
14135 
14136  ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames);
14137 }
14138 
14139 MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
14140 {
14141  if (pSound == NULL) {
14142  return;
14143  }
14144 
14145  ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000);
14146 }
14147 
14149 {
14150  if (pSound == NULL) {
14151  return MA_INVALID_ARGS;
14152  }
14153 
14154  return ma_fader_get_current_volume(&pSound->engineNode.fader);
14155 }
14156 
14157 MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
14158 {
14159  if (pSound == NULL) {
14160  return;
14161  }
14162 
14163  ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames);
14164 }
14165 
14166 MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
14167 {
14168  if (pSound == NULL) {
14169  return;
14170  }
14171 
14172  ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
14173 }
14174 
14175 MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
14176 {
14177  if (pSound == NULL) {
14178  return;
14179  }
14180 
14181  ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames);
14182 }
14183 
14184 MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
14185 {
14186  if (pSound == NULL) {
14187  return;
14188  }
14189 
14190  ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
14191 }
14192 
14194 {
14195  if (pSound == NULL) {
14196  return MA_FALSE;
14197  }
14198 
14200 }
14201 
14203 {
14204  if (pSound == NULL) {
14205  return 0;
14206  }
14207 
14208  return ma_node_get_time(pSound);
14209 }
14210 
14211 MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool8 isLooping)
14212 {
14213  if (pSound == NULL) {
14214  return;
14215  }
14216 
14217  /* Looping is only a valid concept if the sound is backed by a data source. */
14218  if (pSound->pDataSource == NULL) {
14219  return;
14220  }
14221 
14222  c89atomic_exchange_8(&pSound->isLooping, isLooping);
14223 
14224  /*
14225  This is a little bit of a hack, but basically we need to set the looping flag at the data source level if we are running a data source managed by
14226  the resource manager, and that is backed by a data stream. The reason for this is that the data stream itself needs to be aware of the looping
14227  requirements so that it can do seamless loop transitions. The better solution for this is to add ma_data_source_set_looping() and just call this
14228  generically.
14229  */
14230 #ifndef MA_NO_RESOURCE_MANAGER
14231  if (pSound->pDataSource == pSound->pResourceManagerDataSource) {
14233  }
14234 #endif
14235 }
14236 
14238 {
14239  if (pSound == NULL) {
14240  return MA_FALSE;
14241  }
14242 
14243  /* There is no notion of looping for sounds that are not backed by a data source. */
14244  if (pSound->pDataSource == NULL) {
14245  return MA_FALSE;
14246  }
14247 
14248  return c89atomic_load_8(&pSound->isLooping);
14249 }
14250 
14252 {
14253  if (pSound == NULL) {
14254  return MA_FALSE;
14255  }
14256 
14257  /* There is no notion of an end of a sound if it's not backed by a data source. */
14258  if (pSound->pDataSource == NULL) {
14259  return MA_FALSE;
14260  }
14261 
14262  return c89atomic_load_8(&pSound->atEnd);
14263 }
14264 
14266 {
14267  if (pSound == NULL) {
14268  return MA_INVALID_ARGS;
14269  }
14270 
14271  /* Seeking is only valid for sounds that are backed by a data source. */
14272  if (pSound->pDataSource == NULL) {
14273  return MA_INVALID_OPERATION;
14274  }
14275 
14276  /*
14277  Resource manager data sources are thread safe which means we can just seek immediately. However, we cannot guarantee that other data sources are
14278  thread safe as well so in that case we'll need to get the mixing thread to seek for us to ensure we don't try seeking at the same time as reading.
14279  */
14280 #ifndef MA_NO_RESOURCE_MANAGER
14281  if (pSound->pDataSource == pSound->pResourceManagerDataSource) {
14283  if (result != MA_SUCCESS) {
14284  return result;
14285  }
14286 
14287  /* Time dependant effects need to have their timers updated. */
14288  return ma_node_set_time(&pSound->engineNode, frameIndex);
14289  }
14290 #endif
14291 
14292  /* Getting here means the data source is not a resource manager data source so we'll need to get the mixing thread to do the seeking for us. */
14293  pSound->seekTarget = frameIndex;
14294 
14295  return MA_SUCCESS;
14296 }
14297 
14298 MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
14299 {
14300  if (pSound == NULL) {
14301  return MA_INVALID_ARGS;
14302  }
14303 
14304  /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */
14305  if (pSound->pDataSource == NULL) {
14306  if (pFormat != NULL) {
14307  *pFormat = ma_format_f32;
14308  }
14309 
14310  if (pChannels != NULL) {
14311  *pChannels = ma_node_get_input_channels(&pSound->engineNode, 0);
14312  }
14313 
14314  if (pSampleRate != NULL) {
14315  *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn;
14316  }
14317 
14318  return MA_SUCCESS;
14319  } else {
14320  return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate);
14321  }
14322 }
14323 
14325 {
14326  if (pSound == NULL) {
14327  return MA_INVALID_ARGS;
14328  }
14329 
14330  /* The notion of a cursor is only valid for sounds that are backed by a data source. */
14331  if (pSound->pDataSource == NULL) {
14332  return MA_INVALID_OPERATION;
14333  }
14334 
14335  return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor);
14336 }
14337 
14339 {
14340  if (pSound == NULL) {
14341  return MA_INVALID_ARGS;
14342  }
14343 
14344  /* The notion of a sound length is only valid for sounds that are backed by a data source. */
14345  if (pSound->pDataSource == NULL) {
14346  return MA_INVALID_OPERATION;
14347  }
14348 
14349  return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength);
14350 }
14351 
14352 
14353 MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup)
14354 {
14356  config.flags = flags;
14357  config.pInitialAttachment = pParentGroup;
14358  return ma_sound_group_init_ex(pEngine, &config, pGroup);
14359 }
14360 
14362 {
14363  ma_sound_config soundConfig;
14364 
14365  if (pGroup == NULL) {
14366  return MA_INVALID_ARGS;
14367  }
14368 
14369  MA_ZERO_OBJECT(pGroup);
14370 
14371  if (pConfig == NULL) {
14372  return MA_INVALID_ARGS;
14373  }
14374 
14375  /* A sound group is just a sound without a data source. */
14376  soundConfig = *pConfig;
14377  soundConfig.pFilePath = NULL;
14378  soundConfig.pFilePathW = NULL;
14379  soundConfig.pDataSource = NULL;
14380 
14381  /*
14382  Groups need to have spatialization disabled by default because I think it'll be pretty rare
14383  that programs will want to spatialize groups (but not unheard of). Certainly it feels like
14384  disabling this by default feels like the right option. Spatialization can be enabled with a
14385  call to ma_sound_group_set_spatialization_enabled().
14386  */
14387  soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION;
14388 
14389  return ma_sound_init_ex(pEngine, &soundConfig, pGroup);
14390 }
14391 
14393 {
14394  ma_sound_uninit(pGroup);
14395 }
14396 
14398 {
14399  return ma_sound_get_engine(pGroup);
14400 }
14401 
14403 {
14404  return ma_sound_start(pGroup);
14405 }
14406 
14408 {
14409  return ma_sound_stop(pGroup);
14410 }
14411 
14413 {
14414  return ma_sound_set_volume(pGroup, volume);
14415 }
14416 
14418 {
14419  return ma_sound_set_gain_db(pGroup, gainDB);
14420 }
14421 
14422 MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan)
14423 {
14424  ma_sound_set_pan(pGroup, pan);
14425 }
14426 
14428 {
14429  ma_sound_set_pan_mode(pGroup, panMode);
14430 }
14431 
14432 MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch)
14433 {
14434  ma_sound_set_pitch(pGroup, pitch);
14435 }
14436 
14438 {
14439  ma_sound_set_spatialization_enabled(pGroup, enabled);
14440 }
14441 
14443 {
14444  ma_sound_set_pinned_listener_index(pGroup, listenerIndex);
14445 }
14446 
14448 {
14449  return ma_sound_get_pinned_listener_index(pGroup);
14450 }
14451 
14452 MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z)
14453 {
14454  ma_sound_set_position(pGroup, x, y, z);
14455 }
14456 
14458 {
14459  return ma_sound_get_position(pGroup);
14460 }
14461 
14462 MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z)
14463 {
14464  ma_sound_set_direction(pGroup, x, y, z);
14465 }
14466 
14468 {
14469  return ma_sound_get_direction(pGroup);
14470 }
14471 
14472 MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z)
14473 {
14474  ma_sound_set_velocity(pGroup, x, y, z);
14475 }
14476 
14478 {
14479  return ma_sound_get_velocity(pGroup);
14480 }
14481 
14483 {
14484  ma_sound_set_attenuation_model(pGroup, attenuationModel);
14485 }
14486 
14488 {
14489  return ma_sound_get_attenuation_model(pGroup);
14490 }
14491 
14493 {
14494  ma_sound_set_positioning(pGroup, positioning);
14495 }
14496 
14498 {
14499  return ma_sound_get_positioning(pGroup);
14500 }
14501 
14502 MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff)
14503 {
14504  ma_sound_set_rolloff(pGroup, rolloff);
14505 }
14506 
14508 {
14509  return ma_sound_get_rolloff(pGroup);
14510 }
14511 
14512 MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain)
14513 {
14514  ma_sound_set_min_gain(pGroup, minGain);
14515 }
14516 
14518 {
14519  return ma_sound_get_min_gain(pGroup);
14520 }
14521 
14522 MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain)
14523 {
14524  ma_sound_set_max_gain(pGroup, maxGain);
14525 }
14526 
14528 {
14529  return ma_sound_get_max_gain(pGroup);
14530 }
14531 
14532 MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance)
14533 {
14534  ma_sound_set_min_distance(pGroup, minDistance);
14535 }
14536 
14538 {
14539  return ma_sound_get_min_distance(pGroup);
14540 }
14541 
14542 MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance)
14543 {
14544  ma_sound_set_max_distance(pGroup, maxDistance);
14545 }
14546 
14548 {
14549  return ma_sound_get_max_distance(pGroup);
14550 }
14551 
14552 MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
14553 {
14554  ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain);
14555 }
14556 
14557 MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
14558 {
14559  ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
14560 }
14561 
14562 MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor)
14563 {
14564  ma_sound_set_doppler_factor(pGroup, dopplerFactor);
14565 }
14566 
14568 {
14569  return ma_sound_get_doppler_factor(pGroup);
14570 }
14571 
14572 MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
14573 {
14574  ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames);
14575 }
14576 
14577 MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
14578 {
14579  ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds);
14580 }
14581 
14583 {
14584  return ma_sound_get_current_fade_volume(pGroup);
14585 }
14586 
14587 MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
14588 {
14589  ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
14590 }
14591 
14592 MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
14593 {
14594  ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
14595 }
14596 
14597 MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
14598 {
14599  ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
14600 }
14601 
14602 MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
14603 {
14604  ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
14605 }
14606 
14608 {
14609  return ma_sound_is_playing(pGroup);
14610 }
14611 
14613 {
14614  return ma_sound_get_time_in_pcm_frames(pGroup);
14615 }
14616 
14617 
14618 
14619 
14620 /*
14621 Biquad Node
14622 */
14623 MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2)
14624 {
14626 
14627  config.nodeConfig = ma_node_config_init();
14628  config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
14629 
14630  return config;
14631 }
14632 
14633 static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
14634 {
14635  ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
14636 
14637  MA_ASSERT(pNode != NULL);
14638  (void)pFrameCountIn;
14639 
14640  ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
14641 }
14642 
14643 static ma_node_vtable g_ma_biquad_node_vtable =
14644 {
14645  ma_biquad_node_process_pcm_frames,
14646  NULL, /* onGetRequiredInputFrameCount */
14647  1, /* One input. */
14648  1, /* One output. */
14649  0 /* Default flags. */
14650 };
14651 
14652 MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode)
14653 {
14654  ma_result result;
14655  ma_node_config baseNodeConfig;
14656 
14657  if (pNode == NULL) {
14658  return MA_INVALID_ARGS;
14659  }
14660 
14661  MA_ZERO_OBJECT(pNode);
14662 
14663  if (pConfig == NULL) {
14664  return MA_INVALID_ARGS;
14665  }
14666 
14667  if (pConfig->biquad.format != ma_format_f32) {
14668  return MA_INVALID_ARGS; /* The format must be f32. */
14669  }
14670 
14671  result = ma_biquad_init(&pConfig->biquad, &pNode->biquad);
14672  if (result != MA_SUCCESS) {
14673  return result;
14674  }
14675 
14676  baseNodeConfig = ma_node_config_init();
14677  baseNodeConfig.vtable = &g_ma_biquad_node_vtable;
14678  baseNodeConfig.pInputChannels = &pConfig->biquad.channels;
14679  baseNodeConfig.pOutputChannels = &pConfig->biquad.channels;
14680 
14681  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
14682  if (result != MA_SUCCESS) {
14683  return result;
14684  }
14685 
14686  return result;
14687 }
14688 
14690 {
14691  ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
14692 
14693  MA_ASSERT(pNode != NULL);
14694 
14695  return ma_biquad_reinit(pConfig, &pLPFNode->biquad);
14696 }
14697 
14698 MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
14699 {
14700  ma_node_uninit(pNode, pAllocationCallbacks);
14701 }
14702 
14703 
14704 
14705 /*
14706 Low Pass Filter Node
14707 */
14708 MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
14709 {
14711 
14712  config.nodeConfig = ma_node_config_init();
14713  config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
14714 
14715  return config;
14716 }
14717 
14718 static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
14719 {
14720  ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
14721 
14722  MA_ASSERT(pNode != NULL);
14723  (void)pFrameCountIn;
14724 
14725  ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
14726 }
14727 
14728 static ma_node_vtable g_ma_lpf_node_vtable =
14729 {
14730  ma_lpf_node_process_pcm_frames,
14731  NULL, /* onGetRequiredInputFrameCount */
14732  1, /* One input. */
14733  1, /* One output. */
14734  0 /* Default flags. */
14735 };
14736 
14737 MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode)
14738 {
14739  ma_result result;
14740  ma_node_config baseNodeConfig;
14741 
14742  if (pNode == NULL) {
14743  return MA_INVALID_ARGS;
14744  }
14745 
14746  MA_ZERO_OBJECT(pNode);
14747 
14748  if (pConfig == NULL) {
14749  return MA_INVALID_ARGS;
14750  }
14751 
14752  if (pConfig->lpf.format != ma_format_f32) {
14753  return MA_INVALID_ARGS; /* The format must be f32. */
14754  }
14755 
14756  result = ma_lpf_init(&pConfig->lpf, &pNode->lpf);
14757  if (result != MA_SUCCESS) {
14758  return result;
14759  }
14760 
14761  baseNodeConfig = ma_node_config_init();
14762  baseNodeConfig.vtable = &g_ma_lpf_node_vtable;
14763  baseNodeConfig.pInputChannels = &pConfig->lpf.channels;
14764  baseNodeConfig.pOutputChannels = &pConfig->lpf.channels;
14765 
14766  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
14767  if (result != MA_SUCCESS) {
14768  return result;
14769  }
14770 
14771  return result;
14772 }
14773 
14775 {
14776  ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
14777 
14778  MA_ASSERT(pNode != NULL);
14779 
14780  return ma_lpf_reinit(pConfig, &pLPFNode->lpf);
14781 }
14782 
14783 MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
14784 {
14785  ma_node_uninit(pNode, pAllocationCallbacks);
14786 }
14787 
14788 
14789 
14790 /*
14791 High Pass Filter Node
14792 */
14793 MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
14794 {
14796 
14797  config.nodeConfig = ma_node_config_init();
14798  config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
14799 
14800  return config;
14801 }
14802 
14803 static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
14804 {
14805  ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
14806 
14807  MA_ASSERT(pNode != NULL);
14808  (void)pFrameCountIn;
14809 
14810  ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
14811 }
14812 
14813 static ma_node_vtable g_ma_hpf_node_vtable =
14814 {
14815  ma_hpf_node_process_pcm_frames,
14816  NULL, /* onGetRequiredInputFrameCount */
14817  1, /* One input. */
14818  1, /* One output. */
14819  0 /* Default flags. */
14820 };
14821 
14822 MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode)
14823 {
14824  ma_result result;
14825  ma_node_config baseNodeConfig;
14826 
14827  if (pNode == NULL) {
14828  return MA_INVALID_ARGS;
14829  }
14830 
14831  MA_ZERO_OBJECT(pNode);
14832 
14833  if (pConfig == NULL) {
14834  return MA_INVALID_ARGS;
14835  }
14836 
14837  if (pConfig->hpf.format != ma_format_f32) {
14838  return MA_INVALID_ARGS; /* The format must be f32. */
14839  }
14840 
14841  result = ma_hpf_init(&pConfig->hpf, &pNode->hpf);
14842  if (result != MA_SUCCESS) {
14843  return result;
14844  }
14845 
14846  baseNodeConfig = ma_node_config_init();
14847  baseNodeConfig.vtable = &g_ma_hpf_node_vtable;
14848  baseNodeConfig.pInputChannels = &pConfig->hpf.channels;
14849  baseNodeConfig.pOutputChannels = &pConfig->hpf.channels;
14850 
14851  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
14852  if (result != MA_SUCCESS) {
14853  return result;
14854  }
14855 
14856  return result;
14857 }
14858 
14860 {
14861  ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
14862 
14863  MA_ASSERT(pNode != NULL);
14864 
14865  return ma_hpf_reinit(pConfig, &pHPFNode->hpf);
14866 }
14867 
14868 MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
14869 {
14870  ma_node_uninit(pNode, pAllocationCallbacks);
14871 }
14872 
14873 
14874 
14875 
14876 /*
14877 Band Pass Filter Node
14878 */
14879 MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
14880 {
14882 
14883  config.nodeConfig = ma_node_config_init();
14884  config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
14885 
14886  return config;
14887 }
14888 
14889 static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
14890 {
14891  ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
14892 
14893  MA_ASSERT(pNode != NULL);
14894  (void)pFrameCountIn;
14895 
14896  ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
14897 }
14898 
14899 static ma_node_vtable g_ma_bpf_node_vtable =
14900 {
14901  ma_bpf_node_process_pcm_frames,
14902  NULL, /* onGetRequiredInputFrameCount */
14903  1, /* One input. */
14904  1, /* One output. */
14905  0 /* Default flags. */
14906 };
14907 
14908 MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode)
14909 {
14910  ma_result result;
14911  ma_node_config baseNodeConfig;
14912 
14913  if (pNode == NULL) {
14914  return MA_INVALID_ARGS;
14915  }
14916 
14917  MA_ZERO_OBJECT(pNode);
14918 
14919  if (pConfig == NULL) {
14920  return MA_INVALID_ARGS;
14921  }
14922 
14923  if (pConfig->bpf.format != ma_format_f32) {
14924  return MA_INVALID_ARGS; /* The format must be f32. */
14925  }
14926 
14927  result = ma_bpf_init(&pConfig->bpf, &pNode->bpf);
14928  if (result != MA_SUCCESS) {
14929  return result;
14930  }
14931 
14932  baseNodeConfig = ma_node_config_init();
14933  baseNodeConfig.vtable = &g_ma_bpf_node_vtable;
14934  baseNodeConfig.pInputChannels = &pConfig->bpf.channels;
14935  baseNodeConfig.pOutputChannels = &pConfig->bpf.channels;
14936 
14937  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
14938  if (result != MA_SUCCESS) {
14939  return result;
14940  }
14941 
14942  return result;
14943 }
14944 
14946 {
14947  ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
14948 
14949  MA_ASSERT(pNode != NULL);
14950 
14951  return ma_bpf_reinit(pConfig, &pBPFNode->bpf);
14952 }
14953 
14954 MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
14955 {
14956  ma_node_uninit(pNode, pAllocationCallbacks);
14957 }
14958 
14959 
14960 
14961 /*
14962 Notching Filter Node
14963 */
14964 MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
14965 {
14967 
14968  config.nodeConfig = ma_node_config_init();
14969  config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency);
14970 
14971  return config;
14972 }
14973 
14974 static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
14975 {
14976  ma_notch_node* pBPFNode = (ma_notch_node*)pNode;
14977 
14978  MA_ASSERT(pNode != NULL);
14979  (void)pFrameCountIn;
14980 
14981  ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
14982 }
14983 
14984 static ma_node_vtable g_ma_notch_node_vtable =
14985 {
14986  ma_notch_node_process_pcm_frames,
14987  NULL, /* onGetRequiredInputFrameCount */
14988  1, /* One input. */
14989  1, /* One output. */
14990  0 /* Default flags. */
14991 };
14992 
14993 MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode)
14994 {
14995  ma_result result;
14996  ma_node_config baseNodeConfig;
14997 
14998  if (pNode == NULL) {
14999  return MA_INVALID_ARGS;
15000  }
15001 
15002  MA_ZERO_OBJECT(pNode);
15003 
15004  if (pConfig == NULL) {
15005  return MA_INVALID_ARGS;
15006  }
15007 
15008  if (pConfig->notch.format != ma_format_f32) {
15009  return MA_INVALID_ARGS; /* The format must be f32. */
15010  }
15011 
15012  result = ma_notch2_init(&pConfig->notch, &pNode->notch);
15013  if (result != MA_SUCCESS) {
15014  return result;
15015  }
15016 
15017  baseNodeConfig = ma_node_config_init();
15018  baseNodeConfig.vtable = &g_ma_notch_node_vtable;
15019  baseNodeConfig.pInputChannels = &pConfig->notch.channels;
15020  baseNodeConfig.pOutputChannels = &pConfig->notch.channels;
15021 
15022  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
15023  if (result != MA_SUCCESS) {
15024  return result;
15025  }
15026 
15027  return result;
15028 }
15029 
15031 {
15032  ma_notch_node* pBPFNode = (ma_notch_node*)pNode;
15033 
15034  MA_ASSERT(pNode != NULL);
15035 
15036  return ma_notch2_reinit(pConfig, &pBPFNode->notch);
15037 }
15038 
15039 MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
15040 {
15041  ma_node_uninit(pNode, pAllocationCallbacks);
15042 }
15043 
15044 
15045 
15046 /*
15047 Peaking Filter Node
15048 */
15049 MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
15050 {
15052 
15053  config.nodeConfig = ma_node_config_init();
15054  config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
15055 
15056  return config;
15057 }
15058 
15059 static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
15060 {
15061  ma_peak_node* pBPFNode = (ma_peak_node*)pNode;
15062 
15063  MA_ASSERT(pNode != NULL);
15064  (void)pFrameCountIn;
15065 
15066  ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
15067 }
15068 
15069 static ma_node_vtable g_ma_peak_node_vtable =
15070 {
15071  ma_peak_node_process_pcm_frames,
15072  NULL, /* onGetRequiredInputFrameCount */
15073  1, /* One input. */
15074  1, /* One output. */
15075  0 /* Default flags. */
15076 };
15077 
15078 MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode)
15079 {
15080  ma_result result;
15081  ma_node_config baseNodeConfig;
15082 
15083  if (pNode == NULL) {
15084  return MA_INVALID_ARGS;
15085  }
15086 
15087  MA_ZERO_OBJECT(pNode);
15088 
15089  if (pConfig == NULL) {
15090  return MA_INVALID_ARGS;
15091  }
15092 
15093  if (pConfig->peak.format != ma_format_f32) {
15094  return MA_INVALID_ARGS; /* The format must be f32. */
15095  }
15096 
15097  result = ma_peak2_init(&pConfig->peak, &pNode->peak);
15098  if (result != MA_SUCCESS) {
15099  ma_node_uninit(pNode, pAllocationCallbacks);
15100  return result;
15101  }
15102 
15103  baseNodeConfig = ma_node_config_init();
15104  baseNodeConfig.vtable = &g_ma_peak_node_vtable;
15105  baseNodeConfig.pInputChannels = &pConfig->peak.channels;
15106  baseNodeConfig.pOutputChannels = &pConfig->peak.channels;
15107 
15108  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
15109  if (result != MA_SUCCESS) {
15110  return result;
15111  }
15112 
15113  return result;
15114 }
15115 
15117 {
15118  ma_peak_node* pBPFNode = (ma_peak_node*)pNode;
15119 
15120  MA_ASSERT(pNode != NULL);
15121 
15122  return ma_peak2_reinit(pConfig, &pBPFNode->peak);
15123 }
15124 
15125 MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
15126 {
15127  ma_node_uninit(pNode, pAllocationCallbacks);
15128 }
15129 
15130 
15131 
15132 /*
15133 Low Shelf Filter Node
15134 */
15135 MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
15136 {
15138 
15139  config.nodeConfig = ma_node_config_init();
15140  config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
15141 
15142  return config;
15143 }
15144 
15145 static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
15146 {
15147  ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;
15148 
15149  MA_ASSERT(pNode != NULL);
15150  (void)pFrameCountIn;
15151 
15152  ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
15153 }
15154 
15155 static ma_node_vtable g_ma_loshelf_node_vtable =
15156 {
15157  ma_loshelf_node_process_pcm_frames,
15158  NULL, /* onGetRequiredInputFrameCount */
15159  1, /* One input. */
15160  1, /* One output. */
15161  0 /* Default flags. */
15162 };
15163 
15164 MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode)
15165 {
15166  ma_result result;
15167  ma_node_config baseNodeConfig;
15168 
15169  if (pNode == NULL) {
15170  return MA_INVALID_ARGS;
15171  }
15172 
15173  MA_ZERO_OBJECT(pNode);
15174 
15175  if (pConfig == NULL) {
15176  return MA_INVALID_ARGS;
15177  }
15178 
15179  if (pConfig->loshelf.format != ma_format_f32) {
15180  return MA_INVALID_ARGS; /* The format must be f32. */
15181  }
15182 
15183  result = ma_loshelf2_init(&pConfig->loshelf, &pNode->loshelf);
15184  if (result != MA_SUCCESS) {
15185  return result;
15186  }
15187 
15188  baseNodeConfig = ma_node_config_init();
15189  baseNodeConfig.vtable = &g_ma_loshelf_node_vtable;
15190  baseNodeConfig.pInputChannels = &pConfig->loshelf.channels;
15191  baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels;
15192 
15193  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
15194  if (result != MA_SUCCESS) {
15195  return result;
15196  }
15197 
15198  return result;
15199 }
15200 
15202 {
15203  ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;
15204 
15205  MA_ASSERT(pNode != NULL);
15206 
15207  return ma_loshelf2_reinit(pConfig, &pBPFNode->loshelf);
15208 }
15209 
15210 MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
15211 {
15212  ma_node_uninit(pNode, pAllocationCallbacks);
15213 }
15214 
15215 
15216 
15217 /*
15218 High Shelf Filter Node
15219 */
15220 MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
15221 {
15223 
15224  config.nodeConfig = ma_node_config_init();
15225  config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
15226 
15227  return config;
15228 }
15229 
15230 static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
15231 {
15232  ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;
15233 
15234  MA_ASSERT(pNode != NULL);
15235  (void)pFrameCountIn;
15236 
15237  ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
15238 }
15239 
15240 static ma_node_vtable g_ma_hishelf_node_vtable =
15241 {
15242  ma_hishelf_node_process_pcm_frames,
15243  NULL, /* onGetRequiredInputFrameCount */
15244  1, /* One input. */
15245  1, /* One output. */
15246  0 /* Default flags. */
15247 };
15248 
15249 MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode)
15250 {
15251  ma_result result;
15252  ma_node_config baseNodeConfig;
15253 
15254  if (pNode == NULL) {
15255  return MA_INVALID_ARGS;
15256  }
15257 
15258  MA_ZERO_OBJECT(pNode);
15259 
15260  if (pConfig == NULL) {
15261  return MA_INVALID_ARGS;
15262  }
15263 
15264  if (pConfig->hishelf.format != ma_format_f32) {
15265  return MA_INVALID_ARGS; /* The format must be f32. */
15266  }
15267 
15268  result = ma_hishelf2_init(&pConfig->hishelf, &pNode->hishelf);
15269  if (result != MA_SUCCESS) {
15270  return result;
15271  }
15272 
15273  baseNodeConfig = ma_node_config_init();
15274  baseNodeConfig.vtable = &g_ma_hishelf_node_vtable;
15275  baseNodeConfig.pInputChannels = &pConfig->hishelf.channels;
15276  baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels;
15277 
15278  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
15279  if (result != MA_SUCCESS) {
15280  return result;
15281  }
15282 
15283  return result;
15284 }
15285 
15287 {
15288  ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;
15289 
15290  MA_ASSERT(pNode != NULL);
15291 
15292  return ma_hishelf2_reinit(pConfig, &pBPFNode->hishelf);
15293 }
15294 
15295 MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
15296 {
15297  ma_node_uninit(pNode, pAllocationCallbacks);
15298 }
15299 
15300 
15301 
15302 /*
15303 Delay
15304 */
15305 MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
15306 {
15308 
15310  config.channels = channels;
15311  config.sampleRate = sampleRate;
15312  config.delayInFrames = delayInFrames;
15313  config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */
15314  config.wet = 1;
15315  config.dry = 1;
15316  config.decay = decay;
15317 
15318  return config;
15319 }
15320 
15321 
15322 MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay)
15323 {
15324  if (pDelay == NULL) {
15325  return MA_INVALID_ARGS;
15326  }
15327 
15328  MA_ZERO_OBJECT(pDelay);
15329 
15330  if (pConfig == NULL) {
15331  return MA_INVALID_ARGS;
15332  }
15333 
15334  if (pConfig->decay < 0 || pConfig->decay > 1) {
15335  return MA_INVALID_ARGS;
15336  }
15337 
15338  pDelay->config = *pConfig;
15339  pDelay->bufferSizeInFrames = pConfig->delayInFrames;
15340  pDelay->cursor = 0;
15341 
15342  pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks);
15343  if (pDelay->pBuffer == NULL) {
15344  return MA_OUT_OF_MEMORY;
15345  }
15346 
15348 
15349  return MA_SUCCESS;
15350 }
15351 
15352 MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks)
15353 {
15354  if (pDelay == NULL) {
15355  return;
15356  }
15357 
15358  ma_free(pDelay->pBuffer, pAllocationCallbacks);
15359 }
15360 
15361 MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
15362 {
15363  ma_uint32 iFrame;
15364  ma_uint32 iChannel;
15365  float* pFramesOutF32 = (float*)pFramesOut;
15366  const float* pFramesInF32 = (const float*)pFramesIn;
15367 
15368  if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) {
15369  return MA_INVALID_ARGS;
15370  }
15371 
15372  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
15373  for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) {
15374  ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel;
15375 
15376  if (pDelay->config.delayStart) {
15377  /* Delayed start. */
15378 
15379  /* Read */
15380  pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
15381 
15382  /* Feedback */
15383  pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
15384  } else {
15385  /* Immediate start */
15386 
15387  /* Feedback */
15388  pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
15389 
15390  /* Read */
15391  pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
15392  }
15393  }
15394 
15395  pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames;
15396 
15397  pFramesOutF32 += pDelay->config.channels;
15398  pFramesInF32 += pDelay->config.channels;
15399  }
15400 
15401  return MA_SUCCESS;
15402 }
15403 
15404 MA_API void ma_delay_set_wet(ma_delay* pDelay, float value)
15405 {
15406  if (pDelay == NULL) {
15407  return;
15408  }
15409 
15410  pDelay->config.wet = value;
15411 }
15412 
15413 MA_API float ma_delay_get_wet(const ma_delay* pDelay)
15414 {
15415  if (pDelay == NULL) {
15416  return 0;
15417  }
15418 
15419  return pDelay->config.wet;
15420 }
15421 
15422 MA_API void ma_delay_set_dry(ma_delay* pDelay, float value)
15423 {
15424  if (pDelay == NULL) {
15425  return;
15426  }
15427 
15428  pDelay->config.dry = value;
15429 }
15430 
15431 MA_API float ma_delay_get_dry(const ma_delay* pDelay)
15432 {
15433  if (pDelay == NULL) {
15434  return 0;
15435  }
15436 
15437  return pDelay->config.dry;
15438 }
15439 
15440 MA_API void ma_delay_set_decay(ma_delay* pDelay, float value)
15441 {
15442  if (pDelay == NULL) {
15443  return;
15444  }
15445 
15446  pDelay->config.decay = value;
15447 }
15448 
15449 MA_API float ma_delay_get_decay(const ma_delay* pDelay)
15450 {
15451  if (pDelay == NULL) {
15452  return 0;
15453  }
15454 
15455  return pDelay->config.decay;
15456 }
15457 
15458 
15459 
15460 
15461 MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
15462 {
15464 
15465  config.nodeConfig = ma_node_config_init();
15466  config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay);
15467 
15468  return config;
15469 }
15470 
15471 
15472 static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
15473 {
15474  ma_delay_node* pDelayNode = (ma_delay_node*)pNode;
15475 
15476  (void)pFrameCountIn;
15477 
15478  ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
15479 }
15480 
15481 static ma_node_vtable g_ma_delay_node_vtable =
15482 {
15483  ma_delay_node_process_pcm_frames,
15484  NULL,
15485  1, /* 1 input channels. */
15486  1, /* 1 output channel. */
15487  MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Reverb requires continuous processing to ensure the tail get's processed. */
15488 };
15489 
15490 MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode)
15491 {
15492  ma_result result;
15493  ma_node_config baseConfig;
15494 
15495  if (pDelayNode == NULL) {
15496  return MA_INVALID_ARGS;
15497  }
15498 
15499  MA_ZERO_OBJECT(pDelayNode);
15500 
15501  result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay);
15502  if (result != MA_SUCCESS) {
15503  return result;
15504  }
15505 
15506  baseConfig = pConfig->nodeConfig;
15507  baseConfig.vtable = &g_ma_delay_node_vtable;
15508  baseConfig.pInputChannels = &pConfig->delay.channels;
15509  baseConfig.pOutputChannels = &pConfig->delay.channels;
15510 
15511  result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode);
15512  if (result != MA_SUCCESS) {
15513  ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
15514  return result;
15515  }
15516 
15517  return result;
15518 }
15519 
15520 MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks)
15521 {
15522  if (pDelayNode == NULL) {
15523  return;
15524  }
15525 
15526  /* The base node is always uninitialized first. */
15527  ma_node_uninit(pDelayNode, pAllocationCallbacks);
15528  ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
15529 }
15530 
15531 MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value)
15532 {
15533  if (pDelayNode == NULL) {
15534  return;
15535  }
15536 
15537  ma_delay_set_wet(&pDelayNode->delay, value);
15538 }
15539 
15540 MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode)
15541 {
15542  if (pDelayNode == NULL) {
15543  return 0;
15544  }
15545 
15546  return ma_delay_get_wet(&pDelayNode->delay);
15547 }
15548 
15549 MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value)
15550 {
15551  if (pDelayNode == NULL) {
15552  return;
15553  }
15554 
15555  ma_delay_set_dry(&pDelayNode->delay, value);
15556 }
15557 
15558 MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode)
15559 {
15560  if (pDelayNode == NULL) {
15561  return 0;
15562  }
15563 
15564  return ma_delay_get_dry(&pDelayNode->delay);
15565 }
15566 
15567 MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value)
15568 {
15569  if (pDelayNode == NULL) {
15570  return;
15571  }
15572 
15573  ma_delay_set_decay(&pDelayNode->delay, value);
15574 }
15575 
15576 MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode)
15577 {
15578  if (pDelayNode == NULL) {
15579  return 0;
15580  }
15581 
15582  return ma_delay_get_decay(&pDelayNode->delay);
15583 }
15584 
15585 #endif
ma_biquad_node_config::biquad
ma_biquad_config biquad
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2134
ma_engine_node::panner
ma_panner panner
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1878
ma_spatializer_config::channelsOut
ma_uint32 channelsOut
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1764
ma_int64
int64_t ma_int64
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1504
ma_spatializer_config::minGain
float minGain
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1769
ma_sound_group_get_time_in_pcm_frames
MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group *pGroup)
ma_data_source_node_uninit
MA_API void ma_data_source_node_uninit(ma_data_source_node *pDataSourceNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_job_queue_uninit
MA_API ma_result ma_job_queue_uninit(ma_job_queue *pQueue)
ma_panner_config_init
MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels)
ma_paged_audio_buffer_data_uninit
MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data *pData, const ma_allocation_callbacks *pAllocationCallbacks)
ma_uint64
uint64_t ma_uint64
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1505
ma_get_bytes_per_frame
static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels)
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2673
ma_device_init_ex
ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config *pContextConfig, const ma_device_config *pConfig, ma_device *pDevice)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27362
ma_resource_manager_config::decodedChannels
ma_uint32 decodedChannels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1516
ma_gainer::pOldGains
float * pOldGains
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:410
ma_device_config::pDeviceID
ma_device_id * pDeviceID
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3127
ma_hishelf2_config_init
ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32308
ma_data_source_uninit
MA_API void ma_data_source_uninit(ma_data_source *pDataSource)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37255
ma_paged_audio_buffer::ds
ma_data_source_base ds
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:106
ma_sound_uninit
MA_API void ma_sound_uninit(ma_sound *pSound)
ma_spatializer_listener_config_init
MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut)
ma_gainer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:406
ma_node_graph_config_init
MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
ma_resource_manager_data_buffer_node::pChildHi
ma_resource_manager_data_buffer_node * pChildHi
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1446
ma_resource_manager_process_job
MA_API ma_result ma_resource_manager_process_job(ma_resource_manager *pResourceManager, ma_job *pJob)
c89atomic_exchange_64
#define c89atomic_exchange_64(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3993
ma_paged_audio_buffer_seek_to_pcm_frame
MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer *pPagedAudioBuffer, ma_uint64 frameIndex)
ma_device_config::noClip
ma_bool32 noClip
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3109
ma_notch_node_init
MA_API ma_result ma_notch_node_init(ma_node_graph *pNodeGraph, const ma_notch_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_notch_node *pNode)
ma_mix_f32_fast
static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:2350
MA_ATOMIC
#define MA_ATOMIC
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:225
ma_resource_manager_register_decoded_data
MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager *pResourceManager, const char *pName, const void *pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
ma_lpf_process_pcm_frames
ma_result ma_lpf_process_pcm_frames(ma_lpf *pLPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31060
ma_data_source_node_config_init
MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source *pDataSource, ma_bool32 looping)
MA_FALSE
#define MA_FALSE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1531
ma_resource_manager_data_buffer::pNode
ma_resource_manager_data_buffer_node * pNode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1453
ma_sound_group_get_positioning
MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group *pGroup)
ma_resource_manager_config::decodedSampleRate
ma_uint32 decodedSampleRate
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1517
ma_spatializer_set_max_distance
MA_API void ma_spatializer_set_max_distance(ma_spatializer *pSpatializer, float maxDistance)
ma_event_uninit
MA_API void ma_event_uninit(ma_event *pEvent)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4872
ma_data_source_base
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4523
ma_node_base::pCachedData
float * pCachedData
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:997
ma_job::pageDataBufferNode
struct ma_job::@271::@276 pageDataBufferNode
ma_bpf_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2209
ma_node_graph::isReading
MA_ATOMIC ma_bool8 isReading
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1059
ma_sound_group_set_rolloff
MA_API void ma_sound_group_set_rolloff(ma_sound_group *pGroup, float rolloff)
ma_paged_audio_buffer::pData
ma_paged_audio_buffer_data * pData
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:107
ma_sound_group_init
MA_API ma_result ma_sound_group_init(ma_engine *pEngine, ma_uint32 flags, ma_sound_group *pParentGroup, ma_sound_group *pGroup)
ma_hpf_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2186
ma_resource_manager_data_buffer_init_w
MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager *pResourceManager, const wchar_t *pFilePath, ma_uint32 flags, const ma_pipeline_notifications *pNotifications, ma_resource_manager_data_buffer *pDataBuffer)
ma_paged_audio_buffer_data_free_page
MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data *pData, ma_paged_audio_buffer_page *pPage, const ma_allocation_callbacks *pAllocationCallbacks)
ma_spatializer_get_max_gain
MA_API float ma_spatializer_get_max_gain(const ma_spatializer *pSpatializer)
MA_ALLOCATION_TYPE_ENCODED_BUFFER
#define MA_ALLOCATION_TYPE_ENCODED_BUFFER
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:50
ma_gainer::config
ma_gainer_config config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:408
ma_sound_get_engine
MA_API ma_engine * ma_sound_get_engine(const ma_sound *pSound)
ma_gain_db_to_factor
float ma_gain_db_to_factor(float gain)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:28000
ma_hishelf2_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2152
ma_hishelf_node_init
MA_API ma_result ma_hishelf_node_init(ma_node_graph *pNodeGraph, const ma_hishelf_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_hishelf_node *pNode)
ma_bpf_config_init
ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31747
ma_spatializer::velocity
ma_vec3f velocity
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1789
MA_JOB_LOAD_DATA_BUFFER
#define MA_JOB_LOAD_DATA_BUFFER
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1143
ma_thread
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2824
ma_job::next
ma_uint64 next
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1280
ma_resource_manager_data_stream::seekCounter
MA_ATOMIC ma_bool32 seekCounter
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1495
ma_sound_config::pFilePath
const char * pFilePath
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1899
ma_resource_manager_data_stream::decoder
ma_decoder decoder
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1475
ma_node
void ma_node
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:874
ma_pan_mode_pan
@ ma_pan_mode_pan
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1630
ma_data_source_node_config::pDataSource
ma_data_source * pDataSource
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1076
c89atomic_load_32
#define c89atomic_load_32(ptr)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3988
ma_paged_audio_buffer_data_append_page
MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data *pData, ma_paged_audio_buffer_page *pPage)
ma_lpf_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1920
MA_CHANNEL_NONE
#define MA_CHANNEL_NONE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1601
ma_decoder_get_length_in_pcm_frames
ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder *pDecoder)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44780
MA_JOB_QUIT
#define MA_JOB_QUIT
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1139
ma_job_queue_post
MA_API ma_result ma_job_queue_post(ma_job_queue *pQueue, const ma_job *pJob)
ma_paged_audio_buffer_data::head
ma_paged_audio_buffer_page head
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:81
ma_sound_group_get_current_fade_volume
MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group *pGroup)
ma_engine_get_channels
MA_API ma_uint32 ma_engine_get_channels(const ma_engine *pEngine)
ma_data_source_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1073
ma_job::breakup
struct ma_job::@270::@273 breakup
ma_resource_manager_data_buffer_get_data_format
MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer *pDataBuffer, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate)
ma_sound_init_from_data_source
MA_API ma_result ma_sound_init_from_data_source(ma_engine *pEngine, ma_data_source *pDataSource, ma_uint32 flags, ma_sound_group *pGroup, ma_sound *pSound)
ma_splitter_node::base
ma_node_base base
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1107
ma_spatializer_listener::position
ma_vec3f position
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1733
ma_panner_set_mode
MA_API void ma_panner_set_mode(ma_panner *pPanner, ma_pan_mode mode)
ma_sound_set_spatialization_enabled
MA_API void ma_sound_set_spatialization_enabled(ma_sound *pSound, ma_bool32 enabled)
ma_vec3f::x
float x
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1689
ma_resource_manager_data_stream::relativeCursor
ma_uint32 relativeCursor
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1478
ma_spatializer_listener_get_heap_size
MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config *pConfig, size_t *pHeapSizeInBytes)
ma_job::freeDataStream
struct ma_job::@271::@280 freeDataStream
ma_fence
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1194
ma_spatializer_listener_config::pChannelMapOut
ma_channel * pChannelMapOut
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1718
ma_bpf_init
ma_result ma_bpf_init(const ma_bpf_config *pConfig, ma_bpf *pBPF)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31833
ma_resource_manager_data_stream_read_pcm_frames
MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream *pDataStream, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
ma_sound_group_is_playing
MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group *pGroup)
ma_sound_get_max_distance
MA_API float ma_sound_get_max_distance(const ma_sound *pSound)
ma_node_input_bus::lock
MA_ATOMIC ma_spinlock lock
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:984
MA_INVALID_ARGS
#define MA_INVALID_ARGS
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1661
ma_decoder_get_cursor_in_pcm_frames
MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder *pDecoder, ma_uint64 *pCursor)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44763
MA_CHANNEL_FRONT_RIGHT
#define MA_CHANNEL_FRONT_RIGHT
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1604
ma_engine_stop
MA_API ma_result ma_engine_stop(ma_engine *pEngine)
ma_uint8
uint8_t ma_uint8
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1499
ma_biquad_init
ma_result ma_biquad_init(const ma_biquad_config *pConfig, ma_biquad *pBQ)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:30440
ma_copy_string
MA_API char * ma_copy_string(const char *src, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:1156
ma_device_uninit
void ma_device_uninit(ma_device *pDevice)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27425
ma_engine_node_config::sampleRate
ma_uint32 sampleRate
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1860
ma_node_get_output_bus_volume
MA_API float ma_node_get_output_bus_volume(const ma_node *pNode, ma_uint32 outputBusIndex)
MA_LISTENER_INDEX_CLOSEST
#define MA_LISTENER_INDEX_CLOSEST
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1846
ma_channel_map_equal
ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS])
ma_node_set_output_bus_volume
MA_API ma_result ma_node_set_output_bus_volume(ma_node *pNode, ma_uint32 outputBusIndex, float volume)
ma_min
#define ma_min(x, y)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:778
ma_panner_config::format
ma_format format
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1635
ma_engine::listenerCount
ma_uint32 listenerCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1974
ma_lpf_node_uninit
MA_API void ma_lpf_node_uninit(ma_lpf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING
#define MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1393
ma_paged_audio_buffer_data_init
MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data *pData)
ma_engine_get_endpoint
MA_API ma_node * ma_engine_get_endpoint(ma_engine *pEngine)
ma_spatializer_config::attenuationModel
ma_attenuation_model attenuationModel
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1766
ma_resource_manager_data_stream_get_length_in_pcm_frames
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream *pDataStream, ma_uint64 *pLength)
ma_resource_manager_data_stream::flags
ma_uint32 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1474
ma_hishelf_node_uninit
MA_API void ma_hishelf_node_uninit(ma_hishelf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_bpf_node_config_init
MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
MA_MAX_NODE_LOCAL_BUS_COUNT
#define MA_MAX_NODE_LOCAL_BUS_COUNT
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:867
ma_device_start
ma_result ma_device_start(ma_device *pDevice)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27485
ma_sound_group_set_max_gain
MA_API void ma_sound_group_set_max_gain(ma_sound_group *pGroup, float maxGain)
ma_sound_set_position
MA_API void ma_sound_set_position(ma_sound *pSound, float x, float y, float z)
ma_spatializer_get_rolloff
MA_API float ma_spatializer_get_rolloff(const ma_spatializer *pSpatializer)
ma_sound::seekTarget
ma_uint64 seekTarget
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1916
ma_log_uninit
MA_API void ma_log_uninit(ma_log *pLog)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:2032
ma_resource_manager_register_file
MA_API ma_result ma_resource_manager_register_file(ma_resource_manager *pResourceManager, const char *pFilePath, ma_uint32 flags)
ma_node_base
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:992
ma_spatializer_config::coneInnerAngleInRadians
float coneInnerAngleInRadians
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1774
ma_sound_group_set_pan_mode
MA_API void ma_sound_group_set_pan_mode(ma_sound_group *pGroup, ma_pan_mode panMode)
ma_resource_manager_data_buffer_get_available_frames
MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 *pAvailableFrames)
ma_node_init
MA_API ma_result ma_node_init(ma_node_graph *pNodeGraph, const ma_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_node *pNode)
ma_resource_manager_data_buffer_get_cursor_in_pcm_frames
MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 *pCursor)
ma_delay_config::delayStart
ma_bool32 delayStart
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2321
ma_sound_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1897
ma_resource_manager_data_source_init_copy
MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager *pResourceManager, const ma_resource_manager_data_source *pExistingDataSource, ma_resource_manager_data_source *pDataSource)
MA_CANCELLED
#define MA_CANCELLED
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1710
NULL
#define NULL
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/speex_resampler/thirdparty/resample.c:92
ma_paged_audio_buffer_data_get_head
MA_API ma_paged_audio_buffer_page * ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data *pData)
ma_engine_node_config::pinnedListenerIndex
ma_uint8 pinnedListenerIndex
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1863
ma_decoding_backend_vtable
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4723
ma_node_config::inputBusCount
ma_uint32 inputBusCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:939
ma_spatializer_config::maxDistance
float maxDistance
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1772
ma_delay_node_get_dry
MA_API float ma_delay_node_get_dry(const ma_delay_node *pDelayNode)
ma_sound_group_set_velocity
MA_API void ma_sound_group_set_velocity(ma_sound_group *pGroup, float x, float y, float z)
ma_spatializer_get_doppler_factor
MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer *pSpatializer)
c89atomic_load_explicit_8
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8 *ptr, c89atomic_memory_order order)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3765
ma_paged_audio_buffer_config::pData
ma_paged_audio_buffer_data * pData
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:98
ma_spatializer_get_output_channels
MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer *pSpatializer)
ma_splitter_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1105
MA_JOB_SEEK_DATA_STREAM
#define MA_JOB_SEEK_DATA_STREAM
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1148
c89atomic_load_ptr
#define c89atomic_load_ptr(ptr)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3970
ma_hishelf2_init
ma_result ma_hishelf2_init(const ma_hishelf2_config *pConfig, ma_hishelf2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32358
ma_sound_get_time_in_pcm_frames
MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound *pSound)
ma_format_count
@ ma_format_count
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1791
ma_resource_manager_data_buffer_read_pcm_frames
MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer *pDataBuffer, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
ma_thread_priority_normal
@ ma_thread_priority_normal
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2817
ma_notch2_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2076
ma_spatializer_listener_set_position
MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener *pListener, float x, float y, float z)
ma_data_source_node_config::looping
ma_bool32 looping
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1077
ma_fence::counter
ma_uint32 counter
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1197
ma_decoder_uninit
ma_result ma_decoder_uninit(ma_decoder *pDecoder)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44739
ma_resource_manager_data_supply_type_unknown
@ ma_resource_manager_data_supply_type_unknown
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1401
ma_resource_manager_data_source_unmap
MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source *pDataSource, ma_uint64 frameCount)
ma_notch2_reinit
ma_result ma_notch2_reinit(const ma_notch2_config *pConfig, ma_notch2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32001
ma_decoder_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5170
ma_device::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3626
ma_resource_manager_register_encoded_data
MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager *pResourceManager, const char *pName, const void *pData, size_t sizeInBytes)
ma_resource_manager_data_buffer::pagedBuffer
ma_paged_audio_buffer pagedBuffer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1466
ma_resource_manager_data_supply::decodedPaged
struct ma_resource_manager_data_supply::@284::@288 decodedPaged
ma_node_config::outputBusCount
ma_uint32 outputBusCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:940
ma_loshelf_node_init
MA_API ma_result ma_loshelf_node_init(ma_node_graph *pNodeGraph, const ma_loshelf_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_loshelf_node *pNode)
c89atomic_memory_order_release
#define c89atomic_memory_order_release
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3291
ma_delay::cursor
ma_uint32 cursor
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2333
ma_resource_manager_data_supply_type_encoded
@ ma_resource_manager_data_supply_type_encoded
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1402
ma_bool32
ma_uint32 ma_bool32
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1529
ma_data_source_read_pcm_frames
MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead, ma_bool32 loop)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37348
ma_calculate_channel_position_rectangular_weight
static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:33691
ma_resource_manager_data_stream_map
MA_API ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream *pDataStream, void **ppFramesOut, ma_uint64 *pFrameCount)
ma_sound_set_velocity
MA_API void ma_sound_set_velocity(ma_sound *pSound, float x, float y, float z)
ma_paged_audio_buffer::absoluteCursor
ma_uint64 absoluteCursor
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:110
ma_resource_manager_data_source::stream
ma_resource_manager_data_stream stream
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1503
ma_resource_manager_data_stream::pResourceManager
ma_resource_manager * pResourceManager
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1473
ma_sound_group_start
MA_API ma_result ma_sound_group_start(ma_sound_group *pGroup)
ma_spatializer_listener_config::coneOuterGain
float coneOuterGain
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1722
ma_engine_listener_get_position
MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine *pEngine, ma_uint32 listenerIndex)
ma_fader::volumeBeg
float volumeBeg
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1672
ma_resource_manager_data_buffer_node::result
MA_ATOMIC ma_result result
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1439
ma_spatializer::_ownsHeap
ma_bool32 _ownsHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1796
ma_waveform_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5361
ma_slot_allocator::slots
ma_uint32 slots[MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1172
c89atomic_fetch_add_32
#define c89atomic_fetch_add_32(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4004
ma_resource_manager_data_stream::isPageValid
MA_ATOMIC ma_bool32 isPageValid[2]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1494
ma_countof
#define ma_countof(x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:776
ma_bpf_process_pcm_frames
ma_result ma_bpf_process_pcm_frames(ma_bpf *pBPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31853
c89atomic_fetch_sub_32
#define c89atomic_fetch_sub_32(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4008
ma_format_f32
@ ma_format_f32
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1790
ma_sound_group_get_max_distance
MA_API float ma_sound_group_get_max_distance(const ma_sound_group *pGroup)
ma_resource_manager_register_file_w
MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager *pResourceManager, const wchar_t *pFilePath, ma_uint32 flags)
ma_fence_wait
MA_API ma_result ma_fence_wait(ma_fence *pFence)
ma_resource_manager_data_source_get_cursor_in_pcm_frames
MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source *pDataSource, ma_uint64 *pCursor)
ma_panner_process_pcm_frames
MA_API ma_result ma_panner_process_pcm_frames(ma_panner *pPanner, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
ma_resource_manager_config_init
MA_API ma_resource_manager_config ma_resource_manager_config_init(void)
s
XmlRpcServer s
ma_engine_config::listenerCount
ma_uint32 listenerCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1952
ma_engine_node::pitch
MA_ATOMIC float pitch
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1879
ma_resource_manager_data_supply::decoded
struct ma_resource_manager_data_supply::@284::@287 decoded
ma_gainer_set_gains
MA_API ma_result ma_gainer_set_gains(ma_gainer *pGainer, float *pNewGains)
ma_resource_manager_config::customDecodingBackendCount
ma_uint32 customDecodingBackendCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1522
ma_engine::gainSmoothTimeInFrames
ma_uint32 gainSmoothTimeInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1982
ma_resampler_set_rate_ratio
ma_result ma_resampler_set_rate_ratio(ma_resampler *pResampler, float ratio)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:33454
ma_delay_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2318
ma_spatializer::dopplerPitch
float dopplerPitch
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1790
ma_splitter_node_uninit
MA_API void ma_splitter_node_uninit(ma_splitter_node *pSplitterNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_sound_get_direction
MA_API ma_vec3f ma_sound_get_direction(const ma_sound *pSound)
ma_job::seekDataStream
struct ma_job::@271::@282 seekDataStream
ma_node_state
ma_node_state
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:885
ma_engine_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1870
MA_DATA_CONVERTER_STACK_BUFFER_SIZE
#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4226
MA_NO_DATA_AVAILABLE
#define MA_NO_DATA_AVAILABLE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1691
ma_gainer_init
MA_API ma_result ma_gainer_init(const ma_gainer_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_gainer *pGainer)
ma_fader::lengthInFrames
ma_uint64 lengthInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1674
ma_vec3f
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1687
ma_sound_group_get_velocity
MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group *pGroup)
ma_node_output_bus::pPrev
MA_ATOMIC ma_node_output_bus * pPrev
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:970
ma_lpf_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2163
MA_BUSY
#define MA_BUSY
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1678
MA_CHANNEL_POSITION_COUNT
#define MA_CHANNEL_POSITION_COUNT
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1655
ma_decoder_config_init
ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:40646
ma_sound_get_min_distance
MA_API float ma_sound_get_min_distance(const ma_sound *pSound)
ma_sound_group_set_max_distance
MA_API void ma_sound_group_set_max_distance(ma_sound_group *pGroup, float maxDistance)
ma_fader::volumeEnd
float volumeEnd
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1673
ma_format_s32
@ ma_format_s32
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1789
ma_node_base::inputBusCount
ma_uint32 inputBusCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1009
MA_INLINE
#define MA_INLINE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1569
MA_OUT_OF_RANGE
#define MA_OUT_OF_RANGE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1664
ma_spatializer_listener_get_speed_of_sound
MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener *pListener)
ma_node_init_preallocated
MA_API ma_result ma_node_init_preallocated(ma_node_graph *pNodeGraph, const ma_node_config *pConfig, void *pHeap, ma_node *pNode)
ma_spatializer_listener_get_channel_map
MA_API ma_channel * ma_spatializer_listener_get_channel_map(ma_spatializer_listener *pListener)
ma_sound_set_pitch
MA_API void ma_sound_set_pitch(ma_sound *pSound, float pitch)
ma_node_output_bus::inputNodeInputBusIndex
MA_ATOMIC ma_uint8 inputNodeInputBusIndex
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:963
ma_device_stop
ma_result ma_device_stop(ma_device *pDevice)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27544
ma_engine_get_sample_rate
MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine *pEngine)
ma_pipeline_notifications
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1259
ma_delay_config::decay
float decay
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2324
ma_silence_pcm_frames
MA_API void ma_silence_pcm_frames(void *p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27789
c89atomic_load_64
#define c89atomic_load_64(ptr)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3989
ma_resource_manager_data_source_read_pcm_frames
MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
ma_resource_manager_data_buffer_node::data
ma_resource_manager_data_supply data
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1443
ma_channel_map_copy_or_default
MA_API void ma_channel_map_copy_or_default(ma_channel *pOut, const ma_channel *pIn, ma_uint32 channels)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:36197
MA_SOUND_FLAG_NO_SPATIALIZATION
#define MA_SOUND_FLAG_NO_SPATIALIZATION
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1840
ma_delay_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2316
ma_spatializer::position
ma_vec3f position
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1787
ma_biquad_node_uninit
MA_API void ma_biquad_node_uninit(ma_biquad_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_bpf_node_reinit
MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config *pConfig, ma_bpf_node *pNode)
ma_delay_node_set_decay
MA_API void ma_delay_node_set_decay(ma_delay_node *pDelayNode, float value)
ma_resource_manager_data_source_get_length_in_pcm_frames
MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source *pDataSource, ma_uint64 *pLength)
ma_resource_manager_unregister_data_w
MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager *pResourceManager, const wchar_t *pName)
ma_engine_listener_get_cone
MA_API void ma_engine_listener_get_cone(const ma_engine *pEngine, ma_uint32 listenerIndex, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
ma_sound_set_gain_db
MA_API ma_result ma_sound_set_gain_db(ma_sound *pSound, float gainDB)
ma_panner
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1644
ma_int8
int8_t ma_int8
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1498
MA_JOB_FREE_DATA_BUFFER
#define MA_JOB_FREE_DATA_BUFFER
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1144
ma_sound_group_set_doppler_factor
MA_API void ma_sound_group_set_doppler_factor(ma_sound_group *pGroup, float dopplerFactor)
ma_engine_get_device
MA_API ma_device * ma_engine_get_device(ma_engine *pEngine)
ma_panner::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1647
ma_sound_start
MA_API ma_result ma_sound_start(ma_sound *pSound)
ma_engine
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1968
ma_uintptr
uintptr_t ma_uintptr
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1509
ma_int16
int16_t ma_int16
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1500
ma_hpf_reinit
ma_result ma_hpf_reinit(const ma_hpf_config *pConfig, ma_hpf *pHPF)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31530
ma_biquad_node::biquad
ma_biquad biquad
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2143
ma_hpf_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1985
ma_resource_manager_data_source_get_looping
MA_API ma_result ma_resource_manager_data_source_get_looping(const ma_resource_manager_data_source *pDataSource, ma_bool32 *pIsLooping)
c89atomic_load_explicit_f32
static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile float *ptr, c89atomic_memory_order order)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4142
ma_sound_set_start_time_in_milliseconds
MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
ma_semaphore_release
MA_API ma_result ma_semaphore_release(ma_semaphore *pSemaphore)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4974
ma_resource_manager_data_stream_seek_to_pcm_frame
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream *pDataStream, ma_uint64 frameIndex)
MA_DATA_SOURCE_FLAG_WAIT_INIT
#define MA_DATA_SOURCE_FLAG_WAIT_INIT
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1124
ma_paged_audio_buffer_data::pTail
MA_ATOMIC ma_paged_audio_buffer_page * pTail
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:82
ma_resource_manager_data_stream_get_data_format
MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream *pDataStream, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate)
ma_fader_config::format
ma_format format
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1662
ma_sound_group_set_gain_db
MA_API ma_result ma_sound_group_set_gain_db(ma_sound_group *pGroup, float gainDB)
c89atomic_exchange_32
#define c89atomic_exchange_32(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3992
ma_node_output_bus::pInputNode
MA_ATOMIC ma_node * pInputNode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:971
ma_data_source
void ma_data_source
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4500
MA_NODE_FLAG_ALLOW_NULL_INPUT
#define MA_NODE_FLAG_ALLOW_NULL_INPUT
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:880
ma_spatializer_config::coneOuterAngleInRadians
float coneOuterAngleInRadians
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1775
ma_biquad_node_config_init
MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2)
ma_lpf_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2154
ma_get_accumulation_bytes_per_sample
MA_API size_t ma_get_accumulation_bytes_per_sample(ma_format format)
ma_spatializer_listener_set_direction
MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener *pListener, float x, float y, float z)
ma_node_vtable::onGetRequiredInputFrameCount
ma_uint32(* onGetRequiredInputFrameCount)(ma_node *pNode, ma_uint32 outputFrameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:914
ma_sound_is_looping
MA_API ma_bool32 ma_sound_is_looping(const ma_sound *pSound)
ma_semaphore_init
MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore *pSemaphore)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4929
ma_engine_node_config::pEngine
ma_engine * pEngine
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1856
ma_peak2_reinit
ma_result ma_peak2_reinit(const ma_peak2_config *pConfig, ma_peak2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32130
ma_peak_node::peak
ma_peak2 peak
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2258
ma_job_queue_init
MA_API ma_result ma_job_queue_init(ma_uint32 flags, ma_job_queue *pQueue)
ma_engine_node_config::type
ma_engine_node_type type
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1857
ma_engine_start
MA_API ma_result ma_engine_start(ma_engine *pEngine)
ma_notch2_process_pcm_frames
ma_result ma_notch2_process_pcm_frames(ma_notch2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32029
ma_format
ma_format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1779
ma_offset_pcm_frames_ptr_f32
static MA_INLINE float * ma_offset_pcm_frames_ptr_f32(float *p, ma_uint64 offsetInFrames, ma_uint32 channels)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4447
MA_API
#define MA_API
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:174
ma_engine_get_listener_count
MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine *pEngine)
ma_spatializer_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1761
ma_semaphore
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2892
ma_splitter_node_config_init
MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)
ma_fader::cursorInFrames
ma_uint64 cursorInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1675
ma_spatializer_listener_config::handedness
ma_handedness handedness
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1719
ma_attenuation_model
ma_attenuation_model
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1694
ma_node
void ma_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:874
ma_async_notification_poll::cb
ma_async_notification_callbacks cb
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1228
MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT
#define MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1838
ma_paged_audio_buffer_get_length_in_pcm_frames
MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer *pPagedAudioBuffer, ma_uint64 *pLength)
ma_sound
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1912
ma_data_source_init
MA_API ma_result ma_data_source_init(const ma_data_source_config *pConfig, ma_data_source *pDataSource)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37224
ma_sound_inlined::sound
ma_sound sound
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1934
ma_node_detach_output_bus
MA_API ma_result ma_node_detach_output_bus(ma_node *pNode, ma_uint32 outputBusIndex)
ma_device_type_playback
@ ma_device_type_playback
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3012
ma_handedness_left
@ ma_handedness_left
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1711
ma_engine_data_callback
MA_API void ma_engine_data_callback(ma_engine *pEngine, void *pOutput, const void *pInput, ma_uint32 frameCount)
ma_sound_group_set_stop_time_in_pcm_frames
MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInFrames)
ma_spatializer_set_max_gain
MA_API void ma_spatializer_set_max_gain(ma_spatializer *pSpatializer, float maxGain)
ma_spatializer_process_pcm_frames
MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer *pSpatializer, ma_spatializer_listener *pListener, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
ma_node_get_node_graph
MA_API ma_node_graph * ma_node_get_node_graph(const ma_node *pNode)
ma_loshelf2_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2131
ma_engine::pDevice
ma_device * pDevice
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1972
ma_lpf_init
ma_result ma_lpf_init(const ma_lpf_config *pConfig, ma_lpf *pLPF)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31004
ma_node_vtable
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:892
ma_clip_s24
static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:28045
ma_resource_manager_data_buffer::isConnectorInitialized
ma_bool32 isConnectorInitialized
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1461
c89atomic_exchange_ptr
#define c89atomic_exchange_ptr(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3971
ma_splitter_node_config::nodeConfig
ma_node_config nodeConfig
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1099
ma_paged_audio_buffer_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:96
ma_sound_set_direction
MA_API void ma_sound_set_direction(ma_sound *pSound, float x, float y, float z)
ma_async_notification_event::e
ma_event e
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1244
ma_engine::pLog
ma_log * pLog
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1973
ma_node_get_state_time
MA_API ma_uint64 ma_node_get_state_time(const ma_node *pNode, ma_node_state state)
ma_delay_node_config_init
MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
ma_allocation_callbacks
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1823
ma_audio_buffer_get_available_frames
MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer *pAudioBuffer, ma_uint64 *pAvailableFrames)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:38492
ma_engine_set_gain_db
MA_API ma_result ma_engine_set_gain_db(ma_engine *pEngine, float gainDB)
ma_resource_manager_data_buffer::pResourceManager
ma_resource_manager * pResourceManager
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1452
ma_gainer_set_gain
MA_API ma_result ma_gainer_set_gain(ma_gainer *pGainer, float newGain)
ma_spatializer_listener::config
ma_spatializer_listener_config config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1732
ma_loshelf2_config_init
ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32182
ma_resource_manager_data_supply_type_decoded
@ ma_resource_manager_data_supply_type_decoded
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1403
ma_panner_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1633
ma_job_queue::head
ma_uint64 head
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1374
MA_TOO_BIG
#define MA_TOO_BIG
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1670
ma_resource_manager_data_stream_unmap
MA_API ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream *pDataStream, ma_uint64 frameCount)
ma_resource_manager_config::flags
ma_uint32 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1519
MA_NODE_OUTPUT_BUS_FLAG_HAS_READ
#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:952
ma_fence_acquire
MA_API ma_result ma_fence_acquire(ma_fence *pFence)
ma_engine_node_config::channelsIn
ma_uint32 channelsIn
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1858
ma_paged_audio_buffer_data::format
ma_format format
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:79
ma_resource_manager_register_decoded_data_w
MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager *pResourceManager, const wchar_t *pName, const void *pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
ma_resource_manager::pRootDataBufferNode
ma_resource_manager_data_buffer_node * pRootDataBufferNode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1531
ma_engine_node::pEngine
ma_engine * pEngine
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1873
ma_resampler_config::sampleRateIn
ma_uint32 sampleRateIn
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2244
ma_node_graph_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1045
ma_sound_group_set_pinned_listener_index
MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group *pGroup, ma_uint8 listenerIndex)
c89atomic_compare_and_swap_32
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32 *dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3463
ma_delay_node::baseNode
ma_node_base baseNode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2361
ma_sound_set_start_time_in_pcm_frames
MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInFrames)
ma_format_s24
@ ma_format_s24
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1788
ma_engine_node::isSpatializationDisabled
MA_ATOMIC ma_bool8 isSpatializationDisabled
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1883
ma_paged_audio_buffer_data_get_tail
MA_API ma_paged_audio_buffer_page * ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data *pData)
ma_paged_audio_buffer_data::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:80
ma_lpf_node::lpf
ma_lpf lpf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2166
ma_vec3f::z
float z
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1691
ma_sound_inlined::pPrev
ma_sound_inlined * pPrev
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1936
ma_device_config::dataCallback
ma_device_callback_proc dataCallback
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3110
ma_sound_get_rolloff
MA_API float ma_sound_get_rolloff(const ma_sound *pSound)
ma_vfs_open_and_read_file_ex
static ma_result ma_vfs_open_and_read_file_ex(ma_vfs *pVFS, const char *pFilePath, const wchar_t *pFilePathW, void **ppData, size_t *pSize, const ma_allocation_callbacks *pAllocationCallbacks, ma_uint32 allocationType)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:38667
ma_biquad_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1843
c89atomic_fetch_add_16
#define c89atomic_fetch_add_16(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4003
ma_sound_group_set_min_gain
MA_API void ma_sound_group_set_min_gain(ma_sound_group *pGroup, float minGain)
ma_resource_manager_data_stream::isDecoderAtEnd
MA_ATOMIC ma_bool32 isDecoderAtEnd
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1493
ma_resource_manager_data_stream_get_cursor_in_pcm_frames
MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream *pDataStream, ma_uint64 *pCursor)
MA_UNAVAILABLE
#define MA_UNAVAILABLE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1681
ma_async_notification_event_init
MA_API ma_result ma_async_notification_event_init(ma_async_notification_event *pNotificationEvent)
ma_biquad_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1846
ma_data_source_seek_to_pcm_frame
MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source *pDataSource, ma_uint64 frameIndex)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37549
ma_resource_manager_data_buffer_result
MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer *pDataBuffer)
ma_sound_group_set_positioning
MA_API void ma_sound_group_set_positioning(ma_sound_group *pGroup, ma_positioning positioning)
ma_sound_set_min_distance
MA_API void ma_sound_set_min_distance(ma_sound *pSound, float minDistance)
ma_thread_result
void * ma_thread_result
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4266
ma_spatializer_set_min_gain
MA_API void ma_spatializer_set_min_gain(ma_spatializer *pSpatializer, float minGain)
ma_peak2_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2102
ma_paged_audio_buffer_config_init
MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data *pData)
ma_sound_init_ex
MA_API ma_result ma_sound_init_ex(ma_engine *pEngine, const ma_sound_config *pConfig, ma_sound *pSound)
ma_spinlock_lock
MA_API ma_result ma_spinlock_lock(volatile ma_spinlock *pSpinlock)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4291
ma_fader_get_data_format
MA_API void ma_fader_get_data_format(const ma_fader *pFader, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate)
ma_data_source_node_set_looping
MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node *pDataSourceNode, ma_bool32 looping)
ma_spatializer_listener_set_velocity
MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener *pListener, float x, float y, float z)
ma_paged_audio_buffer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:104
ma_sound_get_cone
MA_API void ma_sound_get_cone(const ma_sound *pSound, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
ma_node_set_state
MA_API ma_result ma_node_set_state(ma_node *pNode, ma_node_state state)
ma_node_get_state
MA_API ma_node_state ma_node_get_state(const ma_node *pNode)
ma_engine_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1854
ma_node_base::cachedFrameCountOut
ma_uint16 cachedFrameCountOut
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1001
ma_resource_manager_config::pCustomDecodingBackendUserData
void * pCustomDecodingBackendUserData
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1523
ma_device_set_master_volume
ma_result ma_device_set_master_volume(ma_device *pDevice, float volume)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27617
ma_resource_manager_data_stream_get_looping
MA_API ma_result ma_resource_manager_data_stream_get_looping(const ma_resource_manager_data_stream *pDataStream, ma_bool32 *pIsLooping)
ma_resource_manager::config
ma_resource_manager_config config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1530
MA_CHANNEL_MONO
#define MA_CHANNEL_MONO
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1602
ma_device_config::periodSizeInFrames
ma_uint32 periodSizeInFrames
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3104
ma_decoder::outputFormat
ma_format outputFormat
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5203
ma_node_config_init
MA_API ma_node_config ma_node_config_init(void)
ma_gainer_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:399
ma_resource_manager_data_buffer_init_copy
MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager *pResourceManager, const ma_resource_manager_data_buffer *pExistingDataBuffer, ma_resource_manager_data_buffer *pDataBuffer)
ma_node_input_bus::channels
ma_uint8 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:987
ma_data_source_vtable
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4502
c89atomic_compare_exchange_weak_ptr
#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3973
ma_node_base::pInputBuses
ma_node_input_bus * pInputBuses
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1011
ma_handedness
ma_handedness
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1708
ma_spatializer_set_direction
MA_API void ma_spatializer_set_direction(ma_spatializer *pSpatializer, float x, float y, float z)
ma_spatializer_get_cone
MA_API void ma_spatializer_get_cone(const ma_spatializer *pSpatializer, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
ma_loshelf_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2278
ma_format_unknown
@ ma_format_unknown
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1785
ma_resampler_uninit
void ma_resampler_uninit(ma_resampler *pResampler)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:33154
ma_delay_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2359
ma_log_postf
MA_API ma_result ma_log_postf(ma_log *pLog, ma_uint32 level, const char *pFormat,...)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:2305
ma_dither_mode_none
@ ma_dither_mode_none
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1774
ma_engine_node_config_init
MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine *pEngine, ma_engine_node_type type, ma_uint32 flags)
ma_resource_manager_data_buffer_node::refCount
ma_uint32 refCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1438
ma_engine_node::oldPitch
float oldPitch
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1880
ma_peak2_init
ma_result ma_peak2_init(const ma_peak2_config *pConfig, ma_peak2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32106
ma_peak2_process_pcm_frames
ma_result ma_peak2_process_pcm_frames(ma_peak2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32158
ma_node_vtable::inputBusCount
ma_uint8 inputBusCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:920
ma_node_get_state_by_time
MA_API ma_node_state ma_node_get_state_by_time(const ma_node *pNode, ma_uint64 globalTime)
ma_spatializer_config_init
MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)
ma_engine::pInlinedSoundHead
ma_sound_inlined * pInlinedSoundHead
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1980
ma_engine_node::pinnedListenerIndex
MA_ATOMIC ma_uint8 pinnedListenerIndex
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1884
ma_resource_manager_unregister_data
MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager *pResourceManager, const char *pName)
ma_handedness
ma_handedness
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1708
ma_data_source_node::base
ma_node_base base
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1085
ma_node_vtable::flags
ma_uint32 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:932
ma_peak2_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2096
ma_pipeline_stage_notification
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1253
ma_resource_manager_data_source_get_available_frames
MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source *pDataSource, ma_uint64 *pAvailableFrames)
MA_DATA_SOURCE_FLAG_ASYNC
#define MA_DATA_SOURCE_FLAG_ASYNC
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1123
ma_engine_node::sampleRate
ma_uint32 sampleRate
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1874
ma_fence_uninit
MA_API void ma_fence_uninit(ma_fence *pFence)
f
f
ma_engine_node_config::isSpatializationDisabled
ma_bool8 isSpatializationDisabled
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1862
ma_channel_mix_mode_simple
@ ma_channel_mix_mode_simple
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1797
ma_panner_set_pan
MA_API void ma_panner_set_pan(ma_panner *pPanner, float pan)
MA_NODE_FLAG_CONTINUOUS_PROCESSING
#define MA_NODE_FLAG_CONTINUOUS_PROCESSING
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:879
ma_sound::atEnd
MA_ATOMIC ma_bool8 atEnd
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1918
MA_CHANNEL_SIDE_RIGHT
#define MA_CHANNEL_SIDE_RIGHT
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1613
ma_default_vfs
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4670
ma_delay_node_config::nodeConfig
ma_node_config nodeConfig
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2352
ma_is_spatial_channel_position
static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:33758
ma_spatializer_listener_get_position
MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener *pListener)
ma_node_graph_init
MA_API ma_result ma_node_graph_init(const ma_node_graph_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_node_graph *pNodeGraph)
ma_sound_group_get_cone
MA_API void ma_sound_group_get_cone(const ma_sound_group *pGroup, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
ma_node_get_input_bus_count
MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node *pNode)
ma_paged_audio_buffer_data_allocate_page
MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data *pData, ma_uint64 pageSizeInFrames, const void *pInitialData, const ma_allocation_callbacks *pAllocationCallbacks, ma_paged_audio_buffer_page **ppPage)
ma_async_notification_event_uninit
MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event *pNotificationEvent)
ma_engine_node_type_group
@ ma_engine_node_type_group
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1851
ma_apply_volume_factor_per_channel_f32
MA_API void ma_apply_volume_factor_per_channel_f32(float *pFramesOut, ma_uint64 frameCount, ma_uint32 channels, float *pChannelGains)
ma_fader_config_init
MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
ma_resource_manager_data_buffer_set_looping
MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer *pDataBuffer, ma_bool32 isLooping)
ma_hpf_init
ma_result ma_hpf_init(const ma_hpf_config *pConfig, ma_hpf *pHPF)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31515
ma_engine_node_init_preallocated
MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config *pConfig, void *pHeap, ma_engine_node *pEngineNode)
ma_spatializer_listener_uninit
MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener *pListener, const ma_allocation_callbacks *pAllocationCallbacks)
ma_engine_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1953
ma_spatializer_set_velocity
MA_API void ma_spatializer_set_velocity(ma_spatializer *pSpatializer, float x, float y, float z)
ma_resource_manager_data_stream_init
MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager *pResourceManager, const char *pFilePath, ma_uint32 flags, const ma_pipeline_notifications *pNotifications, ma_resource_manager_data_stream *pDataStream)
ma_decoder_seek_to_pcm_frame
ma_result ma_decoder_seek_to_pcm_frame(ma_decoder *pDecoder, ma_uint64 frameIndex)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44902
ma_node_attach_output_bus
MA_API ma_result ma_node_attach_output_bus(ma_node *pNode, ma_uint32 outputBusIndex, ma_node *pOtherNode, ma_uint32 otherNodeInputBusIndex)
ma_loshelf2_reinit
ma_result ma_loshelf2_reinit(const ma_loshelf2_config *pConfig, ma_loshelf2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32256
ma_decoder_get_available_frames
MA_API ma_result ma_decoder_get_available_frames(ma_decoder *pDecoder, ma_uint64 *pAvailableFrames)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44936
ma_resampler_process_pcm_frames
ma_result ma_resampler_process_pcm_frames(ma_resampler *pResampler, const void *pFramesIn, ma_uint64 *pFrameCountIn, void *pFramesOut, ma_uint64 *pFrameCountOut)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:33398
MA_SOUND_FLAG_NO_PITCH
#define MA_SOUND_FLAG_NO_PITCH
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1839
ma_engine_node::isPitchDisabled
MA_ATOMIC ma_bool8 isPitchDisabled
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1882
ma_lpf_reinit
ma_result ma_lpf_reinit(const ma_lpf_config *pConfig, ma_lpf *pLPF)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31019
ma_fader_config::sampleRate
ma_uint32 sampleRate
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1664
ma_result_description
const char * ma_result_description(ma_result result)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37039
ma_handedness_right
@ ma_handedness_right
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1710
c89atomic_compare_and_swap_64
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64 *dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3473
ma_hpf_config_init
ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31408
ma_bpf_node_init
MA_API ma_result ma_bpf_node_init(ma_node_graph *pNodeGraph, const ma_bpf_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_bpf_node *pNode)
ma_delay_get_decay
MA_API float ma_delay_get_decay(const ma_delay *pDelay)
ma_attenuation_model_inverse
@ ma_attenuation_model_inverse
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1697
ma_node_base::_inputBuses
ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1015
ma_gainer_uninit
MA_API void ma_gainer_uninit(ma_gainer *pGainer, const ma_allocation_callbacks *pAllocationCallbacks)
ma_hpf_node::hpf
ma_hpf hpf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2189
ma_powd
static MA_INLINE double ma_powd(double x, double y)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:805
ma_peak2
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2108
ma_engine_listener_set_world_up
MA_API void ma_engine_listener_set_world_up(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
ma_sound_get_velocity
MA_API ma_vec3f ma_sound_get_velocity(const ma_sound *pSound)
MA_JOB_LOAD_DATA_BUFFER_NODE
#define MA_JOB_LOAD_DATA_BUFFER_NODE
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1140
ma_max
#define ma_max(x, y)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:777
ma_spatializer_get_velocity
MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer *pSpatializer)
ma_resource_manager_data_source::flags
ma_uint32 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1506
ma_panner_config::pan
float pan
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1638
c89atomic_load_i32
#define c89atomic_load_i32(ptr)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4084
ma_resampler
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2260
ma_sound_config::channelsOut
ma_uint32 channelsOut
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1905
ma_sound_group_set_volume
MA_API ma_result ma_sound_group_set_volume(ma_sound_group *pGroup, float volume)
ma_spatializer_listener_init
MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_spatializer_listener *pListener)
ma_job_queue::tail
ma_uint64 tail
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1375
ma_node_get_heap_size
MA_API ma_result ma_node_get_heap_size(const ma_node_config *pConfig, size_t *pHeapSizeInBytes)
ma_spatializer_listener_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1715
ma_lpf_config_init
ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:30897
ma_resource_manager_data_buffer_node::pChildLo
ma_resource_manager_data_buffer_node * pChildLo
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1445
ma_async_notification_poll_init
MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll *pNotificationPoll)
ma_spatializer_config::pChannelMapIn
ma_channel * pChannelMapIn
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1765
ma_sound_group_get_min_distance
MA_API float ma_sound_group_get_min_distance(const ma_sound_group *pGroup)
ma_resampler::config
ma_resampler_config config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2262
ma_bpf_reinit
ma_result ma_bpf_reinit(const ma_bpf_config *pConfig, ma_bpf *pBPF)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31848
ma_spatializer_config::coneOuterGain
float coneOuterGain
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1776
MA_COPY_MEMORY
#define MA_COPY_MEMORY(dst, src, sz)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:754
ma_spatializer_listener::_pHeap
void * _pHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1738
ma_pipeline_notifications::init
ma_pipeline_stage_notification init
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1261
ma_resource_manager_uninit
MA_API void ma_resource_manager_uninit(ma_resource_manager *pResourceManager)
ma_node_set_state_time
MA_API ma_result ma_node_set_state_time(ma_node *pNode, ma_node_state state, ma_uint64 globalTime)
ma_resource_manager_data_supply_type
ma_resource_manager_data_supply_type
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1399
ma_resampler_config::linear
struct ma_resampler_config::@75 linear
ma_engine_listener_get_direction
MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine *pEngine, ma_uint32 listenerIndex)
ma_spatializer_get_min_gain
MA_API float ma_spatializer_get_min_gain(const ma_spatializer *pSpatializer)
ma_engine_node::oldDopplerPitch
float oldDopplerPitch
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1881
ma_engine_node_uninit
MA_API void ma_engine_node_uninit(ma_engine_node *pEngineNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_NODE_BUS_COUNT_UNKNOWN
#define MA_NODE_BUS_COUNT_UNKNOWN
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:871
ma_spatializer_listener::direction
ma_vec3f direction
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1734
ma_resampler_config_init
ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:33078
ma_notch_node_reinit
MA_API ma_result ma_notch_node_reinit(const ma_notch_config *pConfig, ma_notch_node *pNode)
ma_resource_manager_data_buffer_node::executionCounter
ma_uint32 executionCounter
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1440
ma_async_notification_callbacks
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1213
ma_notch2_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2075
ma_node_base::_ownsHeap
ma_bool32 _ownsHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1018
ma_sound_group_get_rolloff
MA_API float ma_sound_group_get_rolloff(const ma_sound_group *pGroup)
ma_resource_manager_data_source_get_data_format
MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate)
ma_hishelf2_reinit
ma_result ma_hishelf2_reinit(const ma_hishelf2_config *pConfig, ma_hishelf2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32382
c89atomic_exchange_explicit_f32
static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float *dst, float src, c89atomic_memory_order order)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4154
ma_engine_config::allocationCallbacks
ma_allocation_callbacks allocationCallbacks
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1960
ma_paged_audio_buffer_uninit
MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer *pPagedAudioBuffer)
ma_spatializer_config::positioning
ma_positioning positioning
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1767
ma_sound_config::pInitialAttachment
ma_node * pInitialAttachment
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1902
ma_pcm_sample_u8_to_s16_no_scale
static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:28017
ma_delay_set_decay
MA_API void ma_delay_set_decay(ma_delay *pDelay, float value)
ma_resource_manager_data_buffer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1449
ma_delay_get_wet
MA_API float ma_delay_get_wet(const ma_delay *pDelay)
ma_sound_group_set_pan
MA_API void ma_sound_group_set_pan(ma_sound_group *pGroup, float pan)
ma_result
int ma_result
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1658
ma_sound_set_attenuation_model
MA_API void ma_sound_set_attenuation_model(ma_sound *pSound, ma_attenuation_model attenuationModel)
ma_device_config::noPreZeroedOutputBuffer
ma_bool32 noPreZeroedOutputBuffer
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3108
ma_spatializer_config::minDistance
float minDistance
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1771
ma_sound_group_set_stop_time_in_milliseconds
MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
ma_context_config_init
ma_context_config ma_context_config_init(void)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:26542
ma_resource_manager_config::pLog
ma_log * pLog
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1514
ma_resource_manager_data_source_map
MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source *pDataSource, void **ppFramesOut, ma_uint64 *pFrameCount)
ma_resource_manager_data_stream::pPageData
void * pPageData
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1488
ma_spatializer_uninit
MA_API void ma_spatializer_uninit(ma_spatializer *pSpatializer, const ma_allocation_callbacks *pAllocationCallbacks)
ma_positioning
ma_positioning
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1702
ma_biquad
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1857
ma_delay::pBuffer
float * pBuffer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2335
ma_engine_config::pPlaybackDeviceID
ma_device_id * pPlaybackDeviceID
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1959
ma_async_notification_event_signal
MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event *pNotificationEvent)
ma_data_source_node::looping
MA_ATOMIC ma_bool32 looping
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1087
ma_lpf_node_config::lpf
ma_lpf_config lpf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2157
ma_engine_config::noAutoStart
ma_bool32 noAutoStart
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1961
ma_node_base::consumedFrameCountIn
ma_uint16 consumedFrameCountIn
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1003
ma_data_source_get_cursor_in_pcm_frames
MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 *pCursor)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37655
ma_resource_manager_data_stream
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1470
ma_resource_manager_data_stream::ds
ma_data_source_base ds
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1472
ma_paged_audio_buffer_data_get_length_in_pcm_frames
MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data *pData, ma_uint64 *pLength)
ma_resource_manager_config::allocationCallbacks
ma_allocation_callbacks allocationCallbacks
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1513
ma_offset_ptr
#define ma_offset_ptr(p, offset)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:781
ma_biquad_node_init
MA_API ma_result ma_biquad_node_init(ma_node_graph *pNodeGraph, const ma_biquad_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_biquad_node *pNode)
ma_mutex_uninit
void ma_mutex_uninit(ma_mutex *pMutex)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4784
ma_resource_manager_data_buffer_uninit
MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer *pDataBuffer)
MA_THREADCALL
#define MA_THREADCALL
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4265
ma_event_wait
MA_API ma_result ma_event_wait(ma_event *pEvent)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4898
ma_biquad_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2140
ma_resource_manager_data_stream::isDecoderInitialized
ma_bool32 isDecoderInitialized
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1476
ma_splitter_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1097
ma_fence_init
MA_API ma_result ma_fence_init(ma_fence *pFence)
ma_bpf_node_config::bpf
ma_bpf_config bpf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2203
ma_resource_manager_data_buffer_seek_to_pcm_frame
MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 frameIndex)
ma_peak_node_config_init
MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
ma_node_output_bus::refCount
MA_ATOMIC ma_uint16 refCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:965
ma_engine_node_type
ma_engine_node_type
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1848
ma_spatializer_config::maxGain
float maxGain
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1770
ma_spatializer_set_attenuation_model
MA_API void ma_spatializer_set_attenuation_model(ma_spatializer *pSpatializer, ma_attenuation_model attenuationModel)
ma_resource_manager_data_stream::executionPointer
ma_uint32 executionPointer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1482
ma_positioning_absolute
@ ma_positioning_absolute
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1704
ma_panner::mode
ma_pan_mode mode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1648
ma_clip_s16
static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:28040
ma_spatializer_get_input_channels
MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer *pSpatializer)
ma_sound_get_min_gain
MA_API float ma_sound_get_min_gain(const ma_sound *pSound)
ma_event_init
MA_API ma_result ma_event_init(ma_event *pEvent)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4829
ma_node_graph_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1047
ma_spatializer_listener_config::worldUp
ma_vec3f worldUp
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1724
ma_sound_group_set_pitch
MA_API void ma_sound_group_set_pitch(ma_sound_group *pGroup, float pitch)
ma_job::freeDataBuffer
struct ma_job::@271::@278 freeDataBuffer
ma_resource_manager_data_stream::currentPageIndex
ma_uint32 currentPageIndex
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1480
ma_sound_set_min_gain
MA_API void ma_sound_set_min_gain(ma_sound *pSound, float minGain)
ma_waveform_init
ma_result ma_waveform_init(const ma_waveform_config *pConfig, ma_waveform *pWaveform)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:45445
c89atomic_load_16
#define c89atomic_load_16(ptr)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3987
ma_spatializer_config::channelsIn
ma_uint32 channelsIn
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1763
ma_clip_f32
static MA_INLINE float ma_clip_f32(float x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:2339
ma_engine_config::gainSmoothTimeInMilliseconds
ma_uint32 gainSmoothTimeInMilliseconds
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1958
ma_fader_init
MA_API ma_result ma_fader_init(const ma_fader_config *pConfig, ma_fader *pFader)
ma_async_notification_event
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1241
ma_sound_group
ma_sound ma_sound_group
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1941
ma_attenuation_model_none
@ ma_attenuation_model_none
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1696
ma_biquad_reinit
ma_result ma_biquad_reinit(const ma_biquad_config *pConfig, ma_biquad *pBQ)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:30459
ma_paged_audio_buffer_data
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:77
ma_node_config::vtable
const ma_node_vtable * vtable
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:937
ma_node_input_bus
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:979
ma_sound_group_set_direction
MA_API void ma_sound_group_set_direction(ma_sound_group *pGroup, float x, float y, float z)
ma_node_base::localTime
MA_ATOMIC ma_uint64 localTime
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1008
MA_JOB_PAGE_DATA_STREAM
#define MA_JOB_PAGE_DATA_STREAM
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1147
ma_fader_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1663
ma_sound_init_from_file_w
MA_API ma_result ma_sound_init_from_file_w(ma_engine *pEngine, const wchar_t *pFilePath, ma_uint32 flags, ma_sound_group *pGroup, ma_fence *pDoneFence, ma_sound *pSound)
ma_engine_set_time
MA_API ma_uint64 ma_engine_set_time(ma_engine *pEngine, ma_uint64 globalTime)
ma_engine_uninit
MA_API void ma_engine_uninit(ma_engine *pEngine)
MA_CHANNEL_INDEX_NULL
#define MA_CHANNEL_INDEX_NULL
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:1276
ma_gainer_get_heap_size
MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config *pConfig, size_t *pHeapSizeInBytes)
ma_fader
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1669
ma_sound_get_cursor_in_pcm_frames
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound *pSound, ma_uint64 *pCursor)
ma_sound_set_positioning
MA_API void ma_sound_set_positioning(ma_sound *pSound, ma_positioning positioning)
ma_node_output_bus::flags
MA_ATOMIC ma_uint8 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:964
ma_slot_allocator_alloc
MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator *pAllocator, ma_uint64 *pSlot)
ma_mix_f32
static MA_INLINE float ma_mix_f32(float x, float y, float a)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:2346
ma_engine_config_init
MA_API ma_engine_config ma_engine_config_init(void)
ma_delay::config
ma_delay_config config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2332
MA_FLT_MAX
#define MA_FLT_MAX
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:575
ma_sound_set_pan_mode
MA_API void ma_sound_set_pan_mode(ma_sound *pSound, ma_pan_mode panMode)
ma_resource_manager_unregister_file
MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager *pResourceManager, const char *pFilePath)
ma_decoder_init_memory
ma_result ma_decoder_init_memory(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:43977
ma_spatializer::_pHeap
void * _pHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1795
ma_resource_manager_post_job
MA_API ma_result ma_resource_manager_post_job(ma_resource_manager *pResourceManager, const ma_job *pJob)
ma_job::freeDataBufferNode
struct ma_job::@271::@275 freeDataBufferNode
ma_sound_group_set_start_time_in_pcm_frames
MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInFrames)
ma_async_notification_event_wait
MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event *pNotificationEvent)
ma_panner_init
MA_API ma_result ma_panner_init(const ma_panner_config *pConfig, ma_panner *pPanner)
ma_resource_manager_data_source_init
MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager *pResourceManager, const char *pName, ma_uint32 flags, const ma_pipeline_notifications *pNotifications, ma_resource_manager_data_source *pDataSource)
ma_async_notification
void ma_async_notification
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1211
ma_resource_manager_data_buffer::seekToCursorOnNextRead
ma_bool32 seekToCursorOnNextRead
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1458
ma_peak2_config_init
ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32054
ma_clip_u8
static MA_INLINE ma_uint8 ma_clip_u8(ma_int16 x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:28035
ma_resource_manager_process_next_job
MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager *pResourceManager)
ma_node_output_bus::channels
ma_uint8 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:960
ma_engine_find_closest_listener
MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine *pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
count
size_t count
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/tests/test_common/ma_test_common.c:31
ma_resource_manager_data_buffer_get_length_in_pcm_frames
MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 *pLength)
ma_job_queue
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1371
ma_engine_config::periodSizeInMilliseconds
ma_uint32 periodSizeInMilliseconds
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1956
ma_hishelf2_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2158
ma_audio_buffer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4597
ma_sound_get_pinned_listener_index
MA_API ma_uint8 ma_sound_get_pinned_listener_index(const ma_sound *pSound)
ma_bpf_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2042
ma_lpf_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1917
ma_engine_init
MA_API ma_result ma_engine_init(const ma_engine_config *pConfig, ma_engine *pEngine)
ma_spatializer_listener_get_velocity
MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener *pListener)
ma_pipeline_stage_notification::pFence
ma_fence * pFence
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1256
ma_semaphore_uninit
MA_API void ma_semaphore_uninit(ma_semaphore *pSemaphore)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4944
ma_resource_manager_config::jobThreadCount
ma_uint32 jobThreadCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1518
ma_engine_node_type
ma_engine_node_type
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1848
ma_async_notification_poll::signalled
ma_bool32 signalled
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1229
ma_engine::listeners
ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1975
ma_job::allocation
ma_uint64 allocation
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1278
ma_notch_node_config_init
MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
ma_sound_set_stop_time_in_milliseconds
MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
ma_spatializer_init_preallocated
MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config *pConfig, void *pHeap, ma_spatializer *pSpatializer)
ma_resource_manager_unregister_file_w
MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager *pResourceManager, const wchar_t *pFilePath)
ma_spatializer_get_position
MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer *pSpatializer)
ma_format_s16
@ ma_format_s16
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1787
ma_engine::inlinedSoundLock
ma_mutex inlinedSoundLock
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1979
MA_JOB_QUEUE_FLAG_NON_BLOCKING
#define MA_JOB_QUEUE_FLAG_NON_BLOCKING
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1369
ma_resource_manager_data_buffer_node::pParent
ma_resource_manager_data_buffer_node * pParent
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1444
ma_thread_wait
static void ma_thread_wait(ma_thread *pThread)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4754
ma_spatializer_set_position
MA_API void ma_spatializer_set_position(ma_spatializer *pSpatializer, float x, float y, float z)
ma_spatializer_listener_set_world_up
MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener *pListener, float x, float y, float z)
ma_device_set_master_gain_db
ma_result ma_device_set_master_gain_db(ma_device *pDevice, float gainDB)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27648
ma_spatializer_config::handedness
ma_handedness handedness
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1768
ma_node_state_started
@ ma_node_state_started
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:887
ma_pan_mode
ma_pan_mode
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1627
ma_sound_set_doppler_factor
MA_API void ma_sound_set_doppler_factor(ma_sound *pSound, float dopplerFactor)
ma_resource_manager_data_buffer_node::isDataOwnedByResourceManager
ma_bool32 isDataOwnedByResourceManager
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1442
ma_sound_set_looping
MA_API void ma_sound_set_looping(ma_sound *pSound, ma_bool8 isLooping)
ma_resource_manager_post_job_quit
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager *pResourceManager)
ma_resource_manager_data_source_init_w
MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager *pResourceManager, const wchar_t *pName, ma_uint32 flags, const ma_pipeline_notifications *pNotifications, ma_resource_manager_data_source *pDataSource)
ma_hpf_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1987
ma_lpf_node_reinit
MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config *pConfig, ma_lpf_node *pNode)
ma_pipeline_stage_notification::pNotification
ma_async_notification * pNotification
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1255
ma_resource_manager_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1511
ma_delay_set_dry
MA_API void ma_delay_set_dry(ma_delay *pDelay, float value)
ma_decoder_init_vfs_w
MA_API ma_result ma_decoder_init_vfs_w(ma_vfs *pVFS, const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44509
ma_data_source_node_init
MA_API ma_result ma_data_source_node_init(ma_node_graph *pNodeGraph, const ma_data_source_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_data_source_node *pDataSourceNode)
MA_LOG_LEVEL_WARNING
#define MA_LOG_LEVEL_WARNING
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1590
MA_DOES_NOT_EXIST
#define MA_DOES_NOT_EXIST
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1666
ma_sound_is_playing
MA_API ma_bool32 ma_sound_is_playing(const ma_sound *pSound)
ma_notch_node_config::notch
ma_notch_config notch
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2226
ma_device::pUserData
void * pUserData
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3592
ma_loshelf2_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2124
d
d
ma_attenuation_model_linear
@ ma_attenuation_model_linear
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1698
ma_hishelf_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2292
MA_NODE_FLAG_PASSTHROUGH
#define MA_NODE_FLAG_PASSTHROUGH
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:878
ma_delay_node_init
MA_API ma_result ma_delay_node_init(ma_node_graph *pNodeGraph, const ma_delay_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_delay_node *pDelayNode)
ma_resource_manager_data_buffer_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1435
ma_engine::pResourceManager
ma_resource_manager * pResourceManager
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1971
ma_device_id
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3055
ma_node_config::pOutputChannels
const ma_uint32 * pOutputChannels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:942
ma_resampler_config::lpfOrder
ma_uint32 lpfOrder
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2249
ma_async_notification
void ma_async_notification
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1211
ma_paged_audio_buffer_page::sizeInFrames
ma_uint64 sizeInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:73
MA_JOB_FREE_DATA_STREAM
#define MA_JOB_FREE_DATA_STREAM
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1146
ma_async_notification_event::cb
ma_async_notification_callbacks cb
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1243
ma_sound_group_uninit
MA_API void ma_sound_group_uninit(ma_sound_group *pGroup)
ma_resource_manager_data_supply::encoded
struct ma_resource_manager_data_supply::@284::@286 encoded
ma_resource_manager_data_source_seek_to_pcm_frame
MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source *pDataSource, ma_uint64 frameIndex)
ma_sound_at_end
MA_API ma_bool32 ma_sound_at_end(const ma_sound *pSound)
ma_gainer_config::smoothTimeInFrames
ma_uint32 smoothTimeInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:400
c89atomic_fetch_or_8
#define c89atomic_fetch_or_8(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4010
ma_sound_group_set_spatialization_enabled
MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group *pGroup, ma_bool32 enabled)
ma_resource_manager_data_supply::type
MA_ATOMIC ma_resource_manager_data_supply_type type
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1409
ma_spatializer_listener::_ownsHeap
ma_bool32 _ownsHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1739
ma_delay_set_wet
MA_API void ma_delay_set_wet(ma_delay *pDelay, float value)
ma_spatializer_get_positioning
MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer *pSpatializer)
ma_sound_group_get_doppler_factor
MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group *pGroup)
ma_async_notification_callbacks::onSignal
void(* onSignal)(ma_async_notification *pNotification)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1215
ma_sound_get_data_source
MA_API ma_data_source * ma_sound_get_data_source(const ma_sound *pSound)
c89atomic_load_8
#define c89atomic_load_8(ptr)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3986
ma_loshelf_node_reinit
MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config *pConfig, ma_loshelf_node *pNode)
ma_spatializer_listener_set_speed_of_sound
MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener *pListener, float speedOfSound)
ma_sound_group_set_fade_in_pcm_frames
MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group *pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
ma_sound_group_stop
MA_API ma_result ma_sound_group_stop(ma_sound_group *pGroup)
ma_get_standard_channel_map
void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
ma_sound_config::flags
ma_uint32 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1906
ma_sound_set_cone
MA_API void ma_sound_set_cone(ma_sound *pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
ma_resource_manager_data_source::buffer
ma_resource_manager_data_buffer buffer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1502
ma_panner::format
ma_format format
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1646
ma_notch2_config_init
ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31928
ma_resource_manager_init
MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config *pConfig, ma_resource_manager *pResourceManager)
ma_spatializer_listener_get_direction
MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener *pListener)
ma_engine_node::baseNode
ma_node_base baseNode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1872
ma_engine_listener_set_velocity
MA_API void ma_engine_listener_set_velocity(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
ma_slot_allocator::groups
struct ma_slot_allocator::@269 groups[MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY/32]
ma_sound_group_get_attenuation_model
MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group *pGroup)
ma_resource_manager::jobQueue
ma_job_queue jobQueue
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1534
ma_pipeline_notifications_init
MA_API ma_pipeline_notifications ma_pipeline_notifications_init(void)
MA_INVALID_OPERATION
#define MA_INVALID_OPERATION
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1662
ma_peak_node_init
MA_API ma_result ma_peak_node_init(ma_node_graph *pNodeGraph, const ma_peak_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_peak_node *pNode)
ma_node_get_state_by_time_range
MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node *pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
ma_engine_play_sound
MA_API ma_result ma_engine_play_sound(ma_engine *pEngine, const char *pFilePath, ma_sound_group *pGroup)
ma_positioning
ma_positioning
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1702
ma_data_source_node_is_looping
MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node *pDataSourceNode)
ma_resource_manager_data_buffer::isLooping
MA_ATOMIC ma_bool32 isLooping
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1460
ma_spatializer_get_attenuation_model
MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer *pSpatializer)
ma_spatializer_set_rolloff
MA_API void ma_spatializer_set_rolloff(ma_spatializer *pSpatializer, float rolloff)
ma_decoder
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5193
ma_spatializer::gainer
ma_gainer gainer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1791
c89atomic_fetch_add_64
#define c89atomic_fetch_add_64(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4005
ma_resource_manager_data_stream_set_looping
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream *pDataStream, ma_bool32 isLooping)
ma_node_base::_outputBuses
ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1016
ma_sound_group_config
ma_sound_config ma_sound_group_config
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1940
ma_panner::pan
float pan
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1649
MA_ASSERT
#define MA_ASSERT(condition)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:770
ma_node_graph_read_pcm_frames
MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph *pNodeGraph, void *pFramesOut, ma_uint32 frameCount, ma_uint32 *pFramesRead)
c89atomic_exchange_explicit_8
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8 *dst, c89atomic_uint8 src, c89atomic_memory_order order)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3488
MA_JOB_LOAD_DATA_STREAM
#define MA_JOB_LOAD_DATA_STREAM
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1145
ma_resampler_get_required_input_frame_count
ma_uint64 ma_resampler_get_required_input_frame_count(ma_resampler *pResampler, ma_uint64 outputFrameCount)
ma_node_output_bus::pNext
MA_ATOMIC ma_node_output_bus * pNext
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:969
ma_sound_config_init
MA_API ma_sound_config ma_sound_config_init(void)
ma_engine_config::pResourceManagerVFS
ma_vfs * pResourceManagerVFS
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1962
ma_fader_process_pcm_frames
MA_API ma_result ma_fader_process_pcm_frames(ma_fader *pFader, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
ma_sound_group_set_attenuation_model
MA_API void ma_sound_group_set_attenuation_model(ma_sound_group *pGroup, ma_attenuation_model attenuationModel)
ma_waveform_config_init
ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:45369
ma_fader_get_current_volume
MA_API float ma_fader_get_current_volume(ma_fader *pFader)
ma_resource_manager_data_buffer_init
MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager *pResourceManager, const char *pFilePath, ma_uint32 flags, const ma_pipeline_notifications *pNotifications, ma_resource_manager_data_buffer *pDataBuffer)
ma_job::loadDataBuffer
struct ma_job::@271::@277 loadDataBuffer
ma_log
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:588
ma_peak_node_reinit
MA_API ma_result ma_peak_node_reinit(const ma_peak_config *pConfig, ma_peak_node *pNode)
ma_offset_pcm_frames_ptr
MA_API void * ma_offset_pcm_frames_ptr(void *p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27802
ma_sound_get_length_in_pcm_frames
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound *pSound, ma_uint64 *pLength)
ma_int32
int32_t ma_int32
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1502
ma_node_get_input_channels
MA_API ma_uint32 ma_node_get_input_channels(const ma_node *pNode, ma_uint32 inputBusIndex)
ma_bpf_node_uninit
MA_API void ma_bpf_node_uninit(ma_bpf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_job_queue::jobs
ma_job jobs[MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1378
ma_node_output_bus::isAttached
MA_ATOMIC ma_bool8 isAttached
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:966
ma_pan_mode
ma_pan_mode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1627
ma_copy_and_apply_volume_factor_per_channel_f32
MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float *pFramesOut, const float *pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float *pChannelGains)
MA_JOB_FREE_DATA_BUFFER_NODE
#define MA_JOB_FREE_DATA_BUFFER_NODE
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1141
ma_sound_set_max_distance
MA_API void ma_sound_set_max_distance(ma_sound *pSound, float maxDistance)
waveform
ma_waveform waveform
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/tests/test_deviceio/ma_test_deviceio.c:59
ma_async_notification_poll
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1226
ma_event
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2868
ma_data_source_node_config::nodeConfig
ma_node_config nodeConfig
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1075
ma_audio_buffer_get_cursor_in_pcm_frames
MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer *pAudioBuffer, ma_uint64 *pCursor)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:38474
ma_resource_manager_data_buffer::connector
union ma_resource_manager_data_buffer::@289 connector
ma_mutex_init
ma_result ma_mutex_init(ma_context *pContext, ma_mutex *pMutex)
ma_node_get_output_channels
MA_API ma_uint32 ma_node_get_output_channels(const ma_node *pNode, ma_uint32 outputBusIndex)
ma_pcm_sample_s24_to_s32_no_scale
static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8 *x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:28022
ma_align_64
#define ma_align_64(x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:783
ma_sound_group_set_start_time_in_milliseconds
MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
ma_hishelf2
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2164
ma_spatializer_listener_config::channelsOut
ma_uint32 channelsOut
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1717
ma_delay_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2350
ma_sound_get_data_format
MA_API ma_result ma_sound_get_data_format(ma_sound *pSound, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate)
ma_sound_set_fade_in_milliseconds
MA_API void ma_sound_set_fade_in_milliseconds(ma_sound *pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
ma_spatializer_listener_config::coneInnerAngleInRadians
float coneInnerAngleInRadians
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1720
ma_clip_s32
static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:28050
MA_AT_END
#define MA_AT_END
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1712
ma_hpf_node_reinit
MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config *pConfig, ma_hpf_node *pNode)
ma_slot_allocator::count
ma_uint32 count
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1173
ma_engine_config::sampleRate
ma_uint32 sampleRate
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1954
ma_waveform_type_sine
@ ma_waveform_type_sine
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5359
ma_delay_node_get_decay
MA_API float ma_delay_node_get_decay(const ma_delay_node *pDelayNode)
ma_node_output_bus::pNode
ma_node * pNode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:958
ma_splitter_node_init
MA_API ma_result ma_splitter_node_init(ma_node_graph *pNodeGraph, const ma_splitter_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_splitter_node *pSplitterNode)
ma_fence::e
ma_event e
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1196
ma_hishelf_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2301
ma_resource_manager
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1528
ma_resampler_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2240
ma_fence_release
MA_API ma_result ma_fence_release(ma_fence *pFence)
ma_device_config::periodSizeInMilliseconds
ma_uint32 periodSizeInMilliseconds
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3105
ma_notch2_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2069
ma_bpf_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2039
ma_job_queue_next
MA_API ma_result ma_job_queue_next(ma_job_queue *pQueue, ma_job *pJob)
ma_engine::allocationCallbacks
ma_allocation_callbacks allocationCallbacks
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1976
ma_semaphore_wait
MA_API ma_result ma_semaphore_wait(ma_semaphore *pSemaphore)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4959
ma_sound_group_get_direction
MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group *pGroup)
ma_clamp
#define ma_clamp(x, lo, hi)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:780
ma_engine::inlinedSoundCount
MA_ATOMIC ma_uint32 inlinedSoundCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1981
MA_RESOURCE_MANAGER_FLAG_NO_THREADING
#define MA_RESOURCE_MANAGER_FLAG_NO_THREADING
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1396
ma_channel_map_get_channel
MA_API ma_channel ma_channel_map_get_channel(const ma_channel *pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:35551
ma_hpf
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1996
ma_lpf_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1919
MA_SOUND_FLAG_ASYNC
#define MA_SOUND_FLAG_ASYNC
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1836
ma_engine_node_config::isPitchDisabled
ma_bool8 isPitchDisabled
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1861
c89atomic_fetch_sub_16
#define c89atomic_fetch_sub_16(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4007
ma_spatializer_listener_get_world_up
MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener *pListener)
ma_delay_config_init
MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
ma_offset_pcm_frames_const_ptr_f32
static const MA_INLINE float * ma_offset_pcm_frames_const_ptr_f32(const float *p, ma_uint64 offsetInFrames, ma_uint32 channels)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4448
ma_job
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1268
ma_sound_group_set_min_distance
MA_API void ma_sound_group_set_min_distance(ma_sound_group *pGroup, float minDistance)
ma_uint32
uint32_t ma_uint32
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1503
ma_bpf
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2050
ma_sound_get_max_gain
MA_API float ma_sound_get_max_gain(const ma_sound *pSound)
ma_sound::pDataSource
ma_data_source * pDataSource
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1915
ma_spatializer_config::dopplerFactor
float dopplerFactor
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1777
ma_waveform
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5373
ma_spatializer_listener_init_preallocated
MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config *pConfig, void *pHeap, ma_spatializer_listener *pListener)
ma_resource_manager_data_buffer_get_looping
MA_API ma_result ma_resource_manager_data_buffer_get_looping(const ma_resource_manager_data_buffer *pDataBuffer, ma_bool32 *pIsLooping)
ma_paged_audio_buffer_page
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:70
ma_apply_volume_factor_f32
void ma_apply_volume_factor_f32(float *pSamples, ma_uint32 sampleCount, float factor)
ma_notch_node::notch
ma_notch2 notch
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2235
ma_engine_config::pResourceManager
ma_resource_manager * pResourceManager
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1948
ma_fader_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1660
ma_slot_allocator
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1166
ma_resource_manager::defaultVFS
ma_default_vfs defaultVFS
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1535
ma_engine_get_log
MA_API ma_log * ma_engine_get_log(ma_engine *pEngine)
ma_job::loadDataStream
struct ma_job::@271::@279 loadDataStream
MA_SIZE_MAX
#define MA_SIZE_MAX
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1549
ma_node_input_bus::nextCounter
MA_ATOMIC ma_uint16 nextCounter
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:983
ma_spatializer_listener_config::speedOfSound
float speedOfSound
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1723
ma_format_u8
@ ma_format_u8
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1786
ma_allocation_callbacks_init_copy
static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks *pDst, const ma_allocation_callbacks *pSrc)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:1922
ma_device_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3128
ma_engine::ownsResourceManager
ma_bool8 ownsResourceManager
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1977
ma_decoder_read_pcm_frames
ma_uint64 ma_decoder_read_pcm_frames(ma_decoder *pDecoder, void *pFramesOut, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44808
ma_resource_manager::log
ma_log log
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1536
ma_attenuation_model_exponential
@ ma_attenuation_model_exponential
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1699
ma_paged_audio_buffer_init
MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config *pConfig, ma_paged_audio_buffer *pPagedAudioBuffer)
ma_engine_listener_set_position
MA_API void ma_engine_listener_set_position(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
ma_device_config_init
ma_device_config ma_device_config_init(ma_device_type deviceType)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27034
ma_panner_config::mode
ma_pan_mode mode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1637
ma_biquad_config_init
ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:30423
ma_sound_get_current_fade_volume
MA_API float ma_sound_get_current_fade_volume(ma_sound *pSound)
ma_panner_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1636
ma_lpf
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1928
ma_audio_buffer_init
MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config *pConfig, ma_audio_buffer *pAudioBuffer)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:38352
ma_resource_manager_config::decodedFormat
ma_format decodedFormat
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1515
ma_node_state_stopped
@ ma_node_state_stopped
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:888
ma_spatializer::config
ma_spatializer_config config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1786
ma_hpf_node_config_init
MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
ma_spatializer_set_cone
MA_API void ma_spatializer_set_cone(ma_spatializer *pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
ma_data_source_get_length_in_pcm_frames
MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 *pLength)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37710
ma_sound_get_positioning
MA_API ma_positioning ma_sound_get_positioning(const ma_sound *pSound)
ma_sound_inlined::pNext
ma_sound_inlined * pNext
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1935
ma_resource_manager_data_supply_type_decoded_paged
@ ma_resource_manager_data_supply_type_decoded_paged
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1404
ma_delay_init
MA_API ma_result ma_delay_init(const ma_delay_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_delay *pDelay)
ma_sound::isLooping
MA_ATOMIC ma_bool8 isLooping
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1917
ma_delay_config::wet
float wet
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2322
c89atomic_load_f32
#define c89atomic_load_f32(ptr)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4174
ma_notch2_init
ma_result ma_notch2_init(const ma_notch2_config *pConfig, ma_notch2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31977
ma_pan_mode_balance
@ ma_pan_mode_balance
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1629
ma_resource_manager_config::ppCustomDecodingBackendVTables
ma_decoding_backend_vtable ** ppCustomDecodingBackendVTables
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1521
ma_loshelf_node::loshelf
ma_loshelf2 loshelf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2281
ma_resource_manager_data_stream::totalLengthInPCMFrames
ma_uint64 totalLengthInPCMFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1477
ma_delay_node_config::delay
ma_delay_config delay
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2353
ma_standard_channel_map_default
@ ma_standard_channel_map_default
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1813
ma_engine::ownsDevice
ma_bool8 ownsDevice
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1978
ma_sound_group_set_fade_in_milliseconds
MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group *pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
ma_biquad_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2131
ma_gainer_process_pcm_frames
MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer *pGainer, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
ma_sound::pResourceManagerDataSource
ma_resource_manager_data_source * pResourceManagerDataSource
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1926
ma_notch2
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2080
ma_mutex
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2846
ma_engine_node::spatializer
ma_spatializer spatializer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1877
ma_resample_algorithm_linear
@ ma_resample_algorithm_linear
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2236
ma_sound_set_max_gain
MA_API void ma_sound_set_max_gain(ma_sound *pSound, float maxGain)
ma_engine_listener_get_world_up
MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine *pEngine, ma_uint32 listenerIndex)
ma_biquad_node_reinit
MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config *pConfig, ma_biquad_node *pNode)
ma_node_graph_get_endpoint
MA_API ma_node * ma_node_graph_get_endpoint(ma_node_graph *pNodeGraph)
ma_spatializer_listener_set_cone
MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener *pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
ma_resource_manager_data_buffer::flags
ma_uint32 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1454
ma_spatializer::pNewChannelGainsOut
float * pNewChannelGainsOut
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1792
ma_copy_pcm_frames
MA_API void ma_copy_pcm_frames(void *dst, const void *src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27780
ma_sound_get_attenuation_model
MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound *pSound)
ma_hishelf2_process_pcm_frames
ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32410
ma_sqrtd
static MA_INLINE double ma_sqrtd(double x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:811
ma_sound_group_config_init
MA_API ma_sound_group_config ma_sound_group_config_init(void)
ma_gainer_config_init
MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames)
ma_node_vtable::onProcess
void(* onProcess)(ma_node *pNode, const float **ppFramesIn, ma_uint32 *pFrameCountIn, float **ppFramesOut, ma_uint32 *pFrameCountOut)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:905
ma_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:935
ma_sound::ownsDataSource
ma_bool8 ownsDataSource
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1919
ma_notch_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2223
ma_node_graph_set_time
MA_API ma_result ma_node_graph_set_time(ma_node_graph *pNodeGraph, ma_uint64 globalTime)
ma_hishelf_node_reinit
MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config *pConfig, ma_hishelf_node *pNode)
c89atomic_exchange_i32
#define c89atomic_exchange_i32(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4088
ma_job::pageDataStream
struct ma_job::@271::@281 pageDataStream
ma_slot_allocator_free
MA_API ma_result ma_slot_allocator_free(ma_slot_allocator *pAllocator, ma_uint64 slot)
ma_resource_manager_data_source
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1498
MA_LOG_LEVEL_ERROR
#define MA_LOG_LEVEL_ERROR
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1591
ma_peak_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2255
ma_spatializer_listener::velocity
ma_vec3f velocity
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1735
ma_device_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3129
ma_node_base::state
MA_ATOMIC ma_node_state state
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1006
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
#define MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:881
ma_loshelf_node_config_init
MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
ma_audio_buffer_config_init
MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void *pData, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:38266
ma_decoder::outputChannels
ma_uint32 outputChannels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5204
ma_sound_config::channelsIn
ma_uint32 channelsIn
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1904
ma_loshelf_node_uninit
MA_API void ma_loshelf_node_uninit(ma_loshelf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY
#define MA_RESOURCE_MANAGER_JOB_QUEUE_CAPACITY
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1136
ma_engine::nodeGraph
ma_node_graph nodeGraph
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1970
ma_waveform_read_pcm_frames
ma_uint64 ma_waveform_read_pcm_frames(ma_waveform *pWaveform, void *pFramesOut, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:45751
ma_spatializer_listener
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1730
MA_ERROR
#define MA_ERROR
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1660
ma_resource_manager_data_stream_get_available_frames
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream *pDataStream, ma_uint64 *pAvailableFrames)
ma_resource_manager_data_stream_result
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream *pDataStream)
ma_spatializer_listener_config::coneOuterAngleInRadians
float coneOuterAngleInRadians
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1721
ma_paged_audio_buffer::pCurrent
ma_paged_audio_buffer_page * pCurrent
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:108
ma_default_vfs_init
MA_API ma_result ma_default_vfs_init(ma_default_vfs *pVFS, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:39337
ma_hpf_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2177
ma_job::toc
union ma_job::@270 toc
ma_node_graph
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1053
ma_node_base::vtable
const ma_node_vtable * vtable
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:996
ma_engine_get_time
MA_API ma_uint64 ma_engine_get_time(const ma_engine *pEngine)
ma_resource_manager_get_log
MA_API ma_log * ma_resource_manager_get_log(ma_resource_manager *pResourceManager)
ma_hpf_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1988
ma_sound_config::pDataSource
ma_data_source * pDataSource
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1901
ma_delay_config::dry
float dry
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2323
ma_job::order
ma_uint32 order
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1281
MA_DATA_SOURCE_FLAG_DECODE
#define MA_DATA_SOURCE_FLAG_DECODE
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1122
ma_thread_create
static ma_result ma_thread_create(ma_thread *pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void *pData, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4721
ma_data_source_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1083
MA_ALREADY_EXISTS
#define MA_ALREADY_EXISTS
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1667
ma_resource_manager_data_source::executionPointer
ma_uint32 executionPointer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1508
ma_mutex_unlock
void ma_mutex_unlock(ma_mutex *pMutex)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4813
ma_job_init
MA_API ma_job ma_job_init(ma_uint16 code)
ma_node_graph_get_time
MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph *pNodeGraph)
ma_loshelf_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2269
MA_ENGINE_MAX_LISTENERS
#define MA_ENGINE_MAX_LISTENERS
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1843
ma_hpf_node_config::hpf
ma_hpf_config hpf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2180
ma_node_detach_all_output_buses
MA_API ma_result ma_node_detach_all_output_buses(ma_node *pNode)
ma_resource_manager_data_source_uninit
MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source *pDataSource)
ma_loshelf_node_config::loshelf
ma_loshelf_config loshelf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2272
ma_resource_manager::dataBufferBSTLock
ma_mutex dataBufferBSTLock
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1532
ma_context
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3210
ma_lpf_node_config_init
MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
ma_node_base::cachedDataCapInFramesPerBus
ma_uint16 cachedDataCapInFramesPerBus
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:998
ma_slot_allocator::bitfield
MA_ATOMIC ma_uint32 bitfield
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1170
ma_resource_manager_data_source_result
MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source *pDataSource)
ma_spatializer_set_min_distance
MA_API void ma_spatializer_set_min_distance(ma_spatializer *pSpatializer, float minDistance)
MA_MAX_NODE_BUS_COUNT
#define MA_MAX_NODE_BUS_COUNT
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:862
ma_spatializer_listener_get_cone
MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener *pListener, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
c89atomic_memory_order_acquire
#define c89atomic_memory_order_acquire
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3290
ma_device_init
ma_result ma_device_init(ma_context *pContext, const ma_device_config *pConfig, ma_device *pDevice)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27048
ma_device
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3584
init
void init(const M_string &remappings)
ma_async_notification_signal
MA_API ma_result ma_async_notification_signal(ma_async_notification *pNotification)
ma_audio_buffer_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4586
MA_BAD_SEEK
#define MA_BAD_SEEK
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1684
ma_resource_manager_data_stream::isLooping
MA_ATOMIC ma_bool32 isLooping
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1485
ma_delay_uninit
MA_API void ma_delay_uninit(ma_delay *pDelay, const ma_allocation_callbacks *pAllocationCallbacks)
ma_paged_audio_buffer_page::pAudioData
ma_uint8 pAudioData[1]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:74
ma_data_source_config::vtable
const ma_data_source_vtable * vtable
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4517
ma_device_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3100
ma_yield
static MA_INLINE void ma_yield()
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:533
ma_log_init
MA_API ma_result ma_log_init(const ma_allocation_callbacks *pAllocationCallbacks, ma_log *pLog)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:2003
ma_device_config::sampleRate
ma_uint32 sampleRate
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3103
ma_spatializer_get_min_distance
MA_API float ma_spatializer_get_min_distance(const ma_spatializer *pSpatializer)
MA_MAX_CHANNELS
#define MA_MAX_CHANNELS
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1753
ma_node_config::pInputChannels
const ma_uint32 * pInputChannels
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:941
ma_resource_manager_data_stream::executionCounter
ma_uint32 executionCounter
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1481
ma_engine_node_config::channelsOut
ma_uint32 channelsOut
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1859
ma_device::sampleRate
ma_uint32 sampleRate
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3588
ma_delay_process_pcm_frames
MA_API ma_result ma_delay_process_pcm_frames(ma_delay *pDelay, void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount)
ma_channel_mix_mode
ma_channel_mix_mode
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1794
ma_mutex_lock
void ma_mutex_lock(ma_mutex *pMutex)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4798
ma_positioning_relative
@ ma_positioning_relative
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1705
MA_NOT_IMPLEMENTED
#define MA_NOT_IMPLEMENTED
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1688
ma_engine_node_get_heap_size
MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config *pConfig, size_t *pHeapSizeInBytes)
ma_peak_node_config::peak
ma_peak_config peak
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2249
ma_sound_inlined
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1932
ma_bpf_node::bpf
ma_bpf bpf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2212
ma_paged_audio_buffer_data_allocate_and_append_page
MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data *pData, ma_uint32 pageSizeInFrames, const void *pInitialData, const ma_allocation_callbacks *pAllocationCallbacks)
ma_spinlock
ma_uint32 ma_spinlock
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:499
ma_engine_node::_pHeap
void * _pHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1888
ma_engine_listener_get_velocity
MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine *pEngine, ma_uint32 listenerIndex)
ma_paged_audio_buffer::relativeCursor
ma_uint64 relativeCursor
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:109
ma_delay_node_set_wet
MA_API void ma_delay_node_set_wet(ma_delay_node *pDelayNode, float value)
ma_slot_allocator_init
MA_API ma_result ma_slot_allocator_init(ma_slot_allocator *pAllocator)
ma_node_base::pNodeGraph
ma_node_graph * pNodeGraph
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:995
ma_context_config::pLog
ma_log * pLog
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:2141
ma_cosd
static MA_INLINE double ma_cosd(double x)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:818
ma_spatializer_get_heap_size
MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config *pConfig, size_t *pHeapSizeInBytes)
ma_engine_config::pContext
ma_context * pContext
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1949
ma_is_little_endian
static MA_INLINE ma_bool32 ma_is_little_endian(void)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:445
config
static sai_transceiver_t config
Definition: imxrt1050/imxrt1050-evkb/source/pv_audio_rec.c:75
ma_engine_listener_set_cone
MA_API void ma_engine_listener_set_cone(ma_engine *pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
ma_vfs
void ma_vfs
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4630
MA_TRUE
#define MA_TRUE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1530
c89atomic_exchange_f32
#define c89atomic_exchange_f32(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4176
ma_resource_manager_data_stream::pageFrameCount
MA_ATOMIC ma_uint32 pageFrameCount[2]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1489
ma_node_graph::endpoint
ma_node_base endpoint
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1056
ma_gainer::_pHeap
void * _pHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:414
ma_spatializer_config::rolloff
float rolloff
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1773
ma_spatializer_init
MA_API ma_result ma_spatializer_init(const ma_spatializer_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_spatializer *pSpatializer)
ma_lpf_node_init
MA_API ma_result ma_lpf_node_init(ma_node_graph *pNodeGraph, const ma_lpf_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_lpf_node *pNode)
MA_SUCCESS
#define MA_SUCCESS
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1659
ma_node_uninit
MA_API void ma_node_uninit(ma_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_engine_config::pDevice
ma_device * pDevice
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1950
ma_sound_group_get_min_gain
MA_API float ma_sound_group_get_min_gain(const ma_sound_group *pGroup)
ma_sound::engineNode
ma_engine_node engineNode
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1914
ma_sound_config::pDoneFence
ma_fence * pDoneFence
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1907
ma_hishelf_node_config_init
MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
ma_bpf_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2041
ma_sound_init_copy
MA_API ma_result ma_sound_init_copy(ma_engine *pEngine, const ma_sound *pExistingSound, ma_uint32 flags, ma_sound_group *pGroup, ma_sound *pSound)
ma_engine_config::gainSmoothTimeInFrames
ma_uint32 gainSmoothTimeInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1957
ma_sound_group_get_pinned_listener_index
MA_API ma_uint8 ma_sound_group_get_pinned_listener_index(const ma_sound_group *pGroup)
ma_job_queue::flags
ma_uint32 flags
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1373
ma_sound_config::initialAttachmentInputBusIndex
ma_uint32 initialAttachmentInputBusIndex
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1903
ma_delay::bufferSizeInFrames
ma_uint32 bufferSizeInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2334
ma_engine_listener_set_direction
MA_API void ma_engine_listener_set_direction(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
ma_peak_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2246
ma_sound_config::pFilePathW
const wchar_t * pFilePathW
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1900
ma_delay_node_set_dry
MA_API void ma_delay_node_set_dry(ma_delay_node *pDelayNode, float value)
ma_loshelf2_init
ma_result ma_loshelf2_init(const ma_loshelf2_config *pConfig, ma_loshelf2 *pFilter)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32232
ma_loshelf2
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2136
ma_resource_manager_data_stream::absoluteCursor
ma_uint64 absoluteCursor
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1479
ma_loshelf2_process_pcm_frames
ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:32284
ma_node_graph_get_channels
MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph *pNodeGraph)
ma_node_base::_pHeap
void * _pHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1017
ma_sound_group_get_max_gain
MA_API float ma_sound_group_get_max_gain(const ma_sound_group *pGroup)
ma_node_config::initialState
ma_node_state initialState
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:938
ma_notch_node
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2232
ma_spatializer_set_positioning
MA_API void ma_spatializer_set_positioning(ma_spatializer *pSpatializer, ma_positioning positioning)
ma_data_source_get_data_format
MA_API ma_result ma_data_source_get_data_format(ma_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37609
ma_sound_set_stop_time_in_pcm_frames
MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInFrames)
ma_node_output_bus::lock
MA_ATOMIC ma_spinlock lock
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:967
ma_sound_group_get_position
MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group *pGroup)
ma_spatializer_set_doppler_factor
MA_API void ma_spatializer_set_doppler_factor(ma_spatializer *pSpatializer, float dopplerFactor)
ma_resource_manager_data_buffer::executionCounter
ma_uint32 executionCounter
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1455
MA_CHANNEL_FRONT_LEFT
#define MA_CHANNEL_FRONT_LEFT
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1603
ma_hishelf_node::hishelf
ma_hishelf2 hishelf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2304
ma_resource_manager_data_buffer_node::hashedName32
ma_uint32 hashedName32
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1437
ma_node_output_bus
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:955
ma_data_source_config_init
MA_API ma_data_source_config ma_data_source_config_init(void)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37214
ma_spinlock_unlock
MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock *pSpinlock)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4301
ma_resource_manager_data_buffer::executionPointer
ma_uint32 executionPointer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1456
MA_ZERO_OBJECT
#define MA_ZERO_OBJECT(p)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:774
ma_delay_node_uninit
MA_API void ma_delay_node_uninit(ma_delay_node *pDelayNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_sound_group_get_engine
MA_API ma_engine * ma_sound_group_get_engine(const ma_sound_group *pGroup)
ma_engine_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1946
ma_attenuation_model
ma_attenuation_model
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1694
ma_job_queue::allocator
ma_slot_allocator allocator
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1377
ma_resource_manager_data_stream::result
MA_ATOMIC ma_result result
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1492
ma_delay
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2330
ma_node_state
ma_node_state
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:885
ma_delay_node_get_wet
MA_API float ma_delay_node_get_wet(const ma_delay_node *pDelayNode)
ma_resource_manager_data_supply_type
ma_resource_manager_data_supply_type
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1399
c89atomic_compare_exchange_weak_32
#define c89atomic_compare_exchange_weak_32(dst, expected, desired)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4000
ma_engine_node::fader
ma_fader fader
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1875
ma_data_source_node::pDataSource
ma_data_source * pDataSource
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1086
ma_channel_mix_mode_rectangular
@ ma_channel_mix_mode_rectangular
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1796
ma_biquad_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1845
ma_resource_manager_data_source::executionCounter
ma_uint32 executionCounter
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1507
ma_sound_group_init_ex
MA_API ma_result ma_sound_group_init_ex(ma_engine *pEngine, const ma_sound_group_config *pConfig, ma_sound_group *pGroup)
ma_decoder_config::allocationCallbacks
ma_allocation_callbacks allocationCallbacks
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5190
ma_spatializer::direction
ma_vec3f direction
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1788
ma_hishelf2_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2159
c89atomic_exchange_8
#define c89atomic_exchange_8(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:3990
ma_hpf_process_pcm_frames
ma_result ma_hpf_process_pcm_frames(ma_hpf *pHPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:31535
ma_node_get_output_bus_count
MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node *pNode)
ma_spatializer_config::gainSmoothTimeInFrames
ma_uint32 gainSmoothTimeInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1778
ma_gainer_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:397
ma_gainer_init_preallocated
MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config *pConfig, void *pHeap, ma_gainer *pGainer)
ma_malloc
void * ma_malloc(size_t sz, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37117
ma_copy_string_w
MA_API wchar_t * ma_copy_string_w(const wchar_t *src, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:1169
ma_biquad_process_pcm_frames
ma_result ma_biquad_process_pcm_frames(ma_biquad *pBQ, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:30570
ma_swap_endian_uint32
static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:461
ma_decoder_init_vfs
MA_API ma_result ma_decoder_init_vfs(ma_vfs *pVFS, const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:44303
ma_resampler_init
ma_result ma_resampler_init(const ma_resampler_config *pConfig, ma_resampler *pResampler)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:33099
ma_context_config::allocationCallbacks
ma_allocation_callbacks allocationCallbacks
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3165
ma_sound_stop
MA_API ma_result ma_sound_stop(ma_sound *pSound)
ma_notch_node_uninit
MA_API void ma_notch_node_uninit(ma_notch_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_resource_manager_data_buffer_node::executionPointer
ma_uint32 executionPointer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1441
ma_resource_manager::jobThreads
ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1533
ma_engine_node_type_sound
@ ma_engine_node_type_sound
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1850
ma_node_output_bus::volume
MA_ATOMIC float volume
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:968
ma_paged_audio_buffer_page::pNext
MA_ATOMIC ma_paged_audio_buffer_page * pNext
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:72
ma_engine_config::pLog
ma_log * pLog
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1951
ma_resource_manager_next_job
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager *pResourceManager, ma_job *pJob)
ma_job_queue::sem
ma_semaphore sem
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1376
ma_hpf_node_init
MA_API ma_result ma_hpf_node_init(ma_node_graph *pNodeGraph, const ma_hpf_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_hpf_node *pNode)
ma_sound_set_rolloff
MA_API void ma_sound_set_rolloff(ma_sound *pSound, float rolloff)
ma_audio_buffer_uninit
MA_API void ma_audio_buffer_uninit(ma_audio_buffer *pAudioBuffer)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:38411
ma_resource_manager_config::pVFS
ma_vfs * pVFS
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1520
ma_sound_set_pan
MA_API void ma_sound_set_pan(ma_sound *pSound, float pan)
ma_sound_seek_to_pcm_frame
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound *pSound, ma_uint64 frameIndex)
ma_fader::config
ma_fader_config config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1671
ma_data_source_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.h:4515
ma_resource_manager_data_buffer::decoder
ma_decoder decoder
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1464
ma_sound_set_pinned_listener_index
MA_API void ma_sound_set_pinned_listener_index(ma_sound *pSound, ma_uint8 listenerIndex)
ma_pipeline_notifications::done
ma_pipeline_stage_notification done
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1262
ma_node_base::outputBusCount
ma_uint32 outputBusCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1010
ma_resource_manager_data_source_set_looping
MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source *pDataSource, ma_bool32 isLooping)
ma_node_set_time
MA_API ma_result ma_node_set_time(ma_node *pNode, ma_uint64 localTime)
ma_spatializer_get_direction
MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer *pSpatializer)
ma_async_notification_poll_is_signalled
MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll *pNotificationPoll)
ma_convert_pcm_frames_format
void ma_convert_pcm_frames_format(void *pOut, ma_format formatOut, const void *pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:30307
ma_node_output_bus::outputBusIndex
ma_uint8 outputBusIndex
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:959
ma_resource_manager_data_buffer::ds
ma_data_source_base ds
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1451
ma_resource_manager_data_supply
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1407
ma_node_get_time
MA_API ma_uint64 ma_node_get_time(const ma_node *pNode)
ma_bool8
ma_uint8 ma_bool8
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1528
ma_fader_set_fade
MA_API void ma_fader_set_fade(ma_fader *pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
ma_sound_set_volume
MA_API ma_result ma_sound_set_volume(ma_sound *pSound, float volume)
c89atomic_fetch_and_8
#define c89atomic_fetch_and_8(dst, src)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4018
ma_resource_manager_data_buffer::buffer
ma_audio_buffer buffer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1465
ma_node_base::pOutputBuses
ma_node_output_bus * pOutputBuses
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1012
ma_gainer::_ownsHeap
ma_bool32 _ownsHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:415
MA_JOB_PAGE_DATA_BUFFER_NODE
#define MA_JOB_PAGE_DATA_BUFFER_NODE
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1142
MA_DATA_SOURCE_FLAG_STREAM
#define MA_DATA_SOURCE_FLAG_STREAM
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1121
ma_job::loadDataBufferNode
struct ma_job::@271::@274 loadDataBufferNode
ma_bpf_node_config
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2200
ma_channel
ma_uint8 ma_channel
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1600
MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
Definition: pvrecorder/src/miniaudio/research/miniaudio_engine.h:1389
ma_sound_set_fade_in_pcm_frames
MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound *pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
ma_resource_manager_register_encoded_data_w
MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager *pResourceManager, const wchar_t *pName, const void *pData, size_t sizeInBytes)
ma_gainer::t
ma_uint32 t
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:409
ma_resource_manager_data_stream_uninit
MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream *pDataStream)
ma_uint16
uint16_t ma_uint16
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1501
ma_delay_get_dry
MA_API float ma_delay_get_dry(const ma_delay *pDelay)
ma_device_config::playback
struct ma_device_config::@97 playback
ma_paged_audio_buffer_get_cursor_in_pcm_frames
MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer *pPagedAudioBuffer, ma_uint64 *pCursor)
ma_node_base::stateTimes
MA_ATOMIC ma_uint64 stateTimes[2]
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1007
ma_vec3f::y
float y
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1690
ma_free
void ma_free(void *p, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:37139
MA_CHANNEL_SIDE_LEFT
#define MA_CHANNEL_SIDE_LEFT
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1612
ma_engine_node::_ownsHeap
ma_bool8 _ownsHeap
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1887
ma_delay_node::delay
ma_delay delay
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2362
ma_sound_group_set_position
MA_API void ma_sound_group_set_position(ma_sound_group *pGroup, float x, float y, float z)
ma_node_input_bus::head
ma_node_output_bus head
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:982
ma_hpf_node_uninit
MA_API void ma_hpf_node_uninit(ma_hpf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_device::playback
struct ma_device::@115 playback
ma_get_accumulation_bytes_per_frame
MA_API size_t ma_get_accumulation_bytes_per_frame(ma_format format, ma_uint32 channels)
ma_sound_init_from_file
MA_API ma_result ma_sound_init_from_file(ma_engine *pEngine, const char *pFilePath, ma_uint32 flags, ma_sound_group *pGroup, ma_fence *pDoneFence, ma_sound *pSound)
ma_resource_manager_data_stream_init_w
MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager *pResourceManager, const wchar_t *pFilePath, ma_uint32 flags, const ma_pipeline_notifications *pNotifications, ma_resource_manager_data_stream *pDataStream)
ma_node_graph_uninit
MA_API void ma_node_graph_uninit(ma_node_graph *pNodeGraph, const ma_allocation_callbacks *pAllocationCallbacks)
ma_sound_get_doppler_factor
MA_API float ma_sound_get_doppler_factor(const ma_sound *pSound)
ma_hishelf_node_config::hishelf
ma_hishelf_config hishelf
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2295
ma_paged_audio_buffer_read_pcm_frames
MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer *pPagedAudioBuffer, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
ma_event_signal
MA_API ma_result ma_event_signal(ma_event *pEvent)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:4913
ma_resource_manager_data_buffer::seekTargetInPCMFrames
ma_uint64 seekTargetInPCMFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1457
ma_resource_manager_data_buffer::result
MA_ATOMIC ma_result result
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1459
ma_decoder::outputSampleRate
ma_uint32 outputSampleRate
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:5205
ma_engine_node::resampler
ma_resampler resampler
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1876
ma_engine_node_init
MA_API ma_result ma_engine_node_init(const ma_engine_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_engine_node *pEngineNode)
ma_engine_set_volume
MA_API ma_result ma_engine_set_volume(ma_engine *pEngine, float volume)
ma_node_base::cachedFrameCountIn
ma_uint16 cachedFrameCountIn
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1002
ma_spatializer
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1784
ma_peak_node_uninit
MA_API void ma_peak_node_uninit(ma_peak_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_engine_config::periodSizeInFrames
ma_uint32 periodSizeInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:1955
ma_gainer::pNewGains
float * pNewGains
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:411
ma_peak2_config::channels
ma_uint32 channels
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2103
ma_device_config::pUserData
void * pUserData
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3112
ma_sound_group_set_cone
MA_API void ma_sound_group_set_cone(ma_sound_group *pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
ma_device_get_log
MA_API ma_log * ma_device_get_log(ma_device *pDevice)
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/extras/miniaudio_split/miniaudio.c:27480
ma_context_config
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:3160
ma_delay_config::delayInFrames
ma_uint32 delayInFrames
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:2320
ma_node_vtable::outputBusCount
ma_uint8 outputBusCount
Definition: porcupine/demo/c/pvrecorder/src/miniaudio/research/miniaudio_engine.h:926
ma_sound_get_position
MA_API ma_vec3f ma_sound_get_position(const ma_sound *pSound)
ma_spatializer_get_max_distance
MA_API float ma_spatializer_get_max_distance(const ma_spatializer *pSpatializer)
ma_loshelf2_config::format
ma_format format
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:2130
MA_OUT_OF_MEMORY
#define MA_OUT_OF_MEMORY
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/miniaudio.h:1663


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