• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Local Sound Stuff

Status
Not open for further replies.
Level 17
Joined
Dec 11, 2014
Messages
2,004
JASS:
            if GetLocalPlayer() == Player(i) then
                set Track[i] = CreateSound(s, false, false, true, 12700, 12700, "")
                // s is a file path String that shows the path correctly
                call StartSound(Track[i])
                call KillSoundWhenDone(Track[i])
            endif

Can I use that? Like we know that playing Music from a local file works, but what about playing Sounds? Can I create a Sound from a Local File, play it locally, and Destroy it?
 
Last edited:
Level 11
Joined
Dec 19, 2012
Messages
411
type sound extends agent

Anything extends agent is unable to create locally, so creating a sound locally would definitely causes desync.

For the other 2 functions, would need to test them first before could be confirmed they wouldn't cause desync. (I seldom touch sound functions though)
 
Just assign it outside the local player block, i.e.:
JASS:
set Track[i] = CreateSound(s, false, false, true, 12700, 12700, "")

Try to do as little as possible within the local player blocks, only use it for playing/starting/showing/hiding things (in most cases) ->
JASS:
if GetLocalPlayer() == Player(i) then
    call StartSound(Track[i])
endif

Destroying them is another story, but I don't know why you would need dynamically allocated sounds to begin with.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Wrong.
I want the sound to be created from a folder in a specific player folder, like Player 1 folder has different sounds than Player 2.

I think this is impossible?
You still don't need it dynamically then:


JASS:
set Track[0] = CreateSound("MyFolder\\MyTrack.mp3", ...)
set DEFAULT = CreateSound("Music\\SomeDefaultWarcraftMusic.mp3", ...)

if GetSoundFileDuration("MyFolder\\MyTrack.mp3") > 0 then
    if GetLocalPlayer() == Player(0) then
        call StartSound(Track[0])
    endif
else
    if GetLocalPlayer() == Player(0) then
        call StartSound(DEFAULT)
    endif
endif
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
You still don't need it dynamically then:


JASS:
set Track[0] = CreateSound("MyFolder\\MyTrack.mp3", ...)
set DEFAULT = CreateSound("Music\\SomeDefaultWarcraftMusic.mp3", ...)

if GetSoundFileDuration("MyFolder\\MyTrack.mp3") > 0 then
    if GetLocalPlayer() == Player(0) then
        call StartSound(Track[0])
    endif
else
    if GetLocalPlayer() == Player(0) then
        call StartSound(DEFAULT)
    endif
endif

Imagine We have 3 players with a single song each (the same filename as well, but different files). I need to play the song that the player owns. Else with yours the system will play the song that player 1 (host) has it (And likely desyncs).
That's why I say the sound has to be created locally.

I have an Idea: What about local strings? Maybe a Local string brings the Player's own song.
 
Level 11
Joined
Dec 19, 2012
Messages
411
Imagine We have 3 players with a single song each (the same filename as well, but different files).
I don't think warcraft 3 have the ability to detect where the music/song file located from a player computer? The location of the music/song file must be constant/fixed as I see.

I need to play the song that the player owns. Else with yours the system will play the song that player 1 (host) has it (And likely desyncs).
That's why I say the sound has to be created locally.
Playing a sound locally wouldn't causes desync afaik.


I have an Idea: What about local strings? Maybe a Local string brings the Player's own song.
I'm not quite sure what you mean by "brings the Player's own song", locally setting the string value?
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
I don't think warcraft 3 have the ability to detect where the music/song file located from a player computer? The location of the music/song file must be constant/fixed as I see.

My system only detects User players.
Playing a sound locally wouldn't causes desync afaik.
It probably will when it doesn't know where to locate the music file once we don't tell it which player's folder it should check.
I'm not quite sure what you mean by "brings the Player's own song", locally setting the string value?
Like player's 1 music file is different from Player 2's. The system is supposed to bring their own songs.

Set the string that has the file path locally.
Then you create the sound with that string.
Be aware that one variable will contain different values then.

Will try.
 
Last edited:
Level 11
Joined
Dec 19, 2012
Messages
411
My system only detects User players.
"computer player" and "player's computer" are different thing. What I mentioned is "player's computer", probably grammar mistake i made mistaken you.

It probably will when it doesn't know where to locate the music file once we don't tell it which player's folder it should check.
That's why Zwiebelchen showed the trigger which check if music exists before playing the music/song locally.

