August 14, 2018, 10:11:29 AM

Author Topic: Guide to playing sounds?  (Read 186 times)

Offline Yellownakji

  • Jr. Member
  • **
  • Posts: 58
Guide to playing sounds?
« on: August 08, 2018, 10:32:45 PM »
So, i've been reading through the manual with no luck...  I could use a pointer.

I have:

MySound:Tsound
MyChannel:Tchannel

I tried loading a .WAV and a .OGG from the root folder as well as loading a .OGG from binary, which is what i need to do in the end.   I did double check and save the bank...  it's a valid OGG.

--

What i did:

MySound = Loadsound(music.wav)

if keyhit(key_4)
MyChannel = Playsound(MySound)
end if

--

Returns invalid or null object exception?

Offline Krischan

  • Full Member
  • ***
  • Posts: 136
    • Krischan's Homepage
Re: Guide to playing sounds?
« Reply #1 on: August 08, 2018, 11:45:11 PM »
Use Cuesound and Resumechannel.

Code: BlitzMax
  1. SuperStrict
  2.  
  3. Import brl.DirectSoundAudio
  4.  
  5. Global VOLUME:Int = 50
  6. Global CHANNEL:TChannel = AllocChannel()
  7.  
  8. Graphics 800, 600
  9.  
  10. Local SAMPLE:TSound = LoadSound("voc/Welcome Commander.ogg")
  11.  
  12. While Not AppTerminate()
  13.  
  14.         If KeyHit(KEY_ESCAPE) Then End
  15.  
  16.         If KeyHit(KEY_4) Then PlaySample(SAMPLE)
  17.        
  18. Wend
  19.  
  20. End
  21.  
  22. Function PlaySample(sample:TSound)
  23.  
  24.         If ChannelPlaying(CHANNEL) Then StopChannel(CHANNEL)
  25.         CHANNEL = CueSound(SAMPLE)
  26.         SetChannelVolume(CHANNEL, VOLUME / 100.0)
  27.         ResumeChannel CHANNEL
  28.  
  29. End Function
Kind regards
Krischan

Windows 10 Pro | iMac 27" Late 2013 [i7 4771 @ 3.5GHz | GTX 780M]
My Blitzbasic Archive | Extrasolar Project

Offline fielder

  • Jr. Member
  • **
  • Posts: 26
Re: Guide to playing sounds?
« Reply #2 on: August 09, 2018, 07:00:43 AM »
MySound = Loadsound(music.wav)
Whatttt? :)

on Windows:  (making WAV inside the EXE when compiled.. better to use OGG)

Code: [Select]
Incbin "music.wav"
SetAudioDriver("DirectSound")
music:TSound=LoadSound("incbin::music.wav")
PlaySound music
delay(5000)

Offline Yellownakji

  • Jr. Member
  • **
  • Posts: 58
Re: Guide to playing sounds?
« Reply #3 on: August 09, 2018, 05:56:05 PM »
MySound = Loadsound(music.wav)
Whatttt? :)

on Windows:  (making WAV inside the EXE when compiled.. better to use OGG)

Code: [Select]
Incbin "music.wav"
SetAudioDriver("DirectSound")
music:TSound=LoadSound("incbin::music.wav")
PlaySound music
delay(5000)


Uhh... no.  I'm not going to INCBIN a WAV.   They're way to big;  I don't want to have it decompress into RAM then have to be loaded again for an Audio.   That's why i made my own binary format to begin with..  |  I am not sure what the "delay" part of the code if for either as i already have my own precise delta timer.

--

Anyways, i didn't get to update yesterday but i managed to work it out.

--

Import BRL.Audio

