|
Direct Music Tutorial
| Version Compatibility: |
Visual Basic 5 Visual Basic 6
|
This code has been viewed 92122 times.
THEORY AND GETTING STARTED
DirectMusic is fairly new in directX history, it only
appeared in version 6 and 7, It is a good component though, and if you have
the resources, a lot better than some other formats.
DirectMusic loads a .MID file into a buffer (similiar to DirectSound/Draw) and
then modifies it and plays it back. There is another file format that can be
used: Downloadable sounds, I might cover this later on, but for the casual game
developer it's a bit over complicated.
For
this tutorial you will need a .MID file, there are several supplied with Windows,
but they are all rubbish. If you haven't got any, there is one supplied with
the DirectMusic Project from my download page.
[top]
THE VARIABLES
These variables go into the (Declarations) section of
your Main Form:
Option
Explicit
Dim dx As New DirectX7
'the root
object, as seen in every DirectX application
Dim perf As DirectMusicPerformance
Dim perf2 As DirectMusicPerformance
'These 2 variables are the buffers, "perf" is the master,
"perf2" is used to gather 'information about the MIDI file
Dim seg As DirectMusicSegment
Dim segstate As DirectMusicSegmentState
'Segments, these hold segments of the music!
Dim loader As DirectMusicLoader 'This
is an object that helps load the files into the buffers.
Public GetStartTime As Long
Public Offset As Long
Public mtTime As Long
Public mtLength As Double
Public dTempo As Double
Dim timesig As DMUS_TIMESIGNATURE 'This'll
hold the time signiture that the MIDI is in.
Dim IsPlayingCheck As Boolean
'A simple true/false statement as to whether or not it's playing.
Dim msg As String
'Used in the error handler sub.
Dim time As Double
Dim fIsPaused As Boolean Dim
ISITPAUSED As Boolean '--TIME--
These settings are sued in the progress bar section.
Dim Total_Time As Double
Dim Current_Time As Double
Dim Percent_Time As Double |
The next bit of code is the error handler, a simple, almost pointless wrapper
sub for generating error messages:
Sub
localerror(ErrorNum As Long, ErrorDesc As String) 'Nothing
much to explain here...
msg = ErrorDesc
msg = "(" & ErrorNum
& ") - " & msg
MsgBox msg
End Sub |
[top]
MAKING
THE INTERFACE
This section needs to be done before anything else. Refer
to this picture for the basic layout:
Note, there is a timer on the form, and a Shape control hidden in the White
PictureBox. (This picture was taken at run-time).
Just
to make it simple for me, use this list as a reference for all the objects:
|
Form1
.Caption="DirectMusic
MIDI Player" - or anything similiar
.ScaleMode=3
(Pixel)
.StartUpPosition=2
(CenterScreen)
cmdOpen
-Top Command Button
.Caption="
Open "Tester.Mid" "
cmdPlay
.Caption="
Play "Tester.Mid" "
cmdPause
.Caption="
Pause "Tester.Mid" "
cmdStop
.Caption="
Stop "Tester.Mid" "
Edit_Volume -The Label that shows
the volume
.Caption=100
.Height=33
.Width=48
.Font=MS Sans
Serif, Bold, size 18
UpDown_Volume - This can be found
in the "Microsoft Common Controls 2" library
.Max=100
.Min=100
.Value=75
Get_Time_Timer -Timer Control
.Enabled=False
.Interval=50
Picture1
.Height=204
.Width=45
.ScaleMode=3
(Pixel)
Shape1 -Place This inside
Picture1
.FillStyle=0
(Solid)
.FillColor=Blue
(Or any colour of your choice)
.Height=200
.Width=41
.Top=200
.Left=0
lblPercentage -The Small Label
in the middle of the Progress Bar
-Right click on it, and select "Bring To Front"
.Caption="%"
.BackStyle=0
(Transparent)
.Alignment=2
(Center)
.Font = MS Sans
Serif, Bold, Size 10
.Height=13
.Width=41
.Left=0
.Top=104
lblLength
.AutoSize=true
.Caption="Length:
"
lblTimeSig
.AutoResize=true
.Caption="Time
Sig: "
lblTempo
.AutoResize=true
.Caption="Tempo:
"
|
Now that's out of the way, we can get onto some code. If you find any
problems with the above descriptions, download the working example from my
"Downloads Page"
[top]
FORM LOAD: WHERE IT ALL STARTS
This is the first code that your program executes, so
place this code in the "Form_Load":
|
Private
Sub Form_Load()
On Error GoTo LocalErrors
Set loader = dx.DirectMusicLoaderCreate()
'This
is the DMusic Main Component
'Creating
a Perf2 so that we can get all the segment information without having
to play 'the segment
Set perf2 = dx.DirectMusicPerformanceCreate()'Create
the Second buffer.
Call perf2.Init(Nothing,
0)
perf2.SetPort -1, 80
Call perf2.GetMasterAutoDownload
Set perf = dx.DirectMusicPerformanceCreate()'create
the first Buffer.
Call perf.Init(Nothing, 0)
perf.SetPort -1, 80
Call perf.SetMasterAutoDownload(True)
perf.SetMasterVolume (Edit_Volume.Caption
* 42 - 3000)'A
Simple equation to send the 'correct volume to DirectX, you'll need
to remember this.
Edit_Volume.Caption = UpDown_Volume.Value
'The
volume is set depending on what
'edit_Volume's caption is. This line transfers
the value from the up/down control
'to the label
Exit Sub
LocalErrors:
Call localerror(Err.Number,
Err.Description) 'We've
already written this sub.
End Sub
|
Most of this code is straight forward, and is explained in the Comments. Now
we can move on
[top]
THE OPEN CODE
This code is where you prepare the buffer's to accept
data, then load the Music into them. Copy the following code into the "cmdOpen"
Command Button:
|
Private
Sub cmdOpen_Click()
Dim Minutes As Integer
Dim a As Integer
Dim length As Integer
Dim length2 As Integer
Dim FileName As String
On
Error GoTo LocalErrors
If Not seg Is Nothing And Not segstate Is Nothing
Then
' There is a Segment and a SegmentState
If perf.IsPlaying(seg, segstate)
= True Then
' Segment currently playing, so exit
MsgBox
"Please Stop currently playing music before selecting a new segment"
Exit
Sub
ElseIf ISITPAUSED = True
Then
MsgBox
"Please Stop currently playing music before selecting a new segment"
'A simple error catcher
Exit
Sub
End If
End If
Set loader = Nothing
Set loader = dx.DirectMusicLoaderCreate
FileName
= App.path & "\Tester.mid" 'If
this were a program, you'd set the music file here using the
'common dialog control
Set
seg = loader.LoadSegment(FileName)
cmdPlay.Enabled
= True
' Set the search directory
based on the placement of the .mid file that was loaded
length = Len(FileName)
length2 = length
Dim path As String
Do While path <> "\"
path
= Mid(FileName, length, 1)
length
= length - 1
Loop
Dim SearchDir As String
SearchDir = Left(FileName,
length)
loader.SetSearchDirectory
(Left(FileName, length + 1))
perf2.SetMasterAutoDownload
True
'Set all the Captions to
empty
lblTempo.Caption = vbNullString
lblTimeSig.Caption = vbNullString
lblLength.Caption = vbNullString
'Play the segment just long
enough to get the info
mtTime = perf2.GetMusicTime()
Call perf2.PlaySegment(seg,
0, mtTime + 2000)
'GetTempo
dTempo = perf2.GetTempo(mtTime
+ 2000, 0)
lblTempo.Caption = "Tempo:
" & Format(dTempo, "00.00")
'GetTimeSig
Call perf2.GetTimeSig(mtTime
+ 2000, 0, timesig)
lblTimeSig.Caption = "Time
Sig: " & timesig.beatsPerMeasure & "/" &
timesig.beat
'GetLength
mtLength = (((seg.GetLength()
/ 768) * 60) / dTempo)
'This is an important set of numbers, this'll result in the total
'number of seconds the file lasts.
Total_Time = mtLength
'a variable copy, "Total_Time" is used later on in the project
' Put the length in a time
that we can relate to
Minutes = 0
a = mtLength - 60
Do While a > 0
Minutes
= Minutes + 1
a
= a - 60
Loop
lblLength.Caption = "Length:
" & Format(Minutes, "00") & ":" &
Format((mtLength - (Minutes * 60)), "00.0")
' Now that we retreived all
the segment info, we'll stop playing the segment, the user wont here
any music being played 'here, as it does it so quickly...
Call perf2.Stop(seg, Nothing,
0, 0)
seg.SetStandardMidiFile
Exit Sub
LocalErrors:
If Not seg Is Nothing Then
Call perf2.Stop(seg, Nothing,
0, 0)
'If there was an error, stop playing the music
End If
MsgBox
("There was a problem loading the requested file. No file
has been loaded")
'output result to user
FileName
= vbNullString
End Sub
|
Now, when you run the application, and click on the load button, it should put
some information into the labels. Now we need to get it to do something!
[top]
THE PLAY CODE
Now you'll get some output. Assuming that everything is
working fine, this code should play the contents of the "perf" buffer.
Dont try running it until you have the "Stop" code written in, as
you may cause it to lock up if it isn't stopped. Copy
the following code into the "Play" command button:
|
Private
Sub cmdPlay_Click()
If seg Is Nothing Then
'The user hasn't clicked "Open" yet.
MsgBox
("Please open a segment or MIDI file before playing")
Exit
Sub
End If
If fIsPaused Then 'If
it is paused, start the file from where it already is.
Offset
= mtTime - GetStartTime + Offset + 1
Call
seg.SetStartPoint(Offset)
Set
segstate = perf.PlaySegment(seg, 0, 0)
cmdPause.BackColor
= &H8000000F
'gray - used as a flag for a later argument
Else
'Reset it back to the beggining, then start playing it.
Offset
= 0
If
perf.IsPlaying(seg, segstate) = True Then
Call perf.Stop(seg, segstate, 0, 0)
End
If
seg.SetStartPoint
(0)
Set
segstate = perf.PlaySegment(seg, 0, 0)
cmdPause.BackColor
= &H8000000F 'gray
-another flag
'SETUP
TIME BAR
get_Time_Timer.Enabled
= True 'enable
the progress bar.
Exit Sub
End If
fIsPaused = False
End Sub
|
[top]
THE PAUSE CODE
Next up, the pausing code. All three (Play/pause/stop)
link together, so dont test the application until you have added all the bits
of code. As
above, place this code in the "Pause" Command button:
|
Private
Sub cmdPause_Click()
On Error GoTo LocalErrors
If seg Is Nothing Then 'If
it hasn't been loaded yet, get out of here
Exit
Sub
End If
IsPlayingCheck = perf.IsPlaying(seg, segstate)
If IsPlayingCheck = True
Then 'music is playing
fIsPaused
= True
'
pause music and button down
mtTime = perf.GetMusicTime()
GetStartTime
= segstate.GetStartTime()
Call
perf.Stop(seg, Nothing, 0, 0)
cmdPause.BackColor
= &HFFFFC0 'blue
This flag is used below:
Else
If
cmdPause.BackColor = &HFFFFC0 Then
'button is blue, therfore it is paused
'unpause
fIsPaused = False
Offset = mtTime - GetStartTime + Offset + 1
Call seg.SetStartPoint(Offset)
Set segstate = perf.PlaySegment(seg, 0, 0)
cmdPause.BackColor = &H8000000F 'gray
End
If
End If
Exit Sub
LocalErrors:
Call localerror(Err.Number,
Err.Description) 'The
Error handler shown earlier
End Sub
|
[top]
THE STOP CODE
And finally, the stop code. Once this code is complete
you can listen to the MIDI at your hearts content. (Should you want to) This
code goes in the "Stop" button:
|
Private
Sub cmdStop_Click()
If seg Is Nothing Then 'nothing
has been loaded yet...
Exit
Sub
End If
fIsPaused = False
'internal flag to tell it that it isn't paused.
cmdPause.BackColor = &H8000000F
'grey
Call perf.Stop(seg, segstate,
0, 0)
cmdPlay.Enabled = True
time = 0
get_Time_Timer.Enabled =
False 'Stop
the timer from counting
End Sub
|
[top]
CHANGING THE VOLUME
The rest of the code from here is about optimizing, and
getting more out of your current application. The above code is necessary in
every application that uses DirectMusic; the following stuff is probably useful
though. Changing the volume is very easy, paste these lines in the appropriate
place:
|
Private
Sub UpDown_Volume_Change()
Edit_Volume.Caption = UpDown_Volume.Value
'This
copies the value to the label, and
'at the same time triggers the following line:
End Sub
Private
Sub Edit_Volume_Change()
perf.SetMasterVolume (Edit_Volume.Caption
* 42 - 3000)
End Sub
|
Experiment
with the volume, it can be used during a game to signify key points (getting
louder) or just to fade out at the end of a level.
[top]
THE PROGRESS BAR
This is the final piece to be added to your project. Although
it isn't implemented in this project, this is how you can make the music loop.
This code iutputs a percentage of the way through the music - you could modify
this code so that, at 100% (finished) it stops the music, resets it back to
the begginning, then starts playing it all over again.
Put
the following code in the timer control:
|
If
perf.IsPlaying(Nothing, segstate) = True Then
'Only update the display if there is music playing,,,
Current_Time = ((((perf.GetMusicTime() - (segstate.GetStartTime()
- Offset)) / 768) * 60) / dTempo) 'get
the current time
Percent_Time = Int((Current_Time / Total_Time)
* 100) 'work
out the percentage - you remember these?
'These bits are irrelevant - I left these lines
in the code when i compressed it up
'so's not to confuse people, I left them here:
'Me.Caption = Percent_Time & "%
{Playing}"
'Update User-Interface
'shape1 moves up the picture box as the MIDI
file plays on....
'2 Pixels=1%
'END useless code!
Shape1.Top = 200 - (Percent_Time * 2)
'Because the shape starts at the bottom, it needs to work out from
the bottom:
'Hence the 200-
lblPercentage = Percent_Time & "%"
'copy
the result to the label
Else
'Me.Caption = "{Stopped}" another
useless line.
End If
|
¸2000 Jack Hoxley - All rights
reserved.
The material on this page may be
reproduced as long as credit is given to myself
"Jack Hoxley"
The author can be e-mailed at jollyjeffers@greenonions.netscapeonline.co.uk.
His web site is http://members.dencity.com/dx4vb/
|