• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece!🔗 Click here to enter!

Queuing Sounds

Status
Not open for further replies.
Level 13
Joined
Mar 24, 2013
Messages
1,105
I have a kill streak and multi-kill system.

However, when a player gets a multi-kill (double/triple etc) while being on a killing streak, the sounds associated with each play on top of each other. This causes the sounds to be distorted and bad.

I want to make it so that each sound is saved in a stack and then called one at a time. I'm not sure how I can go about accomplishing this though.

I want to reference the oldest sound requested, have it play, then upon ending, play the next etc. There are a few natives like GetSoundIsPlaying and GetSoundDuration that I think will be useful, but I can't seem to wrap my head around how I can do it.
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
The simple answer is, one cannot do this...

Since sounds are played locally you need the sound system itself to queue the sounds. All script is executed synchronously between clients so cannot manipulate the local client sound system well enough to achieve what you want perfectly. The game makes no guarantee that any sound played will ever be played on any clients.

That said one might be able to hack kind of what you are after. Store the durations of the sound clips so that triggers can use them along with the sound. The idea is that you create a queue system to play the sounds. Each time you want a sound played you feed it into the queue system at the end of queue consisting of the sound object to be played and its duration. The system gets the top of queue sound, plays it and then uses Wait (TriggerSleepAction) for the expected duration of the sound. Once the wait ends it then fetches the next top of the queue and plays the sound. This continues until the queue is empty. Use a boolean to prevent starting multiple sound play back threads. This is set when a sound is playing and cleared when the sound playback thread reaches end of queue and dies. A new sound play back thread is only created when this boolean is false. When advancing the queue one must stop the currently playing sound in case a client has not yet finished playback for some reason or another.

It is critical that TriggerSleepAction is used. Unlike timers and events, TriggerSleepAction uses real time rather than game time. Sound playback is a real time system, and will continue while the client is waiting for players or paused and at the same rate independent of game speed. TriggerSleepAction does all these things which is why it is critical to be used for this one system. Yes the timing is inaccurate and there may be a second or more pause between different sound playback, however it assures mostly that no sound will be interrupted by another sound.
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
Thanks DSG. You spelling out what exactly would need to be done has made me rethink what I want. :p

It's unlikely people are streaking at the exact same time so seems fair to assume the sounds wont be overlapping. My biggest issue with how I formerly was doing it, was both sounds were fired simultaneously

When a player gets a multi kill I call this to attempt to delay that multi kill sound. Below works well, However, I currently think I have fouled something up as when I get a 5 multi kill streak the "sound played" debug msg and paused timer are spammed a few times.

JASS:
    function QueueSoundLoop takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i >= index
   
            set soundDelay[i] = soundDelay[i] - TIMEOUT
   
            call BJDebugMsg(R2S(soundDelay[i]))
   
            if soundDelay[i] <= 0. then
       
                call StartSound(queuedSound[i])
                call BJDebugMsg("sound played")
       
                set index = index - 1
                set soundDelay[i] = soundDelay[index]
                set queuedSound[i] = queuedSound[index]
                set i = i - 1
                if index == 0 then
                    call PauseTimer(t2)
                    call BJDebugMsg("paused timer")
                endif
       
            endif
            set i = i + 1
        endloop
    endfunction

 
    function QueueSound takes sound s, real delay returns nothing
        set queuedSound[index] = s
        set soundDelay[index] = delay/2000 // the durations are in thousands, but I only need around a half second delay
 
        set index = index + 1
        if index == 1 then
           call TimerStart(t2, TIMEOUT, true, function QueueSoundLoop)
        endif
 
    endfunction

    function MultiKill takes integer pId, integer currentStreak returns nothing
// removed chunks of code that shouldn't be involved.    

        set multiKillCount[pId] = multiKillCount[pId] + 1
        loop
            exitwhen i > RAMPAGE // 5 kill streak
        
            if multiKillCount[pId] == multiKillNumber[i] then
                call QueueSound(multiKillSounds[i], soundDuration[currentStreak]) //
            elseif multiKillCount[pId] > multiKillNumber[RAMPAGE] then
                call QueueSound(multiKillSounds[RAMPAGE], soundDuration[RAMPAGE]) // this is where I think a problem might be
     
            endif
        
            set i = i + 1
        endloop

 
    endfunction
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
You need to break out the loop after the rampage condition procs. That said it makes no sense that you even test for it in the loop since ultimately you are testing a constant. As such one could test for it before entering the loop and then return out the function after queuing the sound.

I also explicitly mentioned you that you cannot use timers with sound... Timers work with game time while sounds work with real time. This is why TriggerSleepAction is a necessary evil in this case as its mechanics more closely match those of sound than timers do.
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
I thank you much for information. I do not think I will be able to faithfully execute your instructions. So I'll work with this less than ideal but functioning, "good enough", work around. It is by no means perfect, but from my testing has been adequate.

JASS:
function QueueSoundLoop takes nothing returns nothing
        local integer i = 0
       
        set totalDelay = totalDelay - TIMEOUT
        loop
            exitwhen i >= index
               
            set soundDelay[i] = soundDelay[i] - TIMEOUT
               
            if soundDelay[i] <= 0. then 
                call StartSound(queuedSound[i])
                set index = index - 1
                set soundDelay[i] = soundDelay[index]
                set queuedSound[i] = queuedSound[index]
                set i = i - 1
                if index == 0 then
                    call PauseTimer(t2)
                endif
               
            endif
            set i = i + 1
        endloop
   
    endfunction

       
    function QueueSound takes sound s, real delay returns nothing
        set queuedSound[index] = s
        set totalDelay = totalDelay + delay/1500.
        set soundDelay[index] = totalDelay

        set index = index + 1
        if index == 1 then
            set soundDelay[index - 1] = soundDuration[0]// set queue delay to 0 when there is no queue 
            call TimerStart(t2, TIMEOUT, true, function QueueSoundLoop)
        endif 
 
    endfunction
 
Last edited:
Status
Not open for further replies.
Top