global MySound:Tsound = Loadsound("music.ogg") 'Actually loads a TBank in my code tho because reading from my binary.
global MyChannel:Tchannel = AllocChannel()  (Thanks Krischan.. didn't realize this bit)
MyChannel = Playsound(Mysound)
Mychannel.setvolume(0.02) '0.02 because this F***** is loud.

--

Thanks.

Offline Derron

  • Hero Member
  • *****
  • Posts: 1163
Re: Guide to playing sounds?
« Reply #4 on: August 09, 2018, 06:26:19 PM »
@ allocchannel

Code: BlitzMax
  1. #PlaySound starts a sound playing through an audio channel.
  2. If no @channel is specified, #PlaySound automatically allocates a channel for you.
  3. end rem
  4. Function PlaySound:TChannel( sound:TSound,channel:TChannel=Null )
  5.         Return sound.Play( channel )
  6. End Function
  7.  

-> AllocChannel creates a channel (if possible)
-> calling "MyChannel = PlaySound(Mysound)" will create another channel
-> calling "PlaySound(Mysound, Mychannel)" will use the "Mychannel" (if not null)


Means: no need to use "AllocChannel" except you want to reuse specific ones (two music channels for crossfading + some sfx channels).



@ volume
Maybe you should consider normalizing your audio files in advance.


bye
Ron

Offline Yellownakji

  • Jr. Member
  • **
  • Posts: 58
Re: Guide to playing sounds?
« Reply #5 on: August 09, 2018, 08:52:13 PM »
Typical Derron..  ::)

Offline Krischan

  • Full Member
  • ***
  • Posts: 136
    • Krischan's Homepage
Re: Guide to playing sounds?
« Reply #6 on: August 09, 2018, 11:55:38 PM »
Let me explain: I had a nasty bug playing queued samples and without the initial Allocchannel for the master Channel the code always ran into a crash later. In the case described here it could be obsolete to use it.
Kind regards
Krischan

Windows 10 Pro | iMac 27" Late 2013 [i7 4771 @ 3.5GHz | GTX 780M]
My Blitzbasic Archive | Extrasolar Project

Offline Derron

  • Hero Member
  • *****
  • Posts: 1163
Re: Guide to playing sounds?
« Reply #7 on: August 10, 2018, 06:35:00 AM »
Typical Derron..  ::)
? I think I was less "elaborative"/"outreaching" than in other cases... so not typical for me ;-)


Let me explain: I had a nasty bug playing queued samples and without the initial Allocchannel for the master Channel the code always ran into a crash later. In the case described here it could be obsolete to use it.
This sounds like a different issue.
As said: "Allocchannel" (tries to) pre-allocates a new TChannel for you. PlaySound() accepts a second (optional) parameter which defines whether to (try to) create a new TChannel or use the given one in the second parameter.

Now let's assume you always do this:
bla = AllocChannel()
sound = PlaySound(file)

This means you always do this:
- try to allocate a channel
- allocate another channel and play a sound on it


I found out some time ago, that there is some kind of "channel limitation", means you cannot eg. play 100 simultaneous things (music + individual explosion sounds of 99 dying space ships after an Area-of-Effect-damage). So maybe your "Alloc Channel" somehow overcomes the issue (returning another channel).
I cannot remember if the application crashed or if other sounds stopped playing or the new sound did not play ... I assume it crashed as I else did not have written a "Channel Pool" taking care of this issue and leading to "stop playing an old sound".

Your "AllocChannel()" might return "null" - did you check for this?


Nonetheless (and months ago I "promoted" it somewhere else already (blitzmax.com?)):
 
Source:
https://github.com/GWRon/Dig/blob/master/base.sfx.channelpool.bmx
Example:
https://github.com/GWRon/Dig/blob/master/samples/channelpool/channelpool.bmx

In essence it does nothing more than "cycling" through a limited amount of channels to avoid reaching the artificial limit for channels (they _should_ be software mixed but hmm hmm). Is the pool-limit reached, it will use one of the previously used ones. The more audio you play, the less it will be recognizeable if one of the channel's audio is stopped/replaced.
 