Like player's 1 music file is different from Player 2's. The system is supposed to bring their own songs.
I don't think warcraft 3 have the ability to detect where the music/song file located from a player's computer? The location of the music/song file must be constant/fixed as I see.
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
You guys aren't understanding me.

The system is supposed to play each player it's own songs in the same folder.

For Example, Player 1 has "Nightsong" renamed "Track-01" in it's Music folder.
Player 2 has "The Shattering" renamed "Track-01" in it's Music folder.
Player 3 has another random song renamed "Track-01" in it's Music folder.
When I use this not in a local block, :
JASS:
set snd = CreateSound("Music\\Track-01.mp3", ...)
"snd" Will be "Nightsong", "The Shattering", or what?
So basically the CreateSound should be in a local block to know which player's Music file it should check, like it should be like this to play "Nightsong":
JASS:
if GetLocalPlayer() == Player(0)
set snd = CreateSound("Music\\Track-01.mp3", ...) //Unhopefully Desyncs :(
endif
And alike for other players else it doesn't know which player's folder it should check.
Guessing it will always check for the Host's folder (when not in a local block), once it plays the music, the music should be downloaded for other people to be played, which it doesn't, so basically it will desync.
So if the system plays the Player's music file from it's own folder, it will be fine.

Now: I'm gonna check for some local string solution (as Wietlol and I have suggested earlier), and see whether it will work or not.

I don't think warcraft 3 have the ability to detect where the music/song file located from a player's computer? The location of the music/song file must be constant/fixed as I see.

Warcraft III has the ability.
It is fixed to "Folder//Prefix-0(0 may or may not be there, system checks it)1", "Folder//Prefix-02", and so on.
 
Level 11
Joined
Dec 19, 2012
Messages
411
You guys aren't understanding me.

The system is supposed to play each player it's own songs in the same folder.

For Example, Player 1 has "Nightsong" renamed "Track-01" in it's Music folder.
Player 2 has "The Shattering" renamed "Track-01" in it's Music folder.
Player 3 has another random song renamed "Track-01" in it's Music folder.
When I use this not in a local block, :
JASS:
set snd = CreateSound("Music\\Track-01.mp3", ...)
"snd" Will be "Nightsong", "The Shattering", or what?
So basically the CreateSound should be in a local block to know which player's Music file it should check, like it should be like this to play "Nightsong":
JASS:
if GetLocalPlayer() == Player(0)
set snd = CreateSound("Music\\Track-01.mp3", ...) //Unhopefully Desyncs :(
endif
And alike for other players else it doesn't know which player's folder it should check.
Guessing it will always check for the Host's folder (when not in a local block), once it plays the music, the music should be downloaded for other people to be played, which it doesn't, so basically it will desync.
So if the system plays the Player's music file from it's own folder, it will be fine.

Now: I'm gonna check for some local string solution (as Wietlol and I have suggested earlier), and see whether it will work or not.
So that what you mean. In that case, I'm totally unsure, never thought about it. So its out of my knowledge.

Warcraft III has the ability.
It is fixed to "Folder//Prefix-0(0 may or may not be there, system checks it)1", "Folder//Prefix-02", and so on.
I'm not too understand about it, is the folder location fixed? What i mean would be :
Player 1 - Folder//Music1
Player 2 - AnotherFolder//Music1
etc..
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
You guys aren't understanding me.

The system is supposed to play each player it's own songs in the same folder.

For Example, Player 1 has "Nightsong" renamed "Track-01" in it's Music folder.
Player 2 has "The Shattering" renamed "Track-01" in it's Music folder.
Player 3 has another random song renamed "Track-01" in it's Music folder.
When I use this not in a local block, :
JASS:
set snd = CreateSound("Music\\Track-01.mp3", ...)
"snd" Will be "Nightsong", "The Shattering", or what?
So basically the CreateSound should be in a local block to know which player's Music file it should check, like it should be like this to play "Nightsong":
JASS:
if GetLocalPlayer() == Player(0)
set snd = CreateSound("Music\\Track-01.mp3", ...) //Unhopefully Desyncs :(
endif
And alike for other players else it doesn't know which player's folder it should check.
Guessing it will always check for the Host's folder (when not in a local block), once it plays the music, the music should be downloaded for other people to be played, which it doesn't, so basically it will desync.
So if the system plays the Player's music file from it's own folder, it will be fine.

Now: I'm gonna check for some local string solution (as Wietlol and I have suggested earlier), and see whether it will work or not.



