Classic Directsound example….

Hello the site that this was hosted on went down, so I figured now was a good time to bring it over to my blog. Later I will fix any “option strict off” errors, and review it against my current understanding of things.

Option Strict Off
Imports Microsoft.DirectX.DirectSound
Imports System.IO
Imports System.Text
Public Class Form1
    WithEvents SecondaryBuffer As SecondaryBuffer
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        '1. The latest DirectX SDK must be installed!
        '2. Add a Reference to C:\Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0\Microsoft.DirectX.DirectSound.dll
        '3. Imports Microsoft.DirectX.DirectSound
        '4. This uses .Net Framwork 2.0
        '5. go to Debug/Exceptions. OPen up the Managed Debugging Assistants node on the tree and scroll down till you find LoaderLock and uncheck it. 
        Dim WaveFileOptions As New Wave.WaveFileOptions
        Dim WaveFile As New Wave(WaveFileOptions)
        Dim ResourceBytes(My.Resources.NewWav.Length - 1) As Byte
        Dim Stream0 As UnmanagedMemoryStream = My.Resources.NewWav
        Dim Buffer(Stream0.Length - 1) As Byte
        Stream0.Read(Buffer, 0, Stream0.Length)
        Stream0.Dispose()
        WaveFile.SetDirectBytes(Buffer)
        Dim DXFormat As New Microsoft.DirectX.DirectSound.WaveFormat
        DXFormat.AverageBytesPerSecond = WaveFile.FileFormatSubChunk.ByteRate
        DXFormat.BitsPerSample = WaveFile.FileFormatSubChunk.BitsPerSample
        DXFormat.BlockAlign = WaveFile.FileFormatSubChunk.BlockAlign
        DXFormat.Channels = WaveFile.FileFormatSubChunk.NumChannels
        DXFormat.FormatTag = WaveFormatTag.Pcm
        DXFormat.SamplesPerSecond = WaveFile.FileFormatSubChunk.SampleRate
        Dim Desc As New BufferDescription(DXFormat)
        Dim BufferDescription As New Microsoft.DirectX.DirectSound.BufferDescription(DXFormat)
        BufferDescription.BufferBytes = WaveFileOptions.BitsPerSample
        BufferDescription.ControlFrequency = True
        BufferDescription.ControlPan = True
        BufferDescription.ControlVolume = True
        BufferDescription.GlobalFocus = True
        BufferDescription.StaticBuffer = True
        Dim Stream As New System.IO.MemoryStream(WaveFile.GetBytes)
        Dim SoundDevice As New Device
        SoundDevice.SetCooperativeLevel(Me.Handle, CooperativeLevel.Priority)
        SecondaryBuffer = New Microsoft.DirectX.DirectSound.SecondaryBuffer(Stream, BufferDescription, SoundDevice)
        SecondaryBuffer.Volume = -1
        SecondaryBuffer.Pan = 0
        SecondaryBuffer.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Looping)
    End Sub
