Files
prosperon/sound.cm
2025-12-19 16:42:40 -06:00

87 lines
1.9 KiB
Plaintext

/*
* sound.cm - Audio playback system for Prosperon
*
* Uses the soundwave package for audio decoding and mixing,
* and routes output to SDL audio.
*
* USAGE:
* var audio = use('sound')
* var voice = audio.play("mysound.mp3")
* voice.loop = true
* voice.vol = 0.5
* voice.stopped = true // stop it
*/
var io = use('cellfs')
var res = use('resources')
var soundwave = use('soundwave/soundwave')
var sdl_audio = use('sdl3/audio')
var audio = {}
// Output format constants
var OUTPUT_RATE = 44100
var OUTPUT_CHANNELS = 2
var FRAMES_PER_CHUNK = 1024
var BYTES_PER_SAMPLE = 4 // f32
var CHUNK_BYTES = FRAMES_PER_CHUNK * OUTPUT_CHANNELS * BYTES_PER_SAMPLE
// Create the audio player instance
var player = soundwave.create({
sample_rate: OUTPUT_RATE,
channels: OUTPUT_CHANNELS,
frames_per_chunk: FRAMES_PER_CHUNK
})
// Load and cache PCM data from a file path
audio.pcm = function pcm(file) {
file = res.find_sound(file)
if (!file) return null
// Check player's cache first
if (player.pcm_cache[file]) return player.pcm_cache[file]
var buf = io.slurp(file)
if (!buf) return null
return player.decode(buf, file)
}
// Play a sound file, returns voice object
audio.play = function play(file, opts) {
var pcm_data = audio.pcm(file)
if (!pcm_data) return null
return player.play(pcm_data, opts)
}
// Convenience: play and return stop function
audio.cry = function cry(file) {
var v = audio.play(file)
if (!v) return null
return function() { v.stopped = true }
}
// Get number of active voices
audio.voice_count = function() {
return player.voice_count()
}
// SDL audio stream setup
var feeder = sdl_audio.open_stream("playback")
feeder.resume_device()
// Audio pump - called periodically to fill the audio buffer
function pump() {
while (feeder.queued() < CHUNK_BYTES * 3) {
var mixed = player.pull(FRAMES_PER_CHUNK)
feeder.put(mixed)
}
$delay(pump, 1/240)
}
pump()
return audio