Проигрывание с помощью 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