直接放入Zframework文件
This commit is contained in:
350
Zframework/bgm.lua
Normal file
350
Zframework/bgm.lua
Normal file
@@ -0,0 +1,350 @@
|
||||
local audio=love.audio
|
||||
local effectsSupported=audio.isEffectsSupported()
|
||||
|
||||
local nameList={}
|
||||
local srcLib={}-- Stored bgm objects: {name='foo', source=bar, ...}, more info at function _addFile()
|
||||
local lastLoadNames={}
|
||||
local nowPlay={}
|
||||
local lastPlay=NONE-- Directly stored last played bgm name(s)
|
||||
|
||||
local defaultBGM=false
|
||||
local maxLoadedCount=3
|
||||
local volume=1
|
||||
|
||||
local function task_setVolume(obj,ve,time,stop)
|
||||
local vs=obj.vol
|
||||
local t=0
|
||||
while true do
|
||||
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
|
||||
local v=MATH.mix(vs,ve,t)
|
||||
obj.vol=v
|
||||
obj.source:setVolume(v*volume)
|
||||
if t==1 then
|
||||
obj.volChanging=false
|
||||
break
|
||||
end
|
||||
end
|
||||
if stop then
|
||||
obj.source:stop()
|
||||
end
|
||||
obj.volChanging=false
|
||||
return true
|
||||
end
|
||||
local function task_setPitch(obj,pe,time)
|
||||
local ps=obj.pitch
|
||||
local t=0
|
||||
while true do
|
||||
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
|
||||
local p=MATH.mix(ps,pe,t)
|
||||
obj.pitch=p
|
||||
obj.source:setPitch(p)
|
||||
if t==1 then
|
||||
obj.pitchChanging=false
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
local function task_setLowgain(obj,pe,time)
|
||||
local ps=obj.lowgain
|
||||
local t=0
|
||||
while true do
|
||||
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
|
||||
local p=MATH.mix(ps,pe,t)
|
||||
obj.lowgain=p
|
||||
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1}
|
||||
if t==1 then
|
||||
obj.lowgainChanging=false
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
local function task_setHighgain(obj,pe,time)
|
||||
local ps=obj.highgain
|
||||
local t=0
|
||||
while true do
|
||||
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
|
||||
local p=MATH.mix(ps,pe,t)
|
||||
obj.highgain=p
|
||||
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1}
|
||||
if t==1 then
|
||||
obj.highgainChanging=false
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
local function _clearTask(obj,mode)
|
||||
local taskFunc=
|
||||
mode=='volume' and task_setVolume or
|
||||
mode=='pitch' and task_setPitch or
|
||||
mode=='lowgain' and task_setLowgain or
|
||||
mode=='highgain' and task_setHighgain or
|
||||
'any'
|
||||
TASK.removeTask_iterate(function(task)
|
||||
return task.args[1]==obj and (taskFunc=='any' or task.code==taskFunc)
|
||||
end,obj)
|
||||
end
|
||||
|
||||
local function _updateSources()
|
||||
local n=#lastLoadNames
|
||||
while #lastLoadNames>maxLoadedCount and n>0 do
|
||||
local name=lastLoadNames[n]
|
||||
if srcLib[name].source and not srcLib[name].source:isPlaying() then
|
||||
srcLib[name].source=srcLib[name].source:release() and nil
|
||||
_clearTask(srcLib[name],'any')
|
||||
end
|
||||
n=n-1
|
||||
end
|
||||
end
|
||||
local function _addFile(name,path)
|
||||
if not srcLib[name] then
|
||||
table.insert(nameList,name)
|
||||
srcLib[name]={
|
||||
name=name,path=path,source=false,
|
||||
vol=0,volChanging=false,
|
||||
pitch=1,pitchChanging=false,
|
||||
lowgain=1,lowgainChanging=false,
|
||||
highgain=1,highgainChanging=false,
|
||||
}
|
||||
end
|
||||
end
|
||||
local function _tryLoad(name)
|
||||
if srcLib[name] then
|
||||
local obj=srcLib[name]
|
||||
if obj.source then
|
||||
return true
|
||||
elseif love.filesystem.getInfo(obj.path) then
|
||||
obj.source=audio.newSource(obj.path,'stream')
|
||||
obj.source:setLooping(true)
|
||||
table.insert(lastLoadNames,1,name)
|
||||
return true
|
||||
else
|
||||
LOG(STRING.repD("Wrong path for BGM '$1': $2",obj.name,obj.path),5)
|
||||
end
|
||||
elseif name then
|
||||
LOG("No BGM: "..name,5)
|
||||
end
|
||||
end
|
||||
|
||||
local BGM={}
|
||||
|
||||
function BGM.getList() return nameList end
|
||||
function BGM.getCount() return #nameList end
|
||||
|
||||
function BGM.setDefault(bgms)
|
||||
if type(bgms)=='string' then
|
||||
bgms={bgms}
|
||||
elseif type(bgms)=='table' then
|
||||
for i=1,#bgms do assert(type(bgms[i])=='string',"BGM list must be list of strings") end
|
||||
else
|
||||
error("BGM.setDefault(bgms): bgms must be string or table")
|
||||
end
|
||||
defaultBGM=bgms
|
||||
end
|
||||
function BGM.setMaxSources(count)
|
||||
assert(type(count)=='number' and count>0 and count%1==0,"BGM.setMaxSources(count): count must be positive integer")
|
||||
maxLoadedCount=count
|
||||
_updateSources()
|
||||
end
|
||||
function BGM.setVol(vol)
|
||||
assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.setVol(vol): count must be in range 0~1")
|
||||
volume=vol
|
||||
for i=1,#nowPlay do
|
||||
local bgm=nowPlay[i]
|
||||
if not bgm.volChanging then
|
||||
bgm.source:setVolume(bgm.vol*vol)
|
||||
end
|
||||
end
|
||||
end
|
||||
function BGM.init(name,path)
|
||||
if type(name)=='table' then
|
||||
for k,v in next,name do
|
||||
_addFile(k,v)
|
||||
end
|
||||
else
|
||||
_addFile(name,path)
|
||||
end
|
||||
table.sort(nameList)
|
||||
LOG(BGM.getCount().." BGM files added")
|
||||
end
|
||||
|
||||
function BGM.play(bgms,args)
|
||||
if not args then args='' end
|
||||
if not bgms then bgms=defaultBGM end
|
||||
if not bgms then return end
|
||||
|
||||
if type(bgms)=='string' then bgms={bgms} end
|
||||
assert(type(bgms)=='table',"BGM.play(name,args): name must be string or table")
|
||||
|
||||
if
|
||||
TABLE.compare(lastPlay,bgms) and
|
||||
srcLib[lastPlay[1]] and srcLib[lastPlay[1]].source and
|
||||
srcLib[lastPlay[1]].source:isPlaying()
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
BGM.stop()
|
||||
|
||||
if not STRING.sArg(args,'-preLoad') then
|
||||
lastPlay=bgms
|
||||
end
|
||||
|
||||
for i=1,#bgms do
|
||||
local bgm=bgms[i]
|
||||
assert(type(bgm)=='string',"BGM list can only be list of string")
|
||||
if _tryLoad(bgm) and not STRING.sArg(args,'-preLoad') then
|
||||
local obj=srcLib[bgms[i]]
|
||||
obj.vol=0
|
||||
obj.pitch=1
|
||||
obj.lowgain=1
|
||||
obj.highgain=1
|
||||
obj.volChanging=false
|
||||
obj.pitchChanging=false
|
||||
obj.lowgainChanging=false
|
||||
obj.highgainChanging=false
|
||||
|
||||
_clearTask(obj)
|
||||
|
||||
local source=obj.source
|
||||
source:setLooping(not STRING.sArg(args,'-noloop'))
|
||||
source:setPitch(1)
|
||||
source:seek(0)
|
||||
source:setFilter()
|
||||
if STRING.sArg(args,'-sdin') then
|
||||
obj.vol=1
|
||||
source:setVolume(volume)
|
||||
BGM.set(bgm,'volume',1,0)
|
||||
else
|
||||
source:setVolume(0)
|
||||
BGM.set(bgm,'volume',1,.626)
|
||||
end
|
||||
source:play()
|
||||
|
||||
table.insert(nowPlay,obj)
|
||||
return true
|
||||
end
|
||||
end
|
||||
_updateSources()
|
||||
end
|
||||
function BGM.stop(time)
|
||||
if #nowPlay>0 then
|
||||
for i=1,#nowPlay do
|
||||
local obj=nowPlay[i]
|
||||
_clearTask(obj,'volume')
|
||||
if time==0 then
|
||||
obj.source:stop()
|
||||
obj.volChanging=false
|
||||
else
|
||||
TASK.new(task_setVolume,obj,0,time or .626,true)
|
||||
obj.volChanging=true
|
||||
end
|
||||
end
|
||||
TABLE.cut(nowPlay)
|
||||
lastPlay=NONE
|
||||
end
|
||||
end
|
||||
|
||||
---@param mode
|
||||
---| 'volume'
|
||||
---| 'lowgain'
|
||||
---| 'highgain'
|
||||
---| 'volume'
|
||||
---| 'pitch'
|
||||
---| 'seek'
|
||||
function BGM.set(bgms,mode,...)
|
||||
if type(bgms)=='string' then
|
||||
if bgms=='all' then
|
||||
bgms=nowPlay
|
||||
else
|
||||
bgms={srcLib[bgms]}
|
||||
end
|
||||
elseif type(bgms)=='table' then
|
||||
bgms=TABLE.shift(bgms)
|
||||
for i=1,#bgms do
|
||||
assert(type(bgms[i])=='string',"BGM list must be list of strings")
|
||||
bgms[i]=srcLib[bgms[i]]
|
||||
end
|
||||
else
|
||||
error("BGM.play(name,args): name must be string or table")
|
||||
end
|
||||
for i=1,#bgms do
|
||||
local obj=bgms[i]
|
||||
if obj.source then
|
||||
if mode=='volume' then
|
||||
_clearTask(obj,'volume')
|
||||
|
||||
local vol,time=...
|
||||
if not time then time=1 end
|
||||
|
||||
assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.set(...,volume): volume must be in range 0~1")
|
||||
assert(type(time)=='number' and time>=0,"BGM.set(...,time): time must be positive number")
|
||||
|
||||
TASK.new(task_setVolume,obj,vol,time)
|
||||
elseif mode=='pitch' then
|
||||
_clearTask(obj,'pitch')
|
||||
|
||||
local pitch,changeTime=...
|
||||
if not pitch then pitch=1 end
|
||||
if not changeTime then changeTime=1 end
|
||||
|
||||
assert(type(pitch)=='number' and pitch>0 and pitch<=32,"BGM.set(...,pitch): pitch must be in range 0~32")
|
||||
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
|
||||
|
||||
TASK.new(task_setPitch,obj,pitch,changeTime)
|
||||
elseif mode=='seek' then
|
||||
local time=...
|
||||
assert(type(time)=='number',"BGM.set(...,time): time must be number")
|
||||
obj.source:seek(MATH.clamp(time,0,obj.source:getDuration()))
|
||||
elseif mode=='lowgain' then
|
||||
if effectsSupported then
|
||||
_clearTask(obj,'lowgain')
|
||||
local lowgain,changeTime=...
|
||||
if not lowgain then lowgain=1 end
|
||||
if not changeTime then changeTime=1 end
|
||||
|
||||
assert(type(lowgain)=='number' and lowgain>=0 and lowgain<=1,"BGM.set(...,lowgain,highgain): lowgain must be in range 0~1")
|
||||
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
|
||||
|
||||
TASK.new(task_setLowgain,obj,lowgain,changeTime)
|
||||
obj.lowgain=lowgain
|
||||
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1}
|
||||
end
|
||||
elseif mode=='highgain' then
|
||||
if effectsSupported then
|
||||
_clearTask(obj,'highgain')
|
||||
local highgain,changeTime=...
|
||||
if not highgain then highgain=1 end
|
||||
if not changeTime then changeTime=1 end
|
||||
|
||||
assert(type(highgain)=='number' and highgain>=0 and highgain<=1,"BGM.set(...,lowgain,highgain): highgain must be in range 0~1")
|
||||
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
|
||||
|
||||
TASK.new(task_setHighgain,obj,highgain,changeTime)
|
||||
obj.highgain=highgain
|
||||
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1}
|
||||
end
|
||||
else
|
||||
error("BGM.set(...,mode): mode must be 'volume', 'pitch', or 'seek'")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BGM.getPlaying()
|
||||
return TABLE.shift(lastPlay)
|
||||
end
|
||||
function BGM.isPlaying()
|
||||
return #nowPlay>0 and nowPlay[1].source:isPlaying()
|
||||
end
|
||||
function BGM.tell()
|
||||
if nowPlay[1] then
|
||||
return nowPlay[1].source:tell()
|
||||
end
|
||||
end
|
||||
function BGM.getDuration()
|
||||
if nowPlay[1] then
|
||||
return nowPlay[1].source:getDuration()
|
||||
end
|
||||
end
|
||||
|
||||
return BGM
|
||||
Reference in New Issue
Block a user