Question about GetPoolChannel issues

BlitzMax Forums/BlitzMax Beginners Area/Question about GetPoolChannel issues

Takis76(Posted 2015) [#1]
Hello,

I am using the PoolChannel functions for my sound of my game.
It was found from here:
https://github.com/GWRon/Dig/blob/master/base.sfx.channelpool.bmx

I have some issues , some times some sounds doesn't play.
for example I use some sound for my interface clicks and if I hit the buttons or play some sound multiple times the sound doesn't play or doesn't sounds well.
I tried to do some trick, to preload the sounds and play them before the game begins because when I tried to play some sound for the first time and when I tried to play another one the previously sound was stopped.

{code}
Function Init_Sounds_Music()

'PREPARE SOUNDS
If Not GetPooledChannel("Music01").Playing() Then PlaySound(Music01, GetPooledChannel("Music01")) GetPooledChannel("Music01").SetVolume(0)
If Not GetPooledChannel("Music02").Playing() Then PlaySound(Music02, GetPooledChannel("Music02")) GetPooledChannel("Music02").SetVolume(0)
If Not GetPooledChannel("Music03").Playing() Then PlaySound(Music03, GetPooledChannel("Music03")) GetPooledChannel("Music03").SetVolume(0)

If Not GetPooledChannel("Inventory_Left").Playing() Then PlaySound(Inventory_Left, GetPooledChannel("Inventory_Left")) GetPooledChannel("Inventory_Left").SetVolume(0)
'If Not GetPooledChannel("Inventory_Left1").Playing() Then PlaySound(Inventory_Left, GetPooledChannel("Inventory_Left1")) GetPooledChannel("Inventory_Left1").SetVolume(0)
'If Not GetPooledChannel("Inventory_Left2").Playing() Then PlaySound(Inventory_Left, GetPooledChannel("Inventory_Left2")) GetPooledChannel("Inventory_Left2").SetVolume(0)
'If Not GetPooledChannel("Inventory_Right1").Playing() Then PlaySound(Inventory_Right, GetPooledChannel("Inventory_Right1")) GetPooledChannel("Inventory_Right1").SetVolume(0)
'If Not GetPooledChannel("Inventory_Right2").Playing() Then PlaySound(Inventory_Right, GetPooledChannel("Inventory_Right2")) GetPooledChannel("Inventory_Right2").SetVolume(0)

If Not GetPooledChannel("Step01a").Playing() Then PlaySound(Step01a, GetPooledChannel("Step01a")) GetPooledChannel("Step01a").SetVolume(0)
If Not GetPooledChannel("Step01b").Playing() Then PlaySound(Step01b, GetPooledChannel("Step01b")) GetPooledChannel("Step01b").SetVolume(0)

If Not GetPooledChannel("Wall_Hit1").Playing() Then PlaySound(Wall_Hit1, GetPooledChannel("Wall_Hit1")) GetPooledChannel("Wall_Hit1").SetVolume(0)

If Not GetPooledChannel("Sound_Bell_1").Playing() Then PlaySound(Bell1, GetPooledChannel("Sound_Bell_1")) GetPooledChannel("Sound_Bell_1").SetVolume(0)
If Not GetPooledChannel("Sound_Bell_2").Playing() Then PlaySound(Bell2, GetPooledChannel("Sound_Bell_2")) GetPooledChannel("Sound_Bell_2").SetVolume(0)
If Not GetPooledChannel("Sound_Bell_3").Playing() Then PlaySound(Bell3, GetPooledChannel("Sound_Bell_3")) GetPooledChannel("Sound_Bell_3").SetVolume(0)
If Not GetPooledChannel("Sound_Bell_4").Playing() Then PlaySound(Bell4, GetPooledChannel("Sound_Bell_4")) GetPooledChannel("Sound_Bell_4").SetVolume(0)
If Not GetPooledChannel("Sound_Bell_5").Playing() Then PlaySound(Bell5, GetPooledChannel("Sound_Bell_5")) GetPooledChannel("Sound_Bell_5").SetVolume(0)
If Not GetPooledChannel("Sound_Bell_6").Playing() Then PlaySound(Bell6, GetPooledChannel("Sound_Bell_6")) GetPooledChannel("Sound_Bell_6").SetVolume(0)

For All_Levels = 1 To Max_Levels

For All_Doors = 1 To Max_Doors
If Not GetPooledChannel("door[" + All_Levels + "," + Doors[All_Levels, All_Doors].x + "," + Doors[All_Levels, All_Doors].y + "]") Then
PlaySound(Door_Opening, GetPooledChannel("door[" + All_Levels + "," + Doors[All_Levels, All_Doors].x + "," + Doors[All_Levels, All_Doors].y + "]"))
GetPooledChannel("door[" + All_Levels + "," + Doors[All_Levels, All_Doors].x + "," + Doors[All_Levels, All_Doors].y + "]").SetVolume(0)
EndIf
Next
Next
If Not GetPooledChannel("Generic_Clink").Playing() Then PlaySound(Generic_Clink, GetPooledChannel("Generic_Clink")) GetPooledChannel("Generic_Clink").SetVolume(0)
'==================================================================================================================================================

'Delay(1000)
'STOP SOUNDS
GetPooledChannel("Music01").Stop()
GetPooledChannel("Music02").Stop()
GetPooledChannel("Music03").Stop()

GetPooledChannel("Inventory_Left").Stop()
'GetPooledChannel("Inventory_Left1").Stop()
'GetPooledChannel("Inventory_Left2").Stop()
'GetPooledChannel("Inventory_Right1").Stop()
'GetPooledChannel("Inventory_Right2").Stop()

GetPooledChannel("Step01a").Stop()
GetPooledChannel("Step01b").Stop()

GetPooledChannel("Wall_Hit1").Stop()

GetPooledChannel("Sound_Bell_1").Stop()
GetPooledChannel("Sound_Bell_2").Stop()
GetPooledChannel("Sound_Bell_3").Stop()
GetPooledChannel("Sound_Bell_4").Stop()
GetPooledChannel("Sound_Bell_5").Stop()
GetPooledChannel("Sound_Bell_6").Stop()

For All_Levels = 1 To Max_Levels
For All_Doors = 1 To Max_Doors
GetPooledChannel("door[" + All_Levels + "," + Doors[All_Levels, All_Doors].x + "," + Doors[All_Levels, All_Doors].y + "]").Stop()
Next
Next
GetPooledChannel("Generic_Clink").Stop()
'==================================================================================================================================================


'NORMALIZE SOUNDS
GetPooledChannel("Music01").SetVolume(1)
GetPooledChannel("Music02").SetVolume(1)
GetPooledChannel("Music03").SetVolume(1)

GetPooledChannel("Inventory_Left").SetVolume(1)
'GetPooledChannel("Inventory_Left1").SetVolume(1)
'GetPooledChannel("Inventory_Left2").SetVolume(1)
'GetPooledChannel("Inventory_Right1").SetVolume(1)
'GetPooledChannel("Inventory_Right2").SetVolume(1)

GetPooledChannel("Step01a").SetVolume(1)
GetPooledChannel("Step01b").SetVolume(1)

GetPooledChannel("Wall_Hit1").SetVolume(1)

GetPooledChannel("Sound_Bell_1").SetVolume(1)
GetPooledChannel("Sound_Bell_2").SetVolume(1)
GetPooledChannel("Sound_Bell_3").SetVolume(1)
GetPooledChannel("Sound_Bell_4").SetVolume(1)
GetPooledChannel("Sound_Bell_5").SetVolume(1)
GetPooledChannel("Sound_Bell_6").SetVolume(1)

For All_Levels = 1 To Max_Levels
For All_Doors = 1 To Max_Doors
GetPooledChannel("door[" + All_Levels + "," + Doors[All_Levels, All_Doors].x + "," + Doors[All_Levels, All_Doors].y + "]").SetVolume(1)
Next
Next
GetPooledChannel("Generic_Clink").SetVolume(1)
'==================================================================================================================================================
End Function
{/code}

The code above plays all sounds of the game and set the volume to 0
Then stop the sound. Then I set the sound volume back to 1.

After this when I started the game , the sounds which played doesn't stop , but when I added even more sounds the older sounds now don't play.

Is it possible to find out how to know if some sound is not playing to be removed to have free channel and do not cause my sounds not playing.

I also tried and TChannelPool.channelLimit = 32

any idea?

Thank you :)


