|
Exceptional article; however, to do all this work and not make a downloadable solution w. src code available makes one wonder how many "speed bumps" one would encounter to get this code to work. Since the project apparently workd, why nt take the extra 15 minutes to zip the project and make available to those interested in this forum. A+ for the article but a D- for availability of replicating the project.
|
|
|
|
|
Can I have it?
Please email to
javiergg8@hotmail.com
THANKS
|
|
|
|
|
Hi There,
thanks for posting the code ENO!
You must dream in code or something!
I got an error however
Cross-thread operation not valid: Control 'lblPos' accessed from a thread other than the thread it was created on.
from what I can see, I made a form WaveP and then plastered the first 2 regions into the designer
then there was 2 classes "Timer" and CwaveReader
this is in the 2nd region of the designer
--------------------------------------------------------------------------------
'The handler to the timer tick event :
'Paints the location of the player pointer on the sound graph as the data is played
Sub MyPainPoint(ByVal obj As Object, ByVal Args As System.Timers.ElapsedEventArgs)
'Control to ensure we stop when the total cumulated bytes read is equal to the total data size
If PlayerPosition >= MYwave.SubChunkSizeTwo Then
StopPlay()
'Update the labels
lblPos.Text = MYwave.SubChunkSizeTwo.ToString
lbltime.Text = MYwave.PlayTimeSeconds().ToString
End If
'Draw a line red from top to bottom showing the curent position based on play position
'The PlayerPOsition is the absolute location of the current played data
'This is taken as a ratio of the total data size adn scaled to the width of the picture
'control
Dim XPos As Single = CSng((PlayerPosition / MYwave.SubChunkSizeTwo) * picWave.Width)
Dim posgraphic As Graphics
'Clone the original canvas (this has the original sound graph). We do not need to
're-draw this large graph as it willtake much processing time!
PlayPicture = CType(myPicture.Clone, Bitmap)
posgraphic = Graphics.FromImage(PlayPicture)
'Draw the pointer
Dim Mypen As Pen = New Pen(Color.Red)
posgraphic.DrawLine(Mypen, XPos, 0, XPos, picWave.Height)
'Draw line to myPicture
MyTime += TimerStep
posgraphic.DrawImage(PlayPicture, picWave.Width, picWave.Height)
'Update the status on the labels
lblPos.Text = PlayerPosition.ToString ' Th is where error
lbltime.Text = (MyTime / 1000).ToString
'force a redraw of the picture updated.
Me.Invalidate(New Drawing.Rectangle(picWave.Left, picWave.Top, picWave.Width, picWave.Height))
End Sub
-----------------------------------------------------------------------------
What am I missing?
It loads the wave - beatiful to behold!
oh - is there a simple way to change the speed of the wave?
Cheers,
Sean
|
|
|
|
|
Seems to be a good piece of work but many of us would love to have the code.
Anybody got it. Please mail me at plouvou@yahoo.gr
Thanks
|
|
|
|
|
'By Edgar Nyamweya Okioga
'.....@gamil.com
'on 24/11/2007
'For AfricaDotNet user Group (www.africadotnet.net)
'Demonstration of static and Circular buffers
'Demonstration of how to play sound portions
'Demonstration of the system.timer class
'Demonstration of double buffering and alpha blending
Public Class CircualrSounds
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents lblPos As System.Windows.Forms.Label
Friend WithEvents lbltime As System.Windows.Forms.Label
Friend WithEvents Label3 As System.Windows.Forms.Label
Friend WithEvents cmdStop As System.Windows.Forms.Button
Friend WithEvents picWave As System.Windows.Forms.PictureBox
Friend WithEvents cmdDefault As System.Windows.Forms.Button
Friend WithEvents txtData As System.Windows.Forms.TextBox
Friend WithEvents cmdCustom As System.Windows.Forms.Button
Friend WithEvents cmdCircular As System.Windows.Forms.Button
Friend WithEvents cmdSeg1 As System.Windows.Forms.Button
Friend WithEvents cmdSeg2 As System.Windows.Forms.Button
Friend WithEvents cmdBrowse As System.Windows.Forms.Button
Friend WithEvents dlgWav As System.Windows.Forms.OpenFileDialog
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.Label1 = New System.Windows.Forms.Label
Me.lblPos = New System.Windows.Forms.Label
Me.lbltime = New System.Windows.Forms.Label
Me.Label3 = New System.Windows.Forms.Label
Me.cmdStop = New System.Windows.Forms.Button
Me.picWave = New System.Windows.Forms.PictureBox
Me.cmdDefault = New System.Windows.Forms.Button
Me.txtData = New System.Windows.Forms.TextBox
Me.cmdCustom = New System.Windows.Forms.Button
Me.cmdCircular = New System.Windows.Forms.Button
Me.cmdSeg1 = New System.Windows.Forms.Button
Me.cmdSeg2 = New System.Windows.Forms.Button
Me.cmdBrowse = New System.Windows.Forms.Button
Me.dlgWav = New System.Windows.Forms.OpenFileDialog
Me.SuspendLayout()
'
'Label1
'
Me.Label1.Location = New System.Drawing.Point(160, 12)
Me.Label1.Name = "Label1"
Me.Label1.Size = New System.Drawing.Size(48, 16)
Me.Label1.TabIndex = 3
Me.Label1.Text = "Position"
'
'lblPos
'
Me.lblPos.Location = New System.Drawing.Point(216, 8)
Me.lblPos.Name = "lblPos"
Me.lblPos.Size = New System.Drawing.Size(80, 24)
Me.lblPos.TabIndex = 4
'
'lbltime
'
Me.lbltime.Location = New System.Drawing.Point(336, 8)
Me.lbltime.Name = "lbltime"
Me.lbltime.Size = New System.Drawing.Size(72, 24)
Me.lbltime.TabIndex = 6
'
'Label3
'
Me.Label3.Location = New System.Drawing.Point(304, 12)
Me.Label3.Name = "Label3"
Me.Label3.Size = New System.Drawing.Size(32, 16)
Me.Label3.TabIndex = 5
Me.Label3.Text = "Time"
'
'cmdStop
'
Me.cmdStop.Enabled = False
Me.cmdStop.Location = New System.Drawing.Point(416, 8)
Me.cmdStop.Name = "cmdStop"
Me.cmdStop.Size = New System.Drawing.Size(56, 24)
Me.cmdStop.TabIndex = 16
Me.cmdStop.Text = "Stop"
'
'picWave
'
Me.picWave.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or
System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.picWave.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
Me.picWave.Enabled = False
Me.picWave.Location = New System.Drawing.Point(8, 40)
Me.picWave.Name = "picWave"
Me.picWave.Size = New System.Drawing.Size(814, 184)
Me.picWave.TabIndex = 17
Me.picWave.TabStop = False
'
'cmdDefault
'
Me.cmdDefault.Enabled = False
Me.cmdDefault.Location = New System.Drawing.Point(88, 8)
Me.cmdDefault.Name = "cmdDefault"
Me.cmdDefault.Size = New System.Drawing.Size(64, 24)
Me.cmdDefault.TabIndex = 18
Me.cmdDefault.Text = "Default"
'
'txtData
'
Me.txtData.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or
System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.txtData.Location = New System.Drawing.Point(8, 232)
Me.txtData.Multiline = True
Me.txtData.Name = "txtData"
Me.txtData.ReadOnly = True
Me.txtData.ScrollBars = System.Windows.Forms.ScrollBars.Vertical
Me.txtData.Size = New System.Drawing.Size(816, 120)
Me.txtData.TabIndex = 19
Me.txtData.Text = ""
'
'cmdCustom
'
Me.cmdCustom.Enabled = False
Me.cmdCustom.Location = New System.Drawing.Point(488, 8)
Me.cmdCustom.Name = "cmdCustom"
Me.cmdCustom.Size = New System.Drawing.Size(56, 24)
Me.cmdCustom.TabIndex = 20
Me.cmdCustom.Text = "Stream"
'
'cmdCircular
'
Me.cmdCircular.Enabled = False
Me.cmdCircular.Location = New System.Drawing.Point(560, 8)
Me.cmdCircular.Name = "cmdCircular"
Me.cmdCircular.Size = New System.Drawing.Size(48, 24)
Me.cmdCircular.TabIndex = 21
Me.cmdCircular.Text = "Array"
'
'cmdSeg1
'
Me.cmdSeg1.Enabled = False
Me.cmdSeg1.Location = New System.Drawing.Point(624, 8)
Me.cmdSeg1.Name = "cmdSeg1"
Me.cmdSeg1.Size = New System.Drawing.Size(80, 24)
Me.cmdSeg1.TabIndex = 22
Me.cmdSeg1.Tag = "1"
Me.cmdSeg1.Text = "Capture 1"
'
'cmdSeg2
'
Me.cmdSeg2.Enabled = False
Me.cmdSeg2.Location = New System.Drawing.Point(720, 8)
Me.cmdSeg2.Name = "cmdSeg2"
Me.cmdSeg2.Size = New System.Drawing.Size(80, 24)
Me.cmdSeg2.TabIndex = 23
Me.cmdSeg2.Tag = "2"
Me.cmdSeg2.Text = "Capture 2"
'
'cmdBrowse
'
Me.cmdBrowse.Location = New System.Drawing.Point(8, 8)
Me.cmdBrowse.Name = "cmdBrowse"
Me.cmdBrowse.Size = New System.Drawing.Size(72, 24)
Me.cmdBrowse.TabIndex = 24
Me.cmdBrowse.Text = "File..."
'
'dlgWav
'
Me.dlgWav.Filter = """WAV Files""|*.wav"
Me.dlgWav.ReadOnlyChecked = True
Me.dlgWav.RestoreDirectory = True
Me.dlgWav.Title = """Select WAV file to open"""
'
'CircualrSounds
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(830, 356)
Me.Controls.Add(Me.cmdBrowse)
Me.Controls.Add(Me.cmdSeg2)
Me.Controls.Add(Me.cmdSeg1)
Me.Controls.Add(Me.cmdCircular)
Me.Controls.Add(Me.cmdCustom)
Me.Controls.Add(Me.txtData)
Me.Controls.Add(Me.cmdDefault)
Me.Controls.Add(Me.picWave)
Me.Controls.Add(Me.cmdStop)
Me.Controls.Add(Me.lbltime)
Me.Controls.Add(Me.Label3)
Me.Controls.Add(Me.lblPos)
Me.Controls.Add(Me.Label1)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.Name = "CircualrSounds"
Me.Text = "Circular Buffers"
Me.ResumeLayout(False)
End Sub
#End Region
#Region "Variables in use"
'Direct Sound Device variable
Dim SoundDevice As Microsoft.DirectX.DirectSound.Device
'Direct Sound secondary buffer to hold data for mixing
Dim SbufferOriginal As Microsoft.DirectX.DirectSound.SecondaryBuffer
'Sample Music
Dim SoundFile As String
'Wave file data parser
Dim MYwave As CWAVReader
'Event handler to process tick intervals
Dim MyPaint As System.Timers.ElapsedEventHandler
'Timer object
Dim MyPaintTimer As Timer
'Varibale to accumulate ticks in milliseconds
Dim MyTime As Integer
'Variable to hold total play time of the wav file
Dim TotalTime As Single
'Variable to holed wav data to draw graph
Dim Data() As Byte
'Wave data picture
Dim myPicture As Bitmap
'Circular buffer update interval variable
Dim TimerStep As Integer
'Play indicator and wav data picture composite
Dim PlayPicture As Bitmap
'For Circular buffer
'Next location in circular buffer we can write
Dim NextWritePos As Integer
'Total bytes writen to circular buffer
Dim PlayerPosition As Integer
'Maximum latency control time in milliseconds....
Dim Latency As Integer = 300
'Memeroy stream to hold actual sound Data.
Dim DataMemStream As IO.MemoryStream
'Event handler to process tick intervals
Dim PlayerTime As System.Timers.ElapsedEventHandler
'Timer object
Dim PlayerEvent As Timer
'DataArray for Circular sound
Dim DataArray As Byte()
'varibale to control if we play using memory stream or Data Array
Dim IsArray As Boolean = False
'Varibales to capture the portions of the sound we want to play
Dim StartPoint As Point
Dim EndPoint As Point
#End Region
#Region "Graphic methods and functions"
'Draws the Sound data as a wave onto a canvas using double buffering
to speed up work
Function DrawGraph(ByVal Data() As Int16) As Bitmap
'Create the Canvas
Dim myBitmap As System.Drawing.Bitmap
'Create an array to hold the wav data which we can sort
Dim tempData(Data.Length) As Integer
'Copy the data to the temporary location .. can be done better
Data.CopyTo(tempData, 0)
'Sort the array to get the maximum and minimum Y values
Array.Sort(tempData)
'generate the Canvas (drawing board in memory
myBitmap = New Bitmap(picWave.Width, picWave.Height)
'Create your paint brush, pens and drawing objects
Dim myGraphic As System.Drawing.Graphics
myGraphic = Graphics.FromImage(myBitmap)
'draw the backgound with a custom color
myGraphic.Clear(Color.FromArgb(181, 223, 225))
'Get the paramerets to draw the data and scale to fit the canvans but
draw form the middle
Dim YMax As Integer = tempData(Data.Length - 1)
Dim YMin As Integer = tempData(0)
Dim XMax As Integer = picWave.Width
Dim Xmin As Integer = 0
'Create an array of points to draw a line
Dim PicPoint(Data.Length - 1) As System.Drawing.PointF
Dim Count As Integer
Dim Step1 As Single 'Scale the data between Ymax and Ymin
Dim Step2 As Single 'Scale the data further to fit between the canvas hieght
Dim step3 As Single 'Draw the point form the middle of the canvas
Dim Mypen As New Pen(Color.FromArgb(24, 101, 123))
'Draw the lines from the series of points representing the sound wav
For Count = 0 To Data.Length - 1
Step1 = CSng(Data(Count) / (YMax - YMin))
Step2 = CSng(Step1 * picWave.Height / 2)
step3 = CSng(Step2 + (picWave.Height / 2))
PicPoint(Count) = New System.Drawing.PointF(CSng(XMax * (Count /
Data.Length)), step3)
Next
'Draw the lines
myGraphic.DrawLines(Mypen, PicPoint)
'Draw graphics onto canvas
myGraphic.DrawImage(myBitmap, picWave.Width, picWave.Height)
'return the picture memeory object
Return (myBitmap)
End Function
'The handler to the timer tick event :
'Paints the location of the player pointer on the sound graph as the
data is played
Sub MyPainPoint(ByVal obj As Object, ByVal Args As
System.Timers.ElapsedEventArgs)
'Control to ensure we stop when the total cumulated bytes read is
equal to the total data size
If PlayerPosition >= MYwave.SubChunkSizeTwo Then
StopPlay()
'Update the labels
lblPos.Text = MYwave.SubChunkSizeTwo.ToString
lbltime.Text = MYwave.PlayTimeSeconds().ToString
End If
'Draw a line red from top to bottom showing the curent position based
on play position
'The PlayerPOsition is the absolute location of the current played data
'This is taken as a ratio of the total data size adn scaled to the
width of the picture
'control
Dim XPos As Single = CSng((PlayerPosition / MYwave.SubChunkSizeTwo) *
picWave.Width)
Dim posgraphic As Graphics
'Clone the original canvas (this has the original sound graph). We do
not need to
're-draw this large graph as it willtake much processing time!
PlayPicture = CType(myPicture.Clone, Bitmap)
posgraphic = Graphics.FromImage(PlayPicture)
'Draw the pointer
Dim Mypen As Pen = New Pen(Color.Red)
posgraphic.DrawLine(Mypen, XPos, 0, XPos, picWave.Height)
'Draw line to myPicture
MyTime += TimerStep
posgraphic.DrawImage(PlayPicture, picWave.Width, picWave.Height)
'Update the status on the labels
lblPos.Text = PlayerPosition.ToString
lbltime.Text = (MyTime / 1000).ToString
'force a redraw of the picture updated.
Me.Invalidate(New Drawing.Rectangle(picWave.Left, picWave.Top,
picWave.Width, picWave.Height))
End Sub
'Capture the start point
Private Sub picWave_MouseDown(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles picWave.MouseDown
'Get the position of the mouse and draw a line
StartPoint = New Point
If e.Button = MouseButtons.Left Then
StartPoint.X = e.X
StartPoint.Y = e.Y
End If
End Sub
'Draw a band over the data we want to capture
Private Sub picWave_MouseMove(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles picWave.MouseMove
If e.Button = MouseButtons.Left Then
If StartPoint.IsEmpty = False Then
EndPoint = New Point
EndPoint.X = e.X
EndPoint.Y = e.Y
DrawSelection()
End If
End If
End Sub
'Draw the band over the selection
Sub DrawSelection()
'Draw a rubberband
Dim posgraphic As Graphics
Dim RubberRect As Rectangle
RubberRect = New Rectangle(StartPoint.X, 0, EndPoint.X - StartPoint.X,
picWave.Height - 3)
'Clone the original canvas
PlayPicture = CType(myPicture.Clone, Bitmap)
posgraphic = Graphics.FromImage(PlayPicture)
'Draw the pointer
Dim Mypen As Pen = New Pen(Color.Green)
'Create a transparent brush using alpha blending techniques
Dim MyBrush As SolidBrush = New SolidBrush(Color.FromArgb(85, 204, 32, 92))
'Draw the boarder
posgraphic.DrawRectangle(Mypen, RubberRect)
'Fill the color
posgraphic.FillRectangle(MyBrush, RubberRect)
'Draw the picture on the form with the updated section of music to clip
posgraphic.DrawImage(PlayPicture, picWave.Width, picWave.Height)
'redraw the portion only
Me.Invalidate(New Drawing.Rectangle(picWave.Left, picWave.Top,
picWave.Width, picWave.Height))
End Sub
'Complete the drawing and capture the start and width/hieght of the rubber band
Private Sub picWave_MouseUp(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles picWave.MouseUp
EndPoint.X = e.X
EndPoint.Y = e.Y
DrawSelection()
End Sub
#End Region
#Region "Sound functions and helper code"
'Creates a memory stream from Array Data to support playing sound from
a memory stream
'In an actual implementation, this would be reading data from an
actual file on the Harddisk
Function GetMemeoryStream(ByVal Data() As Byte) As IO.MemoryStream
Return New IO.MemoryStream(Data)
End Function
'Stop processing any sound
Private Sub cmdStop_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdStop.Click
StopPlay()
cmdBrowse.Enabled = False
cmdDefault.Enabled = True
cmdCustom.Enabled = True
cmdCircular.Enabled = True
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = False
End Sub
'using the form events paint, redraw the picture
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
'Draw the picture of the updated pointer location
picWave.Image = PlayPicture
End Sub
'Stop playing the sound
Sub StopPlay()
'Get the status of the player
If SbufferOriginal.Status.Playing Then
SbufferOriginal.Stop()
'shut the paint timer
If Not IsNothing(MyPaintTimer) Then
MyPaintTimer.Enable(False)
MyPaintTimer = Nothing
End If
'shut the player timer
If Not IsNothing(PlayerTime) Then
PlayerEvent.Enable(False)
PlayerEvent = Nothing
End If
End If
End Sub
'Update the Circular buffer based on either a stream of data array
Sub PlayerEventHandler(ByVal obj As Object, ByVal Args As
System.Timers.ElapsedEventArgs)
'Stop if we have read all data
If PlayerPosition >= MYwave.SubChunkSizeTwo Then
StopPlay()
End If
'If an array, use the dataArray to top up new data to the circular buffer
If IsArray = False Then
'Get the amount of data to write and thereafter write the data
WriteData(GetPlayedSize())
Else
WriteDataArray(GetPlayedSize())
End If
End Sub
'Play a sound wave using a default static buffer
Private Sub cmdDefault_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdDefault.Click
TimerStep = 25 'Interval to draw the pointer on the graph
cmdCustom.Enabled = False
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
Try
MyPaintTimer = New Timer(TimerStep, MyPaint)
TotalTime = CSng(MYwave.SubChunkSizeTwo / MYwave.ByteRate)
SbufferOriginal = New
Microsoft.DirectX.DirectSound.SecondaryBuffer(SoundFile, SoundDevice)
Me.Text = "Buffer size :" & SbufferOriginal.Caps.BufferBytes.ToString
'Start the timer
MyPaintTimer.Enable(True)
MyTime = 0
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
'Use a circular buffer to update the primary buffer with data
Private Sub cmdCustom_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdCustom.Click
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = True
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = True
IsArray = False
'create a format object tro describe the wave properties
'These are not random numbers.. they are corelated based on
'formulae such that a change in one will result to change in
'related properties. Ref the contents of the label during run time
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
'Create the buffer description and buffer size
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
Desc.BufferBytes = CInt(MYwave.SampleRate) * 1
'Add capabilites of the buffer
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
'Create a secondary sound buffer
SbufferOriginal = New
Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
TimerStep = Latency \ 10
MyTime = 0
SbufferOriginal.Stop()
SbufferOriginal.SetCurrentPosition(0)
WriteData(SbufferOriginal.Caps.BufferBytes)
NextWritePos = 0
PlayerPosition = 0
MyPaintTimer = New Timer(TimerStep, MyPaint)
'Create a timer
PlayerTime = New System.Timers.ElapsedEventHandler(AddressOf PlayerEventHandler)
PlayerEvent = New Timer(TimerStep, PlayerTime)
PlayerEvent.Enable(True)
'Play the sound
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
MyPaintTimer.Enable(True)
End Sub
'get the played data size
Function GetPlayedSize() As Integer
Dim Pos As Integer
Pos = SbufferOriginal.PlayPosition
If Pos < NextWritePos Then
Return Pos + (SbufferOriginal.Caps.BufferBytes - NextWritePos)
Else
Return Pos - NextWritePos
End If
End Function
'Ensure we have a latency of maximum 300 milliseconds
Function TimeToDataSize(ByVal Latency As Integer) As Integer
Dim DataSize As Integer
DataSize = CInt(Latency * SbufferOriginal.Format.AverageBytesPerSecond / 1000)
DataSize = DataSize - (DataSize Mod SbufferOriginal.Format.BlockAlign)
Return DataSize
End Function
'Write data to the circular buffer (secondary buffer that is)
Sub WriteData(ByVal DataSize As Integer)
Dim Tocopy As Integer
'make sure the data is less than the latency amount of 300ms
Tocopy = Math.Min(DataSize, TimeToDataSize(Latency))
'Only write data if there is something to write!
If Tocopy > 0 Then
'Restore the buffer
If SbufferOriginal.Status.BufferLost Then
SbufferOriginal.Restore()
End If
'Copy the data to the buffer
'The DataMemStream is a binary stream object. This can also be a
'large WAV file still on 'harddisk
SbufferOriginal.Write(NextWritePos, DataMemStream, Tocopy,
Microsoft.DirectX.DirectSound.LockFlag.None)
'As data is read form the DataMemStream, the position (internal of the
structure) advances
'by the size of Tcopy
'Advance the total cumulative bytes read
PlayerPosition += Tocopy
'advance the NextWrite pointer
NextWritePos += Tocopy
'If we are at the end, we wrap round
If NextWritePos >= SbufferOriginal.Caps.BufferBytes Then
NextWritePos = NextWritePos - SbufferOriginal.Caps.BufferBytes
End If
End If
End Sub
'Write data to a Data Array.... similar to the above..using memory a stream
Sub WriteDataArray(ByVal DataSize As Integer)
Dim Tocopy As Integer
'ensure we do not have a big latency . the maximum is 300 ms
Tocopy = Math.Min(DataSize, TimeToDataSize(Latency))
'is we have data, then write to the array and play
If Tocopy > 0 Then
'Restore the buffer
If SbufferOriginal.Status.BufferLost Then
SbufferOriginal.Restore()
End If
'Copy the data to the Array
're-create the data array (this is very slow!!)
ReDim DataArray(Tocopy - 1)
'Position the memory stream to the last location we read from.
DataMemStream.Position = PlayerPosition
'Copy the data from the stream to the array
DataMemStream.Read(DataArray, 0, Tocopy - 1)
'Write the data to the seconadry buffer
SbufferOriginal.Write(NextWritePos, DataArray,
Microsoft.DirectX.DirectSound.LockFlag.None)
'Adavance the pointers
PlayerPosition += Tocopy
NextWritePos += Tocopy
If NextWritePos >= SbufferOriginal.Caps.BufferBytes Then
NextWritePos = NextWritePos - SbufferOriginal.Caps.BufferBytes
End If
End If
End Sub
'Create a circular buffer based on the data array... similar to the
former button...
Private Sub cmdCircular_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles cmdCircular.Click
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = False
cmdCircular.Enabled = True
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = True
IsArray = True
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
Desc.BufferBytes = CInt(MYwave.SampleRate) * 1
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
SbufferOriginal = New
Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
TimerStep = Latency \ 5
MyTime = 0
SbufferOriginal.Stop()
SbufferOriginal.SetCurrentPosition(0)
WriteDataArray(SbufferOriginal.Caps.BufferBytes)
NextWritePos = 0
PlayerPosition = 0
MyPaintTimer = New Timer(TimerStep, MyPaint)
PlayerTime = New System.Timers.ElapsedEventHandler(AddressOf PlayerEventHandler)
PlayerEvent = New Timer(TimerStep, PlayerTime)
PlayerEvent.Enable(True)
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
MyPaintTimer.Enable(True)
End Sub
'Plays a segment of data based on the rubber-band we have drawn prior
to raising this event
Public Sub SetSegment(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdSeg1.Click, cmdSeg2.Click
'set local variables to use
Dim tag As Int16
Dim theButton As Button
Dim DataStart As Integer
Dim DataStop As Integer
Dim BufferSize As Integer
'initialize the direct sound objects
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
Dim MixBuffer As Microsoft.DirectX.DirectSound.SecondaryBuffer
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = False
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = True
cmdStop.Enabled = False
theButton = CType(sender, Button)
theButton.Enabled = False
'get the locations from where to read data from
DataStart = CInt(MYwave.SubChunkSizeTwo * (StartPoint.X / picWave.Width))
DataStop = CInt(MYwave.SubChunkSizeTwo * (EndPoint.X / picWave.Width))
StartPoint = Nothing
EndPoint = Nothing
'ensure that the data is aligned.. if not, noise results
DataStart = DataStart - (DataStart Mod CInt(MYwave.BlockAlign))
DataStop = DataStop - (DataStop Mod CInt(MYwave.BlockAlign))
'Get the data into a Data array
Dim DataSegment(DataStop - DataStart) As Byte
'Read the data from the Stream
DataMemStream.Position = DataStart
'Read from the stream to the array buffer
DataMemStream.Read(DataSegment, 0, DataStop - DataStart)
'Now play the sound.
'For custom sound, you must set the format
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
BufferSize = DataStop - DataStart + 1
'ensure the size is also aligned
BufferSize = BufferSize + (BufferSize Mod CInt(MYwave.BlockAlign))
Desc.BufferBytes = BufferSize
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
'Play the sound
Try
MixBuffer = New Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
MixBuffer.Stop()
MixBuffer.SetCurrentPosition(0)
If MixBuffer.Status.BufferLost Then
MixBuffer.Restore()
End If
MixBuffer.Write(0, DataSegment, Microsoft.DirectX.DirectSound.LockFlag.None)
MixBuffer.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
'open a file dialogue box
Private Sub cmdBrowse_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdBrowse.Click
dlgWav.ShowDialog(Me)
SoundFile = dlgWav.FileName
If SoundFile <> "" Then
Me.Text = "..reading file....."
Me.Refresh()
'Read the data now!
OpenWavAndSetSystem()
cmdBrowse.Enabled = False
cmdDefault.Enabled = True
cmdCustom.Enabled = True
cmdCircular.Enabled = True
cmdSeg1.Enabled = True
cmdSeg2.Enabled = True
cmdStop.Enabled = True
Me.Text = "Circular Buffers " & SoundFile
picWave.Enabled = True
End If
Me.Refresh()
End Sub
'Open the wave file and read the Data
'Load the wave file and draw the wave data graph
Sub OpenWavAndSetSystem()
'Create the Direct Sound Device object
SoundDevice = New Microsoft.DirectX.DirectSound.Device
'You have to assign the window handler to the device inorder not to
hog the sound card
'This is also required!
SoundDevice.SetCooperativeLevel(Me.Handle,
Microsoft.DirectX.DirectSound.CooperativeLevel.Normal)
'Create the wav file parser and process the wave file
MYwave = New CWAVReader(SoundFile)
'Set the timer using delegates (similar to events)
'Create a pointer to the code that runs whenever a tick event occurs
MyPaint = New System.Timers.ElapsedEventHandler(AddressOf MyPainPoint)
'Create a timer object and pass the interval and function pointer to
the code that shall
'handle the tick events using delegate (similar to event sinks)
'Draw the baseline sound wave from the data onto a canvas : bitmap
myPicture = DrawGraph(MYwave.GetSoundDataValue())
'Create a memory stream from the data contained int he sound file.
'In an actual implementation, we should be reading the data directly from the
'file and play it via stream or small segments of data in an array
DataMemStream = GetMemeoryStream(MYwave.WaveSoundData)
'Draw all other data on the clone picture graph
PlayPicture = CType(myPicture.Clone, Bitmap)
'Get the sound (wav data properties) and display on screen
Dim myData As New System.Text.StringBuilder
myData.Append("Audio Format (Must be PCM for WAV):" &
MYwave.AudioFormat & vbCrLf)
myData.Append("Bits Per Sample :" & MYwave.BitsPerSample & vbCrLf)
myData.Append("Block Align :" & MYwave.BlockAlign & "=[NumChannels *
BitsPerSample/8]" & vbCrLf)
myData.Append("Byte Rate (Number of Bytes per second):" &
MYwave.ByteRate & "=[SampleRate * NumChannels * BitsPerSample/8]" &
vbCrLf)
myData.Append("Format ID :" & MYwave.FormatID & vbCrLf)
myData.Append("Audio Frequency :" & MYwave.Frequency & vbCrLf)
myData.Append("Number of samples:" & MYwave.NumberOfSamples & vbCrLf)
myData.Append("Number of Channels (1 Mono- 2 Stereo):" &
MYwave.NumChannels & vbCrLf)
myData.Append("Audio Play Time:" & MYwave.PlayTimeSeconds.ToString & vbCrLf)
myData.Append("Sample Rate :" & MYwave.SampleRate & vbCrLf)
myData.Append("Data Chunk size :" & MYwave.SubChunkSizeTwo &
"=[NumSamples * NumChannels * BitsPerSample/8]" & vbCrLf)
myData.Append("Audio File Name :" & MYwave.WAVFileName & vbCrLf)
'Spit out the properties of the sound data to a text box
txtData.Text = myData.ToString
End Sub
#End Region
End Class
Class CWAVReader
'by ENO
'To Read a WAV File and Populate basic Properties
'On 3/11/2007
'For AfricaDotNet user group
'BigEndian bytes number 0 - 4
Private mChunkID As String
Public ReadOnly Property ChunkID() As String
Get
ChunkID = mChunkID
End Get
End Property
'from the Constructor
Private mWAVFileName As String
Public ReadOnly Property WAVFileName() As String
Get
WAVFileName = mWAVFileName
End Get
End Property
'Constructor to open stream
Sub New(ByVal SoundFilePathName As String)
mWAVFileName = SoundFilePathName
mOpen = OpenWAVStream(mWAVFileName)
'******************* MAIN WORK HERE ******************
'Parse the WAV file and read the
If mOpen Then
'Read the Header Data in THIS ORDER
'Each Read results to the File Pointer Moving
mChunkID = ReadChunkID(mWAVStream)
mChunkSize = ReadChunkSize(mWAVStream)
mFormatID = ReadFormatID(mWAVStream)
mSubChunkID = ReadSubChunkID(mWAVStream)
mSubChunkSize = ReadSubChunkSize(mWAVStream)
mAudioFormat = ReadAudioFormat(mWAVStream)
mNumChannels = ReadNumChannels(mWAVStream)
mSampleRate = ReadSampleRate(mWAVStream)
mByteRate = ReadByteRate(mWAVStream)
mBlockAlign = ReadBlockAlign(mWAVStream)
mBitsPerSample = ReadBitsPerSample(mWAVStream)
mSubChunkIDTwo = ReadSubChunkIDTwo(mWAVStream)
mSubChunkSizeTwo = ReadSubChunkSizeTwo(mWAVStream)
mWaveSoundData = ReadWAVSampleData(mWAVStream)
mWAVStream.Close()
End If
End Sub
'Property to tell if stream is open or not
Private mOpen As Boolean
Public ReadOnly Property IsWAVFileOPen() As Boolean
Get
IsWAVFileOPen = mOpen
End Get
End Property
'Open an IO Binary Stream to Read the WavFile
Private mWAVStream As IO.BinaryReader
Private Function OpenWAVStream(ByVal SoundFilePathName As String) As Boolean
Dim WAVStreamReader As IO.StreamReader
WAVStreamReader = New IO.StreamReader(SoundFilePathName)
mWAVStream = New IO.BinaryReader(WAVStreamReader.BaseStream)
If mWAVStream Is Nothing Then
Return False
Else
Return True
End If
End Function
'Dipose the resource
Sub Dispose()
If Not mWAVStream Is Nothing Then
mWAVStream.Close()
mWAVStream = Nothing
mOpen = False
End If
GC.SuppressFinalize(Me)
End Sub
'Close the stream
Protected Overrides Sub Finalize()
Me.Dispose()
MyBase.Finalize()
End Sub
'Read the ChunkID and return a string
Private Function ReadChunkID(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'Chunk size little endian bytes No. 5-13
Private mChunkSize As Long
Public ReadOnly Property ChunkSize() As Long
Get
ChunkSize = mChunkSize
End Get
End Property
'Read the ChunkID and return a string
Private Function ReadChunkSize(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadChunkSize = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'BigEndian bytes number 9 - 13
Private mFormatID As String
Public ReadOnly Property FormatID() As String
Get
FormatID = mFormatID
End Get
End Property
'Read the Format and return a string
Private Function ReadFormatID(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'Little Endian bytes number 13 - 17
Private mSubChunkID As String
Public ReadOnly Property SubChunkID() As String
Get
SubChunkID = mSubChunkID
End Get
End Property
'Read the sub chunkID and return a string
Private Function ReadSubChunkID(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'SubChunk size little endian bytes No. 17-21
Private mSubChunkSize As Long
Public ReadOnly Property SubChunkSize() As Long
Get
SubChunkSize = mSubChunkSize
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadSubChunkSize(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSubChunkSize = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Audio Format little endian bytes No. 20-22
Private mAudioFormat As Long
Public ReadOnly Property AudioFormat() As Long
Get
AudioFormat = mAudioFormat
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadAudioFormat(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadAudioFormat = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Number of Channels little endian bytes No. 22-24
Private mNumChannels As Long
Public ReadOnly Property NumChannels() As Long
Get
NumChannels = mNumChannels
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadNumChannels(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadNumChannels = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Sample Rate little endian bytes No. 24-28
Private mSampleRate As Long
Public ReadOnly Property SampleRate() As Long
Get
SampleRate = mSampleRate
End Get
End Property
Public ReadOnly Property Frequency() As Long
Get
Frequency = mSampleRate
End Get
End Property
'Get the number of samples
Public ReadOnly Property NumberOfSamples() As Long
Get
NumberOfSamples = SubChunkSizeTwo * 8 \ (NumChannels * BitsPerSample)
End Get
End Property
'Read the Sample Rate and return a string
Private Function ReadSampleRate(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSampleRate = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Byte Rate little endian bytes No. 28-32
Private mByteRate As Long
Public ReadOnly Property ByteRate() As Long
Get
ByteRate = mByteRate
End Get
End Property
'Get the play time in seconds
Public ReadOnly Property PlayTimeSeconds() As Single
Get
PlayTimeSeconds = CSng(SubChunkSizeTwo / ByteRate)
End Get
End Property
'Read the Byte Rate and return a string
Private Function ReadByteRate(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadByteRate = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Block Align little endian bytes No. 32-34
Private mBlockAlign As Long
Public ReadOnly Property BlockAlign() As Long
Get
BlockAlign = mBlockAlign
End Get
End Property
'Read the Block Align and return a string
Private Function ReadBlockAlign(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadBlockAlign = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Buits Per Sample little endian bytes No. 34-36
Private mBitsPerSample As Long
Public ReadOnly Property BitsPerSample() As Long
Get
BitsPerSample = mBitsPerSample
End Get
End Property
'Read the Bits Per Sample and return a string
Private Function ReadBitsPerSample(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadBitsPerSample = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Buits Per Sample Big endian bytes No. 36-40
Private mSubChunkIDTwo As String
Public ReadOnly Property SubChunkIDTwo() As String
Get
SubChunkIDTwo = mSubChunkIDTwo
End Get
End Property
'Read the SubChunkTwoID Sample and return a string
Private Function ReadSubChunkIDTwo(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
Dim Datastr As String
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(1)
'Ensure we have data to spit out
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
If Datastr <> "d" Then
'Read until you get data
DataBuffer = WAVIOstreamReader.ReadBytes(1)
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
While Datastr <> "d"
DataBuffer = WAVIOstreamReader.ReadBytes(1)
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
End While
DataBuffer = WAVIOstreamReader.ReadBytes(3)
TempString = DataEncoder.GetChars(DataBuffer, 0, 3)
Datastr = "d" & (TempString(0) & TempString(1) & TempString(2))
Else
DataBuffer = WAVIOstreamReader.ReadBytes(3)
TempString = DataEncoder.GetChars(DataBuffer, 0, 3)
Datastr = "d" & (TempString(0) & TempString(1) & TempString(2))
Return Datastr
End If
End Function
'Buits Per Sample little endian bytes No. 40-44
Private mSubChunkSizeTwo As Long
Public ReadOnly Property SubChunkSizeTwo() As Long
Get
SubChunkSizeTwo = mSubChunkSizeTwo
End Get
End Property
'Read the Bits Per Sample and return a string
Private Function ReadSubChunkSizeTwo(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSubChunkSizeTwo = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Get the little endian value
Private Function GetLittleEndianStringValueDuplex(ByVal DataBuffer()
As Byte) As String
Dim ValueString As String = "&h"
If DataBuffer.Length <> 0 Then
'In little endian
If Hex(DataBuffer(1)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(1))
Else
ValueString &= Hex(DataBuffer(1))
End If
If Hex(DataBuffer(0)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(0))
Else
ValueString &= Hex(DataBuffer(0))
End If
Else
ValueString = "0"
End If
GetLittleEndianStringValueDuplex = ValueString
End Function
'Get the small endian value
Private Function GetLittleEndianStringValue(ByVal DataBuffer() As
Byte) As String
Dim ValueString As String = "&h"
If DataBuffer.Length <> 0 Then
'In little endian we reverse the aray data and pad the same where the
length is 1
If Hex(DataBuffer(3)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(3))
Else
ValueString &= Hex(DataBuffer(3))
End If
If Hex(DataBuffer(2)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(2))
Else
ValueString &= Hex(DataBuffer(2))
End If
If Hex(DataBuffer(1)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(1))
Else
ValueString &= Hex(DataBuffer(1))
End If
If Hex(DataBuffer(0)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(0))
Else
ValueString &= Hex(DataBuffer(0))
End If
Else
ValueString = "0"
End If
GetLittleEndianStringValue = ValueString
End Function
'Buffers to hold Data
Private mWaveSoundData As Byte()
'Return the sound Data to display
ReadOnly Property WaveSoundData() As Byte()
Get
WaveSoundData = mWaveSoundData
End Get
End Property
'Read Data to Pans both left and right
Private Function ReadWAVSampleData(ByVal WAVIOstreamReader As
IO.BinaryReader) As Byte()
Dim tempBuffer() As Byte
tempBuffer = WAVIOstreamReader.ReadBytes(CInt(mSubChunkSizeTwo))
Return tempBuffer
End Function
'returns the wave data as a byte array
Public Function GetSoundDataValue() As Int16()
Dim DataCount As Integer
Dim tempStream As IO.BinaryReader
tempStream = New IO.BinaryReader(New IO.MemoryStream(mWaveSoundData))
tempStream.BaseStream.Position = 0
'Create a data array to hold the data read from the stream
'Read chunks of int16 from the stream (already aligned!)
Dim tempData(CInt(tempStream.BaseStream.Length / 2)) As Int16
While DataCount <= tempData.Length - 2
tempData(DataCount) = tempStream.ReadInt16()
DataCount += 1
End While
tempStream.Close()
tempStream = Nothing
Return tempData
End Function
End Class
'By ENO
'System Timer to control precicly the timer interval
'uses delegates
'For AfricaDoTnet user group
'Used for Circular buffer and wave position painting
Imports System.Timers
'Function pointer
Delegate Sub OnTimer(ByVal Obj As Object, ByVal Arg As
System.Timers.ElapsedEventArgs)
Public Class Timer
Dim mTimer As Timers.Timer
Dim MyonTimer As System.Timers.ElapsedEventHandler
Dim mtimerEnabled As Boolean
'Class constructor
Sub New(ByVal Interval As Double, ByRef handle As
System.Timers.ElapsedEventHandler)
mTimer = New Timers.Timer
mTimer.Interval = Interval
MyonTimer = handle
End Sub
'to enable / disable the timer
Sub Enable(ByVal SetIT As Boolean)
If SetIT = True Then
AddHandler mTimer.Elapsed, MyonTimer
mTimer.Enabled = SetIT
mTimer.AutoReset = True
Else
mTimer.Enabled = SetIT
mTimer.AutoReset = False
RemoveHandler mTimer.Elapsed, MyonTimer
End If
mtimerEnabled = SetIT
End Sub
'Get the status of the timer .. running or not
ReadOnly Property TimerEnabled() As Boolean
Get
mtimerEnabled = TimerEnabled
End Get
End Property
End Class
|
|
|
|
|
Hi - could you post the source code for this please?
my email si sean_clancy@yahoo.com
my site http://www.prolevelguitar.com
I made a windows form - added all the functions and there's a lot of undeclared variables there,
Cheers,
Sean
modified on Saturday, October 18, 2008 1:12 PM
|
|
|
|
|
'By Edgar Nyamweya Okioga
'.....@gamil.com
'on 24/11/2007
'For AfricaDotNet user Group (www.africadotnet.net)
'Demonstration of static and Circular buffers
'Demonstration of how to play sound portions
'Demonstration of the system.timer class
'Demonstration of double buffering and alpha blending
Public Class CircualrSounds
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents lblPos As System.Windows.Forms.Label
Friend WithEvents lbltime As System.Windows.Forms.Label
Friend WithEvents Label3 As System.Windows.Forms.Label
Friend WithEvents cmdStop As System.Windows.Forms.Button
Friend WithEvents picWave As System.Windows.Forms.PictureBox
Friend WithEvents cmdDefault As System.Windows.Forms.Button
Friend WithEvents txtData As System.Windows.Forms.TextBox
Friend WithEvents cmdCustom As System.Windows.Forms.Button
Friend WithEvents cmdCircular As System.Windows.Forms.Button
Friend WithEvents cmdSeg1 As System.Windows.Forms.Button
Friend WithEvents cmdSeg2 As System.Windows.Forms.Button
Friend WithEvents cmdBrowse As System.Windows.Forms.Button
Friend WithEvents dlgWav As System.Windows.Forms.OpenFileDialog
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.Label1 = New System.Windows.Forms.Label
Me.lblPos = New System.Windows.Forms.Label
Me.lbltime = New System.Windows.Forms.Label
Me.Label3 = New System.Windows.Forms.Label
Me.cmdStop = New System.Windows.Forms.Button
Me.picWave = New System.Windows.Forms.PictureBox
Me.cmdDefault = New System.Windows.Forms.Button
Me.txtData = New System.Windows.Forms.TextBox
Me.cmdCustom = New System.Windows.Forms.Button
Me.cmdCircular = New System.Windows.Forms.Button
Me.cmdSeg1 = New System.Windows.Forms.Button
Me.cmdSeg2 = New System.Windows.Forms.Button
Me.cmdBrowse = New System.Windows.Forms.Button
Me.dlgWav = New System.Windows.Forms.OpenFileDialog
Me.SuspendLayout()
'
'Label1
'
Me.Label1.Location = New System.Drawing.Point(160, 12)
Me.Label1.Name = "Label1"
Me.Label1.Size = New System.Drawing.Size(48, 16)
Me.Label1.TabIndex = 3
Me.Label1.Text = "Position"
'
'lblPos
'
Me.lblPos.Location = New System.Drawing.Point(216, 8)
Me.lblPos.Name = "lblPos"
Me.lblPos.Size = New System.Drawing.Size(80, 24)
Me.lblPos.TabIndex = 4
'
'lbltime
'
Me.lbltime.Location = New System.Drawing.Point(336, 8)
Me.lbltime.Name = "lbltime"
Me.lbltime.Size = New System.Drawing.Size(72, 24)
Me.lbltime.TabIndex = 6
'
'Label3
'
Me.Label3.Location = New System.Drawing.Point(304, 12)
Me.Label3.Name = "Label3"
Me.Label3.Size = New System.Drawing.Size(32, 16)
Me.Label3.TabIndex = 5
Me.Label3.Text = "Time"
'
'cmdStop
'
Me.cmdStop.Enabled = False
Me.cmdStop.Location = New System.Drawing.Point(416, 8)
Me.cmdStop.Name = "cmdStop"
Me.cmdStop.Size = New System.Drawing.Size(56, 24)
Me.cmdStop.TabIndex = 16
Me.cmdStop.Text = "Stop"
'
'picWave
'
Me.picWave.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or
System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.picWave.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
Me.picWave.Enabled = False
Me.picWave.Location = New System.Drawing.Point(8, 40)
Me.picWave.Name = "picWave"
Me.picWave.Size = New System.Drawing.Size(814, 184)
Me.picWave.TabIndex = 17
Me.picWave.TabStop = False
'
'cmdDefault
'
Me.cmdDefault.Enabled = False
Me.cmdDefault.Location = New System.Drawing.Point(88, 8)
Me.cmdDefault.Name = "cmdDefault"
Me.cmdDefault.Size = New System.Drawing.Size(64, 24)
Me.cmdDefault.TabIndex = 18
Me.cmdDefault.Text = "Default"
'
'txtData
'
Me.txtData.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or
System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.txtData.Location = New System.Drawing.Point(8, 232)
Me.txtData.Multiline = True
Me.txtData.Name = "txtData"
Me.txtData.ReadOnly = True
Me.txtData.ScrollBars = System.Windows.Forms.ScrollBars.Vertical
Me.txtData.Size = New System.Drawing.Size(816, 120)
Me.txtData.TabIndex = 19
Me.txtData.Text = ""
'
'cmdCustom
'
Me.cmdCustom.Enabled = False
Me.cmdCustom.Location = New System.Drawing.Point(488, 8)
Me.cmdCustom.Name = "cmdCustom"
Me.cmdCustom.Size = New System.Drawing.Size(56, 24)
Me.cmdCustom.TabIndex = 20
Me.cmdCustom.Text = "Stream"
'
'cmdCircular
'
Me.cmdCircular.Enabled = False
Me.cmdCircular.Location = New System.Drawing.Point(560, 8)
Me.cmdCircular.Name = "cmdCircular"
Me.cmdCircular.Size = New System.Drawing.Size(48, 24)
Me.cmdCircular.TabIndex = 21
Me.cmdCircular.Text = "Array"
'
'cmdSeg1
'
Me.cmdSeg1.Enabled = False
Me.cmdSeg1.Location = New System.Drawing.Point(624, 8)
Me.cmdSeg1.Name = "cmdSeg1"
Me.cmdSeg1.Size = New System.Drawing.Size(80, 24)
Me.cmdSeg1.TabIndex = 22
Me.cmdSeg1.Tag = "1"
Me.cmdSeg1.Text = "Capture 1"
'
'cmdSeg2
'
Me.cmdSeg2.Enabled = False
Me.cmdSeg2.Location = New System.Drawing.Point(720, 8)
Me.cmdSeg2.Name = "cmdSeg2"
Me.cmdSeg2.Size = New System.Drawing.Size(80, 24)
Me.cmdSeg2.TabIndex = 23
Me.cmdSeg2.Tag = "2"
Me.cmdSeg2.Text = "Capture 2"
'
'cmdBrowse
'
Me.cmdBrowse.Location = New System.Drawing.Point(8, 8)
Me.cmdBrowse.Name = "cmdBrowse"
Me.cmdBrowse.Size = New System.Drawing.Size(72, 24)
Me.cmdBrowse.TabIndex = 24
Me.cmdBrowse.Text = "File..."
'
'dlgWav
'
Me.dlgWav.Filter = """WAV Files""|*.wav"
Me.dlgWav.ReadOnlyChecked = True
Me.dlgWav.RestoreDirectory = True
Me.dlgWav.Title = """Select WAV file to open"""
'
'CircualrSounds
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(830, 356)
Me.Controls.Add(Me.cmdBrowse)
Me.Controls.Add(Me.cmdSeg2)
Me.Controls.Add(Me.cmdSeg1)
Me.Controls.Add(Me.cmdCircular)
Me.Controls.Add(Me.cmdCustom)
Me.Controls.Add(Me.txtData)
Me.Controls.Add(Me.cmdDefault)
Me.Controls.Add(Me.picWave)
Me.Controls.Add(Me.cmdStop)
Me.Controls.Add(Me.lbltime)
Me.Controls.Add(Me.Label3)
Me.Controls.Add(Me.lblPos)
Me.Controls.Add(Me.Label1)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.Name = "CircualrSounds"
Me.Text = "Circular Buffers"
Me.ResumeLayout(False)
End Sub
#End Region
#Region "Variables in use"
'Direct Sound Device variable
Dim SoundDevice As Microsoft.DirectX.DirectSound.Device
'Direct Sound secondary buffer to hold data for mixing
Dim SbufferOriginal As Microsoft.DirectX.DirectSound.SecondaryBuffer
'Sample Music
Dim SoundFile As String
'Wave file data parser
Dim MYwave As CWAVReader
'Event handler to process tick intervals
Dim MyPaint As System.Timers.ElapsedEventHandler
'Timer object
Dim MyPaintTimer As Timer
'Varibale to accumulate ticks in milliseconds
Dim MyTime As Integer
'Variable to hold total play time of the wav file
Dim TotalTime As Single
'Variable to holed wav data to draw graph
Dim Data() As Byte
'Wave data picture
Dim myPicture As Bitmap
'Circular buffer update interval variable
Dim TimerStep As Integer
'Play indicator and wav data picture composite
Dim PlayPicture As Bitmap
'For Circular buffer
'Next location in circular buffer we can write
Dim NextWritePos As Integer
'Total bytes writen to circular buffer
Dim PlayerPosition As Integer
'Maximum latency control time in milliseconds....
Dim Latency As Integer = 300
'Memeroy stream to hold actual sound Data.
Dim DataMemStream As IO.MemoryStream
'Event handler to process tick intervals
Dim PlayerTime As System.Timers.ElapsedEventHandler
'Timer object
Dim PlayerEvent As Timer
'DataArray for Circular sound
Dim DataArray As Byte()
'varibale to control if we play using memory stream or Data Array
Dim IsArray As Boolean = False
'Varibales to capture the portions of the sound we want to play
Dim StartPoint As Point
Dim EndPoint As Point
#End Region
#Region "Graphic methods and functions"
'Draws the Sound data as a wave onto a canvas using double buffering
to speed up work
Function DrawGraph(ByVal Data() As Int16) As Bitmap
'Create the Canvas
Dim myBitmap As System.Drawing.Bitmap
'Create an array to hold the wav data which we can sort
Dim tempData(Data.Length) As Integer
'Copy the data to the temporary location .. can be done better
Data.CopyTo(tempData, 0)
'Sort the array to get the maximum and minimum Y values
Array.Sort(tempData)
'generate the Canvas (drawing board in memory
myBitmap = New Bitmap(picWave.Width, picWave.Height)
'Create your paint brush, pens and drawing objects
Dim myGraphic As System.Drawing.Graphics
myGraphic = Graphics.FromImage(myBitmap)
'draw the backgound with a custom color
myGraphic.Clear(Color.FromArgb(181, 223, 225))
'Get the paramerets to draw the data and scale to fit the canvans but
draw form the middle
Dim YMax As Integer = tempData(Data.Length - 1)
Dim YMin As Integer = tempData(0)
Dim XMax As Integer = picWave.Width
Dim Xmin As Integer = 0
'Create an array of points to draw a line
Dim PicPoint(Data.Length - 1) As System.Drawing.PointF
Dim Count As Integer
Dim Step1 As Single 'Scale the data between Ymax and Ymin
Dim Step2 As Single 'Scale the data further to fit between the canvas hieght
Dim step3 As Single 'Draw the point form the middle of the canvas
Dim Mypen As New Pen(Color.FromArgb(24, 101, 123))
'Draw the lines from the series of points representing the sound wav
For Count = 0 To Data.Length - 1
Step1 = CSng(Data(Count) / (YMax - YMin))
Step2 = CSng(Step1 * picWave.Height / 2)
step3 = CSng(Step2 + (picWave.Height / 2))
PicPoint(Count) = New System.Drawing.PointF(CSng(XMax * (Count /
Data.Length)), step3)
Next
'Draw the lines
myGraphic.DrawLines(Mypen, PicPoint)
'Draw graphics onto canvas
myGraphic.DrawImage(myBitmap, picWave.Width, picWave.Height)
'return the picture memeory object
Return (myBitmap)
End Function
'The handler to the timer tick event :
'Paints the location of the player pointer on the sound graph as the
data is played
Sub MyPainPoint(ByVal obj As Object, ByVal Args As
System.Timers.ElapsedEventArgs)
'Control to ensure we stop when the total cumulated bytes read is
equal to the total data size
If PlayerPosition >= MYwave.SubChunkSizeTwo Then
StopPlay()
'Update the labels
lblPos.Text = MYwave.SubChunkSizeTwo.ToString
lbltime.Text = MYwave.PlayTimeSeconds().ToString
End If
'Draw a line red from top to bottom showing the curent position based
on play position
'The PlayerPOsition is the absolute location of the current played data
'This is taken as a ratio of the total data size adn scaled to the
width of the picture
'control
Dim XPos As Single = CSng((PlayerPosition / MYwave.SubChunkSizeTwo) *
picWave.Width)
Dim posgraphic As Graphics
'Clone the original canvas (this has the original sound graph). We do
not need to
're-draw this large graph as it willtake much processing time!
PlayPicture = CType(myPicture.Clone, Bitmap)
posgraphic = Graphics.FromImage(PlayPicture)
'Draw the pointer
Dim Mypen As Pen = New Pen(Color.Red)
posgraphic.DrawLine(Mypen, XPos, 0, XPos, picWave.Height)
'Draw line to myPicture
MyTime += TimerStep
posgraphic.DrawImage(PlayPicture, picWave.Width, picWave.Height)
'Update the status on the labels
lblPos.Text = PlayerPosition.ToString
lbltime.Text = (MyTime / 1000).ToString
'force a redraw of the picture updated.
Me.Invalidate(New Drawing.Rectangle(picWave.Left, picWave.Top,
picWave.Width, picWave.Height))
End Sub
'Capture the start point
Private Sub picWave_MouseDown(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles picWave.MouseDown
'Get the position of the mouse and draw a line
StartPoint = New Point
If e.Button = MouseButtons.Left Then
StartPoint.X = e.X
StartPoint.Y = e.Y
End If
End Sub
'Draw a band over the data we want to capture
Private Sub picWave_MouseMove(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles picWave.MouseMove
If e.Button = MouseButtons.Left Then
If StartPoint.IsEmpty = False Then
EndPoint = New Point
EndPoint.X = e.X
EndPoint.Y = e.Y
DrawSelection()
End If
End If
End Sub
'Draw the band over the selection
Sub DrawSelection()
'Draw a rubberband
Dim posgraphic As Graphics
Dim RubberRect As Rectangle
RubberRect = New Rectangle(StartPoint.X, 0, EndPoint.X - StartPoint.X,
picWave.Height - 3)
'Clone the original canvas
PlayPicture = CType(myPicture.Clone, Bitmap)
posgraphic = Graphics.FromImage(PlayPicture)
'Draw the pointer
Dim Mypen As Pen = New Pen(Color.Green)
'Create a transparent brush using alpha blending techniques
Dim MyBrush As SolidBrush = New SolidBrush(Color.FromArgb(85, 204, 32, 92))
'Draw the boarder
posgraphic.DrawRectangle(Mypen, RubberRect)
'Fill the color
posgraphic.FillRectangle(MyBrush, RubberRect)
'Draw the picture on the form with the updated section of music to clip
posgraphic.DrawImage(PlayPicture, picWave.Width, picWave.Height)
'redraw the portion only
Me.Invalidate(New Drawing.Rectangle(picWave.Left, picWave.Top,
picWave.Width, picWave.Height))
End Sub
'Complete the drawing and capture the start and width/hieght of the rubber band
Private Sub picWave_MouseUp(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles picWave.MouseUp
EndPoint.X = e.X
EndPoint.Y = e.Y
DrawSelection()
End Sub
#End Region
#Region "Sound functions and helper code"
'Creates a memory stream from Array Data to support playing sound from
a memory stream
'In an actual implementation, this would be reading data from an
actual file on the Harddisk
Function GetMemeoryStream(ByVal Data() As Byte) As IO.MemoryStream
Return New IO.MemoryStream(Data)
End Function
'Stop processing any sound
Private Sub cmdStop_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdStop.Click
StopPlay()
cmdBrowse.Enabled = False
cmdDefault.Enabled = True
cmdCustom.Enabled = True
cmdCircular.Enabled = True
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = False
End Sub
'using the form events paint, redraw the picture
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
'Draw the picture of the updated pointer location
picWave.Image = PlayPicture
End Sub
'Stop playing the sound
Sub StopPlay()
'Get the status of the player
If SbufferOriginal.Status.Playing Then
SbufferOriginal.Stop()
'shut the paint timer
If Not IsNothing(MyPaintTimer) Then
MyPaintTimer.Enable(False)
MyPaintTimer = Nothing
End If
'shut the player timer
If Not IsNothing(PlayerTime) Then
PlayerEvent.Enable(False)
PlayerEvent = Nothing
End If
End If
End Sub
'Update the Circular buffer based on either a stream of data array
Sub PlayerEventHandler(ByVal obj As Object, ByVal Args As
System.Timers.ElapsedEventArgs)
'Stop if we have read all data
If PlayerPosition >= MYwave.SubChunkSizeTwo Then
StopPlay()
End If
'If an array, use the dataArray to top up new data to the circular buffer
If IsArray = False Then
'Get the amount of data to write and thereafter write the data
WriteData(GetPlayedSize())
Else
WriteDataArray(GetPlayedSize())
End If
End Sub
'Play a sound wave using a default static buffer
Private Sub cmdDefault_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdDefault.Click
TimerStep = 25 'Interval to draw the pointer on the graph
cmdCustom.Enabled = False
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
Try
MyPaintTimer = New Timer(TimerStep, MyPaint)
TotalTime = CSng(MYwave.SubChunkSizeTwo / MYwave.ByteRate)
SbufferOriginal = New
Microsoft.DirectX.DirectSound.SecondaryBuffer(SoundFile, SoundDevice)
Me.Text = "Buffer size :" & SbufferOriginal.Caps.BufferBytes.ToString
'Start the timer
MyPaintTimer.Enable(True)
MyTime = 0
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
'Use a circular buffer to update the primary buffer with data
Private Sub cmdCustom_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdCustom.Click
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = True
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = True
IsArray = False
'create a format object tro describe the wave properties
'These are not random numbers.. they are corelated based on
'formulae such that a change in one will result to change in
'related properties. Ref the contents of the label during run time
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
'Create the buffer description and buffer size
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
Desc.BufferBytes = CInt(MYwave.SampleRate) * 1
'Add capabilites of the buffer
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
'Create a secondary sound buffer
SbufferOriginal = New
Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
TimerStep = Latency \ 10
MyTime = 0
SbufferOriginal.Stop()
SbufferOriginal.SetCurrentPosition(0)
WriteData(SbufferOriginal.Caps.BufferBytes)
NextWritePos = 0
PlayerPosition = 0
MyPaintTimer = New Timer(TimerStep, MyPaint)
'Create a timer
PlayerTime = New System.Timers.ElapsedEventHandler(AddressOf PlayerEventHandler)
PlayerEvent = New Timer(TimerStep, PlayerTime)
PlayerEvent.Enable(True)
'Play the sound
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
MyPaintTimer.Enable(True)
End Sub
'get the played data size
Function GetPlayedSize() As Integer
Dim Pos As Integer
Pos = SbufferOriginal.PlayPosition
If Pos < NextWritePos Then
Return Pos + (SbufferOriginal.Caps.BufferBytes - NextWritePos)
Else
Return Pos - NextWritePos
End If
End Function
'Ensure we have a latency of maximum 300 milliseconds
Function TimeToDataSize(ByVal Latency As Integer) As Integer
Dim DataSize As Integer
DataSize = CInt(Latency * SbufferOriginal.Format.AverageBytesPerSecond / 1000)
DataSize = DataSize - (DataSize Mod SbufferOriginal.Format.BlockAlign)
Return DataSize
End Function
'Write data to the circular buffer (secondary buffer that is)
Sub WriteData(ByVal DataSize As Integer)
Dim Tocopy As Integer
'make sure the data is less than the latency amount of 300ms
Tocopy = Math.Min(DataSize, TimeToDataSize(Latency))
'Only write data if there is something to write!
If Tocopy > 0 Then
'Restore the buffer
If SbufferOriginal.Status.BufferLost Then
SbufferOriginal.Restore()
End If
'Copy the data to the buffer
'The DataMemStream is a binary stream object. This can also be a
'large WAV file still on 'harddisk
SbufferOriginal.Write(NextWritePos, DataMemStream, Tocopy,
Microsoft.DirectX.DirectSound.LockFlag.None)
'As data is read form the DataMemStream, the position (internal of the
structure) advances
'by the size of Tcopy
'Advance the total cumulative bytes read
PlayerPosition += Tocopy
'advance the NextWrite pointer
NextWritePos += Tocopy
'If we are at the end, we wrap round
If NextWritePos >= SbufferOriginal.Caps.BufferBytes Then
NextWritePos = NextWritePos - SbufferOriginal.Caps.BufferBytes
End If
End If
End Sub
'Write data to a Data Array.... similar to the above..using memory a stream
Sub WriteDataArray(ByVal DataSize As Integer)
Dim Tocopy As Integer
'ensure we do not have a big latency . the maximum is 300 ms
Tocopy = Math.Min(DataSize, TimeToDataSize(Latency))
'is we have data, then write to the array and play
If Tocopy > 0 Then
'Restore the buffer
If SbufferOriginal.Status.BufferLost Then
SbufferOriginal.Restore()
End If
'Copy the data to the Array
're-create the data array (this is very slow!!)
ReDim DataArray(Tocopy - 1)
'Position the memory stream to the last location we read from.
DataMemStream.Position = PlayerPosition
'Copy the data from the stream to the array
DataMemStream.Read(DataArray, 0, Tocopy - 1)
'Write the data to the seconadry buffer
SbufferOriginal.Write(NextWritePos, DataArray,
Microsoft.DirectX.DirectSound.LockFlag.None)
'Adavance the pointers
PlayerPosition += Tocopy
NextWritePos += Tocopy
If NextWritePos >= SbufferOriginal.Caps.BufferBytes Then
NextWritePos = NextWritePos - SbufferOriginal.Caps.BufferBytes
End If
End If
End Sub
'Create a circular buffer based on the data array... similar to the
former button...
Private Sub cmdCircular_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles cmdCircular.Click
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = False
cmdCircular.Enabled = True
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = True
IsArray = True
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
Desc.BufferBytes = CInt(MYwave.SampleRate) * 1
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
SbufferOriginal = New
Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
TimerStep = Latency \ 5
MyTime = 0
SbufferOriginal.Stop()
SbufferOriginal.SetCurrentPosition(0)
WriteDataArray(SbufferOriginal.Caps.BufferBytes)
NextWritePos = 0
PlayerPosition = 0
MyPaintTimer = New Timer(TimerStep, MyPaint)
PlayerTime = New System.Timers.ElapsedEventHandler(AddressOf PlayerEventHandler)
PlayerEvent = New Timer(TimerStep, PlayerTime)
PlayerEvent.Enable(True)
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
MyPaintTimer.Enable(True)
End Sub
'Plays a segment of data based on the rubber-band we have drawn prior
to raising this event
Public Sub SetSegment(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdSeg1.Click, cmdSeg2.Click
'set local variables to use
Dim tag As Int16
Dim theButton As Button
Dim DataStart As Integer
Dim DataStop As Integer
Dim BufferSize As Integer
'initialize the direct sound objects
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
Dim MixBuffer As Microsoft.DirectX.DirectSound.SecondaryBuffer
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = False
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = True
cmdStop.Enabled = False
theButton = CType(sender, Button)
theButton.Enabled = False
'get the locations from where to read data from
DataStart = CInt(MYwave.SubChunkSizeTwo * (StartPoint.X / picWave.Width))
DataStop = CInt(MYwave.SubChunkSizeTwo * (EndPoint.X / picWave.Width))
StartPoint = Nothing
EndPoint = Nothing
'ensure that the data is aligned.. if not, noise results
DataStart = DataStart - (DataStart Mod CInt(MYwave.BlockAlign))
DataStop = DataStop - (DataStop Mod CInt(MYwave.BlockAlign))
'Get the data into a Data array
Dim DataSegment(DataStop - DataStart) As Byte
'Read the data from the Stream
DataMemStream.Position = DataStart
'Read from the stream to the array buffer
DataMemStream.Read(DataSegment, 0, DataStop - DataStart)
'Now play the sound.
'For custom sound, you must set the format
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
BufferSize = DataStop - DataStart + 1
'ensure the size is also aligned
BufferSize = BufferSize + (BufferSize Mod CInt(MYwave.BlockAlign))
Desc.BufferBytes = BufferSize
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
'Play the sound
Try
MixBuffer = New Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
MixBuffer.Stop()
MixBuffer.SetCurrentPosition(0)
If MixBuffer.Status.BufferLost Then
MixBuffer.Restore()
End If
MixBuffer.Write(0, DataSegment, Microsoft.DirectX.DirectSound.LockFlag.None)
MixBuffer.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
'open a file dialogue box
Private Sub cmdBrowse_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdBrowse.Click
dlgWav.ShowDialog(Me)
SoundFile = dlgWav.FileName
If SoundFile <> "" Then
Me.Text = "..reading file....."
Me.Refresh()
'Read the data now!
OpenWavAndSetSystem()
cmdBrowse.Enabled = False
cmdDefault.Enabled = True
cmdCustom.Enabled = True
cmdCircular.Enabled = True
cmdSeg1.Enabled = True
cmdSeg2.Enabled = True
cmdStop.Enabled = True
Me.Text = "Circular Buffers " & SoundFile
picWave.Enabled = True
End If
Me.Refresh()
End Sub
'Open the wave file and read the Data
'Load the wave file and draw the wave data graph
Sub OpenWavAndSetSystem()
'Create the Direct Sound Device object
SoundDevice = New Microsoft.DirectX.DirectSound.Device
'You have to assign the window handler to the device inorder not to
hog the sound card
'This is also required!
SoundDevice.SetCooperativeLevel(Me.Handle,
Microsoft.DirectX.DirectSound.CooperativeLevel.Normal)
'Create the wav file parser and process the wave file
MYwave = New CWAVReader(SoundFile)
'Set the timer using delegates (similar to events)
'Create a pointer to the code that runs whenever a tick event occurs
MyPaint = New System.Timers.ElapsedEventHandler(AddressOf MyPainPoint)
'Create a timer object and pass the interval and function pointer to
the code that shall
'handle the tick events using delegate (similar to event sinks)
'Draw the baseline sound wave from the data onto a canvas : bitmap
myPicture = DrawGraph(MYwave.GetSoundDataValue())
'Create a memory stream from the data contained int he sound file.
'In an actual implementation, we should be reading the data directly from the
'file and play it via stream or small segments of data in an array
DataMemStream = GetMemeoryStream(MYwave.WaveSoundData)
'Draw all other data on the clone picture graph
PlayPicture = CType(myPicture.Clone, Bitmap)
'Get the sound (wav data properties) and display on screen
Dim myData As New System.Text.StringBuilder
myData.Append("Audio Format (Must be PCM for WAV):" &
MYwave.AudioFormat & vbCrLf)
myData.Append("Bits Per Sample :" & MYwave.BitsPerSample & vbCrLf)
myData.Append("Block Align :" & MYwave.BlockAlign & "=[NumChannels *
BitsPerSample/8]" & vbCrLf)
myData.Append("Byte Rate (Number of Bytes per second):" &
MYwave.ByteRate & "=[SampleRate * NumChannels * BitsPerSample/8]" &
vbCrLf)
myData.Append("Format ID :" & MYwave.FormatID & vbCrLf)
myData.Append("Audio Frequency :" & MYwave.Frequency & vbCrLf)
myData.Append("Number of samples:" & MYwave.NumberOfSamples & vbCrLf)
myData.Append("Number of Channels (1 Mono- 2 Stereo):" &
MYwave.NumChannels & vbCrLf)
myData.Append("Audio Play Time:" & MYwave.PlayTimeSeconds.ToString & vbCrLf)
myData.Append("Sample Rate :" & MYwave.SampleRate & vbCrLf)
myData.Append("Data Chunk size :" & MYwave.SubChunkSizeTwo &
"=[NumSamples * NumChannels * BitsPerSample/8]" & vbCrLf)
myData.Append("Audio File Name :" & MYwave.WAVFileName & vbCrLf)
'Spit out the properties of the sound data to a text box
txtData.Text = myData.ToString
End Sub
#End Region
End Class
Class CWAVReader
'by ENO
'To Read a WAV File and Populate basic Properties
'On 3/11/2007
'For AfricaDotNet user group
'BigEndian bytes number 0 - 4
Private mChunkID As String
Public ReadOnly Property ChunkID() As String
Get
ChunkID = mChunkID
End Get
End Property
'from the Constructor
Private mWAVFileName As String
Public ReadOnly Property WAVFileName() As String
Get
WAVFileName = mWAVFileName
End Get
End Property
'Constructor to open stream
Sub New(ByVal SoundFilePathName As String)
mWAVFileName = SoundFilePathName
mOpen = OpenWAVStream(mWAVFileName)
'******************* MAIN WORK HERE ******************
'Parse the WAV file and read the
If mOpen Then
'Read the Header Data in THIS ORDER
'Each Read results to the File Pointer Moving
mChunkID = ReadChunkID(mWAVStream)
mChunkSize = ReadChunkSize(mWAVStream)
mFormatID = ReadFormatID(mWAVStream)
mSubChunkID = ReadSubChunkID(mWAVStream)
mSubChunkSize = ReadSubChunkSize(mWAVStream)
mAudioFormat = ReadAudioFormat(mWAVStream)
mNumChannels = ReadNumChannels(mWAVStream)
mSampleRate = ReadSampleRate(mWAVStream)
mByteRate = ReadByteRate(mWAVStream)
mBlockAlign = ReadBlockAlign(mWAVStream)
mBitsPerSample = ReadBitsPerSample(mWAVStream)
mSubChunkIDTwo = ReadSubChunkIDTwo(mWAVStream)
mSubChunkSizeTwo = ReadSubChunkSizeTwo(mWAVStream)
mWaveSoundData = ReadWAVSampleData(mWAVStream)
mWAVStream.Close()
End If
End Sub
'Property to tell if stream is open or not
Private mOpen As Boolean
Public ReadOnly Property IsWAVFileOPen() As Boolean
Get
IsWAVFileOPen = mOpen
End Get
End Property
'Open an IO Binary Stream to Read the WavFile
Private mWAVStream As IO.BinaryReader
Private Function OpenWAVStream(ByVal SoundFilePathName As String) As Boolean
Dim WAVStreamReader As IO.StreamReader
WAVStreamReader = New IO.StreamReader(SoundFilePathName)
mWAVStream = New IO.BinaryReader(WAVStreamReader.BaseStream)
If mWAVStream Is Nothing Then
Return False
Else
Return True
End If
End Function
'Dipose the resource
Sub Dispose()
If Not mWAVStream Is Nothing Then
mWAVStream.Close()
mWAVStream = Nothing
mOpen = False
End If
GC.SuppressFinalize(Me)
End Sub
'Close the stream
Protected Overrides Sub Finalize()
Me.Dispose()
MyBase.Finalize()
End Sub
'Read the ChunkID and return a string
Private Function ReadChunkID(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'Chunk size little endian bytes No. 5-13
Private mChunkSize As Long
Public ReadOnly Property ChunkSize() As Long
Get
ChunkSize = mChunkSize
End Get
End Property
'Read the ChunkID and return a string
Private Function ReadChunkSize(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadChunkSize = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'BigEndian bytes number 9 - 13
Private mFormatID As String
Public ReadOnly Property FormatID() As String
Get
FormatID = mFormatID
End Get
End Property
'Read the Format and return a string
Private Function ReadFormatID(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'Little Endian bytes number 13 - 17
Private mSubChunkID As String
Public ReadOnly Property SubChunkID() As String
Get
SubChunkID = mSubChunkID
End Get
End Property
'Read the sub chunkID and return a string
Private Function ReadSubChunkID(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'SubChunk size little endian bytes No. 17-21
Private mSubChunkSize As Long
Public ReadOnly Property SubChunkSize() As Long
Get
SubChunkSize = mSubChunkSize
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadSubChunkSize(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSubChunkSize = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Audio Format little endian bytes No. 20-22
Private mAudioFormat As Long
Public ReadOnly Property AudioFormat() As Long
Get
AudioFormat = mAudioFormat
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadAudioFormat(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadAudioFormat = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Number of Channels little endian bytes No. 22-24
Private mNumChannels As Long
Public ReadOnly Property NumChannels() As Long
Get
NumChannels = mNumChannels
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadNumChannels(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadNumChannels = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Sample Rate little endian bytes No. 24-28
Private mSampleRate As Long
Public ReadOnly Property SampleRate() As Long
Get
SampleRate = mSampleRate
End Get
End Property
Public ReadOnly Property Frequency() As Long
Get
Frequency = mSampleRate
End Get
End Property
'Get the number of samples
Public ReadOnly Property NumberOfSamples() As Long
Get
NumberOfSamples = SubChunkSizeTwo * 8 \ (NumChannels * BitsPerSample)
End Get
End Property
'Read the Sample Rate and return a string
Private Function ReadSampleRate(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSampleRate = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Byte Rate little endian bytes No. 28-32
Private mByteRate As Long
Public ReadOnly Property ByteRate() As Long
Get
ByteRate = mByteRate
End Get
End Property
'Get the play time in seconds
Public ReadOnly Property PlayTimeSeconds() As Single
Get
PlayTimeSeconds = CSng(SubChunkSizeTwo / ByteRate)
End Get
End Property
'Read the Byte Rate and return a string
Private Function ReadByteRate(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadByteRate = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Block Align little endian bytes No. 32-34
Private mBlockAlign As Long
Public ReadOnly Property BlockAlign() As Long
Get
BlockAlign = mBlockAlign
End Get
End Property
'Read the Block Align and return a string
Private Function ReadBlockAlign(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadBlockAlign = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Buits Per Sample little endian bytes No. 34-36
Private mBitsPerSample As Long
Public ReadOnly Property BitsPerSample() As Long
Get
BitsPerSample = mBitsPerSample
End Get
End Property
'Read the Bits Per Sample and return a string
Private Function ReadBitsPerSample(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadBitsPerSample = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Buits Per Sample Big endian bytes No. 36-40
Private mSubChunkIDTwo As String
Public ReadOnly Property SubChunkIDTwo() As String
Get
SubChunkIDTwo = mSubChunkIDTwo
End Get
End Property
'Read the SubChunkTwoID Sample and return a string
Private Function ReadSubChunkIDTwo(ByVal WAVIOstreamReader As
IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
Dim Datastr As String
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(1)
'Ensure we have data to spit out
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
If Datastr <> "d" Then
'Read until you get data
DataBuffer = WAVIOstreamReader.ReadBytes(1)
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
While Datastr <> "d"
DataBuffer = WAVIOstreamReader.ReadBytes(1)
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
End While
DataBuffer = WAVIOstreamReader.ReadBytes(3)
TempString = DataEncoder.GetChars(DataBuffer, 0, 3)
Datastr = "d" & (TempString(0) & TempString(1) & TempString(2))
Else
DataBuffer = WAVIOstreamReader.ReadBytes(3)
TempString = DataEncoder.GetChars(DataBuffer, 0, 3)
Datastr = "d" & (TempString(0) & TempString(1) & TempString(2))
Return Datastr
End If
End Function
'Buits Per Sample little endian bytes No. 40-44
Private mSubChunkSizeTwo As Long
Public ReadOnly Property SubChunkSizeTwo() As Long
Get
SubChunkSizeTwo = mSubChunkSizeTwo
End Get
End Property
'Read the Bits Per Sample and return a string
Private Function ReadSubChunkSizeTwo(ByVal WAVIOstreamReader As
IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSubChunkSizeTwo = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Get the little endian value
Private Function GetLittleEndianStringValueDuplex(ByVal DataBuffer()
As Byte) As String
Dim ValueString As String = "&h"
If DataBuffer.Length <> 0 Then
'In little endian
If Hex(DataBuffer(1)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(1))
Else
ValueString &= Hex(DataBuffer(1))
End If
If Hex(DataBuffer(0)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(0))
Else
ValueString &= Hex(DataBuffer(0))
End If
Else
ValueString = "0"
End If
GetLittleEndianStringValueDuplex = ValueString
End Function
'Get the small endian value
Private Function GetLittleEndianStringValue(ByVal DataBuffer() As
Byte) As String
Dim ValueString As String = "&h"
If DataBuffer.Length <> 0 Then
'In little endian we reverse the aray data and pad the same where the
length is 1
If Hex(DataBuffer(3)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(3))
Else
ValueString &= Hex(DataBuffer(3))
End If
If Hex(DataBuffer(2)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(2))
Else
ValueString &= Hex(DataBuffer(2))
End If
If Hex(DataBuffer(1)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(1))
Else
ValueString &= Hex(DataBuffer(1))
End If
If Hex(DataBuffer(0)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(0))
Else
ValueString &= Hex(DataBuffer(0))
End If
Else
ValueString = "0"
End If
GetLittleEndianStringValue = ValueString
End Function
'Buffers to hold Data
Private mWaveSoundData As Byte()
'Return the sound Data to display
ReadOnly Property WaveSoundData() As Byte()
Get
WaveSoundData = mWaveSoundData
End Get
End Property
'Read Data to Pans both left and right
Private Function ReadWAVSampleData(ByVal WAVIOstreamReader As
IO.BinaryReader) As Byte()
Dim tempBuffer() As Byte
tempBuffer = WAVIOstreamReader.ReadBytes(CInt(mSubChunkSizeTwo))
Return tempBuffer
End Function
'returns the wave data as a byte array
Public Function GetSoundDataValue() As Int16()
Dim DataCount As Integer
Dim tempStream As IO.BinaryReader
tempStream = New IO.BinaryReader(New IO.MemoryStream(mWaveSoundData))
tempStream.BaseStream.Position = 0
'Create a data array to hold the data read from the stream
'Read chunks of int16 from the stream (already aligned!)
Dim tempData(CInt(tempStream.BaseStream.Length / 2)) As Int16
While DataCount <= tempData.Length - 2
tempData(DataCount) = tempStream.ReadInt16()
DataCount += 1
End While
tempStream.Close()
tempStream = Nothing
Return tempData
End Function
End Class
'By ENO
'System Timer to control precicly the timer interval
'uses delegates
'For AfricaDoTnet user group
'Used for Circular buffer and wave position painting
Imports System.Timers
'Function pointer
Delegate Sub OnTimer(ByVal Obj As Object, ByVal Arg As
System.Timers.ElapsedEventArgs)
Public Class Timer
Dim mTimer As Timers.Timer
Dim MyonTimer As System.Timers.ElapsedEventHandler
Dim mtimerEnabled As Boolean
'Class constructor
Sub New(ByVal Interval As Double, ByRef handle As
System.Timers.ElapsedEventHandler)
mTimer = New Timers.Timer
mTimer.Interval = Interval
MyonTimer = handle
End Sub
'to enable / disable the timer
Sub Enable(ByVal SetIT As Boolean)
If SetIT = True Then
AddHandler mTimer.Elapsed, MyonTimer
mTimer.Enabled = SetIT
mTimer.AutoReset = True
Else
mTimer.Enabled = SetIT
mTimer.AutoReset = False
RemoveHandler mTimer.Elapsed, MyonTimer
End If
mtimerEnabled = SetIT
End Sub
'Get the status of the timer .. running or not
ReadOnly Property TimerEnabled() As Boolean
Get
mtimerEnabled = TimerEnabled
End Get
End Property
End Class
|
|
|
|
|
Hi there - this was exactly what I was looking for!
What would I add if I wanted to change the playback speed? to 25%, 50% and 75%?
I know this is a tough call - but it'd be really awesome!
Cheers,
Sean
|
|
|
|
|
Hi there,
This may not be what you are looking for but the theory is that you change the sampling frequency.
So, if the original sampling frequency is Fs and you want to hear it at half the speed you set the new sampling frequency to Fs/2. This will work in DSP applications but I don't know if and how ENO has declared the sampling frequency in his code. I haven't look into his article yet.
|
|
|
|
|
Add a button and use the following two lines:
SbufferOriginal.Frequency = FreqComboBox.SelectedItem * SbufferOriginal.Frequency
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Default)
FreqComboBox is a ComboBox through which you can control the speed change (e.g. integer values)
|
|
|
|
|
The app produces an exception with loaderlock if compiled with vs2005 or above.
here is the quick trick to avoid the loaderlock:
In VS2005, go to the Debug->Exceptions menu. Within the Exceptions dialog expand the "Managed Debugging Assistants" node. Look for LoaderLock and uncheck the "Thrown" column.
|
|
|
|
|
|
Hello
The Article is Very Useful. It was Gr8.It would be helpful if you provide
full source code for this article .Or you can send to my mail id srileena9999@yahoo.co.in
Thanks
|
|
|
|
|
Please can you make for me a very simple application for Wave file Recording with microphone (VB.Net)?
Thank you in advance.
My email : nabil.ghenima@topnet.tn
Best regards.
|
|
|
|
|
Please can you make for me a very simple application for Wave file Recording with microphone (VB.Net)?
Thonk you in advance.
My email : nabil.ghenima@topnet.tn
Best regards.
|
|
|
|
|
Im very interested in source code too. Can you send it to Shabetya.Dmitriy@gmail.com?
|
|
|
|
|
my e-mail is jackyxinli@hotmail.com. thanks.
|
|
|
|
|
You have a gr8 article done, thanks a lot for your effort, but the link to the source code is abscent and the message that u referred to the source code in isnt there anymore. I wish you code re-post it again, or you can send it directly to my email mina.nsami@gmail.com
Thanks a lot
|
|
|
|
|
Can anyone please suggest an example for DirectSoundCapture to record audio from Microphone/Line-In.
|
|
|
|
|
Can you explain me what you make in the function "OpenWAVStream(mWAVFileName)" ???
|
|
|
|
|
Takes a wav file (file name) and opens it as stream. If the stream cannot be opened then the return type is false. Please refer to an post i made on the source code ...
Private Function OpenWAVStream(ByVal SoundFilePathName As String) As Boolean
Dim WAVStreamReader As IO.StreamReader
WAVStreamReader = New IO.StreamReader(SoundFilePathName)
mWAVStream = New IO.BinaryReader(WAVStreamReader.BaseStream)
If mWAVStream Is Nothing Then
Return False
Else
Return True
End If
End Function
best
|
|
|
|
|
|
It is a very good project, but have you got the code please ?
|
|
|
|
|
Look at the comment post Source Code.
Cheers
|
|
|
|
|
'By Edgar Nyamweya Okioga
'.....@gamil.com
'on 24/11/2007
'For AfricaDotNet user Group (www.africadotnet.net)
'Demonstration of static and Circular buffers
'Demonstration of how to play sound portions
'Demonstration of the system.timer class
'Demonstration of double buffering and alpha blending
Public Class CircualrSounds
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents lblPos As System.Windows.Forms.Label
Friend WithEvents lbltime As System.Windows.Forms.Label
Friend WithEvents Label3 As System.Windows.Forms.Label
Friend WithEvents cmdStop As System.Windows.Forms.Button
Friend WithEvents picWave As System.Windows.Forms.PictureBox
Friend WithEvents cmdDefault As System.Windows.Forms.Button
Friend WithEvents txtData As System.Windows.Forms.TextBox
Friend WithEvents cmdCustom As System.Windows.Forms.Button
Friend WithEvents cmdCircular As System.Windows.Forms.Button
Friend WithEvents cmdSeg1 As System.Windows.Forms.Button
Friend WithEvents cmdSeg2 As System.Windows.Forms.Button
Friend WithEvents cmdBrowse As System.Windows.Forms.Button
Friend WithEvents dlgWav As System.Windows.Forms.OpenFileDialog
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.Label1 = New System.Windows.Forms.Label
Me.lblPos = New System.Windows.Forms.Label
Me.lbltime = New System.Windows.Forms.Label
Me.Label3 = New System.Windows.Forms.Label
Me.cmdStop = New System.Windows.Forms.Button
Me.picWave = New System.Windows.Forms.PictureBox
Me.cmdDefault = New System.Windows.Forms.Button
Me.txtData = New System.Windows.Forms.TextBox
Me.cmdCustom = New System.Windows.Forms.Button
Me.cmdCircular = New System.Windows.Forms.Button
Me.cmdSeg1 = New System.Windows.Forms.Button
Me.cmdSeg2 = New System.Windows.Forms.Button
Me.cmdBrowse = New System.Windows.Forms.Button
Me.dlgWav = New System.Windows.Forms.OpenFileDialog
Me.SuspendLayout()
'
'Label1
'
Me.Label1.Location = New System.Drawing.Point(160, 12)
Me.Label1.Name = "Label1"
Me.Label1.Size = New System.Drawing.Size(48, 16)
Me.Label1.TabIndex = 3
Me.Label1.Text = "Position"
'
'lblPos
'
Me.lblPos.Location = New System.Drawing.Point(216, 8)
Me.lblPos.Name = "lblPos"
Me.lblPos.Size = New System.Drawing.Size(80, 24)
Me.lblPos.TabIndex = 4
'
'lbltime
'
Me.lbltime.Location = New System.Drawing.Point(336, 8)
Me.lbltime.Name = "lbltime"
Me.lbltime.Size = New System.Drawing.Size(72, 24)
Me.lbltime.TabIndex = 6
'
'Label3
'
Me.Label3.Location = New System.Drawing.Point(304, 12)
Me.Label3.Name = "Label3"
Me.Label3.Size = New System.Drawing.Size(32, 16)
Me.Label3.TabIndex = 5
Me.Label3.Text = "Time"
'
'cmdStop
'
Me.cmdStop.Enabled = False
Me.cmdStop.Location = New System.Drawing.Point(416, 8)
Me.cmdStop.Name = "cmdStop"
Me.cmdStop.Size = New System.Drawing.Size(56, 24)
Me.cmdStop.TabIndex = 16
Me.cmdStop.Text = "Stop"
'
'picWave
'
Me.picWave.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.picWave.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
Me.picWave.Enabled = False
Me.picWave.Location = New System.Drawing.Point(8, 40)
Me.picWave.Name = "picWave"
Me.picWave.Size = New System.Drawing.Size(814, 184)
Me.picWave.TabIndex = 17
Me.picWave.TabStop = False
'
'cmdDefault
'
Me.cmdDefault.Enabled = False
Me.cmdDefault.Location = New System.Drawing.Point(88, 8)
Me.cmdDefault.Name = "cmdDefault"
Me.cmdDefault.Size = New System.Drawing.Size(64, 24)
Me.cmdDefault.TabIndex = 18
Me.cmdDefault.Text = "Default"
'
'txtData
'
Me.txtData.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.txtData.Location = New System.Drawing.Point(8, 232)
Me.txtData.Multiline = True
Me.txtData.Name = "txtData"
Me.txtData.ReadOnly = True
Me.txtData.ScrollBars = System.Windows.Forms.ScrollBars.Vertical
Me.txtData.Size = New System.Drawing.Size(816, 120)
Me.txtData.TabIndex = 19
Me.txtData.Text = ""
'
'cmdCustom
'
Me.cmdCustom.Enabled = False
Me.cmdCustom.Location = New System.Drawing.Point(488, 8)
Me.cmdCustom.Name = "cmdCustom"
Me.cmdCustom.Size = New System.Drawing.Size(56, 24)
Me.cmdCustom.TabIndex = 20
Me.cmdCustom.Text = "Stream"
'
'cmdCircular
'
Me.cmdCircular.Enabled = False
Me.cmdCircular.Location = New System.Drawing.Point(560, 8)
Me.cmdCircular.Name = "cmdCircular"
Me.cmdCircular.Size = New System.Drawing.Size(48, 24)
Me.cmdCircular.TabIndex = 21
Me.cmdCircular.Text = "Array"
'
'cmdSeg1
'
Me.cmdSeg1.Enabled = False
Me.cmdSeg1.Location = New System.Drawing.Point(624, 8)
Me.cmdSeg1.Name = "cmdSeg1"
Me.cmdSeg1.Size = New System.Drawing.Size(80, 24)
Me.cmdSeg1.TabIndex = 22
Me.cmdSeg1.Tag = "1"
Me.cmdSeg1.Text = "Capture 1"
'
'cmdSeg2
'
Me.cmdSeg2.Enabled = False
Me.cmdSeg2.Location = New System.Drawing.Point(720, 8)
Me.cmdSeg2.Name = "cmdSeg2"
Me.cmdSeg2.Size = New System.Drawing.Size(80, 24)
Me.cmdSeg2.TabIndex = 23
Me.cmdSeg2.Tag = "2"
Me.cmdSeg2.Text = "Capture 2"
'
'cmdBrowse
'
Me.cmdBrowse.Location = New System.Drawing.Point(8, 8)
Me.cmdBrowse.Name = "cmdBrowse"
Me.cmdBrowse.Size = New System.Drawing.Size(72, 24)
Me.cmdBrowse.TabIndex = 24
Me.cmdBrowse.Text = "File..."
'
'dlgWav
'
Me.dlgWav.Filter = """WAV Files""|*.wav"
Me.dlgWav.ReadOnlyChecked = True
Me.dlgWav.RestoreDirectory = True
Me.dlgWav.Title = """Select WAV file to open"""
'
'CircualrSounds
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(830, 356)
Me.Controls.Add(Me.cmdBrowse)
Me.Controls.Add(Me.cmdSeg2)
Me.Controls.Add(Me.cmdSeg1)
Me.Controls.Add(Me.cmdCircular)
Me.Controls.Add(Me.cmdCustom)
Me.Controls.Add(Me.txtData)
Me.Controls.Add(Me.cmdDefault)
Me.Controls.Add(Me.picWave)
Me.Controls.Add(Me.cmdStop)
Me.Controls.Add(Me.lbltime)
Me.Controls.Add(Me.Label3)
Me.Controls.Add(Me.lblPos)
Me.Controls.Add(Me.Label1)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.Name = "CircualrSounds"
Me.Text = "Circular Buffers"
Me.ResumeLayout(False)
End Sub
#End Region
#Region "Variables in use"
'Direct Sound Device variable
Dim SoundDevice As Microsoft.DirectX.DirectSound.Device
'Direct Sound secondary buffer to hold data for mixing
Dim SbufferOriginal As Microsoft.DirectX.DirectSound.SecondaryBuffer
'Sample Music
Dim SoundFile As String
'Wave file data parser
Dim MYwave As CWAVReader
'Event handler to process tick intervals
Dim MyPaint As System.Timers.ElapsedEventHandler
'Timer object
Dim MyPaintTimer As Timer
'Varibale to accumulate ticks in milliseconds
Dim MyTime As Integer
'Variable to hold total play time of the wav file
Dim TotalTime As Single
'Variable to holed wav data to draw graph
Dim Data() As Byte
'Wave data picture
Dim myPicture As Bitmap
'Circular buffer update interval variable
Dim TimerStep As Integer
'Play indicator and wav data picture composite
Dim PlayPicture As Bitmap
'For Circular buffer
'Next location in circular buffer we can write
Dim NextWritePos As Integer
'Total bytes writen to circular buffer
Dim PlayerPosition As Integer
'Maximum latency control time in milliseconds....
Dim Latency As Integer = 300
'Memeroy stream to hold actual sound Data.
Dim DataMemStream As IO.MemoryStream
'Event handler to process tick intervals
Dim PlayerTime As System.Timers.ElapsedEventHandler
'Timer object
Dim PlayerEvent As Timer
'DataArray for Circular sound
Dim DataArray As Byte()
'varibale to control if we play using memory stream or Data Array
Dim IsArray As Boolean = False
'Varibales to capture the portions of the sound we want to play
Dim StartPoint As Point
Dim EndPoint As Point
#End Region
#Region "Graphic methods and functions"
'Draws the Sound data as a wave onto a canvas using double buffering to speed up work
Function DrawGraph(ByVal Data() As Int16) As Bitmap
'Create the Canvas
Dim myBitmap As System.Drawing.Bitmap
'Create an array to hold the wav data which we can sort
Dim tempData(Data.Length) As Integer
'Copy the data to the temporary location .. can be done better
Data.CopyTo(tempData, 0)
'Sort the array to get the maximum and minimum Y values
Array.Sort(tempData)
'generate the Canvas (drawing board in memory
myBitmap = New Bitmap(picWave.Width, picWave.Height)
'Create your paint brush, pens and drawing objects
Dim myGraphic As System.Drawing.Graphics
myGraphic = Graphics.FromImage(myBitmap)
'draw the backgound with a custom color
myGraphic.Clear(Color.FromArgb(181, 223, 225))
'Get the paramerets to draw the data and scale to fit the canvans but draw form the middle
Dim YMax As Integer = tempData(Data.Length - 1)
Dim YMin As Integer = tempData(0)
Dim XMax As Integer = picWave.Width
Dim Xmin As Integer = 0
'Create an array of points to draw a line
Dim PicPoint(Data.Length - 1) As System.Drawing.PointF
Dim Count As Integer
Dim Step1 As Single 'Scale the data between Ymax and Ymin
Dim Step2 As Single 'Scale the data further to fit between the canvas hieght
Dim step3 As Single 'Draw the point form the middle of the canvas
Dim Mypen As New Pen(Color.FromArgb(24, 101, 123))
'Draw the lines from the series of points representing the sound wav
For Count = 0 To Data.Length - 1
Step1 = CSng(Data(Count) / (YMax - YMin))
Step2 = CSng(Step1 * picWave.Height / 2)
step3 = CSng(Step2 + (picWave.Height / 2))
PicPoint(Count) = New System.Drawing.PointF(CSng(XMax * (Count / Data.Length)), step3)
Next
'Draw the lines
myGraphic.DrawLines(Mypen, PicPoint)
'Draw graphics onto canvas
myGraphic.DrawImage(myBitmap, picWave.Width, picWave.Height)
'return the picture memeory object
Return (myBitmap)
End Function
'The handler to the timer tick event :
'Paints the location of the player pointer on the sound graph as the data is played
Sub MyPainPoint(ByVal obj As Object, ByVal Args As System.Timers.ElapsedEventArgs)
'Control to ensure we stop when the total cumulated bytes read is equal to the total data size
If PlayerPosition >= MYwave.SubChunkSizeTwo Then
StopPlay()
'Update the labels
lblPos.Text = MYwave.SubChunkSizeTwo.ToString
lbltime.Text = MYwave.PlayTimeSeconds().ToString
End If
'Draw a line red from top to bottom showing the curent position based on play position
'The PlayerPOsition is the absolute location of the current played data
'This is taken as a ratio of the total data size adn scaled to the width of the picture
'control
Dim XPos As Single = CSng((PlayerPosition / MYwave.SubChunkSizeTwo) * picWave.Width)
Dim posgraphic As Graphics
'Clone the original canvas (this has the original sound graph). We do not need to
're-draw this large graph as it willtake much processing time!
PlayPicture = CType(myPicture.Clone, Bitmap)
posgraphic = Graphics.FromImage(PlayPicture)
'Draw the pointer
Dim Mypen As Pen = New Pen(Color.Red)
posgraphic.DrawLine(Mypen, XPos, 0, XPos, picWave.Height)
'Draw line to myPicture
MyTime += TimerStep
posgraphic.DrawImage(PlayPicture, picWave.Width, picWave.Height)
'Update the status on the labels
lblPos.Text = PlayerPosition.ToString
lbltime.Text = (MyTime / 1000).ToString
'force a redraw of the picture updated.
Me.Invalidate(New Drawing.Rectangle(picWave.Left, picWave.Top, picWave.Width, picWave.Height))
End Sub
'Capture the start point
Private Sub picWave_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picWave.MouseDown
'Get the position of the mouse and draw a line
StartPoint = New Point
If e.Button = MouseButtons.Left Then
StartPoint.X = e.X
StartPoint.Y = e.Y
End If
End Sub
'Draw a band over the data we want to capture
Private Sub picWave_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picWave.MouseMove
If e.Button = MouseButtons.Left Then
If StartPoint.IsEmpty = False Then
EndPoint = New Point
EndPoint.X = e.X
EndPoint.Y = e.Y
DrawSelection()
End If
End If
End Sub
'Draw the band over the selection
Sub DrawSelection()
'Draw a rubberband
Dim posgraphic As Graphics
Dim RubberRect As Rectangle
RubberRect = New Rectangle(StartPoint.X, 0, EndPoint.X - StartPoint.X, picWave.Height - 3)
'Clone the original canvas
PlayPicture = CType(myPicture.Clone, Bitmap)
posgraphic = Graphics.FromImage(PlayPicture)
'Draw the pointer
Dim Mypen As Pen = New Pen(Color.Green)
'Create a transparent brush using alpha blending techniques
Dim MyBrush As SolidBrush = New SolidBrush(Color.FromArgb(85, 204, 32, 92))
'Draw the boarder
posgraphic.DrawRectangle(Mypen, RubberRect)
'Fill the color
posgraphic.FillRectangle(MyBrush, RubberRect)
'Draw the picture on the form with the updated section of music to clip
posgraphic.DrawImage(PlayPicture, picWave.Width, picWave.Height)
'redraw the portion only
Me.Invalidate(New Drawing.Rectangle(picWave.Left, picWave.Top, picWave.Width, picWave.Height))
End Sub
'Complete the drawing and capture the start and width/hieght of the rubber band
Private Sub picWave_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picWave.MouseUp
EndPoint.X = e.X
EndPoint.Y = e.Y
DrawSelection()
End Sub
#End Region
#Region "Sound functions and helper code"
'Creates a memory stream from Array Data to support playing sound from a memory stream
'In an actual implementation, this would be reading data from an actual file on the Harddisk
Function GetMemeoryStream(ByVal Data() As Byte) As IO.MemoryStream
Return New IO.MemoryStream(Data)
End Function
'Stop processing any sound
Private Sub cmdStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdStop.Click
StopPlay()
cmdBrowse.Enabled = False
cmdDefault.Enabled = True
cmdCustom.Enabled = True
cmdCircular.Enabled = True
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = False
End Sub
'using the form events paint, redraw the picture
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
'Draw the picture of the updated pointer location
picWave.Image = PlayPicture
End Sub
'Stop playing the sound
Sub StopPlay()
'Get the status of the player
If SbufferOriginal.Status.Playing Then
SbufferOriginal.Stop()
'shut the paint timer
If Not IsNothing(MyPaintTimer) Then
MyPaintTimer.Enable(False)
MyPaintTimer = Nothing
End If
'shut the player timer
If Not IsNothing(PlayerTime) Then
PlayerEvent.Enable(False)
PlayerEvent = Nothing
End If
End If
End Sub
'Update the Circular buffer based on either a stream of data array
Sub PlayerEventHandler(ByVal obj As Object, ByVal Args As System.Timers.ElapsedEventArgs)
'Stop if we have read all data
If PlayerPosition >= MYwave.SubChunkSizeTwo Then
StopPlay()
End If
'If an array, use the dataArray to top up new data to the circular buffer
If IsArray = False Then
'Get the amount of data to write and thereafter write the data
WriteData(GetPlayedSize())
Else
WriteDataArray(GetPlayedSize())
End If
End Sub
'Play a sound wave using a default static buffer
Private Sub cmdDefault_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdDefault.Click
TimerStep = 25 'Interval to draw the pointer on the graph
cmdCustom.Enabled = False
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
Try
MyPaintTimer = New Timer(TimerStep, MyPaint)
TotalTime = CSng(MYwave.SubChunkSizeTwo / MYwave.ByteRate)
SbufferOriginal = New Microsoft.DirectX.DirectSound.SecondaryBuffer(SoundFile, SoundDevice)
Me.Text = "Buffer size :" & SbufferOriginal.Caps.BufferBytes.ToString
'Start the timer
MyPaintTimer.Enable(True)
MyTime = 0
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
'Use a circular buffer to update the primary buffer with data
Private Sub cmdCustom_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdCustom.Click
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = True
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = True
IsArray = False
'create a format object tro describe the wave properties
'These are not random numbers.. they are corelated based on
'formulae such that a change in one will result to change in
'related properties. Ref the contents of the label during run time
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
'Create the buffer description and buffer size
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
Desc.BufferBytes = CInt(MYwave.SampleRate) * 1
'Add capabilites of the buffer
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
'Create a secondary sound buffer
SbufferOriginal = New Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
TimerStep = Latency \ 10
MyTime = 0
SbufferOriginal.Stop()
SbufferOriginal.SetCurrentPosition(0)
WriteData(SbufferOriginal.Caps.BufferBytes)
NextWritePos = 0
PlayerPosition = 0
MyPaintTimer = New Timer(TimerStep, MyPaint)
'Create a timer
PlayerTime = New System.Timers.ElapsedEventHandler(AddressOf PlayerEventHandler)
PlayerEvent = New Timer(TimerStep, PlayerTime)
PlayerEvent.Enable(True)
'Play the sound
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
MyPaintTimer.Enable(True)
End Sub
'get the played data size
Function GetPlayedSize() As Integer
Dim Pos As Integer
Pos = SbufferOriginal.PlayPosition
If Pos < NextWritePos Then
Return Pos + (SbufferOriginal.Caps.BufferBytes - NextWritePos)
Else
Return Pos - NextWritePos
End If
End Function
'Ensure we have a latency of maximum 300 milliseconds
Function TimeToDataSize(ByVal Latency As Integer) As Integer
Dim DataSize As Integer
DataSize = CInt(Latency * SbufferOriginal.Format.AverageBytesPerSecond / 1000)
DataSize = DataSize - (DataSize Mod SbufferOriginal.Format.BlockAlign)
Return DataSize
End Function
'Write data to the circular buffer (secondary buffer that is)
Sub WriteData(ByVal DataSize As Integer)
Dim Tocopy As Integer
'make sure the data is less than the latency amount of 300ms
Tocopy = Math.Min(DataSize, TimeToDataSize(Latency))
'Only write data if there is something to write!
If Tocopy > 0 Then
'Restore the buffer
If SbufferOriginal.Status.BufferLost Then
SbufferOriginal.Restore()
End If
'Copy the data to the buffer
'The DataMemStream is a binary stream object. This can also be a
'large WAV file still on 'harddisk
SbufferOriginal.Write(NextWritePos, DataMemStream, Tocopy, Microsoft.DirectX.DirectSound.LockFlag.None)
'As data is read form the DataMemStream, the position (internal of the structure) advances
'by the size of Tcopy
'Advance the total cumulative bytes read
PlayerPosition += Tocopy
'advance the NextWrite pointer
NextWritePos += Tocopy
'If we are at the end, we wrap round
If NextWritePos >= SbufferOriginal.Caps.BufferBytes Then
NextWritePos = NextWritePos - SbufferOriginal.Caps.BufferBytes
End If
End If
End Sub
'Write data to a Data Array.... similar to the above..using memory a stream
Sub WriteDataArray(ByVal DataSize As Integer)
Dim Tocopy As Integer
'ensure we do not have a big latency . the maximum is 300 ms
Tocopy = Math.Min(DataSize, TimeToDataSize(Latency))
'is we have data, then write to the array and play
If Tocopy > 0 Then
'Restore the buffer
If SbufferOriginal.Status.BufferLost Then
SbufferOriginal.Restore()
End If
'Copy the data to the Array
're-create the data array (this is very slow!!)
ReDim DataArray(Tocopy - 1)
'Position the memory stream to the last location we read from.
DataMemStream.Position = PlayerPosition
'Copy the data from the stream to the array
DataMemStream.Read(DataArray, 0, Tocopy - 1)
'Write the data to the seconadry buffer
SbufferOriginal.Write(NextWritePos, DataArray, Microsoft.DirectX.DirectSound.LockFlag.None)
'Adavance the pointers
PlayerPosition += Tocopy
NextWritePos += Tocopy
If NextWritePos >= SbufferOriginal.Caps.BufferBytes Then
NextWritePos = NextWritePos - SbufferOriginal.Caps.BufferBytes
End If
End If
End Sub
'Create a circular buffer based on the data array... similar to the former button...
Private Sub cmdCircular_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdCircular.Click
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = False
cmdCircular.Enabled = True
cmdSeg1.Enabled = False
cmdSeg2.Enabled = False
cmdStop.Enabled = True
IsArray = True
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
Desc.BufferBytes = CInt(MYwave.SampleRate) * 1
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
SbufferOriginal = New Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
TimerStep = Latency \ 5
MyTime = 0
SbufferOriginal.Stop()
SbufferOriginal.SetCurrentPosition(0)
WriteDataArray(SbufferOriginal.Caps.BufferBytes)
NextWritePos = 0
PlayerPosition = 0
MyPaintTimer = New Timer(TimerStep, MyPaint)
PlayerTime = New System.Timers.ElapsedEventHandler(AddressOf PlayerEventHandler)
PlayerEvent = New Timer(TimerStep, PlayerTime)
PlayerEvent.Enable(True)
SbufferOriginal.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
MyPaintTimer.Enable(True)
End Sub
'Plays a segment of data based on the rubber-band we have drawn prior to raising this event
Public Sub SetSegment(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSeg1.Click, cmdSeg2.Click
'set local variables to use
Dim tag As Int16
Dim theButton As Button
Dim DataStart As Integer
Dim DataStop As Integer
Dim BufferSize As Integer
'initialize the direct sound objects
Dim Format As Microsoft.DirectX.DirectSound.WaveFormat
Dim Desc As Microsoft.DirectX.DirectSound.BufferDescription
Dim MixBuffer As Microsoft.DirectX.DirectSound.SecondaryBuffer
cmdBrowse.Enabled = False
cmdDefault.Enabled = False
cmdCustom.Enabled = False
cmdCircular.Enabled = False
cmdSeg1.Enabled = False
cmdSeg2.Enabled = True
cmdStop.Enabled = False
theButton = CType(sender, Button)
theButton.Enabled = False
'get the locations from where to read data from
DataStart = CInt(MYwave.SubChunkSizeTwo * (StartPoint.X / picWave.Width))
DataStop = CInt(MYwave.SubChunkSizeTwo * (EndPoint.X / picWave.Width))
StartPoint = Nothing
EndPoint = Nothing
'ensure that the data is aligned.. if not, noise results
DataStart = DataStart - (DataStart Mod CInt(MYwave.BlockAlign))
DataStop = DataStop - (DataStop Mod CInt(MYwave.BlockAlign))
'Get the data into a Data array
Dim DataSegment(DataStop - DataStart) As Byte
'Read the data from the Stream
DataMemStream.Position = DataStart
'Read from the stream to the array buffer
DataMemStream.Read(DataSegment, 0, DataStop - DataStart)
'Now play the sound.
'For custom sound, you must set the format
Format = New Microsoft.DirectX.DirectSound.WaveFormat
Format.AverageBytesPerSecond = CInt(MYwave.ByteRate)
Format.BitsPerSample = CShort(MYwave.BitsPerSample)
Format.BlockAlign = CShort(MYwave.BlockAlign)
Format.Channels = CShort(MYwave.NumChannels)
Format.FormatTag = Microsoft.DirectX.DirectSound.WaveFormatTag.Pcm
Format.SamplesPerSecond = CInt(MYwave.SampleRate)
Desc = New Microsoft.DirectX.DirectSound.BufferDescription(Format)
BufferSize = DataStop - DataStart + 1
'ensure the size is also aligned
BufferSize = BufferSize + (BufferSize Mod CInt(MYwave.BlockAlign))
Desc.BufferBytes = BufferSize
Desc.ControlFrequency = True
Desc.ControlPan = True
Desc.ControlVolume = True
Desc.GlobalFocus = True
'Play the sound
Try
MixBuffer = New Microsoft.DirectX.DirectSound.SecondaryBuffer(Desc, SoundDevice)
MixBuffer.Stop()
MixBuffer.SetCurrentPosition(0)
If MixBuffer.Status.BufferLost Then
MixBuffer.Restore()
End If
MixBuffer.Write(0, DataSegment, Microsoft.DirectX.DirectSound.LockFlag.None)
MixBuffer.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
'open a file dialogue box
Private Sub cmdBrowse_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdBrowse.Click
dlgWav.ShowDialog(Me)
SoundFile = dlgWav.FileName
If SoundFile <> "" Then
Me.Text = "..reading file....."
Me.Refresh()
'Read the data now!
OpenWavAndSetSystem()
cmdBrowse.Enabled = False
cmdDefault.Enabled = True
cmdCustom.Enabled = True
cmdCircular.Enabled = True
cmdSeg1.Enabled = True
cmdSeg2.Enabled = True
cmdStop.Enabled = True
Me.Text = "Circular Buffers " & SoundFile
picWave.Enabled = True
End If
Me.Refresh()
End Sub
'Open the wave file and read the Data
'Load the wave file and draw the wave data graph
Sub OpenWavAndSetSystem()
'Create the Direct Sound Device object
SoundDevice = New Microsoft.DirectX.DirectSound.Device
'You have to assign the window handler to the device inorder not to hog the sound card
'This is also required!
SoundDevice.SetCooperativeLevel(Me.Handle, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal)
'Create the wav file parser and process the wave file
MYwave = New CWAVReader(SoundFile)
'Set the timer using delegates (similar to events)
'Create a pointer to the code that runs whenever a tick event occurs
MyPaint = New System.Timers.ElapsedEventHandler(AddressOf MyPainPoint)
'Create a timer object and pass the interval and function pointer to the code that shall
'handle the tick events using delegate (similar to event sinks)
'Draw the baseline sound wave from the data onto a canvas : bitmap
myPicture = DrawGraph(MYwave.GetSoundDataValue())
'Create a memory stream from the data contained int he sound file.
'In an actual implementation, we should be reading the data directly from the
'file and play it via stream or small segments of data in an array
DataMemStream = GetMemeoryStream(MYwave.WaveSoundData)
'Draw all other data on the clone picture graph
PlayPicture = CType(myPicture.Clone, Bitmap)
'Get the sound (wav data properties) and display on screen
Dim myData As New System.Text.StringBuilder
myData.Append("Audio Format (Must be PCM for WAV):" & MYwave.AudioFormat & vbCrLf)
myData.Append("Bits Per Sample :" & MYwave.BitsPerSample & vbCrLf)
myData.Append("Block Align :" & MYwave.BlockAlign & "=[NumChannels * BitsPerSample/8]" & vbCrLf)
myData.Append("Byte Rate (Number of Bytes per second):" & MYwave.ByteRate & "=[SampleRate * NumChannels * BitsPerSample/8]" & vbCrLf)
myData.Append("Format ID :" & MYwave.FormatID & vbCrLf)
myData.Append("Audio Frequency :" & MYwave.Frequency & vbCrLf)
myData.Append("Number of samples:" & MYwave.NumberOfSamples & vbCrLf)
myData.Append("Number of Channels (1 Mono- 2 Stereo):" & MYwave.NumChannels & vbCrLf)
myData.Append("Audio Play Time:" & MYwave.PlayTimeSeconds.ToString & vbCrLf)
myData.Append("Sample Rate :" & MYwave.SampleRate & vbCrLf)
myData.Append("Data Chunk size :" & MYwave.SubChunkSizeTwo & "=[NumSamples * NumChannels * BitsPerSample/8]" & vbCrLf)
myData.Append("Audio File Name :" & MYwave.WAVFileName & vbCrLf)
'Spit out the properties of the sound data to a text box
txtData.Text = myData.ToString
End Sub
#End Region
End Class
Class CWAVReader
'by ENO
'To Read a WAV File and Populate basic Properties
'On 3/11/2007
'For AfricaDotNet user group
'BigEndian bytes number 0 - 4
Private mChunkID As String
Public ReadOnly Property ChunkID() As String
Get
ChunkID = mChunkID
End Get
End Property
'from the Constructor
Private mWAVFileName As String
Public ReadOnly Property WAVFileName() As String
Get
WAVFileName = mWAVFileName
End Get
End Property
'Constructor to open stream
Sub New(ByVal SoundFilePathName As String)
mWAVFileName = SoundFilePathName
mOpen = OpenWAVStream(mWAVFileName)
'******************* MAIN WORK HERE ******************
'Parse the WAV file and read the
If mOpen Then
'Read the Header Data in THIS ORDER
'Each Read results to the File Pointer Moving
mChunkID = ReadChunkID(mWAVStream)
mChunkSize = ReadChunkSize(mWAVStream)
mFormatID = ReadFormatID(mWAVStream)
mSubChunkID = ReadSubChunkID(mWAVStream)
mSubChunkSize = ReadSubChunkSize(mWAVStream)
mAudioFormat = ReadAudioFormat(mWAVStream)
mNumChannels = ReadNumChannels(mWAVStream)
mSampleRate = ReadSampleRate(mWAVStream)
mByteRate = ReadByteRate(mWAVStream)
mBlockAlign = ReadBlockAlign(mWAVStream)
mBitsPerSample = ReadBitsPerSample(mWAVStream)
mSubChunkIDTwo = ReadSubChunkIDTwo(mWAVStream)
mSubChunkSizeTwo = ReadSubChunkSizeTwo(mWAVStream)
mWaveSoundData = ReadWAVSampleData(mWAVStream)
mWAVStream.Close()
End If
End Sub
'Property to tell if stream is open or not
Private mOpen As Boolean
Public ReadOnly Property IsWAVFileOPen() As Boolean
Get
IsWAVFileOPen = mOpen
End Get
End Property
'Open an IO Binary Stream to Read the WavFile
Private mWAVStream As IO.BinaryReader
Private Function OpenWAVStream(ByVal SoundFilePathName As String) As Boolean
Dim WAVStreamReader As IO.StreamReader
WAVStreamReader = New IO.StreamReader(SoundFilePathName)
mWAVStream = New IO.BinaryReader(WAVStreamReader.BaseStream)
If mWAVStream Is Nothing Then
Return False
Else
Return True
End If
End Function
'Dipose the resource
Sub Dispose()
If Not mWAVStream Is Nothing Then
mWAVStream.Close()
mWAVStream = Nothing
mOpen = False
End If
GC.SuppressFinalize(Me)
End Sub
'Close the stream
Protected Overrides Sub Finalize()
Me.Dispose()
MyBase.Finalize()
End Sub
'Read the ChunkID and return a string
Private Function ReadChunkID(ByVal WAVIOstreamReader As IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'Chunk size little endian bytes No. 5-13
Private mChunkSize As Long
Public ReadOnly Property ChunkSize() As Long
Get
ChunkSize = mChunkSize
End Get
End Property
'Read the ChunkID and return a string
Private Function ReadChunkSize(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadChunkSize = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'BigEndian bytes number 9 - 13
Private mFormatID As String
Public ReadOnly Property FormatID() As String
Get
FormatID = mFormatID
End Get
End Property
'Read the Format and return a string
Private Function ReadFormatID(ByVal WAVIOstreamReader As IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'Little Endian bytes number 13 - 17
Private mSubChunkID As String
Public ReadOnly Property SubChunkID() As String
Get
SubChunkID = mSubChunkID
End Get
End Property
'Read the sub chunkID and return a string
Private Function ReadSubChunkID(ByVal WAVIOstreamReader As IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(4)
'Ensure we have data to spit out
If DataBuffer.Length <> 0 Then
TempString = DataEncoder.GetChars(DataBuffer, 0, 4)
Return TempString(0) & TempString(1) & TempString(2) & TempString(3)
Else
Return ""
End If
End Function
'SubChunk size little endian bytes No. 17-21
Private mSubChunkSize As Long
Public ReadOnly Property SubChunkSize() As Long
Get
SubChunkSize = mSubChunkSize
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadSubChunkSize(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSubChunkSize = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Audio Format little endian bytes No. 20-22
Private mAudioFormat As Long
Public ReadOnly Property AudioFormat() As Long
Get
AudioFormat = mAudioFormat
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadAudioFormat(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadAudioFormat = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Number of Channels little endian bytes No. 22-24
Private mNumChannels As Long
Public ReadOnly Property NumChannels() As Long
Get
NumChannels = mNumChannels
End Get
End Property
'Read the SubChunkID and return a string
Private Function ReadNumChannels(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadNumChannels = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Sample Rate little endian bytes No. 24-28
Private mSampleRate As Long
Public ReadOnly Property SampleRate() As Long
Get
SampleRate = mSampleRate
End Get
End Property
Public ReadOnly Property Frequency() As Long
Get
Frequency = mSampleRate
End Get
End Property
'Get the number of samples
Public ReadOnly Property NumberOfSamples() As Long
Get
NumberOfSamples = SubChunkSizeTwo * 8 \ (NumChannels * BitsPerSample)
End Get
End Property
'Read the Sample Rate and return a string
Private Function ReadSampleRate(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSampleRate = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Byte Rate little endian bytes No. 28-32
Private mByteRate As Long
Public ReadOnly Property ByteRate() As Long
Get
ByteRate = mByteRate
End Get
End Property
'Get the play time in seconds
Public ReadOnly Property PlayTimeSeconds() As Single
Get
PlayTimeSeconds = CSng(SubChunkSizeTwo / ByteRate)
End Get
End Property
'Read the Byte Rate and return a string
Private Function ReadByteRate(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadByteRate = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Block Align little endian bytes No. 32-34
Private mBlockAlign As Long
Public ReadOnly Property BlockAlign() As Long
Get
BlockAlign = mBlockAlign
End Get
End Property
'Read the Block Align and return a string
Private Function ReadBlockAlign(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadBlockAlign = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Buits Per Sample little endian bytes No. 34-36
Private mBitsPerSample As Long
Public ReadOnly Property BitsPerSample() As Long
Get
BitsPerSample = mBitsPerSample
End Get
End Property
'Read the Bits Per Sample and return a string
Private Function ReadBitsPerSample(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(2)
ReadBitsPerSample = CLng(GetLittleEndianStringValueDuplex(DataBuffer))
End Function
'Buits Per Sample Big endian bytes No. 36-40
Private mSubChunkIDTwo As String
Public ReadOnly Property SubChunkIDTwo() As String
Get
SubChunkIDTwo = mSubChunkIDTwo
End Get
End Property
'Read the SubChunkTwoID Sample and return a string
Private Function ReadSubChunkIDTwo(ByVal WAVIOstreamReader As IO.BinaryReader) As String
Dim DataBuffer() As Byte
Dim DataEncoder As System.Text.ASCIIEncoding
Dim TempString As Char()
Dim Datastr As String
DataEncoder = New System.Text.ASCIIEncoding
DataBuffer = WAVIOstreamReader.ReadBytes(1)
'Ensure we have data to spit out
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
If Datastr <> "d" Then
'Read until you get data
DataBuffer = WAVIOstreamReader.ReadBytes(1)
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
While Datastr <> "d"
DataBuffer = WAVIOstreamReader.ReadBytes(1)
TempString = DataEncoder.GetChars(DataBuffer, 0, 1)
Datastr = TempString(0)
End While
DataBuffer = WAVIOstreamReader.ReadBytes(3)
TempString = DataEncoder.GetChars(DataBuffer, 0, 3)
Datastr = "d" & (TempString(0) & TempString(1) & TempString(2))
Else
DataBuffer = WAVIOstreamReader.ReadBytes(3)
TempString = DataEncoder.GetChars(DataBuffer, 0, 3)
Datastr = "d" & (TempString(0) & TempString(1) & TempString(2))
Return Datastr
End If
End Function
'Buits Per Sample little endian bytes No. 40-44
Private mSubChunkSizeTwo As Long
Public ReadOnly Property SubChunkSizeTwo() As Long
Get
SubChunkSizeTwo = mSubChunkSizeTwo
End Get
End Property
'Read the Bits Per Sample and return a string
Private Function ReadSubChunkSizeTwo(ByVal WAVIOstreamReader As IO.BinaryReader) As Long
Dim DataBuffer() As Byte
DataBuffer = WAVIOstreamReader.ReadBytes(4)
ReadSubChunkSizeTwo = CLng(GetLittleEndianStringValue(DataBuffer))
End Function
'Get the little endian value
Private Function GetLittleEndianStringValueDuplex(ByVal DataBuffer() As Byte) As String
Dim ValueString As String = "&h"
If DataBuffer.Length <> 0 Then
'In little endian
If Hex(DataBuffer(1)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(1))
Else
ValueString &= Hex(DataBuffer(1))
End If
If Hex(DataBuffer(0)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(0))
Else
ValueString &= Hex(DataBuffer(0))
End If
Else
ValueString = "0"
End If
GetLittleEndianStringValueDuplex = ValueString
End Function
'Get the small endian value
Private Function GetLittleEndianStringValue(ByVal DataBuffer() As Byte) As String
Dim ValueString As String = "&h"
If DataBuffer.Length <> 0 Then
'In little endian we reverse the aray data and pad the same where the length is 1
If Hex(DataBuffer(3)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(3))
Else
ValueString &= Hex(DataBuffer(3))
End If
If Hex(DataBuffer(2)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(2))
Else
ValueString &= Hex(DataBuffer(2))
End If
If Hex(DataBuffer(1)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(1))
Else
ValueString &= Hex(DataBuffer(1))
End If
If Hex(DataBuffer(0)).Length = 1 Then
ValueString &= "0" & Hex(DataBuffer(0))
Else
ValueString &= Hex(DataBuffer(0))
End If
Else
ValueString = "0"
End If
GetLittleEndianStringValue = ValueString
End Function
'Buffers to hold Data
Private mWaveSoundData As Byte()
'Return the sound Data to display
ReadOnly Property WaveSoundData() As Byte()
Get
WaveSoundData = mWaveSoundData
End Get
End Property
'Read Data to Pans both left and right
Private Function ReadWAVSampleData(ByVal WAVIOstreamReader As IO.BinaryReader) As Byte()
Dim tempBuffer() As Byte
tempBuffer = WAVIOstreamReader.ReadBytes(CInt(mSubChunkSizeTwo))
Return tempBuffer
End Function
'returns the wave data as a byte array
Public Function GetSoundDataValue() As Int16()
Dim DataCount As Integer
Dim tempStream As IO.BinaryReader
tempStream = New IO.BinaryReader(New IO.MemoryStream(mWaveSoundData))
tempStream.BaseStream.Position = 0
'Create a data array to hold the data read from the stream
'Read chunks of int16 from the stream (already aligned!)
Dim tempData(CInt(tempStream.BaseStream.Length / 2)) As Int16
While DataCount <= tempData.Length - 2
tempData(DataCount) = tempStream.ReadInt16()
DataCount += 1
End While
tempStream.Close()
tempStream = Nothing
Return tempData
End Function
End Class
'By ENO
'System Timer to control precicly the timer interval
'uses delegates
'For AfricaDoTnet user group
'Used for Circular buffer and wave position painting
Imports System.Timers
'Function pointer
Delegate Sub OnTimer(ByVal Obj As Object, ByVal Arg As System.Timers.ElapsedEventArgs)
Public Class Timer
Dim mTimer As Timers.Timer
Dim MyonTimer As System.Timers.ElapsedEventHandler
Dim mtimerEnabled As Boolean
'Class constructor
Sub New(ByVal Interval As Double, ByRef handle As System.Timers.ElapsedEventHandler)
mTimer = New Timers.Timer
mTimer.Interval = Interval
MyonTimer = handle
End Sub
'to enable / disable the timer
Sub Enable(ByVal SetIT As Boolean)
If SetIT = True Then
AddHandler mTimer.Elapsed, MyonTimer
mTimer.Enabled = SetIT
mTimer.AutoReset = True
Else
mTimer.Enabled = SetIT
mTimer.AutoReset = False
RemoveHandler mTimer.Elapsed, MyonTimer
End If
mtimerEnabled = SetIT
End Sub
'Get the status of the timer .. running or not
ReadOnly Property TimerEnabled() As Boolean
Get
mtimerEnabled = TimerEnabled
End Get
End Property
End Class
|
|
|
|
|