335 Commits

Author SHA1 Message Date
David Reid f40cf03f80 Version 0.11.23 2025-09-11 06:46:45 +10:00
David Reid 7f2cd5b8d5 Fix an error in the splitting script. 2025-09-11 06:42:16 +10:00
David Reid 3afbdef285 Update dr_libs. 2025-09-10 18:56:36 +10:00
David Reid 9a091f73aa Fix an undefined behavior error in the s16 to s32 conversion routine. 2025-09-10 15:06:34 +10:00
David Reid 4de39a8a37 Update change history. 2025-09-10 14:55:00 +10:00
David Reid 7c8574210d Update gitignore. 2025-09-10 13:49:58 +10:00
David Reid 6648ed005a Initial commit of the documentation generation tool. 2025-09-10 13:47:00 +10:00
David Reid 87bae56937 Fix typos in the documentation. 2025-09-10 13:18:58 +10:00
David Reid 293f5de18f Minor fixes to the readme. 2025-09-10 13:11:34 +10:00
David Reid 233b9b69c4 Reinstate the Twitter badge. 2025-09-10 12:58:20 +10:00
David Reid db514e813f Add splitting script. 2025-09-10 12:45:06 +10:00
David Reid 8130543730 Update fs. 2025-09-10 10:04:27 +10:00
David Reid b306c6a270 Use pkg-config for libvorbis and libopus detection. 2025-09-10 09:51:13 +10:00
spevnev 9e1f02b12a Fix unsigned offset overflow 2025-09-10 06:44:31 +10:00
David Reid fa84240364 Fix a typo. 2025-09-09 17:27:25 +10:00
David Reid 70eb06d3bd CMake: Minor fix for pthread and m. 2025-08-23 09:54:32 +10:00
Marcin Serwin 959283f244 Ignore missing library dependencies in CMake
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 09:45:49 +10:00
David Reid ea59076ba9 Try fixing a warning with MA_FALLTHROUGH. 2025-08-23 08:33:53 +10:00
David Reid 1d7d8dfba0 Fix some CMake errors with the Emscripten build. 2025-08-23 08:09:03 +10:00
David Reid f48d903526 WASAPI: Add a missing field to ma_AudioClientProperties.
Public issue https://github.com/mackron/miniaudio/issues/1028
2025-08-23 06:46:09 +10:00
David Reid 3bdd39fd2c Make some sound functions const.
Public issue https://github.com/mackron/miniaudio/issues/990
2025-08-23 06:20:08 +10:00
David Reid e75a053908 Update CMake script to extract the version from miniaudio.h. 2025-08-23 05:50:15 +10:00
David Reid 346d86ffa1 Update gitignore. 2025-08-23 05:45:05 +10:00
Marcin Serwin a2f92095dc Expose compile defines in pkg-config file
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 05:41:27 +10:00
Marcin Serwin badf36a378 Generate and install pkg-config file
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 05:41:27 +10:00
Marcin Serwin c9d288c3dc Link linux backends if NO_RUNTIME_LINKING is enabled
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 05:41:27 +10:00
David Reid 825d2c4466 Update fs. 2025-08-22 11:27:45 +10:00
David Reid 6fd62e6bbc Update fs. 2025-08-22 07:12:34 +10:00
David Reid b1893aa8f1 Minor C89 compatibility fix. 2025-08-21 14:33:52 +10:00
David Reid df4baf8d40 Fix some errors. 2025-08-21 14:24:21 +10:00
David Reid 21237008df Silence some errors in the Steam Audio headers. 2025-08-21 14:23:31 +10:00
David Reid 587bd83cbb Update fs and fix some build errors with -std=c89. 2025-08-21 13:57:57 +10:00
David Reid b7e5451ef4 Try fixing a compilation error when pthread does not exist. 2025-08-20 18:07:59 +10:00
David Reid ba84e61a18 Try fixing a compilation error when libatomic does not exist. 2025-08-20 18:03:12 +10:00
David Reid a7ab58259e Don't try building C++ tests when forcing C89. 2025-08-20 17:36:07 +10:00
David Reid 18055f34bb voclib: Fix a compilation error. 2025-08-20 16:40:46 +10:00
David Reid f6bae251bd verblib: Try fixing a compilation error on macOS. 2025-08-20 16:09:44 +10:00
David Reid 3567d5cfef Fix compilation error with MA_NO_SSE2. 2025-08-20 16:04:15 +10:00
David Reid 4e3b778c62 Silence some warnings about unused functions. 2025-08-20 15:56:43 +10:00
David Reid 6315130ec6 Make ma_lcg.state uint32 to avoid UB on signed arthimethic overflow 2025-08-19 08:32:06 +10:00
David Reid 6e1cd41622 tests: fix memory leak in filtering and generation tests 2025-08-19 08:26:22 +10:00
David Reid 78cdb9c1cb Add batch file for setting up DJGPP environment. 2025-08-07 17:44:55 +10:00
David Reid 1ea69211ad Fix some errors with the DJGPP build.
This forces `MA_NO_THREADING` and `MA_NO_RUNTIME_LINKING`.

The DOS/DJGPP build currently requires `MA_NO_DEVICE_IO`.
2025-08-07 17:44:19 +10:00
David Reid e9e8f90137 Fix an error when setting loop points.
Public issue https://github.com/mackron/miniaudio/issues/1019
2025-08-07 16:20:00 +10:00
David Reid d93552283f Unify MA_NX and MA_SWITCH platform macros. 2025-08-07 16:17:51 +10:00
David Reid 61a85dca42 Fix NXDK build for Xbox.
This disables the WASAPI, DirectSound and WinMM backends which means
you will not get any actual audio output working. An Xbox backend will
need to come later. The main purpose of this commit is to get the main
library compiling.

The main complication arises from the fact that both _WIN32 and
_MSC_VER are defined which makes miniaudio think it's using a normal
desktop Windows build. In practice it mostly works, but there's a few
things needing to be changed specifically for NXDK:

  - `fopen_s()` is not a thing with NXDK. It always uses `fopen()`.
  - There is no `_wfopen()`, nor `wcsrtombs()`, so attempting to open
    a file from a wide character string will fail.
  - There is also no `CreateFileW()`, so this code path will also
    result in an error if you attempt to open a file from a wide
    character path.
  - `CoInitialize()` is not a thing with NXDK and has therefore been
    excluded from the build.
  - `GetFileInformationByHandle()` does not exist, and neither does
    `struct stat` or `stat()`. Since the only file information miniaudio
    attempts to retrieve is the file size, I've implemented a fall back
    which uses the seek/tell/seek pattern when info retrieval is
    unavailable.
  - A fall back has been implemented for comparing wide character path
    extensions which performs a case-sensitive compare instead. This
    means that if you are using wide character paths, miniaudio will not
    detect an extension like "wav" and "WAV" as the same thing. This
    might be made more robust later if there is enough demand.

Public issue https://github.com/mackron/miniaudio/issues/1023
2025-08-07 13:45:11 +10:00
David Reid c87f207f4e Win32: Use CreateSemaphore() instead of CreateSemaphoreW()
This should improve compiler compatibility for those that do not
define `CreateSemaphoreW()`, such as NXDK.
2025-08-07 13:23:35 +10:00
David Reid 02ae7e41f0 Update dr_libs. 2025-08-07 13:18:56 +10:00
David Reid 457a7279fa Update dr_wav. 2025-07-23 12:08:35 +10:00
David Reid fe1da60e1e Update change history. 2025-07-23 10:11:14 +10:00
David Reid 0b70a990ca Minor update to documentation. 2025-07-23 09:16:20 +10:00
David Reid 556160909e Try fixing the Switch build. 2025-07-22 15:46:17 +10:00
David Reid c48975f4a9 Don't link against dl with NO_RUNTIME_LINKING. 2025-07-21 17:06:34 +10:00
David Reid 79b4ddc27d Fix CMake script for NetBSD and OpenBSD. 2025-07-21 16:54:27 +10:00
Marcin Serwin 0b9f03a376 Respect CMAKE_INSTALL_INCLUDEDIR when installing headers
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-07-21 16:48:25 +10:00
Marcin Serwin 140b9c7f9f Add compile define for specific backends only in CMake
Fixes https://github.com/mackron/miniaudio/pull/1010#issuecomment-3093830252

Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-07-21 16:47:46 +10:00
Marcin Serwin ac8c908283 Don't force static libraries
The default for creating libraries is static but can be overridden
by setting BUILD_SHARED_LIBS variable. Setting it explicitly makes it
impossible to override.

https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html

Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-07-21 16:47:03 +10:00
David Reid 8b87c10681 Fix some errors with MA_NO_RUNTIME_LINKING. 2025-07-20 19:41:28 +10:00
andy5995 629d509072 Fix warning: function declaration without a prototype
Harmless warning on FreeBSD 14.2,
https://cirrus-ci.com/task/4700955851096064?logs=build#L44

```
../subprojects/miniaudio-0.11.22/miniaudio.h:36997:36: warning: a
function declaration without a prototype is deprecated in all versions
of C [-Wstrict-prototypes]
 36997 | static int ma_open_temp_device__oss()
       |                                    ^
       |                                     void
```
2025-07-20 07:51:59 +10:00
David Reid 5c86dd9153 Fix a possible division by zero error. 2025-07-20 07:44:06 +10:00
David Reid e54336996d Update dr_libs. 2025-07-04 11:13:40 +10:00
David Reid 1c7967fc88 PulseAudio: Fix a crash if the requested channel count is too high. 2025-07-04 06:37:08 +10:00
David Reid 2bc0e14abf Minor clarifying comment. 2025-07-02 17:57:50 +10:00
Sam Tupy b348ab0155 fix assertion failure upon loading invalid sound path due to extra ma_fence_release
In ma_resource_manager_data_buffer_node_acquire_critical_section, a job which releases already acquired fences is either processed or posted based on MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT. However on job post or process failure, the fences were being unconditionally released.

This commit moves the fence releases in acquire_critical_section down into a nested branch which only executes if MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is not set, causing the fence release to take place only if the job failed to post while relying on the job itself to release the fences if it processes rather than causing a duplicate ma_fence_release and thus an eventual assertion failure.
2025-07-02 17:54:30 +10:00
David Reid 346d65091a Don't use threading or device IO in the conversion test. 2025-07-02 10:30:47 +10:00
David Reid aa75d5f8e8 Don't include pthread.h if threading is disabled. 2025-07-02 10:30:06 +10:00
David Reid 0ac5c89157 MP3: Disable SIMD when disabled by miniaudio.
This applies when MA_NO_NEON or MA_NO_SSE2 is set.

Public issue https://github.com/mackron/miniaudio/issues/997
2025-06-09 07:37:25 +10:00
Guillaume Prieur 47020e4092 Fix seek origin conversion in ma_mp3_dr_callback__seek 2025-05-19 08:34:46 +10:00
David Reid 02873ca300 Update c89atomic. 2025-05-11 06:48:51 +10:00
David Reid 11177ed19f Style and formatting changes. 2025-05-08 14:15:12 +10:00
caturria 682fad7d55 Revert accidental include change. 2025-05-08 14:04:57 +10:00
caturria 1497f5e467 Fix leaked OggVorbis_File handle when input is not vorbis. Also implements ma_libvorbis_get_length_in_pcm_frames() for the common case of an ogg file with a single bitstream. 2025-05-08 14:04:57 +10:00
David Reid 853f27ed56 Whitespace. 2025-05-08 08:01:12 +10:00
Keith 05d367eed5 CMake: remove macro and fix warnings 2025-05-08 07:11:27 +10:00
Keith 97493bdfcd CMake: support install
All Miniaudio static libraries now install their headers such that they can
still use relative paths, but external code can #include "miniaudio/miniaudio.h"

Also adds a CMake macro to simplify adding static libraries
2025-05-08 07:11:27 +10:00
David Reid 3edfb70a26 Add amalgamation script. 2025-05-08 07:03:54 +10:00
David Reid 72e4721b2c Update c89atomic. 2025-05-08 06:57:38 +10:00
David Reid b255e91e08 Fix a regression with the MP3 amalgamation. 2025-04-30 09:02:32 +10:00
David Reid b261f4aeec Update dr_wav. 2025-04-30 09:01:27 +10:00
David Reid bd59c52309 Update dr_mp3. 2025-04-30 08:07:36 +10:00
David Reid f15ddefce8 Comment out some unused functions. 2025-04-29 07:44:51 +10:00
David Reid 29c17fcb22 Try improving compatibility of ma_yield().
My understanding is that "pause" was introduced with the Pentium 4 and
assembles to opcode F390 which is the same as "rep; nop". I believe this
should be backwards compatible.

Using "pause" here introduces an compilation error when targeting
architectures older than Pentium 4.
2025-04-27 21:01:59 +10:00
David Reid 5a9c322c83 Fix an error with a GCC version check. 2025-04-27 20:27:19 +10:00
David Reid cd14d18d0c Fix an error when pthread_attr_setstacksize() is unavailable. 2025-04-27 19:50:35 +10:00
David Reid 534b43e800 Comment out some unused functions. 2025-04-27 19:28:28 +10:00
David Reid caa3d2a339 Try fixing a compatibility issue with old GCC.
This is untested.
2025-04-27 19:22:20 +10:00
David Reid 4c2d1bb67c Add a missing va_end() to logging functions. 2025-04-27 18:58:15 +10:00
David Reid 5ba0fc51b6 Fix an error with old versions of GCC. 2025-04-27 18:41:05 +10:00
David Reid e58a24124a Try fixing an error with old versions of GCC. 2025-04-27 17:53:07 +10:00
David Reid 0b4646d31a Fix a compiler compatibility issue. 2025-04-27 17:26:49 +10:00
David Reid 9032fdbced Try fixing a null termination bug in ma_channel_map_to_string().
Public issue https://github.com/mackron/miniaudio/issues/980
2025-04-25 18:58:01 +10:00
David Reid 2ee920577e Attempt to fix an error with ma_log_postv().
Public issue https://github.com/mackron/miniaudio/issues/980
2025-04-25 18:41:44 +10:00
David Reid a944e19331 pthread: Add support for falling back to a non-realtime thread.
MA_NO_PTHREAD_REALTIME_PRIORITY_FALLBACK can be used to disable this
functionality for applications that have a hard requirement on a
realtime thread.
2025-04-19 14:38:29 +10:00
Pavel Galkin 8a9ea7ce07 ALSA: Fix undefined symbols during static compilation
These were undefined when linking with the -static flag on:

  1. Alpine Linux v3.21 x86_64
  2. Debian GNU/Linux 12 (bookworm) aarch64

In both cases I had to manually compile alsa-lib as a static library
because the systems have only .so versions. I did it with:

  cd path/to/alsa-libs
  ./gitcompile static
  <this will create alsa-libs/src/.libs/libasound.a>

Tested with alsa-libs 1.12.2 and 1.12.3.

It's possible to reproduce the errors like this:

clang -static -DMA_NO_RUNTIME_LINKING -DMA_ENABLE_ONLY_SPECIFIC_BACKENDS -DMA_ENABLE_ALSA -DMA_DEBUG_OUTPUT -Wall -Wextra -g3 simple_playback_sine.c -o build/miniwave -fcolor-diagnostics -ferror-limit=2 -fuse-ld=lld -I../
-L/home/antharas/code/alsa-lib/src/.libs/ -lasound

However, even though it compiles I can't verify that everything works.

  1. Alpine has musl which doesn't support runtime linking when
     compiling with -static. So even though I disabled it for
     miniaudio.h, libasound.a itself uses dlopen on
     libasound_module_pcm_pipewire.so.
  2. Debian uses glibc so it supports it but fails because my machine is
     a VPS that doesn't have a soundcard.

No big deal, I don't really need to statically link, it was just
something I stumbled upon.

Also these are currently unused:

  _snd_pcm_hw_params_set_rate_minmax
  ma_timer_init
  ma_timer_get_time_in_seconds
  ma_device__on_notification_rerouted
2025-04-19 13:59:09 +10:00
David Reid 80747f440a Minor style changes and add a comment. 2025-04-19 13:45:48 +10:00
Your Name d40a385e07 Make the COM-related code more reasonable 2025-04-19 13:37:23 +10:00
David Reid 7df2a50d7f Fix a ring buffer alignment error.
Public issue https://github.com/mackron/miniaudio/issues/979
2025-04-19 13:23:49 +10:00
David Reid ab80abf061 Fix typo. 2025-03-27 18:54:21 +10:00
caturria b9292a301f Address leaked pResourceManagerDataSource when invalid input causes sound init to fail. 2025-03-18 09:13:44 +10:00
David Reid 484a56499d Fix a warning. 2025-03-09 07:43:36 +10:00
David Reid b12959f1d4 Update dr_libs. 2025-03-09 07:31:55 +10:00
David Reid 94077d5a95 Minor style changes. 2025-03-08 18:23:34 +10:00
znakeeye b3ab0567c3 Final tweaks. Removed unnecessary if statement. 2025-03-08 17:47:48 +10:00
znakeeye 7158cf58f9 Re-route lock moved outside ma_device_reinit__aaudio
Re-route lock moved outside ma_device_reinit__aaudio call to avoid potential race condition. Also cleaned up the reroute code a bit.
2025-03-08 17:47:48 +10:00
znakeeye fe616c1a5a AAudio: Prevent re-routing while tearing down device. Fixes a crash where re-route thread would read ma_device while being destroyed from main thread. 2025-03-08 17:47:48 +10:00
David Reid cd16c5bcd3 Update fs. 2025-03-05 15:02:33 +10:00
David Reid 76dfabbb45 Remove a link from the readme since I no longer monitor it. 2025-02-26 09:08:26 +10:00
David Reid 46d8abf3de Add teardown to custom_decoder_engine. 2025-02-25 06:48:34 +10:00
David Reid 350784a946 Version 0.11.22 2025-02-24 16:31:42 +10:00
David Reid a65a7d139f ALSA: Fix a bug where a playback device can fail to start. 2025-02-24 14:41:45 +10:00
David Reid 2a79d124c1 Fix some bugs in the resource_manager_advanced example. 2025-02-24 12:39:42 +10:00
David Reid 8261dc8972 Minor language change. 2025-02-24 12:25:15 +10:00
David Reid c1daa31759 Update fs. 2025-02-24 12:24:08 +10:00
David Reid b0e845e796 Update readme. 2025-02-24 10:20:34 +10:00
David Reid d28ce1a841 Update changes. 2025-02-24 10:15:22 +10:00
David Reid 1e2be9307e Minor change of language to the readme. 2025-02-24 10:05:04 +10:00
David Reid 17b8dbf948 Remove an invalid comment from some examples. 2025-02-24 10:03:21 +10:00
David Reid 4663423838 Update dr_wav. 2025-02-24 09:10:23 +10:00
David Reid d6a1350c1f Clean up some old code. 2025-02-24 09:09:23 +10:00
David Reid c92e662de7 Simplify some test code. 2025-02-23 18:22:17 +10:00
David Reid e272f56750 Update fs. 2025-02-23 18:14:04 +10:00
David Reid 1a83b0baa4 Update fs. 2025-02-23 15:09:00 +10:00
David Reid 9511596a25 Silence a static analysis warning. 2025-02-23 14:26:58 +10:00
David Reid 5c0724ad59 Fix an extremely unlikely bug with the resource manager.
This checks the result of reading from the decoder when asynchronously
loading a file. In practice the existing check already covers this, but
it's technically possible for an error result the be skipped over.
2025-02-23 14:20:16 +10:00
David Reid c3b0a7fbbc Fix a bug in ma_decoder_read_pcm_frames().
This will abort reading early if the underlying data source returns an
error.
2025-02-23 14:14:23 +10:00
David Reid e4363a90be Fix a channel mapping bug.
Thanks to the Clang static analyzer for this one!
2025-02-23 14:03:34 +10:00
David Reid 6453c9ff22 AAudio: Remove some duplicate code. 2025-02-23 13:46:19 +10:00
David Reid 8c52072f43 Remove const qualifiers from decoding backend vtable arrays. 2025-02-23 12:54:19 +10:00
David Reid b6184fa2a0 Fix some Wnewline-eof warnings. 2025-02-23 12:11:29 +10:00
David Reid 166fd6dfc7 Silence some static analysis warnings. 2025-02-23 12:03:22 +10:00
David Reid 54373128ee Fix a subtle undefined behaviour error. 2025-02-23 11:49:31 +10:00
David Reid c74c90f686 Minor comment changes. 2025-02-23 11:35:26 +10:00
David Reid 01302b9715 Fix a parameter order error.
This did not affect functionality at all because the implementation of
the relevant function is just a simple bitwise OR.
2025-02-23 09:34:48 +10:00
David Reid b5f1ff125e Update documentation for ma_context_get_devices(). 2025-02-23 09:31:52 +10:00
znakeeye 7a1135d448 Goto label placed outside braces for C compliance. 2025-02-23 09:25:16 +10:00
znakeeye 9f9fc2333e Improved synchronization for AAudio rerouting. This should fix the crash observed in ma_device_init__aaudio (when re-routing). 2025-02-23 09:25:16 +10:00
David Reid a497466f75 Add basic testing app for Android.
This is only very basic right now. Will be expanded on later.
2025-02-22 18:54:45 +10:00
David Reid 37b95f0f42 Remove unnecessary example.
The delay node is demonstrated in the engine_effects example.
2025-02-22 14:36:46 +10:00
David Reid 9f10bc7540 Improvements to the build system for extra nodes.
With this change, nodes in the extras folder can now be compiled as a
conventional library.
2025-02-22 14:25:45 +10:00
David Reid 1fbad32949 Stop using MINIAUDIO_IMPLEMENTATION in examples. 2025-02-22 13:09:11 +10:00
David Reid e1f5ed4f79 Rename some more test source files. 2025-02-22 13:00:06 +10:00
David Reid ed5cda309c Simplify the conversion test. 2025-02-22 12:50:00 +10:00
David Reid 3435aafb34 Use a simplified naming scheme for tests. 2025-02-22 12:29:56 +10:00
David Reid 3fd7c9f199 Fix a bug when no data callback is specified in the device config.
Public issue https://github.com/mackron/miniaudio/issues/893
2025-02-22 10:14:04 +10:00
David Reid deafb7e96f Add debugging sandbox for the purpose of debugging miniaudio. 2025-02-22 09:44:03 +10:00
David Reid 2e054f8011 Update gitignore. 2025-02-22 09:42:39 +10:00
David Reid c13504629e Minor update to custom_backend example. 2025-02-22 09:35:16 +10:00
David Reid f9caab2fd5 Update readme.
The user can just look at the CMakeLists.txt file instead of having the
readme duplicate information.
2025-02-21 20:13:51 +10:00
David Reid 57fbc6dd36 WASAPI: Uninitialize a mutex in device uninitialization. 2025-02-21 10:30:22 +10:00
David Reid ee3e532a54 Update fs. 2025-02-21 10:25:02 +10:00
David Reid 178797502e Remove an unnecessary cast. 2025-02-21 08:48:05 +10:00
David Reid 0576191d7d Fix a compilation error with the last commit. 2025-02-21 08:26:52 +10:00
David Reid 6bc3fec34e Try fixing a const-correctness error. 2025-02-21 07:39:16 +10:00
David Reid 2542be5db8 Merge remote-tracking branch 'origin/dev' into dev 2025-02-20 17:46:38 +10:00
David Reid 67d1aca341 iOS: Try fixing an initialization error with capture devices.
Public issue https://github.com/mackron/miniaudio/issues/868
2025-02-20 17:43:40 +10:00
David Reid 5975db4c76 Update fs. 2025-02-20 12:10:26 +10:00
David Reid 08d6d1fac0 Fix a pedantic warning with Clang. 2025-02-20 12:04:16 +10:00
David Reid 4d971fe480 Update CMake script to detect Apple Clang. 2025-02-20 12:03:47 +10:00
David Reid 575790bb29 Update GitHub templates. 2025-02-19 18:43:15 +10:00
David Reid e49ce7df95 Update readme. 2025-02-19 18:43:00 +10:00
David Reid d672b9610f Add a couple of Vorbis and Opus files for testing. 2025-02-19 18:10:47 +10:00
David Reid 3889066fac Update CMake build script.
These changes make it easier to integrate vorbisfile and opusfile from
source.
2025-02-19 18:09:00 +10:00
David Reid ff66923b9a Fix a compilation warning. 2025-02-19 18:05:53 +10:00
David Reid e3151f2df1 Silence an unreachable code warning with MSVC. 2025-02-19 12:32:12 +10:00
David Reid abb81fe95c Fix a warning in miniaudio_libvorbis. 2025-02-19 12:31:48 +10:00
David Reid 8ad250ccf6 Updates to custom decoders. 2025-02-19 12:02:37 +10:00
David Reid b40803cf97 Update fs. 2025-02-19 12:01:22 +10:00
David Reid 466a1354ce Experiment with a fix for older versions of Clang. 2025-02-19 10:30:09 +10:00
David Reid e08c1303ef Fix a bug with the deviceio test. 2025-02-19 09:43:51 +10:00
David Reid 698a4319f0 Update fs. 2025-02-19 09:43:19 +10:00
David Reid eee86a0ae1 Fix the C++ build for some examples. 2025-02-19 08:28:01 +10:00
David Reid d3a4b9cf20 Minor changes to CMakeLists. 2025-02-19 08:23:11 +10:00
David Reid 48ac10d1e1 Fix a C++ compilation error. 2025-02-18 18:41:10 +10:00
Edoardo Lolletti 4b4349af52 Fix miniaudio_libvorbis.h compilation as c++ 2025-02-18 18:39:26 +10:00
David Reid a4d462e39e Add initial CMake file. 2025-02-18 17:53:44 +10:00
David Reid ef662aaddf Add a sound for testing. 2025-02-18 17:51:37 +10:00
David Reid 22a5c65c94 Update tests. 2025-02-18 17:46:57 +10:00
David Reid cff683a1b1 Add a non-interactive mode for the deviceio test. 2025-02-18 17:30:59 +10:00
David Reid 62d64d14bd Fix an error with band-pass filters. 2025-02-18 17:26:53 +10:00
David Reid cf9371748a Fix compilation warnings with some tests. 2025-02-17 18:15:22 +10:00
David Reid 640d70c307 Add deprecation notice to old libopus and libvorbis custom decoders. 2025-02-17 18:14:40 +10:00
David Reid 46788d59a8 Rework the libvorbis and libopus custom decoders.
These decoders have been moved into their own subfolders under the
extras/decoders folder:

  extras/decoders/libvorbis
  extras/decoders/libopus

In addition to being relocated, they have also been split into separate
.c/h pairs. They now work like a more conventional library. The
implementation of these libraries have also been decoupled from the
miniaudio implementation which means they depend only on the header
section of miniaudio.h now.

With this change the custom_decoder and custom_decoder_engine examples
have been updated. To compile these you now need to link in the
miniaudio_libvorbis.c and miniaudio_libopus.c files via your build
tool. For your own code, you can still include the .c files directly
into your code if you want to compile as a single translation unit.
2025-02-17 16:57:47 +10:00
David Reid 01d6297bec Fix some warnings with some more examples. 2025-02-17 16:52:54 +10:00
David Reid de5f370d09 Fix some warnings with examples. 2025-02-17 16:01:19 +10:00
David Reid 47aa3e34e0 Update gitignore. 2025-02-17 15:59:40 +10:00
David Reid 445cdcb82b AAudio: Fix a possible compilation error on older SDKs. 2025-02-17 10:28:43 +10:00
David Reid 34092dbfc8 Fix an unused parameter warning. 2025-02-17 09:44:09 +10:00
David Reid 14b986448f Minor change to ma_calculate_buffer_size_in_milliseconds_from_frames(). 2025-02-17 09:36:50 +10:00
David Reid a6ac898663 Minor adjustment to a calculation. 2025-02-17 09:30:40 +10:00
Andrew Opalach 017f8944d3 WASAPI: Release mapped buffer before stopping device 2025-02-17 09:24:59 +10:00
Andrew Opalach e15fd218be WASAPI: Fix drain on device stop 2025-02-17 09:24:59 +10:00
David Reid 4c7021e53d WASAPI: Fix an error when stopping a device. 2025-02-16 17:44:21 +10:00
David Reid 856494d253 Update docs for missing build options.
Public issue https://github.com/mackron/miniaudio/issues/942
2025-02-16 15:45:56 +10:00
David Reid a0aac6b5ec Remove reference to MA_HAS_OPUS. 2025-02-16 15:17:37 +10:00
David Reid 60c7c776b4 PulseAudio: Fix a possible race condition with device init. 2025-02-16 14:59:16 +10:00
David Reid 125e9226fb Fix an error with ma_sound processing. 2025-02-16 13:18:52 +10:00
David Reid 4deb3d4c6a Remove a stray space. 2025-02-16 13:17:52 +10:00
David Reid 3ffdbdc710 Add miniaudio.c.
This is in preparation for splitting miniaudio into a split .c/h pair,
away from a single header.

