Проигрывание с помощью DirectSound

Поистине, Windows рай для разработчиков. Для воспроизведения звука, только встроенными средствами, мне известны 4 способа. Самый простой SndPlaySound, чуть сложнее MciSendString, и далее примерно одинаковы по реализации DirectSound и DirectShow. При том реализация последних в разы проще чем на Linux. Даже с использованием библиотеки ALSA на Linux, простоты не чувствуется. Более менее спасает библиотека OpenAL.

Пример ниже представляет небольшой класс на основе DirectSound. Так как функции , начинающиеся с префикса mmio.. мне не очень симпатичны, я решил загрузку WAV проделать функциями , работающими с файлами (CreateFile, ReadFile...) .

#INCLUDE Once "windows.bi"
#INCLUDE Once "win/mmsystem.bi"
#INCLUDE Once "win/dsound.bi"

Type PlayDS
    As LPDIRECTSOUND lpDS 
    As LPDIRECTSOUNDBUFFER lpDSB,lpDSB2
    As DSBUFFERDESC lPDSBDECC,lPDSBDECC2 
    As WAVEFORMATEX wf
    As HANDLE hFile
    As Byte Ptr bDsSound, bDsSound2
    As Uinteger iLenDsSound1,iLenDsSound2,iEndPosition
    Declare Sub Init(As HWND)
    Declare Sub Play()
    Declare Sub SetPosition(iNewPosition As Uinteger)
    Declare Function GetPosition() As Uinteger
    Declare Function GetEndPosition() As Uinteger
    Declare Sub SetPanorama(As Integer)
    Declare Sub Stop()
    Declare Sub Pause()
    Declare Sub free()  
    Declare Sub ErrorOutput(As Zstring Ptr)
    Declare Function LoadWav( As Zstring Ptr) As BOOL
End Type

Sub PlayDS.Init(hw As HWND)
    
    ErrorOutput(Iif(DirectSoundCreate(NULL, @lpDS, NULL)=DS_OK,_
                    @"Y",@"Ошибка создания DirectSound объекта"))

    ErrorOutput(Iif(lpDS->lpVtbl->SetCooperativeLevel(_
                    lpDS, hw, DSSCL_NORMAL)=DS_OK,@"Y",_
                    @"Ошибка установки уровня взаимодействия с аппаратурой"))

    lPDSBDECC.dwSize = Sizeof(lPDSBDECC) 
    lPDSBDECC.dwFlags = DSBCAPS_PRIMARYBUFFER

    ErrorOutput(Iif(lpDS->lpVtbl->CreateSoundBuffer(_
                    lpDS, @lPDSBDECC, @lpDSB, NULL)=DS_OK,@"Y",_
                    @"Ошибка создания звукового буфера"))
End Sub

