• 🏆 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!

External Music System V.1.5

This is an unbelievably simple system I've made with basic vJASS (First vJASS spell!!) as I currently don't know advanced vJASS (structs and stuff) but it wasn't needed, as the system is waaaaay too simple, but I think useful. Might not be approved, as it is too simple, but for some Map-Makers that don't want to waste filesize on Music files they can attach the soundtrack to the map and let the player install it.

The is the External Music System. A system where you give it the .mp3 files in your Warcraft III folder (you give the system the path) and it plays the music for you one by one, I tried to make it as Configurable as possible, like you can choose all .mp3 prefixes, the choice whether the 0 before 1 should be there or not (like "Track-01" and "Track-1"), playing the music random, etc. The user can fully configure the system, and of course the .mp3 files!

Fully MUI and GUI friendly. Demo map includes whatever you need to know, and a way to use the system is under the Category "Demo related".

Requirements:
JNGP
Local Files Enabled


Why JNGP?
Because this is coded in vJASS and also you need to have local files enabled. Tick it from the Grimoire extension of JNGP (thanks Emm-A-!).

Disclaimer:
Changing the .mp3 files in the middle of when system is ran is NOT supported.
Only works with .mp3 files. .wav file formats and others are basically avoided.

Remember, if anyone uses this and finds a problem with it, he has the Responsibility to report the problem here (using the debug messages).

Special thanks to Zwiebelchen for his helps.

  • MS Configuration
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- ===== --------
      • -------- Configuration --------
      • -------- ===== --------
      • -------- The Timeout for the system. --------
      • -------- The real timeout will be calculated by how many times(MS_Timeout) checks will happen in a SINGLE SECOND. So the system now currently works every 0.5 seconds. --------
      • -------- The bigger the variable is, the more percise the system gets. But with bigger values than like 40 or 50 you'll have some minor problems on Battle.net. --------
      • Set MS_Timeout = 20.00
      • -------- When set to true, "Test-01" will be used as a sound and "Test-1" won't be. Vica Versa as well. --------
      • Set MS_Use0 = True
      • -------- The Variable would look for a known number of tracks, so it will loop until then for like 3 times. --------
      • -------- Set it to 0 so it would look for all available tracks. (recommended) --------
      • Set MS_ForLoop = 0
      • -------- When true, it may choose random tracks from the folder (the one played earlier might be played :p). --------
      • Set MS_PlayingIsRandom = False
      • -------- ===== --------
      • -------- End of Configuration --------
      • -------- ===== --------
      • Sound - Clear the music list
      • Sound - Stop music Immediately

  • MS How to use
    • Events
    • Conditions
    • Actions
      • -------- ---- --------
      • -------- Just note that for these functions to run you'll need to call StartSoundtrack at least once. --------
      • -------- ---- --------
      • -------- Use this code to start your soundtrack. Most important function. --------
      • -------- The first parameter is the folder path and the second parameter is the .mp3 file prefix. --------
      • -------- So it's like this: Warcraft3Folder\MyFolderPath\MyFilePrefix-01.mp3 --------
      • Custom script: call StartSoundtrack("MyFolderPath", "MyFilePrefix")
      • -------- How to mute a player: --------
      • Set TempPlayer = Player 1 (Red)
      • -------- 0 Volume is mutation. --------
      • Custom script: call EMSSetVolume(udg_TempPlayer, 0)
      • -------- ---- --------
      • -------- You can unmute someone as well, using the line below: --------
      • Set TempPlayer = Player 1 (Red)
      • -------- 127 Is the highest volume possible, I don't know what will happen if it's greater than 127, but I'm not sure it'll work out well. --------
      • Custom script: call EMSSetVolume(udg_TempPlayer, 127)
      • -------- ---- --------
      • -------- ---- --------
      • -------- Here's how to get the number of tracks a player has: --------
      • Set TempPlayer = Player 1 (Red)
      • Custom script: set udg_DemoTrackNumber = EMSGetTrackNum(udg_TempPlayer)
      • -------- Variable Demo_TrackNumber now has the value. --------
      • -------- ---- --------
      • -------- Listen carefully to this one. --------
      • -------- This will get the number of tracks the player has "locally". The integer won't cause desyncs (dont' worry) but the results may be different for each player. --------
      • Custom script: set udg_DemoTrackNumber = EMSGetTrackNumLocal()
      • -------- Variable Demo_TrackNumber now has the value. --------
      • -------- ---- --------
      • -------- Gets the current Track playing. It's local just like GetTrackNumLocal(). --------
      • Custom script: set udg_DemoTrackNumber = EMSGetCurrentTrack()
      • -------- Variable Demo_TrackNumber now has the value. --------
