|
Hello,
Can explain how you derive this method?
Thanks.
|
|
|
|
|
Hello!
You can send to me the source code?
My email is paolo.carilli[@]gmail.com
Best regards,
Paolo
|
|
|
|
|
|
Someone with mad skills, turn my multichannel formulae into a dx mesh (surface) in iso and render it. Then post the code and photobucket some screens so we can all look on in awe.
|
|
|
|
|
Hi, I'm having a go at running this...
It looks good, loads the wave...
However, when I hit "stream" it comes up with
System.InvalidOperationException was unhandled by user code
Message="Cross-thread operation not valid: Control 'lblPos' accessed from a thread other than the thread it was created on."
Source="System.Windows.Forms"
StackTrace:
at System.Windows.Forms.Control.get_Handle() at System.Windows.Forms.Control.set_WindowText(String value) at System.Windows.Forms.Control.set_Text(String value) at System.Windows.Forms.Label.set_Text(String value) at Wave_Program.Form1.MyPainPoint(Object obj, ElapsedEventArgs Args) in C:\Users\Sean Clancy\Documents\Visual Studio 2008\Projects\WaveProgram\WaveProgram\Form1.vb:line 158 at System.Timers.Timer.MyTimerCallback(Object state)
InnerException:
so, what do I do to fix this cross-thread problem?
Sean
|
|
|
|
|
Try this:
Public Delegate Sub TextSetterDelegate(text As String)
Private Sub SetPosText(text as String)
If lblPos.InvokeRequired() Then
lblPos.Invoke(New TextSetterDelegate(AddressOf SetPosText), New Object() {text})
Return
End If
lblPos.Text = text
End Sub
Call this to update the Position Text across threads from within the timer handler. Either that or try using the timer in System.Threading namespace.
|
|
|
|
|
I've adapted from Eno's snippets and come up with a graph that displays multiple channels. I also added the ability to set the number of samples squished into 1 screen pixel, which allows to view larger or smaller pieces of waves, and a slider to scroll through.
Take a look here.
|
|
|
|
|
This sounds great!
Are you going to post the code?
|
|
|
|
|
Here is the source as it is in my project atm. I'm not sure what state it's in but it ran the last time I tried it. I have added the multi-sample and multi-channel selection ability to it. I just need to add eventing for selection changes, that will return appropriate eventargs with a number of channels/samples selected and a nice easy way to straight access that data.
I have also thought about extrapolating this into a standardised multi-channel multi-sample editing control, for editing graphics as well, because they both work on the same concept (ie 32-ARGB has four physical channels, and the frames are the individual pixels, which makes 1 sample the value of red, green, blue or alpha for 1 frame).
Next though, I want to add grab handles to be able to move samples and the ability to re-sample streams 1 or more channels, essentially breaking the channel data into a collection of the base sample data (like recycle from propellerheads). This will also be a nice base for speech detection and recognition.
Anyway, let me prattle on no further. Have fun with it.
*/Form1.Designer.vb/*
<global.microsoft.visualbasic.compilerservices.designergenerated()> _
Partial Class Form1
Inherits System.Windows.Forms.Form
'Form overrides dispose to clean up the component list.
<system.diagnostics.debuggernonusercode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
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.
<system.diagnostics.debuggerstepthrough()> _
Private Sub InitializeComponent()
Me.Button1 = New System.Windows.Forms.Button
Me.PictureBox1 = New System.Windows.Forms.PictureBox
Me.HScrollBar1 = New System.Windows.Forms.HScrollBar
Me.NumericUpDown1 = New System.Windows.Forms.NumericUpDown
Me.StatusStrip1 = New System.Windows.Forms.StatusStrip
Me.ToolStripStatusLabel1 = New System.Windows.Forms.ToolStripStatusLabel
Me.ToolStripProgressBar1 = New System.Windows.Forms.ToolStripProgressBar
Me.Button2 = New System.Windows.Forms.Button
CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.NumericUpDown1, System.ComponentModel.ISupportInitialize).BeginInit()
Me.StatusStrip1.SuspendLayout()
Me.SuspendLayout()
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(12, 12)
Me.Button1.Name = "Button1"
Me.Button1.Size = New System.Drawing.Size(75, 23)
Me.Button1.TabIndex = 0
Me.Button1.Text = "&Open"
Me.Button1.UseVisualStyleBackColor = True
'
'PictureBox1
'
Me.PictureBox1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.PictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D
Me.PictureBox1.Location = New System.Drawing.Point(12, 41)
Me.PictureBox1.Name = "PictureBox1"
Me.PictureBox1.Size = New System.Drawing.Size(794, 362)
Me.PictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage
Me.PictureBox1.TabIndex = 1
Me.PictureBox1.TabStop = False
'
'HScrollBar1
'
Me.HScrollBar1.Anchor = CType(((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.HScrollBar1.Location = New System.Drawing.Point(12, 406)
Me.HScrollBar1.Name = "HScrollBar1"
Me.HScrollBar1.Size = New System.Drawing.Size(794, 19)
Me.HScrollBar1.TabIndex = 2
'
'NumericUpDown1
'
Me.NumericUpDown1.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.NumericUpDown1.DecimalPlaces = 2
Me.NumericUpDown1.Increment = New Decimal(New Integer() {1, 0, 0, 131072})
Me.NumericUpDown1.Location = New System.Drawing.Point(686, 12)
Me.NumericUpDown1.Maximum = New Decimal(New Integer() {25, 0, 0, 0})
Me.NumericUpDown1.Minimum = New Decimal(New Integer() {1, 0, 0, 131072})
Me.NumericUpDown1.Name = "NumericUpDown1"
Me.NumericUpDown1.Size = New System.Drawing.Size(120, 20)
Me.NumericUpDown1.TabIndex = 3
Me.NumericUpDown1.Value = New Decimal(New Integer() {1, 0, 0, 0})
'
'StatusStrip1
'
Me.StatusStrip1.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.ToolStripStatusLabel1, Me.ToolStripProgressBar1})
Me.StatusStrip1.Location = New System.Drawing.Point(0, 425)
Me.StatusStrip1.Name = "StatusStrip1"
Me.StatusStrip1.Size = New System.Drawing.Size(818, 22)
Me.StatusStrip1.TabIndex = 4
Me.StatusStrip1.Text = "StatusStrip1"
'
'ToolStripStatusLabel1
'
Me.ToolStripStatusLabel1.Name = "ToolStripStatusLabel1"
Me.ToolStripStatusLabel1.Size = New System.Drawing.Size(111, 17)
Me.ToolStripStatusLabel1.Text = "ToolStripStatusLabel1"
'
'ToolStripProgressBar1
'
Me.ToolStripProgressBar1.Name = "ToolStripProgressBar1"
Me.ToolStripProgressBar1.Size = New System.Drawing.Size(100, 16)
'
'Button2
'
Me.Button2.Location = New System.Drawing.Point(93, 12)
Me.Button2.Name = "Button2"
Me.Button2.Size = New System.Drawing.Size(75, 23)
Me.Button2.TabIndex = 5
Me.Button2.Text = "&Match"
Me.Button2.UseVisualStyleBackColor = True
'
'Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(818, 447)
Me.Controls.Add(Me.Button2)
Me.Controls.Add(Me.StatusStrip1)
Me.Controls.Add(Me.NumericUpDown1)
Me.Controls.Add(Me.HScrollBar1)
Me.Controls.Add(Me.PictureBox1)
Me.Controls.Add(Me.Button1)
Me.Name = "Form1"
Me.Text = "Form1"
CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.NumericUpDown1, System.ComponentModel.ISupportInitialize).EndInit()
Me.StatusStrip1.ResumeLayout(False)
Me.StatusStrip1.PerformLayout()
Me.ResumeLayout(False)
Me.PerformLayout()
End Sub
Friend WithEvents Button1 As System.Windows.Forms.Button
Friend WithEvents PictureBox1 As System.Windows.Forms.PictureBox
Friend WithEvents HScrollBar1 As System.Windows.Forms.HScrollBar
Friend WithEvents NumericUpDown1 As System.Windows.Forms.NumericUpDown
Friend WithEvents StatusStrip1 As System.Windows.Forms.StatusStrip
Friend WithEvents ToolStripStatusLabel1 As System.Windows.Forms.ToolStripStatusLabel
Friend WithEvents ToolStripProgressBar1 As System.Windows.Forms.ToolStripProgressBar
Friend WithEvents Button2 As System.Windows.Forms.Button
End Class
*/Form1.vb/*
Imports System.IO
Imports System.Text
Public Class Form1
Dim m_Stream As Stream
Dim m_Reader As BinaryReader
Dim m_ChunkLen As Integer
Dim m_DataLen As Integer
Dim m_Data As Int16()
Dim m_Channels As Integer
Dim m_Align As Integer
Dim m_SampleChannelSize As Integer
Dim m_SampleRate As Integer
Dim m_Colors As Color() = New Color() {Color.Red, Color.Blue, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black}
Dim m_CMin() As Int32
Dim m_CMax() As Int32
Dim m_Display As DisplayData
Dim m_DisplayNew As DisplayData
'Stores the point data for the currently displayed section
Dim m_Point()() As PointF
Dim m_Bitmap As Bitmap
Dim m_Loaded As Boolean
Dim m_DragStartSample As Integer
Dim m_DragStartChannel As Integer
Dim m_SelectRect As RectangleF
Dim m_IsSelect As Boolean
Dim l_CurSampleChan As New Point
Dim m_Button As Boolean
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim ofd As New OpenFileDialog
ofd.Filter = "WAV Files (*.wav)|*.wav|All Files (*.*)|*.*"
If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
'A file was selected, open it
m_Data = Nothing
If Not m_Stream Is Nothing Then
m_Reader.Close()
m_Reader = Nothing
m_Stream.Close()
m_Stream = Nothing
End If
m_Stream = ofd.OpenFile
OpenWav(m_Stream)
m_Loaded = True
m_Display = New DisplayData
m_DisplayNew = New DisplayData
InitDisplayData()
ReDim m_Point(m_Channels - 1)
SetGraph()
End If
End Sub
Private Sub OpenWav(ByVal wav As Stream)
m_Reader = New BinaryReader(wav)
Console.WriteLine("Chunk ID : " & ReadChunkID())
m_ChunkLen = ReadInt32()
Console.WriteLine("Chunk Size : " & m_ChunkLen)
Console.WriteLine("Chunk Format : " & ReadChunkID())
While m_Stream.Position < (m_ChunkLen - 8)
ReadSubChunk()
End While
End Sub
Private Sub InitDisplayData()
With m_DisplayNew
.DisplayArea = PictureBox1.ClientSize
.SamplesPerPixel = 1
.SampleCount = m_DataLen / ((m_SampleChannelSize / 8) * m_Channels)
.DisplayOffset = 0
End With
End Sub
Private Sub SetGraph()
If Not DisplayData.Equals(m_Display, m_DisplayNew) Then
m_Display.Update(m_DisplayNew)
HScrollBar1.Minimum = 0
HScrollBar1.Maximum = m_Display.SampleCount - m_Display.SamplesDisplayed
HScrollBar1.SmallChange = (m_Display.SampleCount - m_Display.SamplesDisplayed) / (1500 * m_Display.SamplesPerPixel)
HScrollBar1.LargeChange = (m_Display.SampleCount - m_Display.SamplesDisplayed) / (1000 * m_Display.SamplesPerPixel)
'ToolStripStatusLabel1.Text = GetHMS(m_DisplayOffset / m_SampleRate) & " - " & GetHMS((m_DisplayOffset + m_NumDisplaySamples) / m_SampleRate)
ToolStripStatusLabel1.Text = GetHMS(Math.Round(m_Display.DisplayOffset / m_SampleRate)) & " (" & m_Display.DisplayOffset & ") - " & GetHMS(Math.Round((m_Display.DisplayOffset + m_Display.SamplesDisplayed) / m_SampleRate)) & " (" & (m_Display.DisplayOffset + m_Display.SamplesDisplayed) & ")"
RefreshBuffers()
End If
PictureBox1.Image = DrawGraph()
End Sub
Private Function GetHMS(ByVal time As Double) As String
Return TimeSpan.FromSeconds(time).ToString
End Function
Private Sub ReadChunk()
End Sub
Private Sub ReadSubChunk()
Dim SCID As String
Dim CS As Integer
SCID = ReadChunkID()
Console.WriteLine("Sub-Chunk ID : " & SCID)
CS = ReadInt32()
Console.WriteLine("Sub-Chunk Size: " & CS)
Select Case SCID
Case "fmt "
Console.WriteLine("Format : " & ReadInt16())
m_Channels = ReadInt16()
Console.WriteLine("Channels : " & m_Channels)
m_SampleRate = ReadInt32()
Console.WriteLine("Sample Rate : " & m_SampleRate)
Console.WriteLine("Byte Rate : " & ReadInt32())
m_Align = ReadInt16()
Console.WriteLine("Block Align : " & m_Align)
m_SampleChannelSize = ReadInt16()
Console.WriteLine("Sample Size : " & m_SampleChannelSize)
m_Reader.ReadBytes(CS - 16)
Case "data"
m_DataLen = CS
m_Data = ReadSoundData()
Case Else
'skip
m_Reader.ReadBytes(CS)
End Select
End Sub
Private Function ReadChunkID() As String
Dim l_Bytes As Byte()
l_Bytes = m_Reader.ReadBytes(4)
If l_Bytes.Length > 0 Then
Return Encoding.ASCII.GetString(l_Bytes)
Else
Return ""
End If
End Function
Private Function ReadSubChunkID() As String
Dim l_Bytes As Byte()
l_Bytes = m_Reader.ReadBytes(4)
If l_Bytes.Length > 0 Then
Return Convert.ToString(l_Bytes(0), 16).PadLeft(2, "0") & Convert.ToString(l_Bytes(1), 16).PadLeft(2, "0") & Convert.ToString(l_Bytes(2), 16).PadLeft(2, "0") & Convert.ToString(l_Bytes(3), 16).PadLeft(2, "0")
Else
Return ""
End If
End Function
Private Function ReadInt32() As Int32
Dim l_Bytes As Byte()
l_Bytes = m_Reader.ReadBytes(4)
l_Bytes.Reverse()
If l_Bytes.Length > 0 Then
Return BitConverter.ToInt32(l_Bytes, 0)
Else
Return 0
End If
End Function
Private Function ReadInt16() As Int16
Dim l_Bytes As Byte()
l_Bytes = m_Reader.ReadBytes(2)
'l_Bytes.Reverse()
If l_Bytes.Length > 0 Then
Return BitConverter.ToInt16(l_Bytes, 0)
Else
Return 0
End If
End Function
Private Function ReadSoundData() As Int16()
Dim al As New ArrayList
Dim l_Length As Integer = m_DataLen / (m_SampleChannelSize / 8)
Dim data(l_Length - 1) As Int16
'ReDim ReadSoundData(l_Length - 1)
Dim l_Chan As Integer = 1
Dim l_Val As Int16
ReDim m_CMin(m_Channels - 1)
ReDim m_CMax(m_Channels - 1)
For c As Integer = 0 To m_Channels - 1
m_CMax(c) = Int16.MinValue
m_CMin(c) = Int16.MaxValue
Next
ToolStripProgressBar1.Minimum = 0
ToolStripProgressBar1.Maximum = l_Length / m_Channels
ToolStripProgressBar1.Value = 0
For i As Integer = 0 To (l_Length / m_Channels) - 1
For c = 0 To m_Channels - 1
l_Val = m_Reader.ReadInt16()
If l_Val > m_CMax(c) Then m_CMax(c) = l_Val
If l_Val < m_CMin(c) Then m_CMin(c) = l_Val
data((m_Channels * i) + c) = l_Val
Next
If i Mod 2500 = 0 Then
ToolStripProgressBar1.Value = i
Application.DoEvents()
End If
Next
For c As Integer = 0 To m_Channels - 1
Console.WriteLine("C" & c + 1 & "Min: " & m_CMin(c))
Console.WriteLine("C" & c + 1 & "Max: " & m_CMax(c))
Next
ReadSoundData = data
data = Nothing
End Function
Private Sub RefreshBuffers()
'This loop was changed to only process as many points as are displayable (ie. display are width)
For c As Integer = 0 To m_Channels - 1
m_Point(c) = Nothing
ReDim m_Point(c)(m_Display.SamplesDisplayed - 1)
Next
'For c As Integer = 0 To m_Channels - 1
' ReDim l_Point(c)((m_Data.Length / m_Channels) - 1)
'Next
Dim step1 As Single
Dim step2 As Single
Dim step3 As Single
Dim l_Index As Integer
Try
For i As Integer = 0 To m_Display.SamplesDisplayed - 1
For c As Integer = 0 To m_Channels - 1
l_Index = ((m_Channels * (m_Display.DisplayOffset + i)) + c)
'l_Index = ((m_DisplayOffset + (i * m_Channels)) + c)
'Console.WriteLine("Plot Data at " & c & "," & i & " (" & l_Index & ")")
step1 = CSng(m_Data(l_Index) / (m_CMax(c) - m_CMin(c)))
step2 = CSng(step1 * ((PictureBox1.Height / m_Channels) / 2))
step3 = CSng(step2 + ((c * (PictureBox1.Height / m_Channels)) + ((PictureBox1.Height / m_Channels) / 2)))
'l_Point(c)(i) = New PointF(CSng(l_MaxX * (i / ((m_Data.Length / m_Channels) / 2))), step3)
m_Point(c)(i) = New PointF(CSng(i / m_Display.SamplesPerPixel), step3)
Next
Next
Catch ex As Exception
Console.WriteLine("Error at index " & l_Index & " : " & ex.ToString)
End Try
End Sub
Private Function DrawGraph() As Bitmap
'Dim l_Temp(m_Data.Length - 1) As SByte
'm_Data.CopyTo(l_Temp, 0)
'Array.Sort(l_Temp)
'Dim l_MinY As Integer = l_Temp(0)
'Dim l_MaxY As Integer = l_Temp(m_Data.Length - 1)
'Dim l_MinX As Integer = 0
'Dim l_MaxX As Integer = PictureBox1.Width
'MaxX should always be the width of the display area
'Dim l_MaxX As Integer = Math.Min(PictureBox1.Width, (m_Data.Length / m_Channels) / 8)
'l_MaxX = 35000
m_Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
Dim g As Graphics = Graphics.FromImage(m_Bitmap)
g.Clear(Color.FromArgb(181, 223, 225))
'This dim is fine
Dim l_Pen As Pen
For c As Integer = 0 To m_Channels - 1
l_Pen = New Pen(m_Colors(c))
g.DrawLines(l_Pen, m_Point(c))
Next
l_Pen = Nothing
If m_IsSelect Then
With m_SelectRect
.X = (Math.Min(m_DragStartSample, l_CurSampleChan.X) - m_Display.DisplayOffset) * (m_Display.DisplayArea.Width / m_Display.SamplesDisplayed)
.Y = Math.Min(m_DragStartChannel, l_CurSampleChan.Y) * (m_Display.DisplayArea.Height / m_Channels)
.Width = (Math.Abs(m_DragStartSample - l_CurSampleChan.X) + 1) * (m_Display.DisplayArea.Width / m_Display.SamplesDisplayed)
.Height = (Math.Abs(m_DragStartChannel - l_CurSampleChan.Y) + 1) * (m_Display.DisplayArea.Height / m_Channels)
End With
g.FillRectangle(New SolidBrush(Color.FromArgb(185, 0, 225, 0)), m_SelectRect)
'g.DrawRectangle(New Pen(Color.FromArgb(185, 0, 200, 0)), m_SelectRect)
End If
Return m_Bitmap
End Function
Private Sub HScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) Handles HScrollBar1.Scroll
If m_Loaded Then
m_DisplayNew.DisplayOffset = e.NewValue
SetGraph()
End If
End Sub
Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
If m_Loaded And Not Me.WindowState = FormWindowState.Minimized Then
m_DisplayNew.DisplayArea = PictureBox1.ClientSize
SetGraph()
End If
Application.DoEvents()
End Sub
Private Sub NumericUpDown1_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles NumericUpDown1.ValueChanged
If m_Loaded Then
m_DisplayNew.SamplesPerPixel = NumericUpDown1.Value
SetGraph()
End If
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
'Analyse the first 100 samples of channel 1 and determine the component frequencies (Sine-Waves)
For i As Integer = 0 To 99
Console.WriteLine("Sine : " & m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue)
Console.WriteLine("Angle (Rad): " & Math.Asin(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue))
Console.WriteLine("Angle (Deg): " & Math.Asin(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue) / (Math.PI / 180))
Console.WriteLine("2f : " & (Math.Asin(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue) / (Math.PI / 180)) / Math.PI)
Console.WriteLine("f : " & ((Math.Asin(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue) / (Math.PI / 180)) / Math.PI) / 2)
Console.WriteLine("Cosine : " & m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue)
Console.WriteLine("Angle (Rad): " & Math.Acos(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue))
Console.WriteLine("Angle (Deg): " & Math.Acos(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue) / (Math.PI / 180))
Console.WriteLine("2f : " & (Math.Acos(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue) / (Math.PI / 180)) / Math.PI)
Console.WriteLine("f : " & ((Math.Acos(m_Data(GetDataIndex(i, 0)) / UInt16.MaxValue) / (Math.PI / 180)) / Math.PI) / 2)
Next
End Sub
Private Function GetDataIndex(ByVal sampleIndex As Integer, ByVal channelIndex As Integer)
Return (m_Channels * sampleIndex) + channelIndex
End Function
Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
'Enter selection mode
m_Button = True
'Drag Operation Start Sample Index
m_DragStartSample = m_Display.DisplayOffset + (e.X / m_Display.SamplesPerPixel)
m_DragStartSample = m_Display.DisplayOffset + (e.X - (e.X Mod (m_Display.DisplayArea.Width / m_Display.SamplesDisplayed))) / (m_Display.DisplayArea.Width / m_Display.SamplesDisplayed)
m_DragStartChannel = (e.Y - (e.Y Mod (m_Display.DisplayArea.Height / m_Channels))) / (m_Display.DisplayArea.Height / m_Channels)
Console.WriteLine("Sel drag start")
End Sub
Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
If m_Button Then
Console.WriteLine("Sel drag move")
l_CurSampleChan.X = m_Display.DisplayOffset + (e.X / m_Display.SamplesPerPixel)
l_CurSampleChan.X = m_Display.DisplayOffset + (e.X - (e.X Mod (m_Display.DisplayArea.Width / m_Display.SamplesDisplayed))) / (m_Display.DisplayArea.Width / m_Display.SamplesDisplayed)
l_CurSampleChan.Y = (e.Y - (e.Y Mod (m_Display.DisplayArea.Height / m_Channels))) / (m_Display.DisplayArea.Height / m_Channels)
m_SelectRect = New RectangleF
'With m_SelectRect
' .X = Math.Min(e.X / m_Display.SamplesPerPixel, m_DragStartSample)
' .Y = Math.Min(m_DragStartChannel, (e.Y - (e.Y Mod (m_Display.DisplayArea.Height / m_Channels))) / (m_Display.DisplayArea.Height / m_Channels)) * (m_Display.DisplayArea.Height / m_Channels)
' .Width = Math.Abs((e.X / m_Display.SamplesPerPixel) - m_DragStartSample)
' .Height = (Math.Abs(m_DragStartChannel - ((e.Y - (e.Y Mod (m_Display.DisplayArea.Height / m_Channels))) / (m_Display.DisplayArea.Height / m_Channels))) + 1) * m_Display.DisplayArea.Height / m_Channels
'End With
'If e.X > m_DragStart Then
' 'Positive selection
Console.WriteLine(m_SelectRect)
Console.WriteLine(m_Display.DisplayOffset)
'ElseIf e.X < m_DragStart Then
' 'Negative selection
'End If
m_IsSelect = True
PictureBox1.Image = DrawGraph()
PictureBox1.Refresh()
End If
End Sub
Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
m_Button = False
Console.WriteLine("Sel drag end")
End Sub
Private Function GetSampleAtOffset(ByVal offset As Integer) As Integer
End Function
Private Function GetSampleAtPoint(ByVal p As Point) As Integer
End Function
Private Class DisplayData
Dim m_SamplesPerPixel As Single
Dim m_NumSamples As Integer
Dim m_NumDisplaySamples As Integer
Dim m_DisplayOffset As Integer
Dim m_DisplayArea As Size
Public Property DisplayArea() As Size
Get
Return m_DisplayArea
End Get
Set(ByVal value As Size)
m_DisplayArea = value
End Set
End Property
Public Property DisplayOffset() As Integer
Get
Return m_DisplayOffset
End Get
Set(ByVal value As Integer)
m_DisplayOffset = value
End Set
End Property
Public Property SampleCount() As Integer
Get
Return m_NumSamples
End Get
Set(ByVal value As Integer)
m_NumSamples = value
End Set
End Property
Public ReadOnly Property SamplesDisplayed() As Integer
Get
Return m_DisplayArea.Width * m_SamplesPerPixel
End Get
End Property
Public Property SamplesPerPixel() As Single
Get
Return m_SamplesPerPixel
End Get
Set(ByVal value As Single)
m_SamplesPerPixel = value
End Set
End Property
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If obj.GetType.Equals(Me.GetType) Then
If Not obj Is Nothing Then
With CType(obj, DisplayData)
Return (.m_DisplayOffset = m_DisplayOffset) And (.m_DisplayArea = m_DisplayArea) And (.m_NumSamples = m_NumSamples) And (.m_SamplesPerPixel = m_SamplesPerPixel)
End With
Else
Return False
End If
Else
Return False
End If
End Function
Public Sub Update(ByVal disp As DisplayData)
m_DisplayArea = disp.m_DisplayArea
m_DisplayOffset = disp.m_DisplayOffset
m_NumSamples = disp.m_NumSamples
m_SamplesPerPixel = disp.m_SamplesPerPixel
End Sub
End Class
Private Class BufferedDisplayData
Dim m_SamplesPerPixel As BufferedValue(Of Single)
Dim m_NumSamples As BufferedValue(Of Integer)
Dim m_NumDisplaySamples As BufferedValue(Of Integer)
Dim m_DisplayOffset As BufferedValue(Of Integer)
Public Sub New()
m_SamplesPerPixel = New BufferedValue(Of Single)
m_NumSamples = New BufferedValue(Of Integer)
m_NumDisplaySamples = New BufferedValue(Of Integer)
m_DisplayOffset = New BufferedValue(Of Integer)
End Sub
Public Property DisplayOffset() As Integer
Get
Return m_DisplayOffset.Value
End Get
Set(ByVal value As Integer)
m_DisplayOffset.Value = value
End Set
End Property
Public Property SampleCount() As Integer
Get
Return m_NumSamples.Value
End Get
Set(ByVal value As Integer)
m_NumSamples.Value = value
End Set
End Property
Public Property SamplesDisplayed() As Integer
Get
Return m_NumDisplaySamples.Value
End Get
Set(ByVal value As Integer)
m_NumDisplaySamples.Value = value
End Set
End Property
Public Property SamplesPerPixel() As Single
Get
Return m_SamplesPerPixel.Value
End Get
Set(ByVal value As Single)
m_SamplesPerPixel.Value = value
End Set
End Property
Public ReadOnly Property IsChanged() As Boolean
Get
Return m_DisplayOffset.IsChanged Or m_NumSamples.IsChanged Or m_NumDisplaySamples.IsChanged Or m_SamplesPerPixel.IsChanged
End Get
End Property
Public Sub Reset()
m_DisplayOffset.Reset()
m_NumSamples.Reset()
m_NumDisplaySamples.Reset()
m_SamplesPerPixel.Reset()
End Sub
Public Sub Update()
m_DisplayOffset.Update()
m_NumSamples.Update()
m_NumDisplaySamples.Update()
m_SamplesPerPixel.Update()
End Sub
End Class
Private Class BufferedValue(Of T)
Dim m_Curr As T
Dim m_New As T
Dim m_Changed As Boolean
Public Sub New()
m_Curr = Nothing
m_New = Nothing
m_Changed = False
End Sub
Public Sub New(ByVal initialValue As T)
m_Curr = initialValue
m_New = initialValue
m_Changed = False
End Sub
Public Property Value() As T
Get
Return m_Curr
End Get
Set(ByVal value As T)
If Not Object.Equals(m_Curr, value) Then
m_New = value
m_Changed = True
End If
End Set
End Property
Public ReadOnly Property IsChanged() As Boolean
Get
Return m_Changed
End Get
End Property
Public Sub Reset()
m_New = m_Curr
m_Changed = False
End Sub
Public Sub Update()
m_Curr = m_New
m_Changed = False
End Sub
End Class
End Class
*/Form1.resx/*
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace">
<xsd:element name="root" msdata:isdataset="true">
<xsd:complextype>
<xsd:choice maxoccurs="unbounded">
<xsd:element name="metadata">
<xsd:complextype>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minoccurs="0">
<xsd:attribute name="name" use="required" type="xsd:string">
<xsd:attribute name="type" type="xsd:string">
<xsd:attribute name="mimetype" type="xsd:string">
<xsd:attribute ref="xml:space">
<xsd:element name="assembly">
<xsd:complextype>
<xsd:attribute name="alias" type="xsd:string">
<xsd:attribute name="name" type="xsd:string">
<xsd:element name="data">
<xsd:complextype>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minoccurs="0" msdata:ordinal="1">
<xsd:element name="comment" type="xsd:string" minoccurs="0" msdata:ordinal="2">
<xsd:attribute name="name" type="xsd:string" use="required" msdata:ordinal="1">
<xsd:attribute name="type" type="xsd:string" msdata:ordinal="3">
<xsd:attribute name="mimetype" type="xsd:string" msdata:ordinal="4">
<xsd:attribute ref="xml:space">
<xsd:element name="resheader">
<xsd:complextype>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minoccurs="0" msdata:ordinal="1">
<xsd:attribute name="name" type="xsd:string" use="required">
<resheader name="resmimetype">
<value>text/microsoft-resx
<resheader name="version">
<value>2.0
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
<metadata name="StatusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17
|
|
|
|
|
CAN YOU UPLOAD THE REST OF THE CODE AND STOP TOYING WITH THIS EXAMPLE
|
|
|
|
|
Thx worked fine with my PC.
Since original code required Managed DirectSound,this is better for Wav program study
bourgo
|
|
|
|
|
Please either post the source (lots of people have requested it) or, email it to me: fatty_food7@hotmail.com
I tried the source code posted be even when i copy paste and remove the 150 errors because some lines take 1 in vb and 2 here. The controls appear on the form when i view the form in design view but not when i Debug -
|
|
|
|
|
Solved my own problem .... almost Having same problems with time and position labels.
Posted my source all zipped up on my hosting at (couldn't see a way to do it from this site):
http://www.bobmedia.alfahosts.net/Wave.rar
|
|
|
|
|
|
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.
|
|
|
|
|