Benefit of the class is, that you can protect certain channels  (eg. background music, a channel playing a dialogue's voice-over, ...) and you can easily "name" channels ("bullets", "voiceover", ...) as you know best, what sounds can get muted if you play a new one (have "bullets1" and "bullets2" and you achieve to at least hear two different bullets at the same time).


People who had issues with TChannel:
http://mojolabs.nz/posts.php?topic=104159
http://mojolabs.nz/posts.php?topic=103890
http://mojolabs.nz/posts.php?topic=107675


Might not 100% be related to it - but maybe it helps.


bye
Ron

Offline Krischan

  • Full Member
  • ***
  • Posts: 136
    • Krischan's Homepage
Re: Guide to playing sounds?
« Reply #8 on: August 10, 2018, 09:31:33 AM »
I don't want to hijack this thread but the initial question has been answered already, so Derron here is my Samplequeue code. The goal was to load a bunch of Samples in a more human readable way using TMaps with a text key for each sample, add them to a playing queue TList and play them one by one until the queue is empty and the ability to play a "unqueued" sample instantaneously if needed. Keys 1-3 add samples to the queue and SPACE plays the unexpected sample.

It's not the most elegant way to do this but it's working like I need it - as long as I don't uncomment the Allocchannel in the Globals :-) From my point of view I'm always using a single channel only with Cuesound/Resumesound and need to allocate it first or there is no channel at all, right?

Code: BlitzMax
  1. SuperStrict
  2.  
  3. Import brl.DirectSoundAudio
  4.  
  5. ' Types
  6. Global SAMPLE:TSample = New TSample
  7. Global QUEUE:TSamplequeue = New TSamplequeue
  8.  
  9. ' Sound Globals
  10. Global CHANNEL:TChannel = AllocChannel()
  11. Global SAMPLEMAP:TMap = CreateMap()
  12. Global SAMPLEQUEUE:TList = CreateList()
  13. Global VOLUME:Int = 50
  14.  
  15. Graphics 800, 600
  16.  
  17. ' Load and add Samples to TMap
  18. SAMPLE.AddSample(LoadSound("test/Welcome Commander.ogg"), "WELCOME")
  19. SAMPLE.AddSample(LoadSound("test/Scan completed.ogg"), "COMPLETE")
  20. SAMPLE.AddSample(LoadSound("test/Terraformable.ogg"), "TERRAFORM")
  21.  
  22. While Not AppTerminate()
  23.  
  24.         Cls
  25.        
  26.         If KeyHit(KEY_ESCAPE) Then End
  27.        
  28.         ' Add Samples to Queue
  29.         If KeyHit(KEY_1) Then QUEUE.AddQueue("WELCOME")
  30.         If KeyHit(KEY_2) Then QUEUE.AddQueue("COMPLETE")
  31.         If KeyHit(KEY_3) Then QUEUE.AddQueue("TERRAFORM")
  32.  
  33.         If KeyHit(KEY_SPACE) Then QUEUE.PlaySample("WELCOME")
  34.        
  35.         ' queue handling, check for objects and play them one by one
  36.         QUEUE.PlayQueue()
  37.        
  38.         ' output survey
  39.         DrawText "Elements in Queue: " + SAMPLEQUEUE.count(), 0, 0
  40.         Local i:Int = 0
  41.         For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  42.        
  43.                 If i = 0 Then
  44.                
  45.                         DrawText "Playing Sample: " + s.SAMPLE.desc, 0, 15
  46.                        
  47.                 Else
  48.                                
  49.                         DrawText "Queued Sample #" + i + ": " + s.SAMPLE.desc, 0, 15 + (i * 15)
  50.                        
  51.                 EndIf
  52.                
  53.                 i:+1
  54.        
  55.         Next
  56.        
  57.         Flip
  58.        
  59. Wend
  60.  
  61. End
  62.  
  63. ' ----------------------------------------------------------------------------
  64. ' Single Sample
  65. ' ----------------------------------------------------------------------------
  66. Type TSample
  67.  
  68.         Field sound:TSound
  69.         Field desc:String
  70.        
  71.         Method AddSample(sound:TSound, desc:String)
  72.        
  73.                 Local s:TSample = New TSample
  74.                 s.sound = sound
  75.                 s.desc = desc
  76.                
  77.                 MapInsert(SAMPLEMAP, desc, s)
  78.        
  79.         End Method
  80.  
  81. End Type
  82.  
  83.  
  84. ' ----------------------------------------------------------------------------
  85. ' Queued Sample Type
  86. ' ----------------------------------------------------------------------------
  87. Type TSampleQueue
  88.  
  89.         Field sample:TSample
  90.         Field active:Int
  91.                
  92.  
  93.         ' ----------------------------------------------------------------------------
  94.         ' Adds a sample to the Sample playing Queue
  95.         ' ----------------------------------------------------------------------------
  96.         Method AddQueue(desc:String)
  97.        
  98.                 Local sample:TSample = TSample(MapValueForKey(SAMPLEMAP, desc))
  99.                
  100.                 If TSample(sample) Then
  101.                
  102.                         Local s:TSampleQueue = New TSampleQueue
  103.                         s.sample = sample
  104.                        
  105.                         ListAddLast(SAMPLEQUEUE, s)
  106.                        
  107.                         Print "Sample added to Queue: " + desc
  108.                        
  109.                 EndIf
  110.        
  111.         End Method
  112.                
  113.         ' ----------------------------------------------------------------------------
  114.         ' Plays the Sample Queue
  115.         ' ----------------------------------------------------------------------------
  116.         Method PlayQueue()
  117.        
  118.                 For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  119.                                
  120.                         If TSample(s.sample) Then
  121.                
  122.                                 If (Not ChannelPlaying(CHANNEL)) And s.active = False Then
  123.                                
  124.                                         s.active = True
  125.                                         PlayQueueSample(s.sample)
  126.                                        
  127.                                 Else If (Not ChannelPlaying(CHANNEL)) And s.active = True Then
  128.                                
  129.                                         ListRemove(SAMPLEQUEUE, s)
  130.                                         s = Null
  131.                
  132.                                 EndIf
  133.                                
  134.                         EndIf
  135.                        
  136.                 Next
  137.        
  138.         End Method
  139.  
  140.  
  141.         ' ----------------------------------------------------------------------------
  142.         ' plays a single sample on demand, instanteneous
  143.         ' ----------------------------------------------------------------------------
  144.         Method PlaySample(desc:String)
  145.        
  146.                 Local s:TSample = TSample(MapValueForKey(SAMPLEMAP, desc))
  147.                
  148.                 If TSample(s) Then
  149.                
  150.                         Print "Play Single Sample: " + desc
  151.                
  152.                         If ChannelPlaying(CHANNEL) Then StopChannel(CHANNEL)
  153.                        
  154.                         CHANNEL = CueSound(s.sound)
  155.                        
  156.                         ' set channelvolume
  157.                         SetChannelVolume(CHANNEL, VOLUME / 100.0)
  158.                         ResumeChannel CHANNEL
  159.                        
  160.                 EndIf
  161.                        
  162.         End Method
  163.  
  164.                
  165.                
  166.         ' ----------------------------------------------------------------------------
  167.         ' plays a single queue sample
  168.         ' ----------------------------------------------------------------------------
  169.         Method PlayQueueSample(s:TSample)
  170.        
  171.                 Print "Play Queue Sample: " + s.desc
  172.                
  173.                 If ChannelPlaying(CHANNEL) Then StopChannel(CHANNEL)
  174.                
  175.                 CHANNEL = CueSound(s.sound)
  176.                
  177.                 ' set channelvolume
  178.                 SetChannelVolume(CHANNEL, VOLUME / 100.0)
  179.                 ResumeChannel CHANNEL
  180.                
  181.         End Method
  182.                
  183. End Type
Kind regards
Krischan

Windows 10 Pro | iMac 27" Late 2013 [i7 4771 @ 3.5GHz | GTX 780M]
My Blitzbasic Archive | Extrasolar Project

Offline Derron

  • Hero Member
  • *****
  • Posts: 1163
Re: Guide to playing sounds?
« Reply #9 on: August 10, 2018, 10:11:19 AM »
You do not hijack the thread - as you seem (at least in my opinion) to do it wrong as yellownakji does.


Code: BlitzMax
  1. Rem
  2. bbdoc: Cue a sound
  3. returns: An audio channel object
  4. about:
  5. Prepares a sound for playback through an audio channel.
  6. To actually start the sound, you must use #ResumeChannel.
  7. If no @channel is specified, #CueSound automatically allocates a channel for you.
  8. #CueSound allows you to setup various audio channel states such as volume, pan, depth
  9. and rate before a sound actually starts playing.
  10. End Rem
  11. Function CueSound:TChannel( sound:TSound,channel:TChannel=Null )
  12.         Return sound.Cue( channel )
  13. End Function
  14.  

You have a global "CHANNEL:TChannel". You check if that plays and stop that if it is playing.
Then you use "CueSound" without passing a "channel to use" in the second param. So "CueSound" creates a _new_ channel and returns this then. So "CHANNEL = CueSound(bla)" assigns a _new_ channel to the global one.

You will not hear differences, as you always stop a previously playing channel. Nonetheless you create a new channel each time.

You could easily append "CHANNEL" to the CueSound/PlaySound ... commands to stay with the exact same channel. "CueSound(sound, CHANNEL)"

If you do not believe me: you could always print the memory address of CHANNEL by
print string(CHANNEL)
or
print CHANNEL.ToString()

If it was the same CHANNEL each time, the memory address wont change, else it will change on each "CueSound()".


Your "Queue" might have a use - but most of the time you want an immediate sound effect ("explosion") not an "wait until explosion 1 finished"-thing.
What happens if you added a repeating-sound? the channel will never be stopping.


Side annotation:
Code: BlitzMax
  1.                For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  2.                                
  3.                         If TSample(s.sample) Then
  4.                
  5.                                 If (Not ChannelPlaying(CHANNEL)) And s.active = False Then
  6.                                
  7.                                         s.active = True
  8.                                         PlayQueueSample(s.sample)
  9.                                        
  10.                                 Else If (Not ChannelPlaying(CHANNEL)) And s.active = True Then
  11.                                
  12.                                         ListRemove(SAMPLEQUEUE, s)
  13.                                         s = Null
  14.                
  15.                                 EndIf
  16.                                
  17.                         EndIf
  18.                 Next
  19.  
- you do 2 times check if the channel is active
- once you play a sample you continue checking others in the queue
- you set "s = Null" - a local variable which is no longer used in that for-loop
- active is set to true, but never to false!?


so it could be shortened to:
Code: BlitzMax
  1.                If not ChannelPlaying(CHANNEL)
  2.                         For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  3.                                  If not s.sample then continue 'skip
  4.                                  If s.active = False
  5.                                         s.active = True
  6.                                         PlayQueueSample(s.sample)
  7.                                 Else
  8.                                         ListRemove(SAMPLEQUEUE, s)
  9.                                 EndIf
  10.                                 exit 'handled, finish the for-loop
  11.                         Next
  12.                Endif
  13.  
Now this exposes that your code does something wrong - or misses to do something.
- if the channel is not playing then "ListRemove" is called on each PlayQueue() command
- active stays "true" all the time


Either you truncated your code - or some parts do not work as intented (or just "by luck") - or I am not understanding what is intented to happen.


bye
Ron

Offline Krischan

  • Full Member
  • ***
  • Posts: 136
    • Krischan's Homepage
Re: Guide to playing sounds?
« Reply #10 on: August 10, 2018, 10:49:06 AM »
Thanks Derron - I've missed the Cuesound Channel option, damn. Like I said it isn't the most elegant solution but it worked and I had a lot of problems to get it running - I couldn't find any example like this so I had to try it on my own - how would you solve the quest?

It's a tool for Explorers in Elite Dangerous which adds a voice assistant by scanning the game's Journal logs and gives audible feedback to the player. In my use case I need to play Samples in a Queue (or call it a stack) as new events can happen while a sample is playing and I need to queue them that no event gets missing in the meantime which is different from a game with shooting sounds and explosions. I've split up the spoken text in different elements to re-arrange the parts later using this method. But I don't have looped samples there so this is not a problem.

The nullified s was garbage I forgot to clean.  I've added your suggestions and split the channel in two to play the non-queued samples in a second channel (for example a click sound when you press a button).

Code: BlitzMax
  1. SuperStrict
  2.  
  3. Import brl.DirectSoundAudio
  4.  
  5. ' Types
  6. Global SAMPLE:TSample = New TSample
  7. Global QUEUE:TSamplequeue = New TSamplequeue
  8.  
  9. ' Sound Globals
  10. Global MASTERCHANNEL:TChannel = AllocChannel()
  11. Global QUEUECHANNEL:TChannel = AllocChannel()
  12. Global SAMPLEMAP:TMap = CreateMap()
  13. Global SAMPLEQUEUE:TList = CreateList()
  14. Global MASTERVOLUME:Int = 100
  15. Global QUEUEVOLUME:Int = 50
  16.  
  17. Graphics 800, 600
  18.  
  19. ' Load and add Samples to TMap
  20. SAMPLE.AddSample(LoadSound("test/Welcome Commander.ogg"), "WELCOME")
  21. SAMPLE.AddSample(LoadSound("test/Scan completed.ogg"), "COMPLETE")
  22. SAMPLE.AddSample(LoadSound("test/Terraformable.ogg"), "TERRAFORM")
  23. SAMPLE.AddSample(LoadSound("test/Click.ogg"), "CLICK")
  24.  
  25. While Not AppTerminate()
  26.  
  27.         Cls
  28.  
  29.         If KeyHit(KEY_ESCAPE) Then End
  30.  
  31.         ' Add Samples to Queue
  32.         If KeyHit(KEY_1) Then QUEUE.AddQueue("WELCOME")
  33.         If KeyHit(KEY_2) Then QUEUE.AddQueue("COMPLETE")
  34.         If KeyHit(KEY_3) Then QUEUE.AddQueue("TERRAFORM")
  35.  
  36.         If KeyHit(KEY_SPACE) Then QUEUE.PlaySample("CLICK")
  37.  
  38.         ' queue handling, check for objects and play them one by one
  39.         QUEUE.PlayQueue()
  40.  
  41.         ' output survey
  42.         DrawText "Elements in Queue: " + SAMPLEQUEUE.count(), 0, 0
  43.         Local i:Int = 0
  44.         For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  45.  
  46.                 If i = 0 Then
  47.  
  48.                         DrawText "Playing Sample: " + s.SAMPLE.desc, 0, 15
  49.  
  50.                 Else
  51.  
  52.                         DrawText "Queued Sample #" + i + ": " + s.SAMPLE.desc, 0, 15 + (i * 15)
  53.  
  54.                 EndIf
  55.  
  56.                 i:+1
  57.  
  58.         Next
  59.  
  60.         Flip
  61.  
  62. Wend
  63.  
  64. End
  65.  
  66. ' ----------------------------------------------------------------------------
  67. ' Single Sample
  68. ' ----------------------------------------------------------------------------
  69. Type TSample
  70.  
  71.         Field sound:TSound
  72.         Field desc:String
  73.  
  74.         Method AddSample(sound:TSound, desc:String)
  75.  
  76.                 Local s:TSample = New TSample
  77.                 s.sound = sound
  78.                 s.desc = desc
  79.  
  80.                 MapInsert(SAMPLEMAP, desc, s)
  81.  
  82.         End Method
  83.  
  84. End Type
  85.  
  86.  
  87. ' ----------------------------------------------------------------------------
  88. ' Queued Sample Type
  89. ' ----------------------------------------------------------------------------
  90. Type TSampleQueue
  91.  
  92.         Field sample:TSample
  93.         Field active:Int
  94.  
  95.  
  96.         ' ----------------------------------------------------------------------------
  97.         ' Adds a sample to the Sample playing Queue
  98.         ' ----------------------------------------------------------------------------
  99.         Method AddQueue(desc:String)
  100.  
  101.                 Local sample:TSample = TSample(MapValueForKey(SAMPLEMAP, desc))
  102.  
  103.                 If TSample(SAMPLE) Then
  104.  
  105.                         Local s:TSampleQueue = New TSampleQueue
  106.                         s.SAMPLE = SAMPLE
  107.  
  108.                         ListAddLast(SAMPLEQUEUE, s)
  109.  
  110.                         Print "Sample added to Queue: " + desc
  111.  
  112.                 EndIf
  113.  
  114.         End Method
  115.  
  116.         ' ----------------------------------------------------------------------------
  117.         ' Plays the Sample Queue
  118.         ' ----------------------------------------------------------------------------
  119.         Method PlayQueue()
  120.  
  121.                 If Not ChannelPlaying(QUEUECHANNEL) Then
  122.                
  123.                         For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  124.                        
  125.                                 If Not s.SAMPLE Then Continue
  126.                                
  127.                                 If s.active = False Then
  128.  
  129.                                         s.active = True
  130.                                        
  131.                                         PlayQueueSample(s.SAMPLE)
  132.  
  133.                                 Else
  134.                                
  135.                                         ListRemove(SAMPLEQUEUE, s)
  136.  
  137.                                 EndIf
  138.  
  139.                                 Exit
  140.  
  141.                         Next
  142.                        
  143.                 EndIf
  144.  
  145.         End Method
  146.  
  147.  
  148.         ' ----------------------------------------------------------------------------
  149.         ' plays a single sample on demand, instanteneous
  150.         ' ----------------------------------------------------------------------------
  151.         Method PlaySample(desc:String)
  152.  
  153.                 Local s:TSample = TSample(MapValueForKey(SAMPLEMAP, desc))
  154.  
  155.                 If TSample(s) Then
  156.  
  157.                         Print "Play Single Sample: " + desc
  158.  
  159.                         If ChannelPlaying(MASTERCHANNEL) Then StopChannel(MASTERCHANNEL)
  160.  
  161.                         CueSound(s.sound, MASTERCHANNEL)
  162.  
  163.                         ' set channelvolume
  164.                         SetChannelVolume(MASTERCHANNEL, MASTERVOLUME / 100.0)
  165.                         ResumeChannel MASTERCHANNEL
  166.  
  167.                 EndIf
  168.  
  169.         End Method
  170.  
  171.  
  172.  
  173.         ' ----------------------------------------------------------------------------
  174.         ' plays a single queue sample
  175.         ' ----------------------------------------------------------------------------
  176.         Method PlayQueueSample(s:TSample)
  177.  
  178.                 Print "Play Queue Sample: " + s.desc
  179.  
  180.                 If ChannelPlaying(QUEUECHANNEL) Then StopChannel(QUEUECHANNEL)
  181.  
  182.                 CueSound(s.sound, QUEUECHANNEL)
  183.  
  184.                 ' set channelvolume
  185.                 SetChannelVolume(QUEUECHANNEL, QUEUEVOLUME / 100.0)
  186.                 ResumeChannel QUEUECHANNEL
  187.  
  188.         End Method
  189.  
  190. End Type
Kind regards
Krischan

Windows 10 Pro | iMac 27" Late 2013 [i7 4771 @ 3.5GHz | GTX 780M]
My Blitzbasic Archive | Extrasolar Project

Offline Derron

  • Hero Member
  • *****
  • Posts: 1163
Re: Guide to playing sounds?
« Reply #11 on: August 10, 2018, 11:07:43 AM »
Pay attention to "concurrent list modification" - not an issue for everybody, but it happened multiple times in my game TVTower, so Brucey gave me a little clap ... and I created an array holding "no longer valid objects" which I then remove _after_ iterating over the list.
Why? we discussed this here in the forums already - and BlitzMax NG TList already contains some fixes but for vanilla it might happen that if you iterate over a list - and then remove something within this iteration, then it might skip items as the iterator borks up.
In our case here, this means that it might skip a valid entry (it removed one, iterator borks up and jumps over the "previously next item").


Also you have it defined as "method" while you only access external variables ... so it is a "function" ...
if you used it as "method" you could have "QUEUECHANNEL" and "SAMPLEQUEUES" be fields/globals of the class.
TSampleQueue.queues is then a list containing all "TSampleQueue" instances and so on.

Means instead of "SAMPLEQUEUES:TList" you would access "TSampleQueue.queues".
PlayQueue would need to run the current "default queue" - or you need to pass it what queue to handle...
... but to not overly complicate stuff for now I will concentrate on the content of "PlayQueue":

Code: BlitzMax
  1.        Method PlayQueue()
  2.                 If ChannelPlaying(QUEUECHANNEL) Then return 'nothing to do
  3.                
  4.                 'remove old queue items
  5.                 local toRemove:TSampleQueue[]
  6.                 For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  7.                         If s.active then toRemove :+ [s]
  8.                 Next
  9.                 For Local s:TSampleQueue = EachIn toRemove
  10.                         SAMPLEQUEUE.Remove(s)
  11.                 Next
  12.              
  13.                 'enqueue next possible sample in the queue
  14.                 For Local s:TSampleQueue = EachIn SAMPLEQUEUE
  15.                         If Not s.SAMPLE Then Continue
  16.                                
  17.                         PlayQueueSample(s.SAMPLE)
  18.                         s.active = True
  19.                         Exit
  20.                 Next
  21.         End Method
  22.  

And it might even be better to have "s.active = True" in the method actually playing "s". That way you ensure to have it "true" as soon as it get played, not just when "PlayQueue" handles it.


bye
Ron