1194 Commits

Author SHA1 Message Date
David Reid afa3a0b855 Add support for copying audio buffer data sources. 2026-04-29 16:19:18 +10:00
David Reid ba8d25305c API CHANGE: Rename ma_audio_buffer_init_copy()
This has been renamed to ma_audio_buffer_init_and_copy_data(). This is
to free up the old name for a new purpose (creating a copy of the whole
`ma_audio_buffer` object).
2026-04-29 16:13:07 +10:00
David Reid 9ab3c55587 API CHANGE: Remove ma_audio_buffer_ref.
Use ma_audio_buffer instead.
2026-04-29 16:09:11 +10:00
David Reid d7cec8ba20 Remove an unused function. 2026-04-29 15:40:44 +10:00
David Reid 125f181b2b Add support for copying noise data sources. 2026-04-29 15:37:55 +10:00
David Reid ca9a1e8493 Add support for copying waveform data sources. 2026-04-29 15:37:45 +10:00
David Reid f68fe8b076 Add support for copying audio ring buffers. 2026-04-29 15:25:31 +10:00
David Reid bfd3c98797 Remove an unnecessary function. 2026-04-29 14:41:03 +10:00
David Reid f018f3cb85 Add support for copying decoders. 2026-04-29 14:39:45 +10:00
David Reid f40376031f Fix some warnings. 2026-04-29 13:55:41 +10:00
David Reid b73f0182d7 Add support for copying data streams.
This enables `ma_sound_init_copy()` to work with sounds that were
initialized with `MA_SOUND_FLAG_STREAM`.
2026-04-29 13:35:17 +10:00
David Reid af9c82f3dc API CHANGE: Simplify ma_resource_manager_data_buffer_init_copy()
This removes the ma_resource_manager parameter since it can just be
derived from the data source.
2026-04-29 13:30:26 +10:00
David Reid 1129061e19 Update reverb and vocoder examples. 2026-04-29 13:08:50 +10:00
David Reid ab15b24065 API CHANGE: Remove the flags member from ma_data_source_vtable. 2026-04-29 11:30:27 +10:00
David Reid fcb48c585a Remove an unused data source flag.
This removes MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT which is
no longer used by any data sources.
2026-04-29 11:23:35 +10:00
David Reid 265d250ec2 Simplify some resource manager code.
These changes are mostly for setting up support for copyable streams.
2026-04-29 11:10:20 +10:00
David Reid 50fcc2f41f Make ma_sound_init_copy() more generic.
The copying of the data source is now done generically through the new
data source copying system rather than being restricted to just the
resource manager.
2026-04-29 10:51:27 +10:00
David Reid f223d3e315 Add some new data source APIs.
ma_data_source_get_vtable()
  ma_data_source_init_copy()
2026-04-29 10:46:38 +10:00
David Reid 1403dfcdc9 Add copy callback for the ma_resource_manager_data_buffer data source. 2026-04-29 07:42:42 +10:00
David Reid a9043ba26d API CHANGE: Add an onCopy callback to ma_data_source_vtable.
This is used for making a copy of a data source.

Data sources are not required to support copying, in which case this
callback can be set to NULL, or it can return MA_NOT_IMPLEMENTED.

This commit just sets up the infrastructure. As of this commit, no data
sources actually implement this, however future commits will be
introducing support in stages.
2026-04-29 07:33:50 +10:00
David Reid 137298734e API CHANGE: Add an onSizeof callback to ma_data_source_vtable.
This should return the size of the implementations struct. For example,
`ma_decoder` would return `sizeof(ma_decoder)`.

This is in preparation for future work to support copying data sources.
2026-04-29 06:54:57 +10:00
David Reid 2cb46c7abd Whitespace. 2026-04-28 19:24:46 +10:00
David Reid 70d52e5eaa Simplify ma_allocation_callbacks_init_copy().
This returns the ma_allocation_callbacks object instead of a result
code.
2026-04-28 19:21:30 +10:00
David Reid d7802c3b14 API CHANGE: Remove decoding backend file initialization specializations.
This removes the onInitFile and onInitFileW callbacks from
ma_decoding_backend_vtable. This means decoding backends can no longer
implement a specialized initialization routine for loading from a file.
What this does is enforces all file IO to go through the same code path,
that being the `ma_decoder` and `ma_vfs` infrastructure. It also
simplifies the backend vtable.

The ability to initialize from memory (the onInitMemory callback) is
still available and there are not plans to remove this.
2026-04-28 19:07:23 +10:00
David Reid a92139b686 Enable libvorbis and libopus decoding in the deviceio test. 2026-04-28 19:00:24 +10:00
David Reid 77074f0597 API CHANGE: Add an onUninit callback to ma_data_source_vtable.
This callback to execute the data source's uninitialization routine.
2026-04-28 17:27:31 +10:00
David Reid 3e729e4b28 Make some APIs public.
This makes the following APIs public:

  ma_allocation_callbacks_init_default()
  ma_allocation_callbacks_init_copy()

This is required to allow things like custom data sources and backends
to be able to track allocation callbacks if necessary.
2026-04-28 17:25:23 +10:00
David Reid b5732f515a API CHANGE: Update ma_noise_uninit().
This removes the allocations callback parameter. It is no managed
internally in preparation for some changes to data source management.
2026-04-28 17:23:16 +10:00
David Reid 52f22d6597 API CHANGE: Update ma_node_graph_uninit().
This removes the allocation callbacks parameter. These are now managed
internally. This is in preparation for some future changes to data
source management.
2026-04-28 17:20:31 +10:00
David Reid 44323c9cae API CHANGE: Rename data source init and uninit APIs.
These have been renamed to the following:

  ma_data_source_init   > ma_data_source_base_init
  ma_data_source_uninit > ma_data_source_base_uninit

The new naming scheme makes it clearer that you're initializing the
base data source structure.
2026-04-28 16:24:22 +10:00
David Reid a267a4ca65 Merge branch 'dev' into dev-0.12 2026-04-26 18:56:20 +10:00
David Reid 09615e6bc1 Update c89atomic. 2026-04-26 18:56:05 +10:00
David Reid 3841d2858d Merge branch 'dev' into dev-0.12 2026-04-26 11:55:23 +10:00
David Reid 12a83fcf54 Update dr_wav. 2026-04-26 11:55:12 +10:00
David Reid dad1060489 PipeWire: Clean up to runtime linking.
This will cleanly fail when a required entry point is unavailable.

Public issue https://github.com/mackron/miniaudio/issues/1104
2026-04-26 09:18:26 +10:00
David Reid 72a6203849 Fix build errors with MA_NO_RUNTIME_LINKING. 2026-04-26 09:12:46 +10:00
David Reid 4635de9916 Merge branch 'dev' into dev-0.12 2026-04-26 08:33:38 +10:00
David Reid 2986173662 Emscripten: Fix an error with ALLOW_MEMORY_GROWTH.
Public issue https://github.com/mackron/miniaudio/issues/1114
2026-04-26 08:32:28 +10:00
David Reid 1cd746c5a6 Fix Emscripten test. 2026-04-26 08:23:13 +10:00
David Reid 07c4ae67cf Merge branch 'dev' into dev-0.12 2026-04-26 08:22:51 +10:00
Echo J dcfc24c100 Emscripten: Cast pointer arguments to pointer-sized integers
For some reason, 64-bit pointer arguments are casted to Numbers
in Emscripten's type handling (which causes conversion errors when
passing them back to native code)
2026-04-26 08:11:16 +10:00
David Reid 7533c9d341 Update build instructions for Emscripten test. 2026-04-26 08:10:43 +10:00
Yuri Khrustalev 56ffd77769 Make ma_android_sdk_version static to fix -Wmissing-prototypes. 2026-04-26 07:22:41 +10:00
amaldika 55f16e62f6 Support to build for arm64EC configuration on MSVC. 2026-04-26 07:21:35 +10:00
David Reid 5d8e72d54f Merge branch 'dev' into dev-0.12 2026-04-26 07:15:52 +10:00
David Reid 1df46ae9a0 Update dr_libs. 2026-04-26 07:08:13 +10:00
David Reid c64330dbd9 Clean up some stale comments. 2026-04-20 06:05:31 +10:00
David Reid a513b522ab Dreamcast: Increase lower bound period size from 1024 to 1536.
Testing on real hardware is suggesting that 1024 is too small to be
practical.
2026-04-06 12:03:00 +10:00
David Reid b91098312d Minor chibicc fix.
This commit does not make miniaudio compilable with chibicc. Additional
work is required in c89atomic which will come later.
2026-03-15 08:55:36 +10:00
David Reid d50152e382 Update dr_mp3. 2026-03-15 08:50:40 +10:00
David Reid d654ce32a7 Merge branch 'dev' into dev-0.12 2026-03-13 11:23:26 +10:00
David Reid 12bacf1186 Revert "Enforce a read callback to be specified for decoders."
This reverts commit 52d09d3688.
2026-03-13 11:22:40 +10:00
David Reid 968a8045c0 Merge branch 'dev' into dev-0.12 2026-03-12 06:27:02 +10:00
David Reid 117366df9a Fix an incorrect check for the decoding seek callback. 2026-03-12 06:24:04 +10:00
David Reid ed950dc688 Merge branch 'dev' into dev-0.12 2026-03-12 06:22:48 +10:00
David Reid 0041150de0 Update change history. 2026-03-12 06:16:57 +10:00
David Reid 52d09d3688 Enforce a read callback to be specified for decoders. 2026-03-12 06:15:27 +10:00
David Reid 6922366bb1 Don't crash when a decoding seek callback is unavailable. 2026-03-12 06:14:00 +10:00
David Reid 4c082afe71 Clarify usage of the decoder read callback. 2026-03-12 06:08:27 +10:00
David Reid a6cb08579e Add sizeof profiling test. 2026-03-05 17:58:09 +10:00
David Reid deda7e500f Alignment. 2026-03-05 17:50:34 +10:00
David Reid df6119890e Merge branch 'dev' into dev-0.12 2026-03-04 07:01:18 +10:00
David Reid 9634bedb5b Version 0.11.25 2026-03-04 06:25:00 +10:00
David Reid b113d498a5 Update dr_wav. 2026-03-03 10:34:26 +10:00
David Reid bc52a82903 Add PS Vita backend. 2026-03-01 07:53:00 +10:00
David Reid d7ce0506f6 Fix a minor copy/paste error. 2026-03-01 07:26:20 +10:00
David Reid 41a8ec6a8d Add XAudio options to the CMake script. 2026-03-01 07:24:36 +10:00
David Reid dea554dbb2 Minor rearrangement. 2026-03-01 07:22:06 +10:00
David Reid e552fd20cf Add a PS Vita optimized threading backend.
This addresses an issue where condition variables would throw the
following error:

  _sceKernelWaitSema returned SCE_KERNEL_ERROR_WAIT_TIMEOUT (0x80028005)

Bypassing the pthread backend and using Vita APIs directly addresses
this and should be more optimal.
2026-02-28 19:53:38 +10:00
David Reid 69396f97a7 Fix a threading issue with PS Vita. 2026-02-28 17:08:37 +10:00
David Reid e35c1fd64b Update the backend template. 2026-02-27 20:18:25 +10:00
David Reid bc69c86868 XAudio: Fix an error with device enumeration. 2026-02-27 18:03:23 +10:00
David Reid 8b3ae59b40 Dreamcast: Fix an error with device enumeration. 2026-02-27 18:03:05 +10:00
David Reid d94b45d058 Add a template for device backends. 2026-02-27 17:42:28 +10:00
David Reid bcdb37ff66 Add Vita SDK detection. 2026-02-27 16:34:44 +10:00
David Reid 7d703e60ab Use N3DS nomenclature instead of 3DS in preparation for future work. 2026-02-27 16:08:08 +10:00
David Reid 30e2ca2b46 Add XAudio backend.
This adds support for the original Xbox via NXDK.
2026-02-27 10:58:24 +10:00
David Reid b5eb987b86 SDL2: Include miniaudio.h in the header. 2026-02-23 17:19:54 +10:00
David Reid c580fad1dd Fix NXDK build. 2026-02-23 17:17:13 +10:00
David Reid 4c81dbd81f Add ma_device_get_period_size_in_frames(). 2026-02-22 06:31:48 +10:00
David Reid 7700880b56 Don't unnecessarily apply clipping. 2026-02-20 17:23:42 +10:00
David Reid 0e2c0e1eac Fix an edge case in the data converter. 2026-02-20 09:18:25 +10:00
David Reid a3fd69624b Fix a bug in ma_data_converter_set_rate().
This was not setting the sample rate members of the ma_data_converter
struct.
2026-02-20 08:51:39 +10:00
David Reid d39c874736 Bug fixes to the resampler. 2026-02-20 08:46:00 +10:00
David Reid 0aef190a59 Resampler: Improve stability when ratio is 1. 2026-02-18 13:01:20 +10:00
David Reid 7e944566c4 Resampler: Add back support for passing in NULL for the input/output. 2026-02-18 12:51:58 +10:00
David Reid 3ea9cc468a Documentation fixes. 2026-02-17 07:23:36 +10:00
David Reid 4d8bd6ed84 Add PipeWire to readme. 2026-02-17 06:53:13 +10:00
David Reid 60a96123db Add Dreamcast to CMake script and readme. 2026-02-17 06:52:47 +10:00
David Reid 282ed02f46 Dreamcast: Add a readme to the romdisk folder. 2026-02-17 06:22:55 +10:00
David Reid 788b78169a Use newer tagging system for backend-specific functions. 2026-02-16 20:33:40 +10:00
David Reid c0f0500e43 Code rearrangement. 2026-02-16 20:24:56 +10:00
David Reid 2324d5ad9e Add Dreamcast backend. 2026-02-16 19:07:35 +10:00
David Reid f6662fdb2e Minor documentation fix. 2026-02-16 18:00:20 +10:00
David Reid 2f148fdd12 Channel Converter: Stop doing a micro heap allocation.
With small channel counts, mono and stereo, the channel converter will
no longer allocate memory on the heap for the channel map, but will
instead just store it in the struct directly. For larger channel counts
it will fall back to a heap allocation. This prevents stereo channel
maps resulting in a heap allocation of 8 bytes.

With this change a stereo passthrough `ma_device` can be initialized
with a heap allocation for the internal data converter.

This only affects passthrough channel conversion. When shuffling or
weights are required, a heap allocation will still be done. This
optimization is specifically for passthrough.
2026-02-16 16:36:27 +10:00
David Reid 0d7a9f960f Update documentation to suggest getters instead of direct struct access.
This is for the format, channels, sample rate and channel map.
2026-02-16 14:48:00 +10:00
David Reid e0e05c7b8c Add some more intuitive getters for ma_device.
This adds the following APIs:

    ma_device_get_playback_format()
    ma_device_get_capture_format()
    ma_device_get_playback_channels()
    ma_device_get_capture_channels()
    ma_device_get_playback_channel_map()
    ma_device_get_capture_channel_map()
2026-02-16 10:36:38 +10:00
David Reid ebbe9707e2 Use a heap allocation for the duplex ring buffer in ma_device.
The heap allocation is aligned to MA_CACHE_LINE_SIZE which is an
optimal alignment for ring buffers.

This also reduces the size of the `ma_device` struct for non-duplex
devices which is the most common setup.
2026-02-16 09:50:10 +10:00
David Reid dbc955fb0d Remove some unused placeholder functions. 2026-02-16 07:30:24 +10:00
David Reid d6d26deeda Fix some compilation errors with the resampling test. 2026-02-16 07:12:42 +10:00
David Reid dd6c57664b Add a deinterleaving code path for unaligned buffers. 2026-02-16 07:04:45 +10:00
David Reid e490db3085 Optimizations to ma_interleave_pcm_frames(). 2026-02-16 06:52:12 +10:00
David Reid 6851858937 Fix a bug in the profiling test. 2026-02-16 06:01:35 +10:00
David Reid 242cbf4d8c Optimizations to ma_deinterleave_pcm_frames() for stereo. 2026-02-15 21:54:29 +10:00
David Reid 01e5042bfb Fix an error with s24 deinterleaving. 2026-02-15 16:15:25 +10:00
David Reid 13f50137b0 Optimization to ma_deinterleave_pcm_frames() for s32 and s24. 2026-02-15 14:13:45 +10:00
David Reid 65a0c1f83d Minor struct packing improvement. 2026-02-15 13:16:00 +10:00
David Reid d63deea939 Whitespace. 2026-02-15 13:15:31 +10:00
David Reid 37a7009b3d Merge branch 'dev' into dev-0.12 2026-02-15 08:18:36 +10:00
David Reid 1078dc292a Add a safety check to ma_data_source_read_pcm_frames_within_range().
Public issue https://github.com/mackron/miniaudio/pull/1095
2026-02-15 08:11:57 +10:00
David Reid b33eb2ea4f Win32: Fix a bug in ma_dlopen() with the UWP build.
Public issue https://github.com/mackron/miniaudio/pull/1095
2026-02-15 08:05:02 +10:00
David Reid a6a7a76e6f Update change history. 2026-02-15 07:54:42 +10:00
David Reid 20c9f7fe0a Try fixing some warnings with newer versions of Emscripten.
Public issue https://github.com/mackron/miniaudio/pull/1097
2026-02-15 07:47:36 +10:00
David Reid ce05296055 Remove a TODO. 2026-02-15 07:40:08 +10:00
David Reid 852e91b380 Make ma_device_post_init() private.
Backends should use `ma_device_update_descriptor()` instead.
2026-02-15 07:22:39 +10:00
David Reid d9590dcf6e Remove a TODO. 2026-02-15 07:20:07 +10:00
David Reid 9edf1a558c Fix some warnings. 2026-02-15 06:29:22 +10:00
David Reid f75cd3a784 Remove some now unnecessary warning silencers. 2026-02-15 06:24:38 +10:00
David Reid c8c11615e3 Try fixing the Emscripten build. 2026-02-14 20:55:02 +10:00
David Reid dbf391611d Add resampling test. 2026-02-14 20:26:20 +10:00
David Reid d286a97ab1 Fix some warnings with MSVC. 2026-02-14 20:24:52 +10:00
David Reid 5ffa29a80b Clear out an out of date test. 2026-02-14 20:20:28 +10:00
David Reid ec69cafef8 Fix a typo. 2026-02-14 17:08:26 +10:00
David Reid 6adcbf9034 API CHANGE: Remove some functions relating to resampling.
The following functions are removed:

    ma_linear_resampler_get_required_input_frame_count()
    ma_linear_resampler_get_expected_output_frame_count()
    ma_resampler_get_required_input_frame_count()
    ma_resampler_get_expected_output_frame_count()
    ma_data_converter_get_required_input_frame_count()
    ma_data_converter_get_expected_output_frame_count()

These functions were used for calculating the required number of input
frames given an output capacity, and the number of expected number of
output frames given an input frame count. In practice these have proven
to be extremely annoying and finicky to get right. I myself have had
trouble keeping this working consistently as I make changes to the
processing function and I have zero confidence custom resampling
backends will implement this correctly.

If you need this functionality, take a copy of the resampler from
miniaudio 0.11.x and maintain that yourself.
2026-02-14 17:08:11 +10:00
David Reid fd1369b3fc Resampler: Revert and earlier experiment. 2026-02-14 15:37:52 +10:00
David Reid a8dd56fbbe Remove some unused functions. 2026-02-14 15:01:01 +10:00
David Reid 0f1ead0873 Remove a TODO. 2026-02-14 14:56:49 +10:00
David Reid e2e6bb6334 Resampler: Optimization to the LPF > 0 path.
This moves the channel count checks outside of the loop.
2026-02-14 14:22:06 +10:00
David Reid 5ae52e1a0a Cleanup. 2026-02-14 13:52:23 +10:00
David Reid c39ace1604 Disable filtering for pitch shifting on sounds by design. 2026-02-14 13:29:27 +10:00
David Reid 0615ce28f1 Resampler: Optimization to the no-LPF path.
This moves some checks outside the loop. A bit more code duplication,
but does improve speed.
2026-02-14 13:22:42 +10:00
David Reid 0fe2f7effd Resampler: Remove some now well out of date code. 2026-02-14 08:58:33 +10:00
David Reid c456a2f432 Resampler: Experiment with some timer management optimizations.
The idea here is to only update the resampler object once at the end.
This improves speeds up the problematic s16 mono upsampling path with
Clang, but that same path with GCC is still slow somehow.
2026-02-14 08:54:13 +10:00
David Reid 6d20358df1 Resampler: More work on filter decoupling.
This makes the s16 mono upsampling path slower somehow. This seems to be
the problem code path for some reason. Other paths don't seem to be so
sensitive to seemingly harmless changes.
2026-02-14 08:21:56 +10:00
David Reid 459fc19304 Resampler: Decouple the filtering step from the main resampler object.
The idea here is to have a more clearly defined data dependency
separation between the resampler and the filtering state which I'm
hoping might open up more optimization opportunities. The problem with
this theory, is that this commit makes the GCC build slower on the s16
mono upsampling path. It appears to be slightly fast with Clang though.
2026-02-14 07:10:34 +10:00
David Reid d4382ce478 Resampler: Optimization to the filtering stage.
This seems to trigger a fast optimization strategy when compiling with
GCC. With this change the filtered s16 mono path is almost 2x faster.
2026-02-14 06:20:37 +10:00
David Reid b3340e629a Remove a TODO.
I tried addressing this, but upon doing so the build was slower. It was
especially bad with Clang where is was 2x slower(!), and just slightly
slower with GCC.

Not sure exactly what's going on here, but I guess the compiler is
hitting some edge case that's prevent efficient optimization. What's
weirder, is the slowness only affects the mono s16 code path. Other
code paths are totally fine.
2026-02-13 20:54:38 +10:00
David Reid 9d0f4a4103 Rename a macro. 2026-02-13 19:10:53 +10:00
David Reid db1bc8c4b7 Resampler: Remove dependency on ma_lpf.
This makes the resampler a bit more self-contained and allows us to do
some resampler-specific optimizations to the filtering process. It also
reduces the size of the `ma_linear_resampler` struct.
2026-02-13 18:54:32 +10:00
David Reid cbbe317adc Resampler: Miscellaneous optimizations to the linear resampler. 2026-02-13 18:33:29 +10:00
David Reid 172f8beae6 Resampler: Optimization for floating point stereo.
This applies only to the f32 no-LPF code path. Other code paths will be
integrated later.
2026-02-08 22:01:23 +10:00
David Reid cd02ebe39c Resampler: A loop unrolling optimization experiment.
My idea here is to make it easier to for the compiler to SIMD-ify some
of the interpolation code. I have not confirmed that it is actually
being SIMD-ified by the compiler, but it is still significantly faster
in both debug and release builds.

There is a mono specialization here which further improves performance.
I have not yet experimented with a stereo specialization, but if it
works it'll be added in a future commit.

This applies only to the f32 no-LPF code path. Other paths will come
later once I'm done with this round of experiments.
2026-02-08 20:12:57 +10:00
David Reid 20180b0ae5 Resampler: For LPF orders to be a multiple of 2.
This is in preparation for an implementation simplification for the
purpose of some upcoming optimizations.
2026-02-08 19:21:10 +10:00
David Reid a04f300821 Stop encouraging the use of some resampling functions.
These functions are not reliably implemented by all backends and as such
I'd rather not encourage these use of these. These might be removed in
the future.
2026-02-07 17:09:51 +10:00
David Reid 2ea55eaeaf Resampler: Optimization to f32, no LPF code path. 2026-02-07 15:40:10 +10:00
David Reid c0b57c3aea Resampler: Optimization to s16, no LPF code path. 2026-02-07 11:26:05 +10:00
David Reid 4b8eb8588d Resampler: Stop allowing NULL input and output buffers. 2026-02-07 10:48:42 +10:00
David Reid b6467f3cd0 Resampler: Stop invoking the LPF when the order is zero. 2026-02-05 20:22:34 +10:00
David Reid 848025b9c0 Resampler: Fix a bug where the LPF is not being applied. 2026-02-05 20:17:46 +10:00
David Reid 5ea8bbf701 Minor change to a debugging function. 2026-02-05 20:02:01 +10:00
David Reid cff6c7e28a Resampler: Convert a double to a float.
This saves a a few bytes in `ma_linear_resampler`.
2026-02-05 10:18:20 +10:00
David Reid 445aefae06 Resampler: Optimization to the s16 path in the linear resampler.
The main thing here is moving an integer division out of the inner loop.
2026-02-05 08:06:52 +10:00
David Reid 2c14e2e5a7 Resampler: Move a division out of an inner loop. 2026-02-04 17:00:09 +10:00
David Reid 2683601481 Update the linear resampler in preparation for some optimizing. 2026-02-04 16:59:05 +10:00
David Reid 1e2427f5f7 Tighten up the audio thread.
This should close a hole that could possibly result in the audio thread
getting stuck if the `MA_DEVICE_OP_UNINIT` operation posted from
`ma_device_uninit()` fails.
2026-02-04 14:20:42 +10:00
David Reid 93e74aaf91 Reduce the size of the ma_device_op_queue struct.
This in turn reduces the size of the `ma_device` struct by about half
a kilobyte or so.
2026-02-04 14:18:08 +10:00
David Reid 89fea8a39f Remove channelMap and internalChannelMap arrays from ma_device.
This information is already stored in the data converter and can be
retrieved with `ma_device_get_channel_map()` and
`ma_device_get_internal_channel_map()`.

This reduces the size of the `ma_device` struct by ~1KB.
2026-02-04 13:32:14 +10:00
David Reid 9b66a480c7 Fix a bug with channel map retrieval with the channel converter.
This had the input and output channel maps the wrong way around.
2026-02-04 12:45:20 +10:00
David Reid cb26c7ec52 Remove an unnecessary function.
This removes the horrendously named `ma_device__post_init_setup()` which
I've been meaning to remove for years.
2026-02-04 12:44:35 +10:00
David Reid 02f8a6b952 Set up some infrastructure for some future optimizations.
This changes the way backends update the internal data format in
response to a device reroute. With this change we'll be able to remove
the `channelMap` and `internalChannel` map members from the `ma_device`
struct which should reduce its size by ~1KB.
2026-02-04 11:46:05 +10:00
David Reid dc72a5683b Add some getters:
ma_device_get_format()
  ma_device_get_channels()
  ma_device_get_sample_rate()
  ma_device_get_internal_format()
  ma_device_get_internal_channels()
  ma_device_get_internal_sample_rate()
  ma_device_get_internal_channel_map()
2026-02-04 10:52:08 +10:00
David Reid 5c4cb49ad8 Add ma_device_get_channel_map(). 2026-02-01 16:59:13 +10:00
David Reid d929fafb34 Add a specialized path for deinterleaving u8 samples.
This is not yet optimized.
2026-02-01 11:04:07 +10:00
David Reid 53fce8453c Rename a variable. 2026-02-01 11:03:46 +10:00
David Reid ac12fa70e0 Fix Dreamcast/KallistiOS build. 2026-01-29 16:29:14 +10:00
David Reid 3ab152afb3 audio(4): Failed context initialization if /dev/audioctl does not exist.
This allows initialization to abort at an earlier stage which gives the
fallback logic a chance to try a different backend.
2026-01-28 16:33:30 +10:00
David Reid 38e35935d7 Get the audio(4) backend compiling with Solaris.
I have not been able to figure out how to get audio working with my VM
so this is currently untested.
2026-01-28 16:20:09 +10:00
David Reid bbc7ad1921 audio(4): Make format encoding stuff more robust. 2026-01-28 14:47:24 +10:00
David Reid c134a1c870 Add Sun/Solaris compile-time detection. 2026-01-28 14:16:57 +10:00
David Reid 3b5b433e0c Fix some warnings with the release build. 2026-01-28 10:24:47 +10:00
David Reid 847711e291 Fix some warnings. 2026-01-28 09:48:25 +10:00
David Reid d1f34cd5db Update the audio ring buffer to use the standard config/init pattern.
This makes it consistent with everything else in the library.
2026-01-27 17:44:52 +10:00
David Reid 75b3f7dddc Update to the audio ring buffer.
This changes `write_pcm_frames()` so that when `pFramesWritten` is null,
the write is treated as all or nothing. If the entire write cannot be
performed, nothing at all will be written and `MA_NO_SPACE` will be
returned. The same applies with `read_pcm_frames()` and `pFramesRead` in
which case `MA_NO_DATA_AVAILABLE` will be returned.

The reason for this change is that if the called passes in null for
these values, they'll have no way to know how many frames were actually
written or read. Therefore the most practical way to treat it is as all
or nothing.
2026-01-27 17:04:22 +10:00
David Reid fcd386dd87 Remove ma_audio_queue. 2026-01-27 06:17:14 +10:00
David Reid 2302e58045 API CHANGE: Rename vtable to pVTable.
This applies to `ma_data_source_config` and `ma_node_config` and makes
the naming consistent with other parts of the library.
2026-01-26 15:46:58 +10:00
David Reid 28de8d8947 PipeWire: Try improving mono streams. 2026-01-26 14:04:12 +10:00
David Reid 18df387a84 Updates to ring buffers. 2026-01-26 12:18:41 +10:00
David Reid d30d1cfd89 Update ring buffer. 2026-01-26 06:26:29 +10:00
David Reid 8c2cbf6343 Fix some language in the documentation. 2026-01-25 20:45:15 +10:00
David Reid f99ff4cec0 Remove the old ring buffer.
Public issue https://github.com/mackron/miniaudio/issues/671
2026-01-25 20:30:27 +10:00
David Reid 07ea662dae Update the Emscripten test. 2026-01-25 19:45:01 +10:00
David Reid 5a7bfd7c2c Update the hilo_interop example to use the new ring buffer. 2026-01-25 18:46:25 +10:00
David Reid ca6afd1f49 Fix a bug with the audio ring buffer data source. 2026-01-25 18:45:05 +10:00
David Reid 7c3b8fab04 Remove osaudio.
This belongs in its own repository.
2026-01-25 18:32:57 +10:00
David Reid 7d1c994414 PipeWire: Use the new ring buffer. 2026-01-25 17:47:57 +10:00
David Reid af2cf5d161 Add a new ma_audio_ring_buffer data source.
This is a wrapper around `ma_ring_buffer` and is more specialized
towards audio. It is a data source and replaces `ma_pcm_rb` which will
be removed in a future commit.

Public issue https://github.com/mackron/miniaudio/issues/671
2026-01-25 16:50:28 +10:00
David Reid edb64e6017 Add a new SPSC ring buffer.
This improves on the old ring buffer by having a much simpler
implementation and a much simpler API that does not require the caller
to do reading and writing in a loop.

Future commits will be removing the old ring buffer.

Public issue https://github.com/mackron/miniaudio/issues/671
2026-01-25 14:05:30 +10:00
David Reid bef32b4419 Fix a null pointer dereference in the audio queue. 2026-01-22 11:34:35 +10:00
David Reid 8a43271555 Initial work on the audio queue data source.
Public issue https://github.com/mackron/miniaudio/issues/744
2026-01-22 11:29:53 +10:00
David Reid ba963e46b5 Web Audio: Experimental loopback support.
This uses `getDisplayMedia()`. Support for this is extremely browser and
system specific so I'm not advertising support for this documentation.

Public issue https://github.com/mackron/miniaudio/issues/967
2026-01-21 12:19:25 +10:00
David Reid f6b973d384 Allow backends to work without stepping when threading is disabled.
This is per-backend.
2026-01-21 12:14:34 +10:00
David Reid f37ffed283 Merge PipeWire backend into the main library. 2026-01-20 17:15:32 +10:00
David Reid f2b9d0b480 Merge branch 'dev' into dev-0.12 2026-01-20 09:55:35 +10:00
David Reid dec6c16539 Update social media links on website. 2026-01-20 06:16:43 +10:00
David Reid 9aa4744a94 PipeWire: Fix a memory leak. 2026-01-19 18:20:31 +10:00
David Reid 0cf35695c8 PipeWire: Set up some infrastructure for future work. 2026-01-18 16:29:40 +10:00
David Reid 3a1b85bb53 PipeWire: Rename some variables. 2026-01-18 12:48:25 +10:00
David Reid a187fb0450 PipeWire: Try fixing a compilation error. 2026-01-18 11:58:07 +10:00
David Reid 8d9d61d607 PipeWire: Improve sample rate detection for device enumeration. 2026-01-18 11:41:36 +10:00
David Reid 64b3dd6f66 PipeWire: Comment out some unshippable code.
A better workaround for this is still in progress.
2026-01-18 10:31:43 +10:00
David Reid f215062678 PipeWire: Refactoring in an attempt to work around an PipeWire issue.
It turns out this didn't actually fix the problem, but I actually prefer
this version so I'm going to keep this.
2026-01-18 09:30:49 +10:00
David Reid d043ce61b3 Merge branch 'dev' into dev-0.12 2026-01-18 06:06:51 +10:00
David Reid 13d161bc8d Update split version. 2026-01-18 06:05:35 +10:00
David Reid df405b1fb7 PipeWire: Fix a crash in device enumeration. 2026-01-17 20:30:05 +10:00
David Reid f794044d0a WinMM: Optimize some memory allocations. 2026-01-17 18:50:21 +10:00
David Reid 251e0e59e4 OpenSL: Optimize some memory allocations. 2026-01-17 18:00:32 +10:00
David Reid 70ff2591bc PulseAudio: Optimize some memory allocations. 2026-01-17 17:23:56 +10:00
David Reid 1b7cc965df Prioritize JACK over ALSA.
With ALSA having a higher priority JACK will never actually get picked.
2026-01-17 15:57:57 +10:00
David Reid f1d99a186c Update audioconverter. 2026-01-17 15:08:27 +10:00
David Reid cb0e6afe70 Update to the decoding backend system.
The `onGetEncodingFormat` callback has been removed and replaced with an
`onInfo`. This new callback fills out a struct with the supported
encoding format (is recognized by miniaudio), in addition to the name of
the decoding backend, and the decoding library and vendor.
2026-01-17 14:49:26 +10:00
David Reid 3ab17977ea Have the engine apply clipping to samples at the end of processing.
This can be disabled with the `noClip` config option.
2026-01-17 12:55:09 +10:00
David Reid 89663fa647 Merge branch 'dev' into dev-0.12 2026-01-17 12:19:20 +10:00
David Reid 347321b27c Version 0.11.24 2026-01-17 09:37:44 +10:00
David Reid da94cf2bc6 Update fs. 2026-01-17 09:34:51 +10:00
David Reid 8e6283aa31 Fix a warning. 2026-01-17 09:34:34 +10:00
David Reid d0b98eee6b Update change history. 2026-01-17 09:25:06 +10:00
David Reid 74912d525b Add SECURITY.md 2026-01-17 08:58:02 +10:00
Richard Keller a551f0e70b Free pa_context if connecting to PulseAudio fails. 2026-01-17 08:50:10 +10:00
David Reid 7dae981ad5 Add some helpers for resetting a sound after a fade and stop.
Public issue https://github.com/mackron/miniaudio/issues/714
2026-01-17 07:32:18 +10:00
David Reid 1d6fe3efc9 JACK: Refactoring.
* Devices are now enumerated properly. It will enumerate input or output
  ports depending on the device type, and then group by the client name.
  The client name will be considered a "device" from the perspective of
  miniaudio. The number of local ports will be the channel count.

* The port auto-connection process is now done in init() rather than
  start(). I do not know why this was ever in start() in the first
  place.

* Port auto-connection will now be restricted to the client ports. The
  old system would connect across multiple clients which is just a
  nonsensical way of doing. If more ports are requested than are
  available on the client, the excess ports will just not be connected.
2026-01-16 18:26:34 +10:00
David Reid 72ed924fb5 PipeWire: Make native data format detection more specific. 2026-01-16 15:08:57 +10:00
David Reid b29541068c Minor changes to deviceio output. 2026-01-16 15:08:19 +10:00
David Reid c3dcf0d1db Update tests readme. 2026-01-16 06:42:44 +10:00
David Reid 3a052ada0d Fix a compilation warning. 2026-01-16 06:42:23 +10:00
David Reid 7ac50d477e Clean up some old code relating to the new device info system. 2026-01-16 06:26:01 +10:00
David Reid a0b24f6d3d audio(4): Fix a compilation error on OpenBSD. 2026-01-16 06:20:49 +10:00
David Reid ab06cb340f WASAPI: Update to new device info system. 2026-01-15 19:41:27 +10:00
David Reid b5213e0265 DirectSound: Update to new device info system. 2026-01-15 19:41:10 +10:00
David Reid 48cc2d3cf1 WinMM: Update to new device info system. 2026-01-15 19:40:32 +10:00
David Reid 01b69f7323 Core Audio: Update to the new device info system. 2026-01-15 19:06:41 +10:00
David Reid 17a3941c94 sndio: Update to the new device info system. 2026-01-15 17:33:11 +10:00
David Reid 06394e8e90 audio(4): Update to the new device info system. 2026-01-15 17:21:08 +10:00
David Reid e0e26b5535 OSS: Update to the new device info system. 2026-01-15 16:54:02 +10:00
David Reid cfed5b09a6 Web Audio: Update to the new device info system. 2026-01-15 16:27:06 +10:00
David Reid 131fa04cd6 OpenSL: Update to the new device info system. 2026-01-15 16:06:22 +10:00
David Reid 838174a418 AAudio: Update to the new device info system. 2026-01-15 16:06:08 +10:00
David Reid 878cf797cc JACK: Update to the new device info system. 2026-01-15 15:49:47 +10:00
David Reid 3a84c31cff ALSA: Update to the new device info system. 2026-01-15 15:14:46 +10:00
David Reid 3d697c27e6 PulseAudio: Update to the new device info system. 2026-01-15 14:54:01 +10:00
David Reid 6c1206931b Null: Updates to the new device info system. 2026-01-15 14:50:03 +10:00
David Reid 08d4c60bc3 SDL2: Update to the new device info system. 2026-01-15 14:47:20 +10:00
David Reid 91ddce1d17 PipeWire: Update to the new device info system. 2026-01-15 14:42:14 +10:00
David Reid 434bfc6a0b Set up some infrastructure for the new device info structure. 2026-01-15 14:24:41 +10:00
David Reid 461ced3280 JACK: Minor memory allocation optimization. 2026-01-15 12:34:20 +10:00
David Reid ce4f9aab39 JACK: Optimize some buffer management. 2026-01-15 12:23:34 +10:00
David Reid e69049e6ac Fix a bug with duplex mode in the deviceio test. 2026-01-15 12:01:57 +10:00
David Reid 2b49a81e87 JACK: Optimization for multi-threaded mode.
In multi-threaded mode we can just fire the miniaudio data callback
straight from the JACK data callback instead of going through our
helper.
2026-01-15 11:38:10 +10:00
David Reid e7d94638cf Wake up the backend when uninitializing. 2026-01-15 11:31:53 +10:00
David Reid dc7f5286ca ALSA: A pedantic optimization to avoid a malloc. 2026-01-15 10:45:36 +10:00
David Reid 1509e9b375 Fix an error with the --enumerate-only option in deviceio test. 2026-01-15 10:45:11 +10:00
David Reid b4a26d21dd audio(4): Fix enumeration on OpenBSD. 2026-01-15 10:23:04 +10:00
David Reid 6c0c35387d Add --only-enumerate option to deviceio test. 2026-01-15 09:09:55 +10:00
David Reid cbe640efbf audio(4): Drain the device when stopping on OpenBSD. 2026-01-15 09:01:33 +10:00
David Reid c440b4b670 audio(4): Fix starting and stopping on OpenBSD. 2026-01-14 17:44:56 +10:00
David Reid c1831a677f sndio: Minor restructure for consistency with other backends. 2026-01-14 16:46:42 +10:00
David Reid f15717ba3e OSS: Drain the playback device when stopping. 2026-01-14 16:39:52 +10:00
David Reid 90aa390a08 OSS: Optimize memory allocations during device initialization. 2026-01-14 16:31:29 +10:00
David Reid 2a2eb5bae4 audio(4): Optimize memory allocations during device initialization. 2026-01-14 16:11:01 +10:00
David Reid f0d84c6895 sndio: Fix a playback error. 2026-01-14 15:17:49 +10:00
David Reid 5ad9e784f9 audio(4): Fix a playback error. 2026-01-14 15:14:36 +10:00
David Reid 2fa51de5cf OSS: Fix a playback error. 2026-01-14 15:09:17 +10:00
David Reid 77abc8f69c ALSA: Optimize some memory allocation during device initialization.
This reduces memory allocations down from 3 to 1 (one malloc + realloc).
2026-01-14 15:04:48 +10:00
David Reid 7c3f845658 ALSA: Fix a playback data throughput error.
This has the data callback handling and the ALSA writing operation in
the wrong order.

