111 lines
3.6 KiB
Lua
111 lines
3.6 KiB
Lua
local task = require "lib.utils.task"
|
||
local ease = require "lib.utils.easing"
|
||
|
||
local EFFECTS_SUPPORTED = love.audio.isEffectsSupported()
|
||
|
||
--- @alias SourceFilter { type: "bandpass"|"highpass"|"lowpass", volume: number, highgain: number, lowgain: number }
|
||
|
||
--- @class Audio
|
||
--- @field musicVolume number
|
||
--- @field soundVolume number
|
||
--- @field looped boolean
|
||
--- @field from love.Source?
|
||
--- @field to love.Source?
|
||
audio = {}
|
||
audio.__index = audio
|
||
|
||
--- здесь мы должны выгружать значения из файлика с сохранением настроек
|
||
local function new(musicVolume, soundVolume)
|
||
return setmetatable({
|
||
musicVolume = musicVolume,
|
||
soundVolume = soundVolume,
|
||
looped = true
|
||
}, audio)
|
||
end
|
||
|
||
function audio:update(dt)
|
||
if self.fader then
|
||
local t = self.fader.value
|
||
if self.from then self.from:setVolume(self.musicVolume * (1 - t)) end
|
||
if self.to then self.to:setVolume(self.musicVolume * t) end
|
||
end
|
||
end
|
||
|
||
--- if from is nil, than we have fade in to;
|
||
--- if to is nil, than we have fade out from
|
||
---
|
||
--- also we should guarantee, that from and to have the same volume
|
||
--- @param from love.Source
|
||
--- @param to love.Source
|
||
--- @param ms number? in milliseconds
|
||
function audio:crossfade(from, to, ms)
|
||
print("[Audio]: Triggered crossfade")
|
||
self:play(to)
|
||
to:setVolume(0)
|
||
self.from = from
|
||
self.to = to
|
||
|
||
-- Using a dummy object to tween a value from 0 to 1
|
||
local fade = { value = 0 }
|
||
|
||
task.tween(fade, { value = 1 }, ms or 1000, ease.easeOutCubic)(function()
|
||
self.from:setVolume(0)
|
||
self.to:setVolume(self.musicVolume)
|
||
self.from:stop()
|
||
print("[Audio]: Crossfade done")
|
||
end)
|
||
|
||
-- We need a custom update loop for the volume during the tween
|
||
-- Since task.tween updates properties, we can use a "monitor" task or just rely on `update` if we expose the task?
|
||
-- Actually, task.tween updates `fade.value` every frame.
|
||
-- We need to apply this value to the audio sources every frame.
|
||
-- BUT task.tween doesn't provide an "onUpdate" callback easily.
|
||
-- Workaround: create a task that runs every frame? Or modify task.tween?
|
||
|
||
-- Let's check task.lua... it updates properties on the target object.
|
||
-- So `fade.value` will change. We need to apply it.
|
||
-- We can hook into `update` or use a metatable on `fade`?
|
||
|
||
-- Let's use a "reactive" table with metatable setters? Overkill.
|
||
-- Simplest way: Add `onUpdate` to task.tween or just use `audio:update` to poll `fade.value`?
|
||
-- But `fade` is local.
|
||
|
||
-- Better approach: Tween `self` properties? No, volume is on Source userdate.
|
||
-- Let's add a `fader` object to `self` that we tween, and in `update` we apply it.
|
||
|
||
self.fader = { value = 0 }
|
||
task.tween(self.fader, { value = 1 }, ms or 1000, ease.easeOutCubic)(function()
|
||
self.fader = nil
|
||
end)
|
||
end
|
||
|
||
--- @param source love.Source
|
||
--- @param settings SourceFilter?
|
||
--- @param effectName string?
|
||
function audio:play(source, settings, effectName)
|
||
if source:getType() == "stream" then
|
||
source:setLooping(self.looped)
|
||
source:setVolume(self.musicVolume)
|
||
source:play()
|
||
else
|
||
source:setVolume(self.soundVolume)
|
||
source:play()
|
||
end
|
||
if settings and EFFECTS_SUPPORTED then
|
||
source.setFilter(source, settings)
|
||
end
|
||
if effectName and EFFECTS_SUPPORTED then
|
||
source:setEffect(effectName, true)
|
||
end
|
||
end
|
||
|
||
function audio:setMusicVolume(volume)
|
||
self.musicVolume = volume
|
||
end
|
||
|
||
function audio:setSoundVolume(volume)
|
||
self.soundVolume = volume
|
||
end
|
||
|
||
return { new = new }
|