Загрузка и проигрывание WAV с помощью интерфейса низкого уровня
В примере звукозапись можно увидеть как релизуется запись, проигрывание записанного буфера и сохранение в файл WAV. Пример ниже реализует загрузку звука из файла и его воспроизведение. Данный пример (не весь полностью) я взял из книги Фроловых "Мультимедиа для Windows". Книга писалась еще для древних Windows, однако практически все в ней актульно по сей день. После переписывания исходника на freebasic, я столкнулся с глючностью некоторых функций, при работе на windows7. Так функция (или наверно правильней макрос) mmioFOURCC не возвращает значение, пришлось самому вбивать нужные значения. Другая функция mmioSeek разучилась позиционировать указатель по файлу. В принципе оно и понятно, ведь все функции , начинающиеся с преффикса mmio.. считаются устаревшими и давно не поддерживаются мелкософтом. В другом своем примере (выложу через несколько дней), я решил избавиться от функций mmio.. Там я делал загрузку данных из файла с помощью функций CreateFile, ReadFile и пр.
Платформа: Windows
Авторы: Братья Фроловы
#INCLUDE "windows.bi" #INCLUDE "win/commdlg.bi" #INCLUDE "win/mmsystem.bi" #INCLUDE "crt.bi" #DEFINE WIOERR_BASE (100) #DEFINE WIOERR_NOERROR (0) #DEFINE WIOERR_ERROR (WIOERR_BASE+1) #DEFINE WIOERR_BADHANDLE (WIOERR_BASE+2) #DEFINE WIOERR_BADFLAGS (WIOERR_BASE+3) #DEFINE WIOERR_BADPARAM (WIOERR_BASE+4) #DEFINE WIOERR_BADSIZE (WIOERR_BASE+5) #DEFINE WIOERR_FILEERROR (WIOERR_BASE+6) #DEFINE WIOERR_NOMEM (WIOERR_BASE+7) #DEFINE WIOERR_BADFILE (WIOERR_BASE+8) #DEFINE WIOERR_NODEVICE (WIOERR_BASE+9) #DEFINE WIOERR_BADFORMAT (WIOERR_BASE+10) #DEFINE WIOERR_ALLOCATED (WIOERR_BASE+11) #DEFINE WIOERR_NOTSUPPORTED (WIOERR_BASE+12) #DEFINE WIOERR_READERROR (WIOERR_BASE+13) #DEFINE WAVE_MAPPER -1 Type WAVEIOCB As DWORD dwDataSize As DWORD dwDataOffset As WAVEFORMATEX Ptr lpFmt As LPWAVEHDR lpWaveHdr As HPSTR lpData As WORD wBitsPerSample As WORD wBytesPerSample End Type Dim Shared As WAVEIOCB waveiocbOut Dim Shared As HWAVEOUT hWaveOut Dim msg As MSG Dim As WNDCLASSEX wc Dim As String NameClass="MyClass" Dim As HINSTANCE Hinst=GetModuleHandle(0) '----------------------------------------------------- ' wioSelectFile ' Выбор wav-файла '----------------------------------------------------- Function wioSelectFile(Byref lpszFileName As LPSTR ) As BOOL Dim As OPENFILENAME ofn Dim As ZString*256 szFile,szFileTitle,szFilter = _ !"Wave Files\0*.wav\0Any Files\0*.*\0" memset(@ofn, 0, Sizeof(OPENFILENAME)) ' Инициализируем нужные нам поля ofn.lStructSize = Sizeof(OPENFILENAME) ofn.lpstrFilter = Strptr(szFilter) ofn.nFilterIndex = 1 ofn.lpstrFile = Strptr(szFile) ofn.nMaxFile = Sizeof(szFile) ofn.lpstrFileTitle = Strptr(szFileTitle) ofn.nMaxFileTitle = Sizeof(szFileTitle) ofn.Flags = OFN_PATHMUSTEXIST + OFN_FILEMUSTEXIST + OFN_HIDEREADONLY ' Выбираем входной файл If GetOpenFileName(@ofn) Then ' Копируем путь к выбранному файлу lstrcpy(lpszFileName, szFile) Return TRUE Else Return FALSE Endif End Function '--------------------------------------------------------- ' wioFileOpen ' Открытие и загрузка wav-файла '--------------------------------------------------------- Function wioFileOpen(Byref lpwiocb As WAVEIOCB Ptr ,Byref lpszFileName As LPSTR ) As Integer Dim As HMMIO hmmio Dim As MMCKINFO ckRIFF, ckFMT Dim As Integer dwFmtSize ' Открываем wav-файл hmmio = mmioOpen(lpszFileName, NULL,_ MMIO_READ + MMIO_ALLOCBUF) If hmmio = 0 Then Return WIOERR_FILEERROR ' Ищем фрагмент "WAVE" memset(@ckRIFF, 0, Sizeof(MMCKINFO)) ckRIFF.fccType = &h45564157 ' "WAVE" If mmioDescend(hmmio, @ckRIFF, NULL, MMIO_FINDRIFF) Then mmioClose(hmmio,0) Return WIOERR_BADFORMAT Endif ' Ищем фрагмент "fmt " memset(@ckFMT, 0, Sizeof(MMCKINFO)) ckFMT.ckid = &h20746D66 ' "fmt " If mmioDescend(hmmio,_ @ckFMT, @ckRIFF, MMIO_FINDCHUNK) Then mmioClose(hmmio,0) Return WIOERR_BADFORMAT Endif dwFmtSize = Sizeof(WAVEFORMATEX) 'ckFMT.cksize; ' Получаем память для загрузки формата звукового файла lpwiocb->lpFmt = Cast(WAVEFORMATEX Ptr,Allocate(dwFmtSize)) If lpwiocb->lpFmt = 0 Then mmioClose(hmmio,0) Return WIOERR_NOMEM Endif ' Загружаем формат звукового файла If mmioRead(hmmio, Cast(HPSTR,lpwiocb->lpFmt), dwFmtSize) <> dwFmtSize Then Deallocate(lpwiocb->lpFmt) mmioClose(hmmio,0) Return WIOERR_READERROR Endif ' Проверяем формат звукового файла If lpwiocb->lpFmt->wFormatTag <> WAVE_FORMAT_PCM Then Deallocate(lpwiocb->lpFmt) mmioClose(hmmio,0) Return WIOERR_BADFORMAT Endif ' Проверяем способность драйвера работать с указанным форматом If waveOutOpen(NULL, WAVE_MAPPER, Cast(Any Ptr,lpwiocb->lpFmt),_ NULL, 0L, WAVE_FORMAT_QUERY + WAVE_ALLOWSYNC) Then Deallocate(lpwiocb->lpFmt) mmioClose(hmmio,0) Return WIOERR_BADFORMAT Endif ' Вычисляем количество байт, необходимых для ' хранения одной выборки звукового сигнала lpwiocb->wBitsPerSample = lpwiocb->lpFmt->wBitsPerSample lpwiocb->wBytesPerSample = _ (waveiocbOut.wBitsPerSample/8) * waveiocbOut.lpFmt->nChannels ' Ищем фрагмент "data" ' Функция mmioFOURCC глючит на windows 7, из-за этого ' пришлось ckFMT.ckid прописывать вручную mmioAscend(hmmio, @ckFMT, 0) ckFMT.ckid = &h61746164 ' "data" If mmioDescend(hmmio, @ckFMT, @ckRIFF, MMIO_FINDCHUNK) Then Deallocate(lpwiocb->lpFmt) mmioClose(hmmio,0) Return WIOERR_BADFORMAT Endif ' Определяем размер фрагмента сегмента звуковых ' данных и его смещение в wav-файле lpwiocb->dwDataSize = ckFMT.cksize lpwiocb->dwDataOffset = ckFMT.dwDataOffset ' Проверяем, что файл не пуст If lpwiocb->dwDataSize = 0L Then Deallocate(lpwiocb->lpFmt) mmioClose(hmmio,0) Return WIOERR_BADFORMAT Endif ' Получаем память для заголовка блока lpwiocb->lpWaveHdr = _ Cast(LPWAVEHDR,Allocate(Sizeof(WAVEHDR))) If lpwiocb->lpWaveHdr = 0 Then Return WIOERR_NOMEM Endif ' Получаем память для звуковых данных lpwiocb->lpData = _ Cast(HPSTR,Allocate(lpwiocb->dwDataSize)) If lpwiocb->lpData = 0 Then Return WIOERR_NOMEM Endif ' Позиционирование на начало звуковых данных. ' Функция mmioSeek глючит на windows 7. ' Не перемещает указатель на нужное смещение. ' И чтобы не передали в 3 параметре, всегда ставит его на 0), ' из-за этого пришлось делать лишний вызов mmioRead mmioSeek(hmmio, SEEK_SET, 0) ' Читаем звуковые данные mmioRead(hmmio, lpwiocb->lpData,ckFMT.dwDataOffset) ' Читаем звуковые данные mmioRead(hmmio, lpwiocb->lpData, lpwiocb->dwDataSize) ' Закрываем wav-файл mmioClose(hmmio,0) Return WIOERR_NOERROR End Function '----------------------------------------------------- ' WAVELoad ' Загрузка wav-файла для проигрывания '----------------------------------------------------- Function WAVELoad(Byref lpwiocb As WAVEIOCB Ptr ) As BOOL Dim As ZString*256 szFileName,szBuf Dim As Integer rc ' Проверяем наличие драйвера, способного выводить ' звуковые файлы rc=waveOutGetNumDevs() If (rc=0) Then MessageBox(NULL,_ "Нет устройств для вывода звуковых файлов",_ "Wave Error", MB_OK + MB_ICONHAND) Return FALSE Endif ' Выбираем wav-файл If wioSelectFile(szFileName) = 0 Then Return FALSE ' Открываем и загружаем в память выбранный файл rc = wioFileOpen(lpwiocb, szFileName) If rc = WIOERR_NOERROR Then Return TRUE Elseif rc = WIOERR_FILEERROR Then lstrcpy(szBuf, "Ошибка при открытии файла") Elseif rc = WIOERR_BADFORMAT Then lstrcpy(szBuf, "Неправильный или неподдерживаемый формат файла") Elseif rc = WIOERR_NOMEM Then lstrcpy(szBuf, "Мало памяти") Elseif rc = WIOERR_READERROR Then lstrcpy(szBuf, "Ошибка при чтении") Else lstrcpy(szBuf, "Неизвестная ошибка") Endif MessageBox(NULL, szBuf,_ "Wave Error", MB_OK + MB_ICONHAND) Return FALSE End Function '--------------------------------------------------------- ' wioPlay ' Проигрывание загруженного блока звуковых данных '--------------------------------------------------------- Function wioPlay(Byref lpwiocb As WAVEIOCB Ptr,hwnd As HWND ) As Integer Dim As Short rc ' Открываем устройство вывода rc = waveOutOpen(@hWaveOut, WAVE_MAPPER,_ Cast(Any Ptr,lpwiocb->lpFmt),_ Cint(hwnd), 0, CALLBACK_WINDOW + WAVE_ALLOWSYNC) If rc Then Return rc ' Заполняем заголовок блока данных lpwiocb->lpWaveHdr->lpData = lpwiocb->lpData lpwiocb->lpWaveHdr->dwBufferLength = lpwiocb->dwDataSize lpwiocb->lpWaveHdr->dwBytesRecorded = 0 lpwiocb->lpWaveHdr->dwFlags = 0 lpwiocb->lpWaveHdr->dwLoops = 0 lpwiocb->lpWaveHdr->dwUser = 0 lpwiocb->lpWaveHdr->lpNext = 0 lpwiocb->lpWaveHdr->reserved = 0 ' Подготавливаем заголовок для вывода rc = waveOutPrepareHeader(hWaveOut, lpwiocb->lpWaveHdr,_ Sizeof(WAVEHDR)) If rc Then Deallocate(lpwiocb->lpWaveHdr) Return rc Endif ' Запускаем проигрывание блока rc = waveOutWrite(hWaveOut, lpwiocb->lpWaveHdr, Sizeof(WAVEHDR)) If rc Then waveOutUnprepareHeader(hWaveOut, lpwiocb->lpWaveHdr,_ Sizeof(WAVEHDR)) Deallocate(lpwiocb->lpWaveHdr) Return rc Endif Return 0 End Function '--------------------------------------------------------- ' wioOutError ' Вывод сообщения об ошибке в процессе проигрывания '--------------------------------------------------------- Sub wioOutError(rc As Integer) Dim As ZString*MAXERRORLENGTH szBuf If waveOutGetErrorText(rc , szBuf , MAXERRORLENGTH) = _ MMSYSERR_BADERRNUM Then lstrcpy(szBuf, "Unknown Error") Endif MessageBox(NULL, szBuf,_ "Wave Error", MB_OK + MB_ICONHAND) End Sub ' функция класса Function wndproc(hwnd As HWND, msg As Uinteger,_ wparam As WPARAM, lparam As LPARAM) As Integer Static As BOOL Flag Select Case msg Case WM_CREATE CreateWindowEx(0,"button","Open files Wav",WS_VISIBLE Or WS_CHILD,10,10,130,20,hwnd,Cast(HMENU,1),0,0) CreateWindowEx(0,"button","Play",WS_VISIBLE Or WS_CHILD,10,40,130,20,hwnd,Cast(HMENU,2),0,0) Case wm_command If Loword(wparam) = 1 Then flag = WAVELoad(@waveiocbOut) Elseif Loword(wparam) = 2 Then If flag=TRUE Then wioPlay(@waveiocbOut,hwnd) Endif Endif Case WM_DESTROY ' Останавливаем и закрываем устройство вывода waveOutUnprepareHeader(hWaveOut, waveiocbOut.lpWaveHdr,_ Sizeof(WAVEHDR)) waveOutReset(hWaveOut) waveOutClose(hWaveOut) Deallocate(waveiocbOut.lpFmt) PostQuitMessage(0) End Select Return DefWindowProc(hwnd,msg,wparam,lparam) End Function ' Заполнение структуры WNDCLASSEX With wc .cbSize=SizeOf(WNDCLASSEX) .style=CS_HREDRAW Or CS_VREDRAW .lpfnWndProc=@wndproc .hInstance=Hinst .hIcon=LoadIcon(0,IDI_QUESTION) .hCursor=LoadCursor(0,IDC_ARROW) .hbrBackground=Cast(HBRUSH,COLOR_WINDOW) .lpszClassName=StrPtr(NameClass) .hIconSm=.hIcon End With ' Регистрация класса окна If RegisterClassEx(@wc)=0 Then Print "Register error, press any key" Sleep End Endif 'Создание окна CreateWindowEx(0,NameClass,"Проигрывание Wav файла",_ WS_VISIBLE Or WS_OVERLAPPEDWINDOW,10,10,165,120,0,0,Hinst,0) ' Цикл сообщений While GetMessage(@msg,0,0,0) TranslateMessage(@msg) DispatchMessage(@msg) Wend