I have no idea how I missed this...
2026-01-14 15:00:54 +10:00
David Reid 86a17a30e8 sndio: Optimize some memory management.
This just combines some memory allocations into a single one.
2026-01-14 13:34:55 +10:00
David Reid 463200d3cc OSS: Fix a bug with playback buffer priming. 2026-01-14 11:45:05 +10:00
David Reid 6ca4ddefe6 audio(4): Fix a bug with playback buffer priming. 2026-01-14 11:44:42 +10:00
David Reid 9af808b800 sndio: Prime the playback buffer when starting the device. 2026-01-14 11:43:54 +10:00
David Reid d68dd6c433 sndio: Use a shared intermediary buffer for duplex mode.
Previously this was using two separate buffers for the capture and
playback sides, but it's not necessary to use two separate buffers
because they'll never be used simultaneously. It can therefore be
optimized by allocating a single buffer that is big enough for either of
them.
2026-01-14 11:28:50 +10:00
David Reid d61eaa885e sndio: Improvements to blocking step logic. 2026-01-14 11:09:30 +10:00
David Reid 1438d96b38 audio(4): Prime the playback buffer when starting the device. 2026-01-14 10:11:48 +10:00
David Reid 26ce86a4cf audio(4): Improvements to blocking waiting logic. 2026-01-14 10:09:57 +10:00
David Reid 199cc4a189 Minor cleanup. 2026-01-14 10:09:03 +10:00
David Reid 01fd4b7662 OSS: Prime the playback buffer when starting the device. 2026-01-14 08:06:46 +10:00
David Reid bdde9c46b0 OSS: Improvements to blocking waiting logic.
This now does a single select() rather than two separate ones for
capture and playback. This should make duplex mode better.
2026-01-14 07:36:41 +10:00
David Reid 3277d995a3 ALSA: Reduce the size of a memory allocation. 2026-01-13 17:34:52 +10:00
David Reid b90341fc83 Small improvement to ma_linear_resampler_adjust_timer_for_new_rate(). 2026-01-13 15:51:33 +10:00
David Reid dd3cce3061 Add some infrastructure to the Emscripten test for testing SDL2. 2026-01-13 13:05:30 +10:00
David Reid e5743d666c SDL2: Improve Emscripten support by limiting the period size.
Setting the period size to something too small results in glitching so
this commit will clamp it to a minimum size on the Emscripten build.
2026-01-13 13:03:15 +10:00
David Reid 629e751d56 Fix the SDL2 backend for Emscripten. 2026-01-13 12:56:07 +10:00
David Reid cdd3d39ac0 Emscripten: Force MA_NO_THREADING if not compiling with pthread support. 2026-01-13 12:53:30 +10:00
David Reid b3f32437b8 Update an out of date comment. 2026-01-13 12:46:51 +10:00
David Reid 2b50979bd3 Minor reorganization. 2026-01-13 12:44:35 +10:00
David Reid 2ead596562 PulseAudio: Revert a PipeWire compatibility workaround.
This is no longer needed since a native PipeWire backend is about to be
integrated.
2026-01-13 11:22:46 +10:00
David Reid 27f7eeba91 Change default period count from 3 to 2. 2026-01-13 11:12:08 +10:00
David Reid 51f8235bef Force a minimum of two periods for duplex mode.
Not all backends have the notion of a period, but for those that do this
can make it a bit more reliable.
2026-01-13 10:47:32 +10:00
David Reid d1316a58cf ALSA: Ensure at least two periods are used for duplex mode. 2026-01-13 10:37:25 +10:00
David Reid 98a23f9551 ALSA: Fix an error with underrun recovery.
When an underrun occurs the buffer needs to be filled with data before
restarting or else snd_pcm_start() will return EPIPE.
2026-01-13 10:36:30 +10:00
David Reid 55cdba084d ALSA: Fix an error when priming the playback buffer.
There was a possibility that this process could result in
snd_pcm_writei() getting stuck because the frame count is not being
clamped to the buffer size properly.
2026-01-13 10:22:39 +10:00
David Reid 92a58fae11 ALSA: Fix some typos. 2026-01-13 09:49:10 +10:00
David Reid 1adad94ef8 Try fixing the Emscripten build. 2026-01-12 19:51:40 +10:00
David Reid 994ae48feb CMake: Minor output cleanup. 2026-01-12 19:39:15 +10:00
David Reid c50cc675c9 CMake: Try fixing the build for Clang and FORCE_C89. 2026-01-12 19:38:33 +10:00
David Reid 676b84cad1 ALSA: Remove some out of date comments. 2026-01-12 19:25:02 +10:00
David Reid cd521f9440 Comment out a printf() debugging statement. 2026-01-12 17:45:48 +10:00
David Reid 26029b2250 Add a basic engine test.
This is currently just for some specific tests, but might be expanded
later to be something a bit more practical.
2026-01-12 17:30:05 +10:00
David Reid 628f2c1640 Add a debugging VFS. 2026-01-12 17:29:18 +10:00
David Reid 82ec45e349 CMake: Fix an error with libvorbis. 2026-01-12 17:25:33 +10:00
David Reid f028249019 Add a command line option for exclusive mode to deviceio test. 2026-01-12 15:54:27 +10:00
David Reid 94a79c9cda Fix a bug in ma_calculate_frame_count_after_resampling().
This now uses the same calculation that the linear resampler uses.

Public issue https://github.com/mackron/miniaudio/issues/760
2026-01-12 12:43:09 +10:00
David Reid 9a1b551658 WASAPI: Fix an error with rerouting.
When WASAPI reports `AUDCLNT_E_DEVICE_INVALIDATED` miniaudio will
attempt to reinitialize the device and continue processing. Prior to
this commit the reinit process would use NULL as the device ID which
is incorrect when an explicit device was requested.

With this commit the reinit process will correctly pass in the ID of the
device that it was initialized with. In practice, this will mean the
device will be put into an errored state if it is unplugged.
2026-01-12 10:57:09 +10:00
David Reid e9abcccd86 Update fs. 2026-01-12 07:11:43 +10:00
David Reid c649733b34 WASAPI: Remove old unused code. 2026-01-12 06:54:35 +10:00
David Reid 4d583a4508 WASAPI: Fix device enumeration. 2026-01-12 06:37:14 +10:00
David Reid 6a3d5fde05 Remove some line breaks from log entries. 2026-01-12 06:36:14 +10:00
David Reid df88373cd0 Silence a warning. 2026-01-11 20:21:19 +10:00
David Reid d4631208f9 WASAPI: Clean up a memory leak. 2026-01-11 20:15:51 +10:00
David Reid 8709bef653 WASAPI: Improvement to buffer size negotiation for exclusive mode. 2026-01-11 20:02:47 +10:00
David Reid de3946c9ec WASAPI: Properly recover from overruns in duplex mode. 2026-01-11 17:54:37 +10:00
David Reid 12256314a9 WASAPI: Add a debug log message. 2026-01-11 17:19:58 +10:00
David Reid 39abeb167e WASAPI: Prime the playback buffer before starting. 2026-01-11 17:03:55 +10:00
David Reid 3debb4a20f ALSA: Add some missing snd_pcm_state_t values. 2026-01-11 07:23:14 +10:00
David Reid c4c484794b ALSA: Add a timeout to poll().
I don't trust there won't be edge cases that result in this getting
stuck forever.
2026-01-11 07:22:44 +10:00
David Reid adce75cba6 Show device IDs in the deviceio test. 2026-01-11 07:06:23 +10:00
David Reid 660d5f6f9e ASLA: Fall back to NAME for the device description when DESC is empty. 2026-01-10 13:39:59 +10:00
David Reid 96c4b105b0 ALSA: Use more descriptive error messages. 2026-01-10 13:07:10 +10:00
David Reid 9a4318997b ALSA: Whitespace.
This was off by one space and it was annoying me!
2026-01-10 12:45:30 +10:00
David Reid 280c118dfd ALSA: Try improving native channel count detection heuristics. 2026-01-10 12:41:19 +10:00
David Reid 999c16d834 Merge branch 'dev' into dev-0.12 2026-01-10 12:14:40 +10:00
David Reid 88776cedb7 Whitespace. 2026-01-10 08:49:12 +10:00
spevnev e00cee2af1 Cast tv_sec to 64-bit int before converting 2026-01-10 08:47:03 +10:00
David Reid 760765ec93 ALSA: Use SND_PCM_NONBLOCK when enumerating devices. 2026-01-10 08:10:21 +10:00
David Reid fb132046d4 Add command line switches for periods and period sizes to deviceio test. 2026-01-09 19:21:24 +10:00
David Reid 4bed03a65e Fix a bug in ma_prev_power_of_2(). 2026-01-09 18:51:02 +10:00
David Reid e9eccf49a3 ALSA: Improvements buffer size negotiation.
This makes it so period sizes are a power of 2, which in turns makes it
so the value set by `snd_pcm_sw_params_set_avail_min()` is also a power
of 2. This is inline with a suggestion by the ALSA documentation, and
seems to have an actual positive impact in practice with my testing as
well.
2026-01-09 15:37:05 +10:00
David Reid 46adfae3bb Update the deviceio test with a few more command line switches. 2026-01-09 13:38:49 +10:00
David Reid 948967dcbb ALSA: Change to device enumeration.
In order to get detailed information about a device, that is supported
formats, channels and sample rates, the PCM needs to be opened. This
can fail sometimes, in which case enumeration would previously just not
enumerate the device.

This is OK, but it creates issues. The first is that the enumerated
devices will not be consistent with what's reported by `aplay`. The
other is that when a hardware device is opened, iteration will not
include that device because it'll be opened in exclusive mode. This
creates a practical issue when trying to get the name of an already
opened device.

This commit makes it so that these devices will still be enumerated,
only they'll be missing detailed format, channels and rate information.
2026-01-09 13:38:07 +10:00
David Reid 224a4c9d3a ALSA: Fix a crash when failing to initialize a device. 2026-01-09 13:24:32 +10:00
David Reid 4e28636ed8 Update deviceio test to allow explicit channel count selection. 2026-01-09 13:23:27 +10:00
David Reid 25dcbcb9cd ALSA: Changes to start/stop behaviour for playback devices.
* A start threshold is no longer used meaning an explicit call to
  snd_pcm_start() is required, which was happening already anyway.

* The stop threshold is no longer used.

* Stopping the playback side of device now drains it.

* Starting a device will now pre-fill the playback side with silence
  before starting it which should hopefully avoid an immediate xrun.
2026-01-09 12:55:03 +10:00
David Reid e1058d1eea ALSA: Simplify device enumeration.
This removes the old useVerboseDeviceEnumeration context config option.
2026-01-09 06:17:44 +10:00
David Reid 0b4a861d94 ALSA: Experiment with snd_pcm_link(). 2026-01-08 17:41:58 +10:00
David Reid 597654dcf6 Refactoring to the ALSA backend.
This is intended to address the following issues:

  - Stepping the device now correctly polls both the capture and
    playback side in one call in duplex mode. Prior to this commit it
    would wait separately for each side which was totally incorrect.

  - The initialization process has been simplified and made more robust
    when trying to initialize the default device.

More work still to be done.
2026-01-08 17:20:00 +10:00
David Reid 0129fa3b2a ALSA: Fix a bug where a default device is not detected properly. 2026-01-08 10:52:37 +10:00
David Reid e4a6c348d5 ALSA: Try making channel selection a bit more robust.
Public issues
https://github.com/mackron/miniaudio/issues/775
https://github.com/mackron/miniaudio/issues/945
2026-01-08 09:56:40 +10:00
David Reid 01fe5fe416 Merge branch 'dev' into dev-0.12 2026-01-07 18:20:48 +10:00
David Reid 5ef2e1ec57 Update fs. 2026-01-07 18:19:59 +10:00
David Reid ee8a65bed9 Update dr_libs. 2026-01-07 18:13:08 +10:00
David Reid 44b847fbf8 Update fs. 2026-01-07 18:10:39 +10:00
David Reid ca6361db5e PipeWire: Fix a bug with channel map negotiation. 2026-01-07 16:57:31 +10:00
David Reid 3b0391ad44 Silence an assigned-but-not-used warning. 2026-01-07 16:43:49 +10:00
David Reid 61f1f8b457 Tighten up ma_context_get_backend_info(). 2026-01-07 16:37:59 +10:00
David Reid 5095548174 Merge branch 'dev' into dev-0.12 2026-01-07 15:37:03 +10:00
David Reid 5f3de510b2 Make ma_is_spatial_channel_position() a bit more robust.
This makes it less error prone when new channel positions are added to
the enum.
2026-01-07 12:36:18 +10:00
David Reid 53116ad6da Minor change to an enum to make it less error prone. 2026-01-07 12:31:03 +10:00
David Reid b83869eb09 Update the spatializer to require a listener when processing. 2026-01-07 12:24:30 +10:00
David Reid bedfd053cb Fix a bug in the gainer where a null pointer can be offset. 2026-01-07 12:13:18 +10:00
David Reid 32cc6d53cd Fix a possible null pointer dereference. 2026-01-07 12:08:20 +10:00
David Reid bd26454c26 Fix a possible null pointer dereference. 2026-01-07 12:07:25 +10:00
David Reid d791c16d8d Remove some redundant error checks. 2026-01-07 12:05:57 +10:00
David Reid 86d5f669e4 PipeWire: Comment out an unused function. 2026-01-07 11:10:21 +10:00
David Reid 4d9a66289d Merge branch 'dev' into dev-0.12 2026-01-07 10:33:30 +10:00
David Reid 8c4535c6c5 Fix a bug with sound node processing. 2026-01-07 10:33:13 +10:00
David Reid 4bd8eb0aa0 WASAPI: Handle AUDCLNT_E_DEVICE_INVALIDATED.
When this is detected the device will be reinitialized. If this fails
the device will be put into an errored state and will become unusable.
2026-01-07 10:23:39 +10:00
David Reid 882d7329f9 Add getter functions for backend vtables. 2026-01-07 09:22:43 +10:00
David Reid fd74d344c7 Merge branch 'dev' into dev-0.12 2026-01-06 19:32:41 +10:00
David Reid 27d2d6ac87 Add support for custom resamplers to sounds.
Public issue https://github.com/mackron/miniaudio/issues/965
2026-01-06 19:25:32 +10:00
David Reid 919a01ae4a Use ma_resampler instead of ma_linear_resampler for sound nodes.
This is infrastructure work for supporting custom resamplers for the
pitching and Doppler effects for sounds.

Public issue https://github.com/mackron/miniaudio/issues/965
2026-01-06 18:37:05 +10:00
David Reid 065e6eadb5 Minor code rearrangement. 2026-01-06 18:18:16 +10:00
caturria 962d11b4ce Resource manager can now have a custom resampler. 2026-01-06 18:13:57 +10:00
David Reid 5f3fc86f89 Merge branch 'dev' into dev-0.12 2026-01-06 16:29:55 +10:00
David Reid b62249ceaf Fix an infinite loop bug. 2026-01-06 16:29:42 +10:00
David Reid 25a137752a Merge branch 'dev' into dev-0.12 2026-01-06 16:23:32 +10:00
David Reid 525b04db04 Make ma_sound node processing a bit more robust.
This removes the dependency on querying the required input frame count
from the resampler. This should in turn enable future work to support
custom resamplers.

Public issue https://github.com/mackron/miniaudio/issues/965
2026-01-06 16:11:35 +10:00
David Reid fd89763b2a Merge branch 'dev' into dev-0.12 2026-01-06 15:18:07 +10:00
David Reid e93e1dbba1 Set up some infrastructure for improvements to ma_sound processing. 2026-01-06 15:15:43 +10:00
David Reid 97b2db8c42 Merge branch 'dev' into dev-0.12 2026-01-06 13:21:06 +10:00
David Reid 111d620c63 Fix some node timing errors.
This commit fixes a bug relating to nodes with a scheduled start/stop
time. Whether or not the node is considered started or stopped is being
incorrectly reported by `ma_node_get_state_by_time_range()`.

Another issue is fixed in `ma_node_read_pcm_frames()`, which is related
to the fix above, where the frame count can underflow thereby resulting
in a crash.

Public issue https://github.com/mackron/miniaudio/issues/969
2026-01-06 13:05:40 +10:00
David Reid 3b4e87848b Fix a typo. 2026-01-06 10:11:16 +10:00
David Reid 35acd7a65b Relocate the sound end callback to after the sound is stopped.
Public issue https://github.com/mackron/miniaudio/issues/1013
2026-01-06 10:05:36 +10:00
David Reid 83ef69a79a CMake: Update an option description. 2026-01-06 09:07:43 +10:00
David Reid 524e3c2d06 Merge branch 'dev' into dev-0.12 2026-01-06 08:55:25 +10:00
David Reid 92fb865387 Update some comments. 2026-01-06 08:06:49 +10:00
François Hautier c44ec3f46a Better comment 2026-01-06 07:56:41 +10:00
François Hautier 8c3b213a7c WebAudio: Try to fix a startup noise 2026-01-06 07:56:41 +10:00
David Reid fe31274720 CMake: Add support for MA_NO_THREADING to deviceio test. 2026-01-05 20:19:05 +10:00
David Reid f3dfc97c2c Fix a format selection bug in the PipeWire backend.
This was incorrectly choosing the wrong endian-specific sample format
when the endian.h header was not included. This commit switches to
runtime endian detection.
2026-01-05 20:18:35 +10:00
David Reid 6d906215f1 Move ma_is_little_endian() / ma_is_big_endian() into the public section. 2026-01-05 20:07:43 +10:00
David Reid 5c38d39141 Move architecture detection to the public section.
This can be useful for custom backends.
2026-01-05 20:06:49 +10:00
David Reid a29a3b81f3 Fix a crash when uninitializing a device in single-threaded mode. 2026-01-05 16:47:11 +10:00
David Reid d32cd3f843 Update dr_libs. 2026-01-05 16:08:58 +10:00
David Reid 4aa3d531b4 Fix a compilation warning. 2026-01-05 15:54:35 +10:00
David Reid bfe4b07da4 PipeWire: Fix some warnings. 2026-01-05 14:44:57 +10:00
David Reid 116a06fac7 Add ma_device_get_user_data(). 2026-01-05 09:41:20 +10:00
David Reid 0d94eb7da9 Have ma_node_graph_init() fail if a config is not specified.
The config is required for the channel count.
2026-01-05 09:37:46 +10:00
David Reid d3270d8bf1 Remove some unused members of ma_job. 2026-01-05 06:33:19 +10:00
David Reid 4e827fa977 Use consistent nomenclature for device backend wakeup callbacks.
It should be "wakeup" instead of "wake".
2026-01-05 06:05:17 +10:00
David Reid d5ce388d00 Merge branch 'dev' into dev-0.12 2026-01-04 15:07:52 +10:00
David Reid 88797e9dee Fix a double-uninit error with decoders.
Public issue https://github.com/mackron/miniaudio/issues/1080
2026-01-04 15:03:47 +10:00
David Reid ad85d0c3c4 Update dr_libs. 2026-01-04 14:37:14 +10:00
David Reid b717d19099 Update dr_libs. 2026-01-04 14:35:01 +10:00
David Reid 28d071766d CMake: Remove some superfluous error messages. 2026-01-03 15:06:50 +10:00
David Reid 70a3690499 CMake: Clean up the handling of SteamAudio. 2026-01-03 15:03:05 +10:00
David Reid 9128cbe35b CMake: Clean up the handling of libopus and libvorbis. 2026-01-03 15:02:21 +10:00
David Reid 44b39fe097 Rename SDL2 backend source files. 2026-01-03 13:56:40 +10:00
David Reid 3df99ce51d Rename the SDL backend to SDL2.
This distinction is needed because we'll be doing an SDL3 backend in the
future.
2026-01-03 13:50:48 +10:00
David Reid 23c3277754 OSS: Remove some redundant device status checks. 2026-01-03 13:39:38 +10:00
David Reid 66887aa114 Core Audio: Stop directly changing the device status to stopped. 2026-01-03 13:37:16 +10:00
David Reid f19d26a012 Win32: Clean up some runtime linking code. 2026-01-03 11:36:32 +10:00
David Reid 67389d29ec Remove an unnecessary COM initialization routine. 2026-01-03 11:20:10 +10:00
David Reid c314eb0fa5 Add a new errored status.
This allows a backend to put the device into an errored state to
indicate that it is no longer usable and needs to be reinitialized.
2026-01-03 11:01:46 +10:00
David Reid c64a5c7457 A rule change for stop notifications. 2026-01-03 07:16:26 +10:00
David Reid e81a11e725 DirectSound: Stop logging an error when stopping.
This would result in an error being logged when a capture device is
unplugged.
2026-01-03 07:16:09 +10:00
David Reid 3b2af39864 DirectSound: Fix an error when stopping the device.
If the capture side fails to stop it would result in the function
returning early thereby not giving the playback side a chance to also
stop which would result in a looping glitch.
2026-01-03 06:42:57 +10:00
David Reid 6048a9a73e Fix CMake script for the Emscripten build. 2026-01-02 17:49:26 +10:00
David Reid 53b4d6a4a0 Update build instructions for Emscripten example. 2026-01-02 15:51:35 +10:00
David Reid e78a86d30b Cleanup of the CMake script. 2026-01-02 15:50:15 +10:00
David Reid 46b2cdd0cf Improve SDL2 integration in the CMake script. 2026-01-02 14:44:26 +10:00
David Reid ce41f6cfc9 SDL2: Fix a compilation error due.
This is due to calling an internal miniaudio function.
2026-01-02 14:14:10 +10:00
David Reid da764a5a28 PipeWire: Fix an error when runtime linking is disabled. 2026-01-02 12:34:42 +10:00
David Reid e9fad62f44 Improve default device enumeration with the SDL2 backend. 2026-01-01 19:36:37 +10:00
David Reid d0392288c4 Remove an old device job thread object.
This was used by AAudio for dealing with rerouting, but with the new
backend architecture it is no longer needed.
2026-01-01 19:04:49 +10:00
David Reid a5ef023cee Remove some global variables.
These are replaced by functions. The problem is that declaring a
variable as MA_API will result in errors when it's declared as static.
2026-01-01 18:39:43 +10:00
David Reid 45c7a64299 Add support for device selection the deviceio test. 2026-01-01 15:58:37 +10:00
David Reid 1d308f69bc Add a basic single-threaded test to deviceio test. 2026-01-01 14:52:01 +10:00
David Reid db6bc371eb WebAudio: Remove dependency on -sASYNCIFY for the Audio Worklets path.
Public issue https://github.com/mackron/miniaudio/issues/863
2026-01-01 13:10:15 +10:00
David Reid 1f2e59548b Web Audio: Minor change for multi-threaded mode. 2026-01-01 10:47:39 +10:00
David Reid edc44cbcaa PipeWire: Optimization to multi-threaded mode. 2026-01-01 09:38:21 +10:00
David Reid 1d0a598485 Rename MA_THREADING_MODE_MULTITHREADED.
This not having an underscore while SINGLE_THREADED did was driving me
crazy.
2026-01-01 09:19:43 +10:00
David Reid ff9ff16341 CMake: Remove is_backend_enabled()
This is no longer necessary since we can just use MINIAUDIO_NO_[BACKEND]
as the standard way to check if a backend is enabled.
2025-12-31 20:25:11 +10:00
David Reid 7cbb204abb CMake: Experiment to make it easier to check if a backend is enabled. 2025-12-31 20:17:46 +10:00
David Reid 0e6eaae06b CMake: Don't compile the the PipeWire backend if device IO is disabled. 2025-12-31 19:34:58 +10:00
David Reid c6429bb866 Try fixing a CMake error. 2025-12-31 19:24:16 +10:00
David Reid 552cf613b3 PipeWire: Add support for disabling runtime linking. 2025-12-31 18:19:52 +10:00
David Reid 114b9594c1 PipeWire: Finish work on removing the SPA dependency. 2025-12-31 18:18:17 +10:00
David Reid e1dfc9d77f PipeWire: Remove dependency on spa_format_audio_raw_parse(). 2025-12-31 13:30:49 +10:00
David Reid d2a93567df PipeWire: Remove dependency on spa_pod_builder. 2025-12-31 09:04:00 +10:00
David Reid c67b29de38 PipeWire: More work on removing the SPA dependency. 2025-12-30 18:00:07 +10:00
David Reid 9ebc3efa95 PipeWire: First tentative step towards removing the SPA dependency. 2025-12-30 16:40:16 +10:00
David Reid e086de4eb3 Web Audio: Get single-threaded mode working to spec. 2025-12-30 14:54:36 +10:00
David Reid 07af5ea6f7 Remove an unnecessary function.
The naming of this function is now a bit misleading with the new changes
to the backend system.
2025-12-30 10:32:09 +10:00
David Reid f073a40a1c Fix some warnings for the PipeWire backend. 2025-12-30 10:18:56 +10:00
David Reid d04c1985d6 Fix an error with the Web Audio backend. 2025-12-30 10:13:44 +10:00
David Reid cc4db76c26 Minor update to documentation for the single-threaded example. 2025-12-30 09:32:11 +10:00
David Reid a16756ec3b Add an example for single-threaded mode. 2025-12-30 09:26:38 +10:00
David Reid 350cc0be1f Fix a bug with starting and stopping a device in single-threaded mode. 2025-12-30 09:25:37 +10:00
David Reid 7483e55aee Add ma_device_get_threading_mode(). 2025-12-29 19:39:29 +10:00
David Reid 0f4bc3ca9a Add support for configuring the threading mode for a device. 2025-12-29 19:12:34 +10:00
David Reid b0894c34b1 Add ma_device_step() in preparation for single-threaded mode. 2025-12-29 19:02:18 +10:00
David Reid fa2c1b49b3 Remove unused callbacks from the device backend system. 2025-12-29 18:02:51 +10:00
David Reid 3d79043d7d WASAPI: Move thread characteristic stuff from start/stop to init/uninit.
All audio functions now run from the same thread, so this feels like a
more appropriate location for these functions to me.
2025-12-29 15:43:27 +10:00
David Reid 9df9b19a59 WASAPI: Refactoring of the device rerouting system.
This now performs rerouting from the step callback which is always done
from the main thread. This should address issues relating to COM
initialization and some rerouting race conditions.

There is a very slight change in behaviour here. Previously when a
device is detached (such as headphones being unplugged), the device
would actually be stopped proper, but now it is no longer stopped and
the stop notification is not fired when the device is deactivated.
2025-12-29 15:32:23 +10:00
David Reid d72811c127 WASAPI: Remove some unnecessary variables. 2025-12-29 10:35:00 +10:00
David Reid aaf868f92a WASAPI: Refactoring for the new backend architecture. 2025-12-29 10:31:25 +10:00
David Reid d0b8b07c49 DirectSound: Refactoring for the new backend architecture. 2025-12-28 19:55:59 +10:00
David Reid a4406088f4 WinMM: Refactoring for the new backend architecture. 2025-12-28 11:07:55 +10:00
David Reid c6d44b67e1 PulseAudio: Stop firing the stop callback in response to a suspension. 2025-12-26 19:26:43 +10:00
David Reid 521c224b02 JACK: Stop double-posting the stop callback. 2025-12-26 19:16:55 +10:00
David Reid 5d1c089d15 OpenSL: Stop double-posting the stop callback. 2025-12-26 19:16:43 +10:00
David Reid f270f515e4 AAudio: Stop double-posting the stop callback. 2025-12-26 19:15:03 +10:00
David Reid d10526fb3a Core Audio: Stop double-posting stop callbacks. 2025-12-26 19:04:10 +10:00
David Reid ef59b93a4d Fix a bug with asynchronous style backends.
This was resulting in glitching in duplex mode when internal sample
rates differ.
2025-12-26 18:36:11 +10:00
David Reid b6799e92e0 Update gitignore. 2025-12-26 12:03:50 +10:00
David Reid f932077982 Core Audio: Refactoring for the new backend architecture. 2025-12-26 12:02:57 +10:00
David Reid 5296384289 ALSA: Fix an error with non-blocking mode. 2025-12-25 11:29:10 +10:00
David Reid 4cb0851ccc sndio: Refactoring for the new backend architecture. 2025-12-25 11:28:14 +10:00
David Reid 7cb349fc60 audio(4): Refactoring for the new backend architecture. 2025-12-24 19:16:39 +10:00
David Reid 8c735da4eb OSS: Refactoring for the new backend architecture. 2025-12-24 17:43:20 +10:00
David Reid a2c7e697d1 ALSA: Refactoring for the new backend architecture. 2025-12-24 14:35:06 +10:00
David Reid 61fd14955a PulseAudio: Minor restructure in preparation for the new backend system. 2025-12-24 06:31:40 +10:00
David Reid 5eafc0e9c1 Merge branch 'dev' into dev-0.12 2025-12-23 19:07:52 +10:00
David Reid 364844231d Fix an bug with error recovery when failing to initialize a decoder.
Public issue https://github.com/mackron/miniaudio/issues/1080
2025-12-23 19:04:53 +10:00
David Reid 631bc047d8 Minor C89 compatibility fix. 2025-12-23 14:47:48 +10:00
David Reid bd35758b87 AAudio: Try fixing a glitch when starting a device.
This is only happening when the device is started after being stopped.
The initial start does not glitch.
2025-12-23 13:08:39 +10:00
David Reid 737fb79aae AAudio: Simplify device rerouting.
This now does rerouting in the step which runs on the audio thread,
which is the same thread that init, uininit, start and stop runs on, and
no longer goes through the job system.
2025-12-23 10:17:38 +10:00
David Reid fd6ee4711b Generalize some backend stepping logic for the new backend architecture. 2025-12-22 17:10:03 +10:00
David Reid e6922b360c Minor refactor the AAudio backend. 2025-12-22 13:41:37 +10:00
David Reid 1bb2180005 Minor refactor the OpenSL backend. 2025-12-22 11:50:53 +10:00
David Reid e598eb7fe6 Minor refactor to the null backend. 2025-12-22 11:50:44 +10:00
David Reid 9364a36f04 Whitespace. 2025-12-20 18:22:00 +10:00
David Reid ad7084c9ef SDL2: Add wakeup callback. 2025-12-20 18:21:31 +10:00
David Reid 2f18c53771 PipeWire: Fix a bug with non-blocking mode. 2025-12-20 14:27:29 +10:00
David Reid e97ab1ef32 Another pass on the PipeWire backend.
This removes the "wait" concept and replaces it with an extra parameter
for the step() callback for the blocking mode, which can be blocking or
non-blocking.

This also implements the wake() callback for waking up from a blocking
step.
2025-12-20 12:40:10 +10:00
David Reid d6487d0569 Make ma_blocking_mode public in preparation for some changes. 2025-12-17 07:34:51 +10:00
David Reid 6931d7d159 Cleanup. 2025-12-17 07:34:31 +10:00
David Reid 38a543caa4 Try fixing some glitching with the AAudio backend. 2025-12-17 06:46:50 +10:00
David Reid 60d757a226 Return result codes from step/wait/loop callbacks. 2025-12-16 18:08:32 +10:00
David Reid df79b33aeb Update the JACK backend to the new backend architecture. 2025-12-16 18:02:13 +10:00
David Reid 9d2c943862 Update OpenSL backend to the new backend architecture. 2025-12-14 17:34:07 +10:00
David Reid 4151c05973 Update AAudio backend to the new backend architecture. 2025-12-14 16:08:44 +10:00
David Reid 442e9f4db8 Merge branch 'dev' into dev-0.12 2025-12-14 06:48:16 +10:00
David Reid 9ea38e9f3a Update dr_wav and dr_flac. 2025-12-14 06:38:32 +10:00
David Reid af19bdb6ff Fix a bug where MA_NO_DECODING would disable the WAV encoder.
Public issue https://github.com/mackron/miniaudio/issues/1076
2025-12-13 17:26:06 +10:00
Marty f513f462df cmake: add public include directories for extra decoders
It seems that when linking, for example, `miniaudio_libvorbis`, it doesn't add the necessary include directories for use in the project. This is because the include directories were not being added to the target at all.