End Class
Public Class Wave
    'By Paul Ishak
    'WAVE PCM soundfile format 
    'The Canonical WAVE file format
    'As Described Here: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
    Public FileHeader As Header
    Public FileFormatSubChunk As FormatSubChunk
    Public FileDataSubChunk As DataSubChunk
    Const _Byte As Integer = 1
    Const _word As Integer = 2
    Const _dword As Integer = 4
    Const _qword As Integer = 8
    Public Structure WaveFileOptions
        Public SampleRate As WavSampleRate
        Public AudioFormat As Format
        Public BitsPerSample As BitsPerSample
        Public NumberOfChannels As NumberOfChannels
        Public FormatSize As FormatSize
        Public NumberOfSamples As UInt32
        Public Data As Byte()
    End Structure
    '                                               DATATYPE          OFFSET        Endian           Description
    Structure Header
        Public Property ChunkID As Byte() '          Dword              0             Big            Contains the letters "RIFF" in ASCII form(0x52494646 big-endian form).
        Public Property ChunkSize As UInt32 '        Dword              4             Little         36 + SubChunk2Size, or more precisely: 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
        Public Property Format As Byte() '           Dword              8             Big            Contains the letters "WAVE" in ASCII form (0x57415645 big-endian form).
    End Structure
    Structure FormatSubChunk
        Public Property Subchunk1ID As Byte() '      Dword              12            Big            Contains the letters "fmt "(0x666d7420 big-endian form).
        Public Property Subchunk1Size As UInt32 '    Dword              16            little         16 for PCM.  This is the size of the rest of the Subchunk which follows this number.
        Public Property AudioFormat As UInt16  '     Word               20            little         PCM = 1 (i.e. Linear quantization)Values other than 1 indicate some form of compression.
        Public Property NumChannels As UInt16 '      Word               22            little         Mono = 1, Stereo = 2, etc.
        Public Property SampleRate As UInt32 '       Dword              24            little         8000, 44100, etc.
        Public Property ByteRate As UInt32 '         Dword              28            little         == SampleRate * NumChannels * BitsPerSample/8
        Public Property BlockAlign As UInt16 '       Word               32            little         == NumChannels * BitsPerSample/8
        Public Property BitsPerSample As UInt16 '    Word               34            little         8 bits = 8, 16 bits = 16, etc.
    End Structure
    Structure DataSubChunk
        Public Property Subchunk2ID As Byte() '      Dword              36            Big            Contains the letters "data"(0x64617461 big-endian form).
        Public Property Subchunk2Size As UInt32 '    Dword              40            little         == NumSamples * NumChannels * BitsPerSample/8     This is the number of bytes in the data.
        Public Property Data As Byte() '             VariableLength     44            little         The actual sound data.
    End Structure
    Public Sub OpenFile(FileName As String)
        Try
            If Not InStr(LCase(FileName), ".wav") Then Throw New Exception("Invalid File Extension Specified!")
            If Not My.Computer.FileSystem.FileExists(FileName) Then Throw New Exception("File Does Not Exist!")
            Dim FileBytes() As Byte = My.Computer.FileSystem.ReadAllBytes(FileName)
            'Get Header
            Me.FileHeader.ChunkID = GetDataFromByteArray(FileBytes, 0, 0, _dword)
            Me.FileHeader.ChunkSize = BitConverter.ToInt32(GetDataFromByteArray(FileBytes, 0, 4, _dword), 0)
            Me.FileHeader.Format = GetDataFromByteArray(FileBytes, 0, 8, _dword)
            'Get FormatSubChunk
            Me.FileFormatSubChunk.Subchunk1ID = GetDataFromByteArray(FileBytes, 0, 12, _dword)
            Me.FileFormatSubChunk.Subchunk1Size = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 16, _dword), 0)
            Me.FileFormatSubChunk.AudioFormat = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 20, _word), 0)
            Me.FileFormatSubChunk.NumChannels = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 22, _word), 0)
            Me.FileFormatSubChunk.SampleRate = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 24, _dword), 0)
            Me.FileFormatSubChunk.ByteRate = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 28, _dword), 0)
            Me.FileFormatSubChunk.BlockAlign = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 32, _word), 0)
            Me.FileFormatSubChunk.BitsPerSample = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 34, _word), 0)
            'Get DataSubChunck
            Me.FileDataSubChunk.Subchunk2ID = GetDataFromByteArray(FileBytes, 0, 36, _dword)
            Me.FileDataSubChunk.Subchunk2Size = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 40, _dword), 0)
            Me.FileDataSubChunk.Data = GetDataFromByteArray(FileBytes, 0, 44, Me.FileDataSubChunk.Subchunk2Size)
        Catch
            Throw New Exception("File Is Invalid or corrupt!")
        End Try
    End Sub
    Public Sub SetDirectBytes(FileBytes() As Byte)
        Try
            'Get Header
            Me.FileHeader.ChunkID = GetDataFromByteArray(FileBytes, 0, 0, _dword)
            Me.FileHeader.ChunkSize = BitConverter.ToInt32(GetDataFromByteArray(FileBytes, 0, 4, _dword), 0)
            Me.FileHeader.Format = GetDataFromByteArray(FileBytes, 0, 8, _dword)
            'Get FormatSubChunk
            Me.FileFormatSubChunk.Subchunk1ID = GetDataFromByteArray(FileBytes, 0, 12, _dword)
            Me.FileFormatSubChunk.Subchunk1Size = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 16, _dword), 0)
            Me.FileFormatSubChunk.AudioFormat = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 20, _word), 0)
            Me.FileFormatSubChunk.NumChannels = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 22, _word), 0)
            Me.FileFormatSubChunk.SampleRate = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 24, _dword), 0)
            Me.FileFormatSubChunk.ByteRate = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 28, _dword), 0)
            Me.FileFormatSubChunk.BlockAlign = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 32, _word), 0)
            Me.FileFormatSubChunk.BitsPerSample = BitConverter.ToUInt16(GetDataFromByteArray(FileBytes, 0, 34, _word), 0)
            'Get DataSubChunck
            Me.FileDataSubChunk.Subchunk2ID = GetDataFromByteArray(FileBytes, 0, 36, _dword)
            Me.FileDataSubChunk.Subchunk2Size = BitConverter.ToUInt32(GetDataFromByteArray(FileBytes, 0, 40, _dword), 0)
            Me.FileDataSubChunk.Data = GetDataFromByteArray(FileBytes, 0, 44, Me.FileDataSubChunk.Subchunk2Size)
        Catch ex As Exception
            ' Throw New Exception("File Is Invalid or corrupt!")
            MsgBox(ex.StackTrace)
        End Try
    End Sub
 
    Public Function GetBytes() As Byte()
        Dim Results As Byte() = Nothing
        Results = CombineArrays(FileHeader.ChunkID, BitConverter.GetBytes(FileHeader.ChunkSize))
        Results = CombineArrays(Results, FileHeader.Format)
        Results = CombineArrays(Results, FileFormatSubChunk.Subchunk1ID)
        Results = CombineArrays(Results, BitConverter.GetBytes(FileFormatSubChunk.Subchunk1Size))
        Results = CombineArrays(Results, BitConverter.GetBytes(FileFormatSubChunk.AudioFormat))
        Results = CombineArrays(Results, BitConverter.GetBytes(FileFormatSubChunk.NumChannels))
        Results = CombineArrays(Results, BitConverter.GetBytes(FileFormatSubChunk.SampleRate))
        Results = CombineArrays(Results, BitConverter.GetBytes(FileFormatSubChunk.ByteRate))
        Results = CombineArrays(Results, BitConverter.GetBytes(FileFormatSubChunk.BlockAlign))
        Results = CombineArrays(Results, BitConverter.GetBytes(FileFormatSubChunk.BitsPerSample))
        Results = CombineArrays(Results, FileDataSubChunk.Subchunk2ID)
        Results = CombineArrays(Results, BitConverter.GetBytes(FileDataSubChunk.Subchunk2Size))
        Results = CombineArrays(Results, FileDataSubChunk.Data)
        Return Results
    End Function
    Function CombineArrays(Array1() As Byte, Array2() As Byte) As Byte()
        Dim AllResults(Array1.Length + Array2.Length - 1) As Byte
        Array1.CopyTo(AllResults, 0)
        Array2.CopyTo(AllResults, Array1.Length)
        Return AllResults
    End Function
    Private Function GetDataFromByteArray(ByVal ByteArray As Byte(), ByVal BlockOffset As Long, ByVal RangeStartOffset As Long, ByVal DataLength As Long) As Byte()
        On Error Resume Next
        Dim AnswerL As New List(Of Byte)
        Dim Answer(0 To CInt((DataLength - 1))) As Byte
        Dim CurrentOffset As Long
        For I = 0 To UBound(ByteArray)
            CurrentOffset = BlockOffset + I
            If CurrentOffset >= RangeStartOffset Then
                If CurrentOffset <= RangeStartOffset + DataLength Then
                    AnswerL.Add(ByteArray(I))
                End If
            End If
        Next
        Dim count As Integer = -1
        For Each bt As Byte In AnswerL
            count = count + 1
            Answer(count) = bt
        Next
        Return Answer
    End Function
    Public Function ViewBinary(Optional HexOffsets As Boolean = True) As String
        Dim asc As New StringBuilder
        Dim Hex As New StringBuilder
        Dim result As New StringBuilder
        Dim MB As Byte() = Me.GetBytes
        Select Case HexOffsets
            Case True
                result.Append(" OFFSET " & "    " & "00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F  " & "    " & "0123456789ABCDEF" & vbCrLf & vbCrLf)
            Case False
                result.Append(" OFFSET " & "    " & "00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  " & "    " & "0123456789012345" & vbCrLf & vbCrLf)
        End Select
        Dim Count As Integer = -16
        For Each B As Byte In MB
            Count = Count + 1
            Hex.Append(B.ToString("X2") & "  ")
            If B > 32 And B < 128 Then
                asc.Append(Chr(B))
            Else
                asc.Append(".")
            End If
            If Count Mod 16 = 0 Then
                Select Case HexOffsets
                    Case True
                        result.Append(Count.ToString("X").PadLeft(8, "0") & "    " & Hex.ToString & "    " & asc.ToString & vbCrLf)
                    Case False
                        result.Append(Count.ToString.PadLeft(8, "0") & "    " & Hex.ToString & "    " & asc.ToString & vbCrLf)
                End Select
                Hex.Remove(0, Hex.ToString.Length)
                asc.Remove(0, asc.ToString.Length)
            End If
        Next
        Dim Counts As String = String.Empty
        Select Case HexOffsets
            Case True
                Counts = (Count + (16 - (Count Mod 16))).ToString("X").PadLeft(8, "0").ToString
            Case False
                Counts = (Count + (16 - (Count Mod 16))).ToString.PadLeft(8, "0").ToString
        End Select
        Dim H As Integer = 64 - Hex.ToString.Length
        Dim FinalHex As String = Hex.ToString & Space(H)
        result.Append(Counts & "    " & FinalHex & "    " & asc.ToString & vbCrLf)
        Return result.ToString
    End Function
    Public Shared Function SelectWavData(PictureBox As PictureBox, SelectionColor As Color, Origin As Integer, TextColor As Color, Optional SetLocal As Integer = -1) As SelectedData
        Dim LocalMousePosition As Point = PictureBox.PointToClient(Cursor.Position)
        Dim BM As New Bitmap(PictureBox.Size.Width, PictureBox.Size.Height)
        Dim gr As Graphics = Graphics.FromImage(BM)
        Dim Pen As New Pen(Brushes.Green, 1)
        If SetLocal > -1 Then
            LocalMousePosition.X = SetLocal
        End If
        If LocalMousePosition.X > PictureBox.ClientRectangle.Width Then
            LocalMousePosition.X = PictureBox.ClientRectangle.Width
        End If
        If LocalMousePosition.X < 0 Then
            LocalMousePosition.X = 0
        End If
        Dim Line1point1 As New Point(LocalMousePosition.X, 0)
        Dim Line1point2 As New Point(LocalMousePosition.X, PictureBox.ClientRectangle.Height)
        Dim Line2point1 As New Point(Origin, 0)
        Dim Line2point2 As New Point(Origin, PictureBox.ClientRectangle.Height)
        Dim Poly(3) As Point
        Poly(0) = Line1point1
        Poly(1) = Line2point1
        Poly(2) = Line2point2
        Poly(3) = Line1point2
        If Origin = LocalMousePosition.X = False Then
            gr.FillPolygon(New SolidBrush(SelectionColor), Poly)
            '  gr.DrawPolygon(New Pen(Brushes.Yellow, 2), Poly)
        End If
        PictureBox.Image = BM
        Select Case LocalMousePosition.X > Origin
            Case True
                gr.DrawString("Selected Range(" & Origin & "-" & LocalMousePosition.X & ")", New Font("Consolas", 8), New SolidBrush(TextColor), 1, 1)
                Return New SelectedData(Origin, LocalMousePosition.X)
            Case Else
                gr.DrawString("Selected Range(" & LocalMousePosition.X & "-" & Origin & ")", New Font("Consolas", 8), New SolidBrush(TextColor), 1, 1)
                Return New SelectedData(LocalMousePosition.X, Origin)
        End Select
    End Function
    Public Shared Sub DrawWave(PictureBox As PictureBox, GridSize As Integer, CurrentWaveFile As Wave, GridColor As Color, SineColor As Color)
        Dim Bytes As Byte() = CurrentWaveFile.FileDataSubChunk.Data
        Dim Data(0 To UBound(Bytes)) As UInt16
        Dim Count As Integer = -1
        For Each b As Byte In Bytes
            Count = Count + 1
            Data(Count) = CType(b, UInt16)
        Next
 
        Dim InvertedData(0 To UBound(Bytes)) As UInt32
        For I = 0 To UBound(Data)
            InvertedData(I) = ((255 - Data(I)) \ 2) + ((PictureBox.Height - (255 \ 2)) \ 2)
        Next
        Dim Points As New List(Of Point)
        For I = 0 To UBound(InvertedData)
            Dim Point As New Point(InvertedData(I), I + 1)
            Points.Add(Point)
        Next
        Dim PointsArr() As Point = Points.ToArray
        Dim BM As New Bitmap(PictureBox.Height, PictureBox.Width)
        Dim gr As Graphics = Graphics.FromImage(BM)
        Dim Pen As New Pen(SineColor, 1)
        Dim pen2 As New Pen(Brushes.Red, 3)
        Dim pen3 As New Pen(GridColor, 1)
        For I = 1 To PictureBox.Width Step GridSize
            gr.DrawLine(pen3, New Point(0, I), New Point(PictureBox.Height, I))
            gr.DrawLine(pen3, New Point(I, 0), New Point(I, PictureBox.Width))
        Next
        gr.DrawLines(Pen, PointsArr)
        gr.DrawLine(pen2, New Point(0, PictureBox.ClientRectangle.Width / 2), New Point(PictureBox.Height, PictureBox.ClientRectangle.Width / 2))
        gr.DrawLine(pen3, New Point(0, PictureBox.ClientRectangle.Width / 2), New Point(PictureBox.Height, PictureBox.ClientRectangle.Width / 2))
        BM.RotateFlip(RotateFlipType.Rotate270FlipNone)
        PictureBox.BackgroundImage = BM
    End Sub
    Public Structure SelectedData
        Public SelectionStart As Integer
        Public SelectionEnd As Integer
        Sub New(SelectionStart As Integer, SelectionEnd As Integer)
            Me.SelectionStart = SelectionStart
            Me.SelectionEnd = SelectionEnd
        End Sub
    End Structure
    Sub New(Options As WaveFileOptions)
        FileHeader.ChunkID = Encoding.ASCII.GetBytes("RIFF")
        FileFormatSubChunk.Subchunk1Size = Options.FormatSize
        FileFormatSubChunk.NumChannels = Options.NumberOfChannels
        FileFormatSubChunk.BitsPerSample = Options.BitsPerSample
        FileDataSubChunk.Subchunk2Size = Options.NumberOfSamples * Options.NumberOfChannels * Options.BitsPerSample / 8
        FileHeader.ChunkSize = 4 + (8 + FileFormatSubChunk.Subchunk1Size) + (8 + FileDataSubChunk.Subchunk2Size)
        FileHeader.Format = Encoding.ASCII.GetBytes("WAVE")
        FileFormatSubChunk.Subchunk1ID = Encoding.ASCII.GetBytes("fmt ")
        FileFormatSubChunk.AudioFormat = Options.AudioFormat
        FileFormatSubChunk.SampleRate = Options.SampleRate
        FileFormatSubChunk.ByteRate = Options.SampleRate * Options.NumberOfChannels * Options.BitsPerSample / 8
        FileFormatSubChunk.BlockAlign = Options.NumberOfChannels * Options.BitsPerSample / 8
        FileDataSubChunk.Subchunk2ID = Encoding.ASCII.GetBytes("data")
        FileDataSubChunk.Data = Options.Data
    End Sub
    Public Enum WavSampleRate As UInt32
        hz8000 = 8000
        hz11025 = 11025
        hz16000 = 16000
        hz22050 = 22050
        hz32000 = 32000
        hz44100 = 44100
        hz48000 = 48000
        hz96000 = 96000
        hz192000 = 192000
    End Enum
    Public Enum Format As UInt16
        Standard = 1
    End Enum
    Public Enum BitsPerSample As UInt16
        bps_8 = 8
        bps_16 = 16
        bps_32 = 32
        bps_64 = 64
        bps_128 = 128
        bps_256 = 256
    End Enum
    Public Enum NumberOfChannels As UInt16
        Mono = 1
        Stereo = 2
    End Enum
    Public Enum FormatSize As UInt32
        PCM = 16
    End Enum
End Class
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s