Files
Techmino/Zframework/sfx.lua
2023-12-04 20:02:12 +08:00

170 lines
4.5 KiB
Lua

local type,rem=type,table.remove
local floor,rnd=math.floor,math.random
local sfxList={}
local packSetting={}
local Sources={}
local volume=1
local stereo=1
local noteVal={
C=1,c=1,
D=3,d=3,
E=5,e=5,
F=6,f=6,
G=8,g=8,
A=10,a=10,
B=12,b=12,
}
local noteName={'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'}
local function _getTuneHeight(tune)
local octave=tonumber(tune:sub(-1,-1))
if octave then
local tuneHeight=noteVal[tune:sub(1,1)]
if tuneHeight then
tuneHeight=tuneHeight+(octave-1)*12
local s=tune:sub(2,2)
if s=='s' or s=='#' then
tuneHeight=tuneHeight+1
elseif s=='f' or s=='b' then
tuneHeight=tuneHeight-1
end
return tuneHeight
end
end
end
local SFX={}
function SFX.init(list)
assert(type(list)=='table',"Initialize SFX lib with a list of filenames!")
for i=1,#list do table.insert(sfxList,list[i]) end
end
function SFX.load(path)
local c=0
local missing=0
for i=1,#sfxList do
local fullPath=path..sfxList[i]..'.ogg'
if love.filesystem.getInfo(fullPath) then
if Sources[sfxList[i]] then
for j=1,#Sources[sfxList[i]] do
Sources[sfxList[i]][j]:release()
end
end
Sources[sfxList[i]]={love.audio.newSource(fullPath,'static')}
c=c+1
else
LOG("No SFX: "..sfxList[i]..'.ogg',.1)
missing=missing+1
end
end
LOG(c.."/"..#sfxList.." SFX files loaded")
LOG(missing.." SFX files missing")
if missing>0 then
MES.new('info',missing.." SFX files missing")
end
collectgarbage()
end
function SFX.loadSample(pack)
assert(type(pack)=='table',"Usage: SFX.loadsample([table])")
assert(pack.name,"No field: name")
assert(pack.path,"No field: path")
local num=1
while love.filesystem.getInfo(pack.path..'/'..num..'.ogg') do
Sources[pack.name..num]={love.audio.newSource(pack.path..'/'..num..'.ogg','static')}
num=num+1
end
local base=(_getTuneHeight(pack.base) or 37)-1
local top=base+num-1
packSetting[pack.name]={base=base,top=top}
LOG((num-1).." "..pack.name.." samples loaded")
end
function SFX.getCount()
return #sfxList
end
function SFX.setVol(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong volume')
volume=v
end
function SFX.setStereo(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong stereo')
stereo=v
end
function SFX.getNoteName(note)
if note<1 then
return '---'
else
note=note-1
local octave=floor(note/12)+1
return noteName[note%12+1]..octave
end
end
function SFX.playSample(pack,...)-- vol-1, sampSet1, vol-2, sampSet2
if ... then
local arg={...}
local vol
for i=1,#arg do
local a=arg[i]
if type(a)=='number' and a<=1 then
vol=a
else
local base=packSetting[pack].base
local top=packSetting[pack].top
local tune=type(a)=='string' and _getTuneHeight(a) or a-- Absolute tune in number
local playTune=tune+rnd(-2,2)
if playTune<=base then-- Too low notes
playTune=base+1
elseif playTune>top then-- Too high notes
playTune=top
end
SFX.play(pack..playTune-base,vol,nil,tune-playTune)
end
end
end
end
local function _play(name,vol,pos,pitch)
if volume==0 or vol==0 then return end
local S=Sources[name]-- Source list
if not S then return end
local n=1
while S[n]:isPlaying() do
n=n+1
if not S[n] then
S[n]=S[1]:clone()
S[n]:seek(0)
break
end
end
S=S[n]-- AU_SRC
if S:getChannelCount()==1 then
if pos then
pos=MATH.clamp(pos,-1,1)*stereo
S:setPosition(pos,1-pos^2,0)
else
S:setPosition(0,0,0)
end
end
S:setVolume(vol^1.626)
S:setPitch(pitch and 1.0594630943592953^pitch or 1)
S:play()
end
SFX.fplay=_play-- Play sounds without apply module's volume setting
function SFX.play(name,vol,pos,pitch)
_play(name,(vol or 1)*volume,pos,pitch)
end
function SFX.reset()
for _,L in next,Sources do
if type(L)=='table' then
for i=#L,1,-1 do
if not L[i]:isPlaying() then
rem(L,i)
end
end
end
end
end
return SFX