Here, I fix that by adding them with PUBLIC scope.
2025-12-12 19:12:13 +10:00
David Reid 4f4f93a91b Finish first pass of the PipeWire backend. 2025-12-12 14:39:03 +10:00
David Reid 0c41e62827 Make timing functions public for the benefit of custom backends. 2025-12-04 08:52:17 +10:00
David Reid 2f759f7b62 Convert the SDL2 backend to the new backend architecture. 2025-12-01 10:46:59 +10:00
David Reid 97441567fa Fix a typo. 2025-12-01 05:58:55 +10:00
David Reid 248f27b6e2 Remove an unnecessary comment. 2025-11-30 19:47:50 +10:00
David Reid fc677beac9 Add SDL2 and PipeWire logs to deviceio test. 2025-11-30 19:47:33 +10:00
David Reid ac7a8e691b Fix C++ build of the PipeWire backend. 2025-11-30 19:46:52 +10:00
David Reid 1c5e2400ce Fix an error when stopping a device.
Public issue https://github.com/mackron/miniaudio/pull/1055
2025-11-30 06:34:04 +10:00
Guillaume Prieur 1f717d6ba7 Remove object references in coreaudio context state that are for apple desktop only in other builds 2025-11-30 06:02:48 +10:00
Guillaume Prieur b41a7eabe7 Add missing result declaration 2025-11-30 06:02:10 +10:00
David Reid 477020295a Merge branch 'dev' into dev-0.12 2025-11-30 06:01:16 +10:00
David Reid 8c1dc255db Remove Cosmopolitan pre-processing checks.
https://github.com/mackron/miniaudio/pull/1070
2025-11-30 05:39:49 +10:00
David Delassus dbf8e114f9 CMake: make install directives optional 2025-11-29 14:42:38 +10:00
Louis du Verdier 6d65be5e0e Do not set POSIX thread scheduler policy on systems reporting that they do not support it 2025-11-29 14:20:45 +10:00
Kjetil Berg 6a895501cf Fix: exclude Emscripten from ALSA support check on Linux 2025-11-29 08:58:30 +10:00
David Reid 787318fd8f Update dr_wav and dr_mp3. 2025-11-29 08:36:33 +10:00
David Reid 4a8467852a Fix a shadow declaration warning.
Public issue https://github.com/mackron/miniaudio/issues/1059
2025-11-29 08:34:40 +10:00
David Reid 80cf7b2deb Update dr_flac.
Public issue https://github.com/mackron/miniaudio/issues/1050
2025-09-28 08:10:20 +10:00
David Reid 2db0984566 Fix a possible crash in the resource manager.
This code was prematurely freeing a data buffer node which was resulting
in a dereference of an invalid pointer.
2025-09-27 12:34:21 +10:00
David Reid 1d6b3c6a0f Merge branch 'dev' into dev-0.12 2025-09-24 16:05:30 +10:00
David Reid 669ed3e844 Update dr_mp3. 2025-09-24 16:04:56 +10:00
David Reid c32d2d0e29 Merge branch 'dev' into dev-0.12 2025-09-19 13:03:34 +10:00
David Reid 81410769ae Update c89atomic.
There was a stray line continuation in a macro which was resulting in an
error with MSVC.
2025-09-19 13:03:10 +10:00
David Reid 48d7493c58 Merge branch 'dev' into dev-0.12 2025-09-19 12:48:11 +10:00
David Reid ffe558437f Update change history. 2025-09-19 12:47:58 +10:00
David Reid 089f041120 Update c89atomic.
Public issue https://github.com/mackron/miniaudio/issues/1045
2025-09-19 12:46:50 +10:00
David Reid 2e02046c6d Update dr_libs. 2025-09-19 12:45:59 +10:00
David Reid b22a0cbdb1 Update documentation generator. 2025-09-19 12:45:20 +10:00
David Reid 57851a9cef Update documentation generator. 2025-09-19 12:42:06 +10:00
David Reid c8975bc979 Merge branch 'dev' into dev-0.12 2025-09-14 07:54:07 +10:00
David Reid b3c6bcec39 Update change history and version number. 2025-09-14 07:39:08 +10:00
David Reid ed2c5270c8 Fix a typo. 2025-09-14 07:36:30 +10:00
Michael Müller 3dfcefc75b Fix access to miniaudio in ma_context_uninit__webaudio. 2025-09-14 07:26:31 +10:00
David Reid 63485c2e7d Merge branch 'dev' into dev-0.12 2025-09-11 09:30:16 +10:00
David Reid f40cf03f80 Version 0.11.23 2025-09-11 06:46:45 +10:00
David Reid 7f2cd5b8d5 Fix an error in the splitting script. 2025-09-11 06:42:16 +10:00
David Reid 3afbdef285 Update dr_libs. 2025-09-10 18:56:36 +10:00
David Reid 9a091f73aa Fix an undefined behavior error in the s16 to s32 conversion routine. 2025-09-10 15:06:34 +10:00
David Reid 4de39a8a37 Update change history. 2025-09-10 14:55:00 +10:00
David Reid 7c8574210d Update gitignore. 2025-09-10 13:49:58 +10:00
David Reid 6648ed005a Initial commit of the documentation generation tool. 2025-09-10 13:47:00 +10:00
David Reid 87bae56937 Fix typos in the documentation. 2025-09-10 13:18:58 +10:00
David Reid 293f5de18f Minor fixes to the readme. 2025-09-10 13:11:34 +10:00
David Reid 233b9b69c4 Reinstate the Twitter badge. 2025-09-10 12:58:20 +10:00
David Reid db514e813f Add splitting script. 2025-09-10 12:45:06 +10:00
David Reid 8130543730 Update fs. 2025-09-10 10:04:27 +10:00
David Reid c3245ee3ca Merge branch 'dev' into dev-0.12 2025-09-10 09:53:01 +10:00
David Reid b306c6a270 Use pkg-config for libvorbis and libopus detection. 2025-09-10 09:51:13 +10:00
spevnev 9e1f02b12a Fix unsigned offset overflow 2025-09-10 06:44:31 +10:00
David Reid fa84240364 Fix a typo. 2025-09-09 17:27:25 +10:00
David Reid 0379f18239 Add NetBSD job to CI. 2025-08-23 14:28:09 +10:00
David Reid 8030f3bf7b Merge branch 'dev' into dev-0.12 2025-08-23 09:54:51 +10:00
David Reid 70eb06d3bd CMake: Minor fix for pthread and m. 2025-08-23 09:54:32 +10:00
Marcin Serwin 959283f244 Ignore missing library dependencies in CMake
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 09:45:49 +10:00
David Reid ea59076ba9 Try fixing a warning with MA_FALLTHROUGH. 2025-08-23 08:33:53 +10:00
David Reid 5384cfaf12 Merge branch 'dev' into dev-0.12 2025-08-23 08:11:38 +10:00
David Reid 1d7d8dfba0 Fix some CMake errors with the Emscripten build. 2025-08-23 08:09:03 +10:00
David Reid f48d903526 WASAPI: Add a missing field to ma_AudioClientProperties.
Public issue https://github.com/mackron/miniaudio/issues/1028
2025-08-23 06:46:09 +10:00
David Reid 3bdd39fd2c Make some sound functions const.
Public issue https://github.com/mackron/miniaudio/issues/990
2025-08-23 06:20:08 +10:00
David Reid f9cb8a3e4d Revert "Expose compile defines in pkg-config file"
This reverts commit a2f92095dc.
2025-08-23 06:09:33 +10:00
David Reid 74aa94d980 Merge branch 'dev' into dev-0.12 2025-08-23 05:50:33 +10:00
David Reid e75a053908 Update CMake script to extract the version from miniaudio.h. 2025-08-23 05:50:15 +10:00
David Reid dda0bd100b Merge branch 'dev' into dev-0.12 2025-08-23 05:46:31 +10:00
David Reid 346d86ffa1 Update gitignore. 2025-08-23 05:45:05 +10:00
Marcin Serwin a2f92095dc Expose compile defines in pkg-config file
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 05:41:27 +10:00
Marcin Serwin badf36a378 Generate and install pkg-config file
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 05:41:27 +10:00
Marcin Serwin c9d288c3dc Link linux backends if NO_RUNTIME_LINKING is enabled
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-08-23 05:41:27 +10:00
Copilot bcb198a7e9 Add GitHub CI workflow for compilation checks 2025-08-22 12:52:57 +10:00
David Reid 5221ee1858 Comment out an unused function. 2025-08-22 12:49:14 +10:00
David Reid 3803df947e Update gitignore. 2025-08-22 12:34:06 +10:00
David Reid e094807b46 Update readme for tests. 2025-08-22 12:33:01 +10:00
David Reid 733a3e3ec0 Another attempt at fixing the Emscripten build. 2025-08-22 12:29:51 +10:00
David Reid 0c245dd597 Fix the Android build. 2025-08-22 11:58:53 +10:00
David Reid 8c08e14119 Comment out an unused function. 2025-08-22 11:29:17 +10:00
David Reid fde064bf03 Merge branch 'dev' into dev-0.12 2025-08-22 11:27:57 +10:00
David Reid 825d2c4466 Update fs. 2025-08-22 11:27:45 +10:00
David Reid d522c8a017 Fix some warnings with the Android build. 2025-08-22 11:22:14 +10:00
David Reid cac5825182 Remove an unused variable. 2025-08-22 11:17:43 +10:00
David Reid 125e300c69 Fix a type mismatch error. 2025-08-22 11:15:45 +10:00
David Reid c036a9ff86 Try fixing errors when SDL2 is not found. 2025-08-22 11:12:44 +10:00
David Reid 563f1820cb Improvements to the CMake script for Emscripten. 2025-08-22 09:51:50 +10:00
David Reid fa031e75fd Silence some pedantic warnings with Emscripten. 2025-08-22 09:47:16 +10:00
David Reid 8eba46fdee Merge branch 'dev' into dev-0.12 2025-08-22 07:12:46 +10:00
David Reid 6fd62e6bbc Update fs. 2025-08-22 07:12:34 +10:00
David Reid ce6c21fffc Fix some Clang warnings. 2025-08-22 07:09:06 +10:00
David Reid c366358ed6 Try fixing a possible Emscripten error with the deviceio test. 2025-08-21 18:16:46 +10:00
David Reid 747faa3390 Fix some errors with the Emscripten build. 2025-08-21 18:01:06 +10:00
David Reid ca3ba8c1a8 Update CMake script to relax build options for examples.
This makes it so examples don't throw annoying warnings, that in order
to fix, would require making the example code unnecessarily untidy.
2025-08-21 15:35:32 +10:00
David Reid cb95cd6521 Force silence a warning in an example. 2025-08-21 14:47:11 +10:00
David Reid b37530fdd6 Merge branch 'dev' into dev-0.12 2025-08-21 14:34:11 +10:00
David Reid b1893aa8f1 Minor C89 compatibility fix. 2025-08-21 14:33:52 +10:00
David Reid df4baf8d40 Fix some errors. 2025-08-21 14:24:21 +10:00
David Reid 21237008df Silence some errors in the Steam Audio headers. 2025-08-21 14:23:31 +10:00
David Reid 7263de3b64 Delete the PipeWire test.
The PipeWire backend is now tested with the deviceio test.
2025-08-21 14:18:58 +10:00
David Reid 26a1566cb2 Fix some errors. 2025-08-21 14:09:22 +10:00
David Reid df0358c870 Merge branch 'dev' into dev-0.12 2025-08-21 14:09:02 +10:00
David Reid 587bd83cbb Update fs and fix some build errors with -std=c89. 2025-08-21 13:57:57 +10:00
David Reid 0204c7d788 Fix a compatibility error with va_copy(). 2025-08-21 12:56:20 +10:00
David Reid e826957a12 Disable the PipeWire backend when compiling as C89.
The SPA headers are not compatible with C89 which means the PipeWire
backend is not usable.
2025-08-21 07:33:08 +10:00
David Reid f6453a1418 Add ma_get_stock_device_backends().
This commit many warnings when compiling as C89.
2025-08-21 07:27:42 +10:00
David Reid c3132cb703 Fix a couple of warnings on the Emscripten build. 2025-08-20 19:20:23 +10:00
David Reid f86be65f19 Merge branch 'dev' into dev-0.12 2025-08-20 18:15:54 +10:00
David Reid b7e5451ef4 Try fixing a compilation error when pthread does not exist. 2025-08-20 18:07:59 +10:00
David Reid ba84e61a18 Try fixing a compilation error when libatomic does not exist. 2025-08-20 18:03:12 +10:00
David Reid a7ab58259e Don't try building C++ tests when forcing C89. 2025-08-20 17:36:07 +10:00
David Reid 7f2f0a5a36 Fix a C89 error. 2025-08-20 16:56:04 +10:00
David Reid 5eb78ec819 Merge branch 'dev' into dev-0.12 2025-08-20 16:40:57 +10:00
David Reid 18055f34bb voclib: Fix a compilation error. 2025-08-20 16:40:46 +10:00
David Reid 47f08262a0 For a C89 error. 2025-08-20 16:36:53 +10:00
David Reid 415c50e2f5 Fix a -Wpedantic warning. 2025-08-20 16:12:10 +10:00
David Reid db38f0b003 Merge branch 'dev' into dev-0.12 2025-08-20 16:09:58 +10:00
David Reid f6bae251bd verblib: Try fixing a compilation error on macOS. 2025-08-20 16:09:44 +10:00
David Reid 3567d5cfef Fix compilation error with MA_NO_SSE2. 2025-08-20 16:04:15 +10:00
David Reid 4e3b778c62 Silence some warnings about unused functions. 2025-08-20 15:56:43 +10:00
David Reid ba35370f74 Merge branch 'dev' into dev-0.12 2025-08-19 08:32:35 +10:00
David Reid 6315130ec6 Make ma_lcg.state uint32 to avoid UB on signed arthimethic overflow 2025-08-19 08:32:06 +10:00
David Reid 6e1cd41622 tests: fix memory leak in filtering and generation tests 2025-08-19 08:26:22 +10:00
David Reid 62c10ddd58 Fix an error with duplex mode for the new null backend. 2025-08-15 06:10:34 +10:00
David Reid d0af92764f Big simplification to the null backend.
This is the first backend to experiment with the new wait/step backend
model which, if it works out, will allow miniaudio to work in a single
threaded mode which in turn will open up the opportunity for
applications to have greater control over thread management and to
possibly allow miniaudio to work on single threaded systems like DOS.
2025-08-14 17:32:45 +10:00
David Reid 59566edddf Some fixes to the audio thread. 2025-08-11 15:53:32 +10:00
David Reid 3f3353ee39 Remove an unused variable. 2025-08-11 15:39:47 +10:00
David Reid a1bee2c673 Get device IO APIs compiling with MA_NO_THREADING.
This is only concerned with getting miniaudio compiling. As of this
commit, device IO will not actually work with this option enabled.
2025-08-11 11:45:09 +10:00
David Reid 10700ec157 Address some MA_NO_THREADING compatibility with ma_device. 2025-08-11 08:57:46 +10:00
David Reid 9b6e9b6985 Remove some now unused code. 2025-08-11 08:08:59 +10:00
David Reid 5db8fe5094 Make it so backend start/stop callbacks are fired from the audio thread. 2025-08-11 07:58:33 +10:00
David Reid 87dee0e20c Some cleanup to the new audio thread. 2025-08-11 06:47:49 +10:00
David Reid fa5f282d65 Throw down some early work for some backend refactoring.
This is still heavily WIP and should be considered unstable as of this
commit.

This is the foundation work for getting all device backend callbacks
executing from a single thread. This will not include context related
callbacks, nor the wakeup callback.

Once this work is done, backends can rely on the property that all the
main callbacks, except wakeup, will be executed from the same thread.
Where this is particularly useful is thread hostile backends like
WASAPI. In particular, we'll be able to do isolate the initialization of
COM to only the miniaudio-managed thread which make it easier to use
miniaudio alongside frameworks which do their own COM initialization,
such as Qt.
2025-08-10 17:38:18 +10:00
David Reid fc15cc66ed Minor code rearrangement to reflect correct backend prioritization. 2025-08-08 06:41:40 +10:00
David Reid a9bdbc3033 Update backend priority order in documentation. 2025-08-07 19:23:22 +10:00
David Reid e6483d8bd9 Add some fallback stubs for threading APIs.
These will be used when a platform lacks any kind of threading support,
such as DOS. This is in preparation for future work to allow the device
API to work without threading support.

With this commit, the DJGPP DOS build can be compiled without
`MA_NO_THREADING`.
2025-08-07 19:16:02 +10:00
David Reid 08b8fcd4c6 Fix timing functions with the DJGPP build. 2025-08-07 19:12:43 +10:00
David Reid 0923a484ee Merge branch 'dev' into dev-0.12 2025-08-07 18:00:40 +10:00
David Reid 78cdb9c1cb Add batch file for setting up DJGPP environment. 2025-08-07 17:44:55 +10:00
David Reid 1ea69211ad Fix some errors with the DJGPP build.
This forces `MA_NO_THREADING` and `MA_NO_RUNTIME_LINKING`.

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

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

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

Public issue https://github.com/mackron/miniaudio/issues/1023
2025-08-07 13:45:11 +10:00
David Reid c87f207f4e Win32: Use CreateSemaphore() instead of CreateSemaphoreW()
This should improve compiler compatibility for those that do not
define `CreateSemaphoreW()`, such as NXDK.
2025-08-07 13:23:35 +10:00
David Reid 02ae7e41f0 Update dr_libs. 2025-08-07 13:18:56 +10:00
David Reid 8bb1dc5a88 Merge branch 'dev' into dev-0.12 2025-07-23 12:12:53 +10:00
David Reid 457a7279fa Update dr_wav. 2025-07-23 12:08:35 +10:00
David Reid fe1da60e1e Update change history. 2025-07-23 10:11:14 +10:00
David Reid 0b70a990ca Minor update to documentation. 2025-07-23 09:16:20 +10:00
David Reid 26c347e506 Merge branch 'dev' into dev-0.12 2025-07-22 16:34:01 +10:00
David Reid 556160909e Try fixing the Switch build. 2025-07-22 15:46:17 +10:00
David Reid 2d4cc9c910 ALSA: Add a new config option to use the default channel layout.
This can sometimes be more reliable than `snd_pcm_get_chmap()`.
2025-07-22 15:29:51 +10:00
David Reid 20b9b9c533 Android: Remove enableCompatibilityWorkarounds config options. 2025-07-22 07:53:11 +10:00
David Reid 76e97c6254 PipeWire: Try fixing some compilation errors. 2025-07-22 06:18:46 +10:00
David Reid 384afa3a5f Whitespace. 2025-07-21 17:09:09 +10:00
David Reid 397cadbd64 Merge branch 'dev' into dev-0.12 2025-07-21 17:07:08 +10:00
David Reid c48975f4a9 Don't link against dl with NO_RUNTIME_LINKING. 2025-07-21 17:06:34 +10:00
David Reid db9ad08bf0 Merge branch 'dev' into dev-0.12 2025-07-21 17:04:02 +10:00
David Reid 79b4ddc27d Fix CMake script for NetBSD and OpenBSD. 2025-07-21 16:54:27 +10:00
David Reid 377f589a01 Merge branch 'dev' into dev-0.12 2025-07-21 16:51:09 +10:00
Marcin Serwin 0b9f03a376 Respect CMAKE_INSTALL_INCLUDEDIR when installing headers
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-07-21 16:48:25 +10:00
Marcin Serwin 140b9c7f9f Add compile define for specific backends only in CMake
Fixes https://github.com/mackron/miniaudio/pull/1010#issuecomment-3093830252

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

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

Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2025-07-21 16:47:03 +10:00
David Reid 110ded6fc6 Include the SDL2 and PipeWire backends in the deviceio test. 2025-07-21 16:11:31 +10:00
David Reid 276dd4419d Fix some errors with the PipeWire backend. 2025-07-21 16:08:52 +10:00
David Reid 2a7446fb46 Add some globals for standard sample rates.
It's been useful for backends to be able to iterate over an array of
sample rates in a standard order of priority, so I've made this public
for the benefit of custom backends.

This also removes a hard coding for `ma_standard_sample_rate_count`.
2025-07-21 14:13:18 +10:00
David Reid de31f9ea49 Fix warnings in the PipeWire backend with GCC. 2025-07-21 14:07:24 +10:00
David Reid ed65f5d7ee Standardize naming convention of data source vtable variables.
This naming convention is consistent with decoding and device backends.
2025-07-21 13:06:02 +10:00
David Reid ff73bd7af6 Remove new lines from log messages. 2025-07-21 12:57:48 +10:00
David Reid bd75bcb592 Make ma_context/device_get_backend_state() atomic.
It is technically possible for a backend to create a thread which
calls `ma_context/device_get_backend_state()` before miniaudio has
set the internal pointer.
2025-07-21 12:43:13 +10:00
David Reid 93167d108e Move an implementation detail out of the header section. 2025-07-21 11:50:29 +10:00
David Reid fa4107ae95 API CHANGE: Remove the onContextGetDeviceInfo backend callback.
Device information retrieval is now implemented in terms of device
enumeration. Backends should now return any information that would have
been returned from `onContextGetDeviceInfo` straight from
`onContextEnumerateDevices` instead.
2025-07-21 11:47:14 +10:00
David Reid 2b81f75cca API CHANGE: Change return type of device enumeration callback.
The callback passed into `ma_context_enumerate_devices()` would
previously return a boolean, with true telling miniaudio to continue
enumeration, and false to abort. This got a bit confusing to read at
times, so I've decided to make this more explicit.

The new return type is an enum called `ma_device_enumeration_result`.
Instead of returning true to continue enumeration, the new return value
is `MA_DEVICE_ENUMERATION_CONTINUE`. Similarly, instead of returning
false to abort enumeration, `MA_DEVICE_ENUMERATION_ABORT` should be
returned instead.
2025-07-21 11:33:15 +10:00
David Reid 5d86a6ef82 SDL: Include format information in device enumeration. 2025-07-21 10:08:15 +10:00
David Reid 5b21699ba9 Fix Emscripten test. 2025-07-21 09:13:53 +10:00
David Reid 497f18c4a4 Web Audio: Include format information in device enumeration. 2025-07-21 09:13:40 +10:00
David Reid 4a646fe191 WinMM: Remove ma_context_get_device_info__winmm(). 2025-07-21 08:49:16 +10:00
David Reid b184d81a46 DirectSound: Remove ma_context_get_device_info__dsound(). 2025-07-21 08:48:53 +10:00
David Reid ced8f9e091 DirectSound: Include format information in device enumeration. 2025-07-21 08:40:11 +10:00
David Reid 8b3ac67c89 Improvements to the WASAPI backend.
* Fix an error with the recent refactoring work.
  * Fix some build errors with the UWP build.
  * Update device enumeration to include format information.
2025-07-21 08:14:50 +10:00
David Reid 9507f61689 Merge branch 'dev' into dev-0.12 2025-07-20 19:49:06 +10:00
David Reid 8b87c10681 Fix some errors with MA_NO_RUNTIME_LINKING. 2025-07-20 19:41:28 +10:00
David Reid c62dc472eb Core Audio: Include format information in device enumeration. 2025-07-20 18:23:31 +10:00
David Reid e49fc58584 ALSA: Improve a log message. 2025-07-20 16:40:11 +10:00
David Reid 821399dca1 ALSA: Stop printing internal error messages.
This makes device enumeration obnoxious so I've silenced it.
2025-07-20 16:34:28 +10:00
David Reid 47f9287ac8 Don't unnecessarily call ma_device_get_info() in deviceio test. 2025-07-20 16:20:41 +10:00
David Reid 3add89fdd2 ALSA: Include format information in device enumeration. 2025-07-20 16:20:07 +10:00
David Reid c257d19c86 PulseAudio: Include format information in device enumeration. 2025-07-20 15:27:41 +10:00
David Reid 85c005b52d Merge branch 'dev' into dev-0.12 2025-07-20 07:53:33 +10:00
andy5995 629d509072 Fix warning: function declaration without a prototype
Harmless warning on FreeBSD 14.2,
https://cirrus-ci.com/task/4700955851096064?logs=build#L44

```
../subprojects/miniaudio-0.11.22/miniaudio.h:36997:36: warning: a
function declaration without a prototype is deprecated in all versions
of C [-Wstrict-prototypes]
 36997 | static int ma_open_temp_device__oss()
       |                                    ^
       |                                     void
```
2025-07-20 07:51:59 +10:00
David Reid 5c86dd9153 Fix a possible division by zero error. 2025-07-20 07:44:06 +10:00
David Reid 8bb2b1ae80 Update to the PipeWire backend. 2025-07-18 19:16:50 +10:00
David Reid 7bbecd211e API CHANGE: Add onTell callback to ma_decoder_init().
https://github.com/mackron/miniaudio/issues/959
2025-07-18 16:52:54 +10:00
David Reid fca29d2d57 Clean up some unsafe code. 2025-07-18 16:11:30 +10:00
David Reid 5f84285913 JACK improvements.
* Device enumeration will now report that any channel count is
    supported.
  * When initializing the device, the exact number of channels (ports)
    will be registered to the client as requested.

Public issue https://github.com/mackron/miniaudio/issues/851
2025-07-18 16:09:22 +10:00
David Reid 0b5430572d JACK: Fix a copy/paste error. 2025-07-18 15:18:58 +10:00
David Reid 59333e9721 Improvements to the JACK backend.
* Device enumeration has been updated to report detailed format
    information.
  * A noAutoConnect config option has been added the JACK device config.
  * The number of ports used by the device will be equal to the
    requested channel count, up to the maximum number of ports reported
    by the client.

This is untested as of this commit.
2025-07-18 14:52:58 +10:00
David Reid e6152c0ff9 Update the null backend to the new enumeration system. 2025-07-18 12:57:49 +10:00
David Reid 3b879485be Fix some errors with device names during enumeration. 2025-07-18 12:53:03 +10:00
David Reid d1955c29a9 Improvements to the OpenSL|ES backend.
* Some untested placeholder code has been removed for non-Android
    builds. This was adding unnecessary complexity.
  * Device enumeration now includes detailed info.
2025-07-18 12:20:08 +10:00
David Reid 6ab1cc83cd AAudio: Fix a bug with device enumeration. 2025-07-18 12:18:50 +10:00
David Reid 3f9dd6ddd0 AAudio: Extract detailed device information from enumeration. 2025-07-18 10:21:27 +10:00
David Reid 55564ede75 Fix Android build. 2025-07-18 10:11:57 +10:00
David Reid 7b887e0ca5 Update sndio backend to included detailed device info from enumeration. 2025-07-18 09:22:06 +10:00
David Reid c2b45b58af Improvements to the NetBSD audio(4) backend.
* Device enumeration will now mark appropriate devices as default.
  * The onContextGetDeviceInfo backend callback has been removed so that
    all device info is retrieved from the enumeration process.
2025-07-18 08:25:16 +10:00
David Reid c468230e8f Enable the OSS backend on Linux.
This is the lowest priority stock backend for Linux.
2025-07-17 19:09:30 +10:00
David Reid 7b157815ab Fix ALSA build on very old compilers. 2025-07-17 15:08:58 +10:00
David Reid 291442cd6c Update external/fs. 2025-07-17 15:08:29 +10:00
David Reid d8236f9d22 Improvements to the OSS backend.
These changes add initial support for OSSv3. Where this is of particular
note is Linux which never updated to OSSv4. This compiles on Linux, but
I have not yet tested this at run time. Future commits will enable the
OSS backend for Linux which will be useful for getting miniaudio working
on very old versions of Linux.
2025-07-17 11:13:29 +10:00
David Reid 552ef533d4 Make the onDeviceGetDeviceInfo backend callback optional.
In this case, device information will be retrieved through device
enumeration. This callback will eventually be removed entirely once all
backends have been updated.
2025-07-17 10:53:45 +10:00
David Reid 3191f957c3 Update simple_enumeration to show default devices. 2025-07-16 19:09:35 +10:00
David Reid a0402c6f1f Fix a compilation error. 2025-07-16 14:37:03 +10:00
David Reid 0acd0a4b78 Fix memory leaks. 2025-07-15 15:38:51 +10:00
David Reid f0ff4b6de3 API CHANGE: Remove ma_performance_profile.
This also removes the `performanceProfile` config option from
`ma_device_config`. Increase the period size if you want to be more
conservative.

Backends that have their own notion of a "latency hint" or the like will
have those options exposed via a backend-specific configuration option.
2025-07-15 14:35:24 +10:00
David Reid a014181372 Update fs. 2025-07-15 14:31:56 +10:00
David Reid 27b593c2d2 API CHANGE: Rename ma_device_state to ma_device_status.
This renames the `ma_device_state` enum to `ma_device_status`, and also
renames `ma_device_get_state()` to `ma_device_get_status()`.

The reason for this change is that the new backend system uses the
notion of a "state" for backend-specific state for devices. This change
is just to avoid ambiguity between the two concepts.
2025-07-15 12:09:39 +10:00
David Reid 94b07291fc Remove old code from some earlier experiments. 2025-07-15 11:57:06 +10:00
David Reid 5b3bc33425 WASAPI: Move some code out of the header section. 2025-07-15 11:50:45 +10:00
David Reid 147c6620cb API CHANGE: Remove ma_backend.
This is from the old backend system and is no longer used.
2025-07-15 11:43:57 +10:00
David Reid 24a073eab3 API CHANGE: Remove ma_is_loopback_supported().
Use `ma_get_device_backend_info()` instead. Inspect the
`isLoopbackSupported` member of `ma_device_backend_info`.
2025-07-15 11:38:06 +10:00
David Reid 714afe2c42 API CHANGE: Remove ma_context_is_loopback_supported().
Use `ma_context_get_device_info()` instead. Inspect the
`isLoopbackSupported` member of `ma_device_backend_info`.
2025-07-15 11:33:35 +10:00
David Reid 3df76aa05e Add isLoopbackSupported to ma_device_backend_info. 2025-07-15 11:31:27 +10:00
David Reid 0c7be93f6f API CHANGE: Remove ma_is_backend_enabled() and ma_get_enabled_backends()
For determining if a backend is enabled, just compare it to NULL:

    if (ma_device_backend_wasapi != NULL) {
        /* Enabled */
    } else {
        /* Disabled */
    }

If you need to list available backends, just keep track of a list of
backends that you care about, and then check which ones are null.
2025-07-15 11:25:11 +10:00
David Reid d89e7c8e5d Add back some SDK version checks for Android. 2025-07-15 10:51:51 +10:00
David Reid 9f171142cf API CHANGE: Remove ma_get_backend_from_name().
With the new pluggable backend system, this function no longer makes
sense. If you need this, you should manage it yourself.
2025-07-15 10:23:40 +10:00
David Reid a432e4db4c API CHANGE: Remove ma_get_backend_name().
Use `ma_get_device_backend_info()` or `ma_context_get_backend_info()`
instead.
2025-07-15 10:19:28 +10:00
David Reid fabab10843 Remove the backend member from ma_context.
This is a legacy from the old backend system.
2025-07-15 10:11:18 +10:00
David Reid d1643b3487 Add ma_get_device_backend_info() and ma_context_get_backend_info().
These functions are used to retrieve basic information about the
backend, such as the name. Useful for logging and UI.
2025-07-15 10:10:29 +10:00
David Reid c0336335a6 Make some functions private. 2025-07-15 08:50:07 +10:00
David Reid 51715474d7 CMake: Improve handling of dl. 2025-07-15 08:12:36 +10:00
David Reid a4072246c5 Rename some variables and try silencing a warning. 2025-07-15 07:21:30 +10:00
David Reid 180c7237f8 Fix a warning. 2025-07-15 07:21:04 +10:00
David Reid 1e6c01f31d Fix CMake script for NetBSD. 2025-07-15 07:20:52 +10:00
David Reid 96eea50e7d Remove references to the custom backend from the CMake script. 2025-07-15 06:59:55 +10:00
David Reid 2833ccf4e7 Whitespace. 2025-07-15 06:59:36 +10:00
David Reid ebec10b068 Add early PipeWire backend.
This is incomplete.
2025-07-15 06:59:19 +10:00
David Reid fd37406086 Update build instructions for Emscripten. 2025-07-15 06:53:52 +10:00
David Reid b9f6d99217 Rename some variables for consistency. 2025-07-15 06:50:22 +10:00
David Reid 8890eac6aa One Big Beautiful Commit with refactoring to the device backend system.
This includes API changes that affect custom backends.

`ma_backend_callbacks` has been renamed to `ma_device_backend_vtable`.
The reason for this change is to to be consistent with the naming
convention used in other parts of the library. In addition, using the
term "device backend" rather than just "backend" removes ambiguity with
decoding backends.

A change has been made to the way backends manage their internal state,
and some functions in the vtable have been updated to reflect this.
Previously internal state for stock backends were located directly in
the `ma_device` structure. This works fine if stock backends are the
only backends to be concerned about, but it falls apart when you need
to consider how to manage the internal state of custom backends since
they cannot modify the `ma_device` structure. In order to simplify and
unify state management between stock and custom backends, the decision
was made to change the backend system such that backends now manage
their own internal state.

When the context is initialized with `onContextInit`, the backend must
now allocate an internal state object and output a pointer to it via
an output parameter. Typically you would do something like this:

    ma_result custom_context_init(..., void** ppContextState)
    {
        ctx_state_t* state = malloc(...);

        ...

        *ppContextState = state;
        return MA_SUCCESS;
    }

miniaudio will store a pointer to the state object internally. When you
need to access this later in other backend callbacks, you can retrieve
it straight from the context with `ma_context_get_backend_state()`:

    state = (ctx_state_t*)ma_context_get_backend_state(pContext);

The same idea applies to devices and `onDeviceInit`. You can use
`ma_device_get_backend_state()` to get a pointer to the internal state
object.

When a context and device is initialized, backend-specific
configurations can be supplied. The way these configs are provided to
`onContextInit` and `onDeviceInit` has been changed. Previously, these
callbacks would take a `ma_context/device_config` object. These have
been replaced with a `const void*` which points to a backend-specific
config object which is defined by the backend. All stock backends have
their own backend-specific config object:

    struct ma_context_config_wasapi
    struct ma_context_config_pulseaudio
    etc.

    struct ma_device_config_wasapi
    struct ma_device_config_pulseaudio
    etc.

You can cast the config object inside the relevant callbacks:

    ma_result custom_context_init(..., const void* pBackendConfig, ...)
    {
        ctx_config_t* pCustomConfig = (ctx_config_t*)pBackendConfig;
    }

The backend itself defines whether or not a config is required. None of
the stock backends require a config. If the config is NULL, it'll use
defaults. It's recommended custom backends follow this convention.

In addition to the above, `onContextUninit` and `onDeviceUninit` have
been updated to return void instead of `ma_result`.

The last change to the backend vtable is a new callback called
`onBackendInfo`. This is used to fill the `ma_device_backend_info`
structure.

In addition to the backend vtable, some changes have been made to the
public API to make it much easier to support plugging in custom
backends.

Previously, plugging in more than one custom backend was a complete
mess. It was possible, but you had to use a stupid wrapper thing to
make it work, and you had no control over prioritization. The entire
thing was just aweful, so it's now been stripped out and replaced with
a brand new system.

When a context or device is initialized, it is done so with a config
which is standard across the entire library. A complication to this is
that backends can sometimes require their own backend-specific configs.
But since miniaudio cannot possibly know about custom backends, it
cannot put their config options inside `ma_context/device_config`. The
functions for initializing a context and device have been updated to
allow plugging in backend-specific configs.

When initializing a context, instead of passing in an array of
`ma_backend` enums, an array of `ma_device_backend_config` objects is
passed in instead. This object has two members: A pointer to a backend
vtable, and a pointer to a config object. It can be initialized
something like this:

    ma_context_config_custom customContextConfig;
    ... initialize the custom backend config if necessary ...

    ma_device_backend_config backends[] =
    {
        { ma_device_backend_custom,     &customContextConfig },
        { ma_device_backend_wasapi,     NULL },
        { ma_device_backend_pulseaudio, NULL }
    };

    ma_context_init(backends, backendCount, ...);

Here `ma_device_backend_custom` is our custom backend. You can see how
the config is mapped to the backend. For stock backends (WASAPI and
PulseAudio in this example), you can pass in NULL and just set the
relevant config options straight in `ma_context_config` exactly how it
was done before:

    ma_context_config contextConfig = ma_context_config_init();
    contextConfig.pulseaudio.pApplicationName = "My App";

Here we are just using the standard `ma_context_config` object for
configuring the stock PulseAudio backend. This is possible for all
stock backends, but for custom backends an explicit config object will
be required. You can still use a separate explicit config object for
stock backends if you prefer that style:

    ma_context_config_pulseaudio paContextConfig;
    paContextConfig = ma_context_config_pulseaudio_init();
    paContextConfig.pApplicationName = "My App";

    ma_device_backend_config backends[] =
    {
        { ma_device_backend_pulseaudio, &paContextConfig }
    };

Note that if you do not use custom backends, you can still pass in NULL
for the backends in which case defaults will be used like how it's
always worked in the past.

As with contexts, devices can also have their own backend-specific
configs associated with them. These work exactly the same way, except
these configs are passed into the main `ma_device_config` object. (A
future commit may make this consistent between contexts and devices).

    ma_device_backend_config deviceBackendConfigs[] =
    {
        { ma_device_backend_custom, &customDeviceConfig }
    };

    deviceConfig.pBackendConfigs    = deviceBackendConfigs;
    deviceConfig.backendConfigCount = backendCount;

    ma_device_init(&deviceConfig, &device);

This commit is just the start of many backend related changes. Future
commits will be cleaning up a lot of residual code from the old system,
such as removing `ma_backend`.
2025-07-14 18:05:55 +10:00
David Reid cdfd219377 Clean up some old code. 2025-07-06 11:46:29 +10:00
David Reid aa1cd76b96 Add a new device backend vtable in preparation for backend reworking. 2025-07-06 11:41:29 +10:00
David Reid ab76f7a27d Fix a typo. 2025-07-06 10:20:52 +10:00
David Reid 9bce75d858 Add some some experimental code for a new backend architecture. 2025-07-06 10:19:21 +10:00
David Reid 636fe28d86 API CHANGE: Remove the pContext parameter from device enum callback.
This is part of some future work to decouple `ma_context` and
`ma_device` from backends. If you need access to the context for some
reason, you can pass it in via the user data.
2025-07-04 16:24:22 +10:00
David Reid 017eae73ac Fix compilation of libopus and libvorbis decoders. 2025-07-04 11:37:01 +10:00
David Reid 57423c6cea API CHANGE: Rename seek origin enums.
ma_seek_origin_start   > MA_SEEK_SET
  ma_seek_origin_current > MA_SEEK_CUR
  ma_seek_origin_end     > MA_SEEK_END
2025-07-04 11:23:29 +10:00
David Reid 515e7fc2d3 Merge branch 'dev' into dev-0.12 2025-07-04 11:14:38 +10:00
David Reid e54336996d Update dr_libs. 2025-07-04 11:13:40 +10:00
David Reid 1c7967fc88 PulseAudio: Fix a crash if the requested channel count is too high. 2025-07-04 06:37:08 +10:00
David Reid 2bc0e14abf Minor clarifying comment. 2025-07-02 17:57:50 +10:00
Sam Tupy b348ab0155 fix assertion failure upon loading invalid sound path due to extra ma_fence_release
In ma_resource_manager_data_buffer_node_acquire_critical_section, a job which releases already acquired fences is either processed or posted based on MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT. However on job post or process failure, the fences were being unconditionally released.

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

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

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

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

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

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

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

Tested with alsa-libs 1.12.2 and 1.12.3.

It's possible to reproduce the errors like this:

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

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

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

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

Also these are currently unused:

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

  extras/decoders/libvorbis
  extras/decoders/libopus

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

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

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

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

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

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

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

d46e19fb47
  "PulseAudio: Attempt to fix a deadlock."

61586de203
  "PulseAudio: Default to a blocking main loop."
2024-10-12 10:15:08 +10:00
David Reid 0d0953aa85 Fix build for 3DS.
Public issue https://github.com/mackron/miniaudio/issues/902
2024-10-12 09:30:30 +10:00
David Reid 6ab4567c57 Merge branch 'dev' of https://github.com/mackron/miniaudio into dev 2024-09-13 08:20:38 +10:00
David Reid 4598d72a33 Fix a typo. 2024-09-13 08:20:09 +10:00
Tero Parviainen 4f5106ec77 Add a missing pDevice handle to the Web Audio device index when using worklets
This matches the handle added for script processor node devices in #771
2024-08-22 06:58:38 +10:00
David Reid a611cf5f26 Add ma_device_id_equal(). 2024-08-06 12:07:55 +10:00
David Reid d726e85313 Fix an error with the engine's node graph processing.
Public issue https://github.com/mackron/miniaudio/issues/850
2024-08-06 09:19:54 +10:00
Nishi efa270af91 adjust comparison a bit 2024-08-06 08:18:49 +10:00
Nishi 98f6e923cc check __NetBSD_Version__ 2024-08-06 08:18:49 +10:00
Nishi 427bdc1d2a use AUDIO_GETFORMAT instead of AUDIO_GETINFO for NetBSD 2024-08-06 08:18:49 +10:00
David Reid 61586de203 PulseAudio: Default to a blocking main loop. 2024-08-05 10:34:01 +10:00
David Reid d46e19fb47 PulseAudio: Attempt to fix a deadlock.
Public issues
  https://github.com/mackron/miniaudio/issues/877
  https://github.com/mackron/miniaudio/issues/881
2024-08-05 09:21:23 +10:00
David Reid dc423daa41 Silence some GCC warnings.
This is from c89atomic which has been fixed upstream.
2024-08-04 09:08:27 +10:00
David Reid 5d827878f2 Rename a variable for consistency. 2024-08-04 08:35:05 +10:00
Timo Schwarzer d87230b4bd Allow setting the channel map that is requested from PulseAudio 2024-08-04 08:35:05 +10:00
raduetsya e1328d9d8a Fix an always-false warning 2024-07-27 10:51:44 +10:00
David Reid 8036ac3781 WASAPI: Fix a crash when changing devices. 2024-07-14 07:16:43 +10:00
Matthieu Bouron fc45d8ca06 AAudio: Fix ma_device_get_info() implementation 2024-06-08 12:51:59 +10:00
Matthieu Bouron 35215b266c CHANGES.md: add missing newline at EOF 2024-06-08 12:51:59 +10:00
David Reid 1c15cf6502 WASAPI: Fix a regression where incorrect device info is retrieved. 2024-05-14 07:48:53 +10:00
David Reid 196289592a WASAPI: Fix a bug with loopback device info retrieval. 2024-05-12 08:40:19 +10:00
David Reid 1b35118e31 AAudio: Potential fix for a failed assertion.
Public issue https://github.com/mackron/miniaudio/issues/833
2024-04-29 08:52:12 +10:00
RainRat 88436b25ef Update miniaudio.h
fix typos
2024-04-28 15:17:11 +10:00
RainRat 3db49afa5b revert unneeded changes per request 2024-04-28 15:17:11 +10:00
RainRat aa98e1c493 fix typos 2024-04-28 15:17:11 +10:00
David Reid afb121e2ce Update c89atomic. 2024-04-28 15:12:08 +10:00
David Reid 855628f15f Update dr_wav. 2024-04-28 14:55:42 +10:00
David Reid 9091cbd016 Core Audio: Try fixing a compilation error.
Public issue https://github.com/mackron/miniaudio/issues/841
2024-04-28 13:58:11 +10:00
David Reid e82703482b Fix a minor declaration inconsistency. 2024-04-28 12:59:52 +10:00
Sergey Fedorov 6700c7ecc7 miniaudio.h: fix for macOS 2024-04-26 07:23:10 +10:00
RainRat 3ba0595c6a fix typos 2024-04-11 12:40:39 +10:00
RainRat 4bc18bba5a revert unneeded changes per request 2024-04-11 12:40:39 +10:00
RainRat 26ce355703 fix typos 2024-04-11 12:40:39 +10:00
David Reid f7ad7772d1 ALSA: Try making the handling of poll() a bit more robust.
Public issue https://github.com/mackron/miniaudio/issues/836.
2024-04-07 14:38:09 +10:00
David Reid f760d05c51 API CHANGE: Rename a variable.
This renames `defaultVolumeSmoothTimeInPCMFrames` in `ma_engine_config`
to `defaultVolumeSmoothTimeInFrames` in order to make it consistent
with other variables in the same structure.
2024-03-01 10:56:28 +10:00
David Reid ff9225a878 API CHANGE: Rename a variable.
This renames `gainSmoothTimeInFrames` in `ma_engine_config` to
`spatializationVolumeSmoothTimeInFrames` in order to make it more clear
that it specifically affects spatialization.
2024-03-01 10:49:40 +10:00
David Reid 2c59b302e5 Merge branch 'dev' into dev-0.12 2024-03-01 10:13:29 +10:00
David Reid 0eb86ea1da Fix a bug where sounds loaded with MA_SOUND_FLAG_DECODE do not loop. 2024-03-01 09:59:44 +10:00
David Reid 0262d89ea9 Changes to ma_audio_buffer_ref and ma_audio_buffer.
This updates read_pcm_frames() to return a result code, and to output
the number of frames read through an output parameter.
2024-03-01 09:36:17 +10:00
David Reid f581a23f30 Fix a comment. 2024-03-01 09:35:01 +10:00
David Reid 5a0d1ad433 Merge branch 'dev' into dev-0.12 2024-03-01 08:37:16 +10:00
RainRat 2618c21415 revert unneeded changes per request 2024-03-01 08:35:45 +10:00
RainRat 030b9554c2 fix typos 2024-03-01 08:35:45 +10:00
David Reid c0be89b016 Remove a deprecated config variable.
This removes `isLooping` from these configs:

  * ma_sound_config
  * ma_resource_manager_data_source_config

