Page 1 of 3

Sound Effect / Forward + Jump? / Collision (kinda)

Posted: Fri Dec 10, 2010 4:09 am
by Ryne
Hello again, I'm back with more problems. I have a basic sound created for jumping:

Code: Select all

	audiosrc = {}
	audiosrc.jump = love.audio.newSource("snd/jump.wav", "static" )
Then I have the sound being played in my jump like this:

Code: Select all

	elseif love.keyboard.isDown('x') and player.dir == "right" then -- jump
	love.audio.play(audiosrc.jump)
	curranim = anim.jumpright
	jumping = true
	
	elseif love.keyboard.isDown('x') and player.dir == "left" then
	love.audio.play(audiosrc.jump)
	curranim = anim.jumpleft
	jumping = true
I know the jump code is ugly right now, but when the player jump's it play's the sound just fine, but only once. I can jump about 4-5 times after the first sound and hear nothing, but after a certain amount of time (like 3-5sec) it will play on another jump. Anyone know what's causing this?

EDIT: After adding "love.audio.stop()" to the "else" if no key is down it works but only if I keep held the jump button. So I'm assuming there needs to be some sort of duration?

Re: Sound Effect

Posted: Fri Dec 10, 2010 4:51 am
by Mud
If a given sound source is currently playing and you call play again, it won't start over. You have to either wait until it's done, or call 'stop' on it.

Code: Select all

love.audio.stop(sound)
love.audio.play(sound) 
If you want another instance of the same sound to sound to play, such that it overlaps with the current sound, then you need to create a new instance of the audio source and play *that*.

Re: Sound Effect

Posted: Fri Dec 10, 2010 9:19 am
by thelinx
Like Mud said, a sound source can only be playing one at a time. I'll steal a metaphor I read somewhere and tell it to you:

Think of sound sources like a trumpet. The trumpet can't play the more than once at a time. So, what do you do to solve this issue?
You add more trumpets.

The easiest way to do this is to use a library like Soundmanager (LÖVE-class also has this functionality) to create and play sources for you on demand.

Basically, you need to create your sound as a SoundData, and when you want to play it, create a Source from the SoundData and play that.

Re: Sound Effect

Posted: Fri Dec 10, 2010 9:50 am
by prototypez
A technique I learned from the inimitable 'Ten Second War' is to create a table of identical sound sources thusly:

Code: Select all


sounds = {}

for i = 1, 10 do
    sounds[i] = love.audio.newSource(yoursound, "static")
end

soundcounter = 0
Then, when you play your sound, do it like this:

Code: Select all


    love.audio.play(sounds[soundcounter])

    soundcounter = soundcounter + 1
    if soundcounter > table.getn(sounds) then
      soundcounter = 1
    end


Kinda cheesy, and you're still limited to whatever amount of sounds you have in the table playing at the same time, but for smaller low mem sounds it workd pretty good for me.

Re: Sound Effect

Posted: Fri Dec 10, 2010 3:01 pm
by Ryne
prototypez wrote:A technique I learned from the inimitable 'Ten Second War' is to create a table of identical sound sources thusly:

Code: Select all


sounds = {}

for i = 1, 10 do
    sounds[i] = love.audio.newSource(yoursound, "static")
end

soundcounter = 0
Then, when you play your sound, do it like this:

Code: Select all


    love.audio.play(sounds[soundcounter])

    soundcounter = soundcounter + 1
    if soundcounter > table.getn(sounds) then
      soundcounter = 1
    end


Kinda cheesy, and you're still limited to whatever amount of sounds you have in the table playing at the same time, but for smaller low mem sounds it workd pretty good for me.
To be honest, I actually thought of doing that on my own, I just assumed I was working with sounds wrong, and therefore it wouldn't be as complicated as that.

EDIT: Actually When I use this code outside of update it doesn't work, once inside update it cause sever lag, about 2 fps in the game. This only happens if the iterator is inside of update:

Code: Select all

for i = 1, 10 do
    sounds[i] = love.audio.newSource(yoursound, "static")
end

Re: Sound Effect

Posted: Fri Dec 10, 2010 3:40 pm
by tentus
I'm going to pull a Zac and post an entire block of code: this is a pared-down version of what I use in my game, hopefully you can adapt it to your needs.

Early in the main game I have this:

Code: Select all

require "subclass/sfx.lua"
sfx = soundfx:new()
And then this is the subclass sfx file (please note that I am using classes to make this work):

Code: Select all

require "subclass/class.lua"
soundfx = class:new() 

function soundfx:init()
	self.sfx_jump = love.audio.newSource("media/sfx/jump.ogg", "static")
	self.sfx_break = love.audio.newSource("media/sfx/break.ogg", "static")
	self.sfx_death = love.audio.newSource("media/sfx/death.ogg", "static")
	self.tempsound = nil
end

function soundfx:play(sound)
	if sound == "jump" then
		self.tempsound = self.sfx_jump
	elseif sound == "break" then
		self.tempsound = self.sfx_break
	elseif sound == "death" then
		self.tempsound = self.sfx_death
	end
	love.audio.stop(self.tempsound)
	love.audio.play(self.tempsound)
end
And then this can be called from any other function in the game:

Code: Select all

	sfx:play("jump")

Re: Sound Effect

Posted: Fri Dec 10, 2010 4:03 pm
by TechnoCat
The problem I foresee with the 10 second war method is that if you have 10 sounds going at once, that will work, but if you have 10 sounds followed by another 10 sounds quickly, the last 10 sounds will go unheard.

Re: Sound Effect

Posted: Fri Dec 10, 2010 4:13 pm
by Robin
@tentus: your code could be simplified like this:

Code: Select all

require "subclass/class.lua"
soundfx = class:new() 

function soundfx:init()
	self.jump = love.audio.newSource("media/sfx/jump.ogg", "static")
	self.break = love.audio.newSource("media/sfx/break.ogg", "static")
	self.death = love.audio.newSource("media/sfx/death.ogg", "static")
end

function soundfx:play(sound)
	love.audio.stop(self[sound])
	love.audio.play(self[sound])
end
What the best solution is depends on what you want: for example, the sound of your own death is likely just played once, so you wouldn't want to use stop(). If it is the sound of the death of a random mook, you'll want to take the soundmanager approach, like thelinx pointed out, which scales well.

Re: Sound Effect

Posted: Fri Dec 10, 2010 7:12 pm
by tentus
Robin wrote:@tentus: your code could be simplified like this:

Code: Select all

require "subclass/class.lua"
soundfx = class:new() 

function soundfx:init()
	self.jump = love.audio.newSource("media/sfx/jump.ogg", "static")
	self.break = love.audio.newSource("media/sfx/break.ogg", "static")
	self.death = love.audio.newSource("media/sfx/death.ogg", "static")
end

function soundfx:play(sound)
	love.audio.stop(self[sound])
	love.audio.play(self[sound])
end
What the best solution is depends on what you want: for example, the sound of your own death is likely just played once, so you wouldn't want to use stop(). If it is the sound of the death of a random mook, you'll want to take the soundmanager approach, like thelinx pointed out, which scales well.
@Robin: Thanks, I appreciate it. I didn't use that method before because it pooped out when I first tested it, but now I realize it's because "break" is a keyword in Lua... doh. All I have to do is rename break to something like "crunch" and it works perfectly. I love the way that half of programming (for me) is fixing dumb errors that I should have seen the first time around.

Re: Sound Effect

Posted: Fri Dec 10, 2010 10:37 pm
by Mud
TechnoCat wrote:The problem I foresee with the 10 second war method is that if you have 10 sounds going at once, that will work, but if you have 10 sounds followed by another 10 sounds quickly, the last 10 sounds will go unheard.
Yeah, my solution was to use the same circular buffer idea, but start with only 1 source and allocate others only if necessary. The worst case usage establishes how many sources you buffer for a particular sound. Once that's hit, there's no more allocating sources, just cycling through existing ones. If a sound is played infrequently, you end up with only one source, and the fast path to it is reasonably direct:

Code: Select all

  function Sound:play()
    repeat
       if sources[self.current]:isStopped() then
          love.audio.play(sources[self.current])
          return
          -- check others sources, starting with oldest moving toward most recently played
Here's the full code for it:

Code: Select all

Sound = class()

function Sound:init(filename)
   self.raw = love.sound.newSoundData(filename)
   self.sources = { love.audio.newSource(self.raw, 'static') }
   self.current = 1
end

function Sound:play()
   local start, sources = self.current, self.sources
   repeat
      if sources[self.current]:isStopped() then
         love.audio.play(sources[self.current])
         return
      end
      self.current = self.current % #sources + 1
   until self.current == start

   -- no free sources available, create a new one
   self.current = #sources + 1
   sources[self.current] = love.audio.newSource(self.raw, 'static')
   love.audio.play(sources[self.current])
end