JASS:
library EMS /* v.1.5 */ initializer Init /*
******************************************************************************************
*
*     EMS by Arad MNK
*
******************************************************************************************
*
*     EMS
*
*        EMS is the External Music System which is a GUI-Friendly system. You Can
*        use this system both with JASS and GUI (other things are supported too!)
*        You can control the music playing from other external folders with this system.
*
*           Notes:
*            - Changing the .mp3 files in the middle of when system is on is NOT supported.
*            - Only works with .mp3 files. .wav file formats and others are basically avoided.
*
******************************************************************************************
*/
//! novjass

//*      API

        function StartSoundtrack takes string path, string filePrefix returns nothing
//*         - Most important Function. Initializes the system with any GUI event, with the according folder and file prefix.

        function EMSSetVolume takes player p, integer vol returns nothing
//*       - Set a player's music volume to an integer.

        function EMSPlayCustomTrack takes integer track returns boolean
//*         - Play a custom track while the system is running.

        function EMSGetTrackNum takes player p returns integer
//*         - The number of tracks player p has.

        function EMSGetTrackNumLocal takes nothing returns integer
//*         - The number of tracks the player has. It's a local integer.

        function EMSGetCurrentTrack takes nothing returns integer
//*         - Current track running. Local integer.

//! endnovjass

/*
*
*
******************************************************************************************
*
*      Credits
*          Zwiebelchen - He helped me at some critical points. Also the main idea drives from him.
*
******************************************************************************************
*/

    globals
        private integer array TrackNum // Global how many tracks you have.
        private integer TrackNumLocal // Local how many tracks you have.
        private sound array Track // The sound handles of tracks.
        private integer CurrTrackNum // Current track playing number, i.e MusicFolder\Test-CurrTrackNum.mp3
        private boolean Allowed = true // Should the system run or it's not allowed?
        private string SoundtrackFilePrefix = "NULL" // These two NULLs are used in
        private string SoundtrackPath = "NULL"       //   the function Message.
        private timer SystemTimer = CreateTimer()
        private boolean FirstCall = false
    endglobals

//********************************************//
//*************/Message Function/*************//
//********************************************//
    private function Message takes string s returns nothing //Debug messages
        debug call DisplayTextToPlayer(Player(0), 0, 0, "|c00FFFF00EMS:|r " + s + "\n|c00FF0000ID:|r " + SoundtrackPath +" " + SoundtrackFilePrefix)
    endfunction

//*******************************//
//*************/API/*************//
//*******************************//

    function EMSSetVolume takes player p, integer vol returns nothing
        local integer i = 1

        if FirstCall then
            loop
                exitwhen i == TrackNum[GetPlayerId(p)] + 1
                if GetLocalPlayer() == p then
                    call SetSoundVolume(Track[i], vol)
                endif
                set i = i + 1
            endloop
        
            call Message("New Volume: " + I2S(vol))
        else
            call Message("System is not initialized for the first time yet...")
        endif
    endfunction

    function EMSPlayCustomTrack takes integer track returns boolean //The boolean means that the track exists or not
        local boolean b = track <= TrackNumLocal and FirstCall

        if b then
            set Allowed = false //Does not allow the core to detect the stopped sound and restart
            call StopSound(Track[CurrTrackNum], false, false)
            call Message("Custom Track Playing: " + I2S(track))
            set CurrTrackNum = track - 1
            call StartSound(Track[CurrTrackNum])
            set Allowed = true
        else
            call Message("Invalid Track.")
        endif

        return b
    endfunction

    function EMSGetTrackNum takes player p returns integer
        if FirstCall then
            return TrackNum[GetPlayerId(p)]
        else
            call Message("System is not initialized for the first time yet...")
            return 0
        endif
    endfunction

    function EMSGetTrackNumLocal takes nothing returns integer
        if FirstCall then
            return TrackNumLocal
        else
            call Message("System is not initialized for the first time yet...")
            return 0
        endif
    endfunction

    function EMSGetCurrentTrack takes nothing returns integer
        if FirstCall then
            return CurrTrackNum
        else
            call Message("System is not initialized for the first time yet...")
            return 0
        endif
    endfunction