The new way to set the initial looping state for these objects is to
use a flag:

  * MA_SOUND_FLAG_LOOPING or
  * MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING

The motivation for this change is that it allows the initial looping
state to be set without needing `ma_sound_init_ex()` and
`ma_sound_config`.
2024-03-01 08:33:24 +10:00
David Reid 7c90311f5d Fix some const errors and add ma_decoder_get_encoding_format(). 2024-03-01 07:57:13 +10:00
David Reid f07c4cd3a1 Update engine_custom_decoder example. 2024-03-01 07:55:41 +10:00
David Reid d10a287f23 Rename custom_decoder_engine example to engine_custom_decoder.
This just makes it easier to find engine related examples in the
example list.
2024-03-01 07:30:08 +10:00
David Reid 61ec4b17c6 Update docs and add support for backend-specific userdata for decoders. 2024-03-01 07:06:54 +10:00
David Reid 2eb6f4eda2 Clean up. 2024-02-29 21:13:45 +10:00
David Reid 4fc86eb190 Add helper functions for retrieving encoding format from an extension.
This adds the following functions:

  * ma_encoding_format_from_path()
  * ma_encoding_format_from_path_w()

You can use these to pass into ma_decoder_config to skip over decoding
backends that don't support the given file extension.
2024-02-29 21:07:35 +10:00
David Reid ff0a53fa6f Implement onGetEncodingFormat for libvorbis and libopus decoders. 2024-02-29 20:45:19 +10:00
David Reid 991ae301e4 Remove some unused functions. 2024-02-29 20:35:56 +10:00
David Reid 99c5a9c629 Big simplification decoder initialization.
With this change, there are now new prioritization rules when deciding
which backend to attempt to load from.

  * Backends are iterated in the order they are defined in the config.

  * If the encoding format is set to ma_encoding_format_unknown, all
    backends will be attempted in regular prioritization order.

  * If the encoding format is set to something other than
    ma_encoding_format_unknown, that is a specific format is being
    requested, only backends that report support for that format or
    ma_encoding_format_unknown will attempted.

  * File extensions are no longer used as a hint for prioritization.
    The encoding format in ma_decoder_config should be used to
    accelerate backend selection instead.
2024-02-29 20:29:42 +10:00
David Reid 7d8e8526bc Define stock decoding vtables to null when disabled at compile time. 2024-02-29 16:09:06 +10:00
David Reid 8c2398b1ef Update the custom decoder example. 2024-02-29 16:01:27 +10:00
David Reid 05756ff605 Fix a typo. 2024-02-29 16:00:21 +10:00
David Reid 9a6eefd4a8 Rename some variables in preparation for changes to decoding backends. 2024-02-29 15:35:37 +10:00
David Reid ce848a353d Expose stock decoding backend vtables.
This is in preparation for supporting more flexible decoding backend
prioritization.
2024-02-29 14:59:31 +10:00
David Reid e290104179 Merge branch 'dev' into dev-0.12 2024-02-29 14:39:51 +10:00
David Reid c6d8b591f6 Remove some leftover experimental code. 2024-02-29 14:39:36 +10:00
David Reid 521b6571f3 Fix compilation errors. 2024-02-29 14:38:41 +10:00
David Reid 5888bfadca Merge branch 'dev' into dev-0.12 2024-02-29 14:35:46 +10:00
David Reid 3bdf611768 Fix an invalid comment. 2024-02-29 14:30:48 +10:00
David Reid 63e1900db8 Update documentation. 2024-02-29 10:50:06 +10:00
David Reid 29da9b789c Add new init flags for sounds and resource managed data sources.
This adds the following flags:

  * MA_SOUND_FLAG_LOOPING
  * MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING

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

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

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

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

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

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

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

