Compare commits
335 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f40cf03f80 | |||
| 7f2cd5b8d5 | |||
| 3afbdef285 | |||
| 9a091f73aa | |||
| 4de39a8a37 | |||
| 7c8574210d | |||
| 6648ed005a | |||
| 87bae56937 | |||
| 293f5de18f | |||
| 233b9b69c4 | |||
| db514e813f | |||
| 8130543730 | |||
| b306c6a270 | |||
| 9e1f02b12a | |||
| fa84240364 | |||
| 70eb06d3bd | |||
| 959283f244 | |||
| ea59076ba9 | |||
| 1d7d8dfba0 | |||
| f48d903526 | |||
| 3bdd39fd2c | |||
| e75a053908 | |||
| 346d86ffa1 | |||
| a2f92095dc | |||
| badf36a378 | |||
| c9d288c3dc | |||
| 825d2c4466 | |||
| 6fd62e6bbc | |||
| b1893aa8f1 | |||
| df4baf8d40 | |||
| 21237008df | |||
| 587bd83cbb | |||
| b7e5451ef4 | |||
| ba84e61a18 | |||
| a7ab58259e | |||
| 18055f34bb | |||
| f6bae251bd | |||
| 3567d5cfef | |||
| 4e3b778c62 | |||
| 6315130ec6 | |||
| 6e1cd41622 | |||
| 78cdb9c1cb | |||
| 1ea69211ad | |||
| e9e8f90137 | |||
| d93552283f | |||
| 61a85dca42 | |||
| c87f207f4e | |||
| 02ae7e41f0 | |||
| 457a7279fa | |||
| fe1da60e1e | |||
| 0b70a990ca | |||
| 556160909e | |||
| c48975f4a9 | |||
| 79b4ddc27d | |||
| 0b9f03a376 | |||
| 140b9c7f9f | |||
| ac8c908283 | |||
| 8b87c10681 | |||
| 629d509072 | |||
| 5c86dd9153 | |||
| e54336996d | |||
| 1c7967fc88 | |||
| 2bc0e14abf | |||
| b348ab0155 | |||
| 346d65091a | |||
| aa75d5f8e8 | |||
| 0ac5c89157 | |||
| 47020e4092 | |||
| 02873ca300 | |||
| 11177ed19f | |||
| 682fad7d55 | |||
| 1497f5e467 | |||
| 853f27ed56 | |||
| 05d367eed5 | |||
| 97493bdfcd | |||
| 3edfb70a26 | |||
| 72e4721b2c | |||
| b255e91e08 | |||
| b261f4aeec | |||
| bd59c52309 | |||
| f15ddefce8 | |||
| 29c17fcb22 | |||
| 5a9c322c83 | |||
| cd14d18d0c | |||
| 534b43e800 | |||
| caa3d2a339 | |||
| 4c2d1bb67c | |||
| 5ba0fc51b6 | |||
| e58a24124a | |||
| 0b4646d31a | |||
| 9032fdbced | |||
| 2ee920577e | |||
| a944e19331 | |||
| 8a9ea7ce07 | |||
| 80747f440a | |||
| d40a385e07 | |||
| 7df2a50d7f | |||
| ab80abf061 | |||
| b9292a301f | |||
| 484a56499d | |||
| b12959f1d4 | |||
| 94077d5a95 | |||
| b3ab0567c3 | |||
| 7158cf58f9 | |||
| fe616c1a5a | |||
| cd16c5bcd3 | |||
| 76dfabbb45 | |||
| 46d8abf3de | |||
| 350784a946 | |||
| a65a7d139f | |||
| 2a79d124c1 | |||
| 8261dc8972 | |||
| c1daa31759 | |||
| b0e845e796 | |||
| d28ce1a841 | |||
| 1e2be9307e | |||
| 17b8dbf948 | |||
| 4663423838 | |||
| d6a1350c1f | |||
| c92e662de7 | |||
| e272f56750 | |||
| 1a83b0baa4 | |||
| 9511596a25 | |||
| 5c0724ad59 | |||
| c3b0a7fbbc | |||
| e4363a90be | |||
| 6453c9ff22 | |||
| 8c52072f43 | |||
| b6184fa2a0 | |||
| 166fd6dfc7 | |||
| 54373128ee | |||
| c74c90f686 | |||
| 01302b9715 | |||
| b5f1ff125e | |||
| 7a1135d448 | |||
| 9f9fc2333e | |||
| a497466f75 | |||
| 37b95f0f42 | |||
| 9f10bc7540 | |||
| 1fbad32949 | |||
| e1f5ed4f79 | |||
| ed5cda309c | |||
| 3435aafb34 | |||
| 3fd7c9f199 | |||
| deafb7e96f | |||
| 2e054f8011 | |||
| c13504629e | |||
| f9caab2fd5 | |||
| 57fbc6dd36 | |||
| ee3e532a54 | |||
| 178797502e | |||
| 0576191d7d | |||
| 6bc3fec34e | |||
| 2542be5db8 | |||
| 67d1aca341 | |||
| 5975db4c76 | |||
| 08d6d1fac0 | |||
| 4d971fe480 | |||
| 575790bb29 | |||
| e49ce7df95 | |||
| d672b9610f | |||
| 3889066fac | |||
| ff66923b9a | |||
| e3151f2df1 | |||
| abb81fe95c | |||
| 8ad250ccf6 | |||
| b40803cf97 | |||
| 466a1354ce | |||
| e08c1303ef | |||
| 698a4319f0 | |||
| eee86a0ae1 | |||
| d3a4b9cf20 | |||
| 48ac10d1e1 | |||
| 4b4349af52 | |||
| a4d462e39e | |||
| ef662aaddf | |||
| 22a5c65c94 | |||
| cff683a1b1 | |||
| 62d64d14bd | |||
| cf9371748a | |||
| 640d70c307 | |||
| 46788d59a8 | |||
| 01d6297bec | |||
| de5f370d09 | |||
| 47aa3e34e0 | |||
| 445cdcb82b | |||
| 34092dbfc8 | |||
| 14b986448f | |||
| a6ac898663 | |||
| 017f8944d3 | |||
| e15fd218be | |||
| 4c7021e53d | |||
| 856494d253 | |||
| a0aac6b5ec | |||
| 60c7c776b4 | |||
| 125e9226fb | |||
| 4deb3d4c6a | |||
| 3ffdbdc710 | |||
| 9b9e71ab6c | |||
| f39bbe2f4d | |||
| 79bb4d7a37 | |||
| f970144a3d | |||
| afc7e17fe6 | |||
| 047200eace | |||
| 6d5efde254 | |||
| 9da8df1b9f | |||
| ed5964c9f6 | |||
| 7e81d3ac80 | |||
| 450dcb1af3 | |||
| 38f7d29f6f | |||
| 1fe39f949a | |||
| 3fb7027682 | |||
| 14a455143f | |||
| 977bd616ff | |||
| a3ae2e71ff | |||
| 68a526a759 | |||
| 8383893c9c | |||
| 7a25af64d6 | |||
| fc905ec97f | |||
| 3081e314b7 | |||
| fcddfe6204 | |||
| 547ef1c9b7 | |||
| 928ed8bd85 | |||
| ae2cd4bea4 | |||
| b53daca554 | |||
| 1a7a9a7ed2 | |||
| 82ae0138f3 | |||
| 8d5bf8210c | |||
| ad615af1a8 | |||
| 3a34c049fa | |||
| 970c3801d9 | |||
| da76932f6b | |||
| 7dbb9f5e1a | |||
| bff9689b80 | |||
| bea73835dd | |||
| 6e1b0dbce4 | |||
| 08152a6a6d | |||
| b6747d5efc | |||
| cbabd2d13a | |||
| 546e23c0fb | |||
| 1b6d634299 | |||
| 7f911f3d12 | |||
| 75f46c6105 | |||
| 51e005369f | |||
| d628284548 | |||
| 059a25d9c5 | |||
| 6a8a756b88 | |||
| 8b6611299e | |||
| ee506b17ea | |||
| 12a8d4e491 | |||
| d1abdd100b | |||
| 192a84a106 | |||
| 0d0953aa85 | |||
| 6ab4567c57 | |||
| 4598d72a33 | |||
| 4f5106ec77 | |||
| a611cf5f26 | |||
| d726e85313 | |||
| efa270af91 | |||
| 98f6e923cc | |||
| 427bdc1d2a | |||
| 61586de203 | |||
| d46e19fb47 | |||
| dc423daa41 | |||
| 5d827878f2 | |||
| d87230b4bd | |||
| e1328d9d8a | |||
| 8036ac3781 | |||
| fc45d8ca06 | |||
| 35215b266c | |||
| 1c15cf6502 | |||
| 196289592a | |||
| 1b35118e31 | |||
| 88436b25ef | |||
| 3db49afa5b | |||
| aa98e1c493 | |||
| afb121e2ce | |||
| 855628f15f | |||
| 9091cbd016 | |||
| e82703482b | |||
| 6700c7ecc7 | |||
| 3ba0595c6a | |||
| 4bc18bba5a | |||
| 26ce355703 | |||
| f7ad7772d1 | |||
| 0eb86ea1da | |||
| 2618c21415 | |||
| 030b9554c2 | |||
| c6d8b591f6 | |||
| 3bdf611768 | |||
| 63e1900db8 | |||
| 29da9b789c | |||
| b454e7f14b | |||
| feea26496c | |||
| d36a2ef651 | |||
| e3af234720 | |||
| 5cb0c05675 | |||
| 9aa6e035bb | |||
| 7a8ebd7f4d | |||
| e32cc9ff83 | |||
| babd7fb00f | |||
| f4e5cf99dc | |||
| f20ab8a9ee | |||
| aa57d052da | |||
| c0afa7e621 | |||
| bde517a166 | |||
| 3ed9d05c38 | |||
| a4aa0dc404 | |||
| fd3c1b0af0 | |||
| 10f9ef05a2 | |||
| bdab2fc3e0 | |||
| 9c73849f3b | |||
| fde3a27d93 | |||
| 766a155fb3 | |||
| c29c001840 | |||
| ee3b7df66a | |||
| 1325ac397b | |||
| ed204e4b72 | |||
| 6099e6f41c | |||
| 4f426f6db0 | |||
| 7e38fa0e7e | |||
| d0709098cc | |||
| 3c5b15b48b | |||
| 2b8bb34ca2 | |||
| 2b0c525e53 | |||
| 6cba159210 | |||
| 1583329187 | |||
| 5a3655fea4 | |||
| 4a5b74bef0 | |||
| c77d40ba00 | |||
| 8fcf6889aa | |||
| f9ce46330c | |||
| 3b50a854ec | |||
| da0572e6b8 | |||
| eb0ce6f1a5 |
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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/
|
||||
@@ -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.
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
@@ -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;
|
||||
@@ -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/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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. */
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
@@ -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@
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
set DJGPP=C:\DJGPP\DJGPP.ENV
|
||||
set PATH=C:\DJGPP\BIN;%PATH%
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |