-- libmusic: list and play dfpwm music tracks from a directory. -- -- A factory: `local createMusic = require('/apis/libmusic'); local music = createMusic();` -- -- Non-UI logic lives here so the `music` program stays thin and the listing is -- unit-testable with an injected `fs`. Dependencies are injectable to keep the -- module testable outside the CC runtime (mirrors libccpm/libversion style). local DEFAULT_DIR = '/musics'; local EXTENSION = '.dfpwm'; local CHUNK_SIZE = 16 * 1024; local function createMusic(opts) opts = opts or {}; local fsLib = opts.fs or fs; local peripheralLib = opts.peripheral or peripheral; local osLib = opts.os or os; local ioLib = opts.io or io; local dir = opts.dir or DEFAULT_DIR; local api = {}; -- Returns a sorted list of `{ name = , path = }` for every -- `.dfpwm` file in the music directory; `{}` when the directory is missing. function api.listTracks() local tracks = {}; if not fsLib.isDir(dir) then return tracks; end for _, entry in ipairs(fsLib.list(dir)) do if entry:sub(-#EXTENSION) == EXTENSION then tracks[#tracks + 1] = { name = entry:sub(1, #entry - #EXTENSION), path = dir .. '/' .. entry, }; end end table.sort(tracks, function(a, b) return a.name < b.name; end); return tracks; end -- Plays a dfpwm file through the first speaker peripheral, blocking until the -- whole track has been queued and drained. Errors if no speaker is attached. function api.play(path) local speaker = peripheralLib.find('speaker'); if not speaker then error('no speaker attached', 0); end local dfpwm = opts.dfpwm or require('cc.audio.dfpwm'); local decoder = dfpwm.make_decoder(); for chunk in ioLib.lines(path, CHUNK_SIZE) do local buffer = decoder(chunk); while not speaker.playAudio(buffer) do osLib.pullEvent('speaker_audio_empty'); end end end return api; end return createMusic;