| 04-21-2009, 07:41 PM | #1 |
I have a sound that doesn't play the first time I call it. I get the idea that the sound has to be loaded first, however I have tried preloading and that doesn't help. In the below code, the first sound (s3start) does not play the first time. The second sound (s3loop) does play the first time, although that might have something to do with the fact that the sound loops. JASS:if GetSpellAbilityId() == 'A039'then set uCaster = GetTriggerUnit() set iSound = GetSoundHandle() call SetUnitInvulnerable(uCaster, true) set iUnit = StoreAlphaUnit(uCaster) call UpdateAlphaUnit(iUnit, 255, 255, 0, -1, 0, true) call ConditionalTriggerExecute(gg_trg_UpdateAlphaUnits) //start sound set udg_SoundHandle[iSound] = CreateSound("war3mapImported\\s3start.wav", false, true, false, 10, 10, "DefaultEAXON") call SetSoundDuration(udg_SoundHandle[iSound], 681) call SetSoundChannel(udg_SoundHandle[iSound], 0) call SetSoundVolume(udg_SoundHandle[iSound], 127) call SetSoundPitch(udg_SoundHandle[iSound], 1.0) call SetSoundDistances(udg_SoundHandle[iSound], 600.0, 1024.0) call SetSoundDistanceCutoff(udg_SoundHandle[iSound], 1536.0) call SetSoundConeAngles(udg_SoundHandle[iSound], 0.0, 0.0, 127) call SetSoundConeOrientation(udg_SoundHandle[iSound], 0.0, 0.0, 0.0) call AttachSoundToUnit(udg_SoundHandle[iSound], uCaster) call StartSound(udg_SoundHandle[iSound]) call KillSoundWhenDone(udg_SoundHandle[iSound]) call PolledWaitMx(0.6) //looping sound set udg_SoundHandle[iSound] = CreateSound("war3mapImported\\s3loop.wav", true, true, false, 10, 10, "DefaultEAXON") call SetSoundDuration(udg_SoundHandle[iSound], 5684) call SetSoundChannel(udg_SoundHandle[iSound], 0) call SetSoundVolume(udg_SoundHandle[iSound], 127) call SetSoundPitch(udg_SoundHandle[iSound], 1.0) call SetSoundDistances(udg_SoundHandle[iSound], 600.0, 1024.0) call SetSoundDistanceCutoff(udg_SoundHandle[iSound], 1536.0) call SetSoundConeAngles(udg_SoundHandle[iSound], 0.0, 0.0, 127) call SetSoundConeOrientation(udg_SoundHandle[iSound], 0.0, 0.0, 0.0) call AttachSoundToUnit(udg_SoundHandle[iSound], uCaster) call StartSound(udg_SoundHandle[iSound]) //wait and remove effects call PolledWaitMx(28.4) call StopSound(udg_SoundHandle[iSound], true, true) call ClearSoundHandle(iSound) ... Any thoughts on why the sound won't play first time / how I can get it to play first time? Thanks. |
| 04-21-2009, 08:44 PM | #2 |
You're using the same array global for two different sounds: "udg_SoundHandle[iSound]" Are you sure that the first sound being played doesn't get overwritten by modifying the value of that variable? Give it a check... |
| 04-21-2009, 10:02 PM | #3 |
Hmmm... I don't think that's an issue because the idea is that the start sound plays, and then the loop sound plays. When the loop sound plays I no longer need the start sound. If a second instance arrises then a different SoundHandle element will be returned. The problem is that the sound doesn't play only on the first time I try to play it. All subsequent plays are fine. This suggests that it is a loading/caching issue, but preload doesn't fix the problem (preload during initialisation a problem?). I could have a function that duplicates this code and run it 1 second after initialisation but that seems a rather poor work-around (plus I don't want to have to do that for every sound)... |
| 04-21-2009, 10:24 PM | #4 |
As far as I know it has always been like this. What we do is either to a) ignore the issue or b) "play" the sound at map init. |
| 04-22-2009, 07:11 AM | #5 |
I've added basic code to my InitSounds trigger to just play the sound at zero volume, and changed the trigger to fire at 0.01 seconds instead of init. Now it's working. Thanks. |
| 04-22-2009, 12:57 PM | #6 |
If the sound wont actually play the first time you "play" it, why bother setting the volume to 0? |
| 04-22-2009, 01:20 PM | #7 |
Good point, but better safe than sorry. |
| 04-22-2009, 02:46 PM | #8 |
Preloading it always made it work fine for me. |
| 04-22-2009, 04:31 PM | #9 |
I found that as long as the sound wasn't created in the same thread it was to be played in, it would work the first time. Aka, use a timer or an execute function to play the sound after you create it. |
| 04-22-2009, 06:40 PM | #10 |
Isn't KillSoundWhenDone buggy in some way? |
| 04-22-2009, 08:57 PM | #11 |
KillSoundWhenDone == DestroySound Blizz just used that name not to give the impression that it would stop current reproductions of the sound. |
| 04-23-2009, 01:27 PM | #12 | |
Hmm... this was working for me, but tried it just now and the sound is once again not playing in the first call. Is KillSoundWhenDone buggy in some way? My understanding was that it only destroyed the instance of the sound so that I don't end up with hundreds of instances loaded in memory, but if it unloads the WAV as well then that's not good... Quote:
|
| 04-23-2009, 02:42 PM | #13 |
JASS:set soundFile = CreateSound() call PlaySound(soundFile) vs. JASS:set soundFile = CreateSound() call ExecuteFunc("PlayMySound") .. function PlayMySound takes nothing returns nothing call PlaySound(soundFile) endfunction I have never tried with execute function, but I know a 0 second timer works. |
| 04-23-2009, 08:51 PM | #14 |
I've done pretty much what Ammorth suggested (but dressed up with a slew of other functionality)... and it now works perfectly. No need to preload either. Just one more question regarding TriggerWaitForSound. Does this native actually do anything at all? I was expecting call TriggerWaitForSound(mysound, 0) to pause execution of the thread until the sound finished but it acted as though the call wasn't even there. Unless it's supposed to work like "wait for sound to play to offset" but if that's the case then it's useless because you'd have to know in advance how long your sound is. EDIT: Okay, it STILL isn't working. I thought it was, but now I've discovered that it doesn't play the first time in a given session of Warcraft 3. Meaning, I have the problem, play the sound once (which doesn't work), quit back to the menu, start another game, now the sound will play first time. I am presently preloading the file, playing it 3 seconds after map load, but STILL it doesn't play when I try to play it with my spell (first time). Playing it 3 seconds after map load should have solved it at least, but it hasn't. I can't figure out why because it calls the same function that my spell trigger does. Aaaggghh! EDIT2: Found a post where someone mentioned that KillSoundWhenDone can sometimes stop the sound. That appears to be the case here. When I comment that out of my play sound function the problem goes away. But how am I supposed to kill the sound when done? I don't wanna have to set up a timer for every sound! |
| 04-24-2009, 03:04 AM | #15 |
what I know about KillSoundWhenDone() is that you CAN'T call it before StartSound(), else the sound won't play. So the correct order would be: JASS:call StartSound( s ) call KillSoundWhenDone( s ) I've created a library that feets my needs of making spells, it's preaty basic since it only have two methods of playing a sound: play it globally, so anyone will hear it no matter where the player camera is positioned; play the sound attached to an unit. JASS:library SpellSound globals private integer registered_sounds = 0 endglobals struct soundSX extends array private string name private integer duration private real min_pitch private real max_pitch private boolean rnd_pitch //// Playing method PlayNormal takes nothing returns nothing local sound s = CreateSound( .name, false, false, false, 127, 127, "SpellsEAX" ) if .rnd_pitch then call SetSoundPitch( s, GetRandomReal(.min_pitch, .max_pitch) ) endif call SetSoundDuration( s , .duration ) call SetSoundVolume( s, 127 ) call StartSound( s ) call KillSoundWhenDone( s ) endmethod method PlayAttachUnit takes unit attach returns nothing local sound s = CreateSound( .name, false, true, false, 127, 127, "SpellsEAX" ) if .rnd_pitch then call SetSoundPitch( s, GetRandomReal(.min_pitch, .max_pitch) ) endif call SetSoundDuration( s , .duration ) call SetSoundVolume( s, 127 ) call AttachSoundToUnit( s, attach ) call StartSound( s ) call KillSoundWhenDone( s ) endmethod //// Inicialization private method PreloadSound takes nothing returns nothing local sound s = CreateSound( .name, false, false, false, 127, 127, "" ) call TriggerSleepAction( 0.0 ) call SetSoundVolume( s, 0 ) call StartSound( s ) call KillSoundWhenDone( s ) endmethod method RandomPitch takes real min, real max returns nothing set .rnd_pitch = true set .min_pitch = min set .max_pitch = max endmethod static method NewSound takes string path, integer dur returns soundSX local soundSX snd = soundSX[registered_sounds] set snd.name = path set snd.duration = dur call snd.PreloadSound.execute() set registered_sounds = registered_sounds+1 return snd endmethod endstruct endlibrary How to use it? first declare a global witch is used to reference the sound: JASS:globals soundSX snd_launch soundSX snd_hit soundSX snd_thunder endglobals At some place in your map inicialization, initialize the sounds globals, you need to pass the sound .wav file path, and the duration in miliseconds: JASS:function Init takes nothing returns nothing set snd_launch = soundSX.NewSound( "Abilities\\Spells\\Orc\\LightningBolt\\LightningBolt.wav", 2136 ) set snd_hit = soundSX.NewSound( "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.wav", 3878 ) set snd_thunder = soundSX.NewSound( "Sound\\Interface\\GlueScreenEarthquake1.wav", 3237 ) endfunction and finally you call the method PlayNormal() or PlayAttachUnit() to play it JASS:local unit u call snd_launch.PlayNormal() call snd_hit.PlayAttachUnit( u ) |