Function PlayDS.LoadWav(szFile As Zstring Ptr) As BOOL
    
    Dim As Ubyte Ptr bBuffer
    Dim As Integer iSizeBuffer,iGetReadBytes,iPosition,iSize
    
   hFile = CreateFile(szFile,GENERIC_READ,FILE_SHARE_READ+FILE_SHARE_WRITE,_
            0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
            
   ErrorOutput(Iif(hFile <> INVALID_HANDLE_VALUE,@"Y",_
                @"Ошибка открытия файла WAV"))
                
   ErrorOutput(Iif(SetFilePointer(hFile,4,0,FILE_BEGIN)<>0,@"Y",_
                @"Файл WAV возможно пустой, чтение невозможно!"))   
                            
   ErrorOutput(Iif(ReadFile(hFile,Cast(PVOID,@iSizeBuffer),4,@iGetReadBytes,0)<>0,@"Y",_
                @"Невозможно прочитать размер файла"))  
                    
   bBuffer = Allocate(iSizeBuffer)
   
   If bBuffer<>0 Then
  
    ErrorOutput(Iif(ReadFile(hFile,bBuffer,iSizeBuffer,@iGetReadBytes,0)<>0,@"Y",_
                @"Невозможно прочитать данные файла")) 
                
    CloseHandle(hFile)          
    
    iEndPosition = iGetReadBytes                
                            
    Dim As String sTempBuf = Space(iSizeBuffer)
        CopyMemory(Sadd(sTempBuf),bBuffer,iSizeBuffer)
    
    iPosition = Instr(sTempBuf,"fmt ")+7
    
    If iPosition <> 7 Then
        
        CopyMemory(@wf,bBuffer+iPosition,16)
        
    Endif
    
    ErrorOutput(Iif(wf.wFormatTag = WAVE_FORMAT_PCM,@"Y",_
                @"Формат файла не WAV"))
    
    iPosition = Instr(sTempBuf,"data")+3
    
    If iPosition <> 3 Then

            lPDSBDECC2.dwSize = Sizeof(DSBUFFERDESC)
            lPDSBDECC2.dwFlags = DSBCAPS_STATIC Or DSBCAPS_LOCSOFTWARE Or _
                                        DSBCAPS_CTRLPAN Or DSBCAPS_GLOBALFOCUS 
            lPDSBDECC2.dwBufferBytes = *(Cast(Integer Ptr,(bBuffer+iPosition)))
            lPDSBDECC2.lpwfxFormat = @wf
            ErrorOutput(Iif(lpDS->lpVtbl->CreateSoundBuffer(_
                    lpDS, @lPDSBDECC2, @lpDSB2, NULL)=DS_OK,@"Y",_
                    @"Ошибка создания звукового буфера"))

            ErrorOutput(Iif(lpDSB2->lpVtbl->Lock(lpDSB2, 0, lPDSBDECC2.dwBufferBytes, _
            @bDsSound, @iLenDsSound1, @bDsSound2, @iLenDsSound2, 0)=DS_OK,@"Y",_
                    @"Ошибка запроса обновления данных в буфере"))
                    
            If bDsSound > 0 Then
                CopyMemory(bDsSound,bBuffer+iPosition+4, iLenDsSound1)
            Endif

            If bDsSound2 > 0 Then
                CopyMemory(bDsSound2,bBuffer+iPosition+4+iLenDsSound1, iLenDsSound2)
            Endif
            
            ErrorOutput(Iif(lpDSB2->lpVtbl->Unlock(_
            lpDSB2, bDsSound, iLenDsSound1, bDsSound2, iLenDsSound2)=DS_OK,@"Y",_
                    @"Ошибка завершения обновлений данных в буфере"))   

    Endif       
    
   Endif
     
   Deallocate(bBuffer)
                                                            
   Return TRUE
   
End Function

Sub PlayDS.Play()
    
    ErrorOutput(Iif(lpDSB2->lpVtbl->Play(_
            lpDSB2, 0, 0, 0)=DS_OK,@"Y",_
            @"Ошибка воспроизведения"))  
              
End Sub

Sub PlayDS.SetPanorama(iValue As Integer)
    ErrorOutput(Iif(lpDSB2->lpVtbl->setPan(lpDSB2,iValue)=DS_OK,@"Y",_
            @"Ошибка установки значений панорамного звучания"))
End Sub

Sub PlayDS.SetPosition(iNewPosition As Uinteger)
    
    ErrorOutput(Iif(lpDSB2->lpVtbl->SetCurrentPosition(lpDSB2,iNewPosition)=DS_OK,@"Y",_
            @"Ошибка установки новой позиции"))  
              
End Sub

Function PlayDS.GetPosition() As Uinteger
    Dim iGetPosition As Integer
    ErrorOutput(Iif(lpDSB2->lpVtbl->GetCurrentPosition(lpDSB2,@iGetPosition,0)=DS_OK,@"Y",_
            @"Ошибка получения позиции"))  
    Return iGetPosition       
End Function

Function PlayDS.GetEndPosition() As Uinteger
    Return iEndPosition       
End Function

Sub PlayDS.Stop()
    
    ErrorOutput(Iif(lpDSB2->lpVtbl->Stop(lpDSB2)=DS_OK,@"Y",_
            @"Ошибка остановки"))
    SetPosition(0)      
              
End Sub

Sub PlayDS.Pause()
    
    ErrorOutput(Iif(lpDSB2->lpVtbl->Stop(lpDSB2)=DS_OK,@"Y",_
            @"Ошибка остановки на паузу"))  
              
End Sub

Sub PlayDS.Free()

    If lpDSB2 <> NULL Then lpDSB2->lpVtbl->Release(lpDSB2)
    If lpDSB <> NULL Then lpDSB->lpVtbl->Release(lpDSB)
    If lpDS <> NULL Then lpDS->lpVtbl->Release(lpDS)
              
End Sub

Sub PlayDS.ErrorOutput(Param As Zstring Ptr)
    
    If *param<>"Y" Then
        MessageBox(0,Param,"Ошибка",0)
        End
    Endif
    
End Sub


Dim Shared As PlayDS PDS
Dim msg As MSG
Dim Shared As HWND hw

Sub pr()
    Var i = PDS.GetPosition()
    Dim st As Integer
    PDS.lpDSB2->lpVtbl->GetStatus(PDS.lpDSB2,@st)
    If st=1 Then
        SetWindowText(hw,"Pos: " & i & " " & PDS.GetEndPosition())
    Endif
    
End Sub

Sub pr2()
    Static i As Integer = -2000
    Static j As Integer
    If i = 2000 Then
        j = -50
    Elseif i = -2000 Then
        j = 50
    Endif
    
    i+=j
    PDS.SetPanorama(i)
End Sub

hw = CreateWindowEx(0,"#32770","Первое окно",WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,300,300,0,0,0,0)

PDS.Init(hw)
PDS.LoadWav("C:\1.wav")
PDS.Play

SetTimer(hw,1,100,Cast(Any Ptr,@pr))
SetTimer(hw,2,10,Cast(Any Ptr,@pr2))

While GetMessage(@msg,0,0,0)
    DispatchMessage(@msg)
    
    If msg.message=WM_COMMAND Then
        PDS.Stop
        PDS.Free    
        Exit While
    Endif
Wend