Low level Audio Playback

Apr 23 at 12:22 AM
Hello Mark,

I tried to work the low level winmm.dll functionality out, but encountered several problems. My goal is to play a wave file from stream using WaveOutOpen, WaveOutPrepareHeader und WaveOutWrite, using it only once to play the whole file:
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.IO
Imports System.Threading

Public Class Form1

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure WAVEHDR
        Public lpData As Integer
        Public dwBufferLength As Integer
        Public dwBytesRecorded As Integer
        Public dwUser As Integer
        Public dwFlags As Integer
        Public dwLoops As Integer
        Public lpNext As Integer
        Public Reserved As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure WAVEFORMATEX
        Public wFormatTag As Int16
        Public nChannels As Int16
        Public nSamplesPerSec As Int32
        Public nAvgBytesPerSec As Int32
        Public nBlockAlign As Int16
        Public wBitsPerSample As Int16
        Public cbSize As Int16
    End Structure

    Public Const CALLBACK_WINDOW As UInteger = &H10000

    Private Declare Function waveOutGetNumDevs Lib "winmm.dll" () As UInteger
    Private Declare Function waveOutOpen Lib "winmm.dll" (ByRef lphWaveOut As Int32, ByVal uDeviceID As Int32, ByRef lpFormat As WAVEFORMATEX, ByVal dwCallback As waveOutProc, ByVal dwInstance As Int32, ByVal dwFlags As Int32) As Int32
    Private Declare Function waveOutClose Lib "winmm.dll" (ByVal hWaveOut As Int32) As Int32
    Private Declare Function waveOutPrepareHeader Lib "winmm.dll" (ByVal hWaveOut As Int32, ByRef lpWaveOutHdr As IntPtr, ByVal uSize As Int32) As Int32
    Private Declare Function waveOutUnprepareHeader Lib "winmm.dll" (ByVal hWaveOut As Int32, ByRef lpWaveOutHdr As IntPtr, ByVal uSize As Int32) As Int32
    Private Declare Function waveOutWrite Lib "winmm.dll" (ByVal hWaveOut As Int32, ByRef lpWaveOutHdr As IntPtr, ByVal uSize As Int32) As Int32
    Private Delegate Sub waveOutProc(ByVal hwo As IntPtr, ByVal uMsg As Integer, ByVal dwInstance As IntPtr, ByVal dwParam1 As IntPtr, ByVal dwParam2 As IntPtr)


    Dim hWaveOut As IntPtr = 0
    Private callback As waveOutProc

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Song As Stream = My.Resources.AiR___Waves_Complete_VST_RTAS_TDM_7_1_1_6_installer

        'FormatDataLength lesen.
        Song.Seek(16, SeekOrigin.Begin)
        Dim FormatDataLength As Integer = Song.ReadByte '16 or 17 or 18 Bytes Sized FormatDataBlock. => Benötigt für Bytes Luft.
        FormatDataLength += Song.ReadByte * 256
        FormatDataLength += Song.ReadByte * 256 * 256
        FormatDataLength += Song.ReadByte * 256 * 256 * 256

        'FormatType lesen.
        Song.Seek(20, SeekOrigin.Begin)
        Dim FormatType As Short = Song.ReadByte '1 = PCM.
        FormatType += Song.ReadByte * 256

        'Channels lesen.
        Song.Seek(22, SeekOrigin.Begin)
        Dim Channels As Short = Song.ReadByte '2 Channels.
        Channels += Song.ReadByte * 256

        'SampleRate lesen.
        Song.Seek(24, SeekOrigin.Begin)
        Dim SampleRate As Integer = Song.ReadByte '44100 Hz.
        SampleRate += Song.ReadByte * 256
        SampleRate += Song.ReadByte * 256 * 256
        SampleRate += Song.ReadByte * 256 * 256 * 256

        'Bytes / Second lesen.
        Song.Seek(28, SeekOrigin.Begin)
        Dim BytesPerSecond As Integer = Song.ReadByte '176400 Bytes/s.
        BytesPerSecond += Song.ReadByte * 256
        BytesPerSecond += Song.ReadByte * 256 * 256
        BytesPerSecond += Song.ReadByte * 256 * 256 * 256

        'Bytes / Sample lesen.
        Song.Seek(32, SeekOrigin.Begin)
        Dim BytesPerSample As Short = Song.ReadByte '4 Bytes/Sample.
        BytesPerSample += Song.ReadByte * 256

        'Bits / Sample lesen.
        Song.Seek(34, SeekOrigin.Begin)
        Dim BitsPerSample As Short = Song.ReadByte '16 Bits/Sample.
        BitsPerSample += Song.ReadByte * 256

        'DataSize und DataBeginOffset lesen.
        Dim DataSize As Integer = 0
        Dim DataOffset As Byte = 0
        If 18 - FormatDataLength = 2 Then '0 Bytes Luft.

            'DataSize auslesen.
            Song.Seek(40, SeekOrigin.Begin)

            'Ab Offset 44 ist alles Data innerhalb der Datasize.
            DataOffset = 44

        ElseIf 18 - FormatDataLength = 1 Then '1 Byte Luft.

            'DataSize auslesen.
            Song.Seek(41, SeekOrigin.Begin)

            'Ab Offset 45 ist alles Data innerhalb der Datasize.
            DataOffset = 45

        ElseIf 18 - FormatDataLength = 0 Then '2 Byte Luft.

            'DataSize auslesen.
            Song.Seek(42, SeekOrigin.Begin)

            'Ab Offset 46 ist alles Data innerhalb der Datasize.
            DataOffset = 46

        End If

        DataSize += Song.ReadByte
        DataSize += Song.ReadByte * 256
        DataSize += Song.ReadByte * 256 * 256
        DataSize += Song.ReadByte * 256 * 256 * 256


        Dim wfx As New WAVEFORMATEX
        With wfx
            .wFormatTag = FormatType
            .nChannels = Channels
            .nSamplesPerSec = SampleRate
            .nAvgBytesPerSec = BytesPerSecond
            .nBlockAlign = BytesPerSample
            .wBitsPerSample = BitsPerSample
            .cbSize = 0
        End With

        waveOutOpen(hWaveOut, -1, wfx, callback, CType(0, IntPtr), 196608 Or 8)
        Dim bufferData(DataSize - 1) As Byte
        For i = 0 To DataSize - 1
            bufferData(i) = Song.ReadByte
        Next

        Dim dataHandle As GCHandle = GCHandle.Alloc(bufferData, GCHandleType.Pinned)
        Dim wavHeader As New WAVEHDR
        With wavHeader
            .dwBufferLength = 44100 * Channels * ((BitsPerSample + 7) / 8)
            .dwFlags = 0
            .lpData = dataHandle.AddrOfPinnedObject
            .dwBytesRecorded = 0
            .dwUser = IntPtr.Zero
            .dwLoops = 0
            .lpNext = IntPtr.Zero
            .Reserved = 0
        End With
        Dim headerHandle As GCHandle = GCHandle.Alloc(wavHeader, GCHandleType.Pinned)

        MsgBox(waveOutPrepareHeader(hWaveOut, headerHandle.AddrOfPinnedObject(), Len(wavHeader)))
        MsgBox(waveOutWrite(hWaveOut, headerHandle.AddrOfPinnedObject, Len(wavHeader)))

    End Sub

End Class
Unfortunately, the messageboxes return the code "INVALPARAM = 11", Invalid parameter passed. Can you help me to fix this?

Best regards,

Freefall