182 lines
5.7 KiB
Zig
182 lines
5.7 KiB
Zig
// Implemented via miniaudio
|
|
|
|
const std = @import("std");
|
|
const core = @import("core");
|
|
const assets = @import("assets");
|
|
|
|
const Name = core.Name;
|
|
const AutoHashMapUnmanaged = std.AutoHashMapUnmanaged;
|
|
const tracy = core.tracy;
|
|
|
|
const ma = @import("miniaudio").c;
|
|
|
|
pub fn sound_log(comptime fmt: []const u8, args: anytype) void {
|
|
core.printInner(core.defaultHighlight, "[SOUND ]: " ++ fmt ++ "\n", args);
|
|
}
|
|
|
|
pub fn sound_logs(comptime fmt: []const u8) void {
|
|
core.printInner(core.defaultHighlight, "[SOUND ]: " ++ fmt ++ "\n", .{});
|
|
}
|
|
|
|
pub fn sound_err(comptime fmt: []const u8, args: anytype) void {
|
|
core.printInner(core.errorHighlight, "[SOUND ]: ERROR!! " ++ fmt ++ "\n", args);
|
|
}
|
|
|
|
pub fn sound_errs(comptime fmt: []const u8) void {
|
|
core.printInner(core.errorHighlight, "[SOUND ]: ERROR!!" ++ fmt ++ "\n", .{});
|
|
}
|
|
|
|
pub const SoundLoader = struct {
|
|
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Sound", @This());
|
|
|
|
engine: *SoundEngine,
|
|
|
|
pub fn init(engine: *SoundEngine) @This() {
|
|
return @This(){
|
|
.engine = engine,
|
|
};
|
|
}
|
|
|
|
pub fn discardAll(self: *@This()) void {
|
|
_ = self;
|
|
}
|
|
|
|
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
|
|
allocator.destroy(self);
|
|
}
|
|
|
|
// unfortunately this one is blocking
|
|
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, properties: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
|
|
core.engine_log("loading sound asset {s}", .{properties.?.path});
|
|
var name = assetRef.name;
|
|
self.engine.loadSound(&name, properties.?.path, .{
|
|
.volume = properties.?.soundVolume,
|
|
}) catch {
|
|
core.engine_log("unable to load sound {s}", .{name.utf8()});
|
|
};
|
|
}
|
|
};
|
|
|
|
fn ma_res(value: anytype) !void {
|
|
if (value != ma.MA_SUCCESS) {
|
|
core.engine_err("miniaudio error value: {d}", .{value});
|
|
return error.MA_ERROR;
|
|
}
|
|
}
|
|
// On init, SoundEngine will spawn a
|
|
pub const SoundEngine = struct {
|
|
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "audio.SoundEngine");
|
|
|
|
engine: *ma.ma_engine,
|
|
sounds: AutoHashMapUnmanaged(u32, *ma.ma_sound),
|
|
decoders: AutoHashMapUnmanaged(u32, *ma.ma_decoder),
|
|
allocator: std.mem.Allocator,
|
|
volume: f32 = 1.0,
|
|
|
|
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
|
const self = try allocator.create(@This());
|
|
self.* = @This(){
|
|
.engine = allocator.create(ma.ma_engine) catch unreachable,
|
|
.sounds = .{},
|
|
.decoders = .{},
|
|
.allocator = allocator,
|
|
};
|
|
|
|
_ = ma.ma_engine_init(null, self.engine);
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn shutdown(self: *@This()) void {
|
|
_ = self;
|
|
}
|
|
|
|
pub fn loadSound(
|
|
self: *@This(),
|
|
soundName: *core.Name,
|
|
fileName: []const u8,
|
|
soundParams: struct {
|
|
looping: bool = false,
|
|
volume: f32 = 1.0,
|
|
},
|
|
) !void {
|
|
const sound = try self.allocator.create(ma.ma_sound);
|
|
errdefer self.allocator.destroy(sound);
|
|
|
|
const engineSoundBytes = try core.fs().loadFile(fileName);
|
|
const bytes = engineSoundBytes.bytes;
|
|
|
|
// Create a decoder from the loaded bytes
|
|
const decoder = try self.allocator.create(ma.ma_decoder);
|
|
errdefer self.allocator.destroy(decoder);
|
|
|
|
const res_decoder = ma.ma_decoder_init_memory(bytes.ptr, bytes.len, null, decoder);
|
|
if (res_decoder != ma.MA_SUCCESS) {
|
|
sound_err("failed to initialize decoder for sound: {s}", .{soundName.utf8()});
|
|
return error.MiniAudioError;
|
|
}
|
|
|
|
const res = ma.ma_sound_init_from_data_source(self.engine, decoder, 0, null, sound);
|
|
if (res != ma.MA_SUCCESS) {
|
|
sound_err("tried loading sound: {s} failed", .{soundName.utf8()});
|
|
return error.MiniAudioError;
|
|
}
|
|
|
|
try self.sounds.put(self.allocator, soundName.handle(), sound);
|
|
try self.decoders.put(self.allocator, soundName.handle(), decoder);
|
|
ma.ma_sound_set_volume(sound, soundParams.volume);
|
|
}
|
|
|
|
pub fn playSound(self: *@This(), soundName: *core.Name) !void {
|
|
const maybeSound = self.sounds.get(soundName.handle());
|
|
|
|
if (maybeSound == null)
|
|
return error.NoSoundError;
|
|
|
|
const sound = maybeSound.?;
|
|
|
|
if (ma.ma_sound_start(sound) != ma.MA_SUCCESS)
|
|
sound_err("unable to start sound: {s}", .{soundName.utf8()});
|
|
}
|
|
|
|
pub fn setVolume(self: *@This(), volume: f32) void {
|
|
self.volume = volume;
|
|
_ = ma.ma_engine_set_volume(self.engine, volume);
|
|
}
|
|
|
|
pub fn stopSound(self: *@This(), soundName: *core.Name) void {
|
|
const sound = self.sounds.get(soundName.handle()).?;
|
|
|
|
if (ma.ma_sound_stop(sound) != ma.MA_SUCCESS) {
|
|
//
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
var iter = self.sounds.iterator();
|
|
while (iter.next()) |sound| {
|
|
self.allocator.destroy(sound.value_ptr.*);
|
|
}
|
|
self.sounds.deinit(self.allocator);
|
|
|
|
var decoder_iter = self.decoders.iterator();
|
|
while (decoder_iter.next()) |decoder| {
|
|
_ = ma.ma_decoder_uninit(decoder.value_ptr.*);
|
|
self.allocator.destroy(decoder.value_ptr.*);
|
|
}
|
|
self.decoders.deinit(self.allocator);
|
|
|
|
ma.ma_engine_uninit(self.engine);
|
|
self.allocator.destroy(self.engine);
|
|
self.allocator.destroy(self);
|
|
}
|
|
|
|
pub fn tick(self: *@This(), deltaTime: f64) void {
|
|
var z = tracy.ZoneNC(@src(), "audio engine tick", 0xABBADD);
|
|
defer z.End();
|
|
|
|
_ = self;
|
|
_ = deltaTime;
|
|
}
|
|
};
|