`MINIAUDIO_IMPLEMENTATION` is still supported, but will be removed in
version 0.12 and should be considered deprecated. It's recommended to
start the transition to the new .c file.
2025-02-16 10:30:00 +10:00
David Reid 9b9e71ab6c PulseAudio: Fix an error with the construction of the stream name. 2025-02-13 11:15:32 +10:00
znakeeye f39bbe2f4d Revert "Fix for NDK issue 360. dlclose() skipped pre-API 28. This fixes weird crashes during uninit."
This reverts commit 14f698fcf2.
2025-02-13 07:49:22 +10:00
znakeeye 79bb4d7a37 Improved fix for dlclose() bug on Android. Now applying fix for all backends on Android < 28. 2025-02-13 07:49:22 +10:00
znakeeye f970144a3d Fix for NDK issue 360. dlclose() skipped pre-API 28. This fixes weird crashes during uninit. 2025-02-13 07:49:22 +10:00
znakeeye afc7e17fe6 Added MA_NO_RUNTIME_LINKING support for AAudio backend. 2025-02-13 07:49:22 +10:00
znakeeye 047200eace Fixed double-free issue in AAudio backend. 2025-02-08 07:20:50 +10:00
HeroesOfBalkan 6d5efde254 Rename secondIndex variables to seekPointInSeconds 2025-01-22 09:24:38 +10:00
HeroesOfBalkan 9da8df1b9f Add explicit casts to suppress -Wfloat-conversion warnings 2025-01-22 09:24:38 +10:00
HeroesOfBalkan ed5964c9f6 Fix unused and unitialized variable warning 2025-01-22 09:24:38 +10:00
HeroesOfBalkan 7e81d3ac80 Refactor new methods to wrap around their PCM equivalents 2025-01-22 09:24:38 +10:00
HeroesOfBalkan 450dcb1af3 Fix bug returning success when NULL is passed & typo corrected in a comment 2025-01-22 09:24:38 +10:00
HeroesOfBalkan 38f7d29f6f New API methods to seek data sources using seconds 2025-01-22 09:24:38 +10:00
David Reid 1fe39f949a Fix a documentation error. 2025-01-21 13:04:24 +10:00
David Reid 3fb7027682 Update gitignore. 2025-01-18 14:30:00 +10:00
David Reid 14a455143f Version control some osaudio files. 2025-01-18 14:20:13 +10:00
David Reid 977bd616ff Add icons for website. 2025-01-18 14:19:44 +10:00
David Reid a3ae2e71ff Fix a typo. 2025-01-18 14:13:53 +10:00
David Reid 68a526a759 Update copyright date. 2025-01-18 10:48:35 +10:00
David Reid 8383893c9c Fix a bug in ma_data_source_read_pcm_frames_from_backend(). 2025-01-18 07:14:55 +10:00
David Reid 7a25af64d6 Fix a crash in ma_data_source_seek_pcm_frames(). 2025-01-17 19:11:26 +10:00
David Reid fc905ec97f Add simple_spatialization example. 2025-01-11 18:42:34 +10:00
David Reid 3081e314b7 Update change history. 2025-01-11 16:47:30 +10:00
David Reid fcddfe6204 Update ma_pcm_rb data source implementation.
The data source implementation of a ma_pcm_rb could possibly return a
frame count of 0 which would in turn result in
ma_data_source_read_pcm_frames() returning MA_AT_END which does not
make sense for a ring buffer since it has no notion of an end.
2025-01-11 16:30:15 +10:00
David Reid 547ef1c9b7 Don't return MA_AT_END from ring buffers.
There is no notion of an "end" in a ring buffer. Also, this result is
returned when the operation completed successfully which makes a result
code other than MA_SUCCESS confusing.
2025-01-11 15:57:42 +10:00
David Reid 928ed8bd85 Web Audio: Enable threading in ma_engine if compiling with -pthread.
With this commit, when targeting pthreads with the -pthread, the engine
will allow threading with it's internal resource manager.

Public issue https://github.com/mackron/miniaudio/issues/855
2025-01-11 13:00:33 +10:00
David Reid ae2cd4bea4 Web Audio: Add support for variable buffer sizes to Audio Worklets.
Support for this was added to Emscripten 3.1.70. Currently only
querying the quantum size is supported. I believe support for
configuring the quantum size is coming later in Web Audio 1.1.

Compilation with versions of Emscripten earlier than 3.1.70 is still
supported.
2025-01-11 10:17:20 +10:00
David Reid b53daca554 Clean up. 2025-01-11 09:25:43 +10:00
francois@recisio.com 1a7a9a7ed2 Fix build for emscripten before 3.1.70 2025-01-11 09:21:06 +10:00
francois@recisio.com 82ae0138f3 Improve fix, handle all outputs 2025-01-11 09:21:06 +10:00
francois@recisio.com 8d5bf8210c WebAudio: Fix a noise sound before device was started 2025-01-11 09:21:06 +10:00
David Reid ad615af1a8 Fix some warnings with GCC. 2025-01-11 08:47:29 +10:00
David Reid 3a34c049fa Merge branch 'dev' of https://github.com/mackron/miniaudio into dev 2025-01-10 13:02:37 +10:00
Dmitry Atamanov 970c3801d9 Small fixes in tools/audioconverter 2025-01-10 13:02:21 +10:00
David Reid da76932f6b Update change history. 2025-01-06 10:52:12 +10:00
strager 7dbb9f5e1a Fix missing ma_resampler_init argument in docs
ma_resampler_init has three parameters, not two. Add the missing
pAllocationCallbacks argument in the example code.
2025-01-06 09:04:07 +10:00
znakeeye bff9689b80 Moved null check to ma_close_stream__aaudio(). 2025-01-06 08:34:05 +10:00
David Reid bea73835dd A very minor change for style consistency. 2025-01-06 08:34:05 +10:00
David Reid 6e1b0dbce4 AAudio: Explicitly stop the device in the event of a reroute failure.
This commit removes the calls to ma_device__set_state() and replaces
them with a call to ma_device_stop() which is the intended way for
backends to change the state of the device. In addition, this comes
with the added effect of firing the stop callback when a reroute fails.
2025-01-06 08:34:05 +10:00
David Reid 08152a6a6d AAudio: Adjustments to a compatibility workaround.
This change makes it so that setBufferCapacityInFrames() and
setFramesPerDataCallback() can be opted-in if explicitly requested in
the device config.

This also adds back enableCompatibilityWorkarounds in order to prevent
anyone's build from breaking when updating. This will be removed again
in the 0.12 branch.
2025-01-06 08:34:05 +10:00
znakeeye b6747d5efc Fix rare crash during uninit where the streams got closed by worker thread (re-routing) and never re-opened. 2025-01-06 08:34:05 +10:00
znakeeye cbabd2d13a AAudio re-routing slightly improved. Retrying re-routing at most three times. If some BlueTooth device goes nuts and toggles connection state back and forth, we bail out. 2025-01-06 08:34:05 +10:00
znakeeye 546e23c0fb Fix an edge case where the newly re-routed stream gets disconnected and fails to start. Re-routing (again) solves this very rare error. 2025-01-06 08:34:05 +10:00
znakeeye 1b6d634299 Changed log level for re-routing errors. 2025-01-06 08:34:05 +10:00
znakeeye 7f911f3d12 Add some logging when re-routing fails. Set device status to stopped. 2025-01-06 08:34:05 +10:00
znakeeye 75f46c6105 Fix re-routing issue where device inadvertently switches to legacy path after toggling devices. The legacy path (AudioStreamLegacy) has been proven to cause sudden disconnections with no callbacks being called, resulting in no audio. Also, when reading default device, we give the AAUDIO_PERFORMANCE_MODE_LOW_LATENCY hint, allowing for non-legacy path. 2025-01-06 08:34:05 +10:00
David Reid 51e005369f Fix a possible deadlock.
Public issue https://github.com/mackron/miniaudio/issues/919
2025-01-04 17:52:18 +10:00
David Reid d628284548 Fix a thread-safety error with ma_device_start/stop().
Public issue https://github.com/mackron/miniaudio/issues/919.
2025-01-04 09:07:20 +10:00
David Reid 059a25d9c5 Minor update to tests build instructions for Emscripten. 2025-01-04 09:05:39 +10:00
strager 6a8a756b88 Fix typo in docs
ma_context_get_devices and ma_context_enumerate_devices are mutually related
functions.

enumerate_devices's docs refer to get_devices, but get_devices's docs refer to
get_devices by mistake. Change get_devices's docs to refer to enumerate_devices
as intended.
2024-12-21 08:04:30 +10:00
David Reid 8b6611299e Fix a possible null pointer dereference. 2024-12-17 14:14:18 +10:00
David Reid ee506b17ea Update dr_libs. 2024-12-17 10:06:28 +10:00
Brian Whitman 12a8d4e491 fixing issue: error: implicit conversion turns floating-point number into integer: 'const Float64' (aka 'const double') to 'ma_uint32' (aka 'unsigned int') [-Werror,-Wfloat-conversion] 2024-10-17 15:40:31 +10:00
David Hill d1abdd100b fix crash in ma_device_uninit when using sndio 2024-10-17 15:39:22 +10:00
David Reid 192a84a106 Revert 2 PulseAudio related commits.
This reverts the following commits:

d46e19fb47
  "PulseAudio: Attempt to fix a deadlock."

61586de203
  "PulseAudio: Default to a blocking main loop."
2024-10-12 10:15:08 +10:00
David Reid 0d0953aa85 Fix build for 3DS.
Public issue https://github.com/mackron/miniaudio/issues/902
2024-10-12 09:30:30 +10:00
David Reid 6ab4567c57 Merge branch 'dev' of https://github.com/mackron/miniaudio into dev 2024-09-13 08:20:38 +10:00
David Reid 4598d72a33 Fix a typo. 2024-09-13 08:20:09 +10:00
Tero Parviainen 4f5106ec77 Add a missing pDevice handle to the Web Audio device index when using worklets
This matches the handle added for script processor node devices in #771
2024-08-22 06:58:38 +10:00
David Reid a611cf5f26 Add ma_device_id_equal(). 2024-08-06 12:07:55 +10:00
David Reid d726e85313 Fix an error with the engine's node graph processing.
Public issue https://github.com/mackron/miniaudio/issues/850
2024-08-06 09:19:54 +10:00
Nishi efa270af91 adjust comparison a bit 2024-08-06 08:18:49 +10:00
Nishi 98f6e923cc check __NetBSD_Version__ 2024-08-06 08:18:49 +10:00
Nishi 427bdc1d2a use AUDIO_GETFORMAT instead of AUDIO_GETINFO for NetBSD 2024-08-06 08:18:49 +10:00
David Reid 61586de203 PulseAudio: Default to a blocking main loop. 2024-08-05 10:34:01 +10:00
David Reid d46e19fb47 PulseAudio: Attempt to fix a deadlock.
Public issues
  https://github.com/mackron/miniaudio/issues/877
  https://github.com/mackron/miniaudio/issues/881
2024-08-05 09:21:23 +10:00
David Reid dc423daa41 Silence some GCC warnings.
This is from c89atomic which has been fixed upstream.
2024-08-04 09:08:27 +10:00
David Reid 5d827878f2 Rename a variable for consistency. 2024-08-04 08:35:05 +10:00
Timo Schwarzer d87230b4bd Allow setting the channel map that is requested from PulseAudio 2024-08-04 08:35:05 +10:00
raduetsya e1328d9d8a Fix an always-false warning 2024-07-27 10:51:44 +10:00
David Reid 8036ac3781 WASAPI: Fix a crash when changing devices. 2024-07-14 07:16:43 +10:00
Matthieu Bouron fc45d8ca06 AAudio: Fix ma_device_get_info() implementation 2024-06-08 12:51:59 +10:00
Matthieu Bouron 35215b266c CHANGES.md: add missing newline at EOF 2024-06-08 12:51:59 +10:00
David Reid 1c15cf6502 WASAPI: Fix a regression where incorrect device info is retrieved. 2024-05-14 07:48:53 +10:00
David Reid 196289592a WASAPI: Fix a bug with loopback device info retrieval. 2024-05-12 08:40:19 +10:00
David Reid 1b35118e31 AAudio: Potential fix for a failed assertion.
Public issue https://github.com/mackron/miniaudio/issues/833
2024-04-29 08:52:12 +10:00
RainRat 88436b25ef Update miniaudio.h
fix typos
2024-04-28 15:17:11 +10:00
RainRat 3db49afa5b revert unneeded changes per request 2024-04-28 15:17:11 +10:00
RainRat aa98e1c493 fix typos 2024-04-28 15:17:11 +10:00
David Reid afb121e2ce Update c89atomic. 2024-04-28 15:12:08 +10:00
David Reid 855628f15f Update dr_wav. 2024-04-28 14:55:42 +10:00
David Reid 9091cbd016 Core Audio: Try fixing a compilation error.
Public issue https://github.com/mackron/miniaudio/issues/841
2024-04-28 13:58:11 +10:00
David Reid e82703482b Fix a minor declaration inconsistency. 2024-04-28 12:59:52 +10:00
Sergey Fedorov 6700c7ecc7 miniaudio.h: fix for macOS 2024-04-26 07:23:10 +10:00
RainRat 3ba0595c6a fix typos 2024-04-11 12:40:39 +10:00
RainRat 4bc18bba5a revert unneeded changes per request 2024-04-11 12:40:39 +10:00
RainRat 26ce355703 fix typos 2024-04-11 12:40:39 +10:00
David Reid f7ad7772d1 ALSA: Try making the handling of poll() a bit more robust.
Public issue https://github.com/mackron/miniaudio/issues/836.
2024-04-07 14:38:09 +10:00
David Reid 0eb86ea1da Fix a bug where sounds loaded with MA_SOUND_FLAG_DECODE do not loop. 2024-03-01 09:59:44 +10:00
RainRat 2618c21415 revert unneeded changes per request 2024-03-01 08:35:45 +10:00
RainRat 030b9554c2 fix typos 2024-03-01 08:35:45 +10:00
David Reid c6d8b591f6 Remove some leftover experimental code. 2024-02-29 14:39:36 +10:00
David Reid 3bdf611768 Fix an invalid comment. 2024-02-29 14:30:48 +10:00
David Reid 63e1900db8 Update documentation. 2024-02-29 10:50:06 +10:00
David Reid 29da9b789c Add new init flags for sounds and resource managed data sources.
This adds the following flags:

  * MA_SOUND_FLAG_LOOPING
  * MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING

These can be used to initialize sounds and resource managed data
sources to loop by default. This is the recommended way to enable
looping for streams. The `isLooping` config option in
`ma_sound_config` and `ma_resource_manager_data_source_config` has been
deprecated. If you are using those, you should switch to the new flag
or else you'll get compiler errors when upgrading to a future version.
2024-02-29 10:25:02 +10:00
David Reid b454e7f14b PulseAudio: Try fixing a channel mapping bug.
Public issue https://github.com/mackron/miniaudio/issues/811
2024-02-28 18:40:35 +10:00
David Reid feea26496c Update Steam Audio example. 2024-02-28 17:17:13 +10:00
David Reid d36a2ef651 Minor cleanup. 2024-02-28 17:16:50 +10:00
David Reid e3af234720 Silence a warning in the node graph example. 2024-02-28 08:15:13 +10:00
David Reid 5cb0c05675 Update Steam Audio example to work with latest version. 2024-02-28 08:10:34 +10:00
David Reid 9aa6e035bb Memory improvements to node processing.
When processing a node, miniaudio will read into a temporary buffer
before mixing input attachments. This commit removes the per-node heap
allocation and replaces it with a per-graph stack. This should result
in less memory usage at larger scales, but at the expense of slightly
more usage at smaller scales.

The size of the stack can be configured via ma_node_graph_config. If
ma_engine is being used, it can be done via ma_engine_config.
2024-02-27 17:05:56 +10:00
David Reid 7a8ebd7f4d Update to node processing.
Previously, processing a node would involve a temporary buffer
allocated on the stack. Because this was fixed size, it would result in
processing being sub-divided into chunks in order to avoid overflowing
that buffer. This becomes an issue when a node needs to have a known
processing size. An example might be some kind of effect that requires
processing be in powers of two.

With this commit, the `processingSizeInFrames` variable in
`ma_node_graph_config` can be used to make it so processing always
happens in fixed sized chunks. In this situations, it's recommended you
always call `ma_node_graph_read_pcm_frames()` with a frame count of a
multiple of `processingSizeInFrames`.

The allocation strategy used here is not optimal and will be improved
in future commits. It currently allocates a buffer per-node, but since
the data contained within it is transient in nature, it should be
possible to use a global fixed sized stack that supports allocating a
variable amount of space within the stack buffer.
2024-02-27 15:31:17 +10:00
David Reid e32cc9ff83 Update dr_wav and dr_mp3. 2024-02-27 08:27:05 +10:00
David Reid babd7fb00f Forward declare ma_semaphore API. 2024-02-25 09:50:42 +10:00
David Reid f4e5cf99dc Update change history. 2024-02-08 15:26:55 +10:00
David Reid f20ab8a9ee Web: Increase the default stack size for the AudioWorklets thread. 2024-02-08 15:26:41 +10:00
Chris Genova aa57d052da Fix out of range check in ma_default_vfs_seek__win32 2024-01-30 10:54:46 +10:00
David Reid c0afa7e621 Web: Fix a possible JS error.
Public issue https://github.com/mackron/miniaudio/issues/810
2024-01-28 08:46:56 +10:00
David Reid bde517a166 Update dr_wav. 2024-01-23 16:34:34 +10:00
David Reid 3ed9d05c38 Fix compilation error on Android.
Public issue https://github.com/mackron/miniaudio/issues/805
2024-01-22 07:26:38 +10:00
David Reid a4aa0dc404 Fix amplification with ma_device_set_master_volume().
Public issue https://github.com/mackron/miniaudio/discussions/800
2024-01-12 08:10:53 +10:00
David Reid fd3c1b0af0 Formatting. 2024-01-10 18:19:24 +10:00
David Reid 10f9ef05a2 DirectSound: Attempted fix for an error when switching devices.
Public issue https://github.com/mackron/miniaudio/issues/779
2024-01-08 12:50:20 +10:00
David Reid bdab2fc3e0 Remove an accidental change to the deviceio test. 2024-01-08 12:30:04 +10:00
David Reid 9c73849f3b Fix a typo. 2023-12-23 09:30:08 +10:00
David Reid fde3a27d93 Add support for customizing pthread priorities at compile time.
Use the MA_PTHREAD_REALTTIME_THREAD_PRIORITY define.
2023-12-23 09:21:13 +10:00
David Reid 766a155fb3 Stop using MA_ASSERT in examples.
This is useful because MA_ASSERT is only defined in the implementation
section of miniaudio.h which can cause issues when people copy/paste
the code and use it in a file that does not have visibility of the
implementation.

Note that there are still more references to implementation-defined
macros, but these have been moved to the public section in the dev-0.12
branch so I'm not bothering to change those just yet.

Public issue https://github.com/mackron/miniaudio/issues/787
2023-12-17 08:42:19 +10:00
David Reid c29c001840 Fix an error where the pthread priority is not being set correctly. 2023-12-13 08:36:03 +10:00
David Reid ee3b7df66a Update change history. 2023-12-10 13:50:40 +10:00
David Reid 1325ac397b AAudio: Increase default min SDK version from 26 to 27.
Use `#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 26` if you need to
support SDK version 26.
2023-12-08 19:13:51 +10:00
xielock ed204e4b72 Fix bug in ma_node_detach_full(...) 2023-12-06 08:22:14 +10:00
David Reid 6099e6f41c Add support for customizing the min SDK version for AAudio.
Define MA_AAUDIO_MIN_ANDROID_SDK_VERSION to specify the minimum
required SDK version for enabling the AAudio backend.
2023-12-06 08:12:21 +10:00
David Reid 4f426f6db0 Update change history. 2023-12-06 07:59:52 +10:00
David Reid 7e38fa0e7e Update dr_wav. 2023-12-02 07:42:39 +10:00
David Reid d0709098cc Web: Try fixing a runtime error.
Public issue https://github.com/mackron/miniaudio/issues/781
2023-11-30 09:32:04 +10:00
David Reid 3c5b15b48b Update change history. 2023-11-29 07:51:30 +10:00
David Reid 2b8bb34ca2 Update split version for testing. 2023-11-29 07:31:38 +10:00
David Reid 2b0c525e53 Web Audio: Fix ScriptProcessNode path when compiling with --closure=1.
Audio Worklets do not work with --closure=1 because the callback used
with emscripten_create_wasm_audio_worklet_processor_async never gets
fired which means miniaudio will never be able to escape from it's busy
wait loop.

Public issue https://github.com/mackron/miniaudio/issues/778
2023-11-29 07:29:44 +10:00
David Reid 6cba159210 ALSA: Fix some warnings relating to unhandled return value of read(). 2023-11-29 06:27:03 +10:00
David Reid 1583329187 Update version. 2023-11-28 07:43:00 +10:00
David Reid 5a3655fea4 DirectSound: Add support for specifying an explicit HWND.
Public issue https://github.com/mackron/miniaudio/issues/779
2023-11-28 07:39:47 +10:00
David Reid 4a5b74bef0 Version 0.11.21 2023-11-15 11:23:00 +10:00
David Reid c77d40ba00 Core Audio: Fix a -Wshadow warning. 2023-11-15 10:35:34 +10:00
Michael Labbé 8fcf6889aa Add new ma_device_notification_type_unlocked notification
Unlocked notification fires on emscripten upon successful resume of
audio context attached to a device.  This occurs only once per device
and it happens after the browser has received the input event
necessary to begin playing audio on most webpages.  This is due
to autoplay rules.

