171 lines
5.0 KiB
Zig
171 lines
5.0 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const log = std.log.scoped(.stb_vorbis);
|
|
|
|
const c = @cImport({
|
|
@cDefine("STB_VORBIS_NO_INTEGER_CONVERSION", {});
|
|
@cDefine("STB_VORBIS_NO_STDIO", {});
|
|
@cDefine("STB_VORBIS_HEADER_ONLY", {});
|
|
@cInclude("stb_vorbis.c");
|
|
});
|
|
|
|
const STBVorbis = @This();
|
|
|
|
pub const Error = error {
|
|
NeedMoreData,
|
|
InvalidApiMixing,
|
|
OutOfMemory,
|
|
FeatureNotSupported,
|
|
TooManyChannels,
|
|
FileOpenFailure,
|
|
SeekWithoutLength,
|
|
UnexpectedEof,
|
|
SeekInvalid,
|
|
InvalidSetup,
|
|
InvalidStream,
|
|
MissingCapturePattern,
|
|
InvalidStreamStructureVersion,
|
|
ContinuedPacketFlagInvalid,
|
|
IncorrectStreamSerialNumber,
|
|
InvalidFirstPage,
|
|
BadPacketType,
|
|
CantFindLastPage,
|
|
SeekFailed,
|
|
OggSkeletonNotSupported,
|
|
|
|
Unknown
|
|
};
|
|
|
|
fn errorFromInt(err: c_int) ?Error {
|
|
return switch (err) {
|
|
c.VORBIS__no_error => null,
|
|
c.VORBIS_need_more_data => Error.NeedMoreData,
|
|
c.VORBIS_invalid_api_mixing => Error.InvalidApiMixing,
|
|
c.VORBIS_outofmem => Error.OutOfMemory,
|
|
c.VORBIS_feature_not_supported => Error.FeatureNotSupported,
|
|
c.VORBIS_too_many_channels => Error.TooManyChannels,
|
|
c.VORBIS_file_open_failure => Error.FileOpenFailure,
|
|
c.VORBIS_seek_without_length => Error.SeekWithoutLength,
|
|
c.VORBIS_unexpected_eof => Error.UnexpectedEof,
|
|
c.VORBIS_seek_invalid => Error.SeekInvalid,
|
|
c.VORBIS_invalid_setup => Error.InvalidSetup,
|
|
c.VORBIS_invalid_stream => Error.InvalidStream,
|
|
c.VORBIS_missing_capture_pattern => Error.MissingCapturePattern,
|
|
c.VORBIS_invalid_stream_structure_version => Error.InvalidStreamStructureVersion,
|
|
c.VORBIS_continued_packet_flag_invalid => Error.ContinuedPacketFlagInvalid,
|
|
c.VORBIS_incorrect_stream_serial_number => Error.IncorrectStreamSerialNumber,
|
|
c.VORBIS_invalid_first_page => Error.InvalidFirstPage,
|
|
c.VORBIS_bad_packet_type => Error.BadPacketType,
|
|
c.VORBIS_cant_find_last_page => Error.CantFindLastPage,
|
|
c.VORBIS_seek_failed => Error.SeekFailed,
|
|
c.VORBIS_ogg_skeleton_not_supported => Error.OggSkeletonNotSupported,
|
|
else => Error.Unknown
|
|
};
|
|
}
|
|
|
|
handle: *c.stb_vorbis,
|
|
|
|
pub fn init(data: []const u8, alloc_buffer: []u8) Error!STBVorbis {
|
|
const stb_vorbis_alloc: c.stb_vorbis_alloc = .{
|
|
.alloc_buffer = alloc_buffer.ptr,
|
|
.alloc_buffer_length_in_bytes = @intCast(alloc_buffer.len)
|
|
};
|
|
var error_code: c_int = -1;
|
|
const handle = c.stb_vorbis_open_memory(
|
|
data.ptr,
|
|
@intCast(data.len),
|
|
&error_code,
|
|
&stb_vorbis_alloc
|
|
);
|
|
if (handle == null) {
|
|
return errorFromInt(error_code) orelse Error.Unknown;
|
|
}
|
|
|
|
return STBVorbis{
|
|
.handle = handle.?
|
|
};
|
|
}
|
|
|
|
pub fn getMinimumAllocBufferSize(self: STBVorbis) u32 {
|
|
const info = self.getInfo();
|
|
return info.setup_memory_required + @max(info.setup_temp_memory_required, info.temp_memory_required);
|
|
}
|
|
|
|
fn getLastError(self: STBVorbis) ?Error {
|
|
const error_code = c.stb_vorbis_get_error(self.handle);
|
|
return errorFromInt(error_code);
|
|
}
|
|
|
|
pub fn seek(self: STBVorbis, sample_number: u32) !void {
|
|
const success = c.stb_vorbis_seek(self.handle, sample_number);
|
|
if (success != 1) {
|
|
return self.getLastError() orelse Error.Unknown;
|
|
}
|
|
}
|
|
|
|
pub fn getStreamLengthInSamples(self: STBVorbis) u32 {
|
|
return c.stb_vorbis_stream_length_in_samples(self.handle);
|
|
}
|
|
|
|
pub fn getStreamLengthInSeconds(self: STBVorbis) f32 {
|
|
return c.stb_vorbis_stream_length_in_seconds(self.handle);
|
|
}
|
|
|
|
pub fn getSamples(
|
|
self: STBVorbis,
|
|
channels: []const [*]f32,
|
|
max_samples_per_channel: u32
|
|
) u32 {
|
|
const samples_per_channel = c.stb_vorbis_get_samples_float(
|
|
self.handle,
|
|
@intCast(channels.len),
|
|
@constCast(@ptrCast(channels.ptr)),
|
|
@intCast(max_samples_per_channel)
|
|
);
|
|
return @intCast(samples_per_channel);
|
|
}
|
|
|
|
const Frame = struct {
|
|
channels: []const [*c]const f32,
|
|
samples_per_channel: u32
|
|
};
|
|
|
|
pub fn getFrame(self: STBVorbis) Frame {
|
|
var output: [*c][*c]f32 = null;
|
|
var channels: c_int = undefined;
|
|
const samples_per_channel = c.stb_vorbis_get_frame_float(
|
|
self.handle,
|
|
&channels,
|
|
&output
|
|
);
|
|
return Frame{
|
|
.channels = output[0..@intCast(channels)],
|
|
.samples_per_channel = @intCast(samples_per_channel)
|
|
};
|
|
}
|
|
|
|
pub const Info = struct {
|
|
sample_rate: u32,
|
|
channels: u32,
|
|
|
|
setup_memory_required: u32,
|
|
setup_temp_memory_required: u32,
|
|
temp_memory_required: u32,
|
|
|
|
max_frame_size: u32
|
|
};
|
|
|
|
pub fn getInfo(self: STBVorbis) Info {
|
|
const info = c.stb_vorbis_get_info(self.handle);
|
|
return Info{
|
|
.sample_rate = info.sample_rate,
|
|
.channels = @intCast(info.channels),
|
|
|
|
.setup_memory_required = info.setup_memory_required,
|
|
.setup_temp_memory_required = info.setup_temp_memory_required,
|
|
.temp_memory_required = info.temp_memory_required,
|
|
|
|
.max_frame_size = @intCast(info.max_frame_size),
|
|
};
|
|
}
|