//*********************************************************//
//*************/Functions used by the system./*************//
//*********************************************************//

    private function CreateTrackLocation takes integer i returns string
        local string s
        local string smsg

        if udg_MS_Use0 and i < 10 then
            set s = "0"
        else
            set s = ""
        endif

        set smsg = SoundtrackPath + "\\" + SoundtrackFilePrefix + "-" + s + I2S(i) + ".mp3"
        call Message("Track path generated: " + smsg)
        set s = null

        return smsg
    endfunction
    
    private function NumOfTracksLocal takes nothing returns integer
        local integer i = 1
        local integer i2 = 1

        loop
            exitwhen i != i2
            if GetSoundFileDuration(CreateTrackLocation(i)) > 0 then
                set i2 = i2 + 1
            endif
            set i = i + 1
        endloop

        return i2 - 1
    endfunction

    private function NumOfTracksLimitLocal takes integer limit returns integer
        local integer i = 1
        local integer i2 = 1

        loop
            exitwhen i == udg_MS_ForLoop or i != i2
            if GetSoundFileDuration(CreateTrackLocation(i)) > 0 then
                set i2 = i2 + 1
            endif
            set i = i + 1
        endloop

        return i2 - 1
    endfunction

    private function NumOfTracks takes player p returns integer
        local integer i = 1
        local integer i2 = 1

        loop
            exitwhen i != i2
            if GetLocalPlayer() == p then
            if GetSoundFileDuration(CreateTrackLocation(i)) > 0 then
                set i2 = i2 + 1
            endif
            endif
            set i = i + 1
        endloop
            return i2 - 1
    endfunction
    
    private function NumOfTracksLimit takes player p, integer limit returns integer
        local integer i = 1
        local integer i2 = 1
        loop
            exitwhen i == udg_MS_ForLoop or i != i2
            if GetLocalPlayer() == p then
            if GetSoundFileDuration(CreateTrackLocation(i)) > 0 then
                set i2 = i2 + 1
            endif
            endif
            set i = i + 1
        endloop

        return i2 - 1
    endfunction

    private function Core takes nothing returns nothing
        if not(GetSoundIsLoading(Track[CurrTrackNum]) or GetSoundIsPlaying(Track[CurrTrackNum])) and Allowed then
            call StopSound(Track[CurrTrackNum], false, false)
            if udg_MS_PlayingIsRandom then
                set CurrTrackNum = GetRandomInt(1, TrackNumLocal)
                call Message("Random Track: " + I2S(CurrTrackNum))
            else
                if CurrTrackNum != TrackNumLocal + 1 then
                    set CurrTrackNum = CurrTrackNum + 1
                elseif CurrTrackNum == TrackNumLocal + 1 then
                    set CurrTrackNum = 1 //start from beginning
                endif
            call Message("Next Track: " + I2S(CurrTrackNum))
            endif
            call StartSound(Track[CurrTrackNum])
        endif
    endfunction

    private function Init takes nothing returns nothing
        call Message("Initialized.\nDebug mode is on.")
    endfunction