It is recommended to wait until this event is fired to start a
'main game loop' on the web.
2023-11-15 10:28:02 +10:00
David Reid f9ce46330c Web: Fix an error where the buffer size is incorrectly calculated.
Public issue https://github.com/mackron/miniaudio/issues/773
2023-11-15 10:10:46 +10:00
David Reid 3b50a854ec Version 0.11.20 2023-11-10 07:44:19 +10:00
David Reid da0572e6b8 Fix a compilation error with iOS.
Public issue https://github.com/mackron/miniaudio/issues/770
2023-11-09 15:01:03 +10:00
David Reid eb0ce6f1a5 Fix an error when dynamically linking when forcing the UWP build.
This also fixes a possible crash during initialization due to leaving a
thread running after early termination of the initialization routine.
2023-11-04 08:43:16 +10:00
144 changed files with 32772 additions and 5910 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
Code of Conduct
===============
We don't believe we need a document telling fully grown adults how to conduct themselves within an open source
community. All we ask is that you just don't be unpleasant and keep everything on topic.
I don't believe we need a document telling fully grown adults how to conduct themselves within an open source
community. All I ask is that you just don't be unpleasant and keep everything on topic.
+1 -7
View File
@@ -9,10 +9,4 @@ assignees: ''
**DELETE ALL OF THIS TEXT BEFORE SUBMITTING**
If you think you've found a bug, it will be helpful to run with `#define MA_DEBUG_OUTPUT` above your miniaudio implementation like the code below and to include the output with this bug report:
#define MA_DEBUG_OUTPUT
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
If you are having issues with playback, please run the simple_payback_sine example and report whether or not it's consistent with what's happening in your program.
If you think you've found a bug, it will be helpful to compile with `#define MA_DEBUG_OUTPUT`. If you are having issues with playback, please run the simple_payback_sine example and report whether or not it's consistent with what's happening in your program.
+2 -2
View File
@@ -9,6 +9,6 @@ assignees: ''
**DELETE ALL OF THIS TEXT BEFORE SUBMITTING**
If you have a question about how to use the library, please read the documentation at the top of miniaudio.h and take a look at the examples. If that still doesn't answer your question, consider posting in the miniaudio subreddit at [r/miniaudio](https://www.reddit.com/r/miniaudio) or in the Discussions section here on GitHub instead. Otherwise, feel free to post your issue and we'll get to it as soon as possible.
If you have a question about how to use the library, please read the documentation at the top of miniaudio.h and take a look at the examples. If that still doesn't answer your question, consider posting in the Discussions section here on GitHub instead. Otherwise, feel free to post your issue and we'll get to it as soon as possible.
If you have an issue with playback, please run the simple_playback_sine example first and check whether or not that is working. Likewise for capture, please run the simple_capture example. If these examples work, it probably (but not always) means you're doing something wrong and a question in the subreddit or the Discussions section is more appropriate.
If you have an issue with playback, please run the simple_playback_sine example first and check whether or not that is working. Likewise for capture, please run the simple_capture example. If these examples work, it probably (but not always) means you're doing something wrong and a question in the Discussions section is more appropriate.
+46 -11
View File
@@ -1,14 +1,49 @@
\#issues/
_private/
examples/build/vc6/
examples/build/vc15/
examples/build/bin/
tests/_build/bin/
tests/_build/res/output/
tests/_build/tcc/
tests/_build/vc6/
tests/_build/vc15/
tools/_build/
\#docs/
/_private/
/build/
/debugging/
/evaluations/
/examples/build/bin/
/examples/build/codelite/
/examples/build/vc6/
/examples/build/vc15/
/examples/build/vc17/
/examples/simple_playback_sine.cpp
/external/ogg/
/external/vorbis/
/external/opus/
/external/opusfile/
/extras/osaudio/tests/build/bin/
/extras/osaudio/tests/build/vc17/
/extras/osaudio/tests/build/watcom-dos/
/extras/backends/pipewire/a.out
/extras/decoders/litewav/
/research/_build/
/tests/_build/bin/
/tests/_build/res/output/
/tests/_build/cmake-emcc/
/tests/_build/tcc/
/tests/_build/vc6/
/tests/_build/vc15/
/tests/_build/vc17/
/tests/_build/watcom/
/tests/_build/capture.wav
/tests/_build/a.out
/tests/_build/a.exe
/tests/debugging/archive/
/tests/*.c
/tests/*.cpp
/website/docs/
*.vcxproj.user
.vs/
.idea/
.vscode/
# Below are individual files that I may start version controlling later or delete outright.
/examples/build/COSMO.txt
/research/ma_fft.c
/research/ma_hrtf.c
/research/ma_atomic.c
/research/miniaudio_engine.c
/tests/stress/
/tools/hrtfgen/
+89 -6
View File
@@ -1,3 +1,86 @@
v0.11.23 - 2025-09-11
=====================
* Fixed an error in `ma_channel_map_to_string()` where the output string is not null terminated correctly.
* Fixed an error with logging due to mishandling of va_list.
* Fixed some errors when compiling with `MA_NO_RUNTIME_LINKING`.
* Fixed an error with `ma_sound` initialization where the initial loop points are not set correctly.
* Fixed an alignment error with the ring buffer.
* Fixed a memory leak in the resource manager when opening a file fails.
* Fixed an assertion failure in the resource manager when opening a file fails.
* Fixed a compilation warning relating to `MA_FALLTHROUGH`
* Fixed an undefined behavior error in the s16 to s32 conversion routine.
* Fixed an undefined behavior error relating to MurmurHash3.
* Fixed an undefined behavior error with the LCG random number generator.
* Fixed a compilation error with `MA_NO_SSE2`.
* Fixed some unused function warnings.
* Fixed a rare, but technically possible division by zero error.
* Some const correctness fixes for `ma_sound`.
* Improved compatibility with old versions of GCC.
* Miscellaneous documentation fixes.
* WAV, FLAC and MP3 decoders have been brought up to date with dr_libs. Of particular note, this should fix some long outstanding bugs with MP3 due to metadata not being handled correctly.
* POSIX: Added a fallback for when creation of a real-time thread fails. This fallback can be disabled with `MA_NO_PTHREAD_REALTIME_PRIORITY_FALLBACK` if you need an explicit failure.
* POSIX: pthread.h is no longer included when `MA_NO_THREADING` is defined.
* WASAPI: Improved handling of COM initialization and shutdown to make it a bit more robust.
* WASAPI: Fix an error due to a missing struct member.
* PulseAudio: Fixed a crash when requesting a channel count greater than 32.
* AAudio: Fixed a crash when uninitializing the device while in the middle of rerouting.
v0.11.22 - 2025-02-24
=====================
* Starting from version 0.12, miniaudio will be switching to a split .c/h pair, away from a single header. In preparation for this, a file named "miniaudio.c" has been added to repository. Currently this is just a simple wrapper around miniaudio.h and `MINIAUDIO_IMPLEMENTATION`. Nothing has changed in miniaudio.h, however when version 0.12 is released you will need to use miniaudio.c for the implementation. It's recommended you start the transition away from `MINIAUDIO_IMPLEMENTATION` and towards miniaudio.c. If you want to keep building your project as a single translation unit, you can do `#include "miniaudio.c"` which will continue to be supported with version 0.12 and beyond.
* In the extras folder, the `miniaudio_libvorbis.h` and `miniaudio_libopus.h` files have been deprecated. They have been replaced with versions in the `extras/decoders` folder. They are now split into a separate .c and .h files. The old files still exist for compatibility, but you need to transition over to the new versions. The transition should be trivial. Compile the .c files like a normal source file, and include the .h file like a normal header.
* Add `MA_SOUND_FLAG_LOOPING` and `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flags. These can be used to initialize sounds and resource managed data sources to loop by default. This is the recommended way to enable looping for streams. The `isLooping` config option in `ma_sound_config` and `ma_resource_manager_data_source_config` has been deprecated. If you are using those, you should switch to the new flag or else you'll get compiler errors when upgrading to a future version.
* `ma_rb_commit_read()`, `ma_rb_commit_write()`, `ma_pcm_rb_commit_read()` and `ma_pcm_rb_commit_write()` no longer return `MA_AT_END`. The reason for this change is that there's no real notion of an "end" in a ring buffer which makes this result code confusing. In addition, it's possible for these functions to return something other than `MA_SUCCESS` even when the operation completed successfully which adds to the confusion. The correct way to check if there is any more room in the ring buffer is to look at the frame count returned by `*rb_acquire_read/write()`.
* The `ma_pcm_rb` data source implementation has been modified to pad output data with silence if there is not enough data in the ring buffer to fill the request. What this means is for an `ma_pcm_rb`, `ma_data_source_read_pcm_frames()` should no longer return a frame count of less than what you requested, and will therefore never return `MA_AT_END` which does not make sense for a ring buffer since it does not have the notion of an end. This change should make it much easier to use a ring buffer as the data source for a `ma_sound`.
* There has been a minor change to `ma_calculate_buffer_size_in_milliseconds_from_frames()` to have it return a value rounded up to the nearest integer.
* When initialization of a decoder fails, it will now return the first error code encountered rather than always returning `MA_NO_BACKEND` regardless of the error.
* Add `ma_device_id_equal()` for comparing device IDs.
* Add support for `MA_NO_RUNTIME_LINKING` to the AAudio backend.
* Fix a buffer overrun bug with `ma_sound` processing.
* Fix a bug relating to node detachment.
* Fix a bug where amplification with `ma_device_set_master_volume()` does not work.
* Fix a bug where sounds loaded with `MA_SOUND_FLAG_DECODE` do not loop.
* Fix a bug with initialization of the band pass biquad filter.
* Fix a bug where a device would output distorted audio if initialized without a data callback.
* Fix various compiler and static analysis warnings.
* Various documentation updates.
* WASAPI: Fix an error when stopping the device. The "Failed to reset internal playback device." error should be significantly reduced in frequency.
* WASAPI: Fix an error when stopping the device where it was possible miniaudio would not wait for the device to be drained due to an error with the wait time calculation.
* WASAPI: Fix a COM related crash with device rerouting.
* DirectSound: Add support for specifying an explicit window handle for SetCooperativeLevel().
* ALSA: Fix a bug where a playback device can fail to start.
* ALSA: Fix some warnings relating to unhandled return value of `read()`.
* Web: Fix ScriptProcessorNode path when compiling with `--closure=1`. Note that the Audio Worklets path is not currently working due to the callback specified in `emscripten_create_wasm_audio_worklet_processor_async` never getting fired.
* Web: Fix an error with the unlocked notification when compiling as C++.
* Web: Fix a JavaScript error when initializing and then uninitializing a context before any interactivity.
* Web: miniaudio will now enable threading when the `-pthread` command line flag is used.
* Web: Infrastructure has been added to support configurable buffer sizes. In practice this is still restricted to 128 frames, but once Emscripten adds full support for configuration of the buffer size, it will be trivial to add support to miniaudio.
* AAudio: Fix some crashes relating to stream rerouting.
* AAudio: Fix an error where the device is silenced after rerouting. With this change, miniaudio will no longer give AAudio a hint to use your supplied period size which will therefore result in AAudio using its default latency configuration. If you want AAudio to try to use the period size you supply in the device config, which is the old behaviour, set `aaudio.allowSetBufferCapacity` to true in the device config. Note, however, if you do this you may end up with errors when rerouting between devices.
* AAudio: The default minimum SDK version has been increased from 26 to 27 when enabling AAudio. If you need to support version 26, you can use `#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 26`.
* AAudio: Fix ma_device_get_info() implementation.
* AAudio: Fix an error when an assertion can trigger due to AAudio reporting a frame count of 0.
* PulseAudio: Add a configuration option to control the PulseAudio-defined channel map to use.
* PulseAudio: Fix an extremely unlikely race condition with device initialization.
* PulseAudio: Fix a bug with the miniaudio-generated stream name. Previously this would create names like "miniaudi0" when it was supposed to be "miniaudio:0".
* iOS: Fix an error when trying to capture audio from the simulator with iOS version 16 and newer.
* sndio: Fix a crash with device uninitialization.
v0.11.21 - 2023-11-15
=====================
* Add new ma_device_notification_type_unlocked notification. This is used on Web and will be fired after the user has performed a gesture and thus unlocked the ability to play audio.
* Web: Fix an error where the buffer size is incorrectly calculated.
* Core Audio: Fix a -Wshadow warning.
v0.11.20 - 2023-11-10
=====================
* Fix a compilation error with iOS.
* Fix an error when dynamically linking libraries when forcing the UWP build on desktop.
v0.11.19 - 2023-11-04
=====================
* Fix a bug where `ma_decoder_init_file()` can incorrectly return successfully.
@@ -157,7 +240,7 @@ v0.11.7 - 2022-02-06
v0.11.6 - 2022-01-22
====================
* WASAPI: Fix a bug where the device is not stopped when an error occurrs when writing to a playback device.
* WASAPI: Fix a bug where the device is not stopped when an error occurs when writing to a playback device.
* PulseAudio: Fix a rare crash due to a division by zero.
* The node graph can now be used as a node. This allows node graphs to be connected to other node graphs.
* Fix a crash with high-pass and band-pass filters.
@@ -249,7 +332,7 @@ v0.11.0 - 2021-12-18
- Add support for disabling denormals on the audio thread.
- Add a delay/echo effect called ma_delay.
- Add a stereo pan effect called ma_panner.
- Add a spataializer effect called ma_spatializer.
- Add a spatializer effect called ma_spatializer.
- Add support for amplification for device master volume.
- Remove dependency on MA_MAX_CHANNELS from filters and data conversion.
- Increase MA_MAX_CHANNELS from 32 to 254.
@@ -451,7 +534,7 @@ v0.10.26 - 2020-11-24
v0.10.25 - 2020-11-15
- PulseAudio: Fix a bug where the stop callback isn't fired.
- WebAudio: Fix an error that occurs when Emscripten increases the size of it's heap.
- WebAudio: Fix an error that occurs when Emscripten increases the size of its heap.
- Custom Backends: Change the onContextInit and onDeviceInit callbacks to take a parameter which is a pointer to the config that was
passed into ma_context_init() and ma_device_init(). This replaces the deviceType parameter of onDeviceInit.
- Fix compilation warnings on older versions of GCC.
@@ -835,7 +918,7 @@ v0.9 - 2019-03-06
- API CHANGE: Add log level to the log callback. New signature:
- void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
- API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
a binding mainainer you will need to update your result code constants.
a binding maintainer you will need to update your result code constants.
- API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
will need to update.
- API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
@@ -944,7 +1027,7 @@ v0.8 - 2018-07-05
- Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
way is still supported for now, but you should update as it may be removed in the future.
- API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
mal_context_get_devices(). An additional low-level device enumration API has been introduced called
mal_context_get_devices(). An additional low-level device enumeration API has been introduced called
mal_context_enumerate_devices() which uses a callback to report devices.
- API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add
mal_get_bytes_per_frame().
@@ -1094,4 +1177,4 @@ v0.2 - 2016-10-28
- Added initial implementation of the OpenSL|ES backend.
v0.1 - 2016-10-21
- Initial versioned release.
- Initial versioned release.
+866
View File
@@ -0,0 +1,866 @@
cmake_minimum_required(VERSION 3.10)
# Extract version from miniaudio.h
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.h" MINIAUDIO_HEADER_CONTENTS)
string(REGEX MATCH "#define MA_VERSION_MAJOR[ \t]+([0-9]+)" _major_match "${MINIAUDIO_HEADER_CONTENTS}")
set(MA_VERSION_MAJOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define MA_VERSION_MINOR[ \t]+([0-9]+)" _minor_match "${MINIAUDIO_HEADER_CONTENTS}")
set(MA_VERSION_MINOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define MA_VERSION_REVISION[ \t]+([0-9]+)" _revision_match "${MINIAUDIO_HEADER_CONTENTS}")
set(MA_VERSION_REVISION "${CMAKE_MATCH_1}")
set(MINIAUDIO_VERSION "${MA_VERSION_MAJOR}.${MA_VERSION_MINOR}.${MA_VERSION_REVISION}")
project(miniaudio VERSION ${MINIAUDIO_VERSION})
# Options
option(MINIAUDIO_BUILD_EXAMPLES "Build miniaudio examples" OFF)
option(MINIAUDIO_BUILD_TESTS "Build miniaudio tests" OFF)
option(MINIAUDIO_BUILD_TOOLS "Build miniaudio development tools. Leave this disabled unless you know what you're doing. If you enable this and you get build errors, you clearly do not know what you're doing and yet you still enabled this option. Why would you do that?" OFF)
option(MINIAUDIO_FORCE_CXX "Force compilation as C++" OFF)
option(MINIAUDIO_FORCE_C89 "Force compilation as C89" OFF)
option(MINIAUDIO_NO_EXTRA_NODES "Do not build extra node graph nodes" OFF)
option(MINIAUDIO_NO_LIBVORBIS "Disable miniaudio_libvorbis" OFF)
option(MINIAUDIO_NO_LIBOPUS "Disable miniaudio_libopus" OFF)
option(MINIAUDIO_NO_WASAPI "Disable the WASAPI backend" OFF)
option(MINIAUDIO_NO_DSOUND "Disable the DirectSound backend" OFF)
option(MINIAUDIO_NO_WINMM "Disable the WinMM backend" OFF)
option(MINIAUDIO_NO_ALSA "Disable the ALSA backend" OFF)
option(MINIAUDIO_NO_PULSEAUDIO "Disable the PulseAudio backend" OFF)
option(MINIAUDIO_NO_JACK "Disable the JACK backend" OFF)
option(MINIAUDIO_NO_COREAUDIO "Disable the CoreAudio backend" OFF)
option(MINIAUDIO_NO_SNDIO "Disable the sndio backend" OFF)
option(MINIAUDIO_NO_AUDIO4 "Disable the audio(4) backend" OFF)
option(MINIAUDIO_NO_OSS "Disable the OSS backend" OFF)
option(MINIAUDIO_NO_AAUDIO "Disable the AAudio backend" OFF)
option(MINIAUDIO_NO_OPENSL "Disable the OpenSL|ES backend" OFF)
option(MINIAUDIO_NO_WEBAUDIO "Disable the Web Audio backend" OFF)
option(MINIAUDIO_NO_CUSTOM "Disable support for custom backends" OFF)
option(MINIAUDIO_NO_NULL "Disable the null backend" OFF)
option(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS "Only enable specific backends. Backends can be enabled with MINIAUDIO_ENABLE_[BACKEND]." OFF)
option(MINIAUDIO_ENABLE_WASAPI "Enable the WASAPI backend" OFF)
option(MINIAUDIO_ENABLE_DSOUND "Enable the DirectSound backend" OFF)
option(MINIAUDIO_ENABLE_WINMM "Enable the WinMM backend" OFF)
option(MINIAUDIO_ENABLE_ALSA "Enable the ALSA backend" OFF)
option(MINIAUDIO_ENABLE_PULSEAUDIO "Enable the PulseAudio backend" OFF)
option(MINIAUDIO_ENABLE_JACK "Enable the JACK backend" OFF)
option(MINIAUDIO_ENABLE_COREAUDIO "Enable the CoreAudio backend" OFF)
option(MINIAUDIO_ENABLE_SNDIO "Enable the sndio backend" OFF)
option(MINIAUDIO_ENABLE_AUDIO4 "Enable the audio(4) backend" OFF)
option(MINIAUDIO_ENABLE_OSS "Enable the OSS backend" OFF)
option(MINIAUDIO_ENABLE_AAUDIO "Enable the AAudio backend" OFF)
option(MINIAUDIO_ENABLE_OPENSL "Enable the OpenSL|ES backend" OFF)
option(MINIAUDIO_ENABLE_WEBAUDIO "Enable the Web Audio backend" OFF)
option(MINIAUDIO_ENABLE_CUSTOM "Enable support for custom backends" OFF)
option(MINIAUDIO_ENABLE_NULL "Enable the null backend" OFF)
option(MINIAUDIO_NO_DECODING "Disable decoding APIs" OFF)
option(MINIAUDIO_NO_ENCODING "Disable encoding APIs" OFF)
option(MINIAUDIO_NO_WAV "Disable the built-in WAV decoder" OFF)
option(MINIAUDIO_NO_FLAC "Disable the built-in FLAC decoder" OFF)
option(MINIAUDIO_NO_MP3 "Disable the built-in MP3 decoder" OFF)
option(MINIAUDIO_NO_DEVICEIO "Disable audio playback and capture" OFF)
option(MINIAUDIO_NO_RESOURCE_MANAGER "Disable the resource manager API" OFF)
option(MINIAUDIO_NO_NODE_GRAPH "Disable the node graph API" OFF)
option(MINIAUDIO_NO_ENGINE "Disable the high-level engine API" OFF)
option(MINIAUDIO_NO_THREADING "Disable threading. Must be used with MINIAUDIO_NO_DEVICEIO." OFF)
option(MINIAUDIO_NO_GENERATION "Disable generation APIs such as ma_waveform and ma_noise" OFF)
option(MINIAUDIO_NO_SSE2 "Disable SSE2 optimizations" OFF)
option(MINIAUDIO_NO_AVX2 "Disable AVX2 optimizations" OFF)
option(MINIAUDIO_NO_NEON "Disable NEON optimizations" OFF)
option(MINIAUDIO_NO_RUNTIME_LINKING "Disable runtime linking" OFF)
option(MINIAUDIO_USE_STDINT "Use <stdint.h> for sized types" OFF)
option(MINIAUDIO_DEBUG_OUTPUT "Enable stdout debug output" OFF)
include(GNUInstallDirs)
# Construct compiler options.
set(COMPILE_OPTIONS)
# Store libraries to install
# When installing any header that imports miniaudio.h from a relative path, we
# need to maintain its place in the directory tree so it can find Miniaudio
set(LIBS_TO_INSTALL)
# Special rules for Emscripten.
#
# - MINIAUDIO_FORCE_C89 is not supported.
# - MINIAUDIO_NO_RUNTIME_LINKING must be enabled.
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set(MINIAUDIO_FORCE_C89 OFF)
set(MINIAUDIO_NO_RUNTIME_LINKING ON)
# This is a hack to work around some errors relating to generation of the pkg-config file.
set(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS ON)
set(MINIAUDIO_ENABLE_WEBAUDIO ON)
endif()
if(MINIAUDIO_FORCE_CXX AND MINIAUDIO_FORCE_C89)
message(FATAL_ERROR "MINIAUDIO_FORCE_CXX and MINIAUDIO_FORCE_C89 cannot be enabled at the same time.")
endif()
if(MINIAUDIO_FORCE_CXX)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(STATUS "Compiling as C++ (GNU/Clang)")
list(APPEND COMPILE_OPTIONS -x c++)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message(STATUS "Compiling as C++ (MSVC)")
list(APPEND COMPILE_OPTIONS /TP)
else()
message(WARNING "MINIAUDIO_FORCE_CXX is enabled but the compiler does not support it. Ignoring.")
endif()
endif()
if(MINIAUDIO_FORCE_C89)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(STATUS "Compiling as C89")
list(APPEND COMPILE_OPTIONS -std=c89)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message(WARNING "MSVC does not support forcing C89. MINIAUDIO_FORCE_C89 ignored.")
else()
message(WARNING "MINIAUDIO_FORCE_C89 is enabled but the compiler does not support it. Ignoring.")
endif()
endif()
# Warnings
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND COMPILE_OPTIONS -Wall -Wextra -Wpedantic)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
#list(APPEND COMPILE_OPTIONS /W4)
endif()
# Construct compiler defines
set(COMPILE_DEFINES)
if(MINIAUDIO_NO_WASAPI)
list(APPEND COMPILE_DEFINES MA_NO_WASAPI)
endif()
if(MINIAUDIO_NO_DSOUND)
list(APPEND COMPILE_DEFINES MA_NO_DSOUND)
endif()
if(MINIAUDIO_NO_WINMM)
list(APPEND COMPILE_DEFINES MA_NO_WINMM)
endif()
if(MINIAUDIO_NO_ALSA)
list(APPEND COMPILE_DEFINES MA_NO_ALSA)
endif()
if(MINIAUDIO_NO_PULSEAUDIO)
list(APPEND COMPILE_DEFINES MA_NO_PULSEAUDIO)
endif()
if(MINIAUDIO_NO_JACK)
list(APPEND COMPILE_DEFINES MA_NO_JACK)
endif()
if(MINIAUDIO_NO_COREAUDIO)
list(APPEND COMPILE_DEFINES MA_NO_COREAUDIO)
endif()
if(MINIAUDIO_NO_SNDIO)
list(APPEND COMPILE_DEFINES MA_NO_SNDIO)
endif()
if(MINIAUDIO_NO_AUDIO4)
list(APPEND COMPILE_DEFINES MA_NO_AUDIO4)
endif()
if(MINIAUDIO_NO_OSS)
list(APPEND COMPILE_DEFINES MA_NO_OSS)
endif()
if(MINIAUDIO_NO_AAUDIO)
list(APPEND COMPILE_DEFINES MA_NO_AAUDIO)
endif()
if(MINIAUDIO_NO_OPENSL)
list(APPEND COMPILE_DEFINES MA_NO_OPENSL)
endif()
if(MINIAUDIO_NO_WEBAUDIO)
list(APPEND COMPILE_DEFINES MA_NO_WEBAUDIO)
endif()
if(MINIAUDIO_NO_CUSTOM)
list(APPEND COMPILE_DEFINES MA_NO_CUSTOM)
endif()
if(MINIAUDIO_NO_NULL)
list(APPEND COMPILE_DEFINES MA_NO_NULL)
endif()
if(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS)
list(APPEND COMPILE_DEFINES MA_ENABLE_ONLY_SPECIFIC_BACKENDS)
if(MINIAUDIO_ENABLE_WASAPI)
list(APPEND COMPILE_DEFINES MA_ENABLE_WASAPI)
endif()
if(MINIAUDIO_ENABLE_DSOUND)
list(APPEND COMPILE_DEFINES MA_ENABLE_DSOUND)
endif()
if(MINIAUDIO_ENABLE_WINMM)
list(APPEND COMPILE_DEFINES MA_ENABLE_WINMM)
endif()
if(MINIAUDIO_ENABLE_ALSA)
list(APPEND COMPILE_DEFINES MA_ENABLE_ALSA)
endif()
if(MINIAUDIO_ENABLE_PULSEAUDIO)
list(APPEND COMPILE_DEFINES MA_ENABLE_PULSEAUDIO)
endif()
if(MINIAUDIO_ENABLE_JACK)
list(APPEND COMPILE_DEFINES MA_ENABLE_JACK)
endif()
if(MINIAUDIO_ENABLE_COREAUDIO)
list(APPEND COMPILE_DEFINES MA_ENABLE_COREAUDIO)
endif()
if(MINIAUDIO_ENABLE_SNDIO)
list(APPEND COMPILE_DEFINES MA_ENABLE_SNDIO)
endif()
if(MINIAUDIO_ENABLE_AUDIO4)
list(APPEND COMPILE_DEFINES MA_ENABLE_AUDIO4)
endif()
if(MINIAUDIO_ENABLE_OSS)
list(APPEND COMPILE_DEFINES MA_ENABLE_OSS)
endif()
if(MINIAUDIO_ENABLE_AAUDIO)
list(APPEND COMPILE_DEFINES MA_ENABLE_AAUDIO)
endif()
if(MINIAUDIO_ENABLE_OPENSL)
list(APPEND COMPILE_DEFINES MA_ENABLE_OPENSL)
endif()
if(MINIAUDIO_ENABLE_WEBAUDIO)
list(APPEND COMPILE_DEFINES MA_ENABLE_WEBAUDIO)
endif()
if(MINIAUDIO_ENABLE_CUSTOM)
list(APPEND COMPILE_DEFINES MA_ENABLE_CUSTOM)
endif()
if(MINIAUDIO_ENABLE_NULL)
list(APPEND COMPILE_DEFINES MA_ENABLE_NULL)
endif()
endif()
if(MINIAUDIO_NO_DECODING)
list(APPEND COMPILE_DEFINES MA_NO_DECODING)
endif()
if(MINIAUDIO_NO_ENCODING)
list(APPEND COMPILE_DEFINES MA_NO_ENCODING)
endif()
if(MINIAUDIO_NO_WAV)
list(APPEND COMPILE_DEFINES MA_NO_WAV)
endif()
if(MINIAUDIO_NO_FLAC)
list(APPEND COMPILE_DEFINES MA_NO_FLAC)
endif()
if(MINIAUDIO_NO_MP3)
list(APPEND COMPILE_DEFINES MA_NO_MP3)
endif()
if(MINIAUDIO_NO_DEVICEIO)
list(APPEND COMPILE_DEFINES MA_NO_DEVICE_IO)
endif()
if(MINIAUDIO_NO_RESOURCE_MANAGER)
list(APPEND COMPILE_DEFINES MA_NO_RESOURCE_MANAGER)
endif()
if(MINIAUDIO_NO_NODE_GRAPH)
list(APPEND COMPILE_DEFINES MA_NO_NODE_GRAPH)
endif()
if(MINIAUDIO_NO_ENGINE)
list(APPEND COMPILE_DEFINES MA_NO_ENGINE)
endif()
if(MINIAUDIO_NO_THREADING)
list(APPEND COMPILE_DEFINES MA_NO_THREADING)
endif()
if(MINIAUDIO_NO_GENERATION)
list(APPEND COMPILE_DEFINES MA_NO_GENERATION)
endif()
if(MINIAUDIO_NO_SSE2)
list(APPEND COMPILE_DEFINES MA_NO_SSE2)
endif()
if(MINIAUDIO_NO_AVX2)
list(APPEND COMPILE_DEFINES MA_NO_AVX2)
endif()
if(MINIAUDIO_NO_NEON)
list(APPEND COMPILE_DEFINES MA_NO_NEON)
endif()
if(MINIAUDIO_NO_RUNTIME_LINKING)
list(APPEND COMPILE_DEFINES MA_NO_RUNTIME_LINKING)
endif()
if(MINIAUDIO_USE_STDINT)
list(APPEND COMPILE_DEFINES MA_USE_STDINT)
endif()
if(MINIAUDIO_DEBUG_OUTPUT)
list(APPEND COMPILE_DEFINES MA_DEBUG_OUTPUT)
endif()
# External Libraries
function(add_libogg_subdirectory)
if(NOT TARGET ogg)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external/ogg/CMakeLists.txt)
message(STATUS "Building libogg from source.")
add_subdirectory(external/ogg)
else()
message(STATUS "libogg not found.")
endif()
endif()
endfunction()
function(add_libvorbis_subdirectory)
if(NOT TARGET vorbis)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external/vorbis/CMakeLists.txt)
add_libogg_subdirectory()
if(TARGET ogg)
message(STATUS "Building libvorbis from source.")
add_subdirectory(external/vorbis)
else()
message(STATUS "libogg not found. miniaudio_libvorbis will be excluded.")
endif()
endif()
endif()
endfunction()
function(add_libopus_subdirectory)
if(NOT TARGET opus)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external/opus/CMakeLists.txt)
message(STATUS "Building libopus from source.")
set(OPUS_BUILD_TESTING OFF)
add_subdirectory(external/opus)
else()
message(STATUS "libopus not found. miniaudio_libopus will be excluded.")
endif()
endif()
endfunction()
function(add_libopusfile_subdirectory)
if(NOT TARGET opusfile)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external/opusfile/CMakeLists.txt)
add_libogg_subdirectory()
if(TARGET ogg)
add_libopus_subdirectory()
if(TARGET opus)
message(STATUS "Building libopusfile from source.")
set(OP_DISABLE_HTTP TRUE)
set(OP_DISABLE_DOCS TRUE)
set(OP_DISABLE_EXAMPLES TRUE)
add_subdirectory(external/opusfile)
else()
message(STATUS "libopus not found. miniaudio_libopus will be excluded.")
endif()
else()
message(STATUS "libogg not found. miniaudio_libopus will be excluded.")
endif()
endif()
endif()
endfunction()
# vorbisfile
#
# The vorbisfile target is required for miniaudio_libvorbis. If the vorbisfile target has already been
# defined we'll just use that. Otherwise we'll try to use pkg-config. If that fails, as a last resort
# we'll allow building it from source from the external/vorbis directory.
if(NOT MINIAUDIO_NO_LIBVORBIS)
if(NOT TARGET vorbisfile)
# Try pkg-config first
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_VORBISFILE vorbisfile)
endif()
if(PC_VORBISFILE_FOUND)
message(STATUS "Found vorbisfile via pkg-config: ${PC_VORBISFILE_LIBRARIES}")
set(HAS_LIBVORBIS TRUE)
else()
# Fallback to building from source.
add_libvorbis_subdirectory()
if(NOT TARGET vorbisfile)
message(STATUS "libvorbisfile not found. miniaudio_libvorbis will be excluded.")
else()
set(HAS_LIBVORBIS TRUE)
endif()
endif()
else()
message(STATUS "libvorbisfile already found.")
set(HAS_LIBVORBIS TRUE)
endif()
endif()
# opusfile
#
# This is the same as vorbisfile above, but for opusfile.
if(NOT MINIAUDIO_NO_LIBOPUS)
if(NOT TARGET opusfile)
# Try pkg-config first
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_OPUSFILE opusfile)
endif()
if(PC_OPUSFILE_FOUND)
message(STATUS "Found opusfile via pkg-config: ${PC_OPUSFILE_LIBRARIES}")
set(HAS_LIBOPUS TRUE)
else()
# Fallback to building from source.
add_libopusfile_subdirectory()
if(NOT TARGET opusfile)
message(STATUS "libopusfile not found. miniaudio_libopus will be excluded.")
else()
set(HAS_LIBOPUS TRUE)
endif()
endif()
else()
message(STATUS "libopusfile already found.")
set(HAS_LIBOPUS TRUE)
endif()
endif()
find_library(SDL2_LIBRARY NAMES SDL2)
if(SDL2_LIBRARY)
message(STATUS "Found SDL2: ${SDL2_LIBRARY}")
set(HAS_SDL2 TRUE)
else()
message(STATUS "SDL2 not found. SDL2 examples will be excluded.")
endif()
# SteamAudio has an annoying SDK setup. In the lib folder there is a folder for each platform. We need to specify the
# platform we're compiling for.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
# Assume 64-bit. Now we need to check if it's for Windows or Linux.
if(WIN32)
set(STEAMAUDIO_ARCH windows-x64)
else()
set(STEAMAUDIO_ARCH linux-x64)
endif()
else()
# Assume 32-bit. Now we need to check if it's for Windows or Linux.
if(WIN32)
set(STEAMAUDIO_ARCH windows-x86)
else()
set(STEAMAUDIO_ARCH linux-x86)
endif()
endif()
# When searching for SteamAudio, we'll support installing it in the external/steamaudio directory.
set(STEAMAUDIO_FIND_LIBRARY_HINTS)
list(APPEND STEAMAUDIO_FIND_LIBRARY_HINTS ${CMAKE_CURRENT_SOURCE_DIR}/external/steamaudio/lib/${STEAMAUDIO_ARCH})
if(WIN32)
else()
list(APPEND STEAMAUDIO_FIND_LIBRARY_HINTS /opt/steamaudio/lib/${STEAMAUDIO_ARCH})
list(APPEND STEAMAUDIO_FIND_LIBRARY_HINTS /usr/local/steamaudio/lib/${STEAMAUDIO_ARCH})
endif()
set(STEAMAUDIO_FIND_HEADER_HINTS)
list(APPEND STEAMAUDIO_FIND_HEADER_HINTS ${CMAKE_CURRENT_SOURCE_DIR}/external/steamaudio/include)
if(WIN32)
else()
list(APPEND STEAMAUDIO_FIND_HEADER_HINTS /opt/steamaudio/include)
list(APPEND STEAMAUDIO_FIND_HEADER_HINTS /usr/local/steamaudio/include)
endif()
find_library(STEAMAUDIO_LIBRARY NAMES phonon HINTS ${STEAMAUDIO_FIND_LIBRARY_HINTS})
if(STEAMAUDIO_LIBRARY)
message(STATUS "Found SteamAudio: ${STEAMAUDIO_LIBRARY}")
find_path(STEAMAUDIO_INCLUDE_DIR
NAMES phonon.h
HINTS ${STEAMAUDIO_FIND_HEADER_HINTS}
)
if(STEAMAUDIO_INCLUDE_DIR)
message(STATUS "Found phonon.h in ${STEAMAUDIO_INCLUDE_DIR}")
set(HAS_STEAMAUDIO TRUE)
else()
message(STATUS "Could not find phonon.h. miniaudio_engine_steamaudio will be excluded.")
endif()
else()
message(STATUS "SteamAudio not found. miniaudio_engine_steamaudio will be excluded.")
endif()
# Link libraries
set(COMMON_LINK_LIBRARIES)
if (UNIX)
if(NOT MINIAUDIO_NO_RUNTIME_LINKING)
# Not all platforms actually use a separate "dl" library, notably NetBSD and OpenBSD.
find_library(LIB_DL NAMES dl)
if(LIB_DL)
list(APPEND COMMON_LINK_LIBRARIES ${LIB_DL}) # For dlopen(), etc. Most compilers will link to this by default, but some may not.
endif()
endif()
find_library(LIB_PTHREAD NAMES pthread)
if(LIB_PTHREAD)
list(APPEND COMMON_LINK_LIBRARIES ${LIB_PTHREAD}) # Some compilers will not link to pthread by default so list it here just in case.
endif()
find_library(LIB_M NAMES m)
if(LIB_M)
list(APPEND COMMON_LINK_LIBRARIES ${LIB_M})
endif()
# If we're compiling for 32-bit ARM we need to link to -latomic.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
find_library(LIB_ATOMIC NAMES atomic)
if(LIB_ATOMIC)
list(APPEND COMMON_LINK_LIBRARIES ${LIB_ATOMIC})
endif()
endif()
endif()
# Static Libraries
add_library(miniaudio
miniaudio.c
miniaudio.h
)
list(APPEND LIBS_TO_INSTALL miniaudio)
install(FILES miniaudio.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio)
target_include_directories(miniaudio PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options (miniaudio PRIVATE ${COMPILE_OPTIONS})
target_compile_definitions(miniaudio PRIVATE ${COMPILE_DEFINES})
add_library(libvorbis_interface INTERFACE)
if(HAS_LIBVORBIS)
if(TARGET vorbisfile)
target_link_libraries(libvorbis_interface INTERFACE vorbisfile)
elseif(PC_VORBISFILE_FOUND)
target_link_libraries (libvorbis_interface INTERFACE ${PC_VORBISFILE_LIBRARIES})
target_include_directories(libvorbis_interface INTERFACE ${PC_VORBISFILE_INCLUDE_DIRS})
target_link_directories (libvorbis_interface INTERFACE ${PC_VORBISFILE_LIBRARY_DIRS})
target_compile_options (libvorbis_interface INTERFACE ${PC_VORBISFILE_CFLAGS_OTHER})
endif()
endif()
if(HAS_LIBVORBIS)
add_library(miniaudio_libvorbis
extras/decoders/libvorbis/miniaudio_libvorbis.c
extras/decoders/libvorbis/miniaudio_libvorbis.h
)
list(APPEND LIBS_TO_INSTALL miniaudio_libvorbis)
install(FILES extras/decoders/libvorbis/miniaudio_libvorbis.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/decoders/libvorbis)
target_compile_options (miniaudio_libvorbis PRIVATE ${COMPILE_OPTIONS})
target_compile_definitions(miniaudio_libvorbis PRIVATE ${COMPILE_DEFINES})
target_link_libraries (miniaudio_libvorbis PRIVATE libvorbis_interface)
endif()
add_library(libopus_interface INTERFACE)
if(HAS_LIBOPUS)
if(TARGET opusfile)
target_link_libraries (libopus_interface INTERFACE opusfile)
elseif(PC_OPUSFILE_FOUND)
target_link_libraries (libopus_interface INTERFACE ${PC_OPUSFILE_LIBRARIES})
target_include_directories(libopus_interface INTERFACE ${PC_OPUSFILE_INCLUDE_DIRS})
target_link_directories (libopus_interface INTERFACE ${PC_OPUSFILE_LIBRARY_DIRS})
target_compile_options (libopus_interface INTERFACE ${PC_OPUSFILE_CFLAGS_OTHER})
endif()
endif()
if(HAS_LIBOPUS)
add_library(miniaudio_libopus
extras/decoders/libopus/miniaudio_libopus.c
extras/decoders/libopus/miniaudio_libopus.h
)
list(APPEND LIBS_TO_INSTALL miniaudio_libopus)
install(FILES extras/decoders/libopus/miniaudio_libopus.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/decoders/libopus)
target_compile_options (miniaudio_libopus PRIVATE ${COMPILE_OPTIONS})
target_compile_definitions(miniaudio_libopus PRIVATE ${COMPILE_DEFINES})
target_link_libraries (miniaudio_libopus PRIVATE libopus_interface)
endif()
if (NOT MINIAUDIO_NO_EXTRA_NODES)
function(add_extra_node name)
add_library(miniaudio_${name}_node
extras/nodes/ma_${name}_node/ma_${name}_node.c
extras/nodes/ma_${name}_node/ma_${name}_node.h
)
set(libs "${LIBS_TO_INSTALL}")
list(APPEND libs miniaudio_${name}_node)
set(LIBS_TO_INSTALL "${libs}" PARENT_SCOPE) # without PARENT_SCOPE, any changes are lost
install(FILES extras/nodes/ma_${name}_node/ma_${name}_node.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/nodes/ma_${name}_node)
target_include_directories(miniaudio_${name}_node PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extras/nodes/ma_${name}_node)
target_compile_options (miniaudio_${name}_node PRIVATE ${COMPILE_OPTIONS})
target_compile_definitions(miniaudio_${name}_node PRIVATE ${COMPILE_DEFINES})
if(MINIAUDIO_BUILD_EXAMPLES)
add_executable(miniaudio_${name}_node_example extras/nodes/ma_${name}_node/ma_${name}_node_example.c)
target_link_libraries(miniaudio_${name}_node_example PRIVATE miniaudio_common_options)
endif()
endfunction()
add_extra_node(channel_combiner)
add_extra_node(channel_separator)
add_extra_node(ltrim)
add_extra_node(reverb)
add_extra_node(vocoder)
endif()
# Interface with common options to simplify the setup of tests and examples. Note that we don't pass
# in COMPILE_DEFINES here because want to allow the tests and examples to define their own defines. If
# we were to use COMPILE_DEFINES here many of the tests and examples would not compile.
add_library(miniaudio_common_options INTERFACE)
target_compile_options(miniaudio_common_options INTERFACE ${COMPILE_OPTIONS})
target_link_libraries (miniaudio_common_options INTERFACE ${COMMON_LINK_LIBRARIES})
function(is_backend_enabled NAME)
if (NOT MINIAUDIO_NO_${NAME} AND (NOT MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS OR MINIAUDIO_ENABLE_${NAME}))
set(${NAME}_ENABLED TRUE PARENT_SCOPE)
else()
set(${NAME}_ENABLED FALSE PARENT_SCOPE)
endif()
endfunction()
set(LINKED_LIBS)
if(MINIAUDIO_NO_RUNTIME_LINKING)
is_backend_enabled(PULSEAUDIO)
if (PULSEAUDIO_ENABLED)
find_package(PulseAudio)
if (PulseAudio_FOUND)
target_link_libraries(miniaudio PRIVATE ${PULSEAUDIO_LIBRARY})
target_include_directories(miniaudio SYSTEM PRIVATE ${PULSEAUDIO_INCLUDE_DIR})
list(APPEND LINKED_LIBS libpulse)
endif()
endif()
is_backend_enabled(ALSA)
if (ALSA_ENABLED)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_ALSA alsa)
endif()
find_library(ALSA_LIBRARY
NAMES asound
HINTS ${PC_ALSA_LIBRARY_DIRS}
)
if (ALSA_LIBRARY)
find_path(ALSA_INCLUDE_DIR
NAMES alsa/asoundlib.h
HINTS ${PC_ALSA_INCLUDE_DIRS}
)
target_link_libraries(miniaudio PRIVATE ${ALSA_LIBRARY})
target_include_directories(miniaudio PRIVATE ${ALSA_INCLUDE_DIR})
list(APPEND LINKED_LIBS alsa)
endif()
endif()
is_backend_enabled(SNDIO)
if (SNDIO_ENABLED)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_SNDIO sndio)
endif()
find_library(SNDIO_LIBRARY
NAMES sndio
HINTS ${PC_SNDIO_LIBRARY_DIRS}
)
if (SNDIO_LIBRARY)
target_link_libraries(miniaudio PRIVATE ${SNDIO_LIBRARY})
list(APPEND LINKED_LIBS sndio)
endif()
endif()
is_backend_enabled(JACK)
if (JACK_ENABLED)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_JACK jack)
endif()
find_library(JACK_LIBRARY
NAMES jack
HINTS ${PC_JACK_LIBRARY_DIRS}
)
if (JACK_LIBRARY)
find_path(JACK_INCLUDE_DIR
NAMES jack/jack.h
HINTS ${PC_JACK_INCLUDE_DIRS}
)
target_link_libraries(miniaudio PRIVATE ${JACK_LIBRARY})
target_include_directories(miniaudio PRIVATE ${JACK_INCLUDE_DIR})
list(APPEND LINKED_LIBS jack)
endif()
endif()
endif()
# Tests
#
# All tests are compiled as a single translation unit. There is no need to add miniaudio as a link library.
if(MINIAUDIO_BUILD_TESTS)
enable_testing()
set(TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests)
function(add_miniaudio_test name source)
add_executable(${name} ${TESTS_DIR}/${source})
target_link_libraries(${name} PRIVATE miniaudio_common_options)
endfunction()
# Disable C++ tests when forcing C89. This is needed because we'll be passing -std=c89 which will cause errors when trying to compile a C++ file.
if(NOT MINIAUDIO_FORCE_C89)
# The debugging test is only used for debugging miniaudio itself. Don't do add_test() for this, and do not include it in in any automated testing.
add_miniaudio_test(miniaudio_debugging debugging/debugging.cpp)
add_miniaudio_test(miniaudio_cpp cpp/cpp.cpp)
add_test(NAME miniaudio_cpp COMMAND miniaudio_cpp --auto) # This is just the deviceio test.
endif()
add_miniaudio_test(miniaudio_deviceio deviceio/deviceio.c)
add_test(NAME miniaudio_deviceio COMMAND miniaudio_deviceio --auto)
add_miniaudio_test(miniaudio_conversion conversion/conversion.c)
add_test(NAME miniaudio_conversion COMMAND miniaudio_conversion)
add_miniaudio_test(miniaudio_filtering filtering/filtering.c)
add_test(NAME miniaudio_filtering COMMAND miniaudio_filtering ${CMAKE_CURRENT_SOURCE_DIR}/data/16-44100-stereo.flac)
add_miniaudio_test(miniaudio_generation generation/generation.c)
add_test(NAME miniaudio_generation COMMAND miniaudio_generation)
endif()
# Examples
#
# Like tests, all examples are compiled as a single translation unit. There is no need to add miniaudio as a link library.
if (MINIAUDIO_BUILD_EXAMPLES)
set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/examples)
function(add_miniaudio_example name source)
add_executable(${name} ${EXAMPLES_DIR}/${source})
target_link_libraries(${name} PRIVATE miniaudio_common_options)
endfunction()
add_miniaudio_example(miniaudio_custom_backend custom_backend.c)
add_miniaudio_example(miniaudio_custom_decoder_engine custom_decoder_engine.c)
if(HAS_LIBVORBIS)
target_link_libraries(miniaudio_custom_decoder_engine PRIVATE libvorbis_interface)
else()
target_compile_definitions(miniaudio_custom_decoder_engine PRIVATE MA_NO_LIBVORBIS)
message(STATUS "miniaudio_libvorbis is disabled. Vorbis support is disabled in miniaudio_custom_decoder_engine.")
endif()
if(HAS_LIBOPUS)
target_link_libraries(miniaudio_custom_decoder_engine PRIVATE libopus_interface)
else()
target_compile_definitions(miniaudio_custom_decoder_engine PRIVATE MA_NO_LIBOPUS)
message(STATUS "miniaudio_libopus is disabled. Opus support is disabled in miniaudio_custom_decoder_engine.")
endif()
add_miniaudio_example(miniaudio_custom_decoder custom_decoder.c)
if(HAS_LIBVORBIS)
target_link_libraries(miniaudio_custom_decoder PRIVATE libvorbis_interface)
else()
target_compile_definitions(miniaudio_custom_decoder PRIVATE MA_NO_LIBVORBIS)
message(STATUS "miniaudio_libvorbis is disabled. Vorbis support is disabled in miniaudio_custom_decoder.")
endif()
if(HAS_LIBOPUS)
target_link_libraries(miniaudio_custom_decoder PRIVATE libopus_interface)
else()
target_compile_definitions(miniaudio_custom_decoder PRIVATE MA_NO_LIBOPUS)
message(STATUS "miniaudio_libopus is disabled. Opus support is disabled in miniaudio_custom_decoder.")
endif()
add_miniaudio_example(miniaudio_data_source_chaining data_source_chaining.c)
add_miniaudio_example(miniaudio_duplex_effect duplex_effect.c)
add_miniaudio_example(miniaudio_engine_advanced engine_advanced.c)
add_miniaudio_example(miniaudio_engine_effects engine_effects.c)
add_miniaudio_example(miniaudio_engine_hello_world engine_hello_world.c)
if(HAS_SDL2)
add_miniaudio_example(miniaudio_engine_sdl engine_sdl.c)
target_link_libraries(miniaudio_engine_sdl PRIVATE ${SDL2_LIBRARY})
else()
message(STATUS "SDL2 could not be found. miniaudio_engine_sdl has been excluded.")
endif()
if(HAS_STEAMAUDIO)
add_miniaudio_example(miniaudio_engine_steamaudio engine_steamaudio.c)
target_include_directories(miniaudio_engine_steamaudio PRIVATE ${STEAMAUDIO_INCLUDE_DIR})
target_link_libraries (miniaudio_engine_steamaudio PRIVATE ${STEAMAUDIO_LIBRARY})
else()
message(STATUS "SteamAudio could not be found. miniaudio_engine_steamaudio has been excluded.")
endif()
add_miniaudio_example(miniaudio_hilo_interop hilo_interop.c)
add_miniaudio_example(miniaudio_node_graph node_graph.c)
add_miniaudio_example(miniaudio_resource_manager_advanced resource_manager_advanced.c)
add_miniaudio_example(miniaudio_resource_manager resource_manager.c)
add_miniaudio_example(miniaudio_simple_capture simple_capture.c)
add_miniaudio_example(miniaudio_simple_duplex simple_duplex.c)
add_miniaudio_example(miniaudio_simple_enumeration simple_enumeration.c)
add_miniaudio_example(miniaudio_simple_loopback simple_loopback.c)
add_miniaudio_example(miniaudio_simple_looping simple_looping.c)
add_miniaudio_example(miniaudio_simple_mixing simple_mixing.c)
add_miniaudio_example(miniaudio_simple_playback_sine simple_playback_sine.c)
add_miniaudio_example(miniaudio_simple_playback simple_playback.c)
add_miniaudio_example(miniaudio_simple_spatialization simple_spatialization.c)
endif()
# Tools
if (MINIAUDIO_BUILD_TOOLS)
set(TOOLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools)
add_executable(madoc ${TOOLS_DIR}/madoc/madoc.c)
endif()
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
set(MINIAUDIO_PC_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
else()
set(MINIAUDIO_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
set(MINIAUDIO_PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
else()
set(MINIAUDIO_PC_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
endif()
string(JOIN ", " MINIAUDIO_PC_REQUIRES_PRIVATE ${LINKED_LIBS})
# Add vorbisfile and opusfile to pkg-config dependencies if found via pkg-config
set(PC_REQUIRES_PRIVATE_LIST)
if(PC_VORBISFILE_FOUND AND HAS_LIBVORBIS)
list(APPEND PC_REQUIRES_PRIVATE_LIST "vorbisfile")
endif()
if(PC_OPUSFILE_FOUND AND HAS_LIBOPUS)
list(APPEND PC_REQUIRES_PRIVATE_LIST "opusfile")
endif()
if(PC_REQUIRES_PRIVATE_LIST)
if(MINIAUDIO_PC_REQUIRES_PRIVATE)
string(APPEND MINIAUDIO_PC_REQUIRES_PRIVATE ", ")
endif()
string(JOIN ", " PC_REQUIRES_STR ${PC_REQUIRES_PRIVATE_LIST})
string(APPEND MINIAUDIO_PC_REQUIRES_PRIVATE "${PC_REQUIRES_STR}")
endif()
list(TRANSFORM COMMON_LINK_LIBRARIES PREPEND "-l")
string(JOIN " " MINIAUDIO_PC_LIBS_PRIVATE ${COMMON_LINK_LIBRARIES})
list(TRANSFORM COMPILE_DEFINES PREPEND "-D")
string(JOIN " " MINIAUDIO_PC_CFLAGS ${COMPILE_DEFINES})
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/miniaudio.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/miniaudio.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
message(STATUS "Library list: ${LIBS_TO_INSTALL}")
install(TARGETS ${LIBS_TO_INSTALL}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
+2 -2
View File
@@ -21,7 +21,7 @@ significant work without talking to me first. If I don't like it, it won't be me
[Discord](https://discord.gg/9vpqbjU) and [Twitter](https://twitter.com/mackron).
Always base your pull request branch on the "dev" branch. The master branch contains the latest release, which
means your pull request may not be including the lastest in-development changes which may result in unnecessary
means your pull request may not be including the latest in-development changes which may result in unnecessary
conflicts.
I need to review your pull requests before merging. If your pull request is non-trivial, try to break it up into
@@ -63,7 +63,7 @@ not contribute to this project.
Predictable Questions
---------------------
### "Would you consider splitting out [some section of code] into it's own file?"
### "Would you consider splitting out [some section of code] into its own file?"
No, the idea is to keep everything in one place. It would be nice in specific cases to split out specific sections
of the code, such as the resampler, for example. However, this will completely violate one of the major goals of the
project - to have a complete audio library contained within a single file.
+1 -1
View File
@@ -29,7 +29,7 @@ For more information, please refer to <http://unlicense.org/>
===============================================================================
ALTERNATIVE 2 - MIT No Attribution
===============================================================================
Copyright 2023 David Reid
Copyright 2025 David Reid
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
+20 -26
View File
@@ -7,8 +7,7 @@
<p align="center">
<a href="https://discord.gg/9vpqbjU"><img src="https://img.shields.io/discord/712952679415939085?label=discord&logo=discord&style=flat-square" alt="discord"></a>
<a href="https://fosstodon.org/@mackron"><img src="https://img.shields.io/mastodon/follow/109293691403797709?color=blue&domain=https%3A%2F%2Ffosstodon.org&label=mastodon&logo=mastodon&style=flat-square" alt="mastodon"></a>
<a href="https://www.reddit.com/r/miniaudio"><img src="https://img.shields.io/reddit/subreddit-subscribers/miniaudio?label=r%2Fminiaudio&logo=reddit&style=flat-square" alt="reddit"></a>
<a href="https://x.com/mackron"><img alt="x" src="https://img.shields.io/twitter/url?url=https%3A%2F%2Fx.com%2Fmackron&style=flat-square&logo=x&label=%40mackron"></a>
</p>
<p align="center">
@@ -17,6 +16,7 @@
<a href="#building">Building</a> -
<a href="#documentation">Documentation</a> -
<a href="#supported-platforms">Supported Platforms</a> -
<a href="#security">Security</a> -
<a href="#license">License</a>
</p>
@@ -33,7 +33,7 @@ Features
- High-level API for sound management, mixing, effects and optional 3D spatialization.
- Flexible node graph system for advanced mixing and effect processing.
- Resource management for loading sound files.
- Decoding, with built-in support for WAV, FLAC and MP3, in addition to being able to plug in custom decoders.
- Decoding, with built-in support for WAV, FLAC, and MP3, in addition to being able to plug in custom decoders.
- Encoding (WAV only).
- Data conversion.
- Resampling, including custom resamplers.
@@ -51,8 +51,7 @@ Examples
This example shows one way to play a sound using the high level API.
```c
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include "miniaudio/miniaudio.h"
#include <stdio.h>
@@ -80,8 +79,7 @@ int main()
This example shows how to decode and play a sound using the low level API.
```c
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include "miniaudio/miniaudio.h"
#include <stdio.h>
@@ -149,38 +147,26 @@ More examples can be found in the [examples](examples) folder or online here: ht
Building
========
Do the following in one source file:
```c
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
```
Then just compile. There's no need to install any dependencies. On Windows and macOS there's no need to link
to anything. On Linux just link to `-lpthread`, `-lm` and `-ldl`. On BSD just link to `-lpthread` and `-lm`.
On iOS you need to compile as Objective-C.
Just compile miniaudio.c like any other source file and include miniaudio.h like a normal header. There's no need
to install any dependencies. On Windows and macOS there's no need to link to anything. On Linux and BSD just link
to `-lpthread` and `-lm`. On iOS you need to compile as Objective-C. Link to `-ldl` if you get errors about
`dlopen()`, etc.
If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, etc. you
need to link with `-latomic`.
If you prefer separate .h and .c files, you can find a split version of miniaudio in the extras/miniaudio_split
folder. From here you can use miniaudio as a traditional .c and .h library - just add miniaudio.c to your source
tree like any other source file and include miniaudio.h like a normal header. If you prefer compiling as a
single translation unit (AKA unity builds), you can just #include the .c file in your main source file:
```c
#include "miniaudio.c"
```
Note that the split version is auto-generated using a tool and is based on the main file in the root directory.
If you want to contribute, please make the change in the main file.
ABI compatibility is not guaranteed between versions so take care if compiling as a DLL/SO. The suggested way
to integrate miniaudio is by adding it directly to your source tree.
You can also use CMake if that's your preference.
Documentation
=============
Online documentation can be found here: https://miniaud.io/docs/
Documentation can also be found at the top of [miniaudio.h](https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h)
which is always the most up-to-date and authoritive source of information on how to use miniaudio. All other
which is always the most up-to-date and authoritative source of information on how to use miniaudio. All other
documentation is generated from this in-code documentation.
@@ -217,6 +203,14 @@ Backends
- Custom
Security
========
I deal with all security related issues publicly and transparently, and it can sometimes take a while before I
get a chance to address it. If this is an issue for you, you need to use another library. The fastest way to get
a bug fixed is to submit a pull request, but if this is impractical for you please post a ticket to the public
GitHub issue tracker.
License
=======
Your choice of either public domain or [MIT No Attribution](https://github.com/aws/mit-0).
+13
View File
@@ -0,0 +1,13 @@
miniaudio_h := <../miniaudio.h>;
miniaudio_c := <../miniaudio.c>;
cleanup :: function(src:string) string
{
return @(src)
["\r\n"] <= "\n" // Normalize line endings to "\n". Needed for very old versions of GCC.
["\t"] <= " " // Tabs to spaces.
;
}
miniaudio_h = cleanup(@(miniaudio_h));
miniaudio_c = cleanup(@(miniaudio_c));
+289
View File
@@ -0,0 +1,289 @@
miniaudio_h := <../miniaudio.h>;
miniaudio_c := <../miniaudio.c>;
dr_wav_h :: <../../dr_libs/dr_wav.h>;
dr_flac_h :: <../../dr_libs/dr_flac.h>;
dr_mp3_h :: <../../dr_libs/dr_mp3.h>;
c89atomic_h :: <../../c89atomic/c89atomic.h>;
c89atomic_c :: <../../c89atomic/c89atomic.c>;
minify :: function(src:string) string
{
return @(src)
["/\*[^*]*\*+(?:[^/*][^*]*\*+)*/"] <= "" // Remove all block comments to keep things clean.
["(?m)^\s*\R"] <= "" // Remove all empty lines to compress it all down.
["[ \t]+(?=(?:\R|$))"] <= "" // Remove trailing whitespace.
;
}
// dr_wav
rename_wav_namespace :: function(src:string) string
{
return @(src)
["\bdrwav"] <= "ma_dr_wav"
["\bDRWAV"] <= "MA_DR_WAV"
["\bdr_wav"] <= "ma_dr_wav"
["\bDR_WAV"] <= "MA_DR_WAV"
["\bg_drwav"] <= "ma_dr_wav_g"
// Some common tokens will be namespaced as "ma_dr_wav" when we really want them to be "ma_".
["\bma_dr_wav_int"] <= "ma_int"
["\bma_dr_wav_uint"] <= "ma_uint"
["\bma_dr_wav_bool"] <= "ma_bool"
["\bma_dr_wav_uintptr"] <= "ma_uintptr"
["\bMA_DR_WAV_TRUE"] <= "MA_TRUE"
["\bMA_DR_WAV_FALSE"] <= "MA_FALSE"
["\bMA_DR_WAV_UINT64_MAX"] <= "MA_UINT64_MAX"
["\bMA_DR_WAV_32BIT"] <= "MA_32BIT"
["\bMA_DR_WAV_64BIT"] <= "MA_64BIT"
["\bMA_DR_WAV_ARM32"] <= "MA_ARM32"
["\bMA_DR_WAV_ARM64"] <= "MA_ARM64"
["\bMA_DR_WAV_X64"] <= "MA_X64"
["\bMA_DR_WAV_X86"] <= "MA_X86"
["\bMA_DR_WAV_ARM"] <= "MA_ARM"
["\bMA_DR_WAV_API"] <= "MA_API"
["\bMA_DR_WAV_PRIVATE"] <= "MA_PRIVATE"
["\bMA_DR_WAV_DLL"] <= "MA_DLL"
["\bMA_DR_WAV_DLL_IMPORT"] <= "MA_DLL_IMPORT"
["\bMA_DR_WAV_DLL_EXPORT"] <= "MA_DLL_EXPORT"
["\bMA_DR_WAV_DLL_PRIVATE"] <= "MA_DLL_PRIVATE"
["\bma_dr_wav_result"] <= "ma_result"
["\bma_dr_wav_allocation_callbacks"] <= "ma_allocation_callbacks"
["\bMA_DR_WAV_INLINE"] <= "MA_INLINE"
["\bMA_DR_WAV_SIZE_MAX"] <= "MA_SIZE_MAX"
["\bma_dr_wav_result_from_errno"] <= "ma_result_from_errno"
["\bma_dr_wav_fopen"] <= "ma_fopen"
["\bma_dr_wav_wfopen"] <= "ma_wfopen"
// Result codes.
["MA_DR_WAV_SUCCESS"] <= "MA_SUCCESS"
["MA_DR_WAV_INVALID_ARGS"] <= "MA_INVALID_ARGS"
["MA_DR_WAV_OUT_OF_MEMORY"] <= "MA_OUT_OF_MEMORY"
["MA_DR_WAV_INVALID_FILE"] <= "MA_INVALID_FILE"
["MA_DR_WAV_AT_END"] <= "MA_AT_END"
["MA_DR_WAV_BAD_SEEK"] <= "MA_BAD_SEEK"
;
}
convert_wav_h :: function(src:string) string
{
stripped := @(src);
stripped["/\* Sized Types \*/\R" : "\R/\* End Sized Types \*/" ] = "";
stripped["/\* Decorations \*/\R" : "\R/\* End Decorations \*/" ] = "";
stripped["/\* Result Codes \*/\R" : "\R/\* End Result Codes \*/" ] = "";
stripped["/\* Allocation Callbacks \*/\R" : "\R/\* End Allocation Callbacks \*/" ] = "";
return minify(rename_wav_namespace(stripped));
}
convert_wav_c :: function(src:string) string
{
stripped := @(src);
stripped["/\* Architecture Detection \*/\R" : "\R/\* End Architecture Detection \*/"] = "";
stripped["/\* Inline \*/\R" : "\R/\* End Inline \*/" ] = "";
stripped["/\* SIZE_MAX \*/\R" : "\R/\* End SIZE_MAX \*/" ] = "";
stripped["/\* Errno \*/\R" : "\R/\* End Errno \*/" ] = "";
stripped["/\* fopen \*/\R" : "\R/\* End fopen \*/" ] = "";
return minify(rename_wav_namespace(stripped));
}
miniaudio_h("/\* dr_wav_h begin \*/\R":"\R/\* dr_wav_h end \*/") = convert_wav_h(@(dr_wav_h["#ifndef dr_wav_h\R":"\R#endif /\* dr_wav_h \*/"]));
miniaudio_h("/\* dr_wav_c begin \*/\R":"\R/\* dr_wav_c end \*/") = convert_wav_c(@(dr_wav_h["#ifndef dr_wav_c\R":"\R#endif /\* dr_wav_c \*/"]));
// dr_flac
rename_flac_namespace :: function(src:string) string
{
return @(src)
["\bdrflac"] <= "ma_dr_flac"
["\bDRFLAC"] <= "MA_DR_FLAC"
["\bdr_flac"] <= "ma_dr_flac"
["\bDR_FLAC"] <= "MA_DR_FLAC"
["\bg_drflac"] <= "ma_dr_flac_g"
// Some common tokens will be namespaced as "ma_dr_flac" when we really want them to be "ma_".
["\bma_dr_flac_int"] <= "ma_int"
["\bma_dr_flac_uint"] <= "ma_uint"
["\bma_dr_flac_bool"] <= "ma_bool"
["\bma_dr_flac_uintptr"] <= "ma_uintptr"
["\bMA_DR_FLAC_TRUE"] <= "MA_TRUE"
["\bMA_DR_FLAC_FALSE"] <= "MA_FALSE"
["\bMA_DR_FLAC_UINT64_MAX"] <= "MA_UINT64_MAX"
["\bMA_DR_FLAC_32BIT"] <= "MA_32BIT"
["\bMA_DR_FLAC_64BIT"] <= "MA_64BIT"
["\bMA_DR_FLAC_ARM32"] <= "MA_ARM32"
["\bMA_DR_FLAC_ARM64"] <= "MA_ARM64"
["\bMA_DR_FLAC_X64"] <= "MA_X64"
["\bMA_DR_FLAC_X86"] <= "MA_X86"
["\bMA_DR_FLAC_ARM"] <= "MA_ARM"
["\bMA_DR_FLAC_API"] <= "MA_API"
["\bMA_DR_FLAC_PRIVATE"] <= "MA_PRIVATE"
["\bMA_DR_FLAC_DLL"] <= "MA_DLL"
["\bMA_DR_FLAC_DLL_IMPORT"] <= "MA_DLL_IMPORT"
["\bMA_DR_FLAC_DLL_EXPORT"] <= "MA_DLL_EXPORT"
["\bMA_DR_FLAC_DLL_PRIVATE"] <= "MA_DLL_PRIVATE"
["\bma_dr_flac_result"] <= "ma_result"
["\bma_dr_flac_allocation_callbacks"] <= "ma_allocation_callbacks"
["\bMA_DR_FLAC_INLINE"] <= "MA_INLINE"
["\bMA_DR_FLAC_SIZE_MAX"] <= "MA_SIZE_MAX"
["\bma_dr_flac_result_from_errno"] <= "ma_result_from_errno"
["\bma_dr_flac_fopen"] <= "ma_fopen"
["\bma_dr_flac_wfopen"] <= "ma_wfopen"
// Result codes.
["MA_DR_FLAC_SUCCESS"] <= "MA_SUCCESS"
["MA_DR_FLAC_ERROR"] <= "MA_ERROR"
["MA_DR_FLAC_AT_END"] <= "MA_AT_END"
["MA_DR_FLAC_CRC_MISMATCH"] <= "MA_CRC_MISMATCH"
;
}
convert_flac_h :: function(src:string) string
{
stripped := @(src);
stripped["/\* Sized Types \*/\R" : "\R/\* End Sized Types \*/" ] = "";
stripped["/\* Architecture Detection \*/\R" : "\R/\* End Architecture Detection \*/"] = "";
stripped["/\* Decorations \*/\R" : "\R/\* End Decorations \*/" ] = "";
stripped["/\* Allocation Callbacks \*/\R" : "\R/\* End Allocation Callbacks \*/" ] = "";
return minify(rename_flac_namespace(stripped));
}
convert_flac_c :: function(src:string) string
{
stripped := @(src);
stripped["/\* Result Codes \*/\R" : "\R/\* End Result Codes \*/" ] = "";
stripped["/\* Inline \*/\R" : "\R/\* End Inline \*/" ] = "";
stripped["/\* SIZE_MAX \*/\R" : "\R/\* End SIZE_MAX \*/" ] = "";
stripped["/\* Errno \*/\R" : "\R/\* End Errno \*/" ] = "";
stripped["/\* fopen \*/\R" : "\R/\* End fopen \*/" ] = "";
return minify(rename_flac_namespace(stripped));
}
miniaudio_h("/\* dr_flac_h begin \*/\R":"\R/\* dr_flac_h end \*/") = convert_flac_h(@(dr_flac_h["#ifndef dr_flac_h\R":"\R#endif /\* dr_flac_h \*/"]));
miniaudio_h("/\* dr_flac_c begin \*/\R":"\R/\* dr_flac_c end \*/") = convert_flac_c(@(dr_flac_h["#ifndef dr_flac_c\R":"\R#endif /\* dr_flac_c \*/"]));
// dr_mp3
rename_mp3_namespace :: function(src:string) string
{
return @(src)
["\bdrmp3"] <= "ma_dr_mp3"
["\bDRMP3"] <= "MA_DR_MP3"
["\bdr_mp3"] <= "ma_dr_mp3"
["\bDR_MP3"] <= "MA_DR_MP3"
["\bg_drmp3"] <= "ma_dr_mp3_g"
// Some common tokens will be namespaced as "ma_dr_mp3" when we really want them to be "ma_".
["\bma_dr_mp3_int"] <= "ma_int"
["\bma_dr_mp3_uint"] <= "ma_uint"
["\bma_dr_mp3_bool"] <= "ma_bool"
["\bma_dr_mp3_uintptr"] <= "ma_uintptr"
["\bMA_DR_MP3_TRUE"] <= "MA_TRUE"
["\bMA_DR_MP3_FALSE"] <= "MA_FALSE"
["\bMA_DR_MP3_UINT64_MAX"] <= "MA_UINT64_MAX"
["\bMA_DR_MP3_32BIT"] <= "MA_32BIT"
["\bMA_DR_MP3_64BIT"] <= "MA_64BIT"
["\bMA_DR_MP3_ARM32"] <= "MA_ARM32"
["\bMA_DR_MP3_ARM64"] <= "MA_ARM64"
["\bMA_DR_MP3_X64"] <= "MA_X64"
["\bMA_DR_MP3_X86"] <= "MA_X86"
["\bMA_DR_MP3_ARM"] <= "MA_ARM"
["\bMA_DR_MP3_API"] <= "MA_API"
["\bMA_DR_MP3_PRIVATE"] <= "MA_PRIVATE"
["\bMA_DR_MP3_DLL"] <= "MA_DLL"
["\bMA_DR_MP3_DLL_IMPORT"] <= "MA_DLL_IMPORT"
["\bMA_DR_MP3_DLL_EXPORT"] <= "MA_DLL_EXPORT"
["\bMA_DR_MP3_DLL_PRIVATE"] <= "MA_DLL_PRIVATE"
["\bma_dr_mp3_result"] <= "ma_result"
["\bma_dr_mp3_allocation_callbacks"] <= "ma_allocation_callbacks"
["\bMA_DR_MP3_INLINE"] <= "MA_INLINE"
["\bMA_DR_MP3_SIZE_MAX"] <= "MA_SIZE_MAX"
["\bma_dr_mp3_result_from_errno"] <= "ma_result_from_errno"
["\bma_dr_mp3_fopen"] <= "ma_fopen"
["\bma_dr_mp3_wfopen"] <= "ma_wfopen"
// Result codes.
["MA_DR_MP3_SUCCESS"] <= "MA_SUCCESS"
;
}
convert_mp3_h :: function(src:string) string
{
stripped := @(src);
stripped["/\* Sized Types \*/\R" : "\R/\* End Sized Types \*/" ] = "";
stripped["/\* Decorations \*/\R" : "\R/\* End Decorations \*/" ] = "";
stripped["/\* Result Codes \*/\R" : "\R/\* End Result Codes \*/" ] = "";
stripped["/\* Inline \*/\R" : "\R/\* End Inline \*/" ] = "";
stripped["/\* Allocation Callbacks \*/\R" : "\R/\* End Allocation Callbacks \*/" ] = "";
return minify(rename_mp3_namespace(stripped));
}
convert_mp3_c :: function(src:string) string
{
stripped := @(src);
stripped["/\* SIZE_MAX \*/\R" : "\R/\* End SIZE_MAX \*/" ] = "";
stripped["/\* Errno \*/\R" : "\R/\* End Errno \*/" ] = "";
stripped["/\* fopen \*/\R" : "\R/\* End fopen \*/" ] = "";
return minify(rename_mp3_namespace(stripped));
}
miniaudio_h("/\* dr_mp3_h begin \*/\R":"\R/\* dr_mp3_h end \*/") = convert_mp3_h(@(dr_mp3_h["#ifndef dr_mp3_h\R":"\R#endif /\* dr_mp3_h \*/"]));
miniaudio_h("/\* dr_mp3_c begin \*/\R":"\R/\* dr_mp3_c end \*/") = convert_mp3_c(@(dr_mp3_h["#ifndef dr_mp3_c\R":"\R#endif /\* dr_mp3_c \*/"]));
// c89atomic
rename_c89atomic_namespace :: function(src:string) string
{
return @(src)
["\bc89atomic"] <= "ma_atomic"
["\bC89ATOMIC"] <= "MA_ATOMIC"
// Some common tokens will be namespaced as "ma_atomic" when we really want them to be "ma_".
["\bma_atomic_int"] <= "ma_int"
["\bma_atomic_uint"] <= "ma_uint"
["\bma_atomic_bool"] <= "ma_bool32"
["\bMA_ATOMIC_32BIT"] <= "MA_32BIT"
["\bMA_ATOMIC_64BIT"] <= "MA_64BIT"
["\bMA_ATOMIC_ARM32"] <= "MA_ARM32"
["\bMA_ATOMIC_ARM64"] <= "MA_ARM64"
["\bMA_ATOMIC_X64"] <= "MA_X64"
["\bMA_ATOMIC_X86"] <= "MA_X86"
["\bMA_ATOMIC_ARM"] <= "MA_ARM"
["\bMA_ATOMIC_INLINE"] <= "MA_INLINE"
// We have an "extern c89atomic_spinlock" in c89atomic.h, but since we're putting this into the implementation section we can just
// drop the extern and not bother importing anything from c89atomic.c.
["\bextern ma_atomic_spinlock"] <= "ma_atomic_spinlock"
;
}
convert_c89atomic_h :: function(src:string) string
{
stripped := @(src);
stripped["/\* Sized Types \*/\R" : "\R/\* End Sized Types \*/" ] = "";
stripped["/\* Architecture Detection \*/\R" : "\R/\* End Architecture Detection \*/"] = "";
stripped["/\* Inline \*/\R" : "\R/\* End Inline \*/" ] = "";
return minify(rename_c89atomic_namespace(stripped));
}
miniaudio_h("/\* c89atomic.h begin \*/\R":"\R/\* c89atomic.h end \*/") = convert_c89atomic_h(@(c89atomic_h["#ifndef c89atomic_h\R":"\R#endif /\* c89atomic_h \*/"]));
// Cleanup. If we don't normalize line endings we'll fail to compile on old versions of GCC.
cleanup :: function(src:string) string
{
return @(src)
["\r\n"] <= "\n" // Normalize line endings to "\n". Needed for very old versions of GCC.
["\t"] <= " " // Tabs to spaces.
;
}
miniaudio_h = cleanup(@(miniaudio_h));
miniaudio_c = cleanup(@(miniaudio_c));
+26
View File
@@ -0,0 +1,26 @@
miniaudio_h :: <../miniaudio.h>;
miniaudio_split_h := <../extras/miniaudio_split/miniaudio.h>;
miniaudio_split_c := <../extras/miniaudio_split/miniaudio.c>;
header := @(miniaudio_h["/\*" : "\*/"]);
footer := @(miniaudio_h["/\*\RThis software" : "\*/"]);
content_h : string;
content_h["$"] = header;
content_h["$"] = "\n";
content_h["$"] = @(miniaudio_h["#ifndef miniaudio_h" : "#endif /\* miniaudio_h \*/"]);
content_h["$"] = "\n\n";
content_h["$"] = footer;
content_h["$"] = "\n";
content_c : string;
content_c["$"] = header;
content_c["$"] = "\n";
content_c["$"] = '#include "miniaudio.h"\n\n';
content_c["$"] = @(miniaudio_h["#ifndef miniaudio_c" : "#endif /\* miniaudio_c \*/"]);
content_c["$"] = "\n\n";
content_c["$"] = footer;
content_c["$"] = "\n";
miniaudio_split_h = content_h;
miniaudio_split_c = content_c;
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
Sounds in this folder are used for testing purposes. They are all in the public domain. Below is a
list of all the places I pulled these sounds from.
---
https://freesound.org/people/josefpres/sounds/788664/
+5 -37
View File
@@ -2,7 +2,7 @@
This example show how a custom backend can be implemented.
This implements a full-featured SDL2 backend. It's intentionally built using the same paradigms as the built-in backends in order to make
it suitable as a solid basis for a custom implementation. The SDL2 backend can be disabled with MA_NO_SDL, exactly like the build-in
it suitable as a solid basis for a custom implementation. The SDL2 backend can be disabled with MA_NO_SDL, exactly like the built-in
backends. It supports both runtime and compile-time linking and respects the MA_NO_RUNTIME_LINKING option. It also works on Emscripten
which requires the `-s USE_SDL=2` option.
@@ -23,8 +23,7 @@ Custom backends are identified with the `ma_backend_custom` backend type. For th
`ma_backend_custom` backend type because otherwise the built-in backends would always get chosen first and none of the code for the custom
backends would actually get hit. By default, the `ma_backend_custom` backend is the lowest priority backend, except for `ma_backend_null`.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
@@ -180,9 +179,6 @@ static ma_result ma_context_enumerate_devices__sdl(ma_context* pContext, ma_enum
ma_bool32 cbResult;
int iDevice;
MA_ASSERT(pContext != NULL);
MA_ASSERT(callback != NULL);
/* Playback */
if (!isTerminated) {
int deviceCount = ((MA_PFN_SDL_GetNumAudioDevices)pContextEx->sdl.SDL_GetNumAudioDevices)(0);
@@ -241,8 +237,6 @@ static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device
const char* pDeviceName;
#endif
MA_ASSERT(pContext != NULL);
if (pDeviceID == NULL) {
if (deviceType == ma_device_type_playback) {
pDeviceInfo->id.custom.i = 0;
@@ -266,7 +260,7 @@ static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device
of the device's _actual_ ideal format.
Note: With Emscripten, it looks like non-zero values need to be specified for desiredSpec. Whatever is specified in
desiredSpec will be used by SDL since it uses it just does it's own format conversion internally. Therefore, from what
desiredSpec will be used by SDL since it uses it just does its own format conversion internally. Therefore, from what
I can tell, there's no real way to know the device's actual format which means I'm just going to fall back to the full
range of channels and sample rates on Emscripten builds.
*/
@@ -322,8 +316,6 @@ void ma_audio_callback_capture__sdl(void* pUserData, ma_uint8* pBuffer, int buff
{
ma_device_ex* pDeviceEx = (ma_device_ex*)pUserData;
MA_ASSERT(pDeviceEx != NULL);
ma_device_handle_backend_data_callback((ma_device*)pDeviceEx, NULL, pBuffer, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceEx->device.capture.internalFormat, pDeviceEx->device.capture.internalChannels));
}
@@ -331,8 +323,6 @@ void ma_audio_callback_playback__sdl(void* pUserData, ma_uint8* pBuffer, int buf
{
ma_device_ex* pDeviceEx = (ma_device_ex*)pUserData;
MA_ASSERT(pDeviceEx != NULL);
ma_device_handle_backend_data_callback((ma_device*)pDeviceEx, pBuffer, NULL, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceEx->device.playback.internalFormat, pDeviceEx->device.playback.internalChannels));
}
@@ -344,9 +334,6 @@ static ma_result ma_device_init_internal__sdl(ma_device_ex* pDeviceEx, const ma_
const char* pDeviceName;
int deviceID;
MA_ASSERT(pDeviceEx != NULL);
MA_ASSERT(pDescriptor != NULL);
/*
SDL is a little bit awkward with specifying the buffer size, You need to specify the size of the buffer in frames, but since we may
have requested a period size in milliseconds we'll need to convert, which depends on the sample rate. But there's a possibility that
@@ -430,8 +417,6 @@ static ma_result ma_device_init__sdl(ma_device* pDevice, const ma_device_config*
ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext;
ma_result result;
MA_ASSERT(pDevice != NULL);
/* SDL does not support loopback mode, so must return MA_DEVICE_TYPE_NOT_SUPPORTED if it's requested. */
if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
@@ -463,8 +448,6 @@ static ma_result ma_device_uninit__sdl(ma_device* pDevice)
ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice;
ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext;
MA_ASSERT(pDevice != NULL);
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(pDeviceEx->sdl.deviceIDCapture);
}
@@ -481,8 +464,6 @@ static ma_result ma_device_start__sdl(ma_device* pDevice)
ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice;
ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext;
MA_ASSERT(pDevice != NULL);
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDCapture, 0);
}
@@ -499,8 +480,6 @@ static ma_result ma_device_stop__sdl(ma_device* pDevice)
ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice;
ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext;
MA_ASSERT(pDevice != NULL);
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDCapture, 1);
}
@@ -516,8 +495,6 @@ static ma_result ma_context_uninit__sdl(ma_context* pContext)
{
ma_context_ex* pContextEx = (ma_context_ex*)pContext;
MA_ASSERT(pContext != NULL);
((MA_PFN_SDL_QuitSubSystem)pContextEx->sdl.SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO);
/* Close the handle to the SDL shared object last. */
@@ -545,8 +522,6 @@ static ma_result ma_context_init__sdl(ma_context* pContext, const ma_context_con
#endif
};
MA_ASSERT(pContext != NULL);
(void)pConfig;
/* Check if we have SDL2 installed somewhere. If not it's not usable and we need to abort. */
@@ -641,15 +616,8 @@ Main program starts here.
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS);
if (pDevice->type == ma_device_type_playback) {
ma_waveform* pSineWave;
pSineWave = (ma_waveform*)pDevice->pUserData;
MA_ASSERT(pSineWave != NULL);
ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL);
ma_waveform_read_pcm_frames((ma_waveform*)pDevice->pUserData, pOutput, frameCount, NULL);
}
if (pDevice->type == ma_device_type_duplex) {
@@ -737,4 +705,4 @@ int main(int argc, char** argv)
(void)argv;
return 0;
}
}
+7 -162
View File
@@ -15,167 +15,12 @@ the decoder via the decoder config (`ma_decoder_config`). You need to implement
of your custom decoders. See `ma_decoding_backend_vtable` for the functions you need to implement.
The `onInitFile`, `onInitFileW` and `onInitMemory` functions are optional.
*/
#define MA_NO_VORBIS /* Disable the built-in Vorbis decoder to ensure the libvorbis decoder is picked. */
#define MA_NO_OPUS /* Disable the (not yet implemented) built-in Opus decoder to ensure the libopus decoder is picked. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../extras/miniaudio_libvorbis.h"
#include "../extras/miniaudio_libopus.h"
#include "../miniaudio.c"
#include "../extras/decoders/libvorbis/miniaudio_libvorbis.c"
#include "../extras/decoders/libopus/miniaudio_libopus.c"
#include <stdio.h>
static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libvorbis* pVorbis;
(void)pUserData;
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libvorbis* pVorbis;
(void)pUserData;
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
(void)pUserData;
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
ma_free(pVorbis, pAllocationCallbacks);
}
static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
(void)pUserData;
return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
}
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
{
ma_decoding_backend_init__libvorbis,
ma_decoding_backend_init_file__libvorbis,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__libvorbis
};
static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libopus* pOpus;
(void)pUserData;
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
if (pOpus == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
if (result != MA_SUCCESS) {
ma_free(pOpus, pAllocationCallbacks);
return result;
}
*ppBackend = pOpus;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libopus* pOpus;
(void)pUserData;
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
if (pOpus == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
if (result != MA_SUCCESS) {
ma_free(pOpus, pAllocationCallbacks);
return result;
}
*ppBackend = pOpus;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libopus* pOpus = (ma_libopus*)pBackend;
(void)pUserData;
ma_libopus_uninit(pOpus, pAllocationCallbacks);
ma_free(pOpus, pAllocationCallbacks);
}
static ma_result ma_decoding_backend_get_channel_map__libopus(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
{
ma_libopus* pOpus = (ma_libopus*)pBackend;
(void)pUserData;
return ma_libopus_get_data_format(pOpus, NULL, NULL, NULL, pChannelMap, channelMapCap);
}
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus =
{
ma_decoding_backend_init__libopus,
ma_decoding_backend_init_file__libopus,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__libopus
};
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
@@ -206,8 +51,8 @@ int main(int argc, char** argv)
*/
ma_decoding_backend_vtable* pCustomBackendVTables[] =
{
&g_ma_decoding_backend_vtable_libvorbis,
&g_ma_decoding_backend_vtable_libopus
ma_decoding_backend_libvorbis,
ma_decoding_backend_libopus
};
@@ -219,7 +64,7 @@ int main(int argc, char** argv)
/* Initialize the decoder. */
decoderConfig = ma_decoder_config_init_default();
decoderConfig.pCustomBackendUserData = NULL; /* In this example our backend objects are contained within a ma_decoder_ex object to avoid a malloc. Our vtables need to know about this. */
decoderConfig.pCustomBackendUserData = NULL; /* None of our decoders require user data, so this can be set to null. */
decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;
decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
@@ -267,4 +112,4 @@ int main(int argc, char** argv)
ma_decoder_uninit(&decoder);
return 0;
}
}
+9 -160
View File
@@ -5,166 +5,12 @@ This is the same as the custom_decoder example, only it's used with the high lev
rather than the low level decoding API. You can use this to add support for Opus to your games, for
example (via libopus).
*/
#define MA_NO_VORBIS /* Disable the built-in Vorbis decoder to ensure the libvorbis decoder is picked. */
#define MA_NO_OPUS /* Disable the (not yet implemented) built-in Opus decoder to ensure the libopus decoder is picked. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../extras/miniaudio_libvorbis.h"
#include "../extras/miniaudio_libopus.h"
#include "../miniaudio.c"
#include "../extras/decoders/libvorbis/miniaudio_libvorbis.c"
#include "../extras/decoders/libopus/miniaudio_libopus.c"
#include <stdio.h>
static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libvorbis* pVorbis;
(void)pUserData;
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libvorbis* pVorbis;
(void)pUserData;
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
(void)pUserData;
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
ma_free(pVorbis, pAllocationCallbacks);
}
static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
(void)pUserData;
return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
}
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
{
ma_decoding_backend_init__libvorbis,
ma_decoding_backend_init_file__libvorbis,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__libvorbis
};
static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libopus* pOpus;
(void)pUserData;
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
if (pOpus == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
if (result != MA_SUCCESS) {
ma_free(pOpus, pAllocationCallbacks);
return result;
}
*ppBackend = pOpus;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libopus* pOpus;
(void)pUserData;
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
if (pOpus == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
if (result != MA_SUCCESS) {
ma_free(pOpus, pAllocationCallbacks);
return result;
}
*ppBackend = pOpus;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libopus* pOpus = (ma_libopus*)pBackend;
(void)pUserData;
ma_libopus_uninit(pOpus, pAllocationCallbacks);
ma_free(pOpus, pAllocationCallbacks);
}
static ma_result ma_decoding_backend_get_channel_map__libopus(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
{
ma_libopus* pOpus = (ma_libopus*)pBackend;
(void)pUserData;
return ma_libopus_get_data_format(pOpus, NULL, NULL, NULL, pChannelMap, channelMapCap);
}
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus =
{
ma_decoding_backend_init__libopus,
ma_decoding_backend_init_file__libopus,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__libopus
};
int main(int argc, char** argv)
{
@@ -180,8 +26,8 @@ int main(int argc, char** argv)
*/
ma_decoding_backend_vtable* pCustomBackendVTables[] =
{
&g_ma_decoding_backend_vtable_libvorbis,
&g_ma_decoding_backend_vtable_libopus
ma_decoding_backend_libvorbis,
ma_decoding_backend_libopus
};
@@ -226,5 +72,8 @@ int main(int argc, char** argv)
printf("Press Enter to quit...");
getchar();
ma_engine_uninit(&engine);
ma_resource_manager_uninit(&resourceManager);
return 0;
}
}
+11 -9
View File
@@ -31,9 +31,7 @@ starting the chain from the start again. It is also seeking the head data source
so that playback starts from the start as expected. You do not need to seek non-head items back to
the start as miniaudio will do that for you internally.
*/
#define MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -49,7 +47,11 @@ ma_decoder* g_pDecoders;
static ma_data_source* next_callback_tail(ma_data_source* pDataSource)
{
MA_ASSERT(g_decoderCount > 0); /* <-- We check for this in main() so should never happen. */
(void)pDataSource; /* Unused. */
if (g_decoderCount > 0) { /* <-- We check for this in main() so should never happen. */
return NULL;
}
/*
This will be fired when the last item in the chain has reached the end. In this example we want
@@ -131,15 +133,15 @@ int main(int argc, char** argv)
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = NULL;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
result = ma_device_init(NULL, &deviceConfig, &device);
if (result != MA_SUCCESS) {
printf("Failed to open playback device.\n");
result = -1;
goto done_decoders;
}
if (ma_device_start(&device) != MA_SUCCESS) {
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
printf("Failed to start playback device.\n");
result = -1;
goto done;
}
@@ -156,4 +158,4 @@ done_decoders:
free(g_pDecoders);
return 0;
}
}
+9 -5
View File
@@ -6,13 +6,12 @@ called `ma_vocoder_node` is used to achieve the effect which can be found in the
the miniaudio repository. The vocoder node uses https://github.com/blastbay/voclib to achieve the
effect.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include "../extras/nodes/ma_vocoder_node/ma_vocoder_node.c"
#include <stdio.h>
#define DEVICE_FORMAT ma_format_f32; /* Must always be f32 for this example because the node graph system only works with this. */
#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */
#define DEVICE_CHANNELS 1 /* For this example, always set to 1. */
static ma_waveform g_sourceData; /* The underlying data source of the source node. */
@@ -24,8 +23,13 @@ static ma_node_graph g_nodeGraph;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->capture.format == pDevice->playback.format);
MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels);
/*
This example assumes the playback and capture sides use the same format and channel count. The
format must be f32.
*/
if (pDevice->capture.format != DEVICE_FORMAT || pDevice->playback.format != DEVICE_FORMAT || pDevice->capture.channels != pDevice->playback.channels) {
return;
}
/*
The node graph system is a pulling style of API. At the lowest level of the chain will be a
+1 -2
View File
@@ -14,8 +14,7 @@ Using a shared resource manager, as we do in this example, is useful for when yo
multiple engines so that you can output to multiple playback devices simultaneoulys. An example
might be a local co-op multiplayer game where each player has their own headphones.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#define MAX_DEVICES 2
#define MAX_SOUNDS 32
+2 -3
View File
@@ -13,8 +13,7 @@ This example is playing only a single sound at a time which means only a single
it being used. If you want to play multiple sounds at the same time, even if they're for the same
sound file, you need multiple `ma_sound` objects.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#define DELAY_IN_SECONDS 0.2f
#define DECAY 0.25f /* Volume falloff for each echo. */
@@ -101,4 +100,4 @@ int main(int argc, char** argv)
ma_engine_uninit(&g_engine);
return 0;
}
}
+1 -2
View File
@@ -3,8 +3,7 @@ This example demonstrates how to initialize an audio engine and play a sound.
This will play the sound specified on the command line.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
+7 -4
View File
@@ -9,11 +9,10 @@ This example will load the sound specified on the command line and rotate it aro
head.
*/
#define MA_NO_DEVICE_IO /* <-- Disables the `ma_device` API. We don't need that in this example since SDL will be doing that part for us. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#define SDL_MAIN_HANDLED
#include <SDL.h> /* Change this to your include location. Might be <SDL2/SDL.h>. */
#include <SDL2/SDL.h> /* Change this to your include location. Might be <SDL.h>. */
#define CHANNELS 2 /* Must be stereo for this example. */
#define SAMPLE_RATE 48000
@@ -23,8 +22,12 @@ static ma_sound g_sound; /* This example will play only a single soun
void data_callback(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
ma_uint32 bufferSizeInFrames;
(void)pUserData;
/* Reading is just a matter of reading straight from the engine. */
ma_uint32 bufferSizeInFrames = (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&g_engine));
bufferSizeInFrames = (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&g_engine));
ma_engine_read_pcm_frames(&g_engine, pBuffer, bufferSizeInFrames, NULL);
}
+36 -20
View File
@@ -8,23 +8,26 @@ By implementing this as a node, it can be plugged into any position within the g
channel count of this node is always stereo.
Steam Audio requires fixed sized processing, the size of which must be specified at initialization
time of the IPLBinauralEffect and IPLHRTF objects. This creates a problem because the node graph
will at times need to break down processing into smaller chunks for it's internal processing. The
node graph internally will read into a temporary buffer which is then mixed into the final output
buffer. This temporary buffer is allocated on the stack and is a fixed size. However, variability
comes into play because the channel count of the node is variable. It's not safe to just blindly
process the effect with the frame count specified in miniaudio's node processing callback. Doing so
results in glitching. To work around this, this example is just setting the update size to a known
value that works (256). If it's set to something too big it'll exceed miniaudio's processing size
used by the node graph. Alternatively you could use some kind of intermediary cache which
accumulates input data until enough is available and then do the processing. Ideally, Steam Audio
would support variable sized updates which would avoid this whole mess entirely.
time of the IPLBinauralEffect and IPLHRTF objects. To ensure miniaudio and Steam Audio are
consistent, you must set the period size in the engine config to be consistent with the frame size
you specify in your IPLAudioSettings object. If for some reason you want the period size of the
engine to be different to that of your Steam Audio configuration, you'll need to implement a sort
of buffering solution to your node.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdint.h> /* Required for uint32_t which is used by STEAMAUDIO_VERSION, and a random use of uint8_t. If there's a Steam Audio maintainer reading this, that needs to be fixed to use IPLuint32 and IPLuint8. */
/* Need to silence some warnings from the Steam Audio headers. */
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlong-long"
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#include <phonon.h> /* Steam Audio */
#include <stdint.h> /* Required for uint32_t which is used by STEAMAUDIO_VERSION. That dependency needs to be removed from Steam Audio - use IPLuint32 or "unsigned int" instead! */
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic pop
#endif
#define FORMAT ma_format_f32 /* Must be floating point. */
#define CHANNELS 2 /* Must be stereo for this example. */
@@ -98,6 +101,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
ma_uint32 totalFramesToProcess = *pFrameCountOut;
ma_uint32 totalFramesProcessed = 0;
MA_ZERO_OBJECT(&binauralParams);
binauralParams.direction.x = pBinauralNode->direction.x;
binauralParams.direction.y = pBinauralNode->direction.y;
binauralParams.direction.z = pBinauralNode->direction.z;
@@ -123,7 +127,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
pBinauralNode->ppBuffersIn[0] = (float*)ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, 1);
} else {
/* Slow path. Need to deinterleave the input data. */
ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), pBinauralNode->ppBuffersIn);
ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), (void**)&pBinauralNode->ppBuffersIn[0]);
}
inputBufferDesc.data = pBinauralNode->ppBuffersIn;
@@ -133,7 +137,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
iplBinauralEffectApply(pBinauralNode->iplEffect, &binauralParams, &inputBufferDesc, &outputBufferDesc);
/* Interleave straight into the output buffer. */
ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, pBinauralNode->ppBuffersOut, ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, (const void**)&pBinauralNode->ppBuffersOut[0], ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
/* Advance. */
totalFramesProcessed += framesToProcessThisIteration;
@@ -284,8 +288,16 @@ int main(int argc, char** argv)
/* The engine needs to be initialized first. */
engineConfig = ma_engine_config_init();
engineConfig.channels = CHANNELS;
engineConfig.sampleRate = SAMPLE_RATE;
engineConfig.channels = CHANNELS;
engineConfig.sampleRate = SAMPLE_RATE;
/*
Steam Audio requires processing in fixed sized chunks. Setting the period size in the engine config will
ensure our updates happen in predicably sized chunks as required by Steam Audio.
Note that the configuration of Steam Audio below (IPLAudioSettings) will use this variable to specify the
update size to ensure it remains consistent.
*/
engineConfig.periodSizeInFrames = 256;
result = ma_engine_init(&engineConfig, &g_engine);
@@ -305,6 +317,9 @@ int main(int argc, char** argv)
be documented. If this is for some kind of buffer management with FFT or something, then this
need not be exposed to the public API. There should be no need for the public API to require a
fixed sized update.
It's important that this be set to the periodSizeInFrames specified in the engine config above.
This ensures updates on both the miniaudio side and the Steam Audio side are consistent.
*/
iplAudioSettings.frameSize = engineConfig.periodSizeInFrames;
@@ -322,7 +337,8 @@ int main(int argc, char** argv)
/* IPLHRTF */
MA_ZERO_OBJECT(&iplHRTFSettings);
iplHRTFSettings.type = IPL_HRTFTYPE_DEFAULT;
iplHRTFSettings.type = IPL_HRTFTYPE_DEFAULT;
iplHRTFSettings.volume = 1;
result = ma_result_from_IPLerror(iplHRTFCreate(iplContext, &iplAudioSettings, &iplHRTFSettings, &iplHRTF));
if (result != MA_SUCCESS) {
@@ -429,4 +445,4 @@ int main(int argc, char** argv)
ma_engine_uninit(&g_engine);
return 0;
}
}
+4 -6
View File
@@ -15,8 +15,7 @@ Instead you would probably want to do a custom data source that handles underrun
the ring buffer and deals with desyncs between capture and playback. In the future this example
may be updated to make use of a more advanced data source that handles all of this.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
static ma_pcm_rb rb;
static ma_device device;
@@ -28,6 +27,8 @@ void capture_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFr
ma_result result;
ma_uint32 framesWritten;
(void)pFramesOut;
/* We need to write to the ring buffer. Need to do this in a loop. */
framesWritten = 0;
while (framesWritten < frameCount) {
@@ -44,7 +45,7 @@ void capture_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFr
}
/* Copy the data from the capture buffer to the ring buffer. */
ma_copy_pcm_frames(pMappedBuffer, ma_offset_pcm_frames_const_ptr_f32(pFramesIn, framesWritten, pDevice->capture.channels), framesToWrite, pDevice->capture.format, pDevice->capture.channels);
ma_copy_pcm_frames(pMappedBuffer, ma_offset_pcm_frames_const_ptr_f32((const float*)pFramesIn, framesWritten, pDevice->capture.channels), framesToWrite, pDevice->capture.format, pDevice->capture.channels);
result = ma_pcm_rb_commit_write(&rb, framesToWrite);
if (result != MA_SUCCESS) {
@@ -125,9 +126,6 @@ int main(int argc, char** argv)
return -1;
}
/* Make sure the sound is set to looping or else it'll stop if the ring buffer runs out of data. */
ma_sound_set_looping(&sound, MA_TRUE);
/* Link the starting of the device and sound together. */
ma_device_start(&device);
ma_sound_start(&sound);
+2 -4
View File
@@ -51,8 +51,7 @@ pass and echo effects so that one of them becomes more obvious than the other.
When you want to read from the graph, you simply call `ma_node_graph_read_pcm_frames()`.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
/* Data Format */
#define FORMAT ma_format_f32 /* Must always be f32. */
@@ -81,8 +80,6 @@ static int g_soundNodeCount;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->playback.channels == CHANNELS);
/*
Hearing the output of the node graph is as easy as reading straight into the output buffer. You just need to
make sure you use a consistent data format or else you'll need to do your own conversion.
@@ -90,6 +87,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL);
(void)pInput; /* Unused. */
(void)pDevice; /* Unused. */
}
int main(int argc, char** argv)
+1 -3
View File
@@ -24,8 +24,7 @@ data from the data source. This means the resource manager will ensure all sound
set, each sound will have their own formats and you'll need to do the necessary data conversion yourself.
*/
#define MA_NO_ENGINE /* We're intentionally not using the ma_engine API here. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
@@ -33,7 +32,6 @@ set, each sound will have their own formats and you'll need to do the necessary
void main_loop__em(void* pUserData)
{
ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
MA_ASSERT(pResourceManager != NULL);
/*
The Emscripten build does not support threading which means we need to process jobs manually. If
+21 -14
View File
@@ -15,8 +15,7 @@ In this example we show how you can create a data source, mix them with other da
threads to manage internally and how to implement your own custom job thread.
*/
#define MA_NO_ENGINE /* We're intentionally not using the ma_engine API here. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
static ma_resource_manager_data_source g_dataSources[16];
static ma_uint32 g_dataSourceCount;
@@ -32,8 +31,6 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
This function is intended to be used when the format and channel count of the data source is
known beforehand. The idea is to avoid overhead due to redundant calls to ma_data_source_get_data_format().
*/
MA_ASSERT(pDataSource != NULL);
if (dataSourceFormat == ma_format_f32) {
/* Fast path. No conversion necessary. */
return ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, pFramesRead);
@@ -43,6 +40,10 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
ma_uint64 totalFramesRead;
ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint64 tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
@@ -52,7 +53,10 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
framesToRead = tempCapInFrames;
}
result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, framesToRead, &framesJustRead);
result = ma_data_source_read_pcm_frames(pDataSource, temp, framesToRead, &framesJustRead);
if (result != MA_SUCCESS) {
break;
}
ma_convert_pcm_frames_format(ma_offset_pcm_frames_ptr_f32(pFramesOut, totalFramesRead, dataSourceChannels), ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
totalFramesRead += framesJustRead;
@@ -62,6 +66,10 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return MA_SUCCESS;
}
}
@@ -112,7 +120,7 @@ MA_API ma_result ma_data_source_read_pcm_frames_and_mix_f32(ma_data_source* pDat
result = ma_data_source_read_pcm_frames_f32_ex(pDataSource, temp, framesToRead, &framesJustRead, format, channels);
ma_mix_pcm_frames_f32(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), temp, framesJustRead, channels, volume);
ma_mix_pcm_frames_f32(ma_offset_pcm_frames_ptr_f32(pFramesOut, totalFramesRead, channels), temp, framesJustRead, channels, volume);
totalFramesRead += framesJustRead;
if (result != MA_SUCCESS) {
@@ -136,10 +144,6 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
*/
ma_uint32 iDataSource;
MA_ASSERT(pDevice->playback.format == ma_format_f32);
(void)pInput; /* Unused. */
/*
If the device was configured with noPreSilencedOutputBuffer then you would need to silence the
buffer here, or make sure the first data source to be mixed is copied rather than mixed.
@@ -150,12 +154,15 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
for (iDataSource = 0; iDataSource < g_dataSourceCount; iDataSource += 1) {
ma_data_source_read_pcm_frames_and_mix_f32(&g_dataSources[iDataSource], (float*)pOutput, frameCount, NULL, /* volume = */1);
}
/* Unused. */
(void)pInput;
(void)pDevice;
}
static ma_thread_result MA_THREADCALL custom_job_thread(void* pUserData)
{
ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
MA_ASSERT(pResourceManager != NULL);
for (;;) {
ma_result result;
@@ -191,8 +198,8 @@ static ma_thread_result MA_THREADCALL custom_job_thread(void* pUserData)
event is received which means the `result != MA_SUCCESS` logic above will catch it. If you do not check the
return value of ma_resource_manager_next_job() you will want to check for MA_RESOURCE_MANAGER_JOB_QUIT like the code below.
*/
if (job.toc.breakup.code == MA_RESOURCE_MANAGER_JOB_QUIT) {
printf("CUSTOM JOB THREAD TERMINATING VIA MA_RESOURCE_MANAGER_JOB_QUIT... ");
if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
printf("CUSTOM JOB THREAD TERMINATING VIA MA_JOB_TYPE_QUIT... ");
break;
}
@@ -269,7 +276,7 @@ int main(int argc, char** argv)
ma_thread_create(&jobThread, ma_thread_priority_default, 0, custom_job_thread, &resourceManager, NULL);
/* Create each data source from the resource manager. Note that the caller is the owner. */
for (iFile = 0; iFile < ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) {
for (iFile = 0; iFile < (int)ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) {
result = ma_resource_manager_data_source_init(
&resourceManager,
argv[iFile+1],
+2 -6
View File
@@ -8,18 +8,14 @@ Capturing works in a very similar way to playback. The only difference is the di
the application sending data to the device, the device will send data to the application. This example just writes the
data received by the microphone straight to a WAV file.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdlib.h>
#include <stdio.h>
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
MA_ASSERT(pEncoder != NULL);
ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
ma_encoder_write_pcm_frames((ma_encoder*)pDevice->pUserData, pInput, frameCount, NULL);
(void)pOutput;
}
+5 -4
View File
@@ -10,8 +10,7 @@ glitching which the backend may not be able to recover from. For this reason, mi
sample rate for both capture and playback. If internally the native sample rates differ, miniaudio will perform the
sample rate conversion for you automatically.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -23,8 +22,10 @@ void main_loop__em()
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->capture.format == pDevice->playback.format);
MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels);
/* This example assumes the playback and capture sides use the same format and channel count. */
if (pDevice->capture.format != pDevice->playback.format || pDevice->capture.channels != pDevice->playback.channels) {
return;
}
/* In this example the format and channel count are the same for both input and output which means we can just memcpy(). */
MA_COPY_MEMORY(pOutput, pInput, frameCount * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
+2 -3
View File
@@ -1,14 +1,13 @@
/*
Demonstrates how to enumerate over devices.
Device enumaration requires a `ma_context` object which is initialized with `ma_context_init()`. Conceptually, the
Device enumeration requires a `ma_context` object which is initialized with `ma_context_init()`. Conceptually, the
context sits above a device. You can have many devices to one context.
If you use device enumeration, you should explicitly specify the same context you used for enumeration in the call to
`ma_device_init()` when you initialize your devices.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
+2 -6
View File
@@ -10,18 +10,14 @@ used indirectly with PulseAudio by choosing the appropriate loopback device afte
To use loopback mode you just need to set the device type to ma_device_type_loopback and set the capture device config
properties. The output buffer in the callback will be null whereas the input buffer will be valid.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdlib.h>
#include <stdio.h>
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
MA_ASSERT(pEncoder != NULL);
ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
ma_encoder_write_pcm_frames((ma_encoder*)pDevice->pUserData, pInput, frameCount, NULL);
(void)pOutput;
}
+1 -2
View File
@@ -5,8 +5,7 @@ This example uses a decoder as the data source. Decoders can be used with the `m
supports looping via the `ma_data_source_read_pcm_frames()` API. To use it, all you need to do is pass a pointer to the
decoder straight into `ma_data_source_read_pcm_frames()` and it will just work.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
+5 -6
View File
@@ -4,15 +4,14 @@ Demonstrates one way to load multiple files and play them all back at the same t
When mixing multiple sounds together, you should not create multiple devices. Instead you should create only a single
device and then mix your sounds together which you can do by simply summing their samples together. The simplest way to
do this is to use floating point samples and use miniaudio's built-in clipper to handling clipping for you. (Clipping
is when sample are clampled to their minimum and maximum range, which for floating point is -1..1.)
is when sample are clamped to their minimum and maximum range, which for floating point is -1..1.)
```
Usage: simple_mixing [input file 0] [input file 1] ... [input file n]
Example: simple_mixing file1.wav file2.flac
```
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -29,7 +28,7 @@ ma_bool32* g_pDecodersAtEnd;
ma_event g_stopEvent; /* <-- Signaled by the audio thread, waited on by the main thread. */
ma_bool32 are_all_decoders_at_end()
ma_bool32 are_all_decoders_at_end(void)
{
ma_uint32 iDecoder;
for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
@@ -87,8 +86,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
float* pOutputF32 = (float*)pOutput;
ma_uint32 iDecoder;
MA_ASSERT(pDevice->playback.format == SAMPLE_FORMAT); /* <-- Important for this example. */
/* This example assumes the device was configured to use ma_format_f32. */
for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
if (!g_pDecodersAtEnd[iDecoder]) {
ma_uint32 framesRead = read_and_mix_pcm_frames_f32(&g_pDecoders[iDecoder], pOutputF32, frameCount);
@@ -107,6 +105,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
}
(void)pInput;
(void)pDevice;
}
int main(int argc, char** argv)
+1 -2
View File
@@ -10,8 +10,7 @@ device and can be used independently of it. This example only plays back a singl
back multiple files by simple loading multiple decoders and mixing them (do not create multiple devices to do this). See
the simple_mixing example for how best to do this.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
+2 -10
View File
@@ -14,8 +14,7 @@ This example works with Emscripten.
*/
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -33,14 +32,7 @@ void main_loop__em()
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_waveform* pSineWave;
MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS);
pSineWave = (ma_waveform*)pDevice->pUserData;
MA_ASSERT(pSineWave != NULL);
ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL);
ma_waveform_read_pcm_frames((ma_waveform*)pDevice->pUserData, pOutput, frameCount, NULL);
(void)pInput; /* Unused. */
}
+85
View File
@@ -0,0 +1,85 @@
/*
Demonstrates how to do basic spatialization via the high level API.
You can position and orientate sounds to create a simple spatialization effect. This example shows
how to do this.
In addition to positioning sounds, there is the concept of a listener. This can also be positioned
and orientated to help with spatialization.
This example only covers the basics to get your started. See the documentation for more detailed
information on the available features.
To use this example, pass in the path of a sound as the first argument. The sound will be
positioned in front of the listener, while the listener rotates on the the spot to create an
orbiting effect. Terminate the program with Ctrl+C.
*/
#include "../miniaudio.c"
#include <stdio.h>
#include <math.h> /* For sinf() and cosf() */
/* Silence warning about unreachable code for MSVC. */
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4702)
#endif
int main(int argc, char** argv)
{
ma_result result;
ma_engine engine;
ma_sound sound;
float listenerAngle = 0;
if (argc < 2) {
printf("No input file.\n");
return -1;
}
result = ma_engine_init(NULL, &engine);
if (result != MA_SUCCESS) {
printf("Failed to initialize engine.\n");
return -1;
}
result = ma_sound_init_from_file(&engine, argv[1], 0, NULL, NULL, &sound);
if (result != MA_SUCCESS) {
printf("Failed to load sound: %s\n", argv[1]);
ma_engine_uninit(&engine);
return -1;
}
/* This sets the position of the sound. miniaudio follows the same coordinate system as OpenGL, where -Z is forward. */
ma_sound_set_position(&sound, 0, 0, -1);
/*
This sets the position of the listener. The second parameter is the listener index. If you have only a single listener, which is
most likely, just use 0. The position defaults to (0,0,0).
*/
ma_engine_listener_set_position(&engine, 0, 0, 0, 0);
/* Sounds are stopped by default. We'll start it once initial parameters have been setup. */
ma_sound_start(&sound);
/* Rotate the listener on the spot to create an orbiting effect. */
for (;;) {
listenerAngle += 0.01f;
ma_engine_listener_set_direction(&engine, 0, (float)sin(listenerAngle), 0, (float)cos(listenerAngle));
ma_sleep(1);
}
/* Won't actually get here, but do this to tear down. */
ma_sound_uninit(&sound);
ma_engine_uninit(&engine);
return 0;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
+10403
View File
File diff suppressed because it is too large Load Diff
+3527
View File
File diff suppressed because it is too large Load Diff
+536
View File
@@ -0,0 +1,536 @@
#ifndef miniaudio_libopus_c
#define miniaudio_libopus_c
#include "miniaudio_libopus.h"
#if !defined(MA_NO_LIBOPUS)
#include <opusfile.h>
#endif
#include <string.h> /* For memset(). */
#include <assert.h>
static ma_result ma_libopus_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_libopus_read_pcm_frames((ma_libopus*)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_libopus_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
return ma_libopus_seek_to_pcm_frame((ma_libopus*)pDataSource, frameIndex);
}
static ma_result ma_libopus_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
return ma_libopus_get_data_format((ma_libopus*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
static ma_result ma_libopus_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
{
return ma_libopus_get_cursor_in_pcm_frames((ma_libopus*)pDataSource, pCursor);
}
static ma_result ma_libopus_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
return ma_libopus_get_length_in_pcm_frames((ma_libopus*)pDataSource, pLength);
}
static ma_data_source_vtable g_ma_libopus_ds_vtable =
{
ma_libopus_ds_read,
ma_libopus_ds_seek,
ma_libopus_ds_get_data_format,
ma_libopus_ds_get_cursor,
ma_libopus_ds_get_length,
NULL, /* onSetLooping */
0 /* flags */
};
#if !defined(MA_NO_LIBOPUS)
static int ma_libopus_of_callback__read(void* pUserData, unsigned char* pBufferOut, int bytesToRead)
{
ma_libopus* pOpus = (ma_libopus*)pUserData;
ma_result result;
size_t bytesRead;
result = pOpus->onRead(pOpus->pReadSeekTellUserData, (void*)pBufferOut, bytesToRead, &bytesRead);
if (result != MA_SUCCESS) {
return -1;
}
return (int)bytesRead;
}
static int ma_libopus_of_callback__seek(void* pUserData, ogg_int64_t offset, int whence)
{
ma_libopus* pOpus = (ma_libopus*)pUserData;
ma_result result;
ma_seek_origin origin;
if (whence == SEEK_SET) {
origin = ma_seek_origin_start;
} else if (whence == SEEK_END) {
origin = ma_seek_origin_end;
} else {
origin = ma_seek_origin_current;
}
result = pOpus->onSeek(pOpus->pReadSeekTellUserData, offset, origin);
if (result != MA_SUCCESS) {
return -1;
}
return 0;
}
static opus_int64 ma_libopus_of_callback__tell(void* pUserData)
{
ma_libopus* pOpus = (ma_libopus*)pUserData;
ma_result result;
ma_int64 cursor;
if (pOpus->onTell == NULL) {
return -1;
}
result = pOpus->onTell(pOpus->pReadSeekTellUserData, &cursor);
if (result != MA_SUCCESS) {
return -1;
}
return cursor;
}
#endif
static ma_result ma_libopus_init_internal(const ma_decoding_backend_config* pConfig, ma_libopus* pOpus)
{
ma_result result;
ma_data_source_config dataSourceConfig;
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
memset(pOpus, 0, sizeof(*pOpus));
pOpus->format = ma_format_f32; /* f32 by default. */
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
pOpus->format = pConfig->preferredFormat;
} else {
/* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_libopus_ds_vtable;
result = ma_data_source_init(&dataSourceConfig, &pOpus->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
return MA_SUCCESS;
}
MA_API ma_result ma_libopus_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_libopus_init_internal(pConfig, pOpus);
if (result != MA_SUCCESS) {
return result;
}
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
pOpus->onRead = onRead;
pOpus->onSeek = onSeek;
pOpus->onTell = onTell;
pOpus->pReadSeekTellUserData = pReadSeekTellUserData;
#if !defined(MA_NO_LIBOPUS)
{
int libopusResult;
OpusFileCallbacks libopusCallbacks;
/* We can now initialize the Opus decoder. This must be done after we've set up the callbacks. */
libopusCallbacks.read = ma_libopus_of_callback__read;
libopusCallbacks.seek = ma_libopus_of_callback__seek;
libopusCallbacks.close = NULL;
libopusCallbacks.tell = ma_libopus_of_callback__tell;
pOpus->of = op_open_callbacks(pOpus, &libopusCallbacks, NULL, 0, &libopusResult);
if (pOpus->of == NULL) {
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. */
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_libopus_init_internal(pConfig, pOpus);
if (result != MA_SUCCESS) {
return result;
}
#if !defined(MA_NO_LIBOPUS)
{
int libopusResult;
pOpus->of = op_open_file(pFilePath, &libopusResult);
if (pOpus->of == NULL) {
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. */
(void)pFilePath;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API void ma_libopus_uninit(ma_libopus* pOpus, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pOpus == NULL) {
return;
}
(void)pAllocationCallbacks;
#if !defined(MA_NO_LIBOPUS)
{
op_free((OggOpusFile*)pOpus->of);
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
}
#endif
ma_data_source_uninit(&pOpus->ds);
}
MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pOpus, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
/* We always use floating point format. */
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
ma_uint64 totalFramesRead;
ma_format format;
ma_uint32 channels;
ma_libopus_get_data_format(pOpus, &format, &channels, NULL, NULL, 0);
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
long libopusResult;
ma_uint64 framesToRead;
ma_uint64 framesRemaining;
framesRemaining = (frameCount - totalFramesRead);
framesToRead = 1024;
if (framesToRead > framesRemaining) {
framesToRead = framesRemaining;
}
if (format == ma_format_f32) {
libopusResult = op_read_float((OggOpusFile*)pOpus->of, (float* )ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)(framesToRead * channels), NULL);
} else {
libopusResult = op_read ((OggOpusFile*)pOpus->of, (opus_int16*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)(framesToRead * channels), NULL);
}
if (libopusResult < 0) {
result = MA_ERROR; /* Error while decoding. */
break;
} else {
totalFramesRead += libopusResult;
if (libopusResult == 0) {
result = MA_AT_END;
break;
}
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
if (result == MA_SUCCESS && totalFramesRead == 0) {
result = MA_AT_END;
}
return result;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
(void)pFramesOut;
(void)frameCount;
(void)pFramesRead;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pOpus, ma_uint64 frameIndex)
{
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
int libopusResult = op_pcm_seek((OggOpusFile*)pOpus->of, (ogg_int64_t)frameIndex);
if (libopusResult != 0) {
if (libopusResult == OP_ENOSEEK) {
return MA_INVALID_OPERATION; /* Not seekable. */
} else if (libopusResult == OP_EINVAL) {
return MA_INVALID_ARGS;
} else {
return MA_ERROR;
}
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
(void)frameIndex;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
memset(pChannelMap, 0, sizeof(*pChannelMap) * channelMapCap);
}
if (pOpus == NULL) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pOpus->format;
}
#if !defined(MA_NO_LIBOPUS)
{
ma_uint32 channels = op_channel_count((OggOpusFile*)pOpus->of, -1);
if (pChannels != NULL) {
*pChannels = channels;
}
if (pSampleRate != NULL) {
*pSampleRate = 48000;
}
if (pChannelMap != NULL) {
ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, channels);
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor)
{
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
ogg_int64_t offset = op_pcm_tell((OggOpusFile*)pOpus->of);
if (offset < 0) {
return MA_INVALID_FILE;
}
*pCursor = (ma_uint64)offset;
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength)
{
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
ogg_int64_t length = op_pcm_total((OggOpusFile*)pOpus->of, -1);
if (length < 0) {
return MA_ERROR;
}
*pLength = (ma_uint64)length;
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
/*
The code below defines the vtable that you'll plug into your `ma_decoder_config` object.
*/
#if !defined(MA_NO_LIBOPUS)
static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libopus* pOpus;
(void)pUserData;
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
if (pOpus == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
if (result != MA_SUCCESS) {
ma_free(pOpus, pAllocationCallbacks);
return result;
}
*ppBackend = pOpus;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libopus* pOpus;
(void)pUserData;
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
if (pOpus == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
if (result != MA_SUCCESS) {
ma_free(pOpus, pAllocationCallbacks);
return result;
}
*ppBackend = pOpus;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libopus* pOpus = (ma_libopus*)pBackend;
(void)pUserData;
ma_libopus_uninit(pOpus, pAllocationCallbacks);
ma_free(pOpus, pAllocationCallbacks);
}
static ma_decoding_backend_vtable ma_gDecodingBackendVTable_libopus =
{
ma_decoding_backend_init__libopus,
ma_decoding_backend_init_file__libopus,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__libopus
};
ma_decoding_backend_vtable* ma_decoding_backend_libopus = &ma_gDecodingBackendVTable_libopus;
#else
ma_decoding_backend_vtable* ma_decoding_backend_libopus = NULL;
#endif
#endif /* miniaudio_libopus_c */
@@ -0,0 +1,43 @@
/*
This implements a data source that decodes Opus streams via libopus + libopusfile
This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom
decoding backend. See the custom_decoder example.
*/
#ifndef miniaudio_libopus_h
#define miniaudio_libopus_h
#ifdef __cplusplus
extern "C" {
#endif
#include "../../../miniaudio.h"
typedef struct
{
ma_data_source_base ds; /* The libopus decoder can be used independently as a data source. */
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void* pReadSeekTellUserData;
ma_format format; /* Will be either f32 or s16. */
/*OggOpusFile**/ void* of; /* Typed as void* so we can avoid a dependency on opusfile in the header section. */
} ma_libopus;
MA_API ma_result ma_libopus_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus);
MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus);
MA_API void ma_libopus_uninit(ma_libopus* pOpus, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pOpus, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pOpus, ma_uint64 frameIndex);
MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor);
MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength);
/* Decoding backend vtable. This is what you'll plug into ma_decoder_config.pBackendVTables. No user data required. */
extern ma_decoding_backend_vtable* ma_decoding_backend_libopus;
#ifdef __cplusplus
}
#endif
#endif /* miniaudio_libopus_h */
@@ -0,0 +1,591 @@
#ifndef miniaudio_libvorbis_c
#define miniaudio_libvorbis_c
#include "miniaudio_libvorbis.h"
#if !defined(MA_NO_LIBVORBIS)
#ifndef OV_EXCLUDE_STATIC_CALLBACKS
#define OV_EXCLUDE_STATIC_CALLBACKS
#endif
#include <vorbis/vorbisfile.h>
#endif
#include <string.h> /* For memset(). */
#include <assert.h>
static ma_result ma_libvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_libvorbis_read_pcm_frames((ma_libvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_libvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
return ma_libvorbis_seek_to_pcm_frame((ma_libvorbis*)pDataSource, frameIndex);
}
static ma_result ma_libvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
return ma_libvorbis_get_data_format((ma_libvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
static ma_result ma_libvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
{
return ma_libvorbis_get_cursor_in_pcm_frames((ma_libvorbis*)pDataSource, pCursor);
}
static ma_result ma_libvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
return ma_libvorbis_get_length_in_pcm_frames((ma_libvorbis*)pDataSource, pLength);
}
static ma_data_source_vtable g_ma_libvorbis_ds_vtable =
{
ma_libvorbis_ds_read,
ma_libvorbis_ds_seek,
ma_libvorbis_ds_get_data_format,
ma_libvorbis_ds_get_cursor,
ma_libvorbis_ds_get_length,
NULL, /* onSetLooping */
0 /* flags */
};
#if !defined(MA_NO_LIBVORBIS)
static size_t ma_libvorbis_vf_callback__read(void* pBufferOut, size_t size, size_t count, void* pUserData)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
ma_result result;
size_t bytesToRead;
size_t bytesRead;
/* For consistency with fread(). If `size` of `count` is 0, return 0 immediately without changing anything. */
if (size == 0 || count == 0) {
return 0;
}
bytesToRead = size * count;
result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
if (result != MA_SUCCESS) {
/* Not entirely sure what to return here. What if an error occurs, but some data was read and bytesRead is > 0? */
return 0;
}
return bytesRead / size;
}
static int ma_libvorbis_vf_callback__seek(void* pUserData, ogg_int64_t offset, int whence)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
ma_result result;
ma_seek_origin origin;
if (whence == SEEK_SET) {
origin = ma_seek_origin_start;
} else if (whence == SEEK_END) {
origin = ma_seek_origin_end;
} else {
origin = ma_seek_origin_current;
}
result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, offset, origin);
if (result != MA_SUCCESS) {
return -1;
}
return 0;
}
static long ma_libvorbis_vf_callback__tell(void* pUserData)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
ma_result result;
ma_int64 cursor;
result = pVorbis->onTell(pVorbis->pReadSeekTellUserData, &cursor);
if (result != MA_SUCCESS) {
return -1;
}
return (long)cursor;
}
#endif
static ma_result ma_libvorbis_init_internal(const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis)
{
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
memset(pVorbis, 0, sizeof(*pVorbis));
pVorbis->format = ma_format_f32; /* f32 by default. */
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
pVorbis->format = pConfig->preferredFormat;
} else {
/* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
}
#if !defined(MA_NO_LIBVORBIS)
{
ma_result result;
ma_data_source_config dataSourceConfig;
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_libvorbis_ds_vtable;
result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
pVorbis->vf = (OggVorbis_File*)ma_malloc(sizeof(OggVorbis_File), pAllocationCallbacks);
if (pVorbis->vf == NULL) {
ma_data_source_uninit(&pVorbis->ds);
return MA_OUT_OF_MEMORY;
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. */
(void)pAllocationCallbacks;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
result = ma_libvorbis_init_internal(pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
return result;
}
pVorbis->onRead = onRead;
pVorbis->onSeek = onSeek;
pVorbis->onTell = onTell;
pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
#if !defined(MA_NO_LIBVORBIS)
{
int libvorbisResult;
ov_callbacks libvorbisCallbacks;
/* We can now initialize the vorbis decoder. This must be done after we've set up the callbacks. */
libvorbisCallbacks.read_func = ma_libvorbis_vf_callback__read;
libvorbisCallbacks.seek_func = ma_libvorbis_vf_callback__seek;
libvorbisCallbacks.close_func = NULL;
libvorbisCallbacks.tell_func = ma_libvorbis_vf_callback__tell;
libvorbisResult = ov_open_callbacks(pVorbis, (OggVorbis_File*)pVorbis->vf, NULL, 0, libvorbisCallbacks);
if (libvorbisResult < 0) {
ma_data_source_uninit(&pVorbis->ds);
ma_free(pVorbis->vf, pAllocationCallbacks);
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. */
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */
result = ma_libvorbis_init_internal(pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
return result;
}
#if !defined(MA_NO_LIBVORBIS)
{
int libvorbisResult;
libvorbisResult = ov_fopen(pFilePath, (OggVorbis_File*)pVorbis->vf);
if (libvorbisResult < 0) {
ma_data_source_uninit(&pVorbis->ds);
ma_free(pVorbis->vf, pAllocationCallbacks);
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. */
(void)pFilePath;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pVorbis == NULL) {
return;
}
(void)pAllocationCallbacks;
#if !defined(MA_NO_LIBVORBIS)
{
ov_clear((OggVorbis_File*)pVorbis->vf);
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
}
#endif
ma_data_source_uninit(&pVorbis->ds);
ma_free(pVorbis->vf, pAllocationCallbacks);
}
MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
/* We always use floating point format. */
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
ma_uint64 totalFramesRead;
ma_format format;
ma_uint32 channels;
ma_libvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
long libvorbisResult;
ma_uint64 framesToRead;
ma_uint64 framesRemaining;
framesRemaining = (frameCount - totalFramesRead);
framesToRead = 1024;
if (framesToRead > framesRemaining) {
framesToRead = framesRemaining;
}
if (format == ma_format_f32) {
float** ppFramesF32;
libvorbisResult = ov_read_float((OggVorbis_File*)pVorbis->vf, &ppFramesF32, (int)framesToRead, NULL);
if (libvorbisResult < 0) {
result = MA_ERROR; /* Error while decoding. */
break;
} else {
/* Frames need to be interleaved. */
ma_interleave_pcm_frames(format, channels, libvorbisResult, (const void**)ppFramesF32, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels));
totalFramesRead += libvorbisResult;
if (libvorbisResult == 0) {
result = MA_AT_END;
break;
}
}
} else {
libvorbisResult = ov_read((OggVorbis_File*)pVorbis->vf, (char*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)(framesToRead * ma_get_bytes_per_frame(format, channels)), 0, 2, 1, NULL);
if (libvorbisResult < 0) {
result = MA_ERROR; /* Error while decoding. */
break;
} else {
/* Conveniently, there's no need to interleaving when using ov_read(). I'm not sure why ov_read_float() is different in that regard... */
totalFramesRead += libvorbisResult / ma_get_bytes_per_frame(format, channels);
if (libvorbisResult == 0) {
result = MA_AT_END;
break;
}
}
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
if (result == MA_SUCCESS && totalFramesRead == 0) {
result = MA_AT_END;
}
return result;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
(void)pFramesOut;
(void)frameCount;
(void)pFramesRead;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex)
{
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
int libvorbisResult = ov_pcm_seek((OggVorbis_File*)pVorbis->vf, (ogg_int64_t)frameIndex);
if (libvorbisResult != 0) {
if (libvorbisResult == OV_ENOSEEK) {
return MA_INVALID_OPERATION; /* Not seekable. */
} else if (libvorbisResult == OV_EINVAL) {
return MA_INVALID_ARGS;
} else {
return MA_ERROR;
}
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
(void)frameIndex;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
memset(pChannelMap, 0, sizeof(*pChannelMap) * channelMapCap);
}
if (pVorbis == NULL) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pVorbis->format;
}
#if !defined(MA_NO_LIBVORBIS)
{
vorbis_info* pInfo = ov_info((OggVorbis_File*)pVorbis->vf, 0);
if (pInfo == NULL) {
return MA_INVALID_OPERATION;
}
if (pChannels != NULL) {
*pChannels = pInfo->channels;
}
if (pSampleRate != NULL) {
*pSampleRate = pInfo->rate;
}
if (pChannelMap != NULL) {
ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pInfo->channels);
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor)
{
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
ogg_int64_t offset = ov_pcm_tell((OggVorbis_File*)pVorbis->vf);
if (offset < 0) {
return MA_INVALID_FILE;
}
*pCursor = (ma_uint64)offset;
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength)
{
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
/*
Will work in the supermajority of cases where a file has a single logical bitstream. Concatenated streams
are much harder to determine the length of since they can have sample rate changes, but they should be
extremely rare outside of unseekable livestreams anyway.
*/
if (ov_streams((OggVorbis_File*)pVorbis->vf) == 1) {
ogg_int64_t length = ov_pcm_total((OggVorbis_File*)pVorbis->vf, 0);
if(length != OV_EINVAL) {
*pLength = (ma_uint64)length;
} else {
/* Unseekable. */
}
} else {
/* Concatenated stream. */
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
assert(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
/*
The code below defines the vtable that you'll plug into your `ma_decoder_config` object.
*/
#if !defined(MA_NO_LIBVORBIS)
static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libvorbis* pVorbis;
(void)pUserData;
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_libvorbis* pVorbis;
(void)pUserData;
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
(void)pUserData;
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
ma_free(pVorbis, pAllocationCallbacks);
}
static ma_decoding_backend_vtable ma_gDecodingBackendVTable_libvorbis =
{
ma_decoding_backend_init__libvorbis,
ma_decoding_backend_init_file__libvorbis,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__libvorbis
};
ma_decoding_backend_vtable* ma_decoding_backend_libvorbis = &ma_gDecodingBackendVTable_libvorbis;
#else
ma_decoding_backend_vtable* ma_decoding_backend_libvorbis = NULL;
#endif
#endif /* miniaudio_libvorbis_c */
@@ -0,0 +1,42 @@
/*
This implements a data source that decodes Vorbis streams via libvorbis + libvorbisfile
This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom
decoding backend. See the custom_decoder example.
*/
#ifndef miniaudio_libvorbis_h
#define miniaudio_libvorbis_h
#ifdef __cplusplus
extern "C" {
#endif
#include "../../../miniaudio.h"
typedef struct
{
ma_data_source_base ds; /* The libvorbis decoder can be used independently as a data source. */
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void* pReadSeekTellUserData;
ma_format format; /* Will be either f32 or s16. */
/*OggVorbis_File**/ void* vf; /* Typed as void* so we can avoid a dependency on opusfile in the header section. */
} ma_libvorbis;
MA_API ma_result ma_libvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis);
MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis);
MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex);
MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor);
MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength);
/* Decoding backend vtable. This is what you'll plug into ma_decoder_config.pBackendVTables. No user data required. */
extern ma_decoding_backend_vtable* ma_decoding_backend_libvorbis;
#ifdef __cplusplus
}
#endif
#endif /* miniaudio_libvorbis_h */
+2
View File
@@ -1,3 +1,5 @@
/* THIS HAS BEEN DEPRECATED! Use the libopus decoder in extras/decoders/libopus instead. */
/*
This implements a data source that decodes Opus streams via libopus + libopusfile
+3 -1
View File
@@ -1,3 +1,5 @@
/* THIS HAS BEEN DEPRECATED! Use the libvorbis decoder in extras/decoders/libvorbis instead. */
/*
This implements a data source that decodes Vorbis streams via libvorbis + libvorbisfile
@@ -326,7 +328,7 @@ MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFram
}
}
} else {
libvorbisResult = ov_read(&pVorbis->vf, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * ma_get_bytes_per_frame(format, channels), 0, 2, 1, NULL);
libvorbisResult = ov_read(&pVorbis->vf, (char*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * ma_get_bytes_per_frame(format, channels), 0, 2, 1, NULL);
if (libvorbisResult < 0) {
result = MA_ERROR; /* Error while decoding. */
break;
File diff suppressed because it is too large Load Diff
+158 -79
View File
@@ -1,6 +1,6 @@
/*
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.11.19 - 2023-11-04
miniaudio - v0.11.23 - 2025-09-11
David Reid - mackron@gmail.com
@@ -20,7 +20,7 @@ extern "C" {
#define MA_VERSION_MAJOR 0
#define MA_VERSION_MINOR 11
#define MA_VERSION_REVISION 19
#define MA_VERSION_REVISION 23
#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
#if defined(_MSC_VER) && !defined(__clang__)
@@ -37,8 +37,7 @@ extern "C" {
#endif
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__)
#define MA_SIZEOF_PTR 8
#else
#define MA_SIZEOF_PTR 4
@@ -102,7 +101,7 @@ typedef void* ma_handle;
typedef void* ma_ptr;
/*
ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting
ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting
between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get
warning C4191 about "type cast between incompatible function types". To work around this I'm going
to use a different data type depending on the compiler.
@@ -128,6 +127,8 @@ typedef ma_uint16 wchar_t;
#define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
#endif
#define MA_UINT64_MAX (((ma_uint64)0xFFFFFFFF << 32) | (ma_uint64)0xFFFFFFFF) /* Weird shifting syntax is for VC6 compatibility. */
/* Platform/backend detection. */
#if defined(_WIN32) || defined(__COSMOPOLITAN__)
@@ -136,29 +137,55 @@ typedef ma_uint16 wchar_t;
#define MA_WIN32_UWP
#elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
#define MA_WIN32_GDK
#elif defined(NXDK)
#define MA_WIN32_NXDK
#else
#define MA_WIN32_DESKTOP
#endif
/* The original Xbox. */
#if defined(NXDK) /* <-- Add other Xbox compiler toolchains here, and then add a toolchain-specific define in case we need to discriminate between them later. */
#define MA_XBOX
#if defined(NXDK)
#define MA_XBOX_NXDK
#endif
#endif
#endif
#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */
#if defined(__MSDOS__) || defined(MSDOS) || defined(_MSDOS) || defined(__DOS__)
#define MA_DOS
/* No threading allowed on DOS. */
#ifndef MA_NO_THREADING
#define MA_NO_THREADING
#endif
/* No runtime linking allowed on DOS. */
#ifndef MA_NO_RUNTIME_LINKING
#define MA_NO_RUNTIME_LINKING
#endif
#endif
#if !defined(MA_WIN32) && !defined(MA_DOS) /* If it's not Win32, assume POSIX. */
#define MA_POSIX
/*
Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented.
You can use this to avoid including pthread.h in the header section. The downside is that it
results in some fixed sized structures being declared for the various types that are used in
miniaudio. The risk here is that these types might be too small for a given platform. This
risk is yours to take and no support will be offered if you enable this option.
*/
#ifndef MA_NO_PTHREAD_IN_HEADER
#include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
typedef pthread_t ma_pthread_t;
typedef pthread_mutex_t ma_pthread_mutex_t;
typedef pthread_cond_t ma_pthread_cond_t;
#else
typedef ma_uintptr ma_pthread_t;
typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;
typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;
#if !defined(MA_NO_THREADING)
/*
Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented.
You can use this to avoid including pthread.h in the header section. The downside is that it
results in some fixed sized structures being declared for the various types that are used in
miniaudio. The risk here is that these types might be too small for a given platform. This
risk is yours to take and no support will be offered if you enable this option.
*/
#ifndef MA_NO_PTHREAD_IN_HEADER
#include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
typedef pthread_t ma_pthread_t;
typedef pthread_mutex_t ma_pthread_mutex_t;
typedef pthread_cond_t ma_pthread_cond_t;
#else
typedef ma_uintptr ma_pthread_t;
typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;
typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;
#endif
#endif
#if defined(__unix__)
@@ -185,8 +212,11 @@ typedef ma_uint16 wchar_t;
#if defined(__PROSPERO__)
#define MA_PROSPERO
#endif
#if defined(__NX__)
#define MA_NX
#if defined(__3DS__)
#define MA_3DS
#endif
#if defined(__SWITCH__) || defined(__NX__)
#define MA_SWITCH
#endif
#if defined(__BEOS__) || defined(__HAIKU__)
#define MA_BEOS
@@ -196,12 +226,13 @@ typedef ma_uint16 wchar_t;
#endif
#endif
#if defined(__has_c_attribute)
#if __has_c_attribute(fallthrough)
#define MA_FALLTHROUGH [[fallthrough]]
#endif
#if !defined(MA_FALLTHROUGH) && defined(__cplusplus) && __cplusplus >= 201703L
#define MA_FALLTHROUGH [[fallthrough]]
#endif
#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__))
#if !defined(MA_FALLTHROUGH) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L
#define MA_FALLTHROUGH [[fallthrough]]
#endif
#if !defined(MA_FALLTHROUGH) && defined(__has_attribute)
#if __has_attribute(fallthrough)
#define MA_FALLTHROUGH __attribute__((fallthrough))
#endif
@@ -238,7 +269,7 @@ typedef ma_uint16 wchar_t;
#define MA_NO_INLINE __attribute__((noinline))
#else
#define MA_INLINE MA_GNUC_INLINE_HINT
#define MA_NO_INLINE __attribute__((noinline))
#define MA_NO_INLINE
#endif
#elif defined(__WATCOMC__)
#define MA_INLINE __inline
@@ -296,7 +327,7 @@ Special wchar_t type to ensure any structures in the public sections that refere
consistent size across all platforms.
On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use
wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all
wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all
platforms.
*/
#if !defined(MA_POSIX) && defined(MA_WIN32)
@@ -322,7 +353,7 @@ MA_LOG_LEVEL_INFO
callback.
MA_LOG_LEVEL_WARNING
Warnings. You should enable this in you development builds and action them when encounted. These
Warnings. You should enable this in you development builds and action them when encountered. These
logs usually indicate a potential problem or misconfiguration, but still allow you to keep
running. This will never be called from within the data callback.
@@ -621,7 +652,7 @@ typedef struct
typedef struct
{
ma_int32 state;
ma_uint32 state;
} ma_lcg;
@@ -1754,7 +1785,7 @@ input frames.
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
/*
Resets the resampler's timer and clears it's internal cache.
Resets the resampler's timer and clears its internal cache.
*/
MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);
@@ -1975,7 +2006,7 @@ MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannel
/*
Copies a channel map.
Both input and output channel map buffers must have a capacity of at at least `channels`.
Both input and output channel map buffers must have a capacity of at least `channels`.
*/
MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
@@ -2114,6 +2145,8 @@ MA_API void ma_data_source_uninit(ma_data_source* pDataSource);
MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */
MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */
MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);
MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */
MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */
MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
@@ -2479,6 +2512,12 @@ MA_API ma_result ma_event_wait(ma_event* pEvent);
Signals the specified auto-reset event.
*/
MA_API ma_result ma_event_signal(ma_event* pEvent);
MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore);
MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore);
MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore);
MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore);
#endif /* MA_NO_THREADING */
@@ -2570,7 +2609,7 @@ Job Queue
/*
Slot Allocator
--------------
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
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 allocate an index that can be used
as the insertion point for an object.
Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
@@ -2832,7 +2871,7 @@ This section contains the APIs for device playback and capture. Here is where yo
************************************************************************************************************************************************************/
#ifndef MA_NO_DEVICE_IO
/* Some backends are only supported on certain platforms. */
#if defined(MA_WIN32)
#if defined(MA_WIN32) && !defined(MA_XBOX)
#define MA_SUPPORT_WASAPI
#if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
@@ -3013,7 +3052,8 @@ typedef enum
ma_device_notification_type_stopped,
ma_device_notification_type_rerouted,
ma_device_notification_type_interruption_began,
ma_device_notification_type_interruption_ended
ma_device_notification_type_interruption_ended,
ma_device_notification_type_unlocked
} ma_device_notification_type;
typedef struct
@@ -3302,6 +3342,8 @@ typedef union
int nullbackend; /* The null backend uses an integer for device IDs. */
} ma_device_id;
MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB);
typedef struct ma_context_config ma_context_config;
typedef struct ma_device_config ma_device_config;
@@ -3389,6 +3431,7 @@ struct ma_device_config
{
const char* pStreamNamePlayback;
const char* pStreamNameCapture;
int channelMap;
} pulse;
struct
{
@@ -3408,6 +3451,7 @@ struct ma_device_config
ma_aaudio_allowed_capture_policy allowedCapturePolicy;
ma_bool32 noAutoStartAfterReroute;
ma_bool32 enableCompatibilityWorkarounds;
ma_bool32 allowSetBufferCapacity;
} aaudio;
};
@@ -3480,7 +3524,7 @@ and on output returns detailed information about the device in `ma_device_info`.
case when the device ID is NULL, in which case information about the default device needs to be retrieved.
Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a
This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a
device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to
the requested format. The conversion between the format requested by the application and the device's native format will be handled
@@ -3501,10 +3545,10 @@ asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should
The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback.
This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback
which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been
@@ -3544,6 +3588,10 @@ struct ma_context_config
void* pUserData;
ma_allocation_callbacks allocationCallbacks;
struct
{
ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */
} dsound;
struct
{
ma_bool32 useVerboseDeviceEnumeration;
} alsa;
@@ -3632,6 +3680,7 @@ struct ma_context
#ifdef MA_SUPPORT_DSOUND
struct
{
ma_handle hWnd; /* Can be null. */
ma_handle hDSoundDLL;
ma_proc DirectSoundCreate;
ma_proc DirectSoundEnumerateA;
@@ -3679,6 +3728,7 @@ struct ma_context
ma_proc snd_pcm_hw_params_set_rate_resample;
ma_proc snd_pcm_hw_params_set_rate;
ma_proc snd_pcm_hw_params_set_rate_near;
ma_proc snd_pcm_hw_params_set_rate_minmax;
ma_proc snd_pcm_hw_params_set_buffer_size_near;
ma_proc snd_pcm_hw_params_set_periods_near;
ma_proc snd_pcm_hw_params_set_access;
@@ -4238,6 +4288,8 @@ struct ma_device
{
/*AAudioStream**/ ma_ptr pStreamPlayback;
/*AAudioStream**/ ma_ptr pStreamCapture;
ma_mutex rerouteLock;
ma_atomic_bool32 isTearingDown;
ma_aaudio_usage usage;
ma_aaudio_content_type contentType;
ma_aaudio_input_preset inputPreset;
@@ -4661,6 +4713,10 @@ Retrieves basic information about every active playback and/or capture device.
This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require
opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this,
but don't call it from within the enumeration callback.
Parameters
----------
@@ -4702,7 +4758,7 @@ The returned pointers will become invalid upon the next call this this function,
See Also
--------
ma_context_get_devices()
ma_context_enumerate_devices()
*/
MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
@@ -4841,7 +4897,7 @@ from a microphone. Whether or not you should send or receive data from the devic
playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
device is done via a callback which is fired by miniaudio at periodic time intervals.
The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames
The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames
or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but
miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple
@@ -4915,7 +4971,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c
performanceProfile
A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
`ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
`ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value.
noPreSilencedOutputBuffer
When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of
@@ -4955,7 +5011,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c
A pointer that will passed to callbacks in pBackendVTable.
resampling.linear.lpfOrder
The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher
The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher
the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
`MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
@@ -5032,6 +5088,9 @@ then be set directly on the structure. Below are the members of the `ma_device_c
pulse.pStreamNameCapture
PulseAudio only. Sets the stream name for capture.
pulse.channelMap
PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF.
coreaudio.allowNominalSampleRateChange
Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate
@@ -5205,7 +5264,7 @@ Unsafe. It is not safe to call this inside any callback.
Remarks
-------
You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage
You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage
your own context.
See the documentation for `ma_context_init()` for information on the different context configuration options.
@@ -5970,7 +6029,7 @@ Utilities
************************************************************************************************************************************************************/
/*
Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate.
*/
MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
@@ -6227,7 +6286,7 @@ struct ma_decoder
void* pInputCache; /* In input format. Can be null if it's not needed. */
ma_uint64 inputCacheCap; /* The capacity of the input cache. */
ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */
ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */
ma_allocation_callbacks allocationCallbacks;
union
{
@@ -6268,7 +6327,7 @@ This is not thread safe without your own synchronization.
MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
/*
Seeks to a PCM frame based on it's absolute index.
Seeks to a PCM frame based on its absolute index.
This is not thread safe without your own synchronization.
*/
@@ -6531,7 +6590,8 @@ typedef enum
MA_RESOURCE_MANAGER_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. */
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */
MA_RESOURCE_MANAGER_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(). */
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */
} ma_resource_manager_data_source_flags;
@@ -6599,8 +6659,8 @@ typedef struct
ma_uint64 rangeEndInPCMFrames;
ma_uint64 loopPointBegInPCMFrames;
ma_uint64 loopPointEndInPCMFrames;
ma_bool32 isLooping;
ma_uint32 flags;
ma_bool32 isLooping; /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */
} ma_resource_manager_data_source_config;
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void);
@@ -6843,6 +6903,16 @@ Node Graph
/* Use this when the bus count is determined by the node instance rather than the vtable. */
#define MA_NODE_BUS_COUNT_UNKNOWN 255
/* For some internal memory management of ma_node_graph. */
typedef struct
{
size_t offset;
size_t sizeInBytes;
unsigned char _data[1];
} ma_stack;
typedef struct ma_node_graph ma_node_graph;
typedef void ma_node;
@@ -6882,7 +6952,7 @@ typedef struct
void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
/*
A callback for retrieving the number of a input frames that are required to output the
A callback for retrieving the number of input frames that are required to output the
specified number of output frames. You would only want to implement this when the node performs
resampling. This is optional, even for nodes that perform resampling, but it does offer a
small reduction in latency as it allows miniaudio to calculate the exact number of input frames
@@ -6967,10 +7037,14 @@ typedef struct ma_node_base ma_node_base;
struct ma_node_base
{
/* These variables are set once at startup. */
ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
const ma_node_vtable* vtable;
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. */
ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
ma_uint32 inputBusCount;
ma_uint32 outputBusCount;
ma_node_input_bus* pInputBuses;
ma_node_output_bus* pOutputBuses;
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. */
ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
/* These variables are read and written only from the audio thread. */
ma_uint16 cachedFrameCountOut;
@@ -6978,13 +7052,9 @@ struct ma_node_base
ma_uint16 consumedFrameCountIn;
/* These variables are read and written between different threads. */
MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
MA_ATOMIC(8, 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. */
MA_ATOMIC(8, 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()`. */
ma_uint32 inputBusCount;
ma_uint32 outputBusCount;
ma_node_input_bus* pInputBuses;
ma_node_output_bus* pOutputBuses;
MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
MA_ATOMIC(8, 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. */
MA_ATOMIC(8, 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()`. */
/* Memory management. */
ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
@@ -7020,7 +7090,8 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);
typedef struct
{
ma_uint32 channels;
ma_uint16 nodeCacheCapInFrames;
ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */
size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */
} ma_node_graph_config;
MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels);
@@ -7031,10 +7102,15 @@ struct ma_node_graph
/* Immutable. */
ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */
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(). */
ma_uint16 nodeCacheCapInFrames;
float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */
ma_uint32 processingCacheFramesRemaining;
ma_uint32 processingSizeInFrames;
/* Read and written by multiple threads. */
MA_ATOMIC(4, ma_bool32) isReading;
/* Modified only by the audio thread. */
ma_stack* pPreMixStack;
};
MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
@@ -7319,6 +7395,7 @@ typedef enum
MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */
MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */
/* ma_sound specific flags. */
MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
@@ -7358,7 +7435,7 @@ MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_e
/* Base node object for both ma_sound and ma_sound_group. */
typedef struct
{
ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */
ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */
ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */
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. */
ma_uint32 volumeSmoothTimeInPCMFrames;
@@ -7418,13 +7495,13 @@ typedef struct
ma_uint64 rangeEndInPCMFrames;
ma_uint64 loopPointBegInPCMFrames;
ma_uint64 loopPointEndInPCMFrames;
ma_bool32 isLooping;
ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */
void* pEndCallbackUserData;
#ifndef MA_NO_RESOURCE_MANAGER
ma_resource_manager_pipeline_notifications initNotifications;
#endif
ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */
ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */
} ma_sound_config;
MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */
@@ -7482,12 +7559,13 @@ typedef struct
ma_log* pLog; /* When set to NULL, will use the context's log. */
ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
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. */
ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */
ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native sample rate of the device. */
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.*/
ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */
ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */
ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */
ma_allocation_callbacks allocationCallbacks;
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(). */
ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */
@@ -7502,12 +7580,12 @@ MA_API ma_engine_config ma_engine_config_init(void);
struct ma_engine
{
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. */
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. */
#if !defined(MA_NO_RESOURCE_MANAGER)
ma_resource_manager* pResourceManager;
#endif
#if !defined(MA_NO_DEVICE_IO)
ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
#endif
ma_log* pLog;
ma_uint32 sampleRate;
@@ -7516,10 +7594,10 @@ struct ma_engine
ma_allocation_callbacks allocationCallbacks;
ma_bool8 ownsResourceManager;
ma_bool8 ownsDevice;
ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */
ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */
MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */
ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */
ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */
ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */
MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */
ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */
ma_uint32 defaultVolumeSmoothTimeInPCMFrames;
ma_mono_expansion_mode monoExpansionMode;
ma_engine_process_proc onProcess;
@@ -7644,11 +7722,12 @@ MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping);
MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound);
MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound);
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(). */
MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor);
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength);
MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor);
MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength);
MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */
MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_uint64* pCursor);
MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_uint64* pLength);
MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* pCursor);
MA_API ma_result ma_sound_get_length_in_seconds(const ma_sound* pSound, float* pLength);
MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData);
MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup);
@@ -7746,7 +7825,7 @@ For more information, please refer to <http://unlicense.org/>
===============================================================================
ALTERNATIVE 2 - MIT No Attribution
===============================================================================
Copyright 2023 David Reid
Copyright 2025 David Reid
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -1,11 +1,15 @@
#ifndef miniaudio_channel_combiner_node_c
#define miniaudio_channel_combiner_node_c
#include "ma_channel_combiner_node.h"
#include <string.h> /* For memset(). */
MA_API ma_channel_combiner_node_config ma_channel_combiner_node_config_init(ma_uint32 channels)
{
ma_channel_combiner_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_channel_combiner_node_init(). */
config.channels = channels;
@@ -43,7 +47,7 @@ MA_API ma_result ma_channel_combiner_node_init(ma_node_graph* pNodeGraph, const
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pCombinerNode);
memset(pCombinerNode, 0, sizeof(*pCombinerNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -74,4 +78,6 @@ MA_API void ma_channel_combiner_node_uninit(ma_channel_combiner_node* pCombinerN
{
/* The base node is always uninitialized first. */
ma_node_uninit(pCombinerNode, pAllocationCallbacks);
}
}
#endif /* miniaudio_channel_combiner_node_c */
@@ -1,6 +1,8 @@
/* Include ma_channel_combiner_node.h after miniaudio.h */
#ifndef ma_channel_combiner_node_h
#define ma_channel_combiner_node_h
#ifndef miniaudio_channel_combiner_node_h
#define miniaudio_channel_combiner_node_h
#include "../../../miniaudio.h"
#ifdef __cplusplus
extern "C" {
@@ -27,4 +29,4 @@ MA_API void ma_channel_combiner_node_uninit(ma_channel_combiner_node* pSeparator
#ifdef __cplusplus
}
#endif
#endif /* ma_reverb_node_h */
#endif /* miniaudio_channel_combiner_node_h */
@@ -1,2 +1,2 @@
/* The channel separtor example also demonstrates how to use the combiner. */
/* The channel separator example also demonstrates how to use the combiner. */
#include "../ma_channel_separator_node/ma_channel_separator_node_example.c"
@@ -1,11 +1,15 @@
#ifndef miniaudio_channel_separator_node_c
#define miniaudio_channel_separator_node_c
#include "ma_channel_separator_node.h"
#include <string.h> /* For memset(). */
MA_API ma_channel_separator_node_config ma_channel_separator_node_config_init(ma_uint32 channels)
{
ma_channel_separator_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_channel_separator_node_init(). */
config.channels = channels;
@@ -43,7 +47,7 @@ MA_API ma_result ma_channel_separator_node_init(ma_node_graph* pNodeGraph, const
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pSeparatorNode);
memset(pSeparatorNode, 0, sizeof(*pSeparatorNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -79,3 +83,5 @@ MA_API void ma_channel_separator_node_uninit(ma_channel_separator_node* pSeparat
/* The base node is always uninitialized first. */
ma_node_uninit(pSeparatorNode, pAllocationCallbacks);
}
#endif /* miniaudio_channel_separator_node_c */
@@ -1,6 +1,8 @@
/* Include ma_channel_separator_node.h after miniaudio.h */
#ifndef ma_channel_separator_node_h
#define ma_channel_separator_node_h
#ifndef miniaudio_channel_separator_node_h
#define miniaudio_channel_separator_node_h
#include "../../../miniaudio.h"
#ifdef __cplusplus
extern "C" {
@@ -26,4 +28,4 @@ MA_API void ma_channel_separator_node_uninit(ma_channel_separator_node* pSeparat
#ifdef __cplusplus
}
#endif
#endif /* ma_reverb_node_h */
#endif /* miniaudio_channel_separator_node_h */
@@ -1,5 +1,4 @@
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include "../../../miniaudio.c"
#include "ma_channel_separator_node.c"
#include "../ma_channel_combiner_node/ma_channel_combiner_node.c"
@@ -146,4 +145,4 @@ done0: ma_device_uninit(&device);
(void)argv;
return 0;
}
}
@@ -1,116 +0,0 @@
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include <stdio.h>
#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */
#define DEVICE_CHANNELS 2
#define DEVICE_SAMPLE_RATE 48000
static ma_audio_buffer_ref g_dataSupply; /* The underlying data source of the source node. */
static ma_data_source_node g_dataSupplyNode; /* The node that will sit at the root level. Will be reading data from g_dataSupply. */
static ma_delay_node g_delayNode; /* The delay node. */
static ma_node_graph g_nodeGraph; /* The main node graph that we'll be feeding data through. */
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->capture.format == pDevice->playback.format && pDevice->capture.format == ma_format_f32);
MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels);
/*
The node graph system is a pulling style of API. At the lowest level of the chain will be a
node acting as a data source for the purpose of delivering the initial audio data. In our case,
the data source is our `pInput` buffer. We need to update the underlying data source so that it
read data from `pInput`.
*/
ma_audio_buffer_ref_set_data(&g_dataSupply, pInput, frameCount);
/* With the source buffer configured we can now read directly from the node graph. */
ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL);
}
int main(int argc, char** argv)
{
ma_result result;
ma_device_config deviceConfig;
ma_device device;
ma_node_graph_config nodeGraphConfig;
ma_delay_node_config delayNodeConfig;
ma_data_source_node_config dataSupplyNodeConfig;
deviceConfig = ma_device_config_init(ma_device_type_duplex);
deviceConfig.capture.pDeviceID = NULL;
deviceConfig.capture.format = DEVICE_FORMAT;
deviceConfig.capture.channels = DEVICE_CHANNELS;
deviceConfig.capture.shareMode = ma_share_mode_shared;
deviceConfig.playback.pDeviceID = NULL;
deviceConfig.playback.format = DEVICE_FORMAT;
deviceConfig.playback.channels = DEVICE_CHANNELS;
deviceConfig.dataCallback = data_callback;
result = ma_device_init(NULL, &deviceConfig, &device);
if (result != MA_SUCCESS) {
return result;
}
/* Node graph. */
nodeGraphConfig = ma_node_graph_config_init(device.capture.channels);
result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph);
if (result != MA_SUCCESS) {
printf("Failed to initialize node graph.");
goto done0;
}
/* Delay. Attached straight to the endpoint. */
delayNodeConfig = ma_delay_node_config_init(device.capture.channels, device.sampleRate, (100 * device.sampleRate) / 1000, 0.5f);
result = ma_delay_node_init(&g_nodeGraph, &delayNodeConfig, NULL, &g_delayNode);
if (result != MA_SUCCESS) {
printf("Failed to initialize delay node.");
goto done1;
}
ma_node_attach_output_bus(&g_delayNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0);
/* Data supply. Attached to input bus 0 of the delay node. */
result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_dataSupply);
if (result != MA_SUCCESS) {
printf("Failed to initialize audio buffer for source.");
goto done2;
}
dataSupplyNodeConfig = ma_data_source_node_config_init(&g_dataSupply);
result = ma_data_source_node_init(&g_nodeGraph, &dataSupplyNodeConfig, NULL, &g_dataSupplyNode);
if (result != MA_SUCCESS) {
printf("Failed to initialize source node.");
goto done2;
}
ma_node_attach_output_bus(&g_dataSupplyNode, 0, &g_delayNode, 0);
ma_device_start(&device);
printf("Press Enter to quit...\n");
getchar();
/* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */
ma_device_stop(&device);
/*done3:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL);
done2: ma_delay_node_uninit(&g_delayNode, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
done0: ma_device_uninit(&device);
(void)argc;
(void)argv;
return 0;
}
+14 -4
View File
@@ -1,11 +1,19 @@
#ifndef miniaudio_ltrim_node_c
#define miniaudio_ltrim_node_c
#include "ma_ltrim_node.h"
#include <string.h> /* For memset(). */
#ifndef ma_min
#define ma_min(a, b) (((a) < (b)) ? (a) : (b))
#endif
MA_API ma_ltrim_node_config ma_ltrim_node_config_init(ma_uint32 channels, float threshold)
{
ma_ltrim_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_ltrim_node_init(). */
config.channels = channels;
config.threshold = threshold;
@@ -59,8 +67,8 @@ static ma_node_vtable g_ma_ltrim_node_vtable =
{
ma_ltrim_node_process_pcm_frames,
NULL,
1, /* 1 input channel. */
1, /* 1 output channel. */
1, /* 1 input bus. */
1, /* 1 output bus. */
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
};
@@ -73,7 +81,7 @@ MA_API ma_result ma_ltrim_node_init(ma_node_graph* pNodeGraph, const ma_ltrim_no
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pTrimNode);
memset(pTrimNode, 0, sizeof(*pTrimNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -100,3 +108,5 @@ MA_API void ma_ltrim_node_uninit(ma_ltrim_node* pTrimNode, const ma_allocation_c
/* The base node is always uninitialized first. */
ma_node_uninit(pTrimNode, pAllocationCallbacks);
}
#endif /* miniaudio_ltrim_node_c */
+5 -3
View File
@@ -1,6 +1,8 @@
/* Include ma_ltrim_node.h after miniaudio.h */
#ifndef ma_ltrim_node_h
#define ma_ltrim_node_h
#ifndef miniaudio_ltrim_node_h
#define miniaudio_ltrim_node_h
#include "../../../miniaudio.h"
#ifdef __cplusplus
extern "C" {
@@ -32,4 +34,4 @@ MA_API void ma_ltrim_node_uninit(ma_ltrim_node* pTrimNode, const ma_allocation_c
#ifdef __cplusplus
}
#endif
#endif /* ma_ltrim_node_h */
#endif /* miniaudio_ltrim_node_h */
@@ -1,5 +1,4 @@
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include "../../../miniaudio.c"
#include "ma_ltrim_node.c"
#include <stdio.h>
@@ -112,4 +111,4 @@ done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
done0: ma_device_uninit(&device);
return 0;
}
}
+10 -4
View File
@@ -1,12 +1,16 @@
#ifndef miniaudio_reverb_node_c
#define miniaudio_reverb_node_c
#define VERBLIB_IMPLEMENTATION
#include "ma_reverb_node.h"
#include <string.h> /* For memset(). */
MA_API ma_reverb_node_config ma_reverb_node_config_init(ma_uint32 channels, ma_uint32 sampleRate)
{
ma_reverb_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_reverb_node_init(). */
config.channels = channels;
config.sampleRate = sampleRate;
@@ -34,8 +38,8 @@ static ma_node_vtable g_ma_reverb_node_vtable =
{
ma_reverb_node_process_pcm_frames,
NULL,
1, /* 1 input channel. */
1, /* 1 output channel. */
1, /* 1 input bus. */
1, /* 1 output bus. */
MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Reverb requires continuous processing to ensure the tail get's processed. */
};
@@ -48,7 +52,7 @@ MA_API ma_result ma_reverb_node_init(ma_node_graph* pNodeGraph, const ma_reverb_
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pReverbNode);
memset(pReverbNode, 0, sizeof(*pReverbNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -76,3 +80,5 @@ MA_API void ma_reverb_node_uninit(ma_reverb_node* pReverbNode, const ma_allocati
/* The base node is always uninitialized first. */
ma_node_uninit(pReverbNode, pAllocationCallbacks);
}
#endif /* miniaudio_reverb_node_c */
+4 -3
View File
@@ -1,7 +1,8 @@
/* Include ma_reverb_node.h after miniaudio.h */
#ifndef ma_reverb_node_h
#define ma_reverb_node_h
#ifndef miniaudio_reverb_node_h
#define miniaudio_reverb_node_h
#include "../../../miniaudio.h"
#include "verblib.h"
#ifdef __cplusplus
@@ -39,4 +40,4 @@ MA_API void ma_reverb_node_uninit(ma_reverb_node* pReverbNode, const ma_allocati
#ifdef __cplusplus
}
#endif
#endif /* ma_reverb_node_h */
#endif /* miniaudio_reverb_node_h */
@@ -1,5 +1,4 @@
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include "../../../miniaudio.c"
#include "ma_reverb_node.c"
#include <stdio.h>
@@ -15,8 +14,13 @@ static ma_node_graph g_nodeGraph; /* The main node graph that we'l
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->capture.format == pDevice->playback.format && pDevice->capture.format == ma_format_f32);
MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels);
/*
This example assumes the playback and capture sides use the same format and channel count. The
format must be f32.
*/
if (pDevice->capture.format != DEVICE_FORMAT || pDevice->playback.format != DEVICE_FORMAT || pDevice->capture.channels != pDevice->playback.channels) {
return;
}
/*
The node graph system is a pulling style of API. At the lowest level of the chain will be a
@@ -115,4 +119,4 @@ done0: ma_device_uninit(&device);
(void)argv;
return 0;
}
}
+16 -6
View File
@@ -248,13 +248,23 @@ extern "C" {
#include <math.h>
#ifdef _MSC_VER
#define VERBLIB_INLINE __forceinline
#define VERBLIB_INLINE __forceinline
#elif defined(__GNUC__)
#if defined(__STRICT_ANSI__)
#define VERBLIB_GNUC_INLINE_HINT __inline__
#else
#define VERBLIB_GNUC_INLINE_HINT inline
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
#define VERBLIB_INLINE VERBLIB_GNUC_INLINE_HINT __attribute__((always_inline))
#else
#define VERBLIB_INLINE VERBLIB_GNUC_INLINE_HINT
#endif
#elif defined(__WATCOMC__)
#define VERBLIB_INLINE __inline
#else
#ifdef __GNUC__
#define VERBLIB_INLINE inline __attribute__((always_inline))
#else
#define VERBLIB_INLINE inline
#endif
#define VERBLIB_INLINE
#endif
#define verblib_max(x, y) (((x) > (y)) ? (x) : (y))
+10 -4
View File
@@ -1,12 +1,16 @@
#ifndef miniaudio_vocoder_node_c
#define miniaudio_vocoder_node_c
#define VOCLIB_IMPLEMENTATION
#include "ma_vocoder_node.h"
#include <string.h> /* For memset(). */
MA_API ma_vocoder_node_config ma_vocoder_node_config_init(ma_uint32 channels, ma_uint32 sampleRate)
{
ma_vocoder_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_vocoder_node_init(). */
config.channels = channels;
config.sampleRate = sampleRate;
@@ -30,8 +34,8 @@ static ma_node_vtable g_ma_vocoder_node_vtable =
{
ma_vocoder_node_process_pcm_frames,
NULL,
2, /* 2 input channels. */
1, /* 1 output channel. */
2, /* 2 input buses. */
1, /* 1 output bus. */
0
};
@@ -46,7 +50,7 @@ MA_API ma_result ma_vocoder_node_init(ma_node_graph* pNodeGraph, const ma_vocode
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pVocoderNode);
memset(pVocoderNode, 0, sizeof(*pVocoderNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -78,3 +82,5 @@ MA_API void ma_vocoder_node_uninit(ma_vocoder_node* pVocoderNode, const ma_alloc
/* The base node must always be initialized first. */
ma_node_uninit(pVocoderNode, pAllocationCallbacks);
}
#endif /* miniaudio_vocoder_node_c */
@@ -1,14 +1,14 @@
/* Include ma_vocoder_node.h after miniaudio.h */
#ifndef ma_vocoder_node_h
#define ma_vocoder_node_h
#ifndef miniaudio_vocoder_node_h
#define miniaudio_vocoder_node_h
#include "../../../miniaudio.h"
#include "voclib.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
The vocoder node has two inputs and one output. Inputs:
@@ -42,4 +42,4 @@ MA_API void ma_vocoder_node_uninit(ma_vocoder_node* pVocoderNode, const ma_alloc
#ifdef __cplusplus
}
#endif
#endif /* ma_vocoder_node_h */
#endif /* miniaudio_vocoder_node_h */
@@ -6,8 +6,7 @@ called `ma_vocoder_node` is used to achieve the effect which can be found in the
the miniaudio repository. The vocoder node uses https://github.com/blastbay/voclib to achieve the
effect.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include "../../../miniaudio.c"
#include "ma_vocoder_node.c"
#include <stdio.h>
@@ -24,8 +23,13 @@ static ma_node_graph g_nodeGraph;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->capture.format == pDevice->playback.format);
MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels);
/*
This example assumes the playback and capture sides use the same format and channel count. The
format must be f32.
*/
if (pDevice->capture.format != DEVICE_FORMAT || pDevice->playback.format != DEVICE_FORMAT || pDevice->capture.channels != pDevice->playback.channels) {
return;
}
/*
The node graph system is a pulling style of API. At the lowest level of the chain will be a
+16 -6
View File
@@ -151,13 +151,23 @@ extern "C" {
#include <assert.h>
#ifdef _MSC_VER
#define VOCLIB_INLINE __forceinline
#define VOCLIB_INLINE __forceinline
#elif defined(__GNUC__)
#if defined(__STRICT_ANSI__)
#define VOCLIB_GNUC_INLINE_HINT __inline__
#else
#define VOCLIB_GNUC_INLINE_HINT inline
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
#define VOCLIB_INLINE VOCLIB_GNUC_INLINE_HINT __attribute__((always_inline))
#else
#define VOCLIB_INLINE VOCLIB_GNUC_INLINE_HINT
#endif
#elif defined(__WATCOMC__)
#define VOCLIB_INLINE __inline
#else
#ifdef __GNUC__
#define VOCLIB_INLINE inline __attribute__((always_inline))
#else
#define VOCLIB_INLINE inline
#endif
#define VOCLIB_INLINE
#endif
/* Filters
+10 -10
View File
@@ -27,7 +27,7 @@ A complete low-level audio solution requires the following:
2) The ability to open and close a connection to a device.
3) The ability to start and stop the device.
4) The ability to write and read audio data to/from the device.
5) The ability to query the device for it's data configuration.
5) The ability to query the device for its data configuration.
6) The ability to notify the application when certain events occur, such as the device being
stopped, or rerouted.
@@ -66,8 +66,8 @@ blocking, it can be useful to know how many frames can be written/read without b
achieved with osaudio_get_avail().
Querying the device's configuration is achieved with osaudio_get_info(). This function will return
a pointer to a osaudio_info_t structure which contains information about the device, most
importantly it's name and data configuration. The name is important for displaying on a UI, and
a pointer to an osaudio_info_t structure which contains information about the device, most
importantly its name and data configuration. The name is important for displaying on a UI, and
the data configuration is important for knowing how to format your audio data. The osaudio_info_t
structure will contain an array of osaudio_config_t structures. This will contain one entry, which
will contain the exact information that was returned in the config structure that was passed to
@@ -131,7 +131,7 @@ is an example for how to do this:
free(info); // The pointer returned by osaudio_enumerate() must be freed with free().
The id structure is just a 256 byte array that uniquely identifies the device. Implementations may
have different representations for device IDs, and A 256 byte array should accomodates all
have different representations for device IDs, and A 256 byte array should accommodates all
device ID representations. Implementations are required to zero-fill unused bytes. The osaudio_id_t
structure can be copied which makes it suitable for serialization and deserialization in situations
where you may want to save the device ID to permanent storage so it can be stored in a config file.
@@ -161,7 +161,7 @@ achieved by setting the format, channels and rate to 0. Below is an example:
In addition to the code above, you can explicitly call `osaudio_get_info()` to retrieve the format
configuration. If you need to know the native configuration before opening the device, you can use
enumeration. The format, channels and rate will be contined in the first item in the configs array.
enumeration. The format, channels and rate will be continued in the first item in the configs array.
The examples above all use playback, but the same applies for capture. The only difference is that
the direction is set to OSAUDIO_INPUT instead of OSAUDIO_OUTPUT.
@@ -268,7 +268,7 @@ typedef int osaudio_result_t;
#define OSAUDIO_XRUN -102 /* An underrun or overrun occurred. Can be returned by osaudio_read() or osaudio_write(). */
#define OSAUDIO_DEVICE_STOPPED -103 /* The device is stopped. Can be returned by osaudio_drain(). It is invalid to call osaudio_drain() on a device that is not running because otherwise it'll get stuck. */
/* Directions. Cannot be combined. Use separate osaudio_t objects for birectional setups. */
/* Directions. Cannot be combined. Use separate osaudio_t objects for bidirectional setups. */
typedef int osaudio_direction_t;
#define OSAUDIO_INPUT 1
#define OSAUDIO_OUTPUT 2
@@ -480,10 +480,10 @@ The code above is equivalent to this:
On output the config will be filled with the actual configuration. The implementation will perform
any necessary data conversion between the requested data configuration and the device's native
configuration. If it cannot, the function will return a OSAUDIO_FORMAT_NOT_SUPPORTED error. In this
case the caller can decide to reinitialize the device to use it's native configuration and do it's
case the caller can decide to reinitialize the device to use its native configuration and do its
own data conversion, or abort if it cannot do so. Use the channel map to determine the ordering of
your channels. Automatic channel map conversion is not performed - that must be done manually by
the caller when transfering data to/from the device.
the caller when transferring data to/from the device.
Close the device with osaudio_close().
@@ -494,7 +494,7 @@ osaudio_result_t osaudio_open(osaudio_t* audio, osaudio_config_t* config);
/*
Closes a connection to a device.
As soon as this function is called, the device should be considered invalid and unsuable. Do not
As soon as this function is called, the device should be considered invalid and unusable. Do not
attempt to use the audio object once this function has been called.
It's invalid to call this while any other function is still running. You can use osaudio_flush() to
@@ -593,7 +593,7 @@ There will be one item in the configs array which will contain the device's curr
the contents of which will match that of the config that was returned by osaudio_open().
Returns NULL on failure. Do not free the returned pointer. It's up to the implementation to manage
the meory of this object.
the memory of this object.
*/
const osaudio_info_t* osaudio_get_info(osaudio_t audio);
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -122,7 +122,7 @@ static osaudio_result_t osaudio_determine_miniaudio_backend(ma_backend* pBackend
/*
To do this we initialize a dummy device. We allow the caller to make use of this device as an optimization. This is
only used by osaudio_enumerate_devices() because that can make use of the context from the dummy device rather than
having to create it's own. pDummyDevice can be null.
having to create its own. pDummyDevice can be null.
*/
if (pDummyDevice == NULL) {
pDummyDevice = &dummyDevice;
@@ -135,7 +135,7 @@ static osaudio_result_t osaudio_determine_miniaudio_backend(ma_backend* pBackend
if (result != MA_SUCCESS || pDummyDevice->pContext->backend == ma_backend_null) {
/* Failed to open a default playback device. Try capture. */
if (result == MA_SUCCESS) {
/* This means we successfully initialize a device, but it's backend is null. It could be that there's no playback devices attached. Try capture. */
/* This means we successfully initialize a device, but its backend is null. It could be that there's no playback devices attached. Try capture. */
ma_device_uninit(pDummyDevice);
}
@@ -607,7 +607,7 @@ osaudio_result_t osaudio_open(osaudio_t* audio, osaudio_config_t* config)
}
/* The device object needs to have a it's local info built. We can get the ID and name from miniaudio. */
/* The device object needs to have its local info built. We can get the ID and name from miniaudio. */
result = osaudio_result_from_miniaudio(ma_device_get_info(&(*audio)->device, (*audio)->device.type, &deviceInfo));
if (result == MA_SUCCESS) {
memcpy((*audio)->info.id.data, &deviceInfo.id, sizeof((*audio)->info.id.data));
@@ -630,7 +630,7 @@ osaudio_result_t osaudio_open(osaudio_t* audio, osaudio_config_t* config)
return result;
}
/* Now we need a semaphore to control access to the ring buffer to to block read/write when necessary. */
/* Now we need a semaphore to control access to the ring buffer to block read/write when necessary. */
result = osaudio_result_from_miniaudio(ma_semaphore_init((config->direction == OSAUDIO_OUTPUT) ? periodCount : 0, &(*audio)->bufferSemaphore));
if (result != OSAUDIO_SUCCESS) {
ma_pcm_rb_uninit(&(*audio)->buffer);
+283
View File
@@ -0,0 +1,283 @@
#include "../osaudio.h"
#include "../../decoders/litewav/litewav.c"
#include <stdio.h>
#include <stdlib.h> /* free() */
#if defined(__MSDOS__) || defined(__DOS__)
#include <dos.h>
#define OSAUDIO_DOS
#endif
const char* format_to_string(osaudio_format_t format)
{
switch (format)
{
case OSAUDIO_FORMAT_F32: return "F32";
case OSAUDIO_FORMAT_U8: return "U8";
case OSAUDIO_FORMAT_S16: return "S16";
case OSAUDIO_FORMAT_S24: return "S24";
case OSAUDIO_FORMAT_S32: return "S32";
default: return "Unknown Format";
}
}
void enumerate_devices()
{
osaudio_result_t result;
osaudio_info_t* pDeviceInfos;
unsigned int deviceCount;
unsigned int iDevice;
result = osaudio_enumerate(&deviceCount, &pDeviceInfos);
if (result != OSAUDIO_SUCCESS) {
printf("Failed to enumerate devices.");
return;
}
for (iDevice = 0; iDevice < deviceCount; iDevice += 1) {
osaudio_info_t* pDeviceInfo = &pDeviceInfos[iDevice];
printf("Device %u: [%s] %s\n", iDevice, (pDeviceInfo->direction == OSAUDIO_OUTPUT) ? "Playback" : "Capture", pDeviceInfo->name);
#if 0
{
unsigned int iFormat;
printf(" Native Formats\n");
for (iFormat = 0; iFormat < pDeviceInfo->config_count; iFormat += 1) {
osaudio_config_t* pConfig = &pDeviceInfo->configs[iFormat];
printf(" %s %uHz %u channels\n", format_to_string(pConfig->format), pConfig->rate, pConfig->channels);
}
}
#endif
}
free(pDeviceInfos);
}
extern int g_TESTING;
#include <string.h>
/* Sine wave generation. */
#include <math.h>
#if defined(OSAUDIO_DOS)
/* For farmalloc(). */
static void OSAUDIO_FAR* far_malloc(unsigned int sz)
{
unsigned int segment;
unsigned int err;
err = _dos_allocmem(sz >> 4, &segment);
if (err == 0) {
return MK_FP(segment, 0);
} else {
return NULL;
}
}
#else
#define far_malloc malloc
#endif
static char OSAUDIO_FAR* gen_sine_u8(unsigned long frameCount, unsigned int channels, unsigned int sampleRate)
{
float phase = 0;
float phaseIncrement = 2 * 3.14159265f * 220.0f / 44100.0f;
unsigned long iFrame;
char OSAUDIO_FAR* pData;
char OSAUDIO_FAR* pRunningData;
pData = (char OSAUDIO_FAR*)far_malloc(frameCount * channels);
if (pData == NULL) {
return NULL;
}
pRunningData = pData;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
unsigned int iChannel;
float sample = (float)sin(phase) * 0.2f;
sample = (sample + 1.0f) * 127.5f;
for (iChannel = 0; iChannel < channels; iChannel += 1) {
pRunningData[iChannel] = (unsigned char)sample;
}
pRunningData += channels;
phase += phaseIncrement;
}
return pData;
}
static short OSAUDIO_FAR* gen_sine_s16(unsigned long frameCount, unsigned int channels, unsigned int sampleRate)
{
float phase = 0;
float phaseIncrement = 2 * 3.14159265f * 220.0f / 44100.0f;
unsigned long iFrame;
short OSAUDIO_FAR* pData;
short OSAUDIO_FAR* pRunningData;
pData = (short OSAUDIO_FAR*)far_malloc(frameCount * channels * sizeof(short));
if (pData == NULL) {
return NULL;
}
pRunningData = pData;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
unsigned int iChannel;
float sample = (float)sin(phase) * 0.2f;
sample = sample * 32767.5f;
for (iChannel = 0; iChannel < channels; iChannel += 1) {
pRunningData[iChannel] = (short)sample;
}
pRunningData += channels;
phase += phaseIncrement;
}
return pData;
}
//
//
//float sinePhase = 0;
//float sinePhaseIncrement = 0;
//float sineVolume = 0.2f;
//
//static void sine_init()
//{
// sinePhase = 0;
// sinePhaseIncrement = 2 * 3.14159265f * 440.0f / 44100.0f;
//}
//
//static void sine_u8(unsigned char* dst, unsigned int frameCount, unsigned int channels)
//{
// unsigned int iFrame;
//
// for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
// unsigned int iChannel;
// float sample = (float)sin(sinePhase) * sineVolume;
// sample = (sample + 1.0f) * 127.5f;
//
// for (iChannel = 0; iChannel < channels; iChannel += 1) {
// dst[iChannel] = (unsigned char)sample;
// }
//
// dst += channels;
// sinePhase += sinePhaseIncrement;
// }
//}
//
//
//unsigned char data[4096];
int main(int argc, char** argv)
{
osaudio_result_t result;
osaudio_t audio;
osaudio_config_t config;
void OSAUDIO_FAR* pSineWave;
unsigned long sineWaveFrameCount;
unsigned long sineWaveCursor = 0;
enumerate_devices();
osaudio_config_init(&config, OSAUDIO_OUTPUT);
config.format = OSAUDIO_FORMAT_S16;
config.channels = 2;
config.rate = 44100;
result = osaudio_open(&audio, &config);
if (result != OSAUDIO_SUCCESS) {
printf("Failed to initialize audio.\n");
return -1;
}
printf("Device: %s (%s %uHz %u channels)\n", osaudio_get_info(audio)->name, format_to_string(config.format), config.rate, config.channels);
//printf("sizeof(void*) = %u\n", (unsigned int)sizeof(void far *));
/* 5 seconds. */
sineWaveFrameCount = config.rate * 1;
if (config.format == OSAUDIO_FORMAT_U8) {
pSineWave = gen_sine_u8(sineWaveFrameCount, config.channels, config.rate);
} else {
pSineWave = gen_sine_s16(sineWaveFrameCount, config.channels, config.rate);
}
if (pSineWave == NULL) {
printf("Failed to generate sine wave.\n");
return -1;
}
if (config.format == OSAUDIO_FORMAT_U8) {
/*unsigned int framesToSilence = config.rate;
while (framesToSilence > 0) {
unsigned int framesToWrite;
char silence[256];
memset(silence, 128, sizeof(silence));
framesToWrite = framesToSilence;
if (framesToWrite > sizeof(silence) / config.channels) {
framesToWrite = sizeof(silence) / config.channels;
}
osaudio_write(audio, silence, framesToWrite);
framesToSilence -= framesToWrite;
}*/
while (sineWaveCursor < sineWaveFrameCount) {
unsigned long framesToWrite = sineWaveFrameCount - sineWaveCursor;
if (framesToWrite > 0xFFFF) {
framesToWrite = 0xFFFF;
}
//printf("Writing sine wave: %u\n", (unsigned int)framesToWrite);
osaudio_write(audio, (char OSAUDIO_FAR*)pSineWave + (sineWaveCursor * config.channels), (unsigned int)framesToWrite);
sineWaveCursor += framesToWrite;
//printf("TRACE 0\n");
//sine_u8(data, frameCount, config.channels);
//printf("TRACE: %d\n", frameCount);
//osaudio_write(audio, data, frameCount);
//printf("DONE LOOP\n");
}
} else if (config.format == OSAUDIO_FORMAT_S16) {
while (sineWaveCursor < sineWaveFrameCount) {
unsigned long framesToWrite = sineWaveFrameCount - sineWaveCursor;
if (framesToWrite > 0xFFFF) {
framesToWrite = 0xFFFF;
}
osaudio_write(audio, (short OSAUDIO_FAR*)pSineWave + (sineWaveCursor * config.channels), (unsigned int)framesToWrite);
sineWaveCursor += framesToWrite;
}
}
#if defined(OSAUDIO_DOS)
printf("Processing...\n");
for (;;) {
/* Temporary. Just spinning here to ensure the program stays active. */
//delay(1);
if (g_TESTING > 0) {
//printf("TESTING: %d\n", g_TESTING);
}
}
#endif
printf("Shutting down... ");
osaudio_close(audio);
printf("Done.\n");
(void)argc;
(void)argv;
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
+5536 -2488
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
includedir=@MINIAUDIO_PC_INCLUDEDIR@
libdir=@MINIAUDIO_PC_LIBDIR@
Name: miniaudio
Description: An audio playback and capture library.
URL: https://miniaud.io/
License: Unlicense OR MIT-0
Version: @PROJECT_VERSION@
Requires.private: @MINIAUDIO_PC_REQUIRES_PRIVATE@
Cflags: -I${includedir} @MINIAUDIO_PC_CFLAGS@
Libs: -L${libdir} -lminiaudio
Libs.private: @MINIAUDIO_PC_LIBS_PRIVATE@
+2
View File
@@ -10,6 +10,8 @@ Output files will be placed in the "res/output" folder.
Emscripten
----------
On Linux, do `source ~/emsdk/emsdk_env.sh` before compiling.
On Windows, you need to move into the build and run emsdk_env.bat from a command prompt using an absolute
path like "C:\emsdk\emsdk_env.bat". Note that PowerShell doesn't work for me for some reason. Example:
+2
View File
@@ -0,0 +1,2 @@
set DJGPP=C:\DJGPP\DJGPP.ENV
set PATH=C:\DJGPP\BIN;%PATH%
+15
View File
@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
@@ -0,0 +1 @@
/build
@@ -0,0 +1,56 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "io.miniaud.miniaudiotester"
compileSdk = 35
defaultConfig {
applicationId = "io.miniaud.miniaudiotester"
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package io.miniaud.miniaudiotester
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.miniaud.miniaudiotester", appContext.packageName)
}
}
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MiniaudioTester"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,37 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("miniaudiotester")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
native-lib.cpp)
# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
android
log)
@@ -0,0 +1,173 @@
#include <jni.h>
#include <string>
#define MA_DEBUG_OUTPUT
#include "../../../../../../../miniaudio.c" /* Android projects have very deep folder structures... */
typedef enum
{
BACKEND_AUTO,
BACKEND_AAUDIO,
BACKEND_OPENSL
} backend_choice_t;
typedef struct
{
ma_device device;
ma_waveform waveform;
bool hasDevice;
bool hasError; /* Will be set to true if something went wrong. */
std::string errorMessage; /* Will be an empty string if there is no error message. */
} audio_state_t;
static void audio_state_set_error(audio_state_t* pAudioState, const char* pMessage)
{
assert(pAudioState != nullptr);
pAudioState->hasError = true;
pAudioState->errorMessage = pMessage;
}
static void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{
auto* pAudioState = (audio_state_t*)pDevice->pUserData;
assert(pAudioState != nullptr);
ma_waveform_read_pcm_frames(&pAudioState->waveform, pFramesOut, frameCount, nullptr);
}
extern "C"
JNIEXPORT jlong JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_UninitializeAudio(JNIEnv *env, jobject, jlong audioState)
{
auto* pAudioState = (audio_state_t*)audioState;
if (pAudioState == nullptr) {
return 0;
}
if (pAudioState->hasDevice) {
ma_device_uninit(&pAudioState->device);
ma_waveform_uninit(&pAudioState->waveform);
pAudioState->hasDevice = false;
}
pAudioState->hasError = false;
pAudioState->errorMessage = "";
return (jlong)pAudioState;
}
extern "C"
JNIEXPORT jlong JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_PlayAudio(JNIEnv *env, jobject, jlong audioState, int backend)
{
auto* pAudioState = (audio_state_t*)audioState;
ma_result result;
if (pAudioState == nullptr) {
pAudioState = new audio_state_t;
pAudioState->hasDevice = false;
pAudioState->hasError = false;
}
/* If we don't have a device, create one. */
if (!pAudioState->hasDevice) {
ma_context_config contextConfig = ma_context_config_init();
ma_backend pBackends[1];
size_t backendCount;
if (backend == BACKEND_AUTO) {
backendCount = 0;
} else {
backendCount = 1;
if (backend == BACKEND_AAUDIO) {
pBackends[0] = ma_backend_aaudio;
} else if (backend == BACKEND_OPENSL) {
pBackends[0] = ma_backend_opensl;
} else {
backendCount = 0;
}
}
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = pAudioState;
result = ma_device_init_ex((backendCount == 0) ? nullptr : pBackends, backendCount, &contextConfig, &deviceConfig, &pAudioState->device);
if (result != MA_SUCCESS) {
audio_state_set_error(pAudioState, (std::string("Failed to initialize device. ") + ma_result_description(result)).c_str());
pAudioState->hasDevice = false;
}
/* Before starting the device we will need a waveform object. This should never fail to initialize. */
ma_waveform_config waveformConfig = ma_waveform_config_init(pAudioState->device.playback.format, pAudioState->device.playback.channels, pAudioState->device.sampleRate, ma_waveform_type_sine, 0.2, 400);
ma_waveform_init(&waveformConfig, &pAudioState->waveform);
pAudioState->hasDevice = true;
}
/* At this point we should have a device. Start it. */
result = ma_device_start(&pAudioState->device);
if (result != MA_SUCCESS) {
audio_state_set_error(pAudioState, (std::string("Failed to start device. ") + ma_result_description(result)).c_str());
}
return (jlong)pAudioState;
}
extern "C"
JNIEXPORT jlong JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_PauseAudio(JNIEnv *env, jobject, jlong audioState)
{
auto* pAudioState = (audio_state_t*)audioState;
if (pAudioState == nullptr) {
return true;
}
if (!pAudioState->hasError) {
if (pAudioState->hasDevice) {
ma_result result = ma_device_stop(&pAudioState->device);
if (result != MA_SUCCESS) {
audio_state_set_error(pAudioState, ma_result_description(result));
}
} else {
audio_state_set_error(pAudioState, "Trying to pause audio, but there is no device.");
}
}
return (jlong)pAudioState;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_HasAudioError(JNIEnv *env, jobject, jlong audioState)
{
auto* pAudioState = (audio_state_t*)audioState;
if (pAudioState == nullptr) {
return true;
}
return pAudioState->hasError;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_GetAudioError(JNIEnv *env, jobject, jlong audioState)
{
auto* pAudioState = (audio_state_t*)audioState;
if (pAudioState == nullptr) {
return env->NewStringUTF("Out of memory");
}
return env->NewStringUTF(pAudioState->errorMessage.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_DeleteAudioState(JNIEnv *env, jobject thiz, jlong audioState)
{
Java_io_miniaud_miniaudiotester_MainActivity_UninitializeAudio(env, thiz, audioState);
delete (audio_state_t*)audioState;
}
@@ -0,0 +1,79 @@
package io.miniaud.miniaudiotester
import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import io.miniaud.miniaudiotester.databinding.ActivityMainBinding
enum class AudioBackend(val value: Int)
{
BACKEND_AUTO(0),
BACKEND_AAUDIO(1),
BACKEND_OPENSL(2)
}
class MainActivity : AppCompatActivity()
{
private lateinit var binding: ActivityMainBinding
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnPlay.setOnClickListener {
val backend: AudioBackend;
if (binding.radioAAudio.isChecked) {
backend = AudioBackend.BACKEND_AAUDIO
} else if (binding.radioOpenSL.isChecked) {
backend = AudioBackend.BACKEND_OPENSL
} else {
backend = AudioBackend.BACKEND_AUTO
}
audioState = PlayAudio(audioState, backend.value)
if (HasAudioError(audioState)) {
binding.textInfo.text = GetAudioError(audioState)
} else {
binding.textInfo.text = "Playing..."
}
}
binding.btnStop.setOnClickListener {
audioState = PauseAudio(audioState)
if (HasAudioError(audioState)) {
binding.textInfo.text = GetAudioError(audioState)
} else {
binding.textInfo.text = "Paused."
}
}
binding.btnUninit.setOnClickListener {
audioState = UninitializeAudio(audioState)
if (HasAudioError(audioState)) {
binding.textInfo.text = GetAudioError(audioState)
} else {
binding.textInfo.text = "Device uninitialized."
}
}
}
private var audioState: Long = 0
external fun UninitializeAudio(audioState: Long): Long
external fun PlayAudio(audioState: Long, backend: Int): Long
external fun PauseAudio(audioState: Long): Long
external fun HasAudioError(audioState: Long): Boolean
external fun GetAudioError(audioState: Long): String
external fun DeleteAudioState(audioState: Long)
companion object {
// Used to load the 'miniaudiotester' library on application startup.
init {
System.loadLibrary("miniaudiotester")
}
}
}
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="320dp"
android:layout_height="144dp"
android:layout_marginStart="44dp"
android:layout_marginTop="90dp"
android:layout_marginEnd="44dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioButton
android:id="@+id/radioAutomatic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/automatic" />
<RadioButton
android:id="@+id/radioAAudio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/aaudio" />
<RadioButton
android:id="@+id/radioOpenSL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/opensl" />
</RadioGroup>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="46dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="235dp"
android:text="@string/backend_label"
android:textSize="34sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/btnPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="46dp"
android:layout_marginTop="00dp"
android:contentDescription="@string/play_button_desc"
app:layout_constraintEnd_toStartOf="@+id/btnStop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:srcCompat="@android:drawable/ic_media_play" />
<ImageButton
android:id="@+id/btnStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="00dp"
android:layout_marginEnd="253dp"
android:contentDescription="@string/pause_button_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnPlay"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:srcCompat="@android:drawable/ic_media_pause" />
<ImageButton
android:id="@+id/btnUninit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="197dp"
android:contentDescription="@string/uninit_button_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnStop"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:srcCompat="@android:drawable/ic_delete" />
<TextView
android:id="@+id/textInfo"
android:layout_width="317dp"
android:layout_height="353dp"
android:layout_marginStart="46dp"
android:layout_marginEnd="45dp"
android:layout_marginBottom="46dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Some files were not shown because too many files have changed in this diff Show More