/* * 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