- My apologies, I did some testing this time.
- I was wrong, it really is levelup2.ogg. Damn Blizzard spaghetti code.
- The second code didn't work because apparently PlaySoundFile doesn't actually work exactly like PlaySound does, despite my assumption. It only has file and channel for arguments, no new fourth argument and I could've sworn it had noDupe but it doesn't. Good thing to know now lol.
- The first code didn't work because the UI plays sounds with no duplicates by default and apparently StopSound doesn't release that "lock" in the same frame you try to play the sound again. So, I had to use an OnUpdate script to play the sound a frame later. You won't notice the frame skip. There was also an issue with honoring no duplicates, the sound would start over if the UI or anything else using the sound wanted no duplicates and tried to play it again. While accounting for both of these issues, I went further and allowed the sound to play at 100% on any channel requested, master by default. Other code can even overlap the sound, the volume will go back down once the last duplicate is done. I think I went a bit overboard, but I'm satisfied with how it turned out.
I can now guarantee the following code works, keep your custom sound in the levelup2.ogg file path. If the sound ever changes in the future, just change the number at the very top along with finding the new file path. I doubt that would happen though.
Lua Code:
local readycheck=8960 -- Sound\\Interface\\levelup2.ogg
local pool,f={},CreateFrame('frame') f.handle={} f.last={}
function f.pool(t,i)
if i then pool[i]:SetScript('OnUpdate',nil) pool[i].t=nil return end
for i=1,#pool do
if pool[i] and not pool[i].t then pool[i].t={i=i,t=t} return i end
end
tinsert(pool,CreateFrame('frame')) i=#pool pool[i].t={i=i,t=t} return i
end
f:SetScript('OnEvent',function(_,_,handle)
if f.nodupe==handle then f.nodupe=nil end
if f.handle[handle] then
if f[f.handle[handle]] and f.last[f.handle[handle]]==handle then
SetCVar(f.handle[handle],f[f.handle[handle]])
f[f.handle[handle]]=nil
end
f.handle[handle]=nil
end
end)
f:RegisterEvent('SOUNDKIT_FINISHED')
hooksecurefunc('PlaySound',function(id,channel,nodupe,_,_,_,_,own)
if id==readycheck and not f.nodupe and not own then
local _,handle=PlaySound(64,'Master',false)
if handle then
StopSound(handle) StopSound(handle-1)
channel=(not channel and 'Master') or (strlower(channel)=='sfx' and 'SFX') or strlower(channel):gsub('^%a',strupper)
local cvar='Sound_'..channel..'Volume'
pool[f.pool({id,channel,nodupe,cvar})]:SetScript('OnUpdate',function(self)
local _,handle=PlaySound(self.t.t[1],self.t.t[2],self.t.t[3],true,nil,nil,nil,true)
if handle then
if self.t.t[3]~=false then f.nodupe=handle end
f.last[self.t.t[4]]=handle
f.handle[handle]=self.t.t[4]
if not f[self.t.t[4]] then
f[self.t.t[4]]=GetCVar(self.t.t[4])
if f[self.t.t[4]] then SetCVar(self.t.t[4],1) end
end
end
f.pool('',self.t.i)
end)
end
end
end)
Some tests you can run:
Code:
-- exactly what the UI does, will play on master at 100%, using it again will do nothing while the sound is playing
/run PlaySound(8960)
-- each line will still only play one sound, the last one in each line chooses the channel (which is weird, one would think the first one would go first)
/run PlaySound(8960,'master')PlaySound(8960,'sfx')PlaySound(8960,'dialog')
/run PlaySound(8960,'sfx')PlaySound(8960,'dialog')PlaySound(8960,'master')
/run PlaySound(8960,'dialog')PlaySound(8960,'master')PlaySound(8960,'sfx')
-- plays two overlapping sounds at 100% on master
/run PlaySound(8960,nil,false)PlaySound(8960,nil,false)
-- (WARNING: LOUD) will play five overlapping sounds using all five channels, all at 100%
/run PlaySound(8960,'master',false)PlaySound(8960,'ambience',false)PlaySound(8960,'dialog',false)PlaySound(8960,'music',false)PlaySound(8960,'sfx',false)