//****************************************************************************//
//*************/Most Important user function, starts the system./*************//
//****************************************************************************//

    function StartSoundtrack takes string path, string filePrefix returns nothing
        local integer i = 1

        set Allowed = false
        set SoundtrackPath = path
        set SoundtrackFilePrefix = filePrefix

        if FirstCall then
            call Message("Not first call, Stopping music.")
            call Message("Track stopped: " + I2S(CurrTrackNum))
            call StopSound(Track[CurrTrackNum], false, false) //Stopping the music playing if called when a soundtrack is on

            loop
                exitwhen i == TrackNumLocal
                call KillSoundWhenDone(Track[i])
                call Message("Track destroyed. Track Number:" + I2S(i))
                set i = i + 1
            endloop
        else
            set FirstCall = true
            call Message("First call.")
        endif

        set CurrTrackNum = 1
        set i = 1
        call Message("Starting soundtrack: " + path + "\\" + filePrefix)

        if udg_MS_ForLoop == 0 then
            set TrackNumLocal = NumOfTracksLocal()
        elseif udg_MS_ForLoop > 0 then
            set TrackNumLocal = NumOfTracksLimitLocal(udg_MS_ForLoop)
        endif

        loop
            exitwhen i == TrackNumLocal
            set Track[i] = CreateSound(CreateTrackLocation(i), false, false, false, 12700, 12700, "")
            call Message("Track created. Track Number:" + I2S(i))
            set i = i + 1
        endloop

        call StartSound(Track[1])
        call TimerStart(SystemTimer, 1 / udg_MS_Timeout, true, function Core)
        set Allowed = true
    endfunction
endlibrary

Keywords:
Music, music, arad mnk, arad, mnk, m, n, k, external, system, external music, external music system, bleh, filesize, orpg, sound, mp3
Contents

EMS (Map)

Reviews
16:57, 8th Feb 2016 Tank-Commander: A small system with a lot of use - great for saving map memory space and a good edition to the section. Code looks good from where I'm sat and the API fully explains the system. Well done. Notes (for users)...

Moderator

M

Moderator

16:57, 8th Feb 2016
Tank-Commander: A small system with a lot of use - great for saving map memory space and a good edition to the section. Code looks good from where I'm sat and the API fully explains the system. Well done.

Notes (for users): using it does somewhat make dling from the maps section (with links to tracks for maps) rather necessary (dling a map through bnet won't give the appropriate track)
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
Let us hope that this will be approved.

(PS,I doubt that there's more of those LOL GIF avatars since there's only four;which is Garen,Sona,Vi and Zed(which is you))

Let us hope :)

Oh all those 4 are on the Hive right now (Zed: Me, Vi: ~Nightmare, Sona: Apheraz, Garen: userid907).

EDIT: There's something wrong with the system. Some weird mistakes (thread crashes!!) by me. Don't review this while I fix them! Done. Ready.
EDIT 2: Another shameful update. Removed BJDebugMsg("hh")!!
 
Last edited:
Level 22
Joined
Feb 6, 2014
Messages
2,466
Made a quick look in the system,
  • Why not use a timer instead of call TriggerRegisterTimerEvent(EMS_Trig, (1 / udg_MS_Timeout), true). Read more about TimerStart
  • Why not use initializer so you don't have to force users to call InitEMS()?
  • Try adding a check in your loop for picking all players like PLAYER_SLOT_STATE_PLAYING and MAP_CONTROL_USER
  • You could store (1 / udg_MS_Timeout) to a variable to avoid re-calculating them every loop.
  • Why are you not indenting your local variables?
  • Feel free to use the and boolean operator instead of putting two if ... endif. You also might wanna use elseif for more readability so you don't have to indent again.
  • Do you really have to put a "by Arad MNK" in the name?
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
Made a quick look in the system,
  • Why not use a timer instead of call TriggerRegisterTimerEvent(EMS_Trig, (1 / udg_MS_Timeout), true). Read more about TimerStart
  • Why not use initializer so you don't have to force users to call InitEMS()?
  • Try adding a check in your loop for picking all players like PLAYER_SLOT_STATE_PLAYING and MAP_CONTROL_USER
  • You could store (1 / udg_MS_Timeout) to a variable to avoid re-calculating them every loop.
  • Why are you not indenting your local variables?
  • Feel free to use the and boolean operator instead of putting two if ... endif. You also might wanna use elseif for more readability so you don't have to indent again.
  • Do you really have to put a "by Arad MNK" in the name?

  • Timers are going to be removed in the system, they stop when a player pauses the game, fucking the timing. I'll just use a periodic timer, and figure it out.
    And I'm the master of TimerStart already, used it in my cinematic system for my Cinematic 1000 times.
  • This is going to be GUI friendly, so that the user would use it with any GUI event.
  • Well, I gotta check for all AVAILABLE people in all slots... meh. Will do it by forces.
  • OK. Will try. (In the new system it no longer exists 2 times)
  • Why should I? the readability is still fine.
  • If you're talking about the GetLocalPlayer() part then I'm not sure if it will still localize.
  • Sure. Will remove it.