It is recommended to wait until this event is fired to start a
'main game loop' on the web.
2023-11-15 10:28:02 +10:00
David Reid f9ce46330c Web: Fix an error where the buffer size is incorrectly calculated.
Public issue https://github.com/mackron/miniaudio/issues/773
2023-11-15 10:10:46 +10:00
David Reid c89eb7ccff Merge branch 'master' into dev-0.12 2023-11-10 07:47:20 +10:00
David Reid 3b50a854ec Version 0.11.20 2023-11-10 07:44:19 +10:00
David Reid da0572e6b8 Fix a compilation error with iOS.
Public issue https://github.com/mackron/miniaudio/issues/770
2023-11-09 15:01:03 +10:00
David Reid eb0ce6f1a5 Fix an error when dynamically linking when forcing the UWP build.
This also fixes a possible crash during initialization due to leaving a
thread running after early termination of the initialization routine.
2023-11-04 08:43:16 +10:00
David Reid 7ed8b8c5a5 Merge branch 'master' into dev-0.12 2023-11-04 07:07:28 +10:00
David Reid b19cc09fd0 Version 0.11.19 2023-11-04 07:05:24 +10:00
David Reid 863565a8ed Update dr_libs. 2023-11-04 06:57:53 +10:00
cobyj33 ef0f2e5b16 Fix ma_device_config.noClip docs 2023-11-02 15:13:35 +10:00
David Reid 287881b815 Web Audio: Don't attempt to unlock audio on the touchstart event.
Public issue https://github.com/mackron/miniaudio/issues/759
2023-10-29 08:06:56 +10:00
David Reid 2730775e79 Fix a documentation error. 2023-10-27 17:05:56 +10:00
David Reid 1d17b09e41 Fix an error in ma_sound_get_cursor_in_pcm_frames(). 2023-10-27 16:07:14 +10:00
David Reid 39a7cc444b Fix a crash when uninitialising a device without stopping it first. 2023-10-27 16:06:19 +10:00
a.borisov fe5f17ecf3 fix misspells
occured -> occurred
aquired -> acquired
accomodate -> accommodate
seperate -> separate
etc.
2023-10-22 07:06:12 +10:00
David Reid 0bb56819a8 Address an issue in ma_sound_get_cursor_*().
When seeking, the seek does not immediately get applied to the data
source. Instead it is delayed. This results in a situation where the
sound can be seeked, but a small window will exist where querying the
seek point will not be consistent with the previous seek request.
2023-10-21 07:56:06 +10:00
David Reid 105ffd8b05 Update revision history. 2023-10-21 07:31:05 +10:00
David Reid 9bf256dcf3 Fix a documentation error. 2023-10-21 07:29:24 +10:00
David Reid 2f5c661bb7 Update revision history. 2023-10-21 07:24:23 +10:00
David Reid ecf2a8b917 Fix a crash when using a node with more than 2 outputs. 2023-10-21 07:18:58 +10:00
Marek Maškarinec d282fba0fe Fix AudioContext creation fail if sampleRate is 0 (#745) 2023-10-18 16:38:19 +10:00
Christian Alonso-Daubney a185b99f12 Added pSound check to ma_sound_get_cone() 2023-10-15 07:34:09 +10:00
Christian Alonso-Daubney aef76e251c Added pEngine and listenerIndex check to ma_engine_listener_get_cone() 2023-10-15 07:34:09 +10:00
David Reid b792ccd483 Try making runtime linking more robust on Apple platforms.
Public issue https://github.com/mackron/miniaudio/issues/750
2023-10-14 11:56:16 +10:00
David Reid a3cabad692 Fix some warnings with the Emscripten build. 2023-09-21 08:43:50 +10:00
my1e5 1a9acbad95 fix-sample-rate : fixed value of 11025 Hz sample rate in ma_standard_sample_rate enum 2023-09-21 08:38:43 +10:00
David Reid 381a035fdd Fix some unused function warnings.
Public issue https://github.com/mackron/miniaudio/issues/732
2023-09-11 08:30:30 +10:00
David Reid e1a0f523d0 Update dr_wav. 2023-09-11 08:16:53 +10:00
David Reid 03e36da814 Try fixing a strange error when initializing a POSIX mutex.
https://github.com/mackron/miniaudio/issues/733
2023-09-11 07:55:08 +10:00
David Reid a47f065a4a Don't stop the device in ma_device_uninit().
If the state miniaudio side of the device does not match the actual
state of the backend side of it, such as when the device is stopped but
the backend doesn't post a notification, attempting to stop the device
might result in a deadlock.

This is a just a quick workaround hack for the moment while a more
robust solution is figured out.

https://github.com/mackron/miniaudio/issues/717
2023-09-10 07:33:59 +10:00
David Reid bdf9a5554b Update the deviceio test. 2023-09-10 07:26:09 +10:00
David Reid 537c4ca36c Update the simple_playback_sine example. 2023-09-05 06:48:49 +10:00
David Reid 2922859ea9 Remove cosmo windows.h header. 2023-08-31 18:30:42 +10:00
David Reid 6e6823d9e4 Update deviceio test. 2023-08-31 18:30:04 +10:00
Oldes 568e0ae9e9 FIX: Only white noise type is generated
resolves: https://github.com/mackron/miniaudio/issues/723

Signed-off-by: Oldes <oldes.huhuman@gmail.com>
2023-08-31 07:12:15 +10:00
nmlgc 70bf42392d Fix SSE2 sample swapping in mono expansion.
The SSE2 code paths for mono expansion introduced in Version 0.11.15
mixed up the parameters of `_mm_shuffle_ps()`, which in turn caused
adjacent PCM frames to be swapped in the channel-expanded output.
2023-08-30 08:37:12 +10:00
David Reid 9d461f6d5d Minor changes to osaudio. 2023-08-27 15:26:46 +10:00
David Reid 6539b67163 Remove an incorrect comment. 2023-08-27 15:25:08 +10:00
David Reid 78c6fcb370 Fix some parenthesis errors. 2023-08-27 15:24:39 +10:00
David Reid 1f8c86d9ca Fix a copy/paste error. 2023-08-22 21:20:35 +10:00
David Reid 90342e5f67 Update revision history. 2023-08-19 09:24:02 +10:00
David Reid 46eaf1fa5e Fix a bug where ma_decoder_init_file can incorrectly return successful. 2023-08-19 09:22:39 +10:00
David Reid 509bd6d4f2 Fix some typos. 2023-08-17 20:56:42 +10:00
David Reid a81f09d93c Add osaudio to the extras folder.
This is just a small project to experiment with a few API ideas. This
is not a replacement for miniaudio or anything so don't panic.
2023-08-17 19:45:15 +10:00
David Reid f2ea656297 Correctly mark some functions as static. 2023-08-17 19:32:06 +10:00
David Reid ed68fda5d0 Fix some errors with the SDL backend. 2023-08-17 19:29:27 +10:00
David Reid 7e01c6535b More work on custom device backends.
With this commit, custom backends can now be implemented as
self-contained modules that can easily be plugged in and mixed and
matched depending on a programs requirements. The order in which
custom backends are specified in the context config determine their
priority.

This commit updates the custom_backend example by moving the SDL
code out into its own file in "extras/backends/sdl". The example will
now just include the SDL code files like normal. This represents a more
realistic scenario.
2023-08-07 16:14:05 +10:00
David Reid 60366fe469 Make some code public to ease development of custom backends. 2023-08-07 13:55:37 +10:00
David Reid 5b32ce15d1 Merge branch 'dev' into dev-0.12 2023-08-07 12:13:48 +10:00
David Reid c24141c5ae Remove the use of some deprecated functions. 2023-08-07 12:13:36 +10:00
David Reid 7266a1a6da Merge branch 'master' into dev-0.12 2023-08-07 11:06:21 +10:00
David Reid 3898fff8ed Version 0.11.18 2023-08-07 11:05:14 +10:00
David Reid 9eca9ce0cd Update dr_libs. 2023-08-07 10:00:49 +10:00
David Reid d4fd8411c4 Update Emscripten test. 2023-08-06 15:39:54 +10:00
David Reid efa9e7d727 Web Audio: Memory usage optimization to the Audio Worklet path.
This applies only to duplex devices.
2023-08-06 15:31:50 +10:00
David Reid c24829cbb9 Web Audio: Refactoring to the ScriptProcessorNode path.
This unifies, as much as possible, the ScriptProcessorNode path with
the AudioWorklets path to avoid some code duplication, and to also make
the two paths more similar to each other to ease in maintenance.
2023-08-06 15:06:37 +10:00
David Reid fde7d20414 More improvements to the AudioWorklets Web Audio backend.
* Duplex mode now only creates a single AudioContext and AudioWorklet
  * Devices should now automatically start in response to a gesture
2023-08-06 10:42:48 +10:00
David Reid c36b391cc5 Update changes. 2023-08-05 17:58:01 +10:00
David Reid 4d23c1c5ab Update build instructions for Emscripten. 2023-08-05 17:53:58 +10:00
David Reid 810cdc2380 Improvements to Audio Worklets support for Web Audio.
Public issue https://github.com/mackron/miniaudio/issues/597
2023-08-05 17:02:26 +10:00
David Reid 53907863c7 Add support for stopping and sound and fading out.
This adds the following APIs:

  * ma_sound_stop_with_fade_in_pcm_frames()
  * ma_sound_stop_with_fade_in_milliseconds()
  * ma_sound_set_stop_time_with_fade_in_pcm_frames()
  * ma_sound_set_stop_time_with_fade_in_milliseconds()

These functions will overwrite any existing fades. For the
set_stop_time variants, you specify the time that the sound will be put
into it's stopped state. The fade will start at stopTime - fadeLength.
If the fade length is greater than the stop time, the fade length will
be clamped.

Public issue https://github.com/mackron/miniaudio/issues/669
2023-08-05 10:04:59 +10:00
David Reid f9f542b2fb Fix a fading bug introduced with an earlier commit. 2023-08-05 09:19:17 +10:00
David Reid e43457fcce Add initial implementation for scheduled fades.
This adds the following APIs:

  * ma_sound_set_fade_start_in_pcm_frames()
  * ma_sound_set_fade_start_in_milliseconds()

Public issue https://github.com/mackron/miniaudio/issues/669
2023-08-04 20:28:56 +10:00
David Reid 356eb3252e Set up some infrastructure for starting fades with an offset. 2023-08-04 19:26:55 +10:00
David Reid 3429769623 Update change history. 2023-08-04 12:14:18 +10:00
David Reid f2fc207462 Merge branch 'dev' into dev-0.12 2023-08-04 10:47:09 +10:00
David Reid ca7284fde5 ALSA: Fix an error where restarting a device can fail. 2023-08-03 17:03:33 +10:00
David Reid 2212965267 Fix errors with the C89 build. 2023-08-03 09:16:59 +10:00
David Reid 320245606a Fix C89 build. 2023-08-02 19:41:06 +10:00
David Reid 18e4756be3 Decouple MA_API and MA_STATIC defines. 2023-08-02 08:39:56 +10:00
David Reid 8df02809b5 Remove stale comment. 2023-07-30 08:19:03 +10:00
Taiko2k 1696031633 Tweak pulseaudio stream flags 2023-07-30 08:17:21 +10:00
David Reid 90bdda29ae Fix a typo. 2023-07-30 08:14:38 +10:00
David Reid ba1b349a5a Merge branch 'dev' into dev-0.12 2023-07-22 16:39:45 +10:00
David Reid 69bc820ae8 Fix an error when loading WAV files.
The sample format of a WAV file is not always being set which results
in get_data_format() returning ma_format_unknown.
2023-07-22 16:39:17 +10:00
David Reid 052d8bd857 Merge branch 'dev' into dev-0.12 2023-07-22 16:22:25 +10:00
David Reid 98a39ded77 Fix compilation error with previous commit. 2023-07-21 18:19:49 +10:00
David Reid 4c7e3218e3 Improvements to decoder initialization.
This change makes use of the onInitFile, onInitFileW and onInitMemory
backend callbacks which enables decoding backends to have optimized
implementations for reading from a file or a block of memory without
having to go through an abstraction layer on the miniaudio side.

Public issue https://github.com/mackron/miniaudio/issues/696
2023-07-21 18:18:15 +10:00
David Reid b2ed26cf76 Fix an error with setting of the cursor when seeking a Vorbis file.
Public issue https://github.com/mackron/miniaudio/issues/707
2023-07-21 07:39:07 +10:00
David Reid 7f0a92a08f Don't call CoUninialize() when CoInitializeEx() fails. 2023-07-09 09:49:01 +10:00
David Reid ab87375257 Add ma_engine_get_volume().
Public issue https://github.com/mackron/miniaudio/issues/700
2023-07-08 09:18:41 +10:00
David Reid 0eadb0f30e Make ma_linear_resampler_set_rate_ratio() more accurate. 2023-07-07 16:47:24 +10:00
David Reid 6117016102 Fix some minor linting warnings. 2023-06-17 08:15:02 +10:00
David Reid dcd432bc84 Update data converter to use the default LPF order for resampling.
This ensures the data converter has the same defaults as the resampler
so things work a bit more consistently.
2023-06-17 08:11:10 +10:00
David Reid 22724a9156 Merge branch 'dev' into dev-0.12 2023-06-17 08:06:45 +10:00
David Reid a6eb7d6a6f Update change history. 2023-06-17 08:06:22 +10:00
Jay Baird e9ba163490 Fix issue where duty cycle of a pulsewave was not correctly set at init time 2023-06-10 08:42:27 +10:00
David Reid f9076ef327 Update dr_wav with more AIFF improvements. 2023-06-08 09:10:07 +10:00
David Reid eabc776898 Fix erroneous output with the resampler when in/out rates are the same. 2023-06-08 08:34:04 +10:00
David Reid 4c49c49596 Update change history. 2023-06-07 21:14:58 +10:00
David Reid 34b40bdc17 Update dr_wav with improved AIFF compatibility. 2023-06-07 13:58:46 +10:00
David Reid d30b3a76a5 Merge branch 'dev' into dev-0.12 2023-06-05 16:06:03 +10:00
David Reid e1bfeb212a AAudio: Reverse some incorrect logic when setting up streams. 2023-06-05 15:44:27 +10:00
David Reid db8e77cad4 Fix a compilation error with the C++ build. 2023-06-05 15:19:28 +10:00
David Reid 4548242b96 Merge branch 'dev' into dev-0.12 2023-06-05 09:01:47 +10:00
David Reid 1177997599 Add support for supplying a custom device data callback to ma_engine.
When this is used, the data callback should at some point call
ma_engine_read_pcm_frames() in order to do some processing.
2023-06-05 09:01:30 +10:00
David Reid 7a804e77c2 Merge branch 'dev' into dev-0.12 2023-06-03 16:27:51 +10:00
David Reid 5f32336a34 Use float* instead of void* for the engine processing callback. 2023-06-03 16:27:39 +10:00
David Reid 780e607210 Make per-sound processing consistent with per-engine. 2023-06-03 16:26:04 +10:00
David Reid f625d7abd8 Merge branch 'dev' into dev-0.12 2023-06-03 16:20:27 +10:00
David Reid a0b952eea6 Add support for setting a processing callback for ma_engine.
This is optional and is fired at the end of each call to
ma_engine_read_process_pcm_frames(). The callback will be passed the
processed audio data so they can do their own processing such as
outputting to a file or whatnot.

The callback is configured via the engine config.
2023-06-03 16:20:16 +10:00
David Reid 16db85081f Merge branch 'dev' into dev-0.12 2023-06-03 13:39:09 +10:00
David Reid e7912fa242 Add ma_sound_get_time_in_milliseconds(). 2023-06-03 13:38:55 +10:00
David Reid 0c1c4c7ddc Update dr_wav. 2023-05-29 08:33:31 +10:00
David Reid 04ab2a2d7b Merge branch 'dev' into dev-0.12 2023-05-27 15:45:00 +10:00
David Reid d76b9a1ac4 Version 0.11.17 2023-05-27 12:49:48 +10:00
David Reid e9b6559be1 Very minor code reorganisation. 2023-05-26 13:41:41 +10:00
Jay Baird 1bd7713e85 swap parameters for better compatibility with ma_data_source 2023-05-26 13:38:45 +10:00
Jay Baird e7e666d827 Add ma_pulsewave generator type 2023-05-26 13:38:45 +10:00
David Reid 8c59e9b736 Update change history. 2023-05-23 19:10:59 +10:00
David Reid a2698a0048 Fix compilation error relating to dlopen() and family. 2023-05-23 14:04:40 +10:00
David Reid ea42e16a79 Fix the C++ build. 2023-05-22 18:27:38 +10:00
David Reid 14be2bd394 Fix some long out of date tests. 2023-05-22 18:20:21 +10:00
David Reid a8f3cb857e Fix compilation errors with MA_NO_DEVICE_IO. 2023-05-22 18:09:04 +10:00
David Reid 563e1c52cb Update change history. 2023-05-22 17:51:08 +10:00
David Reid 4520faa1d2 Update dr_flac amalgamation again to remove redundant error codes. 2023-05-22 17:50:41 +10:00
David Reid 8dec4e0b9b Update amalgamation of dr_flac. 2023-05-22 17:43:27 +10:00
David Reid 69f4a19ef5 Fix a copy/paste error. 2023-05-22 17:41:20 +10:00
David Reid 9374f5e8d2 Update dr_mp3 amalgamation. 2023-05-22 17:25:01 +10:00
David Reid b98acd2422 Update amalgamation of dr_wav.
With this change, dr_wav is now namespaced with "ma" which means dr_wav
can now be used alongside miniaudio.

In addition, some duplicate code has been removed, such as sized types,
result codes, allocation callbacks, etc. which reduces the size of the
file slightly.

This should address the following public issue:
  https://github.com/mackron/miniaudio/issues/673
2023-05-22 16:52:16 +10:00
David Reid 5c099791ee Clean up decoding documentation.
miniaudio is updating it's amalgamation of dr_wav, etc. so that it's
all namespaced with "ma" which will make the amalgamated versions of
dr_libs entirely independent. There's no longer any need to mention
the decoding backends.

Documentation regarding stb_vorbis is removed so as to discourage
new users from using it. Support will not be removed until a
replacement Vorbis decoder can be amalgamated, but new users should
instead be guided to the libvorbis custom decoder in the extras folder.
2023-05-22 16:06:13 +10:00
David Reid 773d97a95c Fix a compilation error with VC6 and VS2003.
These compilers do not support noinline.
2023-05-22 14:48:55 +10:00
David Reid fa7cd81027 Improvements to c89atomic amalgamation.
* Sized types will now use miniaudio's types.
  * Architecture macros now use miniaudio's macros.
  * The c89atomic namespace has been renamed to ma_atomic which makes
    it so c89atomic can be used alongside miniaudio without naming
    conflicts.

Public issue https://github.com/mackron/miniaudio/issues/673
2023-05-21 09:41:49 +10:00
David Reid af46c1fcc0 Minor changes to architecture detection.
This is in preparation for some amalgamation improvements.
2023-05-21 08:25:14 +10:00
David Reid 65574f44e3 Update change history. 2023-05-21 07:57:32 +10:00
David Reid f05bb5306d Try fixing Windows 95/98 build.
This commit makes it so SetFilePointer/Ex() are dynamically loaded at
runtime which allows miniaudio to branch dynamically based on available
support.

This is necessary because versions of Windows prior to XP do not
support the Ex version which results in an error when trying to run the
program.

Public issue https://github.com/mackron/miniaudio/issues/672
2023-05-18 20:44:46 +10:00
David Reid 6eeea700f0 Silence a very minor linting warning in VS2022. 2023-05-17 18:22:24 +10:00
David Reid 04a6fe6eea Work around some bad code generation by Clang. 2023-05-17 18:20:15 +10:00
David Reid 6cddea6fe3 Remove Twitter shield. 2023-04-12 07:56:03 +10:00
David Reid 83c221f83e Merge branch 'dev' into dev-0.12 2023-04-08 09:29:54 +10:00
David Reid f59a2c4cba Merge branch 'dev' into dev-0.12 2023-04-07 18:09:56 +10:00
David Reid cc2076055f Merge branch 'dev' into dev-0.12 2023-04-07 17:41:09 +10:00
David Reid 58166e2267 API CHANGE: Updates to custom backends.
Custom backends must now use the `ma_device_backend_vtable` object to
define their callbacks. All of these functions are largely the same,
except they all now take a `void*` as their first parameter which
represents the user data pointer.

The ma_backend_callbacks parameter has been removed from onContextInit
which means you must now statically define your callbacks in the
ma_device_backend_vtable object that you pass into the context config.

The `custom` member of the context config has been replaced with a new
set of members to support the ability to plug in multiple vtables.
2023-04-01 12:06:52 +10:00
David Reid 68c8b8000e More scaffolding in preparation for the new backend vtable system.
With this commit, the device IO code is now using the new vtable
system, but each of the stock backends are still using the old
callbacks internally. There exists a compatibility wrapper vtable
which all stock backends are currently using. As stock backends migrate
over to the new system, the compatiblity vtable will be replaced with
the backend-specific vtable.
2023-04-01 10:47:38 +10:00
David Reid 35403d4b9c Set up some scaffolding for the new backend vtable system.
The new system is in preparation for improving the custom backend
system and making it more modular. The old system does not support
user data which make things a bit less flexible than it should be. In
addition, the old system does not make it easy to plug in multiple
custom backends.

The changes in this commit, and probably the next few, are just
scaffolding so that we don't have to break the build of every platform
as this is worked on.
2023-04-01 10:02:17 +10:00
David Reid fbb0f9dd72 Add support for a processing callbacks for sounds. 2023-04-01 08:51:20 +10:00
David Reid 203de65568 Add support for setting a callback for when a sound finishes loading. 2023-03-31 12:30:27 +10:00
David Reid 48b2986114 API CHANGE: Changes to the sound notification system.
* The pDoneFence parameter has been removed from ma_sound_init_*()
  and replaced with pNotifications which is of the type
  ma_sound_notifications. This object is where the pDoneFence is
  now located.
* ma_sound_set_end_callback() has been removed. The callback is now
  specified in the pNotifications object.
* The endCallback and pEndCallbackUserData members of ma_sound_config
  have been removed. You must now specify this in pNotifications.
2023-03-31 11:54:14 +10:00
David Reid ef5861acdb API CHANGE: Remove pDoneFence from ma_sound_config.
Use initNotifications instead.
2023-03-30 09:54:31 +10:00
David Reid 2bdad08247 API CHANGE: Remove the idea of a ma_node_graph being a node.
A node graph should not be considered a node because a node needs to
be owned by a node graph which means a node graph being a node would
create a cyclic dependency.
2023-03-30 09:09:56 +10:00
David Reid 5da18c3ca8 API CHANGE: Remove playback.name and capture.name from ma_device.
Use ma_device_get_name() instead.
2023-03-30 08:53:52 +10:00
David Reid 529e70c653 API CHANGE: Remove the onStop callback from ma_device.
Use onNotification instead.
2023-03-30 08:44:25 +10:00
David Reid ec1364d3a1 API CHANGE: Remove all ma_resource_manager_job* APIs.
Use `ma_job` instead. Anything that was previously named as
`ma_resource_manager_job*` is now named `ma_job*`.
2023-03-30 08:40:27 +10:00
David Reid 9c69d50468 API CHANGE: Update ma_audio_buffer_config_init() to take a sample rate. 2023-03-30 08:36:19 +10:00
David Reid 35a0c7b465 API CHANGE: Update ma_audio_buffer_ref_init() to accept a sample rate. 2023-03-30 08:33:56 +10:00
David Reid 8042a37822 API CHANGE: Update ma_sound_config_init().
This adds a `ma_engine*` parameter to the following functions:

  * ma_sound_config_init()
  * ma_sound_group_config_init()

The following functions have been removed (use the above functions
instead):

  * ma_sound_config_init_2()
  * ma_sound_group_config_init_2()
2023-03-30 08:31:27 +10:00
David Reid ac09b507ef API CHANGE: Remove the "wet" and "dry" parameters from the delay effect
The "dry" parameter was equivalent to pre-multiplying the input, and
the "wet" parameter was equivalent to multiplying the output. Do this
if you are needing to replicate the old functionality.

Public issue https://github.com/mackron/miniaudio/issues/548
2023-03-30 08:24:17 +10:00
David Reid 30c4b7130f API CHANGE: Remove ma_noise_set_type().
To change the noise type you need to create a new ma_noise object.
2023-03-30 08:14:09 +10:00
David Reid 83fa8f6147 API CHANGE: Remove ma_engine_get/set_time().
These are replaced with ma_engine_get/set_time_in_pcm_frames().
2023-03-30 08:11:09 +10:00
168 changed files with 80542 additions and 43485 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
Code of Conduct
===============
We don't believe we need a document telling fully grown adults how to conduct themselves within an open source
community. All we ask is that you just don't be unpleasant and keep everything on topic.
I don't believe we need a document telling fully grown adults how to conduct themselves within an open source
community. All I ask is that you just don't be unpleasant and keep everything on topic.
+1 -7
View File
@@ -9,10 +9,4 @@ assignees: ''
**DELETE ALL OF THIS TEXT BEFORE SUBMITTING**
If you think you've found a bug, it will be helpful to run with `#define MA_DEBUG_OUTPUT` above your miniaudio implementation like the code below and to include the output with this bug report:
#define MA_DEBUG_OUTPUT
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
If you are having issues with playback, please run the simple_payback_sine example and report whether or not it's consistent with what's happening in your program.
If you think you've found a bug, it will be helpful to compile with `#define MA_DEBUG_OUTPUT`. If you are having issues with playback, please run the simple_payback_sine example and report whether or not it's consistent with what's happening in your program.
+2 -2
View File
@@ -9,6 +9,6 @@ assignees: ''
**DELETE ALL OF THIS TEXT BEFORE SUBMITTING**
If you have a question about how to use the library, please read the documentation at the top of miniaudio.h and take a look at the examples. If that still doesn't answer your question, consider posting in the miniaudio subreddit at [r/miniaudio](https://www.reddit.com/r/miniaudio) or in the Discussions section here on GitHub instead. Otherwise, feel free to post your issue and we'll get to it as soon as possible.
If you have a question about how to use the library, please read the documentation at the top of miniaudio.h and take a look at the examples. If that still doesn't answer your question, consider posting in the Discussions section here on GitHub instead. Otherwise, feel free to post your issue and we'll get to it as soon as possible.
If you have an issue with playback, please run the simple_playback_sine example first and check whether or not that is working. Likewise for capture, please run the simple_capture example. If these examples work, it probably (but not always) means you're doing something wrong and a question in the subreddit or the Discussions section is more appropriate.
If you have an issue with playback, please run the simple_playback_sine example first and check whether or not that is working. Likewise for capture, please run the simple_capture example. If these examples work, it probably (but not always) means you're doing something wrong and a question in the Discussions section is more appropriate.
+4
View File
@@ -0,0 +1,4 @@
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.
+296
View File
@@ -0,0 +1,296 @@
---
name: Compilation Checks
on:
push:
branches: [master, dev, dev-0.12]
pull_request:
branches: [master, dev, dev-0.12]
jobs:
linux:
name: Linux (${{ matrix.compiler }}, ${{ matrix.config.name }})
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.config.continue_on_error || false }}
strategy:
fail-fast: false
matrix:
compiler: [gcc, clang]
config:
- name: "Default"
cmake_args: "-DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"
- name: "No Device IO"
cmake_args: >-
-DMINIAUDIO_NO_DEVICEIO=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
- name: "Force C++"
cmake_args: >-
-DMINIAUDIO_FORCE_CXX=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
- name: "Force C89"
cmake_args: >-
-DMINIAUDIO_FORCE_C89=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
continue_on_error: true
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev libpulse-dev libjack-jackd2-dev
- name: Setup compiler
run: |
if [ "${{ matrix.compiler }}" = "clang" ]; then
sudo apt-get install -y clang
echo "CC=clang" >> $GITHUB_ENV
echo "CXX=clang++" >> $GITHUB_ENV
else
echo "CC=gcc" >> $GITHUB_ENV
echo "CXX=g++" >> $GITHUB_ENV
fi
- name: Configure CMake
run: cmake -B build ${{ matrix.config.cmake_args }}
- name: Build
run: cmake --build build --parallel $(nproc)
windows:
name: Windows (${{ matrix.compiler }}, ${{ matrix.config.name }})
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
compiler: [msvc, mingw]
config:
- name: "Default"
cmake_args: "-DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"
- name: "No Device IO"
cmake_args: >-
-DMINIAUDIO_NO_DEVICEIO=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
- name: "Force C++"
cmake_args: >-
-DMINIAUDIO_FORCE_CXX=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
steps:
- uses: actions/checkout@v4
- name: Setup MSVC
if: matrix.compiler == 'msvc'
uses: ilammy/msvc-dev-cmd@v1
- name: Setup MinGW
if: matrix.compiler == 'mingw'
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: >-
mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-make
- name: Configure CMake (MSVC)
if: matrix.compiler == 'msvc'
run: cmake -B build ${{ matrix.config.cmake_args }}
- name: Configure CMake (MinGW)
if: matrix.compiler == 'mingw'
shell: msys2 {0}
run: cmake -B build -G "MinGW Makefiles" ${{ matrix.config.cmake_args }}
- name: Build (MSVC)
if: matrix.compiler == 'msvc'
run: cmake --build build --parallel
- name: Build (MinGW)
if: matrix.compiler == 'mingw'
shell: msys2 {0}
run: cmake --build build --parallel
macos:
name: macOS (${{ matrix.config.name }})
runs-on: macos-latest
continue-on-error: ${{ matrix.config.continue_on_error || false }}
strategy:
fail-fast: false
matrix:
config:
- name: "Default"
cmake_args: "-DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"
- name: "No Device IO"
cmake_args: >-
-DMINIAUDIO_NO_DEVICEIO=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
- name: "Force C++"
cmake_args: >-
-DMINIAUDIO_FORCE_CXX=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
- name: "Force C89"
cmake_args: >-
-DMINIAUDIO_FORCE_C89=ON
-DMINIAUDIO_BUILD_EXAMPLES=ON
-DMINIAUDIO_BUILD_TESTS=ON
continue_on_error: true
steps:
- uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B build ${{ matrix.config.cmake_args }}
- name: Build
run: cmake --build build --parallel $(sysctl -n hw.ncpu)
emscripten:
name: Emscripten (${{ matrix.config.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
config:
- name: "Default"
cmake_args: "-DMINIAUDIO_BUILD_TESTS=ON"
steps:
- uses: actions/checkout@v4
- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@v14
with:
version: latest
- name: Configure CMake
run: emcmake cmake -B build ${{ matrix.config.cmake_args }}
- name: Build
run: cmake --build build --parallel $(nproc)
android:
name: Android (${{ matrix.arch }}, ${{ matrix.config.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch: [arm64-v8a, armeabi-v7a, x86_64]
config:
- name: "Default"
cmake_args: "-DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"
steps:
- uses: actions/checkout@v4
- name: Setup Android NDK
uses: nttld/setup-ndk@v1
with:
ndk-version: r25c
- name: Configure CMake
run: |
TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake
cmake -B build \
-DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_FILE \
-DANDROID_ABI=${{ matrix.arch }} \
-DANDROID_PLATFORM=android-21 \
${{ matrix.config.cmake_args }}
- name: Build
run: cmake --build build --parallel $(nproc)
freebsd:
name: FreeBSD (${{ matrix.config.name }})
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
config:
- {name: "Default", cmake_args: "-DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"}
steps:
- uses: actions/checkout@v4
- name: Test on FreeBSD
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y cmake
run: |
cmake -B build ${{ matrix.config.cmake_args }}
cmake --build build --parallel $(sysctl -n hw.ncpu)
openbsd:
name: OpenBSD (${{ matrix.config.name }})
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
config:
- {name: "Default", cmake_args: "-DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"}
steps:
- uses: actions/checkout@v4
- name: Test on OpenBSD
uses: vmactions/openbsd-vm@v1
with:
usesh: true
prepare: |
pkg_add cmake
run: |
cmake -B build ${{ matrix.config.cmake_args }}
cmake --build build --parallel $(sysctl -n hw.ncpu)
netbsd:
name: NetBSD (${{ matrix.config.name }})
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
config:
- {name: "Default", cmake_args: "-DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"}
steps:
- uses: actions/checkout@v4
- name: Test on NetBSD
uses: vmactions/netbsd-vm@v1
with:
usesh: true
prepare: |
/usr/sbin/pkg_add cmake
run: |
cmake -B build ${{ matrix.config.cmake_args }}
cmake --build build
additional-configs:
name: Additional Configurations (${{ matrix.config.name }})
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.config.continue_on_error || false }}
strategy:
fail-fast: false
matrix:
config:
- name: "No SSE2"
cmake_args: "-DMINIAUDIO_NO_SSE2=ON -DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"
continue_on_error: true
- name: "No Threading"
cmake_args: "-DMINIAUDIO_NO_THREADING=ON -DMINIAUDIO_NO_DEVICEIO=ON -DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"
- {name: "No Decoding", cmake_args: "-DMINIAUDIO_NO_DECODING=ON -DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"}
- {name: "No Encoding", cmake_args: "-DMINIAUDIO_NO_ENCODING=ON -DMINIAUDIO_BUILD_EXAMPLES=ON -DMINIAUDIO_BUILD_TESTS=ON"}
steps:
- uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B build ${{ matrix.config.cmake_args }}
- name: Build
run: cmake --build build --parallel $(nproc)
+59 -11
View File
@@ -1,14 +1,62 @@
\#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/xcode/
/tests/_build/capture.wav
/tests/_build/a.out
/tests/_build/a.exe
/tests/debugging/archive/
/tests/dreamcast/miniaudio.elf
/tests/dreamcast/miniaudio_dreamcast_test.o
/tests/dreamcast/romdisk.img
/tests/dreamcast/romdisk.o
/tests/dreamcast/romdisk/test.mp3
/tests/xbox/bin/
/tests/xbox/*.d
/tests/xbox/*.obj
/tests/xbox/*.iso
/tests/xbox/*.exe
/tests/xbox/test.mp3
/tests/vita/build/
/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/
+160 -6
View File
@@ -1,3 +1,157 @@
v0.11.26 - TBD
=====================
* Fixed a crash when passing in null for the read or seek callbacks for a decoder.
v0.11.25 - 2026-03-04
=====================
* Bug fixes to the WAV decoder.
* Fixed warnings with the Emscripten build relating to the renaming of of `__EMSCRIPTEN_major/minor/tiny__` macros.
* Win32: Fixed an error with runtime linking on the UWP build. This is actually a non issue in practice because it would require miniaudio to pass in a DLL name of longer than 2048 characters which it never does.
v0.11.24 - 2026-01-17
=====================
* Fixed a possible glitch when processing the audio of a `ma_sound` when doing resampling.
* Fixed a possible crash in the node graph relating to scheduled starts and stops.
* Fixed a bug where MA_NO_DECODING would disable the WAV encoder.
* Fixed a pthread compatibility issue, particularly with Android.
* Fixed a possible crash in the resource manager.
* Fixed a possible double-uninit error when a decoder fails to initialize.
* Fixed a compilation error with the MSVC Aarch64 build.
* Addressed a few errors found through static analysis, particularly around possible null pointer dereferences.
* `ma_sound_is_playing()` will now correctly return false when called from inside the end callback of a sound.
* Miscellaneous compiler compatibility and warning fixes.
* PulseAudio: Fix a resource leak when a context fails to connect.
* Web: Fixed an error when uninitializing a context.
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.
* Fix a crash when using a node with more than 2 outputs.
* Fix a bug where `ma_standard_sample_rate_11025` uses the incorrect rate.
* Fix a bug in `ma_noise` where only white noise would be generated even when specifying pink or Brownian.
* Fix an SSE related bug when converting from mono streams.
* Documentation fixes.
* Remove the use of some deprecated functions.
* Improvements to runtime linking on Apple platforms.
* Web / Emscripten: Audio will no longer attempt to unlock in response to the "touchstart" event. This addresses an issue with iOS and Safari. This results in a change of behavior if you were previously depending on starting audio when the user's finger first touches the screen. Audio will now only unlock when the user's finger is lifted. See this discussion for details: https://github.com/mackron/miniaudio/issues/759
* Web / Emscripten: Fix an error when using a sample rate of 0 in the device config.
v0.11.18 - 2023-08-07
=====================
* Fix some AIFF compatibility issues.
* Fix an error where the cursor of a Vorbis stream is incorrectly incremented.
* Add support for setting a callback on an `ma_engine` object that get's fired after it processes a chunk of audio. This allows applications to do things such as apply a post-processing effect or output the audio to a file.
* Add `ma_engine_get_volume()`.
* Add `ma_sound_get_time_in_milliseconds()`.
* Decouple `MA_API` and `MA_PRIVATE`. This relaxes applications from needing to define both of them if they're only wanting to redefine one.
* Decoding backends will now have their onInitFile/W and onInitMemory initialization routines used where appropriate if they're defined.
* Increase the accuracy of the linear resampler when setting the ratio with `ma_linear_resampler_set_rate_ratio()`.
* Fix erroneous output with the linear resampler when in/out rates are the same.
* AAudio: Fix an error where the buffer size is not configured correctly which sometimes results in excessively high latency.
* ALSA: Fix a possible error when stopping and restarting a device.
* PulseAudio: Minor changes to stream flags.
* Win32: Fix an error where `CoUninialize()` is being called when the corresponding `CoInitializeEx()` fails.
* Web / Emscripten: Add support for AudioWorklets. This is opt-in and can be enabled by defining `MA_ENABLE_AUDIO_WORKLETS`. You must compile with `-sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY` for this to work. Requires at least Emscripten v3.1.32.
v0.11.17 - 2023-05-27
=====================
* Fix compilation errors with MA_USE_STDINT.
* Fix a possible runtime error with Windows 95/98.
* Fix a very minor linting warning in VS2022.
* Add support for AIFF/AIFC decoding.
* Add support for RIFX decoding.
* Work around some bad code generation by Clang.
* Amalgamations of dr_wav, dr_flac, dr_mp3 and c89atomic have been updated so that they're now fully namespaced. This allows each of these libraries to be able to be used alongside miniaudio without any conflicts. In addition, some duplicate code, such as sized type declarations, result codes, etc. has been removed.
v0.11.16 - 2023-05-15
=====================
* Fix a memory leak with `ma_sound_init_copy()`.
@@ -114,7 +268,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.
@@ -206,7 +360,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.
@@ -408,7 +562,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.
@@ -792,7 +946,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
@@ -901,7 +1055,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().
@@ -1051,4 +1205,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.
+1104
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -21,7 +21,7 @@ significant work without talking to me first. If I don't like it, it won't be me
[Discord](https://discord.gg/9vpqbjU) and [Twitter](https://twitter.com/mackron).
Always base your pull request branch on the "dev" branch. The master branch contains the latest release, which
means your pull request may not be including the lastest in-development changes which may result in unnecessary
means your pull request may not be including the latest in-development changes which may result in unnecessary
conflicts.
I need to review your pull requests before merging. If your pull request is non-trivial, try to break it up into
@@ -63,7 +63,7 @@ not contribute to this project.
Predictable Questions
---------------------
### "Would you consider splitting out [some section of code] into it's own file?"
### "Would you consider splitting out [some section of code] into its own file?"
No, the idea is to keep everything in one place. It would be nice in specific cases to split out specific sections
of the code, such as the resampler, for example. However, this will completely violate one of the major goals of the
project - to have a complete audio library contained within a single file.
+1 -1
View File
@@ -29,7 +29,7 @@ For more information, please refer to <http://unlicense.org/>
===============================================================================
ALTERNATIVE 2 - MIT No Attribution
===============================================================================
Copyright 2023 David Reid
Copyright 2025 David Reid
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
+23 -27
View File
@@ -7,8 +7,7 @@
<p align="center">
<a href="https://discord.gg/9vpqbjU"><img src="https://img.shields.io/discord/712952679415939085?label=discord&logo=discord&style=flat-square" alt="discord"></a>
<a href="https://fosstodon.org/@mackron"><img src="https://img.shields.io/mastodon/follow/109293691403797709?color=blue&domain=https%3A%2F%2Ffosstodon.org&label=mastodon&logo=mastodon&style=flat-square" alt="mastodon"></a>
<a href="https://www.reddit.com/r/miniaudio"><img src="https://img.shields.io/reddit/subreddit-subscribers/miniaudio?label=r%2Fminiaudio&logo=reddit&style=flat-square" alt="reddit"></a>
<a href="https://x.com/mackron"><img alt="x" src="https://img.shields.io/twitter/url?url=https%3A%2F%2Fx.com%2Fmackron&style=flat-square&logo=x&label=%40mackron"></a>
</p>
<p align="center">
@@ -17,6 +16,7 @@
<a href="#building">Building</a> -
<a href="#documentation">Documentation</a> -
<a href="#supported-platforms">Supported Platforms</a> -
<a href="#security">Security</a> -
<a href="#license">License</a>
</p>
@@ -33,7 +33,7 @@ Features
- High-level API for sound management, mixing, effects and optional 3D spatialization.
- Flexible node graph system for advanced mixing and effect processing.
- Resource management for loading sound files.
- Decoding, with built-in support for WAV, FLAC and MP3, in addition to being able to plug in custom decoders.
- Decoding, with built-in support for WAV, FLAC, and MP3, in addition to being able to plug in custom decoders.
- Encoding (WAV only).
- Data conversion.
- Resampling, including custom resamplers.
@@ -51,8 +51,7 @@ Examples
This example shows one way to play a sound using the high level API.
```c
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include "miniaudio/miniaudio.h"
#include <stdio.h>
@@ -80,8 +79,7 @@ int main()
This example shows how to decode and play a sound using the low level API.
```c
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include "miniaudio/miniaudio.h"
#include <stdio.h>
@@ -149,38 +147,26 @@ More examples can be found in the [examples](examples) folder or online here: ht
Building
========
Do the following in one source file:
```c
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
```
Then just compile. There's no need to install any dependencies. On Windows and macOS there's no need to link
to anything. On Linux just link to `-lpthread`, `-lm` and `-ldl`. On BSD just link to `-lpthread` and `-lm`.
On iOS you need to compile as Objective-C.
Just compile miniaudio.c like any other source file and include miniaudio.h like a normal header. There's no need
to install any dependencies. On Windows and macOS there's no need to link to anything. On Linux and BSD just link
to `-lpthread` and `-lm`. On iOS you need to compile as Objective-C. Link to `-ldl` if you get errors about
`dlopen()`, etc.
If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, etc. you
need to link with `-latomic`.
If you prefer separate .h and .c files, you can find a split version of miniaudio in the extras/miniaudio_split
folder. From here you can use miniaudio as a traditional .c and .h library - just add miniaudio.c to your source
tree like any other source file and include miniaudio.h like a normal header. If you prefer compiling as a
single translation unit (AKA unity builds), you can just #include the .c file in your main source file:
```c
#include "miniaudio.c"
```
Note that the split version is auto-generated using a tool and is based on the main file in the root directory.
If you want to contribute, please make the change in the main file.
ABI compatibility is not guaranteed between versions so take care if compiling as a DLL/SO. The suggested way
to integrate miniaudio is by adding it directly to your source tree.
You can also use CMake if that's your preference.
Documentation
=============
Online documentation can be found here: https://miniaud.io/docs/
Documentation can also be found at the top of [miniaudio.h](https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h)
which is always the most up-to-date and authoritive source of information on how to use miniaudio. All other
which is always the most up-to-date and authoritative source of information on how to use miniaudio. All other
documentation is generated from this in-code documentation.
@@ -193,6 +179,8 @@ Supported Platforms
- Android
- Raspberry Pi
- Emscripten / HTML5
- Dreamcast (via KallistiOS)
- Original Xbox (via NXDK)
miniaudio should compile clean on other platforms, but it will not include any support for playback or capture
by default. To support that, you would need to implement a custom backend. You can do this without needing to
@@ -204,19 +192,27 @@ Backends
- DirectSound
- WinMM
- Core Audio (Apple)
- ALSA
- PipeWire
- PulseAudio
- JACK
- ALSA
- sndio (OpenBSD)
- audio(4) (NetBSD and OpenBSD)
- OSS (FreeBSD)
- AAudio (Android 8.0+)
- OpenSL|ES (Android only)
- Web Audio (Emscripten)
- Dreamcast (via KallistiOS)
- XAudio (Original Xbox via NXDK)
- Null (Silence)
- Custom
Security
========
See the miniaudio [security policy](.github/SECURITY.md).
License
=======
Your choice of either public domain or [MIT No Attribution](https://github.com/aws/mit-0).
+13
View File
@@ -0,0 +1,13 @@
miniaudio_h := <../miniaudio.h>;
miniaudio_c := <../miniaudio.c>;
cleanup :: function(src:string) string
{
return @(src)
["\r\n"] <= "\n" // Normalize line endings to "\n". Needed for very old versions of GCC.
["\t"] <= " " // Tabs to spaces.
;
}
miniaudio_h = cleanup(@(miniaudio_h));
miniaudio_c = cleanup(@(miniaudio_c));
+312
View File
@@ -0,0 +1,312 @@
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>;
c89atomic_ring_buffer_h :: <../../c89atomic/extras/c89atomic_ring_buffer.h>;
c89atomic_ring_buffer_c :: <../../c89atomic/extras/c89atomic_ring_buffer.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 \*/"]));
// Ring Buffer
rename_c89atomic_ring_buffer_namespace :: function(src:string) string
{
return rename_c89atomic_namespace(src)
["\bma_atomic_ring_buffer"] <= "ma_ring_buffer"
["\bMA_ATOMIC"] <= "MA"
["\bMA_RING_BUFFER_API"] <= "MA_API"
["\bMA_RING_BUFFER_ASSERT"] <= "MA_ASSERT"
["\bMA_RING_BUFFER_COPY_MEMORY"] <= "MA_COPY_MEMORY"
["\bMA_RING_BUFFER_OFFSET_PTR"] <= "ma_offset_ptr"
["\bMA_RING_BUFFER_CACHE_LINE_SIZE"] <= "MA_CACHE_LINE_SIZE"
// Alignment hack.
["void\* pBuffer; "] <= "void* pBuffer;"
;
}
miniaudio_h("/\* BEG ma_ring_buffer.h \*/\R":"\R/\* END ma_ring_buffer.h \*/") = rename_c89atomic_ring_buffer_namespace(@(c89atomic_ring_buffer_h("/\* BEG c89atomic_ring_buffer.h \*/\R":"\R/\* END c89atomic_ring_buffer.h \*/")));
miniaudio_h("/\* BEG ma_ring_buffer.c \*/\R":"\R/\* END ma_ring_buffer.c \*/") = rename_c89atomic_ring_buffer_namespace(@(c89atomic_ring_buffer_c("/\* BEG c89atomic_ring_buffer.c \*/\R":"\R/\* END c89atomic_ring_buffer.c \*/")));
// Cleanup. If we don't normalize line endings we'll fail to compile on old versions of GCC.
cleanup :: function(src:string) string
{
return @(src)
["\r\n"] <= "\n" // Normalize line endings to "\n". Needed for very old versions of GCC.
["\t"] <= " " // Tabs to spaces.
;
}
miniaudio_h = cleanup(@(miniaudio_h));
miniaudio_c = cleanup(@(miniaudio_c));
+26
View File
@@ -0,0 +1,26 @@
miniaudio_h :: <../miniaudio.h>;
miniaudio_split_h := <../extras/miniaudio_split/miniaudio.h>;
miniaudio_split_c := <../extras/miniaudio_split/miniaudio.c>;
header := @(miniaudio_h["/\*" : "\*/"]);
footer := @(miniaudio_h["/\*\RThis software" : "\*/"]);
content_h : string;
content_h["$"] = header;
content_h["$"] = "\n";
content_h["$"] = @(miniaudio_h["#ifndef miniaudio_h" : "#endif /\* miniaudio_h \*/"]);
content_h["$"] = "\n\n";
content_h["$"] = footer;
content_h["$"] = "\n";
content_c : string;
content_c["$"] = header;
content_c["$"] = "\n";
content_c["$"] = '#include "miniaudio.h"\n\n';
content_c["$"] = @(miniaudio_h["#ifndef miniaudio_c" : "#endif /\* miniaudio_c \*/"]);
content_c["$"] = "\n\n";
content_c["$"] = footer;
content_c["$"] = "\n";
miniaudio_split_h = content_h;
miniaudio_split_c = content_c;
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
Sounds in this folder are used for testing purposes. They are all in the public domain. Below is a
list of all the places I pulled these sounds from.
---
https://freesound.org/people/josefpres/sounds/788664/
+4
View File
@@ -11,6 +11,10 @@ path like "C:\emsdk\emsdk_env.bat". Note that PowerShell doesn't work for me for
emcc ../simple_playback_sine.c -o bin/simple_playback_sine.html
emcc ../simple_playback_sine.c -o bin/simple_playback_sine.html -s WASM=0 -Wall -Wextra
To compile with support for Audio Worklets:
emcc ../simple_playback_sine.c -o bin/simple_playback_sine.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY
If you output WASM it may not work when running the web page locally. To test you can run with something
like this:
+76 -667
View File
@@ -1,30 +1,19 @@
/*
This example show how a custom backend can be implemented.
This example shows how to plug in custom backends.
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
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.
There may be times where you want to support more than one custom backend. This example has been designed to make it easy to plug-in extra
custom backends without needing to modify any of the base miniaudio initialization code. A custom context structure is declared called
`ma_context_ex`. The first member of this structure is a `ma_context` object which allows it to be cast between the two. The same is done
for devices, which is called `ma_device_ex`. In these structures there is a section for each custom backend, which in this example is just
SDL. These are only enabled at compile time if `MA_SUPPORT_SDL` is defined, which it always is in this example (you may want to have some
logic which more intelligently enables or disables SDL support).
To use a custom backend, at a minimum you must set the `custom.onContextInit()` callback in the context config. You do not need to set the
other callbacks, but if you don't, you must set them in the implementation of the `onContextInit()` callback which is done via an output
parameter. This is the approach taken by this example because it's the simplest way to support multiple custom backends. The idea is that
the `onContextInit()` callback is set to a generic "loader", which then calls out to a backend-specific implementation which then sets the
remaining callbacks if it is successfully initialized.
To use a custom backend you need to plug in a `ma_device_backend_vtable` pointer into the context config. You can plug in multiple
custom backends, but for this example we're just using the SDL backend which you can find in the extras folder in the miniaudio
repository. If your custom backend requires it, you can also plug in a user data pointer which will be passed to the backend callbacks.
Custom backends are identified with the `ma_backend_custom` backend type. For the purpose of demonstration, this example only uses the
`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`.
backends would actually get hit. By default, the `ma_backend_custom` backend is the second-lowest priority backend, sitting just above
`ma_backend_null`.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
/* We're using SDL for this example. To use this in your own program, you need to include miniaudio_sdl2.h after miniaudio.h. */
#include "../extras/backends/sdl2/miniaudio_sdl2.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
@@ -34,603 +23,6 @@ void main_loop__em()
}
#endif
/* Support SDL on everything. */
#define MA_SUPPORT_SDL
/*
Only enable SDL if it's hasn't been explicitly disabled (MA_NO_SDL) or enabled (MA_ENABLE_SDL with
MA_ENABLE_ONLY_SPECIFIC_BACKENDS) and it's supported at compile time (MA_SUPPORT_SDL).
*/
#if defined(MA_SUPPORT_SDL) && !defined(MA_NO_SDL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SDL))
#define MA_HAS_SDL
#endif
typedef struct
{
ma_context context; /* Make this the first member so we can cast between ma_context and ma_context_ex. */
#if defined(MA_SUPPORT_SDL)
struct
{
ma_handle hSDL; /* A handle to the SDL2 shared object. We dynamically load function pointers at runtime so we can avoid linking. */
ma_proc SDL_InitSubSystem;
ma_proc SDL_QuitSubSystem;
ma_proc SDL_GetNumAudioDevices;
ma_proc SDL_GetAudioDeviceName;
ma_proc SDL_CloseAudioDevice;
ma_proc SDL_OpenAudioDevice;
ma_proc SDL_PauseAudioDevice;
} sdl;
#endif
} ma_context_ex;
typedef struct
{
ma_device device; /* Make this the first member so we can cast between ma_device and ma_device_ex. */
#if defined(MA_SUPPORT_SDL)
struct
{
int deviceIDPlayback;
int deviceIDCapture;
} sdl;
#endif
} ma_device_ex;
#if defined(MA_HAS_SDL)
/* SDL headers are necessary if using compile-time linking. */
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#ifdef MA_EMSCRIPTEN
#if !__has_include(<SDL/SDL_audio.h>)
#undef MA_HAS_SDL
#endif
#else
#if !__has_include(<SDL2/SDL_audio.h>)
#undef MA_HAS_SDL
#endif
#endif
#endif
#endif
#endif
#if defined(MA_HAS_SDL)
#define MA_SDL_INIT_AUDIO 0x00000010
#define MA_AUDIO_U8 0x0008
#define MA_AUDIO_S16 0x8010
#define MA_AUDIO_S32 0x8020
#define MA_AUDIO_F32 0x8120
#define MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
#define MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
#define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
#define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE)
/* If we are linking at compile time we'll just #include SDL.h. Otherwise we can just redeclare some stuff to avoid the need for development packages to be installed. */
#ifdef MA_NO_RUNTIME_LINKING
#define SDL_MAIN_HANDLED
#ifdef MA_EMSCRIPTEN
#include <SDL/SDL.h>
#else
#include <SDL2/SDL.h>
#endif
typedef SDL_AudioCallback MA_SDL_AudioCallback;
typedef SDL_AudioSpec MA_SDL_AudioSpec;
typedef SDL_AudioFormat MA_SDL_AudioFormat;
typedef SDL_AudioDeviceID MA_SDL_AudioDeviceID;
#else
typedef void (* MA_SDL_AudioCallback)(void* userdata, ma_uint8* stream, int len);
typedef ma_uint16 MA_SDL_AudioFormat;
typedef ma_uint32 MA_SDL_AudioDeviceID;
typedef struct MA_SDL_AudioSpec
{
int freq;
MA_SDL_AudioFormat format;
ma_uint8 channels;
ma_uint8 silence;
ma_uint16 samples;
ma_uint16 padding;
ma_uint32 size;
MA_SDL_AudioCallback callback;
void* userdata;
} MA_SDL_AudioSpec;
#endif
typedef int (* MA_PFN_SDL_InitSubSystem)(ma_uint32 flags);
typedef void (* MA_PFN_SDL_QuitSubSystem)(ma_uint32 flags);
typedef int (* MA_PFN_SDL_GetNumAudioDevices)(int iscapture);
typedef const char* (* MA_PFN_SDL_GetAudioDeviceName)(int index, int iscapture);
typedef void (* MA_PFN_SDL_CloseAudioDevice)(MA_SDL_AudioDeviceID dev);
typedef MA_SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MA_SDL_AudioSpec* desired, MA_SDL_AudioSpec* obtained, int allowed_changes);
typedef void (* MA_PFN_SDL_PauseAudioDevice)(MA_SDL_AudioDeviceID dev, int pause_on);
MA_SDL_AudioFormat ma_format_to_sdl(ma_format format)
{
switch (format)
{
case ma_format_unknown: return 0;
case ma_format_u8: return MA_AUDIO_U8;
case ma_format_s16: return MA_AUDIO_S16;
case ma_format_s24: return MA_AUDIO_S32; /* Closest match. */
case ma_format_s32: return MA_AUDIO_S32;
case ma_format_f32: return MA_AUDIO_F32;
default: return 0;
}
}
ma_format ma_format_from_sdl(MA_SDL_AudioFormat format)
{
switch (format)
{
case MA_AUDIO_U8: return ma_format_u8;
case MA_AUDIO_S16: return ma_format_s16;
case MA_AUDIO_S32: return ma_format_s32;
case MA_AUDIO_F32: return ma_format_f32;
default: return ma_format_unknown;
}
}
static ma_result ma_context_enumerate_devices__sdl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
ma_context_ex* pContextEx = (ma_context_ex*)pContext;
ma_bool32 isTerminated = MA_FALSE;
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);
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.id.custom.i = iDevice;
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(iDevice, 0), (size_t)-1);
if (iDevice == 0) {
deviceInfo.isDefault = MA_TRUE;
}
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
if (cbResult == MA_FALSE) {
isTerminated = MA_TRUE;
break;
}
}
}
/* Capture */
if (!isTerminated) {
int deviceCount = ((MA_PFN_SDL_GetNumAudioDevices)pContextEx->sdl.SDL_GetNumAudioDevices)(1);
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.id.custom.i = iDevice;
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(iDevice, 1), (size_t)-1);
if (iDevice == 0) {
deviceInfo.isDefault = MA_TRUE;
}
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
if (cbResult == MA_FALSE) {
isTerminated = MA_TRUE;
break;
}
}
}
return MA_SUCCESS;
}
static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
{
ma_context_ex* pContextEx = (ma_context_ex*)pContext;
#if !defined(__EMSCRIPTEN__)
MA_SDL_AudioSpec desiredSpec;
MA_SDL_AudioSpec obtainedSpec;
MA_SDL_AudioDeviceID tempDeviceID;
const char* pDeviceName;
#endif
MA_ASSERT(pContext != NULL);
if (pDeviceID == NULL) {
if (deviceType == ma_device_type_playback) {
pDeviceInfo->id.custom.i = 0;
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
pDeviceInfo->id.custom.i = 0;
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
} else {
pDeviceInfo->id.custom.i = pDeviceID->custom.i;
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1), (size_t)-1);
}
if (pDeviceInfo->id.custom.i == 0) {
pDeviceInfo->isDefault = MA_TRUE;
}
/*
To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An
alternative to this is to report all channel counts, sample rates and formats, but that doesn't offer a good representation
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
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.
*/
#if defined(__EMSCRIPTEN__)
/* Good practice to prioritize the best format first so that the application can use the first data format as their chosen one if desired. */
pDeviceInfo->nativeDataFormatCount = 3;
pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channel counts supported. */
pDeviceInfo->nativeDataFormats[0].sampleRate = 0; /* All sample rates supported. */
pDeviceInfo->nativeDataFormats[0].flags = 0;
pDeviceInfo->nativeDataFormats[1].format = ma_format_s32;
pDeviceInfo->nativeDataFormats[1].channels = 0; /* All channel counts supported. */
pDeviceInfo->nativeDataFormats[1].sampleRate = 0; /* All sample rates supported. */
pDeviceInfo->nativeDataFormats[1].flags = 0;
pDeviceInfo->nativeDataFormats[2].format = ma_format_u8;
pDeviceInfo->nativeDataFormats[2].channels = 0; /* All channel counts supported. */
pDeviceInfo->nativeDataFormats[2].sampleRate = 0; /* All sample rates supported. */
pDeviceInfo->nativeDataFormats[2].flags = 0;
#else
MA_ZERO_MEMORY(&desiredSpec, sizeof(desiredSpec));
pDeviceName = NULL;
if (pDeviceID != NULL) {
pDeviceName = ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1);
}
tempDeviceID = ((MA_PFN_SDL_OpenAudioDevice)pContextEx->sdl.SDL_OpenAudioDevice)(pDeviceName, (deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (tempDeviceID == 0) {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to open SDL device.");
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
}
((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(tempDeviceID);
/* Only reporting a single native data format. It'll be whatever SDL decides is the best. */
pDeviceInfo->nativeDataFormatCount = 1;
pDeviceInfo->nativeDataFormats[0].format = ma_format_from_sdl(obtainedSpec.format);
pDeviceInfo->nativeDataFormats[0].channels = obtainedSpec.channels;
pDeviceInfo->nativeDataFormats[0].sampleRate = obtainedSpec.freq;
pDeviceInfo->nativeDataFormats[0].flags = 0;
/* If miniaudio does not support the format, just use f32 as the native format (SDL will do the necessary conversions for us). */
if (pDeviceInfo->nativeDataFormats[0].format == ma_format_unknown) {
pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
}
#endif /* __EMSCRIPTEN__ */
return MA_SUCCESS;
}
void ma_audio_callback_capture__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
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));
}
void ma_audio_callback_playback__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
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));
}
static ma_result ma_device_init_internal__sdl(ma_device_ex* pDeviceEx, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor)
{
ma_context_ex* pContextEx = (ma_context_ex*)pDeviceEx->device.pContext;
MA_SDL_AudioSpec desiredSpec;
MA_SDL_AudioSpec obtainedSpec;
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
the sample rate just set to 0, which indicates that the native sample rate should be used. There's no practical way to calculate this
that I can think of right now so I'm just using MA_DEFAULT_SAMPLE_RATE.
*/
if (pDescriptor->sampleRate == 0) {
pDescriptor->sampleRate = MA_DEFAULT_SAMPLE_RATE;
}
/*
When determining the period size, you need to take defaults into account. This is how the size of the period should be determined.
1) If periodSizeInFrames is not 0, use periodSizeInFrames; else
2) If periodSizeInMilliseconds is not 0, use periodSizeInMilliseconds; else
3) If both periodSizeInFrames and periodSizeInMilliseconds is 0, use the backend's default. If the backend does not allow a default
buffer size, use a default value of MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY or
MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE depending on the value of pConfig->performanceProfile.
Note that options 2 and 3 require knowledge of the sample rate in order to convert it to a frame count. You should try to keep the
calculation of the period size as accurate as possible, but sometimes it's just not practical so just use whatever you can.
A helper function called ma_calculate_buffer_size_in_frames_from_descriptor() is available to do all of this for you which is what
we'll be using here.
*/
pDescriptor->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile);
/* SDL wants the buffer size to be a power of 2 for some reason. */
if (pDescriptor->periodSizeInFrames > 32768) {
pDescriptor->periodSizeInFrames = 32768;
} else {
pDescriptor->periodSizeInFrames = ma_next_power_of_2(pDescriptor->periodSizeInFrames);
}
/* We now have enough information to set up the device. */
MA_ZERO_OBJECT(&desiredSpec);
desiredSpec.freq = (int)pDescriptor->sampleRate;
desiredSpec.format = ma_format_to_sdl(pDescriptor->format);
desiredSpec.channels = (ma_uint8)pDescriptor->channels;
desiredSpec.samples = (ma_uint16)pDescriptor->periodSizeInFrames;
desiredSpec.callback = (pConfig->deviceType == ma_device_type_capture) ? ma_audio_callback_capture__sdl : ma_audio_callback_playback__sdl;
desiredSpec.userdata = pDeviceEx;
/* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */
if (desiredSpec.format == 0) {
desiredSpec.format = MA_AUDIO_F32;
}
pDeviceName = NULL;
if (pDescriptor->pDeviceID != NULL) {
pDeviceName = ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(pDescriptor->pDeviceID->custom.i, (pConfig->deviceType == ma_device_type_playback) ? 0 : 1);
}
deviceID = ((MA_PFN_SDL_OpenAudioDevice)pContextEx->sdl.SDL_OpenAudioDevice)(pDeviceName, (pConfig->deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (deviceID == 0) {
ma_log_postf(ma_device_get_log((ma_device*)pDeviceEx), MA_LOG_LEVEL_ERROR, "Failed to open SDL2 device.");
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
}
if (pConfig->deviceType == ma_device_type_playback) {
pDeviceEx->sdl.deviceIDPlayback = deviceID;
} else {
pDeviceEx->sdl.deviceIDCapture = deviceID;
}
/* The descriptor needs to be updated with our actual settings. */
pDescriptor->format = ma_format_from_sdl(obtainedSpec.format);
pDescriptor->channels = obtainedSpec.channels;
pDescriptor->sampleRate = (ma_uint32)obtainedSpec.freq;
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
pDescriptor->periodSizeInFrames = obtainedSpec.samples;
pDescriptor->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */
return MA_SUCCESS;
}
static ma_result ma_device_init__sdl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
{
ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice;
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;
}
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
result = ma_device_init_internal__sdl(pDeviceEx, pConfig, pDescriptorCapture);
if (result != MA_SUCCESS) {
return result;
}
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
result = ma_device_init_internal__sdl(pDeviceEx, pConfig, pDescriptorPlayback);
if (result != MA_SUCCESS) {
if (pConfig->deviceType == ma_device_type_duplex) {
((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(pDeviceEx->sdl.deviceIDCapture);
}
return result;
}
}
return MA_SUCCESS;
}
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);
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(pDeviceEx->sdl.deviceIDCapture);
}
return MA_SUCCESS;
}
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);
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDPlayback, 0);
}
return MA_SUCCESS;
}
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);
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDPlayback, 1);
}
return MA_SUCCESS;
}
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. */
ma_dlclose(pContext, pContextEx->sdl.hSDL);
pContextEx->sdl.hSDL = NULL;
return MA_SUCCESS;
}
static ma_result ma_context_init__sdl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
ma_context_ex* pContextEx = (ma_context_ex*)pContext;
int resultSDL;
#ifndef MA_NO_RUNTIME_LINKING
/* We'll use a list of possible shared object names for easier extensibility. */
size_t iName;
const char* pSDLNames[] = {
#if defined(_WIN32)
"SDL2.dll"
#elif defined(__APPLE__)
"SDL2.framework/SDL2"
#else
"libSDL2-2.0.so.0"
#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. */
for (iName = 0; iName < ma_countof(pSDLNames); iName += 1) {
pContextEx->sdl.hSDL = ma_dlopen(pContext, pSDLNames[iName]);
if (pContextEx->sdl.hSDL != NULL) {
break;
}
}
if (pContextEx->sdl.hSDL == NULL) {
return MA_NO_BACKEND; /* SDL2 could not be loaded. */
}
/* Now that we have the handle to the shared object we can go ahead and load some function pointers. */
pContextEx->sdl.SDL_InitSubSystem = ma_dlsym(pContext, pContextEx->sdl.hSDL, "SDL_InitSubSystem");
pContextEx->sdl.SDL_QuitSubSystem = ma_dlsym(pContext, pContextEx->sdl.hSDL, "SDL_QuitSubSystem");
pContextEx->sdl.SDL_GetNumAudioDevices = ma_dlsym(pContext, pContextEx->sdl.hSDL, "SDL_GetNumAudioDevices");
pContextEx->sdl.SDL_GetAudioDeviceName = ma_dlsym(pContext, pContextEx->sdl.hSDL, "SDL_GetAudioDeviceName");
pContextEx->sdl.SDL_CloseAudioDevice = ma_dlsym(pContext, pContextEx->sdl.hSDL, "SDL_CloseAudioDevice");
pContextEx->sdl.SDL_OpenAudioDevice = ma_dlsym(pContext, pContextEx->sdl.hSDL, "SDL_OpenAudioDevice");
pContextEx->sdl.SDL_PauseAudioDevice = ma_dlsym(pContext, pContextEx->sdl.hSDL, "SDL_PauseAudioDevice");
#else
pContextEx->sdl.SDL_InitSubSystem = (ma_proc)SDL_InitSubSystem;
pContextEx->sdl.SDL_QuitSubSystem = (ma_proc)SDL_QuitSubSystem;
pContextEx->sdl.SDL_GetNumAudioDevices = (ma_proc)SDL_GetNumAudioDevices;
pContextEx->sdl.SDL_GetAudioDeviceName = (ma_proc)SDL_GetAudioDeviceName;
pContextEx->sdl.SDL_CloseAudioDevice = (ma_proc)SDL_CloseAudioDevice;
pContextEx->sdl.SDL_OpenAudioDevice = (ma_proc)SDL_OpenAudioDevice;
pContextEx->sdl.SDL_PauseAudioDevice = (ma_proc)SDL_PauseAudioDevice;
#endif /* MA_NO_RUNTIME_LINKING */
resultSDL = ((MA_PFN_SDL_InitSubSystem)pContextEx->sdl.SDL_InitSubSystem)(MA_SDL_INIT_AUDIO);
if (resultSDL != 0) {
ma_dlclose(pContext, pContextEx->sdl.hSDL);
return MA_ERROR;
}
/*
The last step is to make sure the callbacks are set properly in `pCallbacks`. Internally, miniaudio will copy these callbacks into the
context object and then use them for then on for calling into our custom backend.
*/
pCallbacks->onContextInit = ma_context_init__sdl;
pCallbacks->onContextUninit = ma_context_uninit__sdl;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sdl;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sdl;
pCallbacks->onDeviceInit = ma_device_init__sdl;
pCallbacks->onDeviceUninit = ma_device_uninit__sdl;
pCallbacks->onDeviceStart = ma_device_start__sdl;
pCallbacks->onDeviceStop = ma_device_stop__sdl;
return MA_SUCCESS;
}
#endif /* MA_HAS_SDL */
/*
This is our custom backend "loader". All this does is attempts to initialize our custom backends in the order they are listed. The first
one to successfully initialize is the one that's chosen. In this example we're just listing them statically, but you can use whatever logic
you want to handle backend selection.
This is used as the onContextInit() callback in the context config.
*/
static ma_result ma_context_init__custom_loader(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
ma_result result = MA_NO_BACKEND;
/* Silence some unused parameter warnings just in case no custom backends are enabled. */
(void)pContext;
(void)pCallbacks;
/* SDL. */
#if !defined(MA_NO_SDL)
if (result != MA_SUCCESS) {
result = ma_context_init__sdl(pContext, pConfig, pCallbacks);
}
#endif
/* ... plug in any other custom backends here ... */
/* If we have a success result we have initialized a backend. Otherwise we need to tell miniaudio about the error so it can skip over our custom backends. */
return result;
}
/*
Main program starts here.
@@ -641,15 +33,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) {
@@ -660,36 +45,36 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
int main(int argc, char** argv)
{
ma_result result;
ma_context_config contextConfig;
ma_context_ex context;
ma_context context;
ma_device_config deviceConfig;
ma_device_ex device;
ma_device device;
ma_waveform_config sineWaveConfig;
ma_waveform sineWave;
char name[256];
/*
We're just using ma_backend_custom in this example for demonstration purposes, but a more realistic use case would probably want to include
other backends as well for robustness.
Here is where we would set up the SDL-specific context-level config. The custom SDL backend allows this to be null, but we're
defining it here just for the sake of demonstration. Whether or not this is required depends on the backend. If you're not sure,
check the documentation for the backend.
*/
ma_backend backends[] = {
ma_backend_custom
ma_context_config_sdl2 sdl2ContextConfig = ma_context_config_sdl2_init();
sdl2ContextConfig._unused = 0;
/*
You must include an entry for each backend you're using, even if the config is NULL. This is how miniaudio knows about
your custom backend.
For stock backends, you can just leave the config pointer as NULL and fill out any backend-specific config options in
the ma_context_config structure. Same with device configs.
*/
ma_device_backend_config backends[] =
{
{ ma_device_backend_sdl2, &sdl2ContextConfig },
{ ma_device_backend_wasapi, NULL },
{ ma_device_backend_pulseaudio, NULL }
};
/*
To implement a custom backend you need to implement the callbacks in the "custom" member of the context config. The only mandatory
callback required at this point is the onContextInit() callback. If you do not set the other callbacks, you must set them in
onContextInit() by setting them on the `pCallbacks` parameter.
The way we're doing it in this example enables us to easily plug in multiple custom backends. What we do is set the onContextInit()
callback to a generic "loader" function (ma_context_init__custom_loader() in this example), which then calls out to backend-specific
context initialization routines, one of which will be for SDL. That way, if for example we wanted to add support for another backend,
we don't need to touch this part of the code. Instead we add logic to ma_context_init__custom_loader() to choose the most appropriate
custom backend. That will then fill out the other callbacks appropriately.
*/
contextConfig = ma_context_config_init();
contextConfig.custom.onContextInit = ma_context_init__custom_loader;
result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &contextConfig, (ma_context*)&context);
result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), NULL, &context);
if (result != MA_SUCCESS) {
return -1;
}
@@ -698,28 +83,49 @@ int main(int argc, char** argv)
sineWaveConfig = ma_waveform_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, ma_waveform_type_sine, 0.2, 220);
ma_waveform_init(&sineWaveConfig, &sineWave);
/* The device is created exactly as per normal. */
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = DEVICE_FORMAT;
deviceConfig.playback.channels = DEVICE_CHANNELS;
deviceConfig.capture.format = DEVICE_FORMAT;
deviceConfig.capture.channels = DEVICE_CHANNELS;
deviceConfig.sampleRate = DEVICE_SAMPLE_RATE;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = &sineWave;
result = ma_device_init((ma_context*)&context, &deviceConfig, (ma_device*)&device);
/*
Just like with context configs, we can define some device-level configs as well. It works the same way, except you will pass in
a backend-specific device-level config. If the backend doesn't require a device-level config, you can set this to NULL.
*/
ma_device_config_sdl2 sdl2DeviceConfig = ma_device_config_sdl2_init();
sdl2DeviceConfig._unused = 0;
/*
Unlike with contexts, if your backend does not require a device-level config, you can just leave it out of this list entirely.
*/
ma_device_backend_config pBackendDeviceConfigs[] =
{
{ ma_device_backend_sdl2, &sdl2DeviceConfig },
{ ma_device_backend_wasapi, NULL },
{ ma_device_backend_pulseaudio, NULL }
};
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = DEVICE_FORMAT;
deviceConfig.playback.channels = DEVICE_CHANNELS;
deviceConfig.capture.format = DEVICE_FORMAT;
deviceConfig.capture.channels = DEVICE_CHANNELS;
deviceConfig.sampleRate = DEVICE_SAMPLE_RATE;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = &sineWave;
deviceConfig.pBackendConfigs = pBackendDeviceConfigs;
deviceConfig.backendConfigCount = sizeof(pBackendDeviceConfigs) / sizeof(pBackendDeviceConfigs[0]);
result = ma_device_init(&context, &deviceConfig, &device);
if (result != MA_SUCCESS) {
ma_context_uninit((ma_context*)&context);
ma_context_uninit(&context);
return -1;
}
printf("Device Name: %s\n", ((ma_device*)&device)->playback.name);
ma_device_get_name(&device, ma_device_type_playback, name, sizeof(name), NULL);
printf("Device Name: %s\n", name);
if (ma_device_start((ma_device*)&device) != MA_SUCCESS) {
ma_device_uninit((ma_device*)&device);
ma_context_uninit((ma_context*)&context);
if (ma_device_start(&device) != MA_SUCCESS) {
ma_device_uninit(&device);
ma_context_uninit(&context);
return -5;
}
@@ -729,12 +135,15 @@ int main(int argc, char** argv)
printf("Press Enter to quit...\n");
getchar();
#endif
ma_device_uninit((ma_device*)&device);
ma_context_uninit((ma_context*)&context);
ma_device_uninit(&device);
ma_context_uninit(&context);
(void)argc;
(void)argv;
return 0;
}
}
/* We put the SDL implementation here just to simplify the compilation process. This way you need only compile custom_backend.c. */
#include "../extras/backends/sdl2/miniaudio_sdl2.c"
+30 -169
View File
@@ -1,11 +1,20 @@
/*
Demonstrates how to implement a custom decoder.
Demonstrates how to plug in custom decoders.
This example implements two custom decoders:
* Vorbis via libvorbis
* Opus via libopus
The files miniaudio_libvorbis.h and miniaudio_libopus.h are where the custom decoders are implemented.
Refer to these files for an example of how you can implement your own custom decoders.
To wire up your custom decoders to the `ma_decoder` API, you need to set up a `ma_decoder_config`
object and fill out the `ppBackendVTables` and `backendCount` members. The `ppBackendVTables` member
is an array of pointers to `ma_decoding_backend_vtable` objects. The order of the array defines the
order of priority, with the first being the highest priority. The `backendCount` member is the number
of items in the `ppBackendVTables` array.
A custom decoder must implement a data source. In this example, the libvorbis data source is called
`ma_libvorbis` and the Opus data source is called `ma_libopus`. These two objects are compatible
with the `ma_data_source` APIs and can be taken straight from this example and used in real code.
@@ -15,168 +24,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)
{
ma_data_source* pDataSource = (ma_data_source*)pDevice->pUserData;
@@ -201,13 +54,22 @@ int main(int argc, char** argv)
ma_uint32 sampleRate;
/*
Add your custom backend vtables here. The order in the array defines the order of priority. The
vtables will be passed in via the decoder config.
Add your custom backend vtables here. The order in the array defines the order of priority, with the
first being the highest priority. The vtables are be passed in via the decoder config. If you want to
support stock backends in addition to custom backends, you must add the stock backend vtables here as
well. You should list the backends in your preferred order of priority.
The list below shows how you would set up your array to prioritize the custom decoders over the stock
decoders. If you want to prioritize the stock decoders over the custom decoders, you would simply
change the order.
*/
ma_decoding_backend_vtable* pCustomBackendVTables[] =
ma_decoding_backend_vtable* pBackendVTables[] =
{
&g_ma_decoding_backend_vtable_libvorbis,
&g_ma_decoding_backend_vtable_libopus
ma_decoding_backend_libvorbis,
ma_decoding_backend_libopus,
ma_decoding_backend_wav,
ma_decoding_backend_flac,
ma_decoding_backend_mp3
};
@@ -219,9 +81,8 @@ 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.ppCustomBackendVTables = pCustomBackendVTables;
decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
decoderConfig.ppBackendVTables = pBackendVTables;
decoderConfig.backendCount = sizeof(pBackendVTables) / sizeof(pBackendVTables[0]);
result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder);
if (result != MA_SUCCESS) {
@@ -267,4 +128,4 @@ int main(int argc, char** argv)
ma_decoder_uninit(&decoder);
return 0;
}
}
+23 -167
View File
@@ -5,167 +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)
{
ma_result result;
@@ -175,13 +20,22 @@ int main(int argc, char** argv)
ma_engine engine;
/*
Add your custom backend vtables here. The order in the array defines the order of priority. The
vtables will be passed in to the resource manager config.
Add your custom backend vtables here. The order in the array defines the order of priority, with the
first being the highest priority. The vtables are be passed in via the decoder config. If you want to
support stock backends in addition to custom backends, you must add the stock backend vtables here as
well. You should list the backends in your preferred order of priority.
The list below shows how you would set up your array to prioritize the custom decoders over the stock
decoders. If you want to prioritize the stock decoders over the custom decoders, you would simply
change the order.
*/
ma_decoding_backend_vtable* pCustomBackendVTables[] =
ma_decoding_backend_vtable* pBackendVTables[] =
{
&g_ma_decoding_backend_vtable_libvorbis,
&g_ma_decoding_backend_vtable_libopus
ma_decoding_backend_libvorbis,
ma_decoding_backend_libopus,
ma_decoding_backend_wav,
ma_decoding_backend_flac,
ma_decoding_backend_mp3
};
@@ -193,9 +47,8 @@ int main(int argc, char** argv)
/* Using custom decoding backends requires a resource manager. */
resourceManagerConfig = ma_resource_manager_config_init();
resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
resourceManagerConfig.pCustomDecodingBackendUserData = NULL; /* <-- This will be passed in to the pUserData parameter of each function in the decoding backend vtables. */
resourceManagerConfig.ppDecodingBackendVTables = pBackendVTables;
resourceManagerConfig.decodingBackendCount = sizeof(pBackendVTables) / sizeof(pBackendVTables[0]);
result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager);
if (result != MA_SUCCESS) {
@@ -226,5 +79,8 @@ int main(int argc, char** argv)
printf("Press Enter to quit...");
getchar();
ma_engine_uninit(&engine);
ma_resource_manager_uninit(&resourceManager);
return 0;
}
}
+11 -9
View File
@@ -31,9 +31,7 @@ starting the chain from the start again. It is also seeking the head data source
so that playback starts from the start as expected. You do not need to seek non-head items back to
the start as miniaudio will do that for you internally.
*/
#define MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -49,7 +47,11 @@ ma_decoder* g_pDecoders;
static ma_data_source* next_callback_tail(ma_data_source* pDataSource)
{
MA_ASSERT(g_decoderCount > 0); /* <-- We check for this in main() so should never happen. */
(void)pDataSource; /* Unused. */
if (g_decoderCount > 0) { /* <-- We check for this in main() so should never happen. */
return NULL;
}
/*
This will be fired when the last item in the chain has reached the end. In this example we want
@@ -131,15 +133,15 @@ int main(int argc, char** argv)
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = NULL;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
result = ma_device_init(NULL, &deviceConfig, &device);
if (result != MA_SUCCESS) {
printf("Failed to open playback device.\n");
result = -1;
goto done_decoders;
}
if (ma_device_start(&device) != MA_SUCCESS) {
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
printf("Failed to start playback device.\n");
result = -1;
goto done;
}
@@ -156,4 +158,4 @@ done_decoders:
free(g_pDecoders);
return 0;
}
}
+21 -14
View File
@@ -6,26 +6,30 @@ 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. */
static ma_audio_buffer_ref g_exciteData; /* The underlying data source of the excite node. */
static ma_data_source_node g_sourceNode; /* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */
static ma_data_source_node g_exciteNode; /* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */
static ma_vocoder_node g_vocoderNode; /* The vocoder node. */
static ma_node_graph g_nodeGraph;
static ma_waveform g_sourceData; /* The underlying data source of the source node. */
static ma_audio_ring_buffer g_exciteData; /* The underlying data source of the excite node. */
static ma_data_source_node g_sourceNode; /* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */
static ma_data_source_node g_exciteNode; /* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */
static ma_vocoder_node g_vocoderNode; /* The vocoder node. */
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
@@ -33,7 +37,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
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_exciteData, pInput, frameCount);
ma_audio_ring_buffer_write_pcm_frames(&g_exciteData, pInput, frameCount, NULL);
/* 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);
@@ -49,6 +53,7 @@ int main(int argc, char** argv)
ma_data_source_node_config sourceNodeConfig;
ma_data_source_node_config exciteNodeConfig;
ma_waveform_config waveformConfig;
ma_audio_ring_buffer_config ringBufferConfig;
deviceConfig = ma_device_config_init(ma_device_type_duplex);
deviceConfig.capture.pDeviceID = NULL;
@@ -111,7 +116,9 @@ int main(int argc, char** argv)
/* Excite/modulator. Attached to input bus 1 of the vocoder node. */
result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_exciteData);
ringBufferConfig = ma_audio_ring_buffer_config_init(device.capture.format, device.capture.channels, device.sampleRate, device.capture.internalPeriodSizeInFrames * 3);
result = ma_audio_ring_buffer_init(&ringBufferConfig, &g_exciteData);
if (result != MA_SUCCESS) {
printf("Failed to initialize audio buffer for source.");
goto done2;
@@ -139,7 +146,7 @@ int main(int argc, char** argv)
/*done4:*/ ma_data_source_node_uninit(&g_exciteNode, NULL);
done3: ma_data_source_node_uninit(&g_sourceNode, NULL);
done2: ma_vocoder_node_uninit(&g_vocoderNode, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph);
done0: ma_device_uninit(&device);
(void)argc;
+1 -2
View File
@@ -14,8 +14,7 @@ Using a shared resource manager, as we do in this example, is useful for when yo
multiple engines so that you can output to multiple playback devices simultaneoulys. An example
might be a local co-op multiplayer game where each player has their own headphones.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#define MAX_DEVICES 2
#define MAX_SOUNDS 32
+2 -3
View File
@@ -13,8 +13,7 @@ This example is playing only a single sound at a time which means only a single
it being used. If you want to play multiple sounds at the same time, even if they're for the same
sound file, you need multiple `ma_sound` objects.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#define DELAY_IN_SECONDS 0.2f
#define DECAY 0.25f /* Volume falloff for each echo. */
@@ -101,4 +100,4 @@ int main(int argc, char** argv)
ma_engine_uninit(&g_engine);
return 0;
}
}
+1 -2
View File
@@ -3,8 +3,7 @@ This example demonstrates how to initialize an audio engine and play a sound.
This will play the sound specified on the command line.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
+7 -4
View File
@@ -9,11 +9,10 @@ This example will load the sound specified on the command line and rotate it aro
head.
*/
#define MA_NO_DEVICE_IO /* <-- Disables the `ma_device` API. We don't need that in this example since SDL will be doing that part for us. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#define SDL_MAIN_HANDLED
#include <SDL.h> /* Change this to your include location. Might be <SDL2/SDL.h>. */
#include <SDL2/SDL.h> /* Change this to your include location. Might be <SDL.h>. */
#define CHANNELS 2 /* Must be stereo for this example. */
#define SAMPLE_RATE 48000
@@ -23,8 +22,12 @@ static ma_sound g_sound; /* This example will play only a single soun
void data_callback(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
ma_uint32 bufferSizeInFrames;
(void)pUserData;
/* Reading is just a matter of reading straight from the engine. */
ma_uint32 bufferSizeInFrames = (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&g_engine));
bufferSizeInFrames = (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&g_engine));
ma_engine_read_pcm_frames(&g_engine, pBuffer, bufferSizeInFrames, NULL);
}
+38 -22
View File
@@ -8,23 +8,26 @@ By implementing this as a node, it can be plugged into any position within the g
channel count of this node is always stereo.
Steam Audio requires fixed sized processing, the size of which must be specified at initialization
time of the IPLBinauralEffect and IPLHRTF objects. This creates a problem because the node graph
will at times need to break down processing into smaller chunks for it's internal processing. The
node graph internally will read into a temporary buffer which is then mixed into the final output
buffer. This temporary buffer is allocated on the stack and is a fixed size. However, variability
comes into play because the channel count of the node is variable. It's not safe to just blindly
process the effect with the frame count specified in miniaudio's node processing callback. Doing so
results in glitching. To work around this, this example is just setting the update size to a known
value that works (256). If it's set to something too big it'll exceed miniaudio's processing size
used by the node graph. Alternatively you could use some kind of intermediary cache which
accumulates input data until enough is available and then do the processing. Ideally, Steam Audio
would support variable sized updates which would avoid this whole mess entirely.
time of the IPLBinauralEffect and IPLHRTF objects. To ensure miniaudio and Steam Audio are
consistent, you must set the period size in the engine config to be consistent with the frame size
you specify in your IPLAudioSettings object. If for some reason you want the period size of the
engine to be different to that of your Steam Audio configuration, you'll need to implement a sort
of buffering solution to your node.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdint.h> /* Required for uint32_t which is used by STEAMAUDIO_VERSION, and a random use of uint8_t. If there's a Steam Audio maintainer reading this, that needs to be fixed to use IPLuint32 and IPLuint8. */
/* Need to silence some warnings from the Steam Audio headers. */
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlong-long"
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#include <phonon.h> /* Steam Audio */
#include <stdint.h> /* Required for uint32_t which is used by STEAMAUDIO_VERSION. That dependency needs to be removed from Steam Audio - use IPLuint32 or "unsigned int" instead! */
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic pop
#endif
#define FORMAT ma_format_f32 /* Must be floating point. */
#define CHANNELS 2 /* Must be stereo for this example. */
@@ -98,6 +101,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
ma_uint32 totalFramesToProcess = *pFrameCountOut;
ma_uint32 totalFramesProcessed = 0;
MA_ZERO_OBJECT(&binauralParams);
binauralParams.direction.x = pBinauralNode->direction.x;
binauralParams.direction.y = pBinauralNode->direction.y;
binauralParams.direction.z = pBinauralNode->direction.z;
@@ -123,7 +127,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
pBinauralNode->ppBuffersIn[0] = (float*)ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, 1);
} else {
/* Slow path. Need to deinterleave the input data. */
ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), pBinauralNode->ppBuffersIn);
ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), (void**)&pBinauralNode->ppBuffersIn[0]);
}
inputBufferDesc.data = pBinauralNode->ppBuffersIn;
@@ -133,7 +137,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
iplBinauralEffectApply(pBinauralNode->iplEffect, &binauralParams, &inputBufferDesc, &outputBufferDesc);
/* Interleave straight into the output buffer. */
ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, pBinauralNode->ppBuffersOut, ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, (const void**)&pBinauralNode->ppBuffersOut[0], ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
/* Advance. */
totalFramesProcessed += framesToProcessThisIteration;
@@ -179,7 +183,7 @@ MA_API ma_result ma_steamaudio_binaural_node_init(ma_node_graph* pNodeGraph, con
channelsOut = 2; /* Always stereo output. */
baseConfig = ma_node_config_init();
baseConfig.vtable = &g_ma_steamaudio_binaural_node_vtable;
baseConfig.pVTable = &g_ma_steamaudio_binaural_node_vtable;
baseConfig.pInputChannels = &channelsIn;
baseConfig.pOutputChannels = &channelsOut;
result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pBinauralNode->baseNode);
@@ -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) {
@@ -340,7 +356,7 @@ int main(int argc, char** argv)
{
ma_sound_config soundConfig;
soundConfig = ma_sound_config_init();
soundConfig = ma_sound_config_init(&g_engine);
soundConfig.pFilePath = argv[1];
soundConfig.flags = MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We'll attach this to the graph later. */
@@ -429,4 +445,4 @@ int main(int argc, char** argv)
ma_engine_uninit(&g_engine);
return 0;
}
}
+13 -44
View File
@@ -15,50 +15,27 @@ 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_audio_ring_buffer rb;
static ma_device device;
static ma_engine engine;
static ma_sound sound; /* The sound will be the playback of the capture side. */
void capture_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{
ma_result result;
ma_uint32 framesWritten;
(void)pFramesOut;
(void)pDevice;
/* We need to write to the ring buffer. Need to do this in a loop. */
framesWritten = 0;
while (framesWritten < frameCount) {
void* pMappedBuffer;
ma_uint32 framesToWrite = frameCount - framesWritten;
result = ma_pcm_rb_acquire_write(&rb, &framesToWrite, &pMappedBuffer);
if (result != MA_SUCCESS) {
break;
}
if (framesToWrite == 0) {
break;
}
/* 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);
result = ma_pcm_rb_commit_write(&rb, framesToWrite);
if (result != MA_SUCCESS) {
break;
}
framesWritten += framesToWrite;
}
/* We need to write to the ring buffer. */
ma_audio_ring_buffer_write_pcm_frames(&rb, pFramesIn, frameCount, NULL);
}
int main(int argc, char** argv)
{
ma_result result;
ma_device_config deviceConfig;
ma_audio_ring_buffer_config ringBufferConfig;
/*
The first thing we'll do is set up the capture side. There are two parts to this. The first is
@@ -82,20 +59,15 @@ int main(int argc, char** argv)
return -1;
}
/* Initialize the ring buffer. */
result = ma_pcm_rb_init(device.capture.format, device.capture.channels, device.capture.internalPeriodSizeInFrames * 5, NULL, NULL, &rb);
/* Initialize the ring buffer. Make sure the sample rate is set so the engine can resample it if necessary. */
ringBufferConfig = ma_audio_ring_buffer_config_init(device.capture.format, device.capture.channels, device.sampleRate, device.capture.internalPeriodSizeInFrames * 3);
result = ma_audio_ring_buffer_init(&ringBufferConfig, &rb);
if (result != MA_SUCCESS) {
printf("Failed to initialize the ring buffer.");
return -1;
}
/*
Ring buffers don't require a sample rate for their normal operation, but we can associate it
with a sample rate. We'll want to do this so the engine can resample if necessary.
*/
ma_pcm_rb_set_sample_rate(&rb, device.sampleRate);
/*
At this point the capture side is set up and we can now set up the playback side. Here we are
@@ -119,15 +91,12 @@ int main(int argc, char** argv)
ring buffer. The capture side will be writing data into the ring buffer, whereas the sound
will be reading from it.
*/
result = ma_sound_init_from_data_source(&engine, &rb, 0, NULL, &sound);
result = ma_sound_init_from_data_source(&engine, &rb, 0, NULL, NULL, &sound);
if (result != MA_SUCCESS) {
printf("Failed to initialize the sound.");
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);
@@ -139,7 +108,7 @@ int main(int argc, char** argv)
ma_sound_uninit(&sound);
ma_engine_uninit(&engine);
ma_device_uninit(&device);
ma_pcm_rb_uninit(&rb);
ma_audio_ring_buffer_uninit(&rb);
(void)argc;
+3 -5
View File
@@ -51,8 +51,7 @@ pass and echo effects so that one of them becomes more obvious than the other.
When you want to read from the graph, you simply call `ma_node_graph_read_pcm_frames()`.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
/* Data Format */
#define FORMAT ma_format_f32 /* Must always be f32. */
@@ -81,8 +80,6 @@ static int g_soundNodeCount;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->playback.channels == CHANNELS);
/*
Hearing the output of the node graph is as easy as reading straight into the output buffer. You just need to
make sure you use a consistent data format or else you'll need to do your own conversion.
@@ -90,6 +87,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL);
(void)pInput; /* Unused. */
(void)pDevice; /* Unused. */
}
int main(int argc, char** argv)
@@ -243,7 +241,7 @@ cleanup_graph:
ma_lpf_node_uninit(&g_lpfNode, NULL);
/* Node Graph */
ma_node_graph_uninit(&g_nodeGraph, NULL);
ma_node_graph_uninit(&g_nodeGraph);
}
return 0;
+1 -3
View File
@@ -24,8 +24,7 @@ data from the data source. This means the resource manager will ensure all sound
set, each sound will have their own formats and you'll need to do the necessary data conversion yourself.
*/
#define MA_NO_ENGINE /* We're intentionally not using the ma_engine API here. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
@@ -33,7 +32,6 @@ set, each sound will have their own formats and you'll need to do the necessary
void main_loop__em(void* pUserData)
{
ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
MA_ASSERT(pResourceManager != NULL);
/*
The Emscripten build does not support threading which means we need to process jobs manually. If
+22 -15
View File
@@ -15,8 +15,7 @@ In this example we show how you can create a data source, mix them with other da
threads to manage internally and how to implement your own custom job thread.
*/
#define MA_NO_ENGINE /* We're intentionally not using the ma_engine API here. */
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
static ma_resource_manager_data_source g_dataSources[16];
static ma_uint32 g_dataSourceCount;
@@ -32,8 +31,6 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
This function is intended to be used when the format and channel count of the data source is
known beforehand. The idea is to avoid overhead due to redundant calls to ma_data_source_get_data_format().
*/
MA_ASSERT(pDataSource != NULL);
if (dataSourceFormat == ma_format_f32) {
/* Fast path. No conversion necessary. */
return ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, pFramesRead);
@@ -43,6 +40,10 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
ma_uint64 totalFramesRead;
ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint64 tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
@@ -52,7 +53,10 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
framesToRead = tempCapInFrames;
}
result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, framesToRead, &framesJustRead);
result = ma_data_source_read_pcm_frames(pDataSource, temp, framesToRead, &framesJustRead);
if (result != MA_SUCCESS) {
break;
}
ma_convert_pcm_frames_format(ma_offset_pcm_frames_ptr_f32(pFramesOut, totalFramesRead, dataSourceChannels), ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
totalFramesRead += framesJustRead;
@@ -62,6 +66,10 @@ static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSour
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return MA_SUCCESS;
}
}
@@ -112,7 +120,7 @@ MA_API ma_result ma_data_source_read_pcm_frames_and_mix_f32(ma_data_source* pDat
result = ma_data_source_read_pcm_frames_f32_ex(pDataSource, temp, framesToRead, &framesJustRead, format, channels);
ma_mix_pcm_frames_f32(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), temp, framesJustRead, channels, volume);
ma_mix_pcm_frames_f32(ma_offset_pcm_frames_ptr_f32(pFramesOut, totalFramesRead, channels), temp, framesJustRead, channels, volume);
totalFramesRead += framesJustRead;
if (result != MA_SUCCESS) {
@@ -136,10 +144,6 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
*/
ma_uint32 iDataSource;
MA_ASSERT(pDevice->playback.format == ma_format_f32);
(void)pInput; /* Unused. */
/*
If the device was configured with noPreSilencedOutputBuffer then you would need to silence the
buffer here, or make sure the first data source to be mixed is copied rather than mixed.
@@ -150,16 +154,19 @@ 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;
ma_resource_manager_job job;
ma_job job;
/*
Retrieve a job from the queue first. This defines what it is you're about to do. By default this will be
@@ -191,8 +198,8 @@ static ma_thread_result MA_THREADCALL custom_job_thread(void* pUserData)
event is received which means the `result != MA_SUCCESS` logic above will catch it. If you do not check the
return value of ma_resource_manager_next_job() you will want to check for MA_RESOURCE_MANAGER_JOB_QUIT like the code below.
*/
if (job.toc.breakup.code == MA_RESOURCE_MANAGER_JOB_QUIT) {
printf("CUSTOM JOB THREAD TERMINATING VIA MA_RESOURCE_MANAGER_JOB_QUIT... ");
if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
printf("CUSTOM JOB THREAD TERMINATING VIA MA_JOB_TYPE_QUIT... ");
break;
}
@@ -269,7 +276,7 @@ int main(int argc, char** argv)
ma_thread_create(&jobThread, ma_thread_priority_default, 0, custom_job_thread, &resourceManager, NULL);
/* Create each data source from the resource manager. Note that the caller is the owner. */
for (iFile = 0; iFile < ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) {
for (iFile = 0; iFile < (int)ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) {
result = ma_resource_manager_data_source_init(
&resourceManager,
argv[iFile+1],
+2 -6
View File
@@ -8,18 +8,14 @@ Capturing works in a very similar way to playback. The only difference is the di
the application sending data to the device, the device will send data to the application. This example just writes the
data received by the microphone straight to a WAV file.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdlib.h>
#include <stdio.h>
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
MA_ASSERT(pEncoder != NULL);
ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
ma_encoder_write_pcm_frames((ma_encoder*)pDevice->pUserData, pInput, frameCount, NULL);
(void)pOutput;
}
+5 -4
View File
@@ -10,8 +10,7 @@ glitching which the backend may not be able to recover from. For this reason, mi
sample rate for both capture and playback. If internally the native sample rates differ, miniaudio will perform the
sample rate conversion for you automatically.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -23,8 +22,10 @@ void main_loop__em()
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
MA_ASSERT(pDevice->capture.format == pDevice->playback.format);
MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels);
/* This example assumes the playback and capture sides use the same format and channel count. */
if (pDevice->capture.format != pDevice->playback.format || pDevice->capture.channels != pDevice->playback.channels) {
return;
}
/* In this example the format and channel count are the same for both input and output which means we can just memcpy(). */
MA_COPY_MEMORY(pOutput, pInput, frameCount * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
+4 -5
View File
@@ -1,14 +1,13 @@
/*
Demonstrates how to enumerate over devices.
Device enumaration requires a `ma_context` object which is initialized with `ma_context_init()`. Conceptually, the
Device enumeration requires a `ma_context` object which is initialized with `ma_context_init()`. Conceptually, the
context sits above a device. You can have many devices to one context.
If you use device enumeration, you should explicitly specify the same context you used for enumeration in the call to
`ma_device_init()` when you initialize your devices.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -35,14 +34,14 @@ int main(int argc, char** argv)
printf("Playback Devices\n");
for (iDevice = 0; iDevice < playbackDeviceCount; ++iDevice) {
printf(" %u: %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name);
printf(" %u: %s%s\n", iDevice, pPlaybackDeviceInfos[iDevice].name, (pPlaybackDeviceInfos[iDevice].isDefault) ? " [Default]" : "");
}
printf("\n");
printf("Capture Devices\n");
for (iDevice = 0; iDevice < captureDeviceCount; ++iDevice) {
printf(" %u: %s\n", iDevice, pCaptureDeviceInfos[iDevice].name);
printf(" %u: %s%s\n", iDevice, pCaptureDeviceInfos[iDevice].name, (pPlaybackDeviceInfos[iDevice].isDefault) ? " [Default]" : "");
}
+4 -8
View File
@@ -10,18 +10,14 @@ used indirectly with PulseAudio by choosing the appropriate loopback device afte
To use loopback mode you just need to set the device type to ma_device_type_loopback and set the capture device config
properties. The output buffer in the callback will be null whereas the input buffer will be valid.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdlib.h>
#include <stdio.h>
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
MA_ASSERT(pEncoder != NULL);
ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
ma_encoder_write_pcm_frames((ma_encoder*)pDevice->pUserData, pInput, frameCount, NULL);
(void)pOutput;
}
@@ -35,8 +31,8 @@ int main(int argc, char** argv)
ma_device device;
/* Loopback mode is currently only supported on WASAPI. */
ma_backend backends[] = {
ma_backend_wasapi
ma_device_backend_config backends[] = {
{ ma_device_backend_wasapi, NULL }
};
if (argc < 2) {
+1 -2
View File
@@ -5,8 +5,7 @@ This example uses a decoder as the data source. Decoders can be used with the `m
supports looping via the `ma_data_source_read_pcm_frames()` API. To use it, all you need to do is pass a pointer to the
decoder straight into `ma_data_source_read_pcm_frames()` and it will just work.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
+5 -6
View File
@@ -4,15 +4,14 @@ Demonstrates one way to load multiple files and play them all back at the same t
When mixing multiple sounds together, you should not create multiple devices. Instead you should create only a single
device and then mix your sounds together which you can do by simply summing their samples together. The simplest way to
do this is to use floating point samples and use miniaudio's built-in clipper to handling clipping for you. (Clipping
is when sample are clampled to their minimum and maximum range, which for floating point is -1..1.)
is when sample are clamped to their minimum and maximum range, which for floating point is -1..1.)
```
Usage: simple_mixing [input file 0] [input file 1] ... [input file n]
Example: simple_mixing file1.wav file2.flac
```
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -29,7 +28,7 @@ ma_bool32* g_pDecodersAtEnd;
ma_event g_stopEvent; /* <-- Signaled by the audio thread, waited on by the main thread. */
ma_bool32 are_all_decoders_at_end()
ma_bool32 are_all_decoders_at_end(void)
{
ma_uint32 iDecoder;
for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
@@ -87,8 +86,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
float* pOutputF32 = (float*)pOutput;
ma_uint32 iDecoder;
MA_ASSERT(pDevice->playback.format == SAMPLE_FORMAT); /* <-- Important for this example. */
/* This example assumes the device was configured to use ma_format_f32. */
for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
if (!g_pDecodersAtEnd[iDecoder]) {
ma_uint32 framesRead = read_and_mix_pcm_frames_f32(&g_pDecoders[iDecoder], pOutputF32, frameCount);
@@ -107,6 +105,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
}
(void)pInput;
(void)pDevice;
}
int main(int argc, char** argv)
+1 -2
View File
@@ -10,8 +10,7 @@ device and can be used independently of it. This example only plays back a singl
back multiple files by simple loading multiple decoders and mixing them (do not create multiple devices to do this). See
the simple_mixing example for how best to do this.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
+3 -12
View File
@@ -14,8 +14,7 @@ This example works with Emscripten.
*/
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include "../miniaudio.c"
#include <stdio.h>
@@ -33,14 +32,7 @@ void main_loop__em()
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_waveform* pSineWave;
MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS);
pSineWave = (ma_waveform*)pDevice->pUserData;
MA_ASSERT(pSineWave != NULL);
ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL);
ma_waveform_read_pcm_frames((ma_waveform*)pDevice->pUserData, pOutput, frameCount, NULL);
(void)pInput; /* Unused. */
}
@@ -64,8 +56,6 @@ int main(int argc, char** argv)
return -4;
}
printf("Device Name: %s\n", device.playback.name);
sineWaveConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.2, 220);
ma_waveform_init(&sineWaveConfig, &sineWave);
@@ -83,6 +73,7 @@ int main(int argc, char** argv)
#endif
ma_device_uninit(&device);
ma_waveform_uninit(&sineWave); /* Uninitialize the waveform after the device so we don't pull it from under the device while it's being reference in the data callback. */
(void)argc;
(void)argv;
+85
View File
@@ -0,0 +1,85 @@
/*
Demonstrates how to do basic spatialization via the high level API.
You can position and orientate sounds to create a simple spatialization effect. This example shows
how to do this.
In addition to positioning sounds, there is the concept of a listener. This can also be positioned
and orientated to help with spatialization.
This example only covers the basics to get your started. See the documentation for more detailed
information on the available features.
To use this example, pass in the path of a sound as the first argument. The sound will be
positioned in front of the listener, while the listener rotates on the the spot to create an
orbiting effect. Terminate the program with Ctrl+C.
*/
#include "../miniaudio.c"
#include <stdio.h>
#include <math.h> /* For sinf() and cosf() */
/* Silence warning about unreachable code for MSVC. */
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4702)
#endif
int main(int argc, char** argv)
{
ma_result result;
ma_engine engine;
ma_sound sound;
float listenerAngle = 0;
if (argc < 2) {
printf("No input file.\n");
return -1;
}
result = ma_engine_init(NULL, &engine);
if (result != MA_SUCCESS) {
printf("Failed to initialize engine.\n");
return -1;
}
result = ma_sound_init_from_file(&engine, argv[1], 0, NULL, NULL, &sound);
if (result != MA_SUCCESS) {
printf("Failed to load sound: %s\n", argv[1]);
ma_engine_uninit(&engine);
return -1;
}
/* This sets the position of the sound. miniaudio follows the same coordinate system as OpenGL, where -Z is forward. */
ma_sound_set_position(&sound, 0, 0, -1);
/*
This sets the position of the listener. The second parameter is the listener index. If you have only a single listener, which is
most likely, just use 0. The position defaults to (0,0,0).
*/
ma_engine_listener_set_position(&engine, 0, 0, 0, 0);
/* Sounds are stopped by default. We'll start it once initial parameters have been setup. */
ma_sound_start(&sound);
/* Rotate the listener on the spot to create an orbiting effect. */
for (;;) {
listenerAngle += 0.01f;
ma_engine_listener_set_direction(&engine, 0, (float)sin(listenerAngle), 0, (float)cos(listenerAngle));
ma_sleep(1);
}
/* Won't actually get here, but do this to tear down. */
ma_sound_uninit(&sound);
ma_engine_uninit(&engine);
return 0;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
+111
View File
@@ -0,0 +1,111 @@
/*
Demonstrates the use of single-threaded mode.
By default, miniaudio runs in multi-threaded mode where audio processing is done on a separate
thread that's managed internally by miniaudio. Sometimes this is undesireable, such as when trying
to get miniaudio working on low-end systems where an extra thread is too costly, or when trying to
get it working on extremely old platforms that don't support threading at all, or simply when you
want to have more control over threading in your application.
To enable single-threaded mode, set the `threadingMode` member of the `ma_device_config` structure
to `MA_THREADING_MODE_SINGLE_THREADED`. To process audio, you need to regularly call
`ma_device_step()`, usually from your main application loop. It is from this function that the
data callback will get fired. You should only call `ma_device_step()` when the device is started,
so typically you would do this between your `ma_device_start()` and `ma_device_stop()` calls.
The `ma_device_step()` function lets you control whether or not it should block while waiting for
audio to be processed. This is controlled via the `blockingMode` parameter. You would typically
use `MA_BLOCKING_MODE_BLOCKING` if you want to relax the CPU. For a game you would probably want to
use `MA_BLOCKING_MODE_NON_BLOCKING`.
You should only ever call `ma_device_step()` in single-threaded mode. In multi-threaded mode (the
default), you should never call this function manually. You can query whether or not the device is
in single-threaded mode via `ma_device_get_threading_mode()`.
When in single-threaded mode, you should ensure that all `ma_device` API calls are made from the same
thread. This is due to some backends that require it, such as WASAPI.
*/
#include "../miniaudio.c"
#include <stdio.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
void main_loop__em(void* pUserData)
{
ma_device* pDevice = (ma_device*)pUserData;
ma_device_step(pDevice, MA_BLOCKING_MODE_NON_BLOCKING);
}
#endif
#define DEVICE_FORMAT ma_format_f32
#define DEVICE_CHANNELS 2
#define DEVICE_SAMPLE_RATE 48000
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_waveform_read_pcm_frames((ma_waveform*)pDevice->pUserData, pOutput, frameCount, NULL);
(void)pInput; /* Unused. */
}
int main(int argc, char** argv)
{
ma_waveform sineWave;
ma_device_config deviceConfig;
ma_device device;
ma_waveform_config sineWaveConfig;
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.threadingMode = MA_THREADING_MODE_SINGLE_THREADED; /* <-- This is what enables single-threaded mode. */
deviceConfig.playback.format = DEVICE_FORMAT;
deviceConfig.playback.channels = DEVICE_CHANNELS;
deviceConfig.sampleRate = DEVICE_SAMPLE_RATE;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = &sineWave;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
printf("Failed to open playback device.\n");
return -4;
}
sineWaveConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.2, 220);
ma_waveform_init(&sineWaveConfig, &sineWave);
if (ma_device_start(&device) != MA_SUCCESS) {
printf("Failed to start playback device.\n");
ma_device_uninit(&device);
return -5;
}
printf("Running in single-threaded mode. Press Ctrl+C to quit.\n");
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop_arg(main_loop__em, &device, 0, 1);
#else
/*
We're putting this in an infinite loop for the sake of this example, but in a real application you
would probably integrate this into your normal application loop.
Using blocking mode makes it so the CPU is relaxed. For a game you would probably want to use
non-blocking mode which you can do with `MA_BLOCKING_MODE_NON_BLOCKING`.
If the device is stopped, `ma_device_step()` will return `MA_DEVICE_NOT_STARTED` which means you
need not explicitly check if the device is started before calling this function.
*/
for (;;) {
ma_result result = ma_device_step(&device, MA_BLOCKING_MODE_BLOCKING);
if (result != MA_SUCCESS) {
break;
}
}
#endif
ma_device_uninit(&device);
ma_waveform_uninit(&sineWave); /* Uninitialize the waveform after the device so we don't pull it from under the device while it's being reference in the data callback. */
(void)argc;
(void)argv;
return 0;
}
+10936
View File
File diff suppressed because it is too large Load Diff
+3757
View File
File diff suppressed because it is too large Load Diff
+729
View File
@@ -0,0 +1,729 @@
/*
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_SDL2, 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.
*/
#ifndef miniaudio_backend_sdl2_c
#define miniaudio_backend_sdl2_c
/* Include miniaudio.h if we're not including this file after the implementation. */
#if !defined(MINIAUDIO_IMPLEMENTATION) && !defined(MA_IMPLEMENTATION)
#include "../../../miniaudio.h"
#endif
#include "miniaudio_sdl2.h"
#include <string.h> /* memset() */
#include <assert.h>
#ifndef MA_SDL2_ASSERT
#define MA_SDL2_ASSERT(cond) assert(cond)
#endif
/* Support SDL on everything. */
#define MA_SUPPORT_SDL2
/*
Only enable SDL if it's hasn't been explicitly disabled (MA_NO_SDL2) or enabled (MA_ENABLE_SDL with
MA_ENABLE_ONLY_SPECIFIC_BACKENDS) and it's supported at compile time (MA_SUPPORT_SDL).
*/
#if defined(MA_SUPPORT_SDL2) && !defined(MA_NO_SDL2) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SDL2))
#define MA_HAS_SDL2
#endif
/* SDL headers are necessary if using compile-time linking. Necessary for Emscripten. */
#if defined(MA_HAS_SDL2)
#if defined(MA_NO_RUNTIME_LINKING) || defined(MA_EMSCRIPTEN)
#ifdef __has_include
#ifdef MA_EMSCRIPTEN
#if !__has_include(<SDL/SDL_audio.h>)
#undef MA_HAS_SDL2
#endif
#else
#if !__has_include(<SDL2/SDL_audio.h>)
#undef MA_HAS_SDL2
#endif
#endif
#endif
#endif
#endif
/* Don't compile in the SDL backend if it's been disabled. */
#if defined(MA_HAS_SDL2)
#define MA_SDL_INIT_AUDIO 0x00000010
#define MA_AUDIO_U8 0x0008
#define MA_AUDIO_S16 0x8010
#define MA_AUDIO_S32 0x8020
#define MA_AUDIO_F32 0x8120
#define MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
#define MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
#define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
#define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE)
/* If we are linking at compile time we'll just #include SDL.h. Otherwise we can just redeclare some stuff to avoid the need for development packages to be installed. */
#ifdef MA_NO_RUNTIME_LINKING
#define SDL_MAIN_HANDLED
#ifdef MA_EMSCRIPTEN
#include <SDL/SDL.h>
#else
#include <SDL2/SDL.h>
#endif
typedef SDL_AudioCallback MA_SDL_AudioCallback;
typedef SDL_AudioSpec MA_SDL_AudioSpec;
typedef SDL_AudioFormat MA_SDL_AudioFormat;
typedef SDL_AudioDeviceID MA_SDL_AudioDeviceID;
#else
typedef void (* MA_SDL_AudioCallback)(void* userdata, ma_uint8* stream, int len);
typedef ma_uint16 MA_SDL_AudioFormat;
typedef ma_uint32 MA_SDL_AudioDeviceID;
typedef struct MA_SDL_AudioSpec
{
int freq;
MA_SDL_AudioFormat format;
ma_uint8 channels;
ma_uint8 silence;
ma_uint16 samples;
ma_uint16 padding;
ma_uint32 size;
MA_SDL_AudioCallback callback;
void* userdata;
} MA_SDL_AudioSpec;
#endif
typedef int (* MA_PFN_SDL_InitSubSystem )(ma_uint32 flags);
typedef void (* MA_PFN_SDL_QuitSubSystem )(ma_uint32 flags);
typedef int (* MA_PFN_SDL_GetNumAudioDevices )(int iscapture);
typedef int (* MA_PFN_SDL_GetDefaultAudioInfo)(char** name, MA_SDL_AudioSpec* spec, int iscapture);
typedef int (* MA_PFN_SDL_GetAudioDeviceSpec )(int index, int iscapture, MA_SDL_AudioSpec* spec);
typedef const char* (* MA_PFN_SDL_GetAudioDeviceName )(int index, int iscapture);
typedef void (* MA_PFN_SDL_CloseAudioDevice )(MA_SDL_AudioDeviceID dev);
typedef MA_SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice )(const char* device, int iscapture, const MA_SDL_AudioSpec* desired, MA_SDL_AudioSpec* obtained, int allowed_changes);
typedef void (* MA_PFN_SDL_PauseAudioDevice )(MA_SDL_AudioDeviceID dev, int pause_on);
typedef struct
{
ma_handle hSDL; /* A handle to the SDL2 shared object. We dynamically load function pointers at runtime so we can avoid linking. */
MA_PFN_SDL_InitSubSystem SDL_InitSubSystem;
MA_PFN_SDL_QuitSubSystem SDL_QuitSubSystem;
MA_PFN_SDL_GetNumAudioDevices SDL_GetNumAudioDevices;
MA_PFN_SDL_GetDefaultAudioInfo SDL_GetDefaultAudioInfo;
MA_PFN_SDL_GetAudioDeviceSpec SDL_GetAudioDeviceSpec;
MA_PFN_SDL_GetAudioDeviceName SDL_GetAudioDeviceName;
MA_PFN_SDL_CloseAudioDevice SDL_CloseAudioDevice;
MA_PFN_SDL_OpenAudioDevice SDL_OpenAudioDevice;
MA_PFN_SDL_PauseAudioDevice SDL_PauseAudioDevice;
} ma_context_state_sdl2;
typedef struct
{
ma_device_state_async async;
struct
{
int deviceID;
} capture;
struct
{
int deviceID;
} playback;
} ma_device_state_sdl2;
MA_SDL_AudioFormat ma_format_to_sdl2(ma_format format)
{
switch (format)
{
case ma_format_unknown: return 0;
case ma_format_u8: return MA_AUDIO_U8;
case ma_format_s16: return MA_AUDIO_S16;
case ma_format_s24: return MA_AUDIO_S32; /* Closest match. */
case ma_format_s32: return MA_AUDIO_S32;
case ma_format_f32: return MA_AUDIO_F32;
default: return 0;
}
}
ma_format ma_format_from_sdl2(MA_SDL_AudioFormat format)
{
switch (format)
{
case MA_AUDIO_U8: return ma_format_u8;
case MA_AUDIO_S16: return ma_format_s16;
case MA_AUDIO_S32: return ma_format_s32;
case MA_AUDIO_F32: return ma_format_f32;
default: return ma_format_unknown;
}
}
static ma_context_state_sdl2* ma_context_get_backend_state__sdl2(ma_context* pContext)
{
return (ma_context_state_sdl2*)ma_context_get_backend_state(pContext);
}
static ma_device_state_sdl2* ma_device_get_backend_state__sdl2(ma_device* pDevice)
{
return (ma_device_state_sdl2*)ma_device_get_backend_state(pDevice);
}
static ma_result ma_device_step__sdl2(ma_device* pDevice, ma_blocking_mode blockingMode);
static void ma_backend_info__sdl2(ma_device_backend_info* pBackendInfo)
{
MA_SDL2_ASSERT(pBackendInfo != NULL);
pBackendInfo->pName = "SDL2";
}
static ma_result ma_context_init__sdl2(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState)
{
ma_context_state_sdl2* pContextStateSDL;
const ma_context_config_sdl2* pContextConfigSDL = (ma_context_config_sdl2*)pContextBackendConfig;
ma_log* pLog = ma_context_get_log(pContext);
int resultSDL;
/* The context config is not currently being used for this backend. */
(void)pContextConfigSDL;
/* Allocate our SDL-specific context data. */
pContextStateSDL = (ma_context_state_sdl2*)ma_calloc(sizeof(*pContextStateSDL), ma_context_get_allocation_callbacks(pContext));
if (pContextStateSDL == NULL) {
return MA_OUT_OF_MEMORY;
}
#ifndef MA_NO_RUNTIME_LINKING
{
/* We'll use a list of possible shared object names for easier extensibility. */
size_t iName;
const char* pSDLNames[] = {
#if defined(_WIN32)
"SDL2.dll"
#elif defined(__APPLE__)
"SDL2.framework/SDL2"
#else
"libSDL2-2.0.so.0"
#endif
};
/* Check if we have SDL2 installed somewhere. If not it's not usable and we need to abort. */
for (iName = 0; iName < ma_countof(pSDLNames); iName += 1) {
pContextStateSDL->hSDL = ma_dlopen(pLog, pSDLNames[iName]);
if (pContextStateSDL->hSDL != NULL) {
break;
}
}
if (pContextStateSDL->hSDL == NULL) {
ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext));
return MA_NO_BACKEND; /* SDL2 could not be loaded. */
}
/* Now that we have the handle to the shared object we can go ahead and load some function pointers. */
pContextStateSDL->SDL_InitSubSystem = (MA_PFN_SDL_InitSubSystem )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_InitSubSystem");
pContextStateSDL->SDL_QuitSubSystem = (MA_PFN_SDL_QuitSubSystem )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_QuitSubSystem");
pContextStateSDL->SDL_GetNumAudioDevices = (MA_PFN_SDL_GetNumAudioDevices )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetNumAudioDevices");
pContextStateSDL->SDL_GetDefaultAudioInfo = (MA_PFN_SDL_GetDefaultAudioInfo)ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetDefaultAudioInfo");
pContextStateSDL->SDL_GetAudioDeviceSpec = (MA_PFN_SDL_GetAudioDeviceSpec )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetAudioDeviceSpec");
pContextStateSDL->SDL_GetAudioDeviceName = (MA_PFN_SDL_GetAudioDeviceName )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetAudioDeviceName");
pContextStateSDL->SDL_CloseAudioDevice = (MA_PFN_SDL_CloseAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_CloseAudioDevice");
pContextStateSDL->SDL_OpenAudioDevice = (MA_PFN_SDL_OpenAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_OpenAudioDevice");
pContextStateSDL->SDL_PauseAudioDevice = (MA_PFN_SDL_PauseAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_PauseAudioDevice");
}
#else
{
pContextStateSDL->SDL_InitSubSystem = SDL_InitSubSystem;
pContextStateSDL->SDL_QuitSubSystem = SDL_QuitSubSystem;
pContextStateSDL->SDL_GetNumAudioDevices = SDL_GetNumAudioDevices;
#ifndef __EMSCRIPTEN__
pContextStateSDL->SDL_GetDefaultAudioInfo = SDL_GetDefaultAudioInfo;
pContextStateSDL->SDL_GetAudioDeviceSpec = SDL_GetAudioDeviceSpec;
#else
pContextStateSDL->SDL_GetDefaultAudioInfo = NULL;
pContextStateSDL->SDL_GetAudioDeviceSpec = NULL;
#endif
pContextStateSDL->SDL_GetAudioDeviceName = SDL_GetAudioDeviceName;
pContextStateSDL->SDL_CloseAudioDevice = SDL_CloseAudioDevice;
pContextStateSDL->SDL_OpenAudioDevice = SDL_OpenAudioDevice;
pContextStateSDL->SDL_PauseAudioDevice = SDL_PauseAudioDevice;
}
#endif /* MA_NO_RUNTIME_LINKING */
resultSDL = pContextStateSDL->SDL_InitSubSystem(MA_SDL_INIT_AUDIO);
if (resultSDL != 0) {
ma_dlclose(pLog, pContextStateSDL->hSDL);
ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext));
return MA_ERROR;
}
*ppContextState = pContextStateSDL;
return MA_SUCCESS;
}
static void ma_context_uninit__sdl2(ma_context* pContext)
{
ma_context_state_sdl2* pContextStateSDL = ma_context_get_backend_state__sdl2(pContext);
MA_SDL2_ASSERT(pContextStateSDL != NULL);
pContextStateSDL->SDL_QuitSubSystem(MA_SDL_INIT_AUDIO);
/* Close the handle to the SDL shared object last. */
ma_dlclose(ma_context_get_log(pContext), pContextStateSDL->hSDL);
pContextStateSDL->hSDL = NULL;
ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext));
}
static void ma_add_native_format_from_AudioSpec__sdl2(ma_device_info* pDeviceInfo, const MA_SDL_AudioSpec* pAudioSpec)
{
ma_format format = ma_format_from_sdl2(pAudioSpec->format);
if (format == ma_format_unknown) {
format = ma_format_f32;
}
ma_device_info_add_native_data_format(pDeviceInfo, format, pAudioSpec->channels, pAudioSpec->channels, pAudioSpec->freq, pAudioSpec->freq);
}
static ma_result ma_context_enumerate_devices__sdl2(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData)
{
ma_context_state_sdl2* pContextStateSDL = ma_context_get_backend_state__sdl2(pContext);
ma_device_enumeration_result cbResult = MA_DEVICE_ENUMERATION_CONTINUE;
MA_SDL_AudioSpec defaultAudioSpec;
ma_device_info deviceInfo;
int deviceCount;
int iDevice;
MA_SDL2_ASSERT(pContextStateSDL != NULL);
/* Playback */
if (cbResult == MA_DEVICE_ENUMERATION_CONTINUE) {
ma_bool32 hasDefaultPlaybackDeviceBeenEnumerated = MA_FALSE;
ma_bool32 hasDefaultPlaybackDevice;
char* pDefaultPlaybackDeviceName = NULL;
if (pContextStateSDL->SDL_GetDefaultAudioInfo) {
hasDefaultPlaybackDevice = pContextStateSDL->SDL_GetDefaultAudioInfo(&pDefaultPlaybackDeviceName, &defaultAudioSpec, 0) == 0;
} else {
hasDefaultPlaybackDevice = MA_FALSE;
}
deviceCount = pContextStateSDL->SDL_GetNumAudioDevices(0);
for (iDevice = 0; iDevice < deviceCount; iDevice += 1) {
MA_SDL_AudioSpec audioSpec;
memset(&deviceInfo, 0, sizeof(deviceInfo));
/* Default. For SDL2 we'll just use the first device that matches the default device name. */
if (!hasDefaultPlaybackDeviceBeenEnumerated && hasDefaultPlaybackDevice) {
const char* pDeviceName = pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 0);
if (strcmp(pDeviceName, pDefaultPlaybackDeviceName) == 0) {
deviceInfo.isDefault = MA_TRUE;
hasDefaultPlaybackDeviceBeenEnumerated = MA_TRUE;
}
} else {
deviceInfo.isDefault = MA_FALSE;
}
/* ID. */
deviceInfo.id.custom.i = iDevice;
/* Name. */
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 0), (size_t)-1);
/* Data Format. */
if (pContextStateSDL->SDL_GetAudioDeviceSpec != NULL) {
if (pContextStateSDL->SDL_GetAudioDeviceSpec(iDevice, 0, &audioSpec) == 0) {
ma_add_native_format_from_AudioSpec__sdl2(&deviceInfo, &audioSpec);
}
} else {
/* No way to retrieve the data format. Just report support for everything. */
deviceInfo.nativeDataFormatCount = 1;
}
cbResult = callback(ma_device_type_playback, &deviceInfo, pCallbackUserData);
if (cbResult == MA_DEVICE_ENUMERATION_ABORT) {
break;
}
}
/* SDL2 does not flag the default playback device so we'll enumerate an explicit default device here. */
if (cbResult == MA_DEVICE_ENUMERATION_CONTINUE && !hasDefaultPlaybackDeviceBeenEnumerated) {
memset(&deviceInfo, 0, sizeof(deviceInfo));
deviceInfo.isDefault = MA_TRUE;
deviceInfo.id.custom.i = -1; /* Special ID for the default device. */
if (hasDefaultPlaybackDevice) {
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pDefaultPlaybackDeviceName, (size_t)-1);
ma_add_native_format_from_AudioSpec__sdl2(&deviceInfo, &defaultAudioSpec);
} else {
/* No way to retrieve the data format. Just report support for everything. */
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "Default Playback Device", (size_t)-1);
deviceInfo.nativeDataFormatCount = 1;
}
cbResult = callback(ma_device_type_playback, &deviceInfo, pCallbackUserData);
}
}
/* Capture */
if (cbResult == MA_DEVICE_ENUMERATION_CONTINUE) {
ma_bool32 hasDefaultCaptureDeviceBeenEnumerated = MA_FALSE;
ma_bool32 hasDefaultCaptureDevice;
char* pDefaultCaptureDeviceName = NULL;
if (pContextStateSDL->SDL_GetDefaultAudioInfo) {
hasDefaultCaptureDevice = pContextStateSDL->SDL_GetDefaultAudioInfo(&pDefaultCaptureDeviceName, &defaultAudioSpec, 1) == 0;
} else {
hasDefaultCaptureDevice = MA_FALSE;
}
deviceCount = pContextStateSDL->SDL_GetNumAudioDevices(1);
for (iDevice = 0; iDevice < deviceCount; iDevice += 1) {
MA_SDL_AudioSpec audioSpec;
memset(&deviceInfo, 0, sizeof(deviceInfo));
/* Default. For SDL2 we'll just use the first device that matches the default device name. */
if (!hasDefaultCaptureDeviceBeenEnumerated && hasDefaultCaptureDevice) {
const char* pDeviceName = pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 1);
if (strcmp(pDeviceName, pDefaultCaptureDeviceName) == 0) {
deviceInfo.isDefault = MA_TRUE;
hasDefaultCaptureDeviceBeenEnumerated = MA_TRUE;
}
} else {
deviceInfo.isDefault = MA_FALSE;
}
/* ID. */
deviceInfo.id.custom.i = iDevice;
/* Name. */
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 1), (size_t)-1);
/* Data Format. */
if (pContextStateSDL->SDL_GetAudioDeviceSpec != NULL) {
if (pContextStateSDL->SDL_GetAudioDeviceSpec(iDevice, 1, &audioSpec) == 0) {
ma_add_native_format_from_AudioSpec__sdl2(&deviceInfo, &audioSpec);
}
} else {
/* No way to retrieve the data format. Just report support for everything. */
deviceInfo.nativeDataFormatCount = 1;
}
cbResult = callback(ma_device_type_capture, &deviceInfo, pCallbackUserData);
if (cbResult == MA_DEVICE_ENUMERATION_ABORT) {
break;
}
}
/* SDL2 does not flag the default playback device so we'll enumerate an explicit default device here. */
if (cbResult == MA_DEVICE_ENUMERATION_CONTINUE && !hasDefaultCaptureDeviceBeenEnumerated) {
memset(&deviceInfo, 0, sizeof(deviceInfo));
deviceInfo.isDefault = MA_TRUE;
deviceInfo.id.custom.i = -1; /* Special ID for the default device. */
if (hasDefaultCaptureDevice) {
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pDefaultCaptureDeviceName, (size_t)-1);
ma_add_native_format_from_AudioSpec__sdl2(&deviceInfo, &defaultAudioSpec);
} else {
/* No way to retrieve the data format. Just report support for everything. */
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "Default Capture Device", (size_t)-1);
deviceInfo.nativeDataFormatCount = 1;
}
cbResult = callback(ma_device_type_capture, &deviceInfo, pCallbackUserData);
(void)cbResult; /* Silence a static analysis warning. Want to keep this assignment in case we extend this logic later. */
}
}
return MA_SUCCESS;
}
void ma_audio_callback_capture__sdl2(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
ma_device* pDevice = (ma_device*)pUserData;
ma_device_state_sdl2* pDeviceStateSDL = ma_device_get_backend_state__sdl2(pDevice);
ma_device_state_async_process(&pDeviceStateSDL->async, pDevice, NULL, pBuffer, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceStateSDL->async.capture.format, pDeviceStateSDL->async.capture.channels));
}
void ma_audio_callback_playback__sdl2(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
ma_device* pDevice = (ma_device*)pUserData;
ma_device_state_sdl2* pDeviceStateSDL = ma_device_get_backend_state__sdl2(pDevice);
ma_device_state_async_process(&pDeviceStateSDL->async, pDevice, pBuffer, NULL, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceStateSDL->async.playback.format, pDeviceStateSDL->async.playback.channels));
}
static ma_result ma_device_init_internal__sdl2(ma_device* pDevice, ma_context_state_sdl2* pContextStateSDL, ma_device_state_sdl2* pDeviceStateSDL, const ma_device_config_sdl2* pDeviceConfigSDL, ma_device_type deviceType, ma_device_descriptor* pDescriptor)
{
MA_SDL_AudioSpec desiredSpec;
MA_SDL_AudioSpec obtainedSpec;
const char* pDeviceName;
int deviceID;
(void)pDeviceConfigSDL;
/*
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
the sample rate just set to 0, which indicates that the native sample rate should be used. There's no practical way to calculate this
that I can think of right now so I'm just using MA_DEFAULT_SAMPLE_RATE.
*/
if (pDescriptor->sampleRate == 0) {
pDescriptor->sampleRate = MA_DEFAULT_SAMPLE_RATE;
}
/*
When determining the period size, you need to take defaults into account. This is how the size of the period should be determined.
1) If periodSizeInFrames is not 0, use periodSizeInFrames; else
2) If periodSizeInMilliseconds is not 0, use periodSizeInMilliseconds; else
3) If both periodSizeInFrames and periodSizeInMilliseconds is 0, use the backend's default. If the backend does not allow a default
buffer size, use a default value of MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS.
Note that options 2 and 3 require knowledge of the sample rate in order to convert it to a frame count. You should try to keep the
calculation of the period size as accurate as possible, but sometimes it's just not practical so just use whatever you can.
A helper function called ma_calculate_buffer_size_in_frames_from_descriptor() is available to do all of this for you which is what
we'll be using here.
*/
pDescriptor->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate);
/* SDL wants the buffer size to be a power of 2 for some reason. */
if (pDescriptor->periodSizeInFrames > 32768) {
pDescriptor->periodSizeInFrames = 32768;
} else {
pDescriptor->periodSizeInFrames = ma_next_power_of_2(pDescriptor->periodSizeInFrames);
}
/*
In my experience there is glitching with a period size of anything <= 512. To make this "Just Work" on
the Emscripten build we'll set this to 1024.
*/
#if defined(__EMSCRIPTEN__)
{
if (pDescriptor->periodSizeInFrames < 1024) {
pDescriptor->periodSizeInFrames = 1024;
}
}
#endif
/* We now have enough information to set up the device. */
memset(&desiredSpec, 0, sizeof(desiredSpec));
desiredSpec.freq = (int)pDescriptor->sampleRate;
desiredSpec.format = ma_format_to_sdl2(pDescriptor->format);
desiredSpec.channels = (ma_uint8)pDescriptor->channels;
desiredSpec.samples = (ma_uint16)pDescriptor->periodSizeInFrames;
desiredSpec.callback = (deviceType == ma_device_type_capture) ? ma_audio_callback_capture__sdl2 : ma_audio_callback_playback__sdl2;
desiredSpec.userdata = pDevice;
/* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */
if (desiredSpec.format == 0) {
desiredSpec.format = MA_AUDIO_F32;
}
/* We explicitly want the device name to be NULL for the default device. Otherwise we'll grab the name from the device index. */
pDeviceName = NULL;
if (pDescriptor->pDeviceID != NULL && pDescriptor->pDeviceID->custom.i != -1) {
pDeviceName = pContextStateSDL->SDL_GetAudioDeviceName(pDescriptor->pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1);
}
deviceID = pContextStateSDL->SDL_OpenAudioDevice(pDeviceName, (deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (deviceID == 0) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to open SDL2 device.");
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
}
/* The descriptor needs to be updated with our actual settings. */
pDescriptor->format = ma_format_from_sdl2(obtainedSpec.format);
pDescriptor->channels = obtainedSpec.channels;
pDescriptor->sampleRate = (ma_uint32)obtainedSpec.freq;
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
pDescriptor->periodSizeInFrames = obtainedSpec.samples;
pDescriptor->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */
if (deviceType == ma_device_type_playback) {
pDeviceStateSDL->playback.deviceID = deviceID;
} else {
pDeviceStateSDL->capture.deviceID = deviceID;
}
return MA_SUCCESS;
}
static ma_result ma_device_init__sdl2(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState)
{
ma_device_state_sdl2* pDeviceStateSDL;
ma_device_config_sdl2* pDeviceConfigSDL = (ma_device_config_sdl2*)pDeviceBackendConfig;
ma_context_state_sdl2* pContextStateSDL = ma_context_get_backend_state__sdl2(ma_device_get_context(pDevice));
ma_device_type deviceType = ma_device_get_type(pDevice);
ma_result result;
/* SDL does not support loopback mode, so must return MA_DEVICE_TYPE_NOT_SUPPORTED if it's requested. */
if (deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
/* We need to allocate our backend-specific data. */
pDeviceStateSDL = (ma_device_state_sdl2*)ma_calloc(sizeof(*pDeviceStateSDL), ma_device_get_allocation_callbacks(pDevice));
if (pDeviceStateSDL == NULL) {
return MA_OUT_OF_MEMORY;
}
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
result = ma_device_init_internal__sdl2(pDevice, pContextStateSDL, pDeviceStateSDL, pDeviceConfigSDL, ma_device_type_capture, pDescriptorCapture);
if (result != MA_SUCCESS) {
ma_free(pDeviceStateSDL, ma_device_get_allocation_callbacks(pDevice));
return result;
}
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
result = ma_device_init_internal__sdl2(pDevice, pContextStateSDL, pDeviceStateSDL, pDeviceConfigSDL, ma_device_type_playback, pDescriptorPlayback);
if (result != MA_SUCCESS) {
if (deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->capture.deviceID);
}
ma_free(pDeviceStateSDL, ma_device_get_allocation_callbacks(pDevice));
return result;
}
}
result = ma_device_state_async_init(deviceType, pDescriptorPlayback, pDescriptorCapture, ma_device_get_allocation_callbacks(pDevice), &pDeviceStateSDL->async);
if (result != MA_SUCCESS) {
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->capture.deviceID);
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->playback.deviceID);
}
ma_free(pDeviceStateSDL, ma_device_get_allocation_callbacks(pDevice));
return result;
}
*ppDeviceState = pDeviceStateSDL;
return MA_SUCCESS;
}
static void ma_device_uninit__sdl2(ma_device* pDevice)
{
ma_device_state_sdl2* pDeviceStateSDL = ma_device_get_backend_state__sdl2(pDevice);
ma_context_state_sdl2* pContextStateSDL = ma_context_get_backend_state__sdl2(ma_device_get_context(pDevice));
ma_device_type deviceType = ma_device_get_type(pDevice);
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->capture.deviceID);
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->playback.deviceID);
}
ma_device_state_async_uninit(&pDeviceStateSDL->async, ma_device_get_allocation_callbacks(pDevice));
ma_free(pDeviceStateSDL, ma_device_get_allocation_callbacks(pDevice));
}
static ma_result ma_device_start__sdl2(ma_device* pDevice)
{
ma_device_state_sdl2* pDeviceStateSDL = ma_device_get_backend_state__sdl2(pDevice);
ma_context_state_sdl2* pContextStateSDL = ma_context_get_backend_state__sdl2(ma_device_get_context(pDevice));
ma_device_type deviceType = ma_device_get_type(pDevice);
/* Step the device once to ensure buffers are pre-filled before starting. */
ma_device_step__sdl2(pDevice, MA_BLOCKING_MODE_NON_BLOCKING);
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->capture.deviceID, 0);
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->playback.deviceID, 0);
}
return MA_SUCCESS;
}
static ma_result ma_device_stop__sdl2(ma_device* pDevice)
{
ma_device_state_sdl2* pDeviceStateSDL = ma_device_get_backend_state__sdl2(pDevice);
ma_context_state_sdl2* pContextStateSDL = ma_context_get_backend_state__sdl2(ma_device_get_context(pDevice));
ma_device_type deviceType = ma_device_get_type(pDevice);
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->capture.deviceID, 1);
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->playback.deviceID, 1);
}
return MA_SUCCESS;
}
static ma_result ma_device_step__sdl2(ma_device* pDevice, ma_blocking_mode blockingMode)
{
ma_device_state_sdl2* pDeviceStateSDL = ma_device_get_backend_state__sdl2(pDevice);
return ma_device_state_async_step(&pDeviceStateSDL->async, pDevice, blockingMode, NULL);
}
static void ma_device_wakeup__sdl2(ma_device* pDevice)
{
ma_device_state_sdl2* pDeviceStateSDL = ma_device_get_backend_state__sdl2(pDevice);
ma_device_state_async_release(&pDeviceStateSDL->async);
}
static ma_device_backend_vtable ma_gDeviceBackendVTable_SDL2 =
{
ma_backend_info__sdl2,
ma_context_init__sdl2,
ma_context_uninit__sdl2,
ma_context_enumerate_devices__sdl2,
ma_device_init__sdl2,
ma_device_uninit__sdl2,
ma_device_start__sdl2,
ma_device_stop__sdl2,
ma_device_step__sdl2,
ma_device_wakeup__sdl2
};
ma_device_backend_vtable* ma_device_backend_sdl2 = &ma_gDeviceBackendVTable_SDL2;
#else
ma_device_backend_vtable* ma_device_backend_sdl2 = NULL;
#endif /* MA_HAS_SDL2 */
MA_API ma_device_backend_vtable* ma_sdl2_get_vtable(void)
{
return ma_device_backend_sdl2;
}
MA_API ma_context_config_sdl2 ma_context_config_sdl2_init(void)
{
ma_context_config_sdl2 config;
memset(&config, 0, sizeof(config));
return config;
}
MA_API ma_device_config_sdl2 ma_device_config_sdl2_init(void)
{
ma_device_config_sdl2 config;
memset(&config, 0, sizeof(config));
return config;
}
#endif /* miniaudio_backend_sdl2_c */
+37
View File
@@ -0,0 +1,37 @@
/*
The SDL2 backend does not require any user data, nor configs. Configs are provided here in case
they are needed in the future, however you can safely pass in NULL when setting up your context
and device configs.
*/
#ifndef miniaudio_backend_sdl2_h
#define miniaudio_backend_sdl2_h
#include "../../../miniaudio.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int _unused;
} ma_context_config_sdl2;
MA_API ma_context_config_sdl2 ma_context_config_sdl2_init(void);
typedef struct
{
int _unused;
} ma_device_config_sdl2;
MA_API ma_device_config_sdl2 ma_device_config_sdl2_init(void);
extern ma_device_backend_vtable* ma_device_backend_sdl2;
MA_API ma_device_backend_vtable* ma_sdl2_get_vtable(void);
#ifdef __cplusplus
}
#endif
#endif /* miniaudio_backend_sdl_h */
@@ -0,0 +1,362 @@
#ifndef miniaudio_backend_template_c
#define miniaudio_backend_template_c
/* Do not include this in your backend. It's only used to validate the template build. Needed for MA_ZERO_OBJECT(). */
#include "../../../miniaudio.c"
#include "miniaudio_backend_template.h"
/* Wrap this in a #ifdef/#endif depending on the build environment. */
#define MA_SUPPORT_TEMPLATE
#if defined(MA_SUPPORT_TEMPLATE) && !defined(MA_NO_TEMPLATE) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_TEMPLATE))
#define MA_HAS_TEMPLATE
#endif
#if defined(MA_HAS_TEMPLATE)
typedef struct ma_context_state_template
{
int _unused;
} ma_context_state_template;
typedef struct ma_device_state_template
{
int _unused;
} ma_device_state_template;
static ma_context_state_template* ma_context_get_backend_state__template(ma_context* pContext)
{
return (ma_context_state_template*)ma_context_get_backend_state(pContext);
}
static ma_device_state_template* ma_device_get_backend_state__template(ma_device* pDevice)
{
return (ma_device_state_template*)ma_device_get_backend_state(pDevice);
}
static void ma_backend_info__template(ma_device_backend_info* pBackendInfo)
{
pBackendInfo->pName = "Template";
}
static ma_result ma_context_init__template(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState)
{
ma_context_state_template* pContextStateTemplate;
const ma_context_config_template* pContextConfigTemplate = (ma_context_config_template*)pContextBackendConfig;
pContextStateTemplate = (ma_context_state_template*)ma_calloc(sizeof(*pContextStateTemplate), ma_context_get_allocation_callbacks(pContext));
if (pContextStateTemplate == NULL) {
return MA_OUT_OF_MEMORY;
}
/* Do any context-level initialization here. */
(void)pContextConfigTemplate;
(void)pContext;
*ppContextState = pContextStateTemplate;
return MA_SUCCESS;
}
static void ma_context_uninit__template(ma_context* pContext)
{
ma_context_state_template* pContextStateTemplate = ma_context_get_backend_state__template(pContext);
/* Do any context-level uninitialization here. */
ma_free(pContextStateTemplate, ma_context_get_allocation_callbacks(pContext));
}
static ma_result ma_context_enumerate_devices__template(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData)
{
ma_context_state_template* pContextStateTemplate = ma_context_get_backend_state__template(pContext);
ma_device_info deviceInfo;
ma_device_enumeration_result enumerationResult;
/* This example is only outputting a default playback and capture device. Modify this as required. */
(void)pContextStateTemplate;
/* Playback. */
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.isDefault = MA_TRUE;
deviceInfo.id.custom.i = 0;
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "Default Playback Device", (size_t)-1);
/* Add a native format for each natively supported sample format. The channel count and sample rates are ranges. The format is the key. */
ma_device_info_add_native_data_format(&deviceInfo, ma_format_s16, 1, 2, 44100, 48000);
ma_device_info_add_native_data_format(&deviceInfo, ma_format_f32, 1, 2, 44100, 48000);
/* If the callback has requested that we abort enumeration we need to respect it. */
enumerationResult = callback(ma_device_type_playback, &deviceInfo, pCallbackUserData);
if (enumerationResult == MA_DEVICE_ENUMERATION_ABORT) {
return MA_SUCCESS;
}
/* Capture. */
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.isDefault = MA_TRUE;
deviceInfo.id.custom.i = 0;
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "Default Capture Device", (size_t)-1);
/* Add a native format for each natively supported sample format. The channel count and sample rates are ranges. The format is the key. */
ma_device_info_add_native_data_format(&deviceInfo, ma_format_s16, 1, 2, 44100, 48000);
ma_device_info_add_native_data_format(&deviceInfo, ma_format_f32, 1, 2, 44100, 48000);
/* If the callback has requested that we abort enumeration we need to respect it. */
enumerationResult = callback(ma_device_type_capture, &deviceInfo, pCallbackUserData);
if (enumerationResult == MA_DEVICE_ENUMERATION_ABORT) {
return MA_SUCCESS;
}
/* Enumeration done. */
return MA_SUCCESS;
}
static ma_result ma_device_init__template(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState)
{
ma_device_state_template* pDeviceStateTemplate;
ma_device_config_template* pDeviceConfigTemplate = (ma_device_config_template*)pDeviceBackendConfig;
ma_context_state_template* pContextStateTemplate = ma_context_get_backend_state__template(ma_device_get_context(pDevice));
ma_device_config_template defaultConfig;
ma_device_type deviceType = ma_device_get_type(pDevice);
/* Use a default config if one was not provided. This is not mandated by miniaudio, but it's good practice. */
if (pDeviceConfigTemplate == NULL) {
defaultConfig = ma_device_config_template_init();
pDeviceConfigTemplate = &defaultConfig;
}
/* Return an error for any unsupported device types. */
if (deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
pDeviceStateTemplate = (ma_device_state_template*)ma_calloc(sizeof(*pDeviceStateTemplate), ma_device_get_allocation_callbacks(pDevice));
if (pDeviceStateTemplate == NULL) {
return MA_OUT_OF_MEMORY;
}
/* Not using the context state for this example. */
(void)pContextStateTemplate;
/* No backend-specific config for this example. */
(void)pDeviceConfigTemplate;
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
/* Do any backend initialization for the capture side here. Update pDescriptorCapture with the actual internal format, channels, rate and period size. */
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
ma_uint32 periodSizeInFrames;
/*
On input pDescriptorCapture contains what miniaudio would like, if possible. On output, you set
it to what the backend actually supports. You would typically try to make it as close as possible,
but it's ultimately up to the backend to choose what it gives you.
*/
/* Negotiate the format. */
format = pDescriptorCapture->format;
if (format != ma_format_f32) {
format = ma_format_f32;
}
/* Negotiate the channel count. */
channels = pDescriptorCapture->channels;
if (channels != 1 && channels != 2) {
channels = 2;
}
/* Negotiate the sample rate. */
sampleRate = pDescriptorCapture->sampleRate;
if (sampleRate != 44100 && sampleRate != 48000) {
sampleRate = 48000;
}
/*
Negotiate the period size. You will want to use ma_calculate_buffer_size_in_frames_from_descriptor() to
calculate an appropriate period size based on standardized miniaudio conventions as a base point, and then
massage it based on the requirements of the backend. This example is just clamping it between 1024 and
16384 just for demonstration purposes.
*/
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, sampleRate);
if (periodSizeInFrames < 1024) {
periodSizeInFrames = 1024;
}
/* Initialize any backend-specific stuff here. */
/* Update the descriptor with the actual internal settings. */
pDescriptorCapture->format = format;
pDescriptorCapture->channels = channels;
pDescriptorCapture->sampleRate = sampleRate;
pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
pDescriptorCapture->periodCount = 1; /* Just set this 1 if you're unsure. Set it to 2 if you are using a double-buffering technique. */
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
/* This works exactly the same as capture, but for playback. */
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
ma_uint32 periodSizeInFrames;
format = pDescriptorPlayback->format;
if (format != ma_format_f32) {
format = ma_format_f32;
}
channels = pDescriptorPlayback->channels;
if (channels != 1 && channels != 2) {
channels = 2;
}
sampleRate = pDescriptorPlayback->sampleRate;
if (sampleRate != 44100 && sampleRate != 48000) {
sampleRate = 48000;
}
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, sampleRate);
if (periodSizeInFrames < 1024) {
periodSizeInFrames = 1024;
}
/* Initialize any backend-specific stuff here. */
/* Update the descriptor with the actual internal settings. */
pDescriptorPlayback->format = format;
pDescriptorPlayback->channels = channels;
pDescriptorPlayback->sampleRate = sampleRate;
pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
pDescriptorPlayback->periodCount = 1;
}
*ppDeviceState = pDeviceStateTemplate;
return MA_SUCCESS;
}
static void ma_device_uninit__template(ma_device* pDevice)
{
ma_device_state_template* pDeviceStateTemplate = ma_device_get_backend_state__template(pDevice);
/* Do any device-level uninitialization here. */
ma_free(pDeviceStateTemplate, ma_device_get_allocation_callbacks(pDevice));
}
static ma_result ma_device_start__template(ma_device* pDevice)
{
ma_device_state_template* pDeviceStateTemplate = ma_device_get_backend_state__template(pDevice);
ma_device_type deviceType = ma_device_get_type(pDevice);
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
/* Start the capture side. */
(void)pDeviceStateTemplate;
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
/* Start the playback side. */
(void)pDeviceStateTemplate;
}
return MA_SUCCESS;
}
static ma_result ma_device_stop__template(ma_device* pDevice)
{
ma_device_state_template* pDeviceStateTemplate = ma_device_get_backend_state__template(pDevice);
ma_device_type deviceType = ma_device_get_type(pDevice);
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
/* Stop the capture side. */
(void)pDeviceStateTemplate;
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
/* Stop the playback side. */
(void)pDeviceStateTemplate;
}
return MA_NOT_IMPLEMENTED;
}
static ma_result ma_device_step__template(ma_device* pDevice, ma_blocking_mode blockingMode)
{
ma_device_state_template* pDeviceStateTemplate = ma_device_get_backend_state__template(pDevice);
/*
When blockingMode is MA_BLOCKING_MODE_BLOCKING, this function should block until data has been processed. Otherwise
if it's set to MA_BLOCKING_MODE_NON_BLOCKING it should try processing any data if ready and then immediately return,
regardless of whether or not any data was processed. Use ma_device_handle_backend_data_callback() to process data.
The code below is an example of the general idea. You don't have to follow this exact design.
*/
if (blockingMode == MA_BLOCKING_MODE_BLOCKING) {
/* Wait here. */
(void)pDeviceStateTemplate;
}
/* If after waiting the device has been stopped don't process any audio data. */
if (!ma_device_is_started(pDevice)) {
return MA_DEVICE_NOT_STARTED;
}
/* Call ma_device_handle_backend_data_callback() at some point. */
return MA_SUCCESS;
}
static void ma_device_wakeup__template(ma_device* pDevice)
{
ma_device_state_template* pDeviceStateTemplate = ma_device_get_backend_state__template(pDevice);
/* Do whatever needs to be done to wakeup the step function. It's OK to just do nothing here so long as your backend will return from a blocking step in a reasonable amount of time. */
(void)pDeviceStateTemplate;
}
static ma_device_backend_vtable ma_gDeviceBackendVTable_Template =
{
ma_backend_info__template,
ma_context_init__template,
ma_context_uninit__template,
ma_context_enumerate_devices__template,
ma_device_init__template,
ma_device_uninit__template,
ma_device_start__template,
ma_device_stop__template,
ma_device_step__template,
ma_device_wakeup__template
};
ma_device_backend_vtable* ma_device_backend_template = &ma_gDeviceBackendVTable_Template;
#else
ma_device_backend_vtable* ma_device_backend_template = NULL;
#endif /* MA_HAS_TEMPLATE */
MA_API ma_device_backend_vtable* ma_template_get_vtable(void)
{
return ma_device_backend_template;
}
MA_API ma_context_config_template ma_context_config_template_init(void)
{
ma_context_config_template config;
MA_ZERO_OBJECT(&config);
return config;
}
MA_API ma_device_config_template ma_device_config_template_init(void)
{
ma_device_config_template config;
MA_ZERO_OBJECT(&config);
return config;
}
#endif /* miniaudio_backend_template_c */
@@ -0,0 +1,33 @@
#ifndef miniaudio_backend_template_h
#define miniaudio_backend_template_h
#include "../../../miniaudio.h"
#ifdef __cplusplus
extern "C" {
#endif
extern ma_device_backend_vtable* ma_device_backend_template;
MA_API ma_device_backend_vtable* ma_template_get_vtable(void);
typedef struct
{
int _unused;
} ma_context_config_template;
MA_API ma_context_config_template ma_context_config_template_init(void);
typedef struct
{
int _unused;
} ma_device_config_template;
MA_API ma_device_config_template ma_device_config_template_init(void);
#ifdef __cplusplus
}
#endif
#endif /* miniaudio_backend_template_h */
-104
View File
@@ -1,104 +0,0 @@
/*
IMPORTANT NOTE: Cosmopolitan is not officially supported by miniaudio. This file was added just as
a way to play around and experiment with Cosmopolitan as a proof of concept and to test the viability
of supporting such a compiler. If you get compilation or runtime errors you're on your own.
---------------------------------------------------------------------------------------------------
This is a version of windows.h for compiling with Cosmopolitan. It's not complete. It's intended to
define some missing items from cosmopolitan.h. Hopefully as the project develops we can eventually
eliminate all of the content in this file.
*/
#ifndef _WINDOWS_
#define _WINDOWS_
#define WINAPI
#define STDMETHODCALLTYPE
#define CALLBACK
typedef uint64_t HWND;
typedef uint64_t HANDLE;
typedef uint64_t HKEY;
typedef uint64_t HWAVEIN;
typedef uint64_t HWAVEOUT;
typedef uint32_t HRESULT;
typedef uint8_t BYTE;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef uint64_t DWORDLONG;
typedef int32_t BOOL;
typedef int32_t LONG; /* `long` is always 32-bit on Windows. */
typedef int64_t LONGLONG;
typedef uint32_t ULONG; /* `long` is always 32-bit on Windows. */
typedef uint64_t ULONGLONG;
typedef char16_t WCHAR;
typedef unsigned int UINT;
typedef char CHAR;
typedef uint64_t ULONG_PTR; /* Everything is 64-bit with Cosmopolitan. */
typedef ULONG_PTR DWORD_PTR;
#define TRUE 1
#define FALSE 0
#define WAIT_OBJECT_0 0
#define INFINITE 0xFFFFFFFF
#define CP_UTF8 65001
#define FAILED(hr) ((hr) < 0)
#define SUCCEEDED(hr) ((hr) >= 0)
#define NOERROR 0
#define S_OK 0
#define S_FALSE 1
#define E_POINTER ((HRESULT)0x80004003)
#define E_UNEXPECTED ((HRESULT)0x8000FFFF)
#define E_NOTIMPL ((HRESULT)0x80004001)
#define E_OUTOFMEMORY ((HRESULT)0x8007000E)
#define E_INVALIDARG ((HRESULT)0x80070057)
#define E_NOINTERFACE ((HRESULT)0x80004002)
#define E_HANDLE ((HRESULT)0x80070006)
#define E_ABORT ((HRESULT)0x80004004)
#define E_FAIL ((HRESULT)0x80004005)
#define E_ACCESSDENIED ((HRESULT)0x80070005)
#define ERROR_SUCCESS 0
#define ERROR_FILE_NOT_FOUND 2
#define ERROR_PATH_NOT_FOUND 3
#define ERROR_TOO_MANY_OPEN_FILES 4
#define ERROR_ACCESS_DENIED 5
#define ERROR_NOT_ENOUGH_MEMORY 8
#define ERROR_HANDLE_EOF 38
#define ERROR_INVALID_PARAMETER 87
#define ERROR_DISK_FULL 112
#define ERROR_SEM_TIMEOUT 121
#define ERROR_NEGATIVE_SEEK 131
typedef struct
{
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID, IID;
typedef int64_t LARGE_INTEGER;
#define HKEY_LOCAL_MACHINE ((HKEY)(ULONG_PTR)(0x80000002))
#define KEY_READ 0x00020019
static HANDLE CreateEventA(struct NtSecurityAttributes* lpEventAttributes, bool32 bManualReset, bool32 bInitialState, const char* lpName)
{
assert(lpName == NULL); /* If this is ever triggered we'll need to do a ANSI-to-Unicode conversion. */
return (HANDLE)CreateEvent(lpEventAttributes, bManualReset, bInitialState, (const char16_t*)lpName);
}
static BOOL IsEqualGUID(const GUID* a, const GUID* b)
{
return memcmp(a, b, sizeof(GUID)) == 0;
}
#endif /* _WINDOWS_ */
+508
View File
@@ -0,0 +1,508 @@
#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 size_t ma_libopus_ds_sizeof(void)
{
return sizeof(ma_libopus);
}
static void ma_libopus_ds_uninit(ma_data_source* pDataSource)
{
ma_libopus_uninit((ma_libopus*)pDataSource);
}
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 ma_gDataSourceVTable_libopus =
{
ma_libopus_ds_sizeof,
ma_libopus_ds_uninit,
NULL, /* onCopy. Copying is not supported. */
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 */
};
#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_SET;
} else if (whence == SEEK_END) {
origin = MA_SEEK_END;
} else {
origin = MA_SEEK_CUR;
}
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, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus)
{
ma_result result;
ma_data_source_config dataSourceConfig;
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
memset(pOpus, 0, sizeof(*pOpus));
pOpus->allocationCallbacks = ma_allocation_callbacks_init_copy(pAllocationCallbacks);
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.pVTable = &ma_gDataSourceVTable_libopus;
result = ma_data_source_base_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, pAllocationCallbacks, 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. */
(void)ma_gDataSourceVTable_libopus;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API void ma_libopus_uninit(ma_libopus* pOpus)
{
if (pOpus == NULL) {
return;
}
#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_base_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 void ma_decoding_backend_info__libopus(void* pUserData, ma_decoding_backend_info* pInfo)
{
(void)pUserData;
pInfo->pName = "Opus";
pInfo->pLibraryName = "libopus";
pInfo->pVendor = "Xiph.Org";
pInfo->encodingFormat = ma_encoding_format_opus;
}
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 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);
ma_free(pOpus, pAllocationCallbacks);
}
static ma_decoding_backend_vtable ma_gDecodingBackendVTable_libopus =
{
ma_decoding_backend_info__libopus,
ma_decoding_backend_init__libopus,
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
MA_API ma_decoding_backend_vtable* ma_libopus_get_vtable(void)
{
return ma_decoding_backend_libopus;
}
#endif /* miniaudio_libopus_c */
@@ -0,0 +1,46 @@
/*
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.
This does not support copying.
*/
#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_allocation_callbacks allocationCallbacks;
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 void ma_libopus_uninit(ma_libopus* pOpus);
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;
MA_API ma_decoding_backend_vtable* ma_libopus_get_vtable(void);
#ifdef __cplusplus
}
#endif
#endif /* miniaudio_libopus_h */
@@ -0,0 +1,560 @@
#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 size_t ma_libvorbis_ds_sizeof(void)
{
return sizeof(ma_libvorbis);
}
static void ma_libvorbis_ds_uninit(ma_data_source* pDataSource)
{
ma_libvorbis_uninit((ma_libvorbis*)pDataSource);
}
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 ma_gDataSourceVTable_libvorbis =
{
ma_libvorbis_ds_sizeof,
ma_libvorbis_ds_uninit,
NULL, /* onCopy. Copying is not supported. */
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 */
};
#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_SET;
} else if (whence == SEEK_END) {
origin = MA_SEEK_END;
} else {
origin = MA_SEEK_CUR;
}
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->allocationCallbacks = ma_allocation_callbacks_init_copy(pAllocationCallbacks);
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.pVTable = &ma_gDataSourceVTable_libvorbis;
result = ma_data_source_base_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_base_uninit(&pVorbis->ds);
return MA_OUT_OF_MEMORY;
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. */
(void)ma_gDataSourceVTable_libvorbis;
(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_base_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 void ma_libvorbis_uninit(ma_libvorbis* pVorbis)
{
if (pVorbis == NULL) {
return;
}
#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_base_uninit(&pVorbis->ds);
ma_free(pVorbis->vf, &pVorbis->allocationCallbacks);
}
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 void ma_decoding_backend_info__libvorbis(void* pUserData, ma_decoding_backend_info* pInfo)
{
(void)pUserData;
pInfo->pName = "Vorbis";
pInfo->pLibraryName = "libvorbis";
pInfo->pVendor = "Xiph.Org";
pInfo->encodingFormat = ma_encoding_format_vorbis;
}
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 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);
ma_free(pVorbis, pAllocationCallbacks);
}
static ma_decoding_backend_vtable ma_gDecodingBackendVTable_libvorbis =
{
ma_decoding_backend_info__libvorbis,
ma_decoding_backend_init__libvorbis,
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
MA_API ma_decoding_backend_vtable* ma_libvorbis_get_vtable(void)
{
return ma_decoding_backend_libvorbis;
}
#endif /* miniaudio_libvorbis_c */
@@ -0,0 +1,45 @@
/*
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.
This does not support copying.
*/
#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_allocation_callbacks allocationCallbacks;
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 void ma_libvorbis_uninit(ma_libvorbis* pVorbis);
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;
MA_API ma_decoding_backend_vtable* ma_libvorbis_get_vtable(void);
#ifdef __cplusplus
}
#endif
#endif /* miniaudio_libvorbis_h */
+102 -2
View File
@@ -1,8 +1,13 @@
/* 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
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.
The `ma_libopus` object can be plugged into any `ma_data_source_*()` API.
This can also be used as a custom decoding backend for `ma_decoder`. Use `ma_decoding_backend_libopus`
to wire up the custom decoding backend to `ma_decoder`. See the custom_decoder example for an example
of how to do this.
You need to include this file after miniaudio.h.
*/
@@ -39,6 +44,9 @@ MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pForma
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 const ma_decoding_backend_vtable* ma_decoding_backend_libopus;
#ifdef __cplusplus
}
#endif
@@ -493,4 +501,96 @@ MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint6
#endif
}
/*
The code below defines the vtable that you'll plug into your `ma_decoder_config` object.
*/
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_encoding_format ma_decoding_backend_get_encoding_format__libopus(void* pUserData, ma_data_source* pBackend)
{
(void)pUserData;
(void)pBackend;
/*
When pBackend is null, return ma_encoding_format_unknown if the backend supports multiple
formats. An example might be an FFmpeg backend. If the backend only supports a single format,
like this one, return the format directly (if it's not recognized by miniaudio, return
ma_encoding_format_unknown).
When pBackend is non-null, return the encoded format of the data source. If the format is not
recognized by miniaudio, return ma_encoding_format_unknown.
Since this backend only operates on Opus streams, we can just return ma_encoding_format_opus
in all cases.
*/
return ma_encoding_format_opus;
}
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,
ma_decoding_backend_get_encoding_format__libopus
};
const ma_decoding_backend_vtable* ma_decoding_backend_libopus = &g_ma_decoding_backend_vtable_libopus;
#endif
+103 -3
View File
@@ -1,8 +1,13 @@
/* 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
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.
The `ma_libvorbis` object can be plugged into any `ma_data_source_*()` API.
This can also be used as a custom decoding backend for `ma_decoder`. Use `ma_decoding_backend_libvorbis`
to wire up the custom decoding backend to `ma_decoder`. See the custom_decoder example for an example
of how to do this.
You need to include this file after miniaudio.h.
*/
@@ -42,6 +47,9 @@ MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format*
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 const ma_decoding_backend_vtable* ma_decoding_backend_libvorbis;
#ifdef __cplusplus
}
#endif
@@ -326,7 +334,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;
@@ -513,4 +521,96 @@ MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma
#endif
}
/*
The code below defines the vtable that you'll plug into your `ma_decoder_config` object.
*/
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_encoding_format ma_decoding_backend_get_encoding_format__libvorbis(void* pUserData, ma_data_source* pBackend)
{
(void)pUserData;
(void)pBackend;
/*
When pBackend is null, return ma_encoding_format_unknown if the backend supports multiple
formats. An example might be an FFmpeg backend. If the backend only supports a single format,
like this one, return the format directly (if it's not recognized by miniaudio, return
ma_encoding_format_unknown).
When pBackend is non-null, return the encoded format of the data source. If the format is not
recognized by miniaudio, return ma_encoding_format_unknown.
Since this backend only operates on Vorbis streams, we can just return ma_encoding_format_vorbis
in all cases.
*/
return ma_encoding_format_vorbis;
}
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,
ma_decoding_backend_get_encoding_format__libvorbis
};
const ma_decoding_backend_vtable* ma_decoding_backend_libvorbis = &g_ma_decoding_backend_vtable_libvorbis;
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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;
@@ -57,7 +61,7 @@ MA_API ma_result ma_channel_combiner_node_init(ma_node_graph* pNodeGraph, const
outputChannels[0] = pConfig->channels;
baseConfig = pConfig->nodeConfig;
baseConfig.vtable = &g_ma_channel_combiner_node_vtable;
baseConfig.pVTable = &g_ma_channel_combiner_node_vtable;
baseConfig.inputBusCount = pConfig->channels; /* The vtable has an unknown channel count, so must specify it here. */
baseConfig.pInputChannels = inputChannels;
baseConfig.pOutputChannels = outputChannels;
@@ -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" {
@@ -9,7 +11,7 @@ extern "C" {
typedef struct
{
ma_node_config nodeConfig;
ma_uint32 channels; /* The number of channels of the source, which will be the same as the output. Must be 1 or 2. The excite bus must always have one channel. */
ma_uint32 channels;
} ma_channel_combiner_node_config;
MA_API ma_channel_combiner_node_config ma_channel_combiner_node_config_init(ma_uint32 channels);
@@ -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;
@@ -61,7 +65,7 @@ MA_API ma_result ma_channel_separator_node_init(ma_node_graph* pNodeGraph, const
}
baseConfig = pConfig->nodeConfig;
baseConfig.vtable = &g_ma_channel_separator_node_vtable;
baseConfig.pVTable = &g_ma_channel_separator_node_vtable;
baseConfig.outputBusCount = pConfig->channels; /* The vtable has an unknown channel count, so must specify it here. */
baseConfig.pInputChannels = inputChannels;
baseConfig.pOutputChannels = outputChannels;
@@ -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" {
@@ -9,7 +11,7 @@ extern "C" {
typedef struct
{
ma_node_config nodeConfig;
ma_uint32 channels; /* The number of channels of the source, which will be the same as the output. Must be 1 or 2. The excite bus must always have one channel. */
ma_uint32 channels;
} ma_channel_separator_node_config;
MA_API ma_channel_separator_node_config ma_channel_separator_node_config_init(ma_uint32 channels);
@@ -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"
@@ -139,11 +138,11 @@ int main(int argc, char** argv)
/*done4:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL);
done3: ma_channel_separator_node_uninit(&g_separatorNode, NULL);
done2: ma_channel_combiner_node_uninit(&g_combinerNode, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph);
done0: ma_device_uninit(&device);
(void)argc;
(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;
}
+15 -5
View File
@@ -1,11 +1,19 @@
#ifndef miniaudio_ltrim_node_c
#define miniaudio_ltrim_node_c
#include "ma_ltrim_node.h"
#include <string.h> /* For memset(). */
#ifndef ma_min
#define ma_min(a, b) (((a) < (b)) ? (a) : (b))
#endif
MA_API ma_ltrim_node_config ma_ltrim_node_config_init(ma_uint32 channels, float threshold)
{
ma_ltrim_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_ltrim_node_init(). */
config.channels = channels;
config.threshold = threshold;
@@ -59,8 +67,8 @@ static ma_node_vtable g_ma_ltrim_node_vtable =
{
ma_ltrim_node_process_pcm_frames,
NULL,
1, /* 1 input channel. */
1, /* 1 output channel. */
1, /* 1 input bus. */
1, /* 1 output bus. */
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
};
@@ -73,7 +81,7 @@ MA_API ma_result ma_ltrim_node_init(ma_node_graph* pNodeGraph, const ma_ltrim_no
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pTrimNode);
memset(pTrimNode, 0, sizeof(*pTrimNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -83,7 +91,7 @@ MA_API ma_result ma_ltrim_node_init(ma_node_graph* pNodeGraph, const ma_ltrim_no
pTrimNode->foundStart = MA_FALSE;
baseConfig = pConfig->nodeConfig;
baseConfig.vtable = &g_ma_ltrim_node_vtable;
baseConfig.pVTable = &g_ma_ltrim_node_vtable;
baseConfig.pInputChannels = &pConfig->channels;
baseConfig.pOutputChannels = &pConfig->channels;
@@ -100,3 +108,5 @@ MA_API void ma_ltrim_node_uninit(ma_ltrim_node* pTrimNode, const ma_allocation_c
/* The base node is always uninitialized first. */
ma_node_uninit(pTrimNode, pAllocationCallbacks);
}
#endif /* miniaudio_ltrim_node_c */
+5 -3
View File
@@ -1,6 +1,8 @@
/* Include ma_ltrim_node.h after miniaudio.h */
#ifndef ma_ltrim_node_h
#define ma_ltrim_node_h
#ifndef miniaudio_ltrim_node_h
#define miniaudio_ltrim_node_h
#include "../../../miniaudio.h"
#ifdef __cplusplus
extern "C" {
@@ -32,4 +34,4 @@ MA_API void ma_ltrim_node_uninit(ma_ltrim_node* pTrimNode, const ma_allocation_c
#ifdef __cplusplus
}
#endif
#endif /* ma_ltrim_node_h */
#endif /* miniaudio_ltrim_node_h */
@@ -1,5 +1,4 @@
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include "../../../miniaudio.c"
#include "ma_ltrim_node.c"
#include <stdio.h>
@@ -108,8 +107,8 @@ int main(int argc, char** argv)
/*done3:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL);
done2: ma_ltrim_node_uninit(&g_trimNode, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph);
done0: ma_device_uninit(&device);
return 0;
}
}
+11 -5
View File
@@ -1,12 +1,16 @@
#ifndef miniaudio_reverb_node_c
#define miniaudio_reverb_node_c
#define VERBLIB_IMPLEMENTATION
#include "ma_reverb_node.h"
#include <string.h> /* For memset(). */
MA_API ma_reverb_node_config ma_reverb_node_config_init(ma_uint32 channels, ma_uint32 sampleRate)
{
ma_reverb_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_reverb_node_init(). */
config.channels = channels;
config.sampleRate = sampleRate;
@@ -34,8 +38,8 @@ static ma_node_vtable g_ma_reverb_node_vtable =
{
ma_reverb_node_process_pcm_frames,
NULL,
1, /* 1 input channel. */
1, /* 1 output channel. */
1, /* 1 input bus. */
1, /* 1 output bus. */
MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Reverb requires continuous processing to ensure the tail get's processed. */
};
@@ -48,7 +52,7 @@ MA_API ma_result ma_reverb_node_init(ma_node_graph* pNodeGraph, const ma_reverb_
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pReverbNode);
memset(pReverbNode, 0, sizeof(*pReverbNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -59,7 +63,7 @@ MA_API ma_result ma_reverb_node_init(ma_node_graph* pNodeGraph, const ma_reverb_
}
baseConfig = pConfig->nodeConfig;
baseConfig.vtable = &g_ma_reverb_node_vtable;
baseConfig.pVTable = &g_ma_reverb_node_vtable;
baseConfig.pInputChannels = &pConfig->channels;
baseConfig.pOutputChannels = &pConfig->channels;
@@ -76,3 +80,5 @@ MA_API void ma_reverb_node_uninit(ma_reverb_node* pReverbNode, const ma_allocati
/* The base node is always uninitialized first. */
ma_node_uninit(pReverbNode, pAllocationCallbacks);
}
#endif /* miniaudio_reverb_node_c */
+4 -3
View File
@@ -1,7 +1,8 @@
/* Include ma_reverb_node.h after miniaudio.h */
#ifndef ma_reverb_node_h
#define ma_reverb_node_h
#ifndef miniaudio_reverb_node_h
#define miniaudio_reverb_node_h
#include "../../../miniaudio.h"
#include "verblib.h"
#ifdef __cplusplus
@@ -39,4 +40,4 @@ MA_API void ma_reverb_node_uninit(ma_reverb_node* pReverbNode, const ma_allocati
#ifdef __cplusplus
}
#endif
#endif /* ma_reverb_node_h */
#endif /* miniaudio_reverb_node_h */
@@ -1,5 +1,4 @@
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include "../../../miniaudio.c"
#include "ma_reverb_node.c"
#include <stdio.h>
@@ -8,15 +7,20 @@
#define DEVICE_CHANNELS 1 /* For this example, always set to 1. */
#define DEVICE_SAMPLE_RATE 48000 /* Cannot be less than 22050 for this example. */
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_reverb_node g_reverbNode; /* The reverb node. */
static ma_node_graph g_nodeGraph; /* The main node graph that we'll be feeding data through. */
static ma_audio_ring_buffer 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_reverb_node g_reverbNode; /* The reverb 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);
/*
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
@@ -24,7 +28,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
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);
ma_audio_ring_buffer_write_pcm_frames(&g_dataSupply, pInput, frameCount, NULL);
/* 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);
@@ -38,6 +42,7 @@ int main(int argc, char** argv)
ma_node_graph_config nodeGraphConfig;
ma_reverb_node_config reverbNodeConfig;
ma_data_source_node_config dataSupplyNodeConfig;
ma_audio_ring_buffer_config ringBufferConfig;
deviceConfig = ma_device_config_init(ma_device_type_duplex);
deviceConfig.capture.pDeviceID = NULL;
@@ -78,7 +83,9 @@ int main(int argc, char** argv)
/* Data supply. Attached to input bus 0 of the reverb node. */
result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_dataSupply);
ringBufferConfig = ma_audio_ring_buffer_config_init(device.capture.format, device.capture.channels, device.sampleRate, device.capture.internalPeriodSizeInFrames * 3);
result = ma_audio_ring_buffer_init(&ringBufferConfig, &g_dataSupply);
if (result != MA_SUCCESS) {
printf("Failed to initialize audio buffer for source.");
goto done2;
@@ -89,7 +96,7 @@ int main(int argc, char** argv)
result = ma_data_source_node_init(&g_nodeGraph, &dataSupplyNodeConfig, NULL, &g_dataSupplyNode);
if (result != MA_SUCCESS) {
printf("Failed to initialize source node.");
goto done2;
goto done3;
}
ma_node_attach_output_bus(&g_dataSupplyNode, 0, &g_reverbNode, 0);
@@ -106,13 +113,14 @@ int main(int argc, char** argv)
ma_device_stop(&device);
/*done3:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL);
/*done4:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL);
done3: ma_audio_ring_buffer_uninit(&g_dataSupply);
done2: ma_reverb_node_uninit(&g_reverbNode, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph);
done0: ma_device_uninit(&device);
(void)argc;
(void)argv;
return 0;
}
}
+16 -6
View File
@@ -248,13 +248,23 @@ extern "C" {
#include <math.h>
#ifdef _MSC_VER
#define VERBLIB_INLINE __forceinline
#define VERBLIB_INLINE __forceinline
#elif defined(__GNUC__)
#if defined(__STRICT_ANSI__)
#define VERBLIB_GNUC_INLINE_HINT __inline__
#else
#define VERBLIB_GNUC_INLINE_HINT inline
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
#define VERBLIB_INLINE VERBLIB_GNUC_INLINE_HINT __attribute__((always_inline))
#else
#define VERBLIB_INLINE VERBLIB_GNUC_INLINE_HINT
#endif
#elif defined(__WATCOMC__)
#define VERBLIB_INLINE __inline
#else
#ifdef __GNUC__
#define VERBLIB_INLINE inline __attribute__((always_inline))
#else
#define VERBLIB_INLINE inline
#endif
#define VERBLIB_INLINE
#endif
#define verblib_max(x, y) (((x) > (y)) ? (x) : (y))
+11 -5
View File
@@ -1,12 +1,16 @@
#ifndef miniaudio_vocoder_node_c
#define miniaudio_vocoder_node_c
#define VOCLIB_IMPLEMENTATION
#include "ma_vocoder_node.h"
#include <string.h> /* For memset(). */
MA_API ma_vocoder_node_config ma_vocoder_node_config_init(ma_uint32 channels, ma_uint32 sampleRate)
{
ma_vocoder_node_config config;
MA_ZERO_OBJECT(&config);
memset(&config, 0, sizeof(config));
config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_vocoder_node_init(). */
config.channels = channels;
config.sampleRate = sampleRate;
@@ -30,8 +34,8 @@ static ma_node_vtable g_ma_vocoder_node_vtable =
{
ma_vocoder_node_process_pcm_frames,
NULL,
2, /* 2 input channels. */
1, /* 1 output channel. */
2, /* 2 input buses. */
1, /* 1 output bus. */
0
};
@@ -46,7 +50,7 @@ MA_API ma_result ma_vocoder_node_init(ma_node_graph* pNodeGraph, const ma_vocode
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pVocoderNode);
memset(pVocoderNode, 0, sizeof(*pVocoderNode));
if (pConfig == NULL) {
return MA_INVALID_ARGS;
@@ -61,7 +65,7 @@ MA_API ma_result ma_vocoder_node_init(ma_node_graph* pNodeGraph, const ma_vocode
outputChannels[0] = pConfig->channels; /* Output channels is always the same as the source/carrier. */
baseConfig = pConfig->nodeConfig;
baseConfig.vtable = &g_ma_vocoder_node_vtable;
baseConfig.pVTable = &g_ma_vocoder_node_vtable;
baseConfig.pInputChannels = inputChannels;
baseConfig.pOutputChannels = outputChannels;
@@ -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,26 +6,30 @@ 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>
#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. */
#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 excite node. */
static ma_audio_buffer_ref g_exciteData; /* The underlying data source of the source node. */
static ma_data_source_node g_sourceNode; /* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */
static ma_data_source_node g_exciteNode; /* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */
static ma_vocoder_node g_vocoderNode; /* The vocoder node. */
static ma_node_graph g_nodeGraph;
static ma_waveform g_sourceData; /* The underlying data source of the excite node. */
static ma_audio_ring_buffer g_exciteData; /* The underlying data source of the source node. */
static ma_data_source_node g_sourceNode; /* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */
static ma_data_source_node g_exciteNode; /* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */
static ma_vocoder_node g_vocoderNode; /* The vocoder node. */
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
@@ -33,7 +37,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
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_exciteData, pInput, frameCount);
ma_audio_ring_buffer_write_pcm_frames(&g_exciteData, pInput, frameCount, NULL);
/* 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);
@@ -49,6 +53,7 @@ int main(int argc, char** argv)
ma_data_source_node_config sourceNodeConfig;
ma_data_source_node_config exciteNodeConfig;
ma_waveform_config waveformConfig;
ma_audio_ring_buffer_config ringBufferConfig;
deviceConfig = ma_device_config_init(ma_device_type_duplex);
deviceConfig.capture.pDeviceID = NULL;
@@ -96,7 +101,7 @@ int main(int argc, char** argv)
result = ma_waveform_init(&waveformConfig, &g_sourceData);
if (result != MA_SUCCESS) {
printf("Failed to initialize waveform for excite node.");
goto done3;
goto done2;
}
sourceNodeConfig = ma_data_source_node_config_init(&g_sourceData);
@@ -111,10 +116,12 @@ int main(int argc, char** argv)
/* Excite/modulator. Attached to input bus 1 of the vocoder node. */
result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_exciteData);
ringBufferConfig = ma_audio_ring_buffer_config_init(device.capture.format, device.capture.channels, device.sampleRate, device.capture.internalPeriodSizeInFrames * 3);
result = ma_audio_ring_buffer_init(&ringBufferConfig, &g_exciteData);
if (result != MA_SUCCESS) {
printf("Failed to initialize audio buffer for source.");
goto done2;
goto done4;
}
exciteNodeConfig = ma_data_source_node_config_init(&g_exciteData);
@@ -122,7 +129,7 @@ int main(int argc, char** argv)
result = ma_data_source_node_init(&g_nodeGraph, &exciteNodeConfig, NULL, &g_exciteNode);
if (result != MA_SUCCESS) {
printf("Failed to initialize source node.");
goto done2;
goto done5;
}
ma_node_attach_output_bus(&g_exciteNode, 0, &g_vocoderNode, 1);
@@ -136,10 +143,12 @@ int main(int argc, char** argv)
/* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */
ma_device_stop(&device);
/*done4:*/ ma_data_source_node_uninit(&g_exciteNode, NULL);
done3: ma_data_source_node_uninit(&g_sourceNode, NULL);
/*done6:*/ ma_data_source_node_uninit(&g_exciteNode, NULL);
done5: ma_audio_ring_buffer_uninit(&g_exciteData);
done4: ma_data_source_node_uninit(&g_sourceNode, NULL);
done3: ma_waveform_uninit(&g_sourceData);
done2: ma_vocoder_node_uninit(&g_vocoderNode, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
done1: ma_node_graph_uninit(&g_nodeGraph);
done0: ma_device_uninit(&device);
(void)argc;
+16 -6
View File
@@ -151,13 +151,23 @@ extern "C" {
#include <assert.h>
#ifdef _MSC_VER
#define VOCLIB_INLINE __forceinline
#define VOCLIB_INLINE __forceinline
#elif defined(__GNUC__)
#if defined(__STRICT_ANSI__)
#define VOCLIB_GNUC_INLINE_HINT __inline__
#else
#define VOCLIB_GNUC_INLINE_HINT inline
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
#define VOCLIB_INLINE VOCLIB_GNUC_INLINE_HINT __attribute__((always_inline))
#else
#define VOCLIB_INLINE VOCLIB_GNUC_INLINE_HINT
#endif
#elif defined(__WATCOMC__)
#define VOCLIB_INLINE __inline
#else
#ifdef __GNUC__
#define VOCLIB_INLINE inline __attribute__((always_inline))
#else
#define VOCLIB_INLINE inline
#endif
#define VOCLIB_INLINE
#endif
/* Filters
@@ -0,0 +1,110 @@
#ifndef miniaudio_vfs_debugging_c
#define miniaudio_vfs_debugging_c
#include "miniaudio_vfs_debugging.h"
MA_API ma_vfs_debugging_config ma_vfs_debugging_config_init(ma_vfs* pUnderlyingVFS, ma_uint32 latencyInMilliseconds)
{
ma_vfs_debugging_config config;
MA_ZERO_OBJECT(&config);
config.pUnderlyingVFS = pUnderlyingVFS;
config.latencyInMilliseconds = latencyInMilliseconds;
return config;
}
static ma_vfs_debugging* ma_vfs_debugging_cast(ma_vfs* pVFS)
{
return (ma_vfs_debugging*)pVFS;
}
static ma_vfs* ma_vfs_debugging_get_underlying_vfs(ma_vfs* pVFS)
{
return ma_vfs_debugging_cast(pVFS)->config.pUnderlyingVFS;
}
static ma_result ma_vfs_debugging_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
{
return ma_vfs_open(ma_vfs_debugging_get_underlying_vfs(pVFS), pFilePath, openMode, pFile);
}
static ma_result ma_vfs_debugging_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
{
return ma_vfs_open_w(ma_vfs_debugging_get_underlying_vfs(pVFS), pFilePath, openMode, pFile);
}
static ma_result ma_vfs_debugging_close(ma_vfs* pVFS, ma_vfs_file file)
{
return ma_vfs_close(ma_vfs_debugging_get_underlying_vfs(pVFS), file);
}
static ma_result ma_vfs_debugging_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
{
ma_vfs_debugging* pDebuggingVFS = ma_vfs_debugging_cast(pVFS);
ma_result result;
/* Introduce artificial latency if requested. Ignored on Emscripten. */
#ifndef __EMSCRIPTEN__
{
if (pDebuggingVFS->config.latencyInMilliseconds > 0) {
ma_sleep(pDebuggingVFS->config.latencyInMilliseconds);
}
}
#endif
/*printf("READING\n");*/
result = ma_vfs_read(ma_vfs_debugging_get_underlying_vfs(pVFS), file, pDst, sizeInBytes, pBytesRead);
return result;
}
static ma_result ma_vfs_debugging_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
{
return ma_vfs_write(ma_vfs_debugging_get_underlying_vfs(pVFS), file, pSrc, sizeInBytes, pBytesWritten);
}
static ma_result ma_vfs_debugging_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
{
return ma_vfs_seek(ma_vfs_debugging_get_underlying_vfs(pVFS), file, offset, origin);
}
static ma_result ma_vfs_debugging_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
{
return ma_vfs_tell(ma_vfs_debugging_get_underlying_vfs(pVFS), file, pCursor);
}
static ma_result ma_vfs_debugging_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
{
return ma_vfs_info(ma_vfs_debugging_get_underlying_vfs(pVFS), file, pInfo);
}
MA_API ma_result ma_vfs_debugging_init(const ma_vfs_debugging_config* pConfig, ma_vfs_debugging* pVFS)
{
if (pVFS == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pVFS);
if (pConfig == NULL) {
pVFS->config = ma_vfs_debugging_config_init(NULL, 0);
} else {
pVFS->config = *pConfig;
}
pVFS->cb.onOpen = ma_vfs_debugging_open;
pVFS->cb.onOpenW = ma_vfs_debugging_open_w;
pVFS->cb.onClose = ma_vfs_debugging_close;
pVFS->cb.onRead = ma_vfs_debugging_read;
pVFS->cb.onWrite = ma_vfs_debugging_write;
pVFS->cb.onSeek = ma_vfs_debugging_seek;
pVFS->cb.onTell = ma_vfs_debugging_tell;
pVFS->cb.onInfo = ma_vfs_debugging_info;
return MA_SUCCESS;
}
#endif /* miniaudio_vfs_debugging_c */
@@ -0,0 +1,27 @@
#ifndef miniaudio_vfs_debugging_h
#define miniaudio_vfs_debugging_h
#include "../../../miniaudio.h"
/*
This is a VFS for debugging purposes. I use it for things like artificial latency.
*/
typedef struct ma_vfs_debugging_config
{
ma_vfs* pUnderlyingVFS; /* The underlying VFS to which all calls are forwarded. */
ma_uint32 latencyInMilliseconds; /* The amount of latency to introduce in milliseconds. This will be done with a sleep every read. */
} ma_vfs_debugging_config;
MA_API ma_vfs_debugging_config ma_vfs_debugging_config_init(ma_vfs* pUnderlyingVFS, ma_uint32 latencyInMilliseconds);
typedef struct ma_vfs_debugging
{
ma_vfs_callbacks cb; /* Must be first. */
ma_vfs_debugging_config config;
} ma_vfs_debugging;
MA_API ma_result ma_vfs_debugging_init(const ma_vfs_debugging_config* pConfig, ma_vfs_debugging* pVFS);
#endif /* miniaudio_vfs_debugging_h */
+2
View File
@@ -0,0 +1,2 @@
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
+39228 -27793
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
includedir=@MINIAUDIO_PC_INCLUDEDIR@
libdir=@MINIAUDIO_PC_LIBDIR@
Name: miniaudio
Description: An audio playback and capture library.
URL: https://miniaud.io/
License: Unlicense OR MIT-0
Version: @PROJECT_VERSION@
Requires.private: @MINIAUDIO_PC_REQUIRES_PRIVATE@
Cflags: -I${includedir} @MINIAUDIO_PC_CFLAGS@
Libs: -L${libdir} -lminiaudio
Libs.private: @MINIAUDIO_PC_LIBS_PRIVATE@
+19 -5
View File
@@ -2,22 +2,36 @@ Building
========
Build and run from this directory. Example:
gcc ../test_deviceio/ma_test_deviceio.c -o bin/test_deviceio -ldl -lm -lpthread -Wall -Wextra -Wpedantic -std=c89
./bin/test_deviceio
gcc ../deviceio/deviceio.c -o bin/deviceio -ldl -lm -lpthread -Wall -Wextra -Wpedantic -std=c89
./bin/deviceio
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:
emcc ../test_emscripten/ma_test_emscripten.c -o bin/test_emscripten.html -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY -DMA_ENABLE_AUDIO_WORKLETS -Wall -Wextra
emcc ../emscripten/emscripten.c -o bin/emscripten.html -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -DMA_ENABLE_AUDIO_WORKLETS -Wall -Wextra
If you output WASM it may not work when running the web page locally. To test you can run with something
like this:
emrun ./bin/test_emscripten.html
emrun ./bin/emscripten.html
If you want to see stdout on the command line when running from emrun, add `--emrun` to your emcc command.
If you want to see stdout on the command line when running from emrun, add `--emrun` to your emcc command.
To use with CMake, you can do something like this:
emcmake cmake -S ../../ -B cmake-emcc -DCMAKE_BUILD_TYPE=Debug -DMINIAUDIO_NO_LIBVORBIS=Yes -DMINIAUDIO_NO_LIBOPUS=Yes -DMINIAUDIO_BUILD_TESTS=Yes -DMINIAUDIO_BUILD_EXAMPLES=No
Then to compile with CMake:
cmake --build cmake-emcc -j
To do a clean rebuild:
cmake --build cmake-emcc -j --clean-first
+2
View File
@@ -0,0 +1,2 @@
set DJGPP=C:\DJGPP\DJGPP.ENV
set PATH=C:\DJGPP\BIN;%PATH%
+15
View File
@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
@@ -0,0 +1 @@
/build
@@ -0,0 +1,62 @@
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"
externalNativeBuild {
cmake {
cppFlags += "-Wall -Wextra -pedantic"
}
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package io.miniaud.miniaudiotester
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.miniaud.miniaudiotester", appContext.packageName)
}
}
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MiniaudioTester"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,37 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("miniaudiotester")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
native-lib.cpp)
# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
android
log)
@@ -0,0 +1,183 @@
#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);
(void)pFramesIn;
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 = "";
(void)env;
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_device_backend_config pBackends[1];
size_t backendCount;
if (backend == BACKEND_AUTO) {
backendCount = 0;
} else {
backendCount = 1;
if (backend == BACKEND_AAUDIO) {
pBackends[0] = ma_device_backend_config_init(ma_device_backend_aaudio, nullptr);
} else if (backend == BACKEND_OPENSL) {
pBackends[0] = ma_device_backend_config_init(ma_device_backend_opensl, nullptr);
} 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());
}
(void)env;
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.");
}
}
(void)env;
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;
}
(void)env;
return pAudioState->hasError;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_GetAudioError(JNIEnv *env, jobject, jlong audioState)
{
auto* pAudioState = (audio_state_t*)audioState;
if (pAudioState == nullptr) {
return env->NewStringUTF("Out of memory");
}
return env->NewStringUTF(pAudioState->errorMessage.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_io_miniaud_miniaudiotester_MainActivity_DeleteAudioState(JNIEnv *env, jobject thiz, jlong audioState)
{
Java_io_miniaud_miniaudiotester_MainActivity_UninitializeAudio(env, thiz, audioState);
delete (audio_state_t*)audioState;
}
@@ -0,0 +1,79 @@
package io.miniaud.miniaudiotester
import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import io.miniaud.miniaudiotester.databinding.ActivityMainBinding
enum class AudioBackend(val value: Int)
{
BACKEND_AUTO(0),
BACKEND_AAUDIO(1),
BACKEND_OPENSL(2)
}
class MainActivity : AppCompatActivity()
{
private lateinit var binding: ActivityMainBinding
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnPlay.setOnClickListener {
val backend: AudioBackend;
if (binding.radioAAudio.isChecked) {
backend = AudioBackend.BACKEND_AAUDIO
} else if (binding.radioOpenSL.isChecked) {
backend = AudioBackend.BACKEND_OPENSL
} else {
backend = AudioBackend.BACKEND_AUTO
}
audioState = PlayAudio(audioState, backend.value)
if (HasAudioError(audioState)) {
binding.textInfo.text = GetAudioError(audioState)
} else {
binding.textInfo.text = "Playing..."
}
}
binding.btnStop.setOnClickListener {
audioState = PauseAudio(audioState)
if (HasAudioError(audioState)) {
binding.textInfo.text = GetAudioError(audioState)
} else {
binding.textInfo.text = "Paused."
}
}
binding.btnUninit.setOnClickListener {
audioState = UninitializeAudio(audioState)
if (HasAudioError(audioState)) {
binding.textInfo.text = GetAudioError(audioState)
} else {
binding.textInfo.text = "Device uninitialized."
}
}
}
private var audioState: Long = 0
external fun UninitializeAudio(audioState: Long): Long
external fun PlayAudio(audioState: Long, backend: Int): Long
external fun PauseAudio(audioState: Long): Long
external fun HasAudioError(audioState: Long): Boolean
external fun GetAudioError(audioState: Long): String
external fun DeleteAudioState(audioState: Long)
companion object {
// Used to load the 'miniaudiotester' library on application startup.
init {
System.loadLibrary("miniaudiotester")
}
}
}
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="320dp"
android:layout_height="144dp"
android:layout_marginStart="44dp"
android:layout_marginTop="90dp"
android:layout_marginEnd="44dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioButton
android:id="@+id/radioAutomatic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/automatic" />
<RadioButton
android:id="@+id/radioAAudio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/aaudio" />
<RadioButton
android:id="@+id/radioOpenSL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/opensl" />
</RadioGroup>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="46dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="235dp"
android:text="@string/backend_label"
android:textSize="34sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/btnPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="46dp"
android:layout_marginTop="00dp"
android:contentDescription="@string/play_button_desc"
app:layout_constraintEnd_toStartOf="@+id/btnStop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:srcCompat="@android:drawable/ic_media_play" />
<ImageButton
android:id="@+id/btnStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="00dp"
android:layout_marginEnd="253dp"
android:contentDescription="@string/pause_button_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnPlay"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:srcCompat="@android:drawable/ic_media_pause" />
<ImageButton
android:id="@+id/btnUninit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="197dp"
android:contentDescription="@string/uninit_button_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnStop"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:srcCompat="@android:drawable/ic_delete" />
<TextView
android:id="@+id/textInfo"
android:layout_width="317dp"
android:layout_height="353dp"
android:layout_marginStart="46dp"
android:layout_marginEnd="45dp"
android:layout_marginBottom="46dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

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