music
BlitzMax Forums/BlitzMax Programming/music
| ||
| Hi... need some help with bmax music. I'd like to play music in my game but handle it properly. Do I need a channel? whats the best way to go about it? any tips and please share your own ways of doing this. I ask because the docs are very open ended, they also confuse me. Thanks! |
| ||
I use the following code for music, loops, one shot and sfx queuing. Hope it helps. It based on some old code that I think was written by Eikon.
DebugLog "MOD_SFX_Engine.bmx"
Const CH_ANY = -1
Const CH_1 = 1 'Brick Sounds
Const CH_2 = 2 'Score Sounds
Const CH_3 = 3 'Powerup Sounds
Const CH_4 = 4 'Incidential Sounds
Const CH_5 = 5
Const CH_6 = 6 ' Diamond Sounds
Const CH_7 = 7 ' Chords
Const CH_8 = 8 ' Vox
Const CH_9 = 9 ' GUI Sounds
Const CH_10 = 10 ' BG Music
Const CH_11 = 11 ' Score Sounds
' ---------------------------------------------------------------------
Global ChannelTList:TList = New TList
Type ActiveChannel
Field SampleID :Int
Field Address :TChannel
Field FadeTime :Double
Field FadeStart :Double
Field CreateTime:Int
Field Volume :Double
Field VolumeS :Double
Field Queue :TSound[8]
Field VolQ :Double[8]
Field Qcount :Double
Method New ()
If ChannelTList = Null Then ChannelTList = New TList
ChannelTList.AddLast Self
End Method
'----------------------------------------------------------------
Function Destroy (a:ActiveChannel)
Local b:Int
a.Address = Null
For b = 0 To 7
a.Queue[b] = Null
Next
ChannelTList.Remove a
a = Null
End Function
'----------------------------------------------------------------
Function MusicQueueState(ID:Int)
Local c:ActiveChannel
For c:ActiveChannel = EachIn ChannelTList
If c.SampleID = ID Then Return Int(c.QCount)
Next
End Function
'----------------------------------------------------------------
Function PlaySample(add:TSound,ID:Int=-1,Vol:Double=1,Queue:Byte=False,overlap:Byte=False,rate:Double=1)
Local Channel:ActiveChannel
Local Simultaneous:Byte = False
Local Playing:Byte = False
If ID <0 Then Simultaneous = True
For Channel:ActiveChannel = EachIn ChannelTList
If Channel.SampleID = ID
If Queue = True
Channel.Qcount = Channel.Qcount + 1
If Channel.Qcount > 1 Then
Channel.QCount = 1
DebugLog "**SFX Queue Overload!"
Return
EndIf
Channel.Queue[Channel.Qcount] = add
Channel.VolQ[Channel.Qcount] = vol
Playing = True
Else
Playing = True
EndIf
If overlap And (MilliSecs() - Channel.Createtime > 100) Then Playing = False
EndIf
If Not ChannelPlaying(Channel.Address) Then
StopChannel(Channel.Address)
Destroy(Channel)
EndIf
Next
If Not (Simultaneous = False And Playing = True)
Channel:ActiveChannel = New ActiveChannel
Channel.SampleID = ID
Channel.Address = AllocChannel()
Channel.Volume = Vol
Channel.VolumeS = Vol
SetChannelVolume(Channel.Address,Channel.Volume)
PlaySound(Add,Channel.Address)
Channel.FadeTime = 0
Channel.CreateTime = MilliSecs()
SetChannelRate(Channel.Address,rate)
EndIf
End Function
'----------------------------------------------------------------
Method Update()
Local a:Int
If Not ChannelPlaying(self.Address)
If self.Qcount = 0
StopChannel(self.address)
Destroy(Self)
Return
Else
SetChannelVolume(self.address,self.volQ[1])
PlaySound(self.queue[1],self.address)
For a = 1 To self.qCount - 1
self.queue[a] = self.queue[a+1]
self.volQ[a] = self.volQ[a+1]
Next
self.Queue[self.Qcount] = Null
self.Qcount = self.Qcount - 1
EndIf
EndIf
If Self.FadeTime
If self.Volume <= 0 Then
If self.Qcount = 0
StopChannel(self.address)
Destroy(Self)
Else
Self.FadeTime = 0
Self.Volume = self.volQ[1]
SetChannelVolume(self.address,self.volQ[1])
PlaySound(self.queue[1],self.address)
For a = 1 To self.qCount - 1
self.queue[a] = self.queue[a+1]
self.volQ[a] = self.volQ[a+1]
Next
self.Queue[self.Qcount] = Null
self.Qcount = self.Qcount - 1
EndIf
Else
Self.Volume = self.VolumeS - Float(MilliSecs() - self.FadeStart) / self.fadetime
SetChannelVolume(self.address,self.volume)
EndIf
EndIf
End Method
'----------------------------------------------------------------
Function UpdateAll()
Local c:ActiveChannel
For c:ActiveChannel = EachIn ChannelTList
c.Update()
Next
End Function
'----------------------------------------------------------------
Function SoundOff()
Local c:ActiveChannel
For c:ActiveChannel = EachIn ChannelTList
StopChannel(c.address)
Destroy(c)
Next
End Function
'----------------------------------------------------------------
Function SetVolume(ID:Int,Vol:Double)
Local c:ActiveChannel
For c:ActiveChannel = EachIn ChannelTList
If c.SampleID = ID Then SetChannelVolume(c.address,vol)
Next
End Function
'----------------------------------------------------------------
Function Playing(ID:Int)
Local c:ActiveChannel
For c:ActiveChannel = EachIn ChannelTList
If c.SampleID = ID And ChannelPlaying(c.address) Then Return True
Next
Return False
End Function
'----------------------------------------------------------------
Function ReleaseSample(ID:Int,Timeout:Double)
Local c:ActiveChannel
For c:ActiveChannel = EachIn ChannelTList
If Not c.FadeTime
If c.SampleID = ID Then
If Timeout
c.FadeTime = Timeout
c.FadeStart = MilliSecs()
Else
StopChannel(c.Address)
Destroy(c)
EndIf
EndIf
EndIf
Next
End Function
'----------------------------------------------------------------
Function NextTrack()
MusicTrack:+1
If MusicTrack > MusicTracks Then MusicTrack = 1
ActiveChannel.ReleaseSample(CH_10,4000)
DebugLog "Music Track : "+MusicTrack
ActiveChannel.Playsample(MainSkin.BGMusic[MusicTrack],CH_10,MusicVolume,True,False)
Return True
End Function
End Type
|
| ||
| Thanks - is there an example of use? |
| ||
| Just make sure you call UpdateAll() each loop and then just chuck stuff at PlaySample. The NextTrack() function shows how the system can be used. It fades the current track and then queues the next track to play when the current one has finished. It's a fire and forget system. [edit]You can also change the volume and pitch as an audio stream is playing, for when a game is paused etc. |
| ||
| Thanks. I will try and learn from it and then roll my own because it's peculiar way of working hurts my delicate little head :) If anyone could continue contributing their own ways of doing it I will appreciate and learn from it rather than blatantly copying? Would be very helpful, thanks! |
| ||
| I have made significant improvements over this version, this is quite messy and I was one of the first things I ever wrote in BMAX. |
| ||
| I am cutting my teeth in BMax. I am a noob to BMax, and your sound engine looks like a good way for me to learn some BMax functionality. Could you please tell me what changes I need to make to my calling code (below) to enable your sound engine. I have a sound called word1.wav within the same directory as your sound engine and my calling code. Thanks in advance - BMax is making my head hurt also.... |
| ||
| I think it has to be something like: Remember the PlaySample & UpdateAll functions are within the ActiveChannel type, so you need to call them using Type.Function(). |
| ||
| Cheers Seb, it worked after a minor modification to the SFX_Engine, compiler was hunting for BG Music so commented out...: @ Indiepath : Thx for the post |
| ||
| @ Indiepath: You mention: "I have made significant improvements over this version, this is quite messy and I was one of the first things I ever wrote in BMAX." Does that mean you'll have a newer one available soon? I saw on your site for your modules the list for the SFX mod. Is that your latest and greatest? Any ETA? Just curious. Nothing mission-critical for me. Just never played around with sound much and your mod looked pretty neat. @Blitzplotter: Got an example of it in action after you fixed it? |
| ||
| I've got a pretty simple sound engine that's got automatic stereo placement based on a coordinate system, email if you want it, it's really too big to paste. diordna (at) gmail.com |
| ||
Here's my panning solution (relative to player in the game world):Local pan# = (enemy_x-player_x)/PLAYFIELDW Local vol# = (1 - Abs(pan)/10) * (1 - (Abs(enemy_y-player_y)/PLAYFIELDH)/10) PlaySound2(enemy_sfx, 1, pan, vol) 'The PlaySound replacement Function PlaySound2:TChannel(snd:TSound, freq# = 1, pan#=0, vol# = 1) Local ch:TChannel = Null If sfxvol > 0 And snd <> Null ch=CueSound(snd) If freq <> 1 SetChannelRate(ch,freq) EndIf SetChannelPan ch, pan SetChannelVolume ch,sfxvol*vol ResumeChannel ch EndIf Return ch End Function |
| ||
| @Booticus, yes this is complete but I won't be releasing it yet. Well not until I decide if I'm staying with the BMAX sound engine or move to something like OpenAL. |
| ||
| @Booticus... If you use the big post from indiepath above, just comment out the BG field in the snippet I put in above. Then use Sebs suggestion to call the SFX_Engine (big piece of code at top), as long as you have your .wav of choice within the directory of the two files talked about... should play. @Indiepath.... I'm quite new to this sound engine malarky... however Grey Alien's Game Framework has a pretty good implementation of playing sound files - Once you suss it out. By good, I mean the quality of the sound that is played... Regards.... |
| ||
| @Indiepath.... I'm quite new to this sound engine malarky... however Grey Alien's Game Framework has a pretty good implementation of playing sound files - Once you suss it out. By good, I mean the quality of the sound that is played... The quality of the sound has nothing to do with the code playing it :P The issue with the BMAX sound module is that it takes a couple of days to load .ogg files, and it does not offer streaming of any kind. |
| ||
| no my code use special magic sprinked in there to make it sound better ;-) Yeah that non-streaming ogg stuff is a pain. |