Thanks for viewing this!
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
- I mean using TimerStart instead of creating a trigger with a periodic timer event.
- I don't see a disadvantage of using initializer. It prevents users from manually calling InitEMS.
- I mean just adding a condition in the beginning of the loop so it won't execute on empty slots or computer players.
- Ok if you're comfortable with that indention scheme. It was merely a suggestion because it's best to have uniform coding style. Read more here
- I think it will work fine with and though I'm not 100% sure.
 
Level 6
Joined
Jul 2, 2013
Messages
151
Dude, It doesn't work for me. :( I edited the folder name and set random = true, prefix = false.
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
This system is open for public. Like, even people not registered on hive.

Although this system currently uses timers, that is inaccurate when somebody pauses the game. Maybe the next update removes this issue.

EDIT:

Tune up for the next update as IT'S COMING SOON!!!
 
Last edited:
Level 17
Joined
Dec 11, 2014
Messages
2,004
System updated.


  • Timer issue no longer exists!
  • Cooler demo!
  • System code improved.
  • Whole system changed it's technique. It now uses sounds but you can easily control 'em (it's more configurable).
  • The code is now more efficient. Doesn't have leaks or efficiency or desync-related problems as much as I see.
  • READY FOR REVIEW!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I think /* */ is not a valid type for comment blocks in the standard editor. ( untested )
Only JPNG probably supports /*
The normal editor does only support //


Just saw the system in coded in vJass so the upper comment is pointless.

//just a linebreak without a use, readability improvement
Just a line without use to kill readability... :D Just leave a whitespace.
Comments should be informative or really great jokes. :)
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
Really absurd.

That'll be easy. It'll be a check, and an update. Will be done once I implement the debug mode.

Hey, next update will include debug mode and more control over the music. The update after that will be the "Soundtracks" update. Using different musics for different situations (dungeons, villages and etc for example).
 
I get the debug messages spammed "Next Track: 1", "Next Track: 2", and no sound.
Also there exists a debug mode already, under JASSHelper settings. In code you then
would need to write debug in front of the command/operation.

JASS:
loop
    call DisplayTimedTextToPlayer(Player(i), 0, 0, 60, s)
    set i = i + 1
    exitwhen i == 12
endloop
-> BJDebugMsg could be used instead, but actually why does it print messages to all players if a player changes sound for example?

The indentation is massed up at certain places. Always use 1x tab for indenting.
For example:

JASS:
    private function Core takes nothing returns nothing
            if not(GetSoundIsLoading(Track[CurrTrackNum]) or GetSoundIsPlaying(Track[CurrTrackNum])) and Allowed then
                call StopSound(Track[CurrTrackNum], false, false)
                    if udg_MS_PlayingIsRandom then
                        set CurrTrackNum = GetRandomInt(1, TrackNumLocal)
                        call Message("Random Track: " + I2S(CurrTrackNum))
                    else
                        if CurrTrackNum != TrackNumLocal + 1 then
                            set CurrTrackNum = CurrTrackNum + 1
                        elseif CurrTrackNum == TrackNumLocal + 1 then
                            set CurrTrackNum = 1 //start from beginning
                        endif
                        call Message("Next Track: " + I2S(CurrTrackNum))
                    endif
                call StartSound(Track[CurrTrackNum])
            endif
    endfunction
shoudl look like:
JASS:
    private function Core takes nothing returns nothing
        if not(GetSoundIsLoading(Track[CurrTrackNum]) or GetSoundIsPlaying(Track[CurrTrackNum])) and Allowed then
            call StopSound(Track[CurrTrackNum], false, false)
            if udg_MS_PlayingIsRandom then
                set CurrTrackNum = GetRandomInt(1, TrackNumLocal)
                call Message("Random Track: " + I2S(CurrTrackNum))
            else
                if CurrTrackNum != TrackNumLocal + 1 then
                    set CurrTrackNum = CurrTrackNum + 1
                elseif CurrTrackNum == TrackNumLocal + 1 then
                    set CurrTrackNum = 1 //start from beginning
                endif
                call Message("Next Track: " + I2S(CurrTrackNum))
            endif
            call StartSound(Track[CurrTrackNum])
        endif
    endfunction

And a function's body should always be indented. Doesn't matter if it's declaring and initialisizing locals.
It should follow JPAG

Can you add some more documention for the functions, please?

I'm not very sure a function like "MuteAtInit" is really needed.
I mean the user has fully access to mute/unmute commands,
so if necessariy he can normaly take usage of them instead of
calling the wrapper that might be anyways only used once at all.
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
Not playing sounds: Check your sound options in Warcraft III options. When you see the messages, you MUST hear the music/sound. They are SOUNDS. Don't look for music mutation or etc.

Debug mode: Tell me moar please! Never heard of that ;p Adding a debug before the function, the call, the what?

Indentation: I'll try to follow JPAG. Will be done as soon as possible.

Documentation: Isn't it explaining it correctly?

MuteAtInit: So I'd better remove it? Mkay :$
 
Level 2
Joined
Feb 25, 2016
Messages
20
Can it b open in classic WE or it requires JNPG? Cause its not working for me whn i tried opening it using classic WE.
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
great job on your system. This is excellent for saving filesize. It would be great, if you uploaded a spin-off snippet, which would just enable someone to load a sound handle from a specified folder without playing it, so we could use this sound in another sound system :)
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
That's actually easy, doing it even away from PC:
JASS:
struct test

    method loadSoundFromFolder takes string path returns sound
        return CreateSound(path, false, false, false, 12700, 12700, "")
    endmethod

//Example:

    private static method onInit takes nothing returns nothing
        call StartSound(.loadSoundFromFolder("Music\\MahTest.mp3"))
    endmethod
endstruct

//First time using structs! Not sure that I'm using it correctly in onInit :p

Save it to a variable and done. Use it. Just note it may be different for different players (handles are different) that's why this is a system, the main point is to control the localizations and not to cause desyncs. Else the whole thing is the code forementioned.
 
Last edited:
Level 14
Joined
Jul 1, 2008
Messages
1,314
That's actually easy, doing it even away from PC:
JASS:
struct test

    method loadSoundFromFolder takes string path returns sound
        return CreateSound(path, false, false, false, 12700, 12700, "")
    endmethod

//Example:

    private static method onInit takes nothing returns nothing
        call StartSound(.loadSoundFromFolder("Music\\MahTest.mp3"))
    endmethod
endstruct

//First time using structs! Not sure that I'm using it correctly in onInit :p

Save it to a variable and done. Use it. Just note it may be different for different players (handles are different) that's why this is a system, the main point is to control the localizations and not to cause desyncs. Else the whole thing is the code forementioned.

thanks a lot for posting this :thumbs_up: This is so great, because I use a lot of custom music, and I didn't know, the possibility of outsourcing was there.

This actually brought me to think about directly creating the sound from folders into variables for each player in that map I am talking about.
It uses a custom music system with timers for each player, and anyway sound is only played locally, so it won't desync, right? I mean of course, every player would have to save the music folder and subfolders at the specified location ...

Anyway, your system looks great! :thumbs_up:

(sry I dont want to spam, but I thought, this might be interresting for others as well)
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
Oh, if you don't want to change tracks you can use this to work with musics not sounds:

JASS:
function PlayExMusic takes string path, player forPlayer returns nothing
    if GetLocalPlayer() == forPlayer
        if GetSoundFileDuration(path) >= 0
            //music exists
            call StopMusic(false) //Stops the currently playing music
            call PlayMusic(path)
        endif
    endif
endfunction

No desyncs, all safe. Changing tracks is a bit more complicated and requires timers when using musics, but with sounds there is no need for timers (system base). Timers are stopped when the player pauses the game.
Using this system allows you to change tracks safely using sounds with a lot of configurabilities. No need for your own system (I mean I've already created it).

If you want to change between tracks, use this system. Else, use the easy snippet up here. This snippet is used for variabling external .mp3 files.
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
Oh, if you don't want to change tracks you can use this to work with musics not sounds:

JASS:
function PlayExMusic takes string path, player forPlayer returns nothing
    if GetLocalPlayer() == forPlayer
        if GetSoundFileDuration(path) >= 0
            //music exists
            call StopMusic(false) //Stops the currently playing music
            call PlayMusic(path)
        endif
    endif
endfunction

No desyncs, all safe. Changing tracks is a bit more complicated and requires timers when using musics, but with sounds there is no need for timers (system base). Timers are stopped when the player pauses the game.
Using this system allows you to change tracks safely using sounds with a lot of configurabilities. No need for your own system (I mean I've already created it).

If you want to change between tracks, use this system. Else, use the easy snippet up here. This snippet is used for variabling external .mp3 files.

thanks again.

so to clarify this, in my map I am using a highly artificial mp3 sound system with timers for each player (sry its not music, I am also using mp3 sounds), so that music will be looping. It has to react very individually to all kinds of situations (seasons, events, dayNight etc.) and play local sound for each player according to its individual situation.

I didn't see an easy way to implement your system in exchange for that.

If I used your system and wanted to play a certain track at a specific situation, I could find it via the track number of the system, right? E.g. test-01.mp3 would be that specific sound I needed so I could find it in the system via its track number 1 and so on? Also, I didnt see, whether your system checks, if a requested sound is already played (which is very important for me that it is able to).

Also I would have to adapt your system anyway, so I am currently thinking of a good way or if I will be able to at all (of course I will credit you even if I will only use your idea (and Zwiebelchen as well) ;) )
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
To do list:

  • Soundtrack system

This is what you need.

I'm still trying to figure out keywords for the API... Else I woulda upload it today :p

The demo will include what you want.


Soundtrack update will be released either tomorrow or the day after (EMS v1.5).



When released, with different events you can use:
JASS:
//register the event you want, GUI friendly as well
call StartSoundtrack("myFolder", "myFilePrefix") //stops the music, changes the folder with the .mp3 files, and starts playing

The demo event will be a peseant that with moving it you'd change track folders (specific songs you need).

EDIT: I had some problems with thread crashes. It'll be released tomorrow, if my time allows that.

EDIT 2: BRUH. Thread crash problem fixed, now when starting another soundtrack the other soundtrack plays the next track, making 2 tracks play at once. (You'll feel like a DJ)

EDIT 3: FINALLY! Managed to find the issue. Preparing to upload.
 
Last edited:
Level 14
Joined
Jul 1, 2008
Messages
1,314
Unfortunately the system causes havy lag every time I enter one of these regions with the worker in your demo map ... Other than that it is really cool, thanks for creating this system. If I find some time, I will think about this more :)
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
Note that the sounds can't be preloaded as they are created at call. This might be the reason for your lag. Maybe I'll add a new function to be able to preload sounds.

I think this would be important .. I dont really get why my computer has problems with that as it runs wither 3 on max details but lags with that create native here. Might be some stupid special problem and not generally related to your function ..
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
Hey by testing the CreateSound native like in your system, I just figured out, that you have to enable Warcraft to be able to use Local Files. I didnt read this here or I didnt see it, and as I have 2 computers with 2 editors I have been searching for almost 2 days now ... :ogre_rage:

So I suggest you add this important tip to your description :) instruction using regedit.exe (or simply tick the "enable local files" in grimoire extensions of JNGP.


@Lag: this laggy problem dissappeared when I changed some thing in my system, so besides a little first lag, there is no problem anymore :thumbs_up:
 
Top