Renamed 'ljud' to 'miniaudio' to better represent what engine it is.
This commit is contained in:
39
engine/miniaudio/bus.odin
Normal file
39
engine/miniaudio/bus.odin
Normal file
@@ -0,0 +1,39 @@
|
||||
package miniaudio_wrapper
|
||||
|
||||
import "core:log"
|
||||
import "core:strings"
|
||||
|
||||
import ma "vendor:miniaudio"
|
||||
|
||||
Bus :: ma.sound_group
|
||||
|
||||
create_bus :: proc(engine: ^Engine) -> (^Bus, bool) {
|
||||
assert(engine != nil)
|
||||
|
||||
b := new(Bus)
|
||||
assert(b != nil)
|
||||
|
||||
result := ma.sound_group_init(
|
||||
pEngine = &engine.ma_engine,
|
||||
flags = {},
|
||||
pParentGroup = nil, // TODO: SS - Support parent busses.
|
||||
pGroup = b,
|
||||
)
|
||||
if result != .SUCCESS {
|
||||
free(b)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
ma.sound_group_set_spatialization_enabled(b, true) // TODO: SS - Needs to be configurable outside.
|
||||
ma.sound_group_set_min_distance(b, 1.0) // TODO: SS - Needs to be configurable outside.
|
||||
ma.sound_group_set_max_distance(b, 50) // TODO: SS - Needs to be configurable outside.
|
||||
|
||||
return b, true
|
||||
}
|
||||
|
||||
destroy_bus :: proc(engine: ^Engine, bus: ^Bus) {
|
||||
assert(engine != nil)
|
||||
assert(bus != nil)
|
||||
|
||||
ma.sound_group_uninit(bus)
|
||||
}
|
||||
94
engine/miniaudio/engine.odin
Normal file
94
engine/miniaudio/engine.odin
Normal file
@@ -0,0 +1,94 @@
|
||||
package miniaudio_wrapper
|
||||
|
||||
import "core:container/queue"
|
||||
import "core:log"
|
||||
import "core:strings"
|
||||
|
||||
import ma "vendor:miniaudio"
|
||||
|
||||
Engine :: struct {
|
||||
ma_engine: ma.engine,
|
||||
}
|
||||
|
||||
init :: proc(engine: ^Engine) -> bool {
|
||||
assert(engine != nil)
|
||||
log.infof("Initializing Miniaudio Engine")
|
||||
|
||||
engine_config := ma.engine_config_init()
|
||||
engine_config.channels = 0
|
||||
engine_config.sampleRate = 0
|
||||
engine_config.listenerCount = ma.ENGINE_MAX_LISTENERS // HMM: SS - Get this passed in?
|
||||
|
||||
res := ma.engine_init(&engine_config, &engine.ma_engine)
|
||||
if res != .SUCCESS {
|
||||
log.errorf("Failed to initialize miniaudio! Result: %v.", res)
|
||||
return false
|
||||
}
|
||||
|
||||
start_res := ma.engine_start(&engine.ma_engine) // TODO: SS - Move elsewhere?
|
||||
if start_res != .SUCCESS {
|
||||
log.errorf("Failed to start miniaudio! Result: %v.", start_res)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
tick :: proc(engine: ^Engine) {
|
||||
assert(engine != nil)
|
||||
}
|
||||
|
||||
tick_listener :: proc(engine: ^Engine, id: u8, enabled: bool, position, velocity, direction_forward, world_up: [3]f32) {
|
||||
assert(engine != nil)
|
||||
|
||||
// Enabled.
|
||||
ma.engine_listener_set_enabled(
|
||||
&engine.ma_engine,
|
||||
u32(id),
|
||||
b32(enabled),
|
||||
)
|
||||
|
||||
// Position.
|
||||
ma.engine_listener_set_position(
|
||||
&engine.ma_engine,
|
||||
u32(id),
|
||||
expand_values(position)
|
||||
)
|
||||
|
||||
// Velocity.
|
||||
ma.engine_listener_set_velocity(
|
||||
&engine.ma_engine,
|
||||
u32(id),
|
||||
expand_values(velocity)
|
||||
)
|
||||
|
||||
// Direction (forward).
|
||||
ma.engine_listener_set_direction(
|
||||
&engine.ma_engine,
|
||||
u32(id),
|
||||
expand_values(direction_forward)
|
||||
)
|
||||
|
||||
// World (up).
|
||||
ma.engine_listener_set_world_up(
|
||||
&engine.ma_engine,
|
||||
u32(id),
|
||||
expand_values(world_up)
|
||||
)
|
||||
|
||||
// // Cone. // TODO: SS - Support! :)
|
||||
// ma.engine_listener_set_cone(
|
||||
// &engine.ma_engine,
|
||||
// u32(id),
|
||||
// ..
|
||||
// )
|
||||
|
||||
// log.infof("Ticked listener %v. Position: %v, velocity: %v, direction forward: %v, world up: %v", id, position, direction_forward, world_up)
|
||||
}
|
||||
|
||||
shutdown :: proc(engine: ^Engine) {
|
||||
assert(engine != nil)
|
||||
|
||||
log.infof("Shutting down LJUD Engine")
|
||||
ma.engine_uninit(&engine.ma_engine)
|
||||
}
|
||||
157
engine/miniaudio/sound.odin
Normal file
157
engine/miniaudio/sound.odin
Normal file
@@ -0,0 +1,157 @@
|
||||
package miniaudio_wrapper
|
||||
|
||||
import "core:sys/orca"
|
||||
import "base:runtime"
|
||||
import "core:log"
|
||||
import "core:strings"
|
||||
import "core:c"
|
||||
|
||||
import ma "vendor:miniaudio"
|
||||
|
||||
Sound :: struct {
|
||||
// data_source_base: ma.data_source_base,
|
||||
|
||||
decoder: ma.decoder,
|
||||
}
|
||||
|
||||
Sound_Instance :: ma.sound
|
||||
|
||||
@(private) on_read :: proc "c" (pDataSource: ^ma.data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> ma.result {
|
||||
// Read data here. Output in the same format returned by my_data_source_get_data_format().
|
||||
|
||||
context = runtime.default_context()
|
||||
log.infof("%v", #procedure)
|
||||
return .ERROR
|
||||
}
|
||||
|
||||
@(private) on_seek :: proc "c" (pDataSource: ^ma.data_source, frameIndex: u64) -> ma.result {
|
||||
// Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported.
|
||||
|
||||
context = runtime.default_context()
|
||||
log.infof("%v", #procedure)
|
||||
return .ERROR
|
||||
}
|
||||
|
||||
@(private) on_get_data_format :: proc "c" (pDataSource: ^ma.data_source, pFormat: ^ma.format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]ma.channel, channelMapCap: c.size_t) -> ma.result {
|
||||
// Return the format of the data here.
|
||||
|
||||
context = runtime.default_context()
|
||||
log.infof("%v", #procedure)
|
||||
return .ERROR
|
||||
}
|
||||
|
||||
@(private) on_get_cursor :: proc "c" (pDataSource: ^ma.data_source, pCursor: ^u64) -> ma.result {
|
||||
// Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor.
|
||||
|
||||
context = runtime.default_context()
|
||||
log.infof("%v", #procedure)
|
||||
return .ERROR
|
||||
}
|
||||
|
||||
@(private) on_get_length :: proc "c" (pDataSource: ^ma.data_source, pLength: ^u64) -> ma.result {
|
||||
// Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown.
|
||||
|
||||
context = runtime.default_context()
|
||||
log.infof("%v", #procedure)
|
||||
return .ERROR
|
||||
}
|
||||
|
||||
@(private) on_set_looping :: proc "c" (pDataSource: ^ma.data_source, isLooping: b32) -> ma.result {
|
||||
context = runtime.default_context()
|
||||
log.infof("%v", #procedure)
|
||||
return .ERROR
|
||||
}
|
||||
|
||||
load_sound_from_data :: proc(engine: ^Engine, data: []u8, extension: string) -> (^Sound, bool) {
|
||||
assert(engine != nil)
|
||||
|
||||
sound := new(Sound)
|
||||
assert(sound != nil)
|
||||
|
||||
// Configure our decoder
|
||||
decoder_config := ma.decoder_config_init(
|
||||
outputFormat = .f32,
|
||||
outputChannels = 0,
|
||||
outputSampleRate = 0,
|
||||
)
|
||||
|
||||
// NOTE: SS - "When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type is already known. In this case you can use encodingFormat variable in the device config to specify a specific encoding format you want to decode."
|
||||
decoder_config.encodingFormat = .unknown // TODO: SS - Check 'extension'.
|
||||
|
||||
decoder_result := ma.decoder_init_memory(
|
||||
pData = raw_data(data),
|
||||
dataSize = len(data),
|
||||
pConfig = &decoder_config,
|
||||
pDecoder = &sound.decoder,
|
||||
)
|
||||
if decoder_result != .SUCCESS {
|
||||
log.errorf("Failed to initialize decoder! Result: %v.", decoder_result)
|
||||
free(sound)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return sound, true
|
||||
}
|
||||
|
||||
unload_sound :: proc(engine: ^Engine, sound: ^Sound) {
|
||||
assert(engine != nil)
|
||||
assert(sound != nil)
|
||||
// assert(sound.data_source != nil)
|
||||
|
||||
ma.data_source_uninit(sound.decoder.ds.pCurrent)
|
||||
}
|
||||
|
||||
init_sound_instance :: proc(engine: ^Engine, bus: ^Bus, sound: ^Sound, sound_instance: ^Sound_Instance) -> bool {
|
||||
result := ma.sound_init_from_data_source(
|
||||
pEngine = &engine.ma_engine,
|
||||
pDataSource = sound.decoder.ds.pCurrent,
|
||||
flags = {},
|
||||
pGroup = bus,
|
||||
pSound = sound_instance,
|
||||
)
|
||||
if result != .SUCCESS {
|
||||
log.errorf("Failed to create a sound instance! Result: %v.", result)
|
||||
return false
|
||||
}
|
||||
|
||||
ma.sound_stop(sound_instance)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
play_sound_instance :: proc(
|
||||
engine: ^Engine,
|
||||
sound_instance: ^Sound_Instance,
|
||||
spatialized: bool,
|
||||
loop: bool,
|
||||
volume: f32,
|
||||
position: [3]f32,
|
||||
min_distance, max_distance: f32,
|
||||
) -> bool {
|
||||
assert(engine != nil)
|
||||
assert(sound_instance != nil)
|
||||
|
||||
ma.sound_set_spatialization_enabled(sound_instance, b32(spatialized))
|
||||
ma.sound_set_looping(sound_instance, b32(loop))
|
||||
ma.sound_set_volume(sound_instance, volume)
|
||||
ma.sound_set_position(sound_instance, expand_values(position))
|
||||
ma.sound_set_min_distance(sound_instance, min_distance)
|
||||
ma.sound_set_max_distance(sound_instance, max_distance)
|
||||
|
||||
// TODO: SS - So many cool thingss available! Expose them.
|
||||
// 'ma.sound_set_pitch' etc.
|
||||
|
||||
seek_result := ma.sound_seek_to_pcm_frame(sound_instance, 0)
|
||||
if seek_result != .SUCCESS {
|
||||
log.warnf("Failed to play sound instance! Could not seek to start. Result: %v", seek_result)
|
||||
return false
|
||||
}
|
||||
|
||||
start_result := ma.sound_start(sound_instance)
|
||||
if start_result != .SUCCESS {
|
||||
log.warnf("Failed to play sound instance! Result: %v", start_result)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user