From 88badc611b3044455cb9563e44661eb7090e60e0 Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Mon, 25 Aug 2025 15:35:07 +0800 Subject: [PATCH 01/13] - Start The Decoder with DataConverter Previously, I didn't carefully read the original implementation and how zaudio bridges the function, created a bunch of mess. With a bit more studies, I think I have more confidence on porting Decoder Type into zaudio, starting with the unimplemented type for the Decoder so that I don't get lost in the midway of creating the Decoder type. --- src/zaudio.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/zaudio.c b/src/zaudio.c index 4e7435c..422dd09 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -675,3 +675,42 @@ void WA_ma_sound_get_velocity(const ma_sound* sound, ma_vec3f* vout) { *vout = ma_sound_get_velocity(sound); } //-------------------------------------------------------------------------------------------------- +// ma_data_converter, required for decoder +void zaudioDataConverterConfigInit( + ma_format formatIn, + ma_format formatOut, + ma_uint32 channelsIn, + ma_uint32 channelsOut, + ma_uint32 sampleRateIn, + ma_uint32 sampleRateOut, + ma_data_converter_config* out_config, +){ + assert(out_config != NULL); + *out_config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); +} + +// ma_data_converter +ma_result zaudioDataConverterCreate( + ma_data_converter_config* config, + ma_data_converter** out_handle +){ + assert(config != NULL && out_handle != NULL); + *out_handle = s_mem.onMalloc(sizeof(ma_data_converter), s_mem.pUserData); + ma_result res = ma_data_converter_init(config, &s_mem, *out_handle); + if (res != MA_SUCCESS){ + s_mem.onFree(*out_handle, s_mem.pUserData); + *out_handle = NULL; + } + return res; +} + +void zaudioDataConverterDestroy( + ma_data_converter* handle +){ + assert(handle != NULL); + ma_data_converter_uninit(handle, &s_mem); + s_mem.onFree(handle, s_mem.pUserData); +} + + +//-------------------------------------------------------------------------------------------------- From 9df0f3c9ea0802c23b2d052bf41c52f30e27e45d Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Mon, 25 Aug 2025 17:57:14 +0800 Subject: [PATCH 02/13] - Ported DataConverter The coding part of DataConverter is now done, but this will require testing; nonetheless, now we have the DataConverter and Resampler.Config, building the decoder should be less difficult. I will test all the type once I have done decoder. --- src/zaudio.zig | 132 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/src/zaudio.zig b/src/zaudio.zig index 95591c7..49f2b44 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -267,6 +267,8 @@ pub const PanMode = enum(u32) { pan, }; +pub const DetherMode = enum(u32) { none, rectangle, triangle }; + pub const AttenuationModel = enum(u32) { none, inverse, @@ -736,6 +738,136 @@ pub const AudioBuffer = opaque { return @as(*DataSource, @ptrCast(audio_buffer)); } }; +//-------------------------------------------------------------------------------------------------- +// +// Data Converter +// +//-------------------------------------------------------------------------------------------------- +pub const DataConverter = opaque { + pub const destroy = zaudioDataConverterDestroy; + extern fn zaudioDataConverterDestroy(handle: DataConverter) void; + + pub fn create(config: Config) Error!*DataConverter { + var handle: ?*DataConverter = null; + try maybeError(zaudioDataConverterCreate(&config, &handle)); + return handle.?; + } + extern fn zaudioDataConverterCreate(config: Config, handle: ?*?*DataConverter) Result; + + pub fn processPcmFrames( + converter: *DataConverter, + frames_in: *anyopaque, + frame_count_in: *u64, + frame_out: *anyopaque, + frame_count_out: *u64, + ) Error!void { + try maybeError(ma_data_converter_process_pcm_frames( + converter, + frames_in, + frame_count_in, + frame_out, + frame_count_out, + )); + } + extern fn ma_data_converter_process_pcm_frames(converter: *DataConverter, frames_in: *anyopaque, frame_count_in: *u64, frame_out: *anyopaque, frame_count_out: *u64) Result; + + pub fn setRate(converter: *DataConverter, sample_rate_in: u32, sample_rate_out: u32) Error!void { + try maybeError(setRate(converter, sample_rate_in, sample_rate_out)); + } + extern fn ma_data_converter_set_rate(converter: *DataConverter, sample_rate_in: u32, sample_rate_out: u32) Result; + + pub fn setRateRatio(converter: *DataConverter, ratio_in_out: f32) Error!void { + try maybeError(ma_data_converter_set_rate_ratio(converter, ratio_in_out)); + } + extern fn ma_data_converter_set_rate_ratio(converter: *DataConverter, ratioInOut: f32) Result; + + pub fn getInputLatency(converter: *DataConverter) u64 { + ma_data_converter_get_input_latency(converter); + } + extern fn ma_data_converter_get_input_latency(converter: *DataConverter) u64; + + pub fn getOutPutLatency(converter: *DataConverter) u64 { + ma_data_converter_get_output_latency(converter); + } + extern fn ma_data_converter_get_output_latency(converter: *DataConverter) u64; + + pub fn getRequiredInputFrameCount(converter: *DataConverter, output_frame_count: u64) Error!u64 { + var input_frame_count: u64 = undefined; + try maybeError(ma_data_converter_get_required_input_frame_count(converter, output_frame_count, &input_frame_count)); + return input_frame_count; + } + extern fn ma_data_converter_get_required_input_frame_count(converter: *DataConverter, output_frame_count: u64, input_frame_count: *u64) Result; + + pub fn getExpectedOutputFrameCount(converter: *DataConverter, input_frame_count: u64) Error!u64 { + var output_frame_count: u64 = undefined; + try maybeError(ma_data_converter_get_expected_output_frame_count(converter, input_frame_count, &output_frame_count)); + return output_frame_count; + } + extern fn ma_data_converter_get_expected_output_frame_count(converter: *DataConverter, input_frame_count: u64, output_frame_count: *u64) Result; + + pub fn getInputChannelMap(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Error!void { + try maybeError(ma_data_converter_get_input_channel_map(converter, channel_map, channel_map_cap)); + } + extern fn ma_data_converter_get_input_channel_map(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Result; + + pub fn getOutputChannelMap(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Error!void { + try maybeError(ma_data_converter_get_output_channel_map(converter, channel_map, channel_map_cap)); + } + extern fn ma_data_converter_get_output_channel_map(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Result; + + pub fn reset(converter: *DataConverter) Error!void { + try maybeError(ma_data_converter_reset(converter)); + } + extern fn ma_data_converter_reset(converter: *DataConverter) Result; + + pub const ExecutionPath = enum(u32) { + passthrough, + format_only, + channels_only, + resample_only, + resample_first, + channels_first, + }; + + pub const Config = extern struct { + format_in: Format, + format_out: Format, + channels_in: u32, + channels_out: u32, + sample_rate_in: u32, + sample_rate_out: u32, + channel_map_in: *Channel, + channel_map_out: *Channel, + dither_mode: DetherMode, + channel_mix_mode: ChannelMixMode, + calculate_LFE_from_spatial_channels: u32, + channel_weighs_io_ptr: [*][*]f32, // [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. + allow_dynamic_sample_rate: u32, + resampling: Resampler.Config, + }; +}; + +//-------------------------------------------------------------------------------------------------- +// +// Resampler (Incomplete, but since many of the function requires the type, especially the config, +// we eventually need an opaque function to reduce code repetition) +// +//-------------------------------------------------------------------------------------------------- +pub const Resampler = opaque { + pub const Config = extern struct { + format: Format, + channels: u32, + sample_rate_in: u32, + sample_rate_out: u32, + algorithm: ResampleAlgorithm, + backend_vtable: ?*anyopaque, // TODO: Should be `*ma_resampling_backend_vtable` (custom resamplers). + backend_user_data: ?*anyopaque, + linear: extern struct { + lpf_order: u32, + }, + }; +}; + //-------------------------------------------------------------------------------------------------- // // Node From cb48c0690301be783d5394f5c53d71a30470ca9e Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Tue, 26 Aug 2025 16:15:21 +0800 Subject: [PATCH 03/13] - Daily Commit There is nothing interesting in this commit because I am only half way through the Decoder type. --- src/zaudio.c | 15 ++++++++- src/zaudio.zig | 91 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/src/zaudio.c b/src/zaudio.c index 422dd09..094cb35 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -683,7 +683,7 @@ void zaudioDataConverterConfigInit( ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, - ma_data_converter_config* out_config, + ma_data_converter_config* out_config ){ assert(out_config != NULL); *out_config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); @@ -712,5 +712,18 @@ void zaudioDataConverterDestroy( s_mem.onFree(handle, s_mem.pUserData); } +//-------------------------------------------------------------------------------------------------- +// ma_decoder +void ma_decoder_config_init( + ma_format outputFormat, + ma_uint32 outputChannels, + ma_uint32 outputSampleRate, + ma_decoder_config* out_config +){ + assert(out_config != NULL); + *out_config = ma_decoder_config_init(outputFormat, outputChannels, outputSampleRate); +} + +// There are more variants in decoder than all other types, //-------------------------------------------------------------------------------------------------- diff --git a/src/zaudio.zig b/src/zaudio.zig index 49f2b44..1781e08 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -267,7 +267,11 @@ pub const PanMode = enum(u32) { pan, }; -pub const DetherMode = enum(u32) { none, rectangle, triangle }; +pub const DitherMode = enum(u32) { + none, + rectangle, + triangle, +}; pub const AttenuationModel = enum(u32) { none, @@ -290,6 +294,14 @@ pub const Format = enum(u32) { float32, }; +pub const EncodingFormat = enum(u32) { + unknown, + wav, + flac, + mp3, + vorbis, +}; + pub const PerformanceProfile = enum(u32) { low_latency, conservative, @@ -451,6 +463,12 @@ pub const Vfs = extern struct { on_info: ?*const fn (self: *Vfs, handle: FileHandle, info: *FileInfo) callconv(.C) Result, }; +// these functions were originally located under vfs, but they seems to have no correlation to the type, +// while decoder require such type in order to make it work, so I will temporary locate these functions in here: +pub const readProc = fn (user_data: ?*anyopaque, buffer_out: ?*anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.C) Result; +pub const seekProc = fn (user_data: ?*anyopaque, offset: i64, origin: Vfs.SeekOrigin) callconv(.C) Result; +pub const tellProc = fn (user_data: ?*anyopaque, cursor: ?*i64) callconv(.C) Result; + pub const Context = opaque { // TODO: Add methods. }; @@ -738,6 +756,7 @@ pub const AudioBuffer = opaque { return @as(*DataSource, @ptrCast(audio_buffer)); } }; + //-------------------------------------------------------------------------------------------------- // // Data Converter @@ -838,7 +857,7 @@ pub const DataConverter = opaque { sample_rate_out: u32, channel_map_in: *Channel, channel_map_out: *Channel, - dither_mode: DetherMode, + dither_mode: DitherMode, channel_mix_mode: ChannelMixMode, calculate_LFE_from_spatial_channels: u32, channel_weighs_io_ptr: [*][*]f32, // [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. @@ -847,6 +866,74 @@ pub const DataConverter = opaque { }; }; +//-------------------------------------------------------------------------------------------------- +// +// Decoder +// +//-------------------------------------------------------------------------------------------------- +pub const Decoder = opaque { + + // here are the init functions, but after observed other examples + // I will skip the _w variant until there is a solution to handle wchar_t + + pub const VTable = extern struct { + onInit: ?*const fn ( + user_data: *anyopaque, + on_read: readProc, + on_seek: seekProc, + on_tell: tellProc, + read_seek_tell_user_data: *anyopaque, + config: *const BackendConfig, + allocation_callbacks: AllocationCallbacks, + backend: *?*?DataSource, + ) callconv(.C) Result, + + onInitFile: ?*const fn ( + user_data: *anyopaque, + file_path: [:0]const u8, + config: BackendConfig, + allocation_callbacks: AllocationCallbacks, + backend: *?*?DataSource, + ) callconv(.C) Result, + + onInitMemory: ?*const fn ( + user_data: *anyopaque, + data: *const anyopaque, + data_size: usize, + config: BackendConfig, + allocation_callbacks: AllocationCallbacks, + backend: *?*?DataSource, + ) callconv(.C) Result, + + onUninit: ?*const fn ( + user_data: *anyopaque, + backend: *?DataSource, + allocation_callbacks: AllocationCallbacks, + ) callconv(.C) void, + }; + + pub const Config = extern struct { + format: Format, + channels: u32, + sample_rate: u32, + channel_map: *Channel, + channel_mix_mode: ChannelMixMode, + dither_mode: DitherMode, + resampling: Resampler.Config, + allocation_callbacks: AllocationCallbacks, + encoding_format: EncodingFormat, + seek_point_count: u32, + custom_backend_vtable: ?*?*VTable, + custom_backend_count: u32, + custom_backend_user_data: *anyopaque, + }; + + pub const BackendConfig = extern struct { + preferred_format: Format, + seek_point_count: u32, + }; +}; + //-------------------------------------------------------------------------------------------------- // // Resampler (Incomplete, but since many of the function requires the type, especially the config, From f5d18809fd2cc1b3ab4c75e8abcd98006e1c9d29 Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Wed, 27 Aug 2025 11:22:29 +0800 Subject: [PATCH 04/13] - Completed the C part of the zaudio The bridge has been finished for Decoder, but since the zaudio doesn't handle the wchar_t type from the original C implementation, I am not going to port any functions that involve the type unless there are a better one size fits all solutions for all other functions requiring wchar_t as well. --- src/zaudio.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/zaudio.c b/src/zaudio.c index 094cb35..ff18bc4 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -724,6 +724,71 @@ void ma_decoder_config_init( *out_config = ma_decoder_config_init(outputFormat, outputChannels, outputSampleRate); } -// There are more variants in decoder than all other types, +// There are more variants in decoder than all other types, but I will only handle the non-w type +// since the other type with the similar file access method don't include such type as well +// unless there is a good solution for wchar_t. +ma_result zaudioDecoderCreate( + ma_decoder_read_proc on_read, + ma_decoder_seek_proc on_seek, + void* user_data, + const ma_decoder_config* config, + ma_decoder** out_handle +){ + assert(user_data!= NULL && config != NULL && out_handle != NULL); + *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); + ma_result res = ma_decoder_init(on_read, on_seek, user_data, config, out_handle); + if (res != MA_SUCCESS){ + s_mem.onFree(*out_handle, s_mem.pUserData); + *out_handle = NULL; + } + return res; +} + +ma_result zaudioDecoderCreateFromMemory( + const void* data, + size_t data_size, + const ma_decoder_config* config, + ma_decoder** out_handle +){ + assert(data != NULL && config != NULL && decoder != NULL); + *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); + ma_result res = ma_decoder_init_memory(data, data_size, config, out_handle); + if (res != MA_SUCCESS){ + s_mem.onFree(*out_handle, s_mem.pUserData); + *out_handle = NULL; + } + return res; +} + +ma_result zaudioDecoderCreateVfs( + ma_vfs* vfs, + const char* file_path, + const ma_decoder_config* config, + ma_decoder** out_handle +){ + assert(vfs != NULL && file_path != NULL && config != NULL && out_handle != NULL); + *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); + ma_result res = ma_decoder_init_vfs(vfs, file_path, config, out_handle); + if (res != MA_SUCCESS){ + s_mem.onFree(*out_handle, s_mem.pUserData); + *out_handle = NULL; + } + return res; +} + +ma_result zaudioDecoderFile( + const char* file_path, + const ma_decoder_config* config, + ma_decoder** out_handle +){ + assert(file_path != NULL && config != NULL, out_handle != NULL); + *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); + ma_result res = ma_decoder_init_file(file_path, config, out_handle); + if (res != MA_SUCCESS){ + s_mem.onFree(*out_handle, s_mem.pUserData); + *out_handle = NULL; + } + return res; +} //-------------------------------------------------------------------------------------------------- From 6b1ef442baefc0aa800f7e22935da781e4158fde Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Wed, 27 Aug 2025 17:47:41 +0800 Subject: [PATCH 05/13] - All Required Coding is Done All of the coding related to the decoder is done, but clearly, there will be many errors in the code especially the pointer which I have missed out a bunch of optionals. Thus, I will do a series of testing to ensure everything works fine, and after all, we will finally able to load ourselves some samples at the low level using zaudio. --- src/zaudio.c | 16 ++++++++--- src/zaudio.zig | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/zaudio.c b/src/zaudio.c index ff18bc4..8ec88d2 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -713,8 +713,8 @@ void zaudioDataConverterDestroy( } //-------------------------------------------------------------------------------------------------- -// ma_decoder -void ma_decoder_config_init( +// ma_decoder zaudioDataConverterConfigInit +void zaudioDecoderConfigInit( ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate, @@ -760,7 +760,7 @@ ma_result zaudioDecoderCreateFromMemory( return res; } -ma_result zaudioDecoderCreateVfs( +ma_result zaudioDecoderCreateFromVfs( ma_vfs* vfs, const char* file_path, const ma_decoder_config* config, @@ -776,7 +776,7 @@ ma_result zaudioDecoderCreateVfs( return res; } -ma_result zaudioDecoderFile( +ma_result zaudioDecoderCreateFromFile( const char* file_path, const ma_decoder_config* config, ma_decoder** out_handle @@ -791,4 +791,12 @@ ma_result zaudioDecoderFile( return res; } +ma_result zaudioDecoderDestroy( + ma_decoder* handle +){ + assert(handle != NULL); + ma_data_converter_uninit(handle, &s_mem); + s_mem.onFree(handle, s_mem.pUserData); +} + //-------------------------------------------------------------------------------------------------- diff --git a/src/zaudio.zig b/src/zaudio.zig index 1781e08..d4178aa 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -872,9 +872,77 @@ pub const DataConverter = opaque { // //-------------------------------------------------------------------------------------------------- pub const Decoder = opaque { + pub const destroy = zaudioDecoderDestroy; + extern fn zaudioDecoderDestroy(handle: *Decoder) void; // here are the init functions, but after observed other examples // I will skip the _w variant until there is a solution to handle wchar_t + pub fn create(decoder_on_read: decoderReadProc, decoder_on_seek: decoderSeekProc, user_data: *anyopaque, config: Config) Error!*Decoder { + var handle: ?*Decoder = null; + try maybeError(zaudioDecoderCreate(decoder_on_read, decoder_on_seek, user_data, &config, &handle)); + return handle.?; + } + extern fn zaudioDecoderCreate(on_read: decoderReadProc, on_seek: decoderSeekProc, user_data: *anyopaque, config: *const Config, out_handle: *?*?Decoder) Result; + + pub fn createFromMemory(data: ?*const anyopaque, data_size: usize, config: Config) Error!*Decoder { + var handle: ?*Decoder = null; + try maybeError(zaudioDecoderCreateFromMemory(data, data_size, &config, &handle)); + return handle.?; + } + extern fn zaudioDecoderCreateFromMemory(data: ?*const anyopaque, data_size: usize, config: *const Config, out_handle: ?*?*Decoder) Result; + + pub fn createFromVfs(vfs: *Vfs, file_path: []const u8, config: Config) Error!*Decoder { + var handle: ?*Decoder = null; + try maybeError(zaudioDecoderCreateFromVfs(vfs, file_path, &config, &handle)); + return handle.?; + } + extern fn zaudioDecoderCreateFromVfs(vfs: *Vfs, file_path: []const u8, config: *const Config, out_handle: ?*?*Decoder) Result; + + pub fn createFromFile(file_path: []const u8, config: Config) Error!*Decoder { + var handle: ?*Decoder = null; + try maybeError(zaudioDecoderCreateFromFile(file_path, &config, &handle)); + return handle.?; + } + extern fn zaudioDecoderCreateFromFile(file_path: []const u8, config: *const Config, out_handle: ?*?*Decoder) Result; + + // The remaing related functions for manipulate the samples: + pub fn readPCMFrame(decoder: *Decoder, frames_count: u64, frames_read: *u64) Error!*anyopaque { + var frame_out: anyopaque = undefined; + try maybeError(ma_decoder_read_pcm_frame(decoder, &frame_out, frames_count, frames_read)); + return frame_out; + } + extern fn ma_decoder_read_pcm_frame(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: ?*u64) Result; + + pub fn seekToPCMFrame(decoder: *Decoder, frame_index: u64) Error!void { + try maybeError(ma_decoder_seek_to_pcm_frame(decoder, frame_index)); + } + extern fn ma_decoder_seek_to_pcm_frame(decoder: *Decoder, frame_index: u64) Result; + + pub fn getDataFormat(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: [*]Channel, channel_map_cap: usize) Error!void { + try maybeError(ma_decoder_get_data_format(decoder, format, channels, sample_rate, channel_map, channel_map_cap)); + } + extern fn ma_decoder_get_data_format(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: [*]Channel, channel_map_cap: usize) Result; + + pub fn getCursorInPCMFrames(decoder: *Decoder) Error!u64 { + var cursor: *u64 = undefined; + try maybeError(ma_decoder_get_cursor_in_pcm_frames(decoder, &cursor)); + return cursor.*; + } + extern fn ma_decoder_get_cursor_in_pcm_frames(decoder: *Decoder, cursor: *u64) Result; + + pub fn getLengthInPCMFrames(decoder: *Decoder) Error!u64 { + var length: *u64 = undefined; + try maybeError(ma_decoder_get_length_in_pcm_frames(decoder, &length)); + return length.*; + } + extern fn ma_decoder_get_length_in_pcm_frames(decoder: *Decoder, length: *u64) Result; + + pub fn getAvailableFrames(decoder: *Decoder) Error!u64 { + var available_frames: *u64 = undefined; + try maybeError(ma_decoder_get_available_frames(decoder, &available_frames)); + return available_frames.*; + } + extern fn ma_decoder_get_available_frames(decoder: *Decoder, available_frames: *u64) Result; pub const VTable = extern struct { onInit: ?*const fn ( @@ -934,6 +1002,10 @@ pub const Decoder = opaque { }; }; +pub const decoderReadProc = fn (decoder: *Decoder, buffer_out: *anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.C) Result; +pub const decoderSeekProc = fn (decoder: *Decoder, byte_offset: i64, origin: Vfs.SeekOrigin) Result; +pub const decoderTellProc = fn (decoder: *Decoder, cursor: *i64) Result; + //-------------------------------------------------------------------------------------------------- // // Resampler (Incomplete, but since many of the function requires the type, especially the config, From 1a2ea91e06a14eac7c70881331585ff88f020b66 Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Thu, 28 Aug 2025 10:50:48 +0800 Subject: [PATCH 06/13] - Cleared The Compile Time Errors, but... The library is finally successfully compiled, but clearly this is not enough because we need to test if the library really works in practice; thus, we are going to do some test to ensure the correct of the decoders. --- src/zaudio.c | 14 ++++++------ src/zaudio.zig | 58 ++++++++++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/zaudio.c b/src/zaudio.c index 8ec88d2..e4f9e37 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -736,7 +736,7 @@ ma_result zaudioDecoderCreate( ){ assert(user_data!= NULL && config != NULL && out_handle != NULL); *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); - ma_result res = ma_decoder_init(on_read, on_seek, user_data, config, out_handle); + ma_result res = ma_decoder_init(on_read, on_seek, user_data, config, *out_handle); if (res != MA_SUCCESS){ s_mem.onFree(*out_handle, s_mem.pUserData); *out_handle = NULL; @@ -750,9 +750,9 @@ ma_result zaudioDecoderCreateFromMemory( const ma_decoder_config* config, ma_decoder** out_handle ){ - assert(data != NULL && config != NULL && decoder != NULL); + assert(data != NULL && config != NULL && out_handle != NULL); *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); - ma_result res = ma_decoder_init_memory(data, data_size, config, out_handle); + ma_result res = ma_decoder_init_memory(data, data_size, config, *out_handle); if (res != MA_SUCCESS){ s_mem.onFree(*out_handle, s_mem.pUserData); *out_handle = NULL; @@ -768,7 +768,7 @@ ma_result zaudioDecoderCreateFromVfs( ){ assert(vfs != NULL && file_path != NULL && config != NULL && out_handle != NULL); *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); - ma_result res = ma_decoder_init_vfs(vfs, file_path, config, out_handle); + ma_result res = ma_decoder_init_vfs(vfs, file_path, config, *out_handle); if (res != MA_SUCCESS){ s_mem.onFree(*out_handle, s_mem.pUserData); *out_handle = NULL; @@ -781,9 +781,9 @@ ma_result zaudioDecoderCreateFromFile( const ma_decoder_config* config, ma_decoder** out_handle ){ - assert(file_path != NULL && config != NULL, out_handle != NULL); + assert(file_path != NULL && config != NULL && out_handle != NULL); *out_handle = s_mem.onMalloc(sizeof(ma_decoder), s_mem.pUserData); - ma_result res = ma_decoder_init_file(file_path, config, out_handle); + ma_result res = ma_decoder_init_file(file_path, config, *out_handle); if (res != MA_SUCCESS){ s_mem.onFree(*out_handle, s_mem.pUserData); *out_handle = NULL; @@ -795,7 +795,7 @@ ma_result zaudioDecoderDestroy( ma_decoder* handle ){ assert(handle != NULL); - ma_data_converter_uninit(handle, &s_mem); + ma_decoder_uninit(handle); s_mem.onFree(handle, s_mem.pUserData); } diff --git a/src/zaudio.zig b/src/zaudio.zig index d4178aa..dd38209 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -465,9 +465,9 @@ pub const Vfs = extern struct { // these functions were originally located under vfs, but they seems to have no correlation to the type, // while decoder require such type in order to make it work, so I will temporary locate these functions in here: -pub const readProc = fn (user_data: ?*anyopaque, buffer_out: ?*anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.C) Result; -pub const seekProc = fn (user_data: ?*anyopaque, offset: i64, origin: Vfs.SeekOrigin) callconv(.C) Result; -pub const tellProc = fn (user_data: ?*anyopaque, cursor: ?*i64) callconv(.C) Result; +pub const readProc = *const fn (user_data: ?*anyopaque, buffer_out: ?*anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.C) Result; +pub const seekProc = *const fn (user_data: ?*anyopaque, offset: i64, origin: Vfs.SeekOrigin) callconv(.C) Result; +pub const tellProc = *const fn (user_data: ?*anyopaque, cursor: ?*i64) callconv(.C) Result; pub const Context = opaque { // TODO: Add methods. @@ -764,14 +764,14 @@ pub const AudioBuffer = opaque { //-------------------------------------------------------------------------------------------------- pub const DataConverter = opaque { pub const destroy = zaudioDataConverterDestroy; - extern fn zaudioDataConverterDestroy(handle: DataConverter) void; + extern fn zaudioDataConverterDestroy(handle: *DataConverter) void; pub fn create(config: Config) Error!*DataConverter { var handle: ?*DataConverter = null; try maybeError(zaudioDataConverterCreate(&config, &handle)); return handle.?; } - extern fn zaudioDataConverterCreate(config: Config, handle: ?*?*DataConverter) Result; + extern fn zaudioDataConverterCreate(config: *const Config, handle: ?*?*DataConverter) Result; pub fn processPcmFrames( converter: *DataConverter, @@ -791,7 +791,7 @@ pub const DataConverter = opaque { extern fn ma_data_converter_process_pcm_frames(converter: *DataConverter, frames_in: *anyopaque, frame_count_in: *u64, frame_out: *anyopaque, frame_count_out: *u64) Result; pub fn setRate(converter: *DataConverter, sample_rate_in: u32, sample_rate_out: u32) Error!void { - try maybeError(setRate(converter, sample_rate_in, sample_rate_out)); + try maybeError(ma_data_converter_set_rate(converter, sample_rate_in, sample_rate_out)); } extern fn ma_data_converter_set_rate(converter: *DataConverter, sample_rate_in: u32, sample_rate_out: u32) Result; @@ -801,12 +801,12 @@ pub const DataConverter = opaque { extern fn ma_data_converter_set_rate_ratio(converter: *DataConverter, ratioInOut: f32) Result; pub fn getInputLatency(converter: *DataConverter) u64 { - ma_data_converter_get_input_latency(converter); + return ma_data_converter_get_input_latency(converter); } extern fn ma_data_converter_get_input_latency(converter: *DataConverter) u64; pub fn getOutPutLatency(converter: *DataConverter) u64 { - ma_data_converter_get_output_latency(converter); + return ma_data_converter_get_output_latency(converter); } extern fn ma_data_converter_get_output_latency(converter: *DataConverter) u64; @@ -891,27 +891,25 @@ pub const Decoder = opaque { } extern fn zaudioDecoderCreateFromMemory(data: ?*const anyopaque, data_size: usize, config: *const Config, out_handle: ?*?*Decoder) Result; - pub fn createFromVfs(vfs: *Vfs, file_path: []const u8, config: Config) Error!*Decoder { + pub fn createFromVfs(vfs: *Vfs, file_path: [:0]const u8, config: Config) Error!*Decoder { var handle: ?*Decoder = null; - try maybeError(zaudioDecoderCreateFromVfs(vfs, file_path, &config, &handle)); + try maybeError(zaudioDecoderCreateFromVfs(vfs, file_path.ptr, &config, &handle)); return handle.?; } - extern fn zaudioDecoderCreateFromVfs(vfs: *Vfs, file_path: []const u8, config: *const Config, out_handle: ?*?*Decoder) Result; + extern fn zaudioDecoderCreateFromVfs(vfs: *Vfs, file_path: [*:0]const u8, config: *const Config, out_handle: ?*?*Decoder) Result; - pub fn createFromFile(file_path: []const u8, config: Config) Error!*Decoder { + pub fn createFromFile(file_path: [:0]const u8, config: Config) Error!*Decoder { var handle: ?*Decoder = null; - try maybeError(zaudioDecoderCreateFromFile(file_path, &config, &handle)); + try maybeError(zaudioDecoderCreateFromFile(file_path.ptr, &config, &handle)); return handle.?; } - extern fn zaudioDecoderCreateFromFile(file_path: []const u8, config: *const Config, out_handle: ?*?*Decoder) Result; + extern fn zaudioDecoderCreateFromFile(file_path: [*:0]const u8, config: *const Config, out_handle: ?*?*Decoder) Result; // The remaing related functions for manipulate the samples: - pub fn readPCMFrame(decoder: *Decoder, frames_count: u64, frames_read: *u64) Error!*anyopaque { - var frame_out: anyopaque = undefined; - try maybeError(ma_decoder_read_pcm_frame(decoder, &frame_out, frames_count, frames_read)); - return frame_out; + pub fn readPCMFrame(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: *u64) Error!void { + try maybeError(ma_decoder_read_pcm_frames(decoder, frame_out, frames_count, frames_read)); } - extern fn ma_decoder_read_pcm_frame(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: ?*u64) Result; + extern fn ma_decoder_read_pcm_frames(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: ?*u64) Result; pub fn seekToPCMFrame(decoder: *Decoder, frame_index: u64) Error!void { try maybeError(ma_decoder_seek_to_pcm_frame(decoder, frame_index)); @@ -924,23 +922,23 @@ pub const Decoder = opaque { extern fn ma_decoder_get_data_format(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: [*]Channel, channel_map_cap: usize) Result; pub fn getCursorInPCMFrames(decoder: *Decoder) Error!u64 { - var cursor: *u64 = undefined; + var cursor: u64 = undefined; try maybeError(ma_decoder_get_cursor_in_pcm_frames(decoder, &cursor)); - return cursor.*; + return cursor; } extern fn ma_decoder_get_cursor_in_pcm_frames(decoder: *Decoder, cursor: *u64) Result; pub fn getLengthInPCMFrames(decoder: *Decoder) Error!u64 { - var length: *u64 = undefined; + var length: u64 = undefined; try maybeError(ma_decoder_get_length_in_pcm_frames(decoder, &length)); - return length.*; + return length; } extern fn ma_decoder_get_length_in_pcm_frames(decoder: *Decoder, length: *u64) Result; pub fn getAvailableFrames(decoder: *Decoder) Error!u64 { - var available_frames: *u64 = undefined; + var available_frames: u64 = undefined; try maybeError(ma_decoder_get_available_frames(decoder, &available_frames)); - return available_frames.*; + return available_frames; } extern fn ma_decoder_get_available_frames(decoder: *Decoder, available_frames: *u64) Result; @@ -953,15 +951,15 @@ pub const Decoder = opaque { read_seek_tell_user_data: *anyopaque, config: *const BackendConfig, allocation_callbacks: AllocationCallbacks, - backend: *?*?DataSource, + backend: **DataSource, ) callconv(.C) Result, onInitFile: ?*const fn ( user_data: *anyopaque, - file_path: [:0]const u8, + file_path: [*:0]const u8, config: BackendConfig, allocation_callbacks: AllocationCallbacks, - backend: *?*?DataSource, + backend: **DataSource, ) callconv(.C) Result, onInitMemory: ?*const fn ( @@ -970,12 +968,12 @@ pub const Decoder = opaque { data_size: usize, config: BackendConfig, allocation_callbacks: AllocationCallbacks, - backend: *?*?DataSource, + backend: **DataSource, ) callconv(.C) Result, onUninit: ?*const fn ( user_data: *anyopaque, - backend: *?DataSource, + backend: *DataSource, allocation_callbacks: AllocationCallbacks, ) callconv(.C) void, }; From c54c2ec4d3953961f8ffb1450c208d31e71d3f3d Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Thu, 28 Aug 2025 16:41:15 +0800 Subject: [PATCH 07/13] - Successfully created Decoder and its Config, but... There are many more test to do, but I can't do this on the zaudio level where there is no way to place the callback function within the test block. I will open up a separated project to conduct a series of testing such that to ensure the functions working flawlessly and acting as an example code to overcome with the lacking low-level example for zaudio. --- src/zaudio.c | 4 ++++ src/zaudio.zig | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/zaudio.c b/src/zaudio.c index e4f9e37..0158287 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -724,6 +724,10 @@ void zaudioDecoderConfigInit( *out_config = ma_decoder_config_init(outputFormat, outputChannels, outputSampleRate); } +void zaudioDecoderConfigInitDefault(ma_decoder_config* out_config){ + *out_config = ma_decoder_config_init_default(); +} + // There are more variants in decoder than all other types, but I will only handle the non-w type // since the other type with the similar file access method don't include such type as well // unless there is a good solution for wchar_t. diff --git a/src/zaudio.zig b/src/zaudio.zig index dd38209..169ab86 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -906,12 +906,12 @@ pub const Decoder = opaque { extern fn zaudioDecoderCreateFromFile(file_path: [*:0]const u8, config: *const Config, out_handle: ?*?*Decoder) Result; // The remaing related functions for manipulate the samples: - pub fn readPCMFrame(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: *u64) Error!void { + pub fn readPCMFrames(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: ?*u64) Error!void { try maybeError(ma_decoder_read_pcm_frames(decoder, frame_out, frames_count, frames_read)); } extern fn ma_decoder_read_pcm_frames(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: ?*u64) Result; - pub fn seekToPCMFrame(decoder: *Decoder, frame_index: u64) Error!void { + pub fn seekToPCMFrames(decoder: *Decoder, frame_index: u64) Error!void { try maybeError(ma_decoder_seek_to_pcm_frame(decoder, frame_index)); } extern fn ma_decoder_seek_to_pcm_frame(decoder: *Decoder, frame_index: u64) Result; @@ -992,6 +992,20 @@ pub const Decoder = opaque { custom_backend_vtable: ?*?*VTable, custom_backend_count: u32, custom_backend_user_data: *anyopaque, + + pub fn initDefault() Config { + var config: Config = undefined; + zaudioDecoderConfigInitDefault(&config); + return config; + } + extern fn zaudioDecoderConfigInitDefault(out_config: *Config) void; + + pub fn init(output_format: Format, output_channels: u32, output_sample_rate: u32) Config { + var config: Config = undefined; + zaudioDecoderConfigInit(output_format, output_channels, output_sample_rate, &config); + return config; + } + extern fn zaudioDecoderConfigInit(output_format: Format, output_channels: u32, output_sample_rate: u32, out_config: *Config) void; }; pub const BackendConfig = extern struct { From d08ac9b6b971ae07b40e8377fee26e7bca39dcec Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Fri, 29 Aug 2025 15:36:30 +0800 Subject: [PATCH 08/13] - bug fixes This commit addresses the inconsistent convention for channel_map input parameter for the decoder type, and handle a case where some operation requiring to passing the decoder as datasource for other functions. The decoder is now confirmed to fetch and decoder mp3 successfully in a separated project, but more test are required to be conducted, not to mention that the pull request will be done after zig 0.15.1 has properly been merged. --- src/zaudio.zig | 119 ++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/src/zaudio.zig b/src/zaudio.zig index 169ab86..999d8e8 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -17,7 +17,7 @@ pub fn init(allocator: std.mem.Allocator) void { zaudioMemInit(); } -extern fn zaudioMemInit() callconv(.C) void; +extern fn zaudioMemInit() callconv(.c) void; pub fn deinit() void { assert(mem_allocator != null); @@ -406,15 +406,15 @@ pub const MonoExpansionMode = enum(u32) { pub const AllocationCallbacks = extern struct { user_data: ?*anyopaque, - onMalloc: ?*const fn (len: usize, user_data: ?*anyopaque) callconv(.C) ?*anyopaque, + onMalloc: ?*const fn (len: usize, user_data: ?*anyopaque) callconv(.c) ?*anyopaque, onRealloc: ?*const fn ( ptr: ?*anyopaque, len: usize, user_data: ?*anyopaque, - ) callconv(.C) ?*anyopaque, + ) callconv(.c) ?*anyopaque, - onFree: ?*const fn (ptr: ?*anyopaque, user_data: ?*anyopaque) callconv(.C) void, + onFree: ?*const fn (ptr: ?*anyopaque, user_data: ?*anyopaque) callconv(.c) void, }; pub const Bool32 = enum(u32) { @@ -453,21 +453,21 @@ pub const Vfs = extern struct { size_in_bytes: usize, }; - on_open: ?*const fn (self: *Vfs, file_path: [*:0]const u8, mode: OpenMode, handle: *FileHandle) callconv(.C) Result, - on_openw: ?*const fn (self: *Vfs, file_path: [*:0]const u32, mode: OpenMode, handle: *FileHandle) callconv(.C) Result, - on_close: ?*const fn (self: *Vfs, handle: FileHandle) callconv(.C) Result, - on_read: ?*const fn (self: *Vfs, handle: FileHandle, dst: [*]u8, size: usize, bytes_read: *usize) callconv(.C) Result, - on_write: ?*const fn (self: *Vfs, handle: FileHandle, src: [*]const u8, size: usize, bytes_written: *usize) callconv(.C) Result, - on_seek: ?*const fn (self: *Vfs, handle: FileHandle, offset: i64, origin: SeekOrigin) callconv(.C) Result, - on_tell: ?*const fn (self: *Vfs, handle: FileHandle, offset: *i64) callconv(.C) Result, - on_info: ?*const fn (self: *Vfs, handle: FileHandle, info: *FileInfo) callconv(.C) Result, + on_open: ?*const fn (self: *Vfs, file_path: [*:0]const u8, mode: OpenMode, handle: *FileHandle) callconv(.c) Result, + on_openw: ?*const fn (self: *Vfs, file_path: [*:0]const u32, mode: OpenMode, handle: *FileHandle) callconv(.c) Result, + on_close: ?*const fn (self: *Vfs, handle: FileHandle) callconv(.c) Result, + on_read: ?*const fn (self: *Vfs, handle: FileHandle, dst: [*]u8, size: usize, bytes_read: *usize) callconv(.c) Result, + on_write: ?*const fn (self: *Vfs, handle: FileHandle, src: [*]const u8, size: usize, bytes_written: *usize) callconv(.c) Result, + on_seek: ?*const fn (self: *Vfs, handle: FileHandle, offset: i64, origin: SeekOrigin) callconv(.c) Result, + on_tell: ?*const fn (self: *Vfs, handle: FileHandle, offset: *i64) callconv(.c) Result, + on_info: ?*const fn (self: *Vfs, handle: FileHandle, info: *FileInfo) callconv(.c) Result, }; // these functions were originally located under vfs, but they seems to have no correlation to the type, // while decoder require such type in order to make it work, so I will temporary locate these functions in here: -pub const readProc = *const fn (user_data: ?*anyopaque, buffer_out: ?*anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.C) Result; -pub const seekProc = *const fn (user_data: ?*anyopaque, offset: i64, origin: Vfs.SeekOrigin) callconv(.C) Result; -pub const tellProc = *const fn (user_data: ?*anyopaque, cursor: ?*i64) callconv(.C) Result; +pub const readProc = *const fn (user_data: ?*anyopaque, buffer_out: ?*anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.c) Result; +pub const seekProc = *const fn (user_data: ?*anyopaque, offset: i64, origin: Vfs.SeekOrigin) callconv(.c) Result; +pub const tellProc = *const fn (user_data: ?*anyopaque, cursor: ?*i64) callconv(.c) Result; pub const Context = opaque { // TODO: Add methods. @@ -489,7 +489,7 @@ pub const DataSourceBase = extern struct { loop_end_in_frames: u64, p_current: *DataSource, p_next: *DataSource, - onGetNext: ?*const fn (*DataSource) callconv(.C) void, + onGetNext: ?*const fn (*DataSource) callconv(.c) void, is_looping: Bool32, }; @@ -525,9 +525,9 @@ pub const DataSource = opaque { frames_out: ?*anyopaque, frame_count: u64, frames_read: *u64, - ) callconv(.C) Result, + ) callconv(.c) Result, - onSeek: ?*const fn (ds: *DataSource, frame_index: u64) callconv(.C) Result, + onSeek: ?*const fn (ds: *DataSource, frame_index: u64) callconv(.c) Result, onGetDataFormat: ?*const fn ( ds: *DataSource, @@ -536,13 +536,13 @@ pub const DataSource = opaque { sample_rate: ?*u32, channel_map: ?[*]Channel, channel_map_cap: usize, - ) callconv(.C) Result, + ) callconv(.c) Result, - onGetCursor: ?*const fn (ds: *DataSource, cursor: ?*u64) callconv(.C) Result, + onGetCursor: ?*const fn (ds: *DataSource, cursor: ?*u64) callconv(.c) Result, - onGetLength: ?*const fn (ds: *DataSource, length: ?*u64) callconv(.C) Result, + onGetLength: ?*const fn (ds: *DataSource, length: ?*u64) callconv(.c) Result, - onSetLooping: ?*const fn (ds: *DataSource, is_looping: Bool32) callconv(.C) Result, + onSetLooping: ?*const fn (ds: *DataSource, is_looping: Bool32) callconv(.c) Result, flags: Flags, }; @@ -824,15 +824,15 @@ pub const DataConverter = opaque { } extern fn ma_data_converter_get_expected_output_frame_count(converter: *DataConverter, input_frame_count: u64, output_frame_count: *u64) Result; - pub fn getInputChannelMap(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Error!void { - try maybeError(ma_data_converter_get_input_channel_map(converter, channel_map, channel_map_cap)); + pub fn getInputChannelMap(converter: *DataConverter, channel_map: ?[]Channel, channel_map_cap: usize) Error!void { + try maybeError(ma_data_converter_get_input_channel_map(converter, channel_map.ptr, channel_map_cap)); } - extern fn ma_data_converter_get_input_channel_map(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Result; + extern fn ma_data_converter_get_input_channel_map(converter: *DataConverter, channel_map: ?[*]Channel, channel_map_cap: usize) Result; - pub fn getOutputChannelMap(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Error!void { - try maybeError(ma_data_converter_get_output_channel_map(converter, channel_map, channel_map_cap)); + pub fn getOutputChannelMap(converter: *DataConverter, channel_map: ?[]Channel, channel_map_cap: usize) Error!void { + try maybeError(ma_data_converter_get_output_channel_map(converter, channel_map.ptr, channel_map_cap)); } - extern fn ma_data_converter_get_output_channel_map(converter: *DataConverter, channel_map: *Channel, channel_map_cap: usize) Result; + extern fn ma_data_converter_get_output_channel_map(converter: *DataConverter, channel_map: ?[*]Channel, channel_map_cap: usize) Result; pub fn reset(converter: *DataConverter) Error!void { try maybeError(ma_data_converter_reset(converter)); @@ -855,8 +855,8 @@ pub const DataConverter = opaque { channels_out: u32, sample_rate_in: u32, sample_rate_out: u32, - channel_map_in: *Channel, - channel_map_out: *Channel, + channel_map_in: ?[*]Channel, + channel_map_out: ?[*]Channel, dither_mode: DitherMode, channel_mix_mode: ChannelMixMode, calculate_LFE_from_spatial_channels: u32, @@ -872,6 +872,13 @@ pub const DataConverter = opaque { // //-------------------------------------------------------------------------------------------------- pub const Decoder = opaque { + pub fn asDataSource(handle: *const Decoder) *const DataSource { + return @as(*const DataSource, @ptrCast(handle)); + } + pub fn asDataSourceMut(handle: *Decoder) *DataSource { + return @as(*DataSource, @ptrCast(handle)); + } + pub const destroy = zaudioDecoderDestroy; extern fn zaudioDecoderDestroy(handle: *Decoder) void; @@ -916,10 +923,10 @@ pub const Decoder = opaque { } extern fn ma_decoder_seek_to_pcm_frame(decoder: *Decoder, frame_index: u64) Result; - pub fn getDataFormat(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: [*]Channel, channel_map_cap: usize) Error!void { - try maybeError(ma_decoder_get_data_format(decoder, format, channels, sample_rate, channel_map, channel_map_cap)); + pub fn getDataFormat(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: ?[]Channel, channel_map_cap: usize) Error!void { + try maybeError(ma_decoder_get_data_format(decoder, format, channels, sample_rate, channel_map.ptr, channel_map_cap)); } - extern fn ma_decoder_get_data_format(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: [*]Channel, channel_map_cap: usize) Result; + extern fn ma_decoder_get_data_format(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: ?[*]Channel, channel_map_cap: usize) Result; pub fn getCursorInPCMFrames(decoder: *Decoder) Error!u64 { var cursor: u64 = undefined; @@ -952,7 +959,7 @@ pub const Decoder = opaque { config: *const BackendConfig, allocation_callbacks: AllocationCallbacks, backend: **DataSource, - ) callconv(.C) Result, + ) callconv(.c) Result, onInitFile: ?*const fn ( user_data: *anyopaque, @@ -960,7 +967,7 @@ pub const Decoder = opaque { config: BackendConfig, allocation_callbacks: AllocationCallbacks, backend: **DataSource, - ) callconv(.C) Result, + ) callconv(.c) Result, onInitMemory: ?*const fn ( user_data: *anyopaque, @@ -969,20 +976,20 @@ pub const Decoder = opaque { config: BackendConfig, allocation_callbacks: AllocationCallbacks, backend: **DataSource, - ) callconv(.C) Result, + ) callconv(.c) Result, onUninit: ?*const fn ( user_data: *anyopaque, backend: *DataSource, allocation_callbacks: AllocationCallbacks, - ) callconv(.C) void, + ) callconv(.c) void, }; pub const Config = extern struct { format: Format, channels: u32, sample_rate: u32, - channel_map: *Channel, + channel_map: ?[*]Channel, channel_mix_mode: ChannelMixMode, dither_mode: DitherMode, resampling: Resampler.Config, @@ -991,7 +998,7 @@ pub const Decoder = opaque { seek_point_count: u32, custom_backend_vtable: ?*?*VTable, custom_backend_count: u32, - custom_backend_user_data: *anyopaque, + custom_backend_user_data: ?*anyopaque, pub fn initDefault() Config { var config: Config = undefined; @@ -1014,7 +1021,7 @@ pub const Decoder = opaque { }; }; -pub const decoderReadProc = fn (decoder: *Decoder, buffer_out: *anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.C) Result; +pub const decoderReadProc = fn (decoder: *Decoder, buffer_out: *anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.c) Result; pub const decoderSeekProc = fn (decoder: *Decoder, byte_offset: i64, origin: Vfs.SeekOrigin) Result; pub const decoderTellProc = fn (decoder: *Decoder, cursor: *i64) Result; @@ -1066,13 +1073,13 @@ pub const Node = opaque { frame_count_in: ?*u32, frames_out: *[*]f32, frame_count_out: *u32, - ) callconv(.C) void, + ) callconv(.c) void, onGetRequiredInputFrameCount: ?*const fn ( node: *Node, output_frame_count: u32, input_frame_count: *u32, - ) callconv(.C) Result, + ) callconv(.c) Result, input_bus_count: u8, output_bus_count: u8, @@ -2024,13 +2031,13 @@ pub const Device = opaque { output: ?*anyopaque, input: ?*const anyopaque, frame_count: u32, - ) callconv(.C) void; + ) callconv(.c) void; pub const NotificationProc = *const fn ( *const anyopaque, // TODO: Should be `*const ma_device_notification`. - ) callconv(.C) void; + ) callconv(.c) void; - pub const StopProc = *const fn (device: *Device) callconv(.C) void; + pub const StopProc = *const fn (device: *Device) callconv(.c) void; pub const Id = extern union { wasapi: [64]i32, @@ -2097,7 +2104,7 @@ pub const Engine = opaque { user_data: ?*anyopaque, frames_out: [*]f32, frame_count: u64, - ) callconv(.C) void; + ) callconv(.c) void; pub fn create(config: ?Config) Error!*Engine { var handle: ?*Engine = null; @@ -2726,7 +2733,7 @@ pub const Sound = opaque { pub const EndProc = *const fn ( user_data: ?*anyopaque, sound: *Sound, - ) callconv(.C) void; + ) callconv(.c) void; }; //-------------------------------------------------------------------------------------------------- // @@ -3013,15 +3020,15 @@ var mem_allocations: ?std.AutoHashMap(usize, usize) = null; var mem_mutex: std.Thread.Mutex = .{}; const mem_alignment = 16; -extern var zaudioMallocPtr: ?*const fn (size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque; +extern var zaudioMallocPtr: ?*const fn (size: usize, _: ?*anyopaque) callconv(.c) ?*anyopaque; -fn zaudioMalloc(size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque { +fn zaudioMalloc(size: usize, _: ?*anyopaque) callconv(.c) ?*anyopaque { mem_mutex.lock(); defer mem_mutex.unlock(); const mem = mem_allocator.?.alignedAlloc( u8, - mem_alignment, + .fromByteUnits(mem_alignment), size, ) catch @panic("zaudio: out of memory"); @@ -3030,9 +3037,9 @@ fn zaudioMalloc(size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque { return mem.ptr; } -extern var zaudioReallocPtr: ?*const fn (ptr: ?*anyopaque, size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque; +extern var zaudioReallocPtr: ?*const fn (ptr: ?*anyopaque, size: usize, _: ?*anyopaque) callconv(.c) ?*anyopaque; -fn zaudioRealloc(ptr: ?*anyopaque, size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque { +fn zaudioRealloc(ptr: ?*anyopaque, size: usize, _: ?*anyopaque) callconv(.c) ?*anyopaque { mem_mutex.lock(); defer mem_mutex.unlock(); @@ -3054,9 +3061,9 @@ fn zaudioRealloc(ptr: ?*anyopaque, size: usize, _: ?*anyopaque) callconv(.C) ?*a return new_mem.ptr; } -extern var zaudioFreePtr: ?*const fn (maybe_ptr: ?*anyopaque, _: ?*anyopaque) callconv(.C) void; +extern var zaudioFreePtr: ?*const fn (maybe_ptr: ?*anyopaque, _: ?*anyopaque) callconv(.c) void; -fn zaudioFree(maybe_ptr: ?*anyopaque, _: ?*anyopaque) callconv(.C) void { +fn zaudioFree(maybe_ptr: ?*anyopaque, _: ?*anyopaque) callconv(.c) void { if (maybe_ptr) |ptr| { mem_mutex.lock(); defer mem_mutex.unlock(); @@ -3266,7 +3273,7 @@ test "zaudio.audio_buffer" { defer deinit(); var samples = try std.ArrayList(f32).initCapacity(std.testing.allocator, 1000); - defer samples.deinit(); + defer samples.deinit(std.testing.allocator); var prng = std.Random.DefaultPrng.init(0); const rand = prng.random(); @@ -3295,7 +3302,7 @@ test "zaudio.audio_buffer" { sound.setLooping(true); try sound.start(); - std.time.sleep(1e8); + std.Thread.sleep(1e8); } test { From b102c8aa11f3a89e577599cd5b4eb0409102291c Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Mon, 1 Sep 2025 16:54:47 +0800 Subject: [PATCH 09/13] - Updated decoder function involving channel_map The original function that involving channel_map has an inconsistent input type comparing with other existing function. In this version, I have replaced the two parameters channel_map and channel_map_cap with a slice for my decoder type. All of the implemented functions for decoder are tested and have worked properly; thus, the next task will be testing about the data converter and add the remaining function I missed from the previous commits. --- src/zaudio.zig | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/zaudio.zig b/src/zaudio.zig index 999d8e8..8a5142d 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -824,13 +824,24 @@ pub const DataConverter = opaque { } extern fn ma_data_converter_get_expected_output_frame_count(converter: *DataConverter, input_frame_count: u64, output_frame_count: *u64) Result; - pub fn getInputChannelMap(converter: *DataConverter, channel_map: ?[]Channel, channel_map_cap: usize) Error!void { - try maybeError(ma_data_converter_get_input_channel_map(converter, channel_map.ptr, channel_map_cap)); + pub fn getInputChannelMap(converter: *DataConverter, channel_map: ?[]Channel) Error!void { + try maybeError(ma_data_converter_get_input_channel_map( + converter, + if (channel_map) |chm| chm.ptr else null, + if (channel_map) |chm| chm.len else 0, + )); } extern fn ma_data_converter_get_input_channel_map(converter: *DataConverter, channel_map: ?[*]Channel, channel_map_cap: usize) Result; - pub fn getOutputChannelMap(converter: *DataConverter, channel_map: ?[]Channel, channel_map_cap: usize) Error!void { - try maybeError(ma_data_converter_get_output_channel_map(converter, channel_map.ptr, channel_map_cap)); + pub fn getOutputChannelMap( + converter: *DataConverter, + channel_map: ?[]Channel, + ) Error!void { + try maybeError(ma_data_converter_get_output_channel_map( + converter, + if (channel_map) |chm| chm.ptr else null, + if (channel_map) |chm| chm.len else 0, + )); } extern fn ma_data_converter_get_output_channel_map(converter: *DataConverter, channel_map: ?[*]Channel, channel_map_cap: usize) Result; @@ -923,10 +934,23 @@ pub const Decoder = opaque { } extern fn ma_decoder_seek_to_pcm_frame(decoder: *Decoder, frame_index: u64) Result; - pub fn getDataFormat(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: ?[]Channel, channel_map_cap: usize) Error!void { - try maybeError(ma_decoder_get_data_format(decoder, format, channels, sample_rate, channel_map.ptr, channel_map_cap)); + pub fn getDataFormat( + decoder: *const Decoder, + format: ?*Format, + channels: ?*u32, + sample_rate: ?*u32, + channel_map: ?[]Channel, + ) Error!void { + try maybeError(ma_decoder_get_data_format( + decoder, + format, + channels, + sample_rate, + if (channel_map) |chm| chm.ptr else null, + if (channel_map) |chm| chm.len else 0, + )); } - extern fn ma_decoder_get_data_format(decoder: *Decoder, format: *Format, channels: *u32, sample_rate: *u32, channel_map: ?[*]Channel, channel_map_cap: usize) Result; + extern fn ma_decoder_get_data_format(decoder: *const Decoder, format: ?*Format, channels: ?*u32, sample_rate: ?*u32, channel_map: ?[*]Channel, channel_map_cap: usize) Result; pub fn getCursorInPCMFrames(decoder: *Decoder) Error!u64 { var cursor: u64 = undefined; From 8cf3ecabc9e740f0ad10aa67f83c5d93f5d74a27 Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Tue, 2 Sep 2025 16:42:40 +0800 Subject: [PATCH 10/13] - Added Missing Functions For DataConverter After the test, it turns out the config init Functions are missing in the type, so I have added it such that we can have a more convenient way to declare the converter. --- src/zaudio.c | 5 +++++ src/zaudio.zig | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/zaudio.c b/src/zaudio.c index 0158287..2ae9302 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -689,6 +689,11 @@ void zaudioDataConverterConfigInit( *out_config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); } +void zaudioDataConverterConfigInitDefault(ma_data_converter_config* out_config){ + assert(out_config != NULL); + *out_config = ma_data_converter_config_init_default(); +} + // ma_data_converter ma_result zaudioDataConverterCreate( ma_data_converter_config* config, diff --git a/src/zaudio.zig b/src/zaudio.zig index 8a5142d..57b62ea 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -874,6 +874,43 @@ pub const DataConverter = opaque { channel_weighs_io_ptr: [*][*]f32, // [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. allow_dynamic_sample_rate: u32, resampling: Resampler.Config, + + pub fn initDefault() Config { + var config: Config = undefined; + zaudioDataConverterConfigInitDefault(&config); + return config; + } + extern fn zaudioDataConverterConfigInitDefault(out_config: *Config) void; + + pub fn init( + format_in: Format, + format_out: Format, + channels_in: u32, + channels_out: u32, + sample_rate_in: u32, + sample_rate_out: u32, + ) Config { + var config: Config = undefined; + zaudioDataConverterConfigInit( + format_in, + format_out, + channels_in, + channels_out, + sample_rate_in, + sample_rate_out, + &config, + ); + return config; + } + extern fn zaudioDataConverterConfigInit( + format_in: Format, + format_out: Format, + channels_in: u32, + channels_out: u32, + sample_rate_in: u32, + sample_rate_out: u32, + out_config: *Config, + ) void; }; }; From 0d6d2f74ff7a36e0867df85627dbee86a635f1c9 Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Tue, 2 Sep 2025 16:54:20 +0800 Subject: [PATCH 11/13] - Updated README With The New Type --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6a90f7e..95ac123 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ Provided structs: - [x] `HishelfNode // High Shelf Filter` - [x] `DelayNode` - [x] custom nodes +- [x] `Decoder` (missing methods) +- [x] `DataConverter` ## Getting started From 7652850d40076deee24dbcf1c3ed513e2853fafa Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Mon, 8 Sep 2025 09:48:18 +0800 Subject: [PATCH 12/13] - Updated Naming and Type Inconsistency The GitHub review does reflect the inconsistency of my code, with comparing the existing code from the library. Thus, I have made the change according to the copilot review such that to align the coding standard and format of the existing codebase. --- src/zaudio.c | 4 ++-- src/zaudio.zig | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/zaudio.c b/src/zaudio.c index 2ae9302..9422395 100644 --- a/src/zaudio.c +++ b/src/zaudio.c @@ -718,7 +718,7 @@ void zaudioDataConverterDestroy( } //-------------------------------------------------------------------------------------------------- -// ma_decoder zaudioDataConverterConfigInit +// ma_decoder zaudioDecoderConfigInit void zaudioDecoderConfigInit( ma_format outputFormat, ma_uint32 outputChannels, @@ -800,7 +800,7 @@ ma_result zaudioDecoderCreateFromFile( return res; } -ma_result zaudioDecoderDestroy( +void zaudioDecoderDestroy( ma_decoder* handle ){ assert(handle != NULL); diff --git a/src/zaudio.zig b/src/zaudio.zig index 57b62ea..7784733 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -937,7 +937,7 @@ pub const Decoder = opaque { try maybeError(zaudioDecoderCreate(decoder_on_read, decoder_on_seek, user_data, &config, &handle)); return handle.?; } - extern fn zaudioDecoderCreate(on_read: decoderReadProc, on_seek: decoderSeekProc, user_data: *anyopaque, config: *const Config, out_handle: *?*?Decoder) Result; + extern fn zaudioDecoderCreate(on_read: decoderReadProc, on_seek: decoderSeekProc, user_data: *anyopaque, config: *const Config, out_handle: ?*?*Decoder) Result; pub fn createFromMemory(data: ?*const anyopaque, data_size: usize, config: Config) Error!*Decoder { var handle: ?*Decoder = null; @@ -1083,8 +1083,8 @@ pub const Decoder = opaque { }; pub const decoderReadProc = fn (decoder: *Decoder, buffer_out: *anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.c) Result; -pub const decoderSeekProc = fn (decoder: *Decoder, byte_offset: i64, origin: Vfs.SeekOrigin) Result; -pub const decoderTellProc = fn (decoder: *Decoder, cursor: *i64) Result; +pub const decoderSeekProc = fn (decoder: *Decoder, byte_offset: i64, origin: Vfs.SeekOrigin) callconv(.c) Result; +pub const decoderTellProc = fn (decoder: *Decoder, cursor: *i64) callconv(.c) Result; //-------------------------------------------------------------------------------------------------- // From 0fe8f17068b6099b79beb393c3dc8fe23719c528 Mon Sep 17 00:00:00 2001 From: Logickin-Lambda Date: Sun, 14 Sep 2025 10:00:34 +0800 Subject: [PATCH 13/13] - Remove bad comments --- src/zaudio.zig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/zaudio.zig b/src/zaudio.zig index 7784733..f827579 100644 --- a/src/zaudio.zig +++ b/src/zaudio.zig @@ -463,8 +463,6 @@ pub const Vfs = extern struct { on_info: ?*const fn (self: *Vfs, handle: FileHandle, info: *FileInfo) callconv(.c) Result, }; -// these functions were originally located under vfs, but they seems to have no correlation to the type, -// while decoder require such type in order to make it work, so I will temporary locate these functions in here: pub const readProc = *const fn (user_data: ?*anyopaque, buffer_out: ?*anyopaque, bytes_to_read: usize, bytes_read: *usize) callconv(.c) Result; pub const seekProc = *const fn (user_data: ?*anyopaque, offset: i64, origin: Vfs.SeekOrigin) callconv(.c) Result; pub const tellProc = *const fn (user_data: ?*anyopaque, cursor: ?*i64) callconv(.c) Result; @@ -930,8 +928,6 @@ pub const Decoder = opaque { pub const destroy = zaudioDecoderDestroy; extern fn zaudioDecoderDestroy(handle: *Decoder) void; - // here are the init functions, but after observed other examples - // I will skip the _w variant until there is a solution to handle wchar_t pub fn create(decoder_on_read: decoderReadProc, decoder_on_seek: decoderSeekProc, user_data: *anyopaque, config: Config) Error!*Decoder { var handle: ?*Decoder = null; try maybeError(zaudioDecoderCreate(decoder_on_read, decoder_on_seek, user_data, &config, &handle)); @@ -960,7 +956,6 @@ pub const Decoder = opaque { } extern fn zaudioDecoderCreateFromFile(file_path: [*:0]const u8, config: *const Config, out_handle: ?*?*Decoder) Result; - // The remaing related functions for manipulate the samples: pub fn readPCMFrames(decoder: *Decoder, frame_out: *anyopaque, frames_count: u64, frames_read: ?*u64) Error!void { try maybeError(ma_decoder_read_pcm_frames(decoder, frame_out, frames_count, frames_read)); }