LoopSound restarting endlessly

Blitz3D Forums/Blitz3D Beginners Area/LoopSound restarting endlessly

fox95871(Posted 2009) [#1]
I can't believe I'm stuck on something like this, but whatever. First off, don't run this code unless your computer can handle a new instance of a sound playing every frame. You've been warned!

Okay, what I'm going for here is the same effect almost all commercial games have: the song intro plays, then the song loop, then the loop repeats over and over so you never hear the intro again. I got everything figured out except the loop keeps restarting every frame like I said. I've tried ChannelPlaying, nested loops, everything. Please help/alter my code for me.




Ked(Posted 2009) [#2]
Try this:



fox95871(Posted 2009) [#3]
Thanks, I'll try that.


fox95871(Posted 2009) [#4]
Perfect, thanks.

One last thing. I've added a time grabber thing, which basically tells me the time the intro ends so I know what to set the start time to for the loop. The problem is, it always gives me a slightly different reading! Not only that, but it seems like whenever I decide on a good average, I always end up with a blip of silence if I set it to that. I know it's not supposed to be there because I split the file perfectly with Audacity. Is there any way to get rid of this apparent loading delay? I've tried overlapping the times, and that does work, but on my computer. Who knows when people download my game if some people will get the nice overlap, and others the blip. Please help.

Code:


Necessary files:

(Links removed. Please see post 21.)

Last edited 2010


Guy Fawkes(Posted 2009) [#5]
yes. PLEASE perfect this.

its BEAUTIFUL! :O


Guy Fawkes(Posted 2009) [#6]
did anyone fix this?


Kryzon(Posted 2009) [#7]
Why can't you go to an audio editing application and just look at how long the sound file lasts? if it lasts 2.5 seconds, then you know in Blitz it should last 2500 milliseconds.


fox95871(Posted 2009) [#8]
It's nothing like that, I can adjust the timing so they line up exactly right on my computer, but then on other computers there's either a gap or an overlap. I need to make it compatible, otherwise the whole thing's useless.

Dark shadow wing, thanks. I'm hoping this will be useful to a lot of people.


Guy Fawkes(Posted 2009) [#9]
he has a point.

it has to play like it does

in Final Fantasy.

or ANY game for that matter.


Warner(Posted 2009) [#10]
Use ChannelPlaying to determine if a channel is playing. If is not playing, start the other sound. There will allways be a gap, because of the way PCs work. Basically, the more often you check if a channel is playing, the smaller the gap will be.
In this code, the gap should be unnoticable, since the check is done as fast as possible:
intro = loadsound("intro.wav")
middle = loadsound("middle.wav")

chn = playsound(intro)

repeat
  if not(channelplaying(chn)) then playsound middle
until keyhit(1)

However, the more code is in between the checks, the bigger the gap will/can be:
intro = loadsound("intro.wav")
middle = loadsound("middle.wav")

chn = playsound(intro)

repeat
  if not(channelplaying(chn)) then playsound middle
  delay 1000 ;<---simulates heavy code, tweening and/or waiting for vsync (flip)
until keyhit(1)

I wonder if BlitzBass has functions that might help you? I never used it, but it is a populair sound module.


Guy Fawkes(Posted 2009) [#11]
o no there wont be a gap ;)

not if u fade out and fade back in quickly ;)


fox95871(Posted 2009) [#12]
I'll try that. Isn't there some way to do a system check so it can be determined correctly for each system? Looking at the complexity of render tween code for example, it makes me think there must be a way. Better yet, how do most pc games handle this problem? I assume it's not a problem with console games, they already know the speed of the system in advance. But if someone knew how pc music looping was coded, I think that would be the answer.


Kryzon(Posted 2009) [#13]
It's probably hardwired into the sound API, like, a script of sounds, you know?

But if you stick a lot of ChannePlayings throughout your game, there shouldn't be any problem. Like, outside of the tweening loop (so it's always checked, instead of ocasionally).


Warner(Posted 2009) [#14]
Maybe you could create a separate thread that checks if the sound is still playing? A .dll could maybe achieve this. That is why I thought about BlitzBass.


Kryzon(Posted 2009) [#15]
If you put the check outside of the tweening loop you'll be checking your sound(s) every other millisecond.

A gap of 1 millisecond is unoticeable.


Guy Fawkes(Posted 2009) [#16]
sos fade in+out


fox95871(Posted 2009) [#17]
If you put the check outside of the tweening loop you'll be checking your sound(s) every other millisecond.

Could you show me how? I haven't been programming very long.


Kryzon(Posted 2009) [#18]
Sure thing:

IntroPart = LoadSound(...)
LoopPart = LoadSound(...) : LoopSound LoopPart

[...]

Channel = PlaySound(IntroPart)
status=0 ;just so we can check how things are going

While Not KeyHit(1)

   Cls
 
   Repeat
   
      If status = 0 and ChannelPlaying(Channel)=0 then
         status = 1
         Channel = PlaySound(LoopPart)
      Endif 
      elapsed=MilliSecs()-time

   Until elapsed

   ticks=elapsed/period
   tween#=Float(elapsed Mod period)/Float(period)
   
   For rt=1 To ticks
   
         time=time+period
         If rt=ticks Then CaptureWorld()
         UpdateWorld()
         
         If status = 0 and ChannelPlaying(Channel)=0 then
            status = 1
            Channel = PlaySound(LoopPart)
         Endif
   
   Next

   RenderWorld tween

   Flip

Wend

Freesound IntroPart
FreeSound LoopPart  

;just free the sounds if you're not going to use them in the game anymore.

Hmm, after seeing it for a while I changed it a bit. At first I placed the "If status=0 and channelplaying[...]" outside of both loops, but then I realised that this Repeat...Until loop could actually hold things for 1 millisecond of more as well as the For...Next, so we place the check inside them so it's always checked, even if it's more than once per frame.

It's not too CPU consuming, I'm sure it's just as fast as peeking a memory value, and it's not like you'll be doing this in the middle of the game.
If you were, then you could just place that IF right after "Renderworld Tween", just so it's outside of the tweening loop.

You can script whatever you want your game to do based on the value of "status".
When it was zero, we checked to see if the intro part had ended. When it's 1, you can see if the player picks an option or something, and then change the value of "status" to 2. Make sure that whatever value you want to be last is the condition of the While...Wend loop:

While status<>3

[...]

Wend

That way, when status = 3 or the last value you'll give it, the loop will stop executing and the game will progress. It's how I script things in my game, such as the transparency of objects, playing of sounds or anything that demands a certain period of time that only loops can give you.


Warner(Posted 2009) [#19]
A statement like this:
If status = 0 and ChannelPlaying(Channel)=0 then
could be optimised by this:
If status = 0 then if ChannelPlaying(Channel)=0 then

The bb compiler doesn't stop checking if the left branch of the AND statement is false:
status = 1
If status = 0 And ImageWidth(test)>10 Then Print "ok"



Kryzon(Posted 2009) [#20]
You are definitely right. But then you could save all that trouble just by using a Select:

Select status
          Case 0
                   If ChannelPlaying(Channel)=0 Then
                      [...]
          Case 1
                   [...]
End Select


EDIT: Which is what I recommend fox should use in case he has like, a 4+ step script. It facilitates a lot to add new steps.


fox95871(Posted 2009) [#21]
I tried moving the code so it's outside the render tween like you suggested, but I don't really see a change. The start time is still lands on different numbers each time you run it, usually about 150 milliseconds late. I also don't know which of the two places to put it. Your example showed one between Repeat and elapsed=MilliSecs()-time, and the other between UpdateWorld and Next. Could you try this new example and see what I mean about how the times are always different? Many things have changed now.

http://www.mediafire.com/?75w3147jdhnplaj

Last edited 2010


Bobysait(Posted 2009) [#22]
Eventually you can try to catch the deltatime for loops :
for the first step, you'll have to get the channel "length", so the first looping will crack a bit according to the framerate.
But for every next loops, there won't be any cracking (or really lesser)

don't use the LoopSound function, just do something like this :

Local Snd=LoadSound("MySound.ogg")
Local Chan=PlaySound(Snd)
Local SndLength%=0
local chanloop%=0
Local chanTime=millisecs()

local mt%,lt%,dt%
repeat
   ; get the delta time
   Lt=mt
   Mt=Millisecs()
   dt=mt-lt

   ; check if channel reached the end
   if not(channelplaying(Chan)

      if chanloop=0
         ChanLoop=1
         ChanLength=millisecs()-ChanTime
         ChanTime=millisecs()
      else
         ChanLoop=ChanLoop+1
         ThisLength=millisecs()-ChanTime
         ChanTime=millisecs()
         ; here we can check the new time to be closer and closer
         ; from the real length.
         ; So every loop will get a better looping mode.
         if ThisLength<ChanLength
            ChanLength=ThisLength
         endif
      endif
      Chan=PlaySound(Snd)

   ; if we know the length, we can check if the next loop
   ; it will end or not.
   Else

      if ChanLoop>0
         if mt-ChanTime >= ChanLength-Dt-5
            Chan=PlaySound(Snd)
            ChanTime=millisecs()
            ChanLoop=ChanLoop+1
         endif
      endif

   endif

until keyhit(1)




this code is not tested, so there may be mistakes.