Derron(Posted 2015) [#2]
First of all: use [ ] for code blocks, not { } (this would be seen when reviewing your post before pressing the [Post]-Button).


What are you doing here:
If Not GetPooledChannel("Sound_Bell_1").Playing() Then PlaySound(Bell1, GetPooledChannel("Sound_Bell_1")) GetPooledChannel("Sound_Bell_1").SetVolume(0)


if not playing "sound_bell_1" then start playing it AND set volume to 0.
I think you could skip playing that sound at all.

THIS does not preload (predecode) the sounds, this is done via "LoadSound(url)".


I think you try to pre-allocate channels for the sound - but this is why we have the pool, what if you cannot pre-allocate enough channels? This is why the pool-principle was introduced.


The whole class does not manage "freeing channels" or other things, it just adds some convenience regarding managing various channels. If you remove a channel from the pool, it is up to you to "channel.Stop()" etc. else the channel will keep playing the sounds if they were set to be repeating forever.



@channelLimit
it just limits the amount of simultaneous managed channels. If you try to get the "33th" channel, it returns a prior one - and if you start another sound in it, it will NOT stop the other sound playing in this channel, it will add it to that channel (I think it behaves that way, so I am nur sure about it.
Of course it is up to you to just create a channel for each "sound". With "channelLimit = 32" you could play up to 32 sounds at the same time (bgmusic, door sfx, monster cry, spell sfx, ... I cannot imagine enough to fill the 32 slots for a SIMULTANEOUS usage)



Ok, so what to check for you:
Just experiment with ONE 1-2 channels to get a feel how to work with it (setting new sounds etc).
Remove (or comment out) that whole "getChannel(bla);PlaySound(blubb, bla);getChannel(bla).setVolume(0)" and the same for the "Stop(), setVolume(1)" part.

Then use "CueSound" if you want to "prepare" every channel to start playing. CueSound() instead of PlaySound() is like putting the sound into a channel, and pressing "stop".

I do not know if that is all even needed. In my game we just "playsound" (+ some wrappings) and use the appropiate channel for that kind of sound (to avoid eg. 20 different gui sounds to play simultaneously).

So in your case the easiest way to play a door sound is:

PlaySound(myDoorsound, GetPooledChannel("door_1"))

And if you play with volume, panning in GENERAL!
GetPooledChannel("door_1").SetVolume(1)
GetPooledChannel("door_1").SetPan(0)
PlaySound(myDoorsound, GetPooledChannel("door_1"))

or using caches:
local c:TChannel = GetPooledChannel("door_1")
c.SetVolume(1); c.SetPan(0)
PlaySound(myDoorsound, c)


Why?
Because as soon as you limit the channels, you will reuse existing channels. And existing channels might have adjusted settings (volume, pan).



bye
Ron


Takis76(Posted 2015) [#3]
I used TChannelPool.GetChannelCount(True) and I found I occupy 17 channels for NOW.

And then I used TChannelPool.ProtectChannel("bluh key") for all of my sounds and for now all sound issues solved. But I am experimenting and I am not sure what will happen later on. SO, I am not sure if the Protected Channels corrected whole problem.

I increased the channel limit to 128. All modern sound blaster and on board sound cards supports at least up to 128 channels.
For now all static not changing sounds , backgrounds , interface sounds and repeatable sounds that do not need to be panned are in protected mode. the other 3D sounds from doors (According to example you sent me in previous post and other dungeon objects which changes dynamically) I left them to unprotected mode.

So far so good and I am thanking your again for this nice collection of sound functions.
:)


First of all: use [ ] for code blocks, not { }


I was using square brackets for my code before and you told me to use curly brackets and I was changed to curly. You said to use {codebox} instead of {code} I used to use [code] and now I used {code}. what is the difference between {code} and [code]? You said the {codebox} activates horizontal and vertical scroll bars for huge code.


GfK(Posted 2015) [#4]
always use square brackets.

The CODE tag creates a lump of code on a black background, which extends to fit the code. Good for small amounts of code.

The CODEBOX tax does the same, but the height is restricted and scrollbars are added - better for posting lots of code.


Derron(Posted 2015) [#5]
I wrote in "{codebox}" etc to avoid the forum turning it into something ... so when I write "{b}boldtext{/b}" this keeps the way it is, replacing it with [ and ] the result is "boldtext".

Sorry for the confusion I brought up.


@ProtectChannel()
Like I hope to have explained already: if you limit the channels somehow and you request a new channel which would exceed that limit, an existing channel is returned instead of a new one.
For channels containing things running forever or channels which should not get suddenly stopped (eg. speech of a dialogue) then you should "protect" that channel.

For normal SFX (opening a door) you should not protect, because it is not that bad if another sound is played (imagine dozens of glasses falling down).

If you do not enounter problems, do not use the channelLimit at all, set it to -1 and it should create channels without limitation.
If you then encounter problems with your sound card, you reached its limit and should think about setting a channelLimit.

Really think about adding channels for each object VERSUS having channels for specific types. If you only have 1 background music playing at the same time, then only have a channel "backgroundmusic" instead of channels for each track (bgmusic1, bgmusic2 ...).
If you want "cross fading" then have two backgroundmusic-channels (lowering volume on A and raising on 2 will crossfade).


bye
Ron


Takis76(Posted 2015) [#6]
When I am changing levels , for example if the party reach a staircase and changing level , I am fading out the current background music until reaches the setvolume(0) and then stop that music and after the party arrived to next level , I have one generic routine which checks if the level was changed and a new single background music is starting.

I post this code here for idea for games when you are changing levels.
Now I will use {codebox} :P

This code checks what music you will play for each level



The code to fade out the sound when you are changing level:



Note1 {codebox} with curly brackets doesn't work. After Previewing post.
Note2 The sound files are missing from this post you can put your own.
The above code is just an idea for a game and fades the music during changing levels. It works perfectly in my game. (Not cross fade).


Derron(Posted 2015) [#7]
As long as it works enjoy your work.

The more complex sceneries get, the more you will like generic functions doing things for you.

Eg. "crossfading".

[I skipped a bit of this posts content as this portion repeatingly crashed the blitzmax-servers and even was not "preview"-able - that is why the server was down for some time and only was up for some minutes before I tried to post my reply again]

Idea is:
- Crossfader-Type has method "PlayNext(audio, crossfadingDuration)"
- when calling this method:
- - a timer/millisecs()-storage is set so you know the "starting point"
- - the "audio" file is stored in the non-active channel of the two ones
- - if there is no active channel, the non-active channel gets the active one (maybe only fade in?)
- - start playing the channel of the audio file
- each time you update() the crossfader (should be done each "tick" then) you lower the volume of the active channel ( 1.0 minus (currentTime-startTime)/Duration = progress between 0.0 and 1.0). Raise the volume by progress (which is then 0.0 - 1.0).

It is pretty simple and straightforward. Most complex part is surely the switch "inactive" to "active" channel (to allow playing a new audio while crossfading is still in progress).


bye
Ron