Warcraft III has the ability.
It is fixed to "Folder//Prefix-0(0 may or may not be there, system checks it)1", "Folder//Prefix-02", and so on.

If it's a music, why don't you use PlayMusic? It won't desync when you call it locally.

In anyway, you can't create any kind of agent locally. You can force every player to create the sound no matter do they have the file or not:
JASS:
local sound s1 = CreateSound("Music\\Track-01.mp3", ...
local sound s2 = CreateSound("DefaultMusic.mp3", ...

// Then this code will run locally
if GetSoundFileDuration(s1) > 0 then // if the sound file exists
    call StartSound(s1)
else
    // If you want players who don't have the file to play another music
    call StartSound(s2)
    // If not, just delete the "else" block and s2 variable
endif

"snd" Will be "Nightsong", "The Shattering", or what?
It's depend on what song is inside the corresponding folder in each player's PC. Based on your example:
When player 1 call GetSoundFileDuration("Music\\Track-01") it will return "Nightsong"'s file duration.
When player 2 call GetSoundFileDuration("Music\\Track-01") it will return "The Shattering"'s file duration.
And player 3 is alike.

And alike for other players else it doesn't know which player's folder it should check.
Guessing it will always check for the Host's folder (when not in a local block),
It will check each player's folder. In player 1's machine, it will check player 1's folder, etc.

So if the system plays the Player's music file from it's own folder, it will be fine.
It does, fortunately. ;)
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
You guys aren't understanding me.

Now what makes you think that?

The game will not desync if all players have the same sort of agents.
If you create an agent locally, you have to create an agent of the same type for all other players. Some (almost all for units) values of the agent also have to be the same.

Setting the string (filepath) in local blocks is fine, creating the actual sound in local blocks is fine... but all players require the sound.

I still think Music should be doing a better job though.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
@Arad MNK:
It's you who isn't understanding us here, not the reverse. ;)

It doesn't matter if the sound file contains a different audio file or not. The game doesn't care about how long the sound file is. Playing sound files is not synchronized at all. Only the sound handle itself matters, not that it plays or how long it plays.


For example, take the extension pack I made for Gaias:
You can replace any of the sound files inside with whatever you want, as long as you rename it to be the same as the original file. The game will plays these sounds just fine.

It doesn't even matter how long these files are for the restarting loop, as you can simply put GetSoundIsPlaying(sound) or GetSoundIsLoading(sound) as a condition in there.


Here, I just wrote the system for you:

JASS:
library CustomMusic initializer init

globals
    private constant integer MAX_TRACKS = 99 //defines how many custom tracks the player can have in the folder
    private constant string EMPTY = "Sound\\Units\\Human\\Rifleman\\RiflemanDeath.wav"
    //this is a random default sound file that it used as a placeholder to make sure CreateSound always creates a proper sound handle

    private string array Track[MAX_TRACKS]
    private string array Default[MAX_TRACKS]
    private sound array TrackSnd[MAX_TRACKS]
    private integer currentTrack = 0 //we are working local anyway, so we don't need an array
endglobals

private function SetupTracks takes nothing returns nothing //register the names for the files here
    set Track[0] = "CustomMusic\\Track-01.mp3"
    set Track[1] = "CustomMusic\\Track-02.mp3"
    //... you can also loop and increment the track numbers via string concatenation, but I thought it's better to actually have descriptive names for these files
    set Default[0] = "Music\\SomeDefaultWC3musicInMPQ.mp3"
    set Default[1] = "Music\\SomeDefaultWC3musicInMPQ.mp3"
    //... these are the defaults that play if the user doesn't have any sounds in the folder
endfunction

private function update takes nothing returns nothing
    if not (GetSoundIsPlaying(TrackSnd[currentTrack]) or GetSoundIsLoading(TrackSnd[currentTrack])) then
        //track has finished playing, start the next one
        loop
            set currentTrack = currentTrack + 1
            if currentTrack >= MAX_TRACKS then
                set currentTrack = 0 //start again from the top
            endif
            if Track[currentTrack] != EMPTY or Default[currentTrack] != EMPTY then
                call StartSound(TrackSnd[currentTrack])
                exitwhen true
            endif
        endloop
    endif
endfunction

private function initCallback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer i = 0
    local string s = EMPTY

    call TimerStart(t, 1, true, function update)
    set t = null

    call SetupTracks()

    loop
        exitwhen i >= MAX_TRACKS
        if GetSoundFileDuration(Track[i]) > 0 then
            set s = Track[i]
        elseif GetSoundFileDuration(Default[i]) > 0 then
            set s = Default[i]
        endif
        set TrackSnd[i] = CreateSound(s, false, false, false, 12700, 12700, "")
        set i = i + 1
    endloop

    //start the first track on map initialization
    if Track[currentTrack] != EMPTY or Default[currentTrack] != EMPTY then
        call StartSound(TrackSnd[currentTrack])
    else
        call update()
    endif
endfunction

private function init takes nothing returns nothing
    call TimerStart(CreateTimer(), 0, false, initCallback)
endfunction
 
Last edited:
Level 17
Joined
Dec 11, 2014
Messages
2,004
Now what makes you think that?

The game will not desync if all players have the same sort of agents.
If you create an agent locally, you have to create an agent of the same type for all other players. Some (almost all for units) values of the agent also have to be the same.

Setting the string (filepath) in local blocks is fine, creating the actual sound in local blocks is fine... but all players require the sound.

I still think Music should be doing a better job though.

For Musics I can't use GetSoundIsPlaying(sound) or GetSoundIsLoading(sound), Musics aren't sound variables, as I want to remove Timers so that the timing won't fuck up once the player pauses the game. But I can create a sound system that can set the volume and stuff.
OK. Will try it real soon.

@Arad MNK:
It's you who isn't understanding us here, not the reverse. ;)

It doesn't matter if the sound file contains a different audio file or not. The game doesn't care about how long the sound file is. Playing sound files is not synchronized at all. Only the sound handle itself matters, not that it plays or how long it plays.


For example, take the extension pack I made for Gaias:
You can replace any of the sound files inside with whatever you want, as long as you rename it to be the same as the original file. The game will plays these sounds just fine.

It doesn't even matter how long these files are, as you can simply put GetSoundIsPlaying(sound) or GetSoundIsLoading(sound) as a condition in your restart loop.

Yes, that's why I'm developing it with CreateSound as we don't have GetSoundFileIsPlaying(string) or GetSoundFileIsLoading(string) which seems dumb when thinking of them :p
Soooooo I need to create sounds from a string (CreateSound), to use them with GetSoundIsPlaying(sound) or GetSoundIsLoading(sound) to remove timers.

Will work on the local string version. Posting results soon, if I have enough time.
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
So basically using the CreateSound, we ALWAYS create it locally without GetLocalPlayer, like:
JASS:
set TrackSnd[i] = CreateSound(s, false, false, false, 12700, 12700, "") //not in GetLocalPlayer()
If we use the array that has nothing to do with the player (use track[0] for player blue for example) we'll desync. Legit enough. Thank you. Will try and post the results (again).
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
So basically using the CreateSound, we ALWAYS create it locally without GetLocalPlayer, like:
JASS:
set TrackSnd[i] = CreateSound(s, false, false, false, 12700, 12700, "") //not in GetLocalPlayer()
What? No. That's what we were constantly telling you for two pages.
The array is global. You can not create sounds locally (well, technically you can, but it will desync).
The file registered to the handle (as in the audio file on your hard drive) has nothing to do with the sound handle.

Each player will have a different audio file for TrackSnd[0], but they will still have the same handle id.
The only stuff that is local in the script above is the audio file registered to the created sound handle via GetSoundFileDuration and currentTrack, which is incremented whenever GetSoundIsPlaying or GetSoundIsLoading returns false (since this condition returns different results for every player).
This is also the reason why StartSound does not need to be in a local block. If another player is currently running a track, the condition will not allow the code on their client to reach the part where the sound is started. And since currentTrack is also different for every player, it will also run the correct track for everyone even if two players happen to finish a track at the same time.

If we use the array that has nothing to do with the player (use track[0] for player blue for example) we'll desync.
No, it will not desync. Playing sounds never desyncs. Playing sounds does not generate any net traffic.

Legit enough. Thank you. Will try and post the results (again).
Just use the system I posted. It works.
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
Slight necro and BUMP, but even this isn't working. Went into some problems and wanted to see that even the most basic form works and It failed.
JASS:
library meh
    function pft takes nothing returns nothing
    local sound s = CreateSound("Musics\\Test-01", false, false, false, 12700, 12700, "")
        call BJDebugMsg("init")
        call TriggerSleepAction(1)
        call BJDebugMsg("start")
        call StartSound(s)
    endfunction
endlibrary

It shoes "init" and "start", so no thread crashes, and the function is called correctly.
Ideas?
 
Status
Not open for further replies.
Top