Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
how do you handle your audio (soundmanager)? — Gideros Forum

how do you handle your audio (soundmanager)?

MoKaLuxMoKaLux Member
edited September 2021 in Game & application design
how are you all doing :) ?
I know the basics stuff for sound and sound channel but I wonder how to handle all the audio files?
https://wiki.gideros.rocks/index.php/Playing_Sound_and_Music

Is it good to declare every audio files in init or main?
Is it good to declare them as global?
...

I would also like to control the volume of all the sounds at once (via the option menu).

I have seen this class written in 2012!!! but I am not sure how to handle the management of audio files. Any tips for a sound manager please ;) ?

Thanks!

--[[
Bismillahirahmanirrahim
 
Audio control class for Gideros
by: Edwin Zaniar Putra (zaniar@nightspade.com)
 
This code is MIT licensed, see <a href="http://www.opensource.org/licenses/mit-license.php" rel="nofollow">http://www.opensource.org/licenses/mit-license.php</a>
Copyright 2012 Nightspade (<a href="http://nightspade.com" rel="nofollow">http://nightspade.com</a>)
 
gideros_audio
=============
Audio control class for Gideros
This class provides:
* easy way to mute/unmute
* easy way to manage and play BGM
* easy way to manage and play SFX
```Audio.new()``` Creates a new Audio object
```Audio:setBgms(bgms)``` Sets BGMs table with ```bgms``` table
```Audio:setSfxs(sfxs)``` Sets SFXs table with ```sfxs``` table 
```Audio:clearBgms()``` Clears BGMs table
```Audio:clearSfxs()```Clears SFXs table
```Audio:addBgm(name, path)``` Adds new BGM to BGM table
```Audio:addSfx(name, path)``` Adds new SFX to SFX table
```Audio:removeBgm(name)``` Removes a BGM identified by ```name```
```Audio:removeSfx(name)``` Removes a SFX identified by ```name```
```Audio:playBgm(name, force)``` Plays a BGM identified by ```name```. Allow to replay currently playing BGM by setting ```force``` to ```true```
```Audio:playSfx(name)``` Plays a SFX identified by ```name```
```Audio:mute(mode)``` Mutes audio. Available ```mode```s are "bgm", "sfx". To mute all audio set ```mode``` to ```nil```
```Audio:unmute(mode)``` Unmutes audio. Available ```mode```s are "bgm", "sfx". To unmute all audio set ```mode``` to ```nil```
 
Simple example:
```
audio = Audio.new()
audio:setBgms{
	["mainmenu"] = "path/to/mainmenu.mp3",
	["gameplay"] = "path/to/gameplay.mp3"
}
audio:setSfx{
	["explosion"] = "path/to/explosion.mp3",
	["ding"] = "path/to/ding.mp3"
}
audio:playBgm("mainmenu")
audio:playSfx("explosion")
```
]]
 
 
Audio = Core.class()
 
function Audio:init()
	self.bgmChannel = nil
	self.bgmCurrentName = nil
	self.bgmCurrent = nil
	self.bgmPos = 0
 
	self.bgmMute = false
	self.sfxMute = false
 
	self.bgms = {}
	self.sfxs = {}
end
 
function Audio:setBgms(bgms) self.bgms = bgms end
function Audio:setSfxs(sfxs)
	for name, path in pairs(sfxs) do self.sfxs[name] = Sound.new(path) end
end
 
function Audio:clearBgms() self.bgms = {} end
function Audio:clearSfxs() self.sfxs = {} end
 
function Audio:addBgm(name, path) self.bgms[name] = path end
function Audio:addSfx(name, path) self.sfxs[name] = Sound.new(path) end
 
function Audio:removeBgm(name) self.bgms[name] = nil end
function Audio:removeSfx(name) self.sfxs[name] = nil end
 
function Audio:playBgm(name, force)
	if name ~= self.bgmCurrentName or force then
		if self.bgmChannel then
			self.bgmChannel:stop()
			self.bgmChannel = nil
			self.bgmCurrentName = nil
			self.bgmCurrent = nil
			self.bgmPos = 0
		end
		if not self.bgmMute and not self.bgmChannel then
			self.bgmCurrentName = name
			self.bgmCurrent = Sound.new(self.bgms[name])
			self.bgmChannel = self.bgmCurrent:play(self.bgmPos, math.huge)
		end
	end
end
function Audio:playSfx(name)
	if not self.sfxMute then return self.sfxs[name]:play() end
end
 
function Audio:mute(mode)
	if mode == "bgm" then
		self.bgmMute = true
		if self.bgmChannel then
			self.bgmPos = self.bgmChannel:getPosition()
			self.bgmChannel:stop()
			self.bgmChannel = nil
		end
	elseif mode == "sfx" then
		self.sfxMute = true
	else
		self.bgmMute = true
		if self.bgmChannel then
			self.bgmPos = self.bgmChannel:getPosition()
			self.bgmChannel:stop()
			self.bgmChannel = nil
		end
		self.sfxMute = true
	end
end
function Audio:unmute(mode)
	if mode == "bgm" then
		self.bgmMute = false
		self.bgmChannel = self.bgmCurrent:play(self.bgmPos, math.huge)
	elseif mode == "sfx" then
		self.sfxMute = false
	else
		self.bgmMute = false
		self.bgmChannel = self.bgmCurrent:play(self.bgmPos, math.huge)
		self.sfxMute = false
	end
end
my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories

Comments

  • MoKaLuxMoKaLux Member
    edited September 2021
    to add to my question: I have different levels, in each level I have various monsters with various sound... how do I do?

    EDIT: I ended up using the class in the previous post for the main scenes (menu, options, ...) and vanilla Sound and SoundChannel for the various monsters. It is a bit messy to be honest, I need to rethink how to organise my sounds :#
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • MoKaLuxMoKaLux Member
    edited March 2023
    is this the correct way to avoid "accumulating" sounds and having a crissy sound :s in the speakers?
    self.sndnmeshoot = Sound.new("audio/sfx/sfx_wpn_laser4.wav")
    self.channel = self.sndplayer1hurt:play(0, false, true)
    self.channel2 = self.sndcoin:play(0, false, true)
    self.volume = 0.5 -- 0.01
    ...
    local function dosound(xchannelid, xsound)
    	if xchannelid == 1 then
    		if not self.channel:isPlaying() then self.channel = xsound:play() end
    		self.channel:setVolume(self.volume)
    	elseif xchannelid == 2 then
    		if not self.channel2:isPlaying() then self.channel2 = xsound:play() end
    		self.channel2:setVolume(self.volume)
    	end
    end
    ...
    --self.channel = self.sndplayer1hurt:play()
    dosound(1, self.sndplayer1hurt)
    The problem with this approach is that only one sound will be played at a time by channels (or I can add more channels though) :/
    Thank you :)

    PS: bonus question, how do you initialize a channel without playing the sound, is self.channel2 = self.sndcoin:play(0, false, true) the only way (also channel:setPause(true)?
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • piepie Member
    I am not sure this will still work, but if you want to take a chance I managed to put this thing together years ago from the credited sources, and it WAS working at the time :smile: hope this helps
    --[[
     
     Bismillahirahmanirrahim
     
     Audio control class for Gideros
     by: Edwin Zaniar Putra (zaniar@nightspade.com)
     
     This code is MIT licensed, see <a href="http://www.opensource.org/licenses/mit-license.php" rel="nofollow">http://www.opensource.org/licenses/mit-license.php</a>
     Copyright � 2012 Nightspade (<a href="http://nightspade.com" rel="nofollow">http://nightspade.com</a>).
     
    --]]
     
    --soundExtensions by BowerAndy
     
    SoundChannel.___set=SoundChannel.set
     
    function SoundChannel:set(param, value)
    	if param=="volume" then
    		self:setVolume(value)
    	elseif param=="pitch" then
    		self:setPitch(value)
    	else
    		SoundChannel.___set(self, param, value)
    	end
    	return self
    end
     
    SoundChannel.___get=SoundChannel.get
     
    function SoundChannel:get(param, value)
    	if param=="volume" then
    		return self:getVolume()
    	end
    	if param=="pitch" then
    		return self:getPitch()
    	end
    	return SoundChannel.___get(self, param, value)
    end
     
     
     
    function SoundChannel:fadeIn(duration, optFinalLevel, completionFunc)
    	self:setVolume(0)
    	GTween.new(self, duration, { volume=optFinalLevel or 1 }, { onComplete=completionFunc })
    end
     
    function SoundChannel:fadeOut(duration, optFinalLevel, completionFunc)
    	GTween.new(self, duration, { volume=optFinalLevel or 0 }, { onComplete=
    		function() 
    			self:stop()
    			if completionFunc then completionFunc() end
    		end
    		})
    end
     
    ]]
     
     
     
    Audio = Core.class()
     
    function Audio:init()
    	self.bgmChannel = nil
    	self.bgmCurrentName = nil
    	self.bgmCurrent = nil
    	self.bgmPos = 0
     
    	self.bgmMute = false
    	self.sfxMute = false
     
    	self.bgms = {}
    	self.sfxs = {}
    end
     
    function Audio:setBgms(bgms)
    	self.bgms = bgms
    end
     
    function Audio:setSfxs(sfxs)
    print("sfxs", sfxs)
    	for name, path in pairs(sfxs) do
    		if name and path then
    			self.sfxs[name] = Sound.new(path)
    		end
    	end
    end
     
    function Audio:clearBgms()
    	self.bgms = {}
    end
     
    function Audio:clearSfxs()
    	self.sfxs = {}
    end
     
    function Audio:addBgm(name, path)
    	self.bgms[name] = path
    end
     
    function Audio:addSfx(name, path)
    	self.sfxs[name] = Sound.new(path)
    end
     
    function Audio:removeBgm(name)
    	self.bgms[name] = nil
    end
     
    function Audio:removeSfx(name)
    	self.sfxs[name] = nil
    end
     
    function Audio:playBgm(name, force)
    	if name ~= self.bgmCurrentName or force then
    		if self.bgmChannel then
    			if force == "fadePreviousSound" then
    				self.bgmChannel:fadeOut(3)
    			else
    				self.bgmChannel:stop()
    			end
    			self.bgmChannel = nil
    			self.bgmCurrentName = nil
    			self.bgmCurrent = nil
    			self.bgmPos = 0
    		end
    		if not self.bgmMute and not self.bgmChannel then
    			self.bgmCurrentName = name
    			self.bgmCurrent = Sound.new(self.bgms[name])
    			self.bgmChannel = self.bgmCurrent:play(self.bgmPos, math.huge)
    			if self.bgm_volume then
    				self.bgmChannel:setVolume(self.bgm_volume)
    			end
    		end
    	end
    end
     
    function Audio:playSfx(name)
    	if not self.sfxMute then
    		local sfxReturn = self.sfxs[name]:play()
    	    if self.sfx_volume then
    			sfxReturn:setVolume(self.sfx_volume)
    		end
    		return sfxReturn
    	end
    end
     
     
    function Audio:mute(mode)
    	if mode == "bgm" then
    		self.bgmMute = true
    		if self.bgmChannel then
    			self.bgmPos = self.bgmChannel:getPosition()
    			self.bgmChannel:stop()
    			self.bgmChannel = nil
    		end
    	elseif mode == "sfx" then
    		self.sfxMute = true
    	else
    		self.bgmMute = true
    		if self.bgmChannel then
    			self.bgmPos = self.bgmChannel:getPosition()
    			self.bgmChannel:stop()
    			self.bgmChannel = nil
    		end
    		self.sfxMute = true
    	end
    end
     
    function Audio:unmute(mode)
    	if mode == "bgm" then
    		self.bgmMute = false
    		self.bgmChannel = self.bgmCurrent:play(self.bgmPos, math.huge)
    		self.bgmChannel:setVolume(self.bgm_volume)
    	elseif mode == "sfx" then
    		self.sfxMute = false
    	else
    		self.bgmMute = false
    		self.bgmChannel = self.bgmCurrent:play(self.bgmPos, math.huge)
    		self.sfxMute = false
    	end
    end
     
     
    function Audio:setVolume(volume, channel) --volume from 0 to 1
    --channel: nil = "all"  | "bgm"  bgm only, "sfx"  sfx only
    local ch
    	if not channel then 
    		--all
    		ch = "all"
    	else
    		ch = channel
    	end
     
    	if ch == "all" or ch == "bgm" then	
    		self.bgm_volume = volume
    		if self.bgmChannel then
    			self.bgmChannel:setVolume(self.bgm_volume)
    		end
    	end
     
    	if ch == "all" or ch == "sfx" then	
    		self.sfx_volume = volume
    	end
     
    	if volume == 0 then --if volume = 0 mute
    		self:mute(ch)
    	elseif volume>0 then --else unmute
    		local var = ch.."Mute" --bgmMute or sfxMute
    		if self[var] then --if self.bgmMute or self.sfxMute true unmute
    			self:unmute(ch)
    		end
    	end
    end

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • MoKaLuxMoKaLux Member
    edited March 2023
    cool thank you pie, I may use a mix of fadeIn, fadeOut for bgm but for short wav sounds like shoot, hurt, ... ? When multiple shooting sounds occur they "accumulate" and make my speakers scream :s

    I think I need to arrange/distribute them in a couple of Channels to avoid the screamings.
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • piepie Member
    edited March 2023 Accepted Answer
    @MoKaLux you can't stop the screaming if multiple audio sources overlap: their volume is never handled and can only increase until the screaming occurs.
    The only way would be to have different sounds with different spans of the audio spectre, so that you won't ever overlap the same frequencies (ie the first sound happens between 80 and 500mhz, the second one spans through 501 to 750, the third from 751 to 1000.. but you can't have the same sound played twice because the problem would rise again as soon as the same frequencies overlap and reach the speaker limit).

    But you can trick the listener as you can read here:
    https://www.reddit.com/r/gamedev/comments/dq4k3q/how_do_you_handle_a_bunch_of_sound_effects/

    A slight mod to the provided audio class (assuming it still works) should make it able to check if the sound is already playing and restart it, or do it only if there are already (guessing) 2 instances of the same sfx, or it could lower each sfx volume for the time needed (but I think this option would sound awkward :wink: )

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • also from atilim https://github.com/gideros/gideros/tree/master/samplecode-test/performance-test/v2
    SoundManager = Core.class()
     
    function SoundManager:init()
    	self.sounds = {
    		hit = {sound = Sound.new("sound/hit.wav"), time = 0, delay = 0.05},
    	}
    end
     
    function SoundManager:play(name)	
    	local sound = self.sounds[name]
    	local curr = os.timer()
    	local prev = sound.time
    	if curr - prev > sound.delay then
    		sound.sound:play()
    		sound.time = curr
    	end
    end
     
    soundManager = SoundManager.new()
     
    -- use
    soundManager:play("hit")
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
Sign In or Register to comment.