Приложение WAVE
В качестве примера использования интерфейса нижнего уровня для записи и воспроизведения wav-файлов мы представим вам исходные тексты приложения WAVE (рис. 2.5).
Рис. 2.5. Главное окно приложения WAVE
Это приложение по своему внешнему виду и функциям аналогично приложению MCIWAVER, которое было создано с использованием интерфейса MCI.
Приложение WAVE заказывает два глобальных блока памяти, один из которых используется при записи, другой - при воспроизведении. Исходный текст основного модуля приложения представлен в листинге 2.16.
Листинг 2.16. Файл wave\wave.cpp
// ---------------------------------------- // Приложение WAVE // Проигрывание и запись wav-файлов на низком уровне // ----------------------------------------
#define STRICT #include <windows.h> #include <windowsx.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop
#include "wave.hpp" #include "waveio.hpp"
// Идентификатор таймера #define BEEP_TIMER 1
// Идентификатор полосы просмотра #define ID_SCROLL 10
// Длина полосы просмотра #define SCROLL_SIZE 400
// Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); BOOL WAVELoad(LPWAVEIOCB lpwiocb);
// Глобальные переменные HWAVEOUT hWaveOut; WAVEIOCB waveiocbOut; HWAVEIN hWaveIn; WAVEIOCB waveiocbIn; int nMode = MODE_STOP; MMTIME mmtimeIn, mmtimeOut; BOOL fNeedSave = FALSE; BOOL fFileLoaded = FALSE; int nPosition; HWND hScroll; char const szClassName[] = "WaveClass"; char const szWindowTitle[] = "Wave Player/Recorder"; HINSTANCE hInst;
// ===================================== // Функция WinMain // ===================================== #pragma argsused
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения
if(hPrevInstance) return FALSE;
if(!InitApp(hInstance)) return FALSE;
hInst = hInstance;
hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // размеры и расположение окна CW_USEDEFAULT, 450, 120, 0, 0, hInstance, NULL);
if(!hwnd) return FALSE;
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
// ===================================== // Функция InitApp // Выполняет регистрацию класса окна // =====================================
BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации
memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APPICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName = (LPSTR)szClassName;
aWndClass = RegisterClass(&wc); return (aWndClass != 0); }
// ===================================== // Функция WndProc // =====================================
LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; int rc;
switch (msg) {
// ------------------------------------------------------------ // WM_CREATE // Создание главного окна приложения // ------------------------------------------------------------ case WM_CREATE: { nMode = MODE_STOP; fNeedSave = FALSE; fFileLoaded = FALSE; hWaveIn = NULL; hWaveOut = NULL;
// Создаем таймер SetTimer(hwnd, BEEP_TIMER, 100, NULL);
// Создаем полосу просмотра hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 10, 40, SCROLL_SIZE, 15, hwnd, (HMENU) ID_SCROLL, hInst, NULL);
// Устанавливаем текущую позицию nPosition = 0;
// Устанавливаем минимальное и максимальное // значения для полосы просмотра SetScrollRange(hScroll, SB_CTL, 1, SCROLL_SIZE, TRUE);
// Устанавливаем ползунок SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); return 0; }
// ------------------------------------------------------------ // WM_PAINT // Рисование в окне // ------------------------------------------------------------ case WM_PAINT: { // Получаем контекст отображения для // рисования во внутренней области окна hdc = BeginPaint(hwnd, &ps);
// Отображаем текущий режим работы if(nMode == MODE_STOP) TextOut(hdc, 10, 10, "Остановлено", 11); else if(nMode == MODE_RECORDING) TextOut(hdc, 10, 10, "Идет запись...", 14); else if(nMode == MODE_PLAYING) TextOut(hdc, 10, 10, "Идет проигрывание...", 20); else if(nMode == MODE_RECORDINGPAUSED) TextOut(hdc, 10, 10, "Запись остановлена", 18); else if(nMode == MODE_PLAYINGPAUSED) TextOut(hdc, 10, 10, "Проигрывание остановлено", 24); else TextOut(hdc, 10, 10, "Неправильный режим!", 19);
// Освобождаем контекст отображения EndPaint(hwnd, &ps); return 0; }
// ------------------------------------------------------------ // WM_COMMAND // Обработка сообщений от меню // ------------------------------------------------------------ case WM_COMMAND: { switch (wParam) { // ------------------------------------------------- // Строка "About" меню "Help" // ------------------------------------------------- case CM_HELPABOUT: { MessageBox(hwnd, "Wave, v.1.0\nWave Player/Recorder\n" "(C) Frolov A.V., 1994", "About Wave", MB_OK | MB_ICONINFORMATION); return 0; }
// ------------------------------------------------- // Строка "Open" меню "File" // ------------------------------------------------- case CM_FILEOPEN: { // Если файл уже был загружен, возвращаем WAVE в // исходное состояние if(fFileLoaded) { if(hWaveOut) { // Останавливаем устройство вывода rc=waveOutReset(hWaveOut); if(rc) wioOutError(rc);
// Закрываем устройство вывода rc=waveOutClose(hWaveOut); if(rc) wioOutError(rc); }
// Освобождаем буфера, предназначенные для вывода GlobalFreePtr(waveiocbOut.lpWaveHdr); GlobalFreePtr(waveiocbOut.lpData); GlobalFreePtr(waveiocbOut.lpFmt); }
// Загружаем новый файл if(!WAVELoad(&waveiocbOut)) return 0;
// Устанавливаем флаг загрузки файла fFileLoaded = TRUE; return 0; }
// ------------------------------------------------- // Строка "Play!" // Проигрывание загруженного wav-файла // ------------------------------------------------- case CM_CTLPLAY: { if(nMode == MODE_STOP) { // Если файл загружен и не проигрывается, // запускаем проигрывание файла if((fFileLoaded == TRUE) && (nMode != MODE_PLAYING)) { // Новый режим nMode = MODE_PLAYING;
// Перерисовываем окно для отображения строки, // соответствующей новому режиму InvalidateRect(hwnd, NULL, TRUE);
// Проигрываем файл rc=wioPlay(&waveiocbOut, hwnd); if(rc) wioOutError(rc); } } return 0; }
// ------------------------------------------------- // Строка "Record!" // Запись wav-файла // ------------------------------------------------- case CM_CTLRECORD: { // Запись возможна только из состояния останова if(nMode == MODE_STOP) { nMode = MODE_RECORDING; InvalidateRect(hwnd, NULL, TRUE);
// Требуется сохранить записанный файл fNeedSave = TRUE;
// Запись файла wioRecord(&waveiocbIn, hwnd); } return 0; }
// ------------------------------------------------- // Строка "Stop!" // Останов проигрывания или записи wav-файла // ------------------------------------------------- case CM_CTLSTOP: { if(nMode == MODE_RECORDING nMode == MODE_RECORDINGPAUSED) { if(hWaveIn) { // Останавливаем запись rc=waveInReset(hWaveIn); if(rc) wioInError(rc);
// Закрываем устройство записи rc=waveInClose(hWaveIn); if(rc) wioInError(rc); hWaveIn = 0; } }
else if(nMode == MODE_PLAYING nMode == MODE_PLAYINGPAUSED) { if(hWaveOut) { // Останавливаем проигрывание rc=waveOutReset(hWaveOut); if(rc) wioOutError(rc);
// Закрываем устройство вывода rc=waveOutClose(hWaveOut); if(rc) wioOutError(rc); hWaveOut = 0; } }
// Устанавливаем движок в начало полосы просмотра nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);
// Новый режим nMode = MODE_STOP; InvalidateRect(hwnd, NULL, TRUE); return 0; }
// ------------------------------------------------- // Строка "Pause!" // Временный останов проигрывания или // полный останов записи wav-файла // ------------------------------------------------- case CM_CTLPAUSE: { if(nMode == MODE_RECORDING) { // Останов записи rc=waveInStop(hWaveIn); if(rc) wioInError(rc); nMode = MODE_STOP; }
else if(nMode == MODE_PLAYING) { // Временный останов проигрывания rc=waveOutPause(hWaveOut); if(rc) wioOutError(rc); nMode = MODE_PLAYINGPAUSED; }
InvalidateRect(hwnd, NULL, TRUE); return 0; }
// ------------------------------------------------- // Строка "Resume!" // Продолжение проигрывания после останова // ------------------------------------------------- case CM_CTLRESUME: { if(nMode == MODE_PLAYINGPAUSED) { rc=waveOutRestart(hWaveOut); if(rc) wioOutError(rc); nMode = MODE_PLAYING; InvalidateRect(hwnd, NULL, TRUE); } return 0; }
// ------------------------------------------------- // Строка "Exit" меню "File" // Завершение работы приложения // ------------------------------------------------- case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; }
default: return 0; } }
// ------------------------------------------------------------ // MM_WOM_DONE // Завершение проигрывания блока // ------------------------------------------------------------ case MM_WOM_DONE: { nMode = MODE_STOP; nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); InvalidateRect(hwnd, NULL, TRUE);
// Удаляем блок из очереди проигрывания waveOutUnprepareHeader((HWAVEOUT)wParam, (LPWAVEHDR)lParam, sizeof(WAVEHDR));
// Останавливаем и закрываем устройство вывода waveOutReset((HWAVEOUT)wParam); waveOutClose((HWAVEOUT)wParam); hWaveOut = 0; return 0; }
// ------------------------------------------------------------ // MM_WIM_DATA // Завершение записи блока // ------------------------------------------------------------ case MM_WIM_DATA: { nMode = MODE_STOP; nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); InvalidateRect(hwnd, NULL, TRUE);
// Удаляем блок из очереди записи waveInUnprepareHeader((HWAVEIN)wParam, (LPWAVEHDR)lParam, sizeof(WAVEHDR));
// Сохраняем записанный блок в файле wioFileSave("RECORDED.WAV"); fNeedSave = FALSE;
// Освобождаем буфера, связанные с блоком GlobalFreePtr(waveiocbIn.lpWaveHdr); GlobalFreePtr(waveiocbIn.lpData);
// Останавливаем и закрываем устройство ввода waveInReset((HWAVEIN)wParam); waveInClose((HWAVEIN)wParam); hWaveIn = 0; return 0; }
// ------------------------------------------------------------ // WM_TIMER // Сообщение от таймера // ------------------------------------------------------------ case WM_TIMER: { if(nMode == MODE_RECORDING) { // Определяем текущую позицию внутри проигрываемого блока mmtimeIn.wType = TIME_SAMPLES; waveInGetPosition(hWaveIn, (LPMMTIME)&mmtimeIn, sizeof(MMTIME));
// Вычисляем новое положение движка полосы просмотра nPosition = ((DWORD)SCROLL_SIZE * mmtimeIn.u.sample) / MAXSAMPLES;
// Ограничиваем пределы изменения текущей // позиции значениями от 1 до SCROLL_SIZE if(nPosition > SCROLL_SIZE) nPosition = SCROLL_SIZE; if(nPosition < 1) nPosition = 1;
// Устанавливаем ползунок полосы просмотра // в соответствии с новым значением текущей позиции SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); }
else if(nMode == MODE_PLAYING) { // Определяем текущую позицию внутри записываемого блока mmtimeOut.wType = TIME_SAMPLES; waveOutGetPosition(hWaveOut, (LPMMTIME)&mmtimeOut, sizeof(MMTIME));
// Вычисляем новое положение движка полосы просмотра nPosition = ((DWORD)SCROLL_SIZE * mmtimeOut.u.sample) / (waveiocbOut.dwDataSize / waveiocbOut.wBytesPerSample);
// Ограничиваем пределы изменения текущей // позиции значениями от 1 до SCROLL_SIZE if(nPosition > SCROLL_SIZE) nPosition = SCROLL_SIZE; if(nPosition < 1) nPosition = 1;
// Устанавливаем ползунок полосы просмотра // в соответствии с новым значением текущей позиции SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); } return 0; }
// ------------------------------------------------------------ // WM_DESTROY // Уничтожение главного окна приложения // ------------------------------------------------------------ case WM_DESTROY: { // Удаляем таймер и полосу просмотра KillTimer(hwnd, BEEP_TIMER); DestroyWindow(hScroll);
// Если находимся в режиме записи, останавливаем // запись и закрываем устройство ввода if(nMode == MODE_RECORDING nMode == MODE_RECORDINGPAUSED) { if(hWaveIn) { rc=waveInReset(hWaveIn); if(rc) wioInError(rc); rc=waveInClose(hWaveIn); if(rc) wioInError(rc); } }
// Если запись началась, но еще не закончилась, после // остановки записи удаляем буфера if(fNeedSave) { GlobalFreePtr(waveiocbIn.lpWaveHdr); GlobalFreePtr(waveiocbIn.lpData); }
else if(fFileLoaded) { // Если находимся в режиме проигрывания, останавливаем // запись и закрываем устройство вывода if(nMode == MODE_PLAYING nMode == MODE_PLAYINGPAUSED) { if(hWaveOut) { rc=waveOutReset(hWaveOut); if(rc) wioOutError(rc);
rc=waveOutUnprepareHeader(hWaveOut, waveiocbOut.lpWaveHdr, sizeof(WAVEHDR)); if(rc) wioOutError(rc);
rc=waveOutClose(hWaveOut); if(rc) wioOutError(rc); } }
nMode = MODE_STOP;
// Освобождаем буфера GlobalFreePtr(waveiocbOut.lpWaveHdr); GlobalFreePtr(waveiocbOut.lpData); GlobalFreePtr(waveiocbOut.lpFmt); } PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); }
После стандартной инициализации запускается цикл обработки сообщений. При создании главного окна приложения во время обработки сообщения WM_CREATE устанавливаются начальные значения переменных, создается таймер с периодом работы примерно 100 мс, создается и инициализируется полоса просмотра, которая используется как индикатор текущей позиции при записи и воспроизведении.
Обработчик сообщения WM_PAINT отображает в верхней части главного окна текстовую строку, соответствующую текущему режиму работы приложения (переменная nMode).
При выборе из меню "File" строки "Open" приложение проверяет, не был ли раньше загружен файл. Если файл был загружен и устройство вывода открыто, это устройство останавливается функцией waveOutReset и закрывается функцией waveOutClose. После этого освобождаются буфера, которые использовались для вывода.
Затем вызывается функция WAVELoad, определенная в нашем приложении в файле waveio.cpp (листинг 2.18).
В переменной fFileLoaded устанавливается признак того, что файл загружен и готов для воспроизведения.
Для проигрывания файла нужно выбрать из главного меню приложения строку "Play!". Соответствующий обработчик проверит текущий режим работы приложения. Если wav-файл загружен, и приложение уже не находится в режиме воспроизведения, вызывается функция wioPlay, выполняющая проигрывание файла. Эта функция также определена в нашем приложении в файле waveio.cpp.
С помощью строки "Record!" главного меню приложения можно включить режим записи (если приложение находится в состоянии останова). При этом устанавливается флаг fNeedSave, который используется как признак необходимости сохранения записанных звуковых данных в wav-файле. Запись выполняется функцией wioRecord, определенной в файле waveio.cpp.
В любой момент времени можно остановить запись или воспроизведение, если из главного меню приложения выбрать строку "Stop!". При этом, если приложение находилось в режиме записи, выполняется сброс и закрытие устройства записи, а если в режиме воспроизведения - сброс и закрытие устройства воспроизведения. В любом случае движок полосы просмотра устанавливается в начальное, самое левое, положение, так как после сброса устройства начальная позиция равна нулю.
Строка "Pause!" главного меню приложения предназначена для останова записи и временного останова воспроизведения. В режиме записи устройство останавливается функцией waveInStop. В режиме воспроизведения вызывается функция waveOutPause, выполняющая временный останов.
Для продолжения воспроизведения после временного останова из главного меню приложения следует выбрать строку "Resume!". В этом случае будет вызвана функция waveOutRestart, которая возобновит работу устройства вывода.
При завершении процесса проигрывания блока главное окно приложения получит сообщение MM_WOM_DONE. Обработчик этого сообщения установит полосу просмотра в исходное состояние, удалит блок из очереди проигрывания, вызвав функцию waveOutUnprepareHeader, после чего остановит и закроет устройство вывода.
В режиме записи при достижении конца блока памяти или при останове записи главное окно приложения получит сообщение MM_WIM_DATA. Обработчик этого сообщения также установит полосу просмотра в исходное состояние и удалит блок из очереди записи, вызвав функцию waveInUnprepareHeader. Затем содержимое блока будет сохранено в wav-файле с именем recorded.wav, который будет создан или перезаписан в текущем каталоге. Для записи файла вызывается функция wioFileSave, определенная в файле waveio.cpp.
Далее освобождаются буфера, использовавшиеся при записи, устройство записи останавливается и закрывается.
Сообщения, поступающие от таймера, используются для обновления положения движка полосы просмотра в соответствии с текущей позицией устройства записи (в режиме записи) или устройства воспроизведения (в режиме воспроизведения).
В режиме записи с помощью функции waveInGetPosition определяется текущая позиция в формате TIME_SAMPLES:
mmtimeIn.wType = TIME_SAMPLES; waveInGetPosition(hWaveIn, (LPMMTIME)&mmtimeIn, sizeof(MMTIME));
Новое положение движка полосы просмотра в режиме записи определяется на основании текущей позиции и максимального размера буфера записи MAXSAMPLES, заданного как константа:
nPosition = ((DWORD)SCROLL_SIZE * mmtimeIn.u.sample) / MAXSAMPLES;
В режиме воспроизведения для вычисления положения движка используется размер загруженного wav-файла:
nPosition = ((DWORD)SCROLL_SIZE * mmtimeOut.u.sample) / (waveiocbOut.dwDataSize / waveiocbOut.wBytesPerSample);
При завершении работы приложения обработчик сообщения WM_DESTROY удаляет таймер и полосу просмотра. Если работа приложения завершается во время записи, устройство записи останавливается и закрывается, а соответствующие глобальные буфера освобождаются. Аналогичные действия выполняются и в режиме воспроизведения.
Файл wave.hpp содержит определения символических имен констант (листинг 2.17).
Листинг 2.15. Файл wave\wave.hpp
#define CM_HELPABOUT 301 #define CM_FILEEXIT 302 #define CM_FILEOPEN 303 #define CM_FILESAVEAS 304 #define CM_FILENEW 305
#define CM_CTLPLAY 401 #define CM_CTLRECORD 402 #define CM_CTLRESUME 403 #define CM_CTLPAUSE 404 #define CM_CTLSTOP 405
Исходные тексты всех функций, используемых для работы с wav-файлами на низком уровне, определены в файле waveio.cpp (листинг 2.18).
Листинг 2.18. Файл wave\waveio.cpp
#define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop #include "waveio.hpp"
BOOL WAVEPlay(HWND);
extern WAVEIOCB waveiocbOut; extern WAVEIOCB waveiocbIn; extern HWAVEOUT hWaveOut; extern HWAVEIN hWaveIn; extern int nMode; extern int nPosition;
//----------------------------------------------------- // WAVELoad // Загрузка wav-файла для проигрывания //----------------------------------------------------- BOOL WAVELoad(LPWAVEIOCB lpwiocb) { BYTE szFileName[256]; OPENFILENAME ofn; int rc; BYTE szBuf[256];
// Проверяем наличие драйвера, способного выводить // звуковые файлы rc=waveOutGetNumDevs(); if(!rc) { MessageBox(NULL, (LPSTR)"Нет устройств для вывода звуковых файлов", "Wave Error", MB_OK | MB_ICONHAND); return FALSE; }
// Выбираем wav-файл if(!wioSelectFile(szFileName)) return FALSE;
// Открываем и загружаем в память выбранный файл rc = wioFileOpen(lpwiocb, (LPSTR)szFileName);
if(rc == WIOERR_NOERROR) return TRUE; else if(rc == WIOERR_FILEERROR) lstrcpy(szBuf, "Ошибка при открытии файла"); else if(rc == WIOERR_BADFORMAT) lstrcpy(szBuf, "Неправильный или неподдерживаемый формат файла"); else if(rc == WIOERR_NOMEM) lstrcpy(szBuf, "Мало памяти"); else if(rc == WIOERR_READERROR) lstrcpy(szBuf, "Ошибка при чтении"); else lstrcpy(szBuf, "Неизвестная ошибка");
MessageBox(NULL, (LPSTR)szBuf, "Wave Error", MB_OK | MB_ICONHAND); return FALSE; }
//----------------------------------------------------- // wioSelectFile // Выбор wav-файла //----------------------------------------------------- BOOL wioSelectFile(LPSTR lpszFileName) { OPENFILENAME ofn;
char szFile[256]; char szFileTitle[256]; char szFilter[256] = "Wave Files\0*.wav\0Any Files\0*.*\0"; szFile[0] = '\0'; memset(&ofn, 0, sizeof(OPENFILENAME));
// Инициализируем нужные нам поля ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = NULL; ofn.lpstrFilter = szFilter; ofn.nFilterIndex = 1; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = sizeof(szFileTitle); ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
// Выбираем входной файл if (GetOpenFileName(&ofn)) { // Копируем путь к выбранному файлу lstrcpy(lpszFileName, (LPSTR)szFile); return TRUE; } else return FALSE; }
//--------------------------------------------------------- // wioFileOpen // Открытие и загрузка wav-файла //--------------------------------------------------------- int wioFileOpen(LPWAVEIOCB lpwiocb, LPSTR lpszFileName) { HMMIO hmmio; MMCKINFO ckRIFF, ckFMT; DWORD dwFmtSize;
// Открываем wav-файл hmmio = mmioOpen((LPSTR)lpszFileName, NULL, MMIO_READ | MMIO_ALLOCBUF); if(!hmmio) return WIOERR_FILEERROR;
// Ищем фрагмент "WAVE" memset(&ckRIFF, 0, sizeof(MMCKINFO)); ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if(mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
// Ищем фрагмент "fmt " memset(&ckFMT, 0, sizeof(MMCKINFO)); ckFMT.ckid = mmioFOURCC('f', 'm', 't', ' ');
if(mmioDescend(hmmio, &ckFMT, &ckRIFF, MMIO_FINDCHUNK)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
dwFmtSize = sizeof(PCMWAVEFORMAT); //ckFMT.cksize;
// Получаем память для загрузки формата звукового файла lpwiocb->lpFmt = (PCMWAVEFORMAT FAR *)GlobalAllocPtr(GPTR, dwFmtSize);
if(!lpwiocb->lpFmt) { mmioClose(hmmio,0); return WIOERR_NOMEM; }
// Загружаем формат звукового файла if(mmioRead(hmmio, (HPSTR)lpwiocb->lpFmt, dwFmtSize) != (LONG)dwFmtSize) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_READERROR; }
// Проверяем формат звукового файла if(lpwiocb->lpFmt->wf.wFormatTag != WAVE_FORMAT_PCM) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
// Проверяем способность драйвера работать с указанным форматом if(waveOutOpen(NULL, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, NULL, 0L, WAVE_FORMAT_QUERY | WAVE_ALLOWSYNC)) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
// Вычисляем количество байт, необходимых для // хранения одной выборки звукового сигнала lpwiocb->wBitsPerSample = lpwiocb->lpFmt->wBitsPerSample; lpwiocb->wBytesPerSample = (waveiocbOut.wBitsPerSample/8) * waveiocbOut.lpFmt->wf.nChannels;
// Ищем фрагмент "data" mmioAscend(hmmio, &ckFMT, 0); ckFMT.ckid = mmioFOURCC('d', 'a', 't', 'a');
if(mmioDescend(hmmio, &ckFMT, &ckRIFF, MMIO_FINDCHUNK)) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
// Определяем размер фрагмента сегмента звуковых // данных и его смещение в wav-файле lpwiocb->dwDataSize = ckFMT.cksize; lpwiocb->dwDataOffset = ckFMT.dwDataOffset;
// Проверяем, что файл не пуст if(lpwiocb->dwDataSize == 0L) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
// Получаем память для заголовка блока lpwiocb->lpWaveHdr = (LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR)); if(!lpwiocb->lpWaveHdr) return WIOERR_NOMEM;
// Получаем память для звуковых данных lpwiocb->lpData = (HPSTR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, lpwiocb->dwDataSize); if(!lpwiocb->lpData) return WIOERR_NOMEM;
// Позиционирование на начало звуковых данных mmioSeek(hmmio, SEEK_SET, lpwiocb->dwDataOffset);
// Читаем звуковые данные mmioRead(hmmio, lpwiocb->lpData, lpwiocb->dwDataSize);
// Закрываем wav-файл mmioClose(hmmio,0);
return WIOERR_NOERROR; }
//--------------------------------------------------------- // wioPlay // Проигрывание загруженного блока звуковых данных //--------------------------------------------------------- int wioPlay(LPWAVEIOCB lpwiocb, HWND hwnd) { WORD rc;
// Открываем устройство вывода rc = waveOutOpen(&hWaveOut, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC); if(rc) return rc;
// Заполняем заголовок блока данных lpwiocb->lpWaveHdr->lpData = (LPSTR)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) { GlobalFreePtr(lpwiocb->lpWaveHdr); return rc; }
// Запускаем проигрывание блока rc = waveOutWrite(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); if(rc) { waveOutUnprepareHeader(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); GlobalFreePtr(lpwiocb->lpWaveHdr); return rc; }
return 0; }
//--------------------------------------------------------- // wioRecord // Запись звуковых данных //--------------------------------------------------------- int wioRecord(LPWAVEIOCB lpwiocb, HWND hwnd) { int rc;
// Проверяем наличие драйвера, способного // выполнять запись звука rc = waveInGetNumDevs(); if(!rc) { MessageBox(NULL, (LPSTR)"Нет устройств для записи звуковых файлов", "Wave Error", MB_OK | MB_ICONHAND); return WIOERR_NODEVICE; }
// Максимальный размер блока в байтах lpwiocb->dwDataSize = MAXSAMPLES;
// Получаем память для заголовка блока lpwiocb->lpWaveHdr = (LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR)); if(!lpwiocb->lpWaveHdr) return WIOERR_NOMEM;
// Получаем память для блока звуковых данных lpwiocb->lpData = (HPSTR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, lpwiocb->dwDataSize); if(!lpwiocb->lpData) return WIOERR_NOMEM;
// Получаем память для блока формата lpwiocb->lpFmt = (PCMWAVEFORMAT FAR *)GlobalAllocPtr(GPTR, sizeof(WAVEFORMAT));
if(!lpwiocb->lpFmt) { return WIOERR_NOMEM; }
// Заполняем блок формата. Наше приложение способно // записывать монофонические файлы в формате WAVE_FORMAT_PCM // с частотой дискретизации 22,05 Кгц lpwiocb->lpFmt->wf.wFormatTag = WAVE_FORMAT_PCM; lpwiocb->lpFmt->wf.nChannels = 1; lpwiocb->lpFmt->wf.nSamplesPerSec = 22050; lpwiocb->lpFmt->wf.nAvgBytesPerSec = 22050; lpwiocb->lpFmt->wf.nBlockAlign = 1;
// Открываем устройство записи rc=waveInOpen(&hWaveIn, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC); if(rc) { wioInError(rc); return WIOERR_BADFORMAT; }
// Заполняем заголовок блока lpwiocb->lpWaveHdr->lpData = (LPSTR)lpwiocb->lpData; lpwiocb->lpWaveHdr->dwBufferLength = lpwiocb->dwDataSize; lpwiocb->lpWaveHdr->dwFlags = 0L; lpwiocb->lpWaveHdr->dwLoops = 0L; lpwiocb->lpWaveHdr->dwUser = 0L;
// Подготавливаем блок для записи rc = waveInPrepareHeader(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); if(rc) { wioInError(rc); GlobalFreePtr(lpwiocb->lpWaveHdr); return WIOERR_BADFORMAT; }
// Передаем блок устройству записи rc = waveInAddBuffer(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); if(rc) { wioInError(rc); waveInUnprepareHeader(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); GlobalFreePtr(lpwiocb->lpWaveHdr); GlobalFreePtr(lpwiocb->lpFmt); GlobalFreePtr(lpwiocb->lpData); return WIOERR_ERROR; }
// Запускаем запись rc = waveInStart(hWaveIn); if(rc) wioInError(rc);
return TRUE; }
//--------------------------------------------------------- // wioFileSave // Сохранение записанного звука в wav-файле //--------------------------------------------------------- BOOL wioFileSave(LPSTR szFileName) { DWORD dwDataSize; char szdata[] = "data"; HMMIO hFile; MMCKINFO ck; WORD wBitsPerSample = 8; char szfmt[] = "fmt "; DWORD dwFmtSize = sizeof(PCMWAVEFORMAT);
// Создаем новый файл или перезаписываем существующий hFile = mmioOpen(szFileName, NULL, MMIO_CREATE | MMIO_READWRITE); if(hFile != NULL) { // Создаем заголовок wav-файла ck.ckid = MMIO_CREATERIFF; ck.cksize = waveiocbIn.lpWaveHdr->dwBytesRecorded + sizeof(PCMWAVEFORMAT) + 20; ck.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmioCreateChunk(hFile, (LPMMCKINFO)&ck, MMIO_CREATERIFF);
// Записываем фрагмент "fmt " mmioWrite(hFile, (HPSTR)szfmt, 4); mmioWrite(hFile, (HPSTR)&dwFmtSize, sizeof(DWORD));
mmioWrite(hFile, (HPSTR)waveiocbIn.lpFmt, sizeof(WAVEFORMAT)); mmioWrite(hFile, (HPSTR)&wBitsPerSample, sizeof(WORD));
mmioWrite(hFile, (HPSTR)szdata, 4); dwDataSize = waveiocbIn.lpWaveHdr->dwBytesRecorded; mmioWrite(hFile, (HPSTR)&dwDataSize, sizeof(DWORD));
// Записываем данные mmioWrite(hFile, (HPSTR)waveiocbIn.lpData, waveiocbIn.lpWaveHdr->dwBytesRecorded);
// Закрываем файл mmioClose(hFile, 0); return TRUE; } return FALSE; }
//--------------------------------------------------------- // wioOutError // Вывод сообщения об ошибке в процессе проигрывания //--------------------------------------------------------- void wioOutError(int rc) { BYTE szBuf[MAXERRORLENGTH];
if(waveOutGetErrorText(rc, (LPSTR)szBuf, MAXERRORLENGTH) == MMSYSERR_BADERRNUM) { lstrcpy(szBuf, "Unknown Error"); } MessageBox(NULL, (LPSTR)szBuf, "Wave Error", MB_OK | MB_ICONHAND); }
//--------------------------------------------------------- // wioInError // Вывод сообщения об ошибке в процессе записи //--------------------------------------------------------- void wioInError(int rc) { BYTE szBuf[MAXERRORLENGTH];
if(waveInGetErrorText(rc, (LPSTR)szBuf, MAXERRORLENGTH) == MMSYSERR_BADERRNUM) { lstrcpy(szBuf, "Unknown Error"); } MessageBox(NULL, (LPSTR)szBuf, "Wave Error", MB_OK | MB_ICONHAND); }
Функция WAVLoad предназначена для выбора и загрузки проигрываемого wav-файла.
В самом начале своей работы она определяет наличие драйвера, способного воспроизводить звуковую информацию, для чего вызывает функцию waveOutGetNumDevs. Если нужный драйвер есть, вызывается функция wioSelectFile, предоставляющая пользователю возможность выбрать файл при помощи стандартной диалоговой панели "Open". Выбранный файл открывается и загружается в память функцией wioFileOpen.
Функция wioSelectFile не имеет никаких особенностей.
Для выбора файла в ней используется функция GetOpenFileName из библиотеки commdlg.dll. Путь к выбранному файлу копируется в буфер, адрес которого передается функции wioSelectFile в качестве единственного параметра.
Функция wioFileOpen выполняет всю работу по загрузке и анализу wav-файла.
Для открытия файла используется функция mmioOpen. При этом файл открывается на чтение с использованием буферизации, для чего в последнем параметре функции указаны флаги MMIO_READ и MMIO_ALLOCBUF.
Далее в wav-файле ищутся фрагменты "WAVE" и "fmt ", для чего используется функция mmioDescend:
memset(&ckRIFF, 0, sizeof(MMCKINFO)); ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if(mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
memset(&ckFMT, 0, sizeof(MMCKINFO)); ckFMT.ckid = mmioFOURCC('f', 'm', 't', ' '); if(mmioDescend(hmmio, &ckFMT, &ckRIFF, MMIO_FINDCHUNK)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
После этого приложение заказывает память для структуры PCMWAVEFORMAT, которая находится во фрагменте "fmt " и содержит сведения о формате wav-файла. Загрузка данных в структуру выполняется функцией mmioRead:
if(mmioRead(hmmio, (HPSTR)lpwiocb->lpFmt, dwFmtSize) != (LONG)dwFmtSize) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_READERROR; }
Далее проверяется формат звукового файла: поле wFormatTag структуры WAVEFORMAT должно содержать значение WAVE_FORMAT_PCM.
Для того чтобы определить, может ли установленный в системе драйвер звукового адаптера работать с форматом загруженного файла, вызывается функция waveOutOpen с флагом WAVE_FORMAT_QUERY:
if(waveOutOpen(NULL, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, NULL, 0L, WAVE_FORMAT_QUERY | WAVE_ALLOWSYNC)) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }
Так как пользователь может загрузить wav-файл любого формата, как монофонический, так и стереофонический, с использованием 8 или 16 битов для представления одной выборки сигнала, приложение должно динамически определять количество байт памяти, необходимых для хранения одной выборки сигнала.
После анализа формата наше приложение ищет фрагмент "data", в котором находятся звуковые данные. Для поиска используется функция mmioDescend. После определения размера фрагмента данных приложение заказывает память для заголовка блока и самого блока, пользуясь макрокомандой GlobalAllocPtr.
Перед чтением звуковых данных в заказанный буфер выполняется позиционирование на начало звуковых данных, для чего используется функция mmioSeek. Чтение данных выполняется функцией mmioRead. Сразу после чтения файл закрывается функцией mmioClose, так как он нам больше не нужен.
Функция wioPlay проигрывает загруженный блок. Адрес блока и его заголовка передаются функции через первый параметр. Через второй параметр функция wioPlay получает идентификатор окна, в которое после завершения проигрывания поступит сообщение MM_WOM_DONE.
Прежде всего функция wioPlay открывает устройство вывода, вызывая для этого функцию waveOutOpen:
rc = waveOutOpen(&hWaveOut, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC);
В качестве номера устройства мы указали константу WAVE_MAPPER, поэтому будет выбрано любое устройство, подходящее для проигрывания данных указанного формата.
После этого функция wioPlay заполняет заголовок блока данных, записывая в него адрес и размер буфера, содержащего звуковые данные. Во все неиспользуемые поля записываются нулевые значения.
Затем заголовок блока подготавливается для вывода при помощи функции waveOutPrepareHeader:
rc = waveOutPrepareHeader(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR));
Для запуска процесса проигрывания вызывается функция waveOutWrite:
rc = waveOutWrite(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR));
Функция wioRecord запускает процесс записи.
В начале своей работы она проверяет, установлен ли в системе драйвер, способный выполнять запись звука, вызывая для этого функцию waveInGetNumDevs. Далее заказывается память для заголовка блока данных и самого блока данных. Так как приложение WAVE может записывать только монофонические wav-файлы с 8-битовым представлением звуковых данных, размер буфера для записи численно равен константе MAXSAMPLES (максимальный размер буфера в выборках сигнала).
Затем функция wioRecord заказывает память для блока формата и заполняет этот блок. Для простоты мы использовали только один формат, а именно монофонический 8-битовый формат с частотой дискретизации 22,05 Кгц.
После заполнения блока формата функция открывает устройство записи, заполняет заголовок блока. Заполненный заголовок подготавливается для записи, для чего вызывается функция waveInPrepareHeader.
Далее блок передается устройству записи функцией waveInAddBuffer:
rc = waveInAddBuffer(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR));
Для запуска записи вызывается функция waveInStart:
rc = waveInStart(hWaveIn);
Функция wioFileSave сохраняет содержимое буфера записи в wav-файле.
Для создания нового или перезаписи существующего файла он открывается функцией mmioOpen с флагами MMIO_CREATE и MMIO_READWRITE.
Далее создается заголовок файла при помощи функции mmioCreateChunk:
ck.cksize = waveiocbIn.lpWaveHdr->dwBytesRecorded + sizeof(PCMWAVEFORMAT) + 20; ck.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmioCreateChunk(hFile, (LPMMCKINFO)&ck, MMIO_CREATERIFF);
При этом в заголовке указывается длина с учетом объема записанных звуковых данных.
После этого с помощью функции mmioWrite в файл записывается фрагмент "fmt " и содержимое буфера звуковых данных. Файл закрывается функцией mmioClose.
Для обработки ошибок в процессе воспроизведения используется функция wioOutError, которая с помощью функции waveOutGetErrorText преобразует код ошибки, передаваемый через параметр, в текстовое сообщение и выводит его на экран. Аналогичные действия выполняются при записи функцией wioInError, преобразующей код ошибки в текстовую строку при помощи функции waveInGetErrorText.
Файл waveio.hpp (листинг 2.19) содержит определение структуры WAVEIOCB и указателей на нее, а также определения констант и прототипы функций. Структура WAVEIOCB используется функциями записи и проигрывания блоков звуковых данных.
Листинг 2.19. Файл wave\waveio.hpp
#include <windows.h> #include <mmsystem.h>
typedef struct tagWAVEIOCB { DWORD dwDataSize; DWORD dwDataOffset; PCMWAVEFORMAT FAR *lpFmt; LPWAVEHDR lpWaveHdr; HPSTR lpData; WORD wBitsPerSample; WORD wBytesPerSample;
} WAVEIOCB, *PWAVEIOCB, FAR *LPWAVEIOCB;
#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 MAXSAMPLES 1024000L
#define MODE_STOP 0 #define MODE_PLAYING 1 #define MODE_RECORDING 2 #define MODE_PLAYINGPAUSED 3 #define MODE_RECORDINGPAUSED 4
BOOL wioSelectFile(LPSTR); int wioFileOpen(LPWAVEIOCB, LPSTR); int wioPlay(LPWAVEIOCB, HWND); int wioRecord(LPWAVEIOCB, HWND); BOOL wioFileSave(LPSTR szFileName); void wioOutError(int rc); void wioInError(int rc);
Файл определения ресурсов приложения WAVE представлен в листинге 2.20.
Листинг 2.20. Файл wave\wave.rc
#include "wave.hpp" APPICON ICON "wave.ico" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open...", CM_FILEOPEN MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END
MENUITEM "&Play!", CM_CTLPLAY MENUITEM "&Stop!", CM_CTLSTOP MENUITEM "Resu&me!", CM_CTLRESUME MENUITEM "P&ause!", CM_CTLPAUSE MENUITEM "&Record!", CM_CTLRECORD
POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END
Файл определения модуля приложения WAVE вы сможете найти в листинге 2.21.
Листинг 2.21. Файл wave\wave.def
NAME WAVE DESCRIPTION 'Приложение WAVE, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 10240 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple
Программы, работающие со звуком и видео
Для работы со звуковыми адаптерами существует большое количество программ, рассчитанных на работу в среде MS-DOS, Windows и других операционных систем. Прежде чем приступать к созданию собственных программ, полезно познакомиться с тем, что уже сделано в этом направлении. Отметим, что наибольшей популярностью пользуются мультимедиа-приложения, разработанные для операционной системы Windows, хотя есть интересные программы MS-DOS, работающие со звуком и анимацией. Следующие версии Windows, такие, как Chicago, будут иметь очень мощные встроенные средства мультимедиа, поэтому, на наш взгляд, в первую очередь имеет смысл научиться создавать мультимедиа-приложения для Windows.
В составе операционной системы Windows версии 3.1 поставляются драйверы, dll-библиотеки и некоторые приложения, предназначенные для работы со звуком. Дополнительно можно приобрести такое изделие, как Microsoft Video for Windows версии 1.1, которое можно считать средством для создания систем мультимедиа второго поколения.
Нельзя сказать, что создавая программы MS-DOS, программист неизбежно столкнется с необходимостью программирования звукового адаптера на уровне портов, каналов прямого доступа и обработки прерываний. Для звуковых плат поставляются специальные средства разработки, содержащие драйверы в виде резидентных программ, значительно облегчающие программирование в среде MS-DOS.
В качестве примера можно привести такой программный продукт, как Sound Galaxy Software Developer Kit. В него входит набор драйверов для MS-DOS, библиотеки функций языка программирования С для работы с этими драйверами в среде MS-DOS, а также подробное описание особенностей аппаратуры. К сожалению, в комплекте поставки нет ни одного примера программы, работающей на уровне портов ввода/вывода звукового адаптера, что необходимо, если ваша программа работает не в среде MS-DOS или Windows.
Приложения Windows находятся в лучшем положении, так как они могут пользоваться высокоуровневым или низкоуровневым интерфейсом, который обеспечивается мультимедиа-расширением Windows. Само это расширение работает со звуковым адаптером через драйвер, который поставляется вместе с адаптером. Поэтому приложение Windows в значительной мере изолировано от звукового адаптера и его аппаратных особенностей, работая на уровне унифицированного интерфейса.
К описанию этого интерфейса мы вернемся позже, а сейчас расскажем о некоторых приложениях, созданных для Windows с использованием технологии мультимедиа.
Проигрывание MIDI-файлов
4.1.
4.2.
4.3.
В этой главе мы кратко расскажем вам о том, как приложения Windows могут проигрывать музыкальные файлы формата MIDI при помощи средств интерфейса MCI.
Стандарт MIDI (Musical Instrument Digital Interface - цифровой интерфейс музыкальных инструментов) был разработан давно, в 1982 году. В рамках этого стандарта определены электрические и логические спецификации (уровни сигналов, временные диаграммы и коммуникационный протокол) для подключения таких музыкальных инструментов, как синтезаторы и музыкальные клавиатуры друг к другу и к компьютеру.
Для электрического подключения используется последовательный интерфейс типа "токовая петля" со скоростью передачи данных 31250 бит в секунду. Данные передаются байтами с одним стартовым и одним стоповым битом. Подробная спецификация разъема приведена в документации на звуковой адаптер (если адаптер оснащен портом для подключения внешних устройств MIDI). Отметим, что устройства MIDI могут подключаться цепочкой, для чего на корпусе устройства обычно предусмотрены три разъема. Входной разъем обозначается MIDI In , выходной - MIDI Out . Разъем MIDI Thru предназначен для организации цепочки, он является выходным и дублирует сигналы с разъема MIDI In. Некоторые устройства, например, простые музыкальные клавиатуры, могут иметь только один входной или выходной разъем. В звуковом адаптере Sound Galaxy NX Pro предусмотрено подключение входного и выходного сигнала, причем оба они подключаются к одному разъему.
Как правило, все звуковые адаптеры оборудованы музыкальным синтезатором. Синтезаторы могут иметь базовый или расширенный уровень. Синтезатор базового уровня содержит в себе голоса трех мелодичных и трех ударных инструментов. Уровень полифонии такого синтезатора равен 6 для мелодичных и 3 для ударных инструментов. Это означает, что синтезатор базового уровня может играть одновременно 6 нот на мелодичных инструментах и 3 ноты на ударных. Синтезаторы расширенного уровня содержат в себе 9 мелодичных и 8 ударных инструментов при уровне полифонии, равном 16.
Хорошие синтезаторы содержат в себе цифровые записи (образцы) голосов музыкальных инструментов, которые модифицируются для получения нужной высоты тона и нужного уровня выходного сигнала. Более простые модели синтезируют звучание программным способом, что, однако, отдаляет качество звучания от идеала.
Для управления синтезатором, подключенным через порт MIDI, используется механизм сообщений MIDI. Сообщения могут иметь длину от одного до нескольких байт. Они передают такую информацию, как номер нажатой или отпущенной клавиши музыкальной клавиатуры или номер одного из 16 логических каналов, через которые осуществляется управление синтезатором.
Файлы в стандарте MIDI имеют расширение имени .mid и содержат заголовок и сообщения для музыкального синтезатора. Используется также стандарт RIFF. Файлы, содержащие сообщения MIDI и соответствующие стандарту RIFF, обычно создаются с расширением имени rmi. Если приложение Windows будет проигрывать такие файлы при помощи интерфейса MCI, ему не нужно знать внутренний формат файлов.
Но есть одна тонкость, связанная с использованием логических каналов и кодов инструментов.
В спецификации MIDI определены 16 логических каналов, предназначенных для адресации 16 логических синтезаторов. Каналы с номерами 13...16 используются синтезаторами базового уровня, каналы с номерами 1...10 - синтезаторами расширенного уровня. Каналы 11 и 12 не используются. Файлы MIDI содержат сообщения, предназначенные для разных каналов и для разных инструментов. Проблема заключается в том, что не все синтезаторы имеют одинаковое распределение каналов и инструментов. Самый простой способ уйти от трудностей, связанных с распределением каналов - приобретать mid-файлы, созданные специально для работы в среде Windows (авторизованные для Windows). В магазинах есть компакт-диски с многими сотнями таких файлов, причем в большинстве случаев вы можете свободно продавать эти файлы в составе своего программного обеспечения.
В нашей книге мы не будем рассматривать вопросы создания mid-файлов.Во-первых, есть много готовых, которые можно купить в магазине. Во-вторых, для создания mid-файлов требуется музыкальная клавиатура, соответствующее программное обеспечение и, разумеется, некоторые музыкальные способности. Кроме того, нам надо еще оставить место в книге для рассказа о новейшем направлении в мультимедиа - системе Microsoft Video for Windows.
А сейчас рассмотрим особенности использования интерфейса MCI для проигрывания файлов MIDI.
Работа с окном MCI
В этом разделе мы научимся создавать приложения для проигрывания файлов мультимедиа и дорожек компакт-дисков (а также для записи звуковых файлов) с использованием окна MCI.
Однако перед тем как приступить к описанию способов работы с этим окном, рассмотрим пример простейшего приложения, использующего окно MCI для проигрывания файлов мультимедиа.
Record
Эта команда запускает запись звукового фрагмента.
В качестве необязательного параметра parameter можно указывать одну из следующих строк:
insert
Новые данные будут добавлены к ранее записанным
from position
Определение начальной позиции для записи. Если начальная позиция не задана, данные будут вставлены начиная с текущей позиции. Если используются параметры from или to, необходимо задать формат времени командой set time format
to position
Определение конечной позиции при записи. Если конечная позиция не задана, запись будет продолжаться до тех пор, пока не будет выдана команда stop или pause
overwrite
Новые данные должны заместить записанные ранее
Resume
Продолжение воспроизведения или записи после временного останова по команде pause.
resume device_id [notify] [wait]
Самые простые способы воспроизведения звука
В самом простейшем случае приложение должно уметь выдавать звуковые сигналы или проигрывать небольшие звуковые сообщения при появлении каких-либо непредвиденных событий, когда нужно привлечь внимание пользователя. Для этого можно воспользоваться функциями MessageBeep и sndPlaySound .
Save
Сохранение записанного звукового фрагмента в файле
save device_id [filename] [notify] [wait]
Параметр filename задает путь к файлу, в который должен быть записан звуковой фрагмент
Сброс устройства
Функции waveInReset и waveOutReset выполняют, соответственно, останов устройства ввода или вывода и сброс текущей позиции для устройства в 0.
Функция waveInReset
UINT waveInReset( HWAVEIN hWaveIn); // идентификатор устройства ввода
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Функция waveOutReset
UINT waveOutReset ( HWAVEOUT hWaveOut); // идентификатор устройства вывода
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Seek
Позиционирование с последующим остановом. Перед использованием этой команды необходимо задать формат времени командой set time format.
seek device_id parameter [notify] [wait]
В качестве необязательного параметра parameter можно указывать одну из следующих строк:
to position
Позиционирование в указанное место фрагмента
to start
Позиционирование в начало
to end
Позиционирование в конец
Set
Команда set в зависимости от параметров позволяет выполнять установку различных режимов работы.
set device_id parameter [notify] [wait]
В качестве параметра parameter можно указывать одну из следующих строк (за один раз можно указывать сразу несколько параметров):
alignment int
Установка выравнивания блока данных относительно начала данных звукового фрагмента, переданного драйверу звукового адаптера
any input
Использование любого устройства ввода, поддерживающего текущий формат при записи. Этот режим включен по умолчанию
any output
Использование любого устройства вывода, поддерживающего текущий формат при воспроизведении. Этот режим включен по умолчанию
audio all off
Отключение звукового выхода
audio all on
Включение звукового выхода
audio left off
Отключение левого канала
audio left on
Включение левого канала
audio right off
Отключение правого канала
audio right on
Включение правого канала
bitspersample bit_count
Установка количества бит для представления выборки сигнала. Параметр bit_count задает количество бит (8 или 16)
bytespersec byte_rate
Установка частоты дискретизации при записи или воспроизведении. Параметр byte_rate задает частоту (байты в секунду)
channels channel_count
Установка количества каналов для записи или воспроизведения (1 - монофонический режим, 2 - стереофонический режим)
format tag tag
Установка типа формата
format tag pcm
Установка формата PCM (импульсно-кодовая модуляция)
input int
Выбор канала для ввода
output int
Выбор канала для вывода
samplepersec int
Установка скорости записи или воспроизведения
time format bytes
В качестве единицы измерения при позиционировании используются байты блока звуковых данных
time format milliseconds
В качестве единицы измерения при позиционировании используются миллисекунды. Строку milliseconds можно также указывать как ms
time format samples
В качестве единицы измерения при позиционировании используются выборки сигнала
Системы распознавания речи
Несмотря на сложность задачи распознавания речи, в этом направлении есть определенные достижения. Все существующие системы можно разделить на две группы. К первой группе относятся системы распознавания, которые обучаются пользователем. Пользователь сам формирует словарь, причем система настраивается на произношение конкретного человека. Вторая группа систем содержит фиксированный словарь и не нуждается в предварительном обучении или настройке на конкретного пользователя.
Примером системы, относящейся к первой группе, может послужить приложение Voice Mouse, созданное фирмой IPI (рис. 1.25).
Рис. 1.25. Приложение Voice Mouse
Это приложение добавляет в Windows голосовое управление, что позволяет выполнять некоторые функции, не прикасаясь к мыши или клавиатуре. Во время своей работы Voice Mouse анализирует структуру меню активного приложения, формируя в своем окне список команд, входящих в меню. Этот список можно расширить произвольным образом. Для каждой команды можно определить звуковую последовательность, при распознавании которой данная команда будет запущена на выполнение, а также действия, которые нужно выполнить.
Для редактирования команд используется диалоговая панель "Command Editor" (рис.1.26).
Рис. 1.26. Редактор команд
С помощью кнопки "Train..." можно обучить Voice Mouse распознавать команду, повторив в микрофон соответствующее этой команде слово два раза.
С помощью кнопки "Edit..." можно назначить действие, которое должно быть выполнено при распознавании команды. В качестве действия можно задать либо запуск произвольного приложения, либо имитацию ввода с клавиатуры произвольной последовательности символов. Например, исходный текст приложения, показанный на рис. 1.25, был "набран" без использования клавиатуры, мы просто надиктовали его в микрофон, предварительно обучив систему Voice Mouse таким словам, как WinMain и HINSTANCE.
Аналогичная система создана фирмой Cylogic Software и называется Voice User. Среди возможных областей ее применения - помощь пользователям-инвалидам.
К системам распознавания речи второй группы можно отнести IBM Personal Dictation System, разработанную фирмой IBM Personal Software Products. Эта система имеет словарь размером 32 тыс. слов и способна работать с английским, немецким, французским, итальянским и испанскими языками. Аналогичные системы создаются и в России.
Сообщение MM_MCINOTIFY
Немного о сообщении MM_MCINOTIFY .
Как мы уже говорили, приложение может передать функции mciSendString через последний параметр идентификатор окна. Если команда MCI выдана с параметром notify, после ее завершения функция окна получит сообщение MM_MCINOTIFY. Это сообщение - извещение о завершении (удачном или нет) процесса выполнения команды.
Через параметр wParam сообщения MM_MCINOTIFY функция окна получает код извещения, по которому можно судить о результатах выполнения команды. Возможны следующие значения (описанные в файле mmsystem.h):
Значение | Описание |
MCI_NOTIFY_ABORTED | Устройство получило такую команду, в результате которой не будет получено извещение о завершении выполнения предыдущей команды. Если новая команда прерывает выполнение текущей команды и также требует извещения, функция окна получит сообщение MCI_NOTIFY_ABORTED (но не MCI_NOTIFY_SUPERSEDED) |
MCI_NOTIFY_SUCCESSFUL | Успешное завершение команды |
MCI_NOTIFY_SUPERSEDED | Устройство получило еще одну команду, так же требующую извещения, в результате чего извещение от первой команды не будет получено |
MCI_NOTIFY_FAILURE | В устройстве произошла ошибка во время выполнения команды |
Параметр lParam содержит идентификатор устройства, приславшего извещение.
В случае успешного завершения обработчик сообщения MM_MCINOTIFY должен вернуть нулевое значение, при ошибке - соответствующий код ошибки MCI.
Далее мы перейдем к изучению отдельных групп команд, предназначенных для работы со звуковым адаптером.
Создание avi-файла из файлов анимации
С помощью строки "Insert..." меню "File" вы можете вставить flc - или fli -файлы анимации в формате Autodesk Animation , подготовленные, например, с помощью таких программ, как 3D-Studio или Autodesk Animator. Для этого в списке "List Files of Type" диалоговой панели "Insert File" выберите строку "Autodesk Animation" и укажите нужный файл. Он будет преобразован в формат avi.
Создание и редактирование avi-файлов
Перед тем как приступить к изучению программного интерфейса Video for Windows (вернее, его небольшой части, касающейся окна MCI) неплохо было бы научиться создавать и редактировать avi-файлы, пользуясь утилитами, входящими в состав Video for Windows и Video for Windows Development Kit. Существует несколько способов создания avi-файлов, и не все они связаны с использованием видеокамеры и адаптера для ввода изображения. Даже если у вас нет оборудования для ввода в компьютер видео, все равно вы можете создавать мультфильмы и демонстрационные ролики.
Создание окна
Для создания окна MCI проще всего воспользоваться функцией MCIWndCreate .
Функция MCIWndCreate
HWND MCIWndCreate( HWND hwndParent, // идентификатор родительского окна HINSTANCE hInstance, // идентификатор приложения DWORD dwStyle, // стиль окна LPSTR szFile); // имя устройства или путь к файлу
Параметры функции:
hwndParent
Через этот параметр приложение передает функции идентификатор родительского окна, то есть окна, создавшего окно MCI. Если родительского окна нет, в качестве этого параметра можно указать NULL
hInstance
Идентификатор приложения, полученных через параметры функции WinMain или LibMain (для DLL-библиотеки)
dwStyle
Стиль создаваемого окна. Можно указывать стили, стандартные для функции CreateWindow, а также дополнительные, список которых приведен ниже. Если стандратные стили не указаны (что допустимо), то если есть родительское окно, используются стили WS_CHILD, WS_BORDER, иWS_VISIBLE. Если же параметр hwndParent указан как NULL, используются стили WS_OVERLAPPEDWINDOW и WS_VISIBLE. Для создания невидимого окна следует использовать один из стандартных стилей, например, WS_CHILD
szFile
Указатель на текстовую строку, содержащую имя устройства (например, "cdaudio") или путь к файлу
Возвращаемое значение:
Идентификатор созданного окна при успешном завершении или NULL при ошибке
Привдем список дополнительных стилей, которые можно использовать при создании окна MCI.
Стиль | Описание |
MCIWNDF_NOAUTOSIZEWINDOW | Размер окна не изменяется при изменении размера изображения |
MCIWNDF_NOAUTOSIZEMOVIE | При изменении размеров окна не следует выполнять масштабирование изображения для полного заполнения внутренней области окна |
MCIWNDF_NOPLAYBAR | Если задан этот стиль, не отображается полоса просмотра |
MCIWNDF_NOMENU | Не отображается кнопка для доступа к меню |
MCIWNDF_RECORD | Отображается кнопка записи, в меню добавляется строка "New" |
MCIWNDF_NOERRORDLG | При возникновении ошибки на экран не выводится диалогоая панель с описанием этой ошибки. Приложение может получить описание самой последней возникшей ошибки при помощи функции MCIWndGetError |
MCIWNDF_NOTIFYMODE | При изменении режима родительское окно получит извещающее сообщение MCIWNDM_NOTIFYMODE |
MCIWNDF_NOTIFYPOS | При изменении текущей позиции приложение получит извещающее сообщение MCIWNDM_NOTIFYPOS |
MCIWNDF_NOTIFYMEDIA | При замене носителя данных (например, звукового компакт-диска) приложение получит извещающее сообщение MCIWNDM_NOTIFYMEDIA |
MCIWNDF_NOTIFYSIZE | Родительское окно получит извещающее сообщение MCIWNDM_NOTIFYSIZE при изменении размера окна MCI |
MCIWNDF_NOTIFYERROR | При возникновении ошибки родительское окно получит сообщение MCIWNDM_NOTIFYERROR |
MCIWNDF_NOTIFYALL | Окно MCI будет извещать родительское окно в случае возникновения любых событий |
MCIWNDF_SHOWNAME | В заголовке окна будет отображаться имя устройства или путь к файлу |
MCIWNDF_SHOWPOS | В заголовке окна будет отображаться текущая позиция |
MCIWNDF_SHOWMODE | В заголовке окна будет отображаться текущий режим работы |
MCIWNDF_SHOWALL | Будут использованы все возможности окна MCI (то есть все органы управления, отображение информации в заголовке и т. д.) |
Другой способ создания окна MCI заключается в регистрации класса окна MCIWND_WINDOW_CLASS функцией MCIWndRegisterClass , не имеющей парамеров, и создании на базе этого класса окна функцией CreateWindow . В случае успеха функция MCIWndRegisterClass возвращает значение FALSE.
Создание видео из отдельных кадров
Подготовьте несколько десятков или сотен (а может быть, тысяч?) bmp-файлов, содержащих отдельные кадры мультфильма. Имена файлов должны оканчиваться их порядковым номером, например, pic001.bmp, pic002.bmp, ..., pic867.bmp и т. д.. Разумеется, размеры и цветовое разрешение всех кадров должны быть одинаковые, а что же касается содержимого, тут можно полностью положиться на ваш вкус. Если кадры должны сменяться, например, десять раз в секунду, то для создания десятисекундного мультфильма вам нужно подготовить "всего" сто рисунков.
Запустите приложение VidEdit , которое поставляется в составе Video for Windows (рис.5.5).
Рис. 5.5. Редактор avi-файлов VidEdit
С помощью меню "File" вы можете загрузить для редактирования готовый avi-файл (строка "Open...") или создать новый (строка "New"). Для создания avi-файла из последовательности bmp-файлов выберите из этого меню строку "Insert...". В списке "List Files of Type" появившейся диалоговой панели "Insert File" выберите строку "DIB Sequence".
Замените в поле "File Name" шаблон *.dib на *.bmp и выберите первый файл из подготовленной вами последовательности. Укажите формат файла как "DIB Sequence" и нажмите кнопку "OK". Последовательность bmp-файлов будет преобразована в avi-файл.
Сохраните созданный файл, выбрав из меню "File" строку "Save As...". На экране появится диалоговая панель "Save Video File", напоминающая стандартную диалоговую панель "Save As" из библиотеки commdlg.dll. Единственное существенное отличие заключается в том, что при помощи кнопки "Compression Options..." вы можете задать алгоритм сжатия, который будет использован при сохранении avi-файла (рис. 5.6).
Рис. 5.6. Диалоговая панель для выбора алгоритма сжатия
Для начала используйте параметры, указанные на рис. 5.6. При необходимости вы можете изменить скорость воспроизведения видео, выбрав в меню "Video" строку "Synchronize...". На экране появится диалоговая панель "Synchronize", предназначенная для выполнения синхронизации видео и звука (рис. 5.7).
Рис. 5.7. Диалоговая панель "Synchronize"
Изменив значение частоты кадров в поле "Video Speed", вы измените скорость воспроизведения видео.
Последовательность bmp-файлов (а также отедльные bmp-файлы) можно вставлять в любое место видео. Кроме графических файлов в формате DIB, редактор VidEdit позволяет вставлять отдельные изображения в форматах PC Paintbrush, TIFF, TGA, GIF и других популярных графических форматов.
Справочные команды
Эта группа команд предназначена для получения различной справочной информации об устройстве. Перед использованием справочных команд следует открыть устройство командой open.
Команда sysinfo не имеет особенностей. В качестве имени устройства для этой команды следует указывать строку cdaudio, даже если при открытии был использован алиас.
Для команды info можно указывать только параметр product.
С помощью команды capability с параметром can eject вы можете узнать, имеет ли устройство CD ROM возможность автоматического извлечения компакт-дисков. Вы можете также использовать и другие параметры: can play, can record, can save, compound device, device type, has audio, has video, uses files.
Для определения текущего состояния CD ROM следует использовать команду status . Вы можете указать следующие параметры:
current track
Номер текущей дорожки
length
Общая длина
length track track_number
Длина заданной дорожки
media present
Если в устройство вставлен компакт-диск, возвращается строка true
mode
Текущий режим работы: not ready (не готов), playing (проигрывание), stopped (останов), recording (запись), seeking (позиционирование)
number of tracks
Количество дорожек
position
Текущая позиция
position track track_number
Текущая позиция на заданной дорожке
ready
Если устройство готово, возвращается строка true
start position
Начальная позиция
time format
Текущий формат времени
Средства для работы с avi-файлами
Файлы avi имеют довольно сложную внутреннюю структуру и состоят из большого числа вложенных фрагментов. Структура avi-файла приведена в документации, которая поставляется вместе с Video for Windows Development Kit. Мы не будем ее описывать, так как в большинстве случаев вам не потребуется выполнять непосредственное чтение или запись avi-файлов. Все функции, необходимые для работы с avi-файлами, имеются в библиотеке avifile.dll.
Заметим, что в отличие от, например, wav-файла, avi-файл может содержать несколько потоков данных, обрабатываемых одновременно. Все данные хранятся в виде блоков (кадров), причем для обеспечения непрерывности воспроизведения звуковых данных последние чередуются с видеоданными. Как правило, для сокращения объема avi-файла используются различные методы компресии, поэтому звуковые данные и видеоданные обычно хранятся в сжатом виде. Таким образом, сложность внутренней структуры avi-файла сильно затрудняет работу с ним на низком уровне без использования функций библиотеки avifile.dll.
Функции, предназначенные для работы с avi- и wav-файлами, определенные в библиотеке avifile.dll, позволяют работать на уровне потоков данных и кадров. Вы можете получать информацию об avi-файле и о потоках данных, читать и писать потоки данных, получать отдельные кадры, причем при необходимости эти данные будут автоматически разжаты или сжаты. Есть средства для копирования потоков из разных файлов, позиционирования внутри потока, создания временных потоков в оперативной памяти. Вы можете скопировать весь avi-файл или любую его часть в Clipboard.
В распоряжении приложений есть функции GetOpenFileNamePreview и GetSaveFileNamePreview , аналогичные по своему назначению функциям GetOpenFileName и GetSaveFileName библиотеки commdlg.dll, но дополнительно обеспечивающие возможность предварительного просмотра avi-файлов или прослушивания wav-файлов. Мы используем функцию GetOpenFileNamePreview в приложении MCIWNDC, исходные тексты которого приведены в этой главе.
Стандартные приложения Windows
Первое, что вы можете сделать после установки звукового адаптера и драйвера для него - это "озвучить" Windows. Запустите приложение Control Panel и выберите для работы пиктограмму "Sound". На экране появится диалоговая панель "Sound" (рис. 1.8).
Рис. 1.8. Диалоговая панель "Sound" в Windows for Workgroups версии 3.11
Из списка "Events" вы можете выбрать то или иное событие в системе, назначив ему соответствующий звуковой фрагмент из файла с расширением имени wav. Если произойдет событие, например, запуск или завершение работы Windows, критическая ошибка и т. п., звуковой адаптер проиграет соответствующий звуковой файл. Несколько таких файлов поставляются в комплекте с операционными системами Windows или Windows for Workgroups. Для прослушивания подключенного фрагмента можно воспользоваться кнопкой "Test".
Для отключения фрагмента выберите строку "<none>". Вы можете отключить "музыкальное сопровождение" Windows, если переведете переключатель "Enable System Sounds" в выключенное состояние.
Если вас не удовлетворяет набор звуковых wav-файлов, поставляемых в составе Windows, с помощью стандартного приложения Sound Recorder (рис. 1.9) можно записать свои файлы (а также прослушать и отредактировать записанные ранее).
Рис. 1.9. Приложение Sound Recorder
Для прослушивания wav-файла загрузите его с помощью строки "Open..." меню "File" и нажмите на среднюю кнопку. Вы можете "перематывать" звуковую запись в любом направлении с помощью полосы просмотра или левых двух кнопок, а также временно останавливать ее, нажимая вторую кнопку справа. В центре окна приложения для наглядности отображается осциллограмма звукового сигнала.
Кнопка с нарисованным на ней микрофоном включает режим записи. Записанную фонограмму можно сохранить в файле при помощи строки "Save as..." меню "File".
Что еще, кроме записи и воспроизведения звуковых файлов, можно делать при помощи приложения Sound Recorder?
Меню "Effects" позволяет выполнять различные операции над загруженным или записанным звуковым файлом. Можно увеличивать или уменьшать громкость, скорость воспроизведения, добавлять эхо или перевернуть звуковой файл для проигрывания из конца в начало.
Меню "Edit" также предоставляет вам некоторые возможности для редактирования звукового файла. Вы можете объединять звуковые файлы или накладывать их друг на друга, удалять ненужные фрагменты, и, что самое интересное, копировать содержимое звукового файла в универсальный буфер обмена Clipboard. Зачем это нужно?
Современные приложения, такие как Microsoft Write, Microsoft Word for Windows версий 2.0 и 6.0, Microsoft Excel и т. п. позволяют вставлять в документ из Clipboard не только текст и графические изображения, но и звук! Разумеется, звук нельзя распечатать на принтере, но зато можно добавить звуковые комментарии в текстовый документ или электронную таблицу. Звук будет записан в Clipboard в виде OLE-объекта. Такой объект можно впоследствии не только прослушивать, но и редактировать.
В результате вставки звука из Clipboard в документе появится пиктограмма в виде микрофона. Если сделать по этой пиктограмме двойной щелчок левой клавишей мыши, вы услышите звук, вставленный ранее в документ. |
Еще одно приложение, которое работает со звуком и поставляется вместе с Windows, называется Media Player (рис. 1.10).
Рис. 1.10. Приложение Media Player
С помощью этого приложения вы можете проигрывать звуковые wav-файлы, файлы в стандарте MIDI (mid- и rmi-файлы), а также звуковые компакт диски. Можно также записывать в Clipboard звуковые фрагменты в виде OLE-объектов приложения Media Player. Для проигрывания MIDI-файлов и звуковых компакт-дисков необходимо установить соответствующие драйверы: MCI MIDI Sequencer, MIDI Mapper (которые входят в дистрибутив Windows и устанавливаются по умолчанию), а также драйвер, специфический для звукового адаптера (поставляется вместе с адаптером).
Например, вместе с адаптером Sound Galaxy NX Pro поставляется драйвер Galaxy NX-Pro FM Synth, который можно использовать для проигрывания MIDI-файлов. Вопросы, связанные с MIDI, будут рассмотрены нами позже в отдельной главе.
Приложение Media Player можно использовать и для проигрывания звуковых компакт-дисков. Для этого из меню "Device" следует выбрать строку "CD Audio". Однако следует убедиться, что в системе установлен драйвер MCI CD Audio. Если такого драйвера нет (а по умолчанию он не устанавливается), его следует добавить при помощи приложения Control Panel. Драйвер MCI CD Audio входит в комплект поставки операционной системы Windows. Кроме того, перед запуском Windows следует установить драйвер устройства чтения компакт-дисков (поставляется вместе с устройством), и запустить резидентную программу mscdex.exe (поставляется вместе с MS-DOS).
Звуковые компакт-диски содержат отдельные дорожки (треки), каждый из которых обычно соответствует одной музыкальной записи. С помощью Media Player вы можете проигрывать их последовательно или в произвольном порядке.
Если включить электронный микшер, аналоговый выход проигрывателя компакт-дисков будет подключен ко входу аналого-цифрового преобразователя звукового адаптера, что позволит переписать музыкальный фрагмент в wav-файл. Для записи wav-файла можно воспользоваться приложением Sound Recorder. Не пытайтесь переписать весь компакт-диск, так как для этого придется создать wav-файл огромного размера (некоторые приложения, предназначенные для записи wav-файлов, могут накладывать ограничения на размер файла).
На этом мы завершим обзор стандартных приложений Windows, предназначенных для работы со звуком. Более подробное описание вы сможете найти в руководстве пользователя Windows. Подводя итоги, заметим, что сервис, предоставляемый этими приложениями, нельзя назвать очень мощным. Такие приложения, как Sound Recorder и Media Player, позволяют записать, прослушать и отредактировать звуковой wav-файл, вставить звук в документ в виде OLE-объекта, проиграть MIDI-файлы и звуковые компакт-диски.Но это и все, что с их помощью можно сделать.
К настоящему моменту времени разными фирмами создано большое количество приложений Windows, способных работать со звуком и анимацией. Такие приложения реализуют технологию мультимедиа первого поколения, в которой основной упор делался на использование звука и простейшей анимации (второе поколение предполагает использование видео, совмещенного со звуком, этот подход реализован, например, в системе Microsoft Video for Windows, о которой мы еще расскажем). Рассмотрим кратко возможности приложений мультимедиа, разработанных различными фирмами.
Status
Команда status позволяет определить текущее состояние устройства.
status device_id parameter [notify] [wait]
В качестве параметра parameter можно указывать одну из следующих строк:
alignment
Выравнивание блока данных в байтах
bitspersample
Количество байт на одну выборку сигнала
bytespersec
Скорость проигрывания или записи, байт в секунду
channels
Количество каналов, 1 - моно, 2 - стерео
current track
Номер текущей дорожки. Для звукового адаптера всегда равно 1
format tag
Тег формата
input
Устройство ввода
length
Общая длина звукового фрагмента
length track track_number
Длина фрагмента, соответствующая заданной дорожке
level
Текущий уровень звукового сигнала
media present
Признак присутствия носителя (среды). Для звукового адаптера всегда равно true
mode
Текущий режим работы: not ready (не готов), playing (проигрывание), stopped (останов), recording (запись), seeking (позиционирование)
number of tracks
Количество дорожек. Для звукового адаптера всегда равно 1
output
Устройство вывода
position
Текущая позиция
position track track_number
Текущая позиция на заданной дорожке. Для звукового адаптера всегда равно 0
ready
Если устройство готово, возвращается строка true
samplespersec
Количество выборок сигнала в секунду при проигрывании или записи (частота дискретизации)
start position
Начальная позиция
time format
Текущий формат времени
Sysinfo
Команда sysinfo предназначена для получения системной информации об MCI-устройстве:
sysinfo device_id parameter [notify] [wait]
В качестве параметра parameter можно указывать одну из следующих строк:
installname
Имя, использованное в файле system.ini при установке драйвера устройства
quantity
Количество MCI-устройств типа device_id, установленных в системе и указанных в файле system.ini. Если в качестве device_id указать строку all, будет подсчитано общее количество установленных в системе MCI-драйверов
quantity open
Количество открытых MCI-устройств типа device_id, установленных в системе и указанных в файле system.ini
name index
Имя устройства MCI, номер которого задан строкой index. Первому устройству соответствует строка 1
name index open
Имя открытого устройства MCI, номер которого задан строкой index
Текстовое описание ошибки
Когда мы рассказывали об использовании функции mciSendString , то упоминали функцию mciGetErrorString, с помощью которой можно преобразовать код ошибки в текстовое описание в виде строки символов. Аналогичная возможность есть и у приложений, работающих со звуковым адаптером на низком уровне. Для выполнения такого преобразования приложение может воспользоваться функцией waveInGetErrorText (для устройства ввода) и waveOutGetErrorText (для устройства вывода).
Приведем описание функции waveInGetErrorText:
Функция waveInGetErrorText
UINT waveInGetErrorText( UINT wError, // код ошибки LPSTR lpstrBuffer, // буфер для записи текстовой строки UINT wLength); // размер буфера
Параметры функции:
wError
Код ошибки, полученный от функций низкого уровня
lpstrBuffer
Буфер, в который будет записано текстовое описание ошибки
wLength
Размер буфера в байтах. В файле mmsystem.h определена константа MAXERRORLENGTH , которая соответствует размеру самого длинного сообщения об ошибке
Возвращаемое значение:
Функция возвращает нулевое значение при успешном завершении или значение MMSYSERR_BADERRNUM , если переданному коду ошибки не соответствует ни одно текстовое описание
Функция waveOutGetErrorText используется аналогично функции waveInGetErrorText:
Функция waveOutGetErrorText
UINT waveOutGetErrorText( UINT wError, // код ошибки LPSTR lpstrBuffer, // буфер для записи текстовой строки UINT wLength); // размер буфера
Параметры функции:
Аналогичны параметрам функции waveInGetErrorText
Возвращаемое значение:
Аналогично функции waveInGetErrorText
Удаление окна
Если окно MCI больше не нужно, его можно удалить макрокомандой MCIWndDestroy . Идентификатор удаляемого окна MCI передается этой функции в качестве единственного параметра:
#define MCIWndDestroy(hwnd) (VOID)MCIWndSM(hwnd, WM_CLOSE, 0, 0)
Макро MCIWndSM определяется как функция SendMessage (см. файл mciwnd.h, поставляемый вместе с Video for Windows Development Kit и включаемый в файл vfw.h).
Для того чтобы закрыть устройство, открытое ранее в окне MCI без удаления окна, используйте макрокоманду MCIWndClose, передав ей идентификатор окна.
Управление громкостью
Ваше приложение может управлять громкостью сигнала при его воспроизведении. Для установки громкости следует использовать функцию waveOutSetVolume :
Функция waveOutSetVolume
UINT waveOutSetVolume( UINT wDeviceID, // номер устройства вывода DWORD dwVolume); // громкость
Параметры функции:
wDeviceID
Параметр wDeviceID служит для выбора устройства. Заметим, что для функции waveOutSetVolume нужно указывать не идентификатор открытого устройства, а номер устройства, который может изменяться от 0 и до значения, определенного с помощью функции waveOutGetNumDevs. Если известен только идентификатор открытого устройства, номер этого устройства можно получить, вызвав функцию waveOutGetID, рассмотренную нами ранее
dwVolume
Младшее слово параметра dwVolume задает громкость для левого канала (или единственного монофонического канала), старшее - для правого. Максимальной громкости соответствует значение 0xffff, минимальной - 0x0000. Промежуточные значения интерпретируются в логарифмическом масштабе
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_NOTSUPPORTED
Функция не поддерживается драйвером
MMSYSERR_NODRIVER
В системе нет нужного драйвера
Как правило, вместе со звуковым адаптером проставляется приложение, выполняющее функции единой управляющей панели, с помощью которой можно регулировать громкость и тембр звука для разных каналов и устройств. Если ваше приложение изменяет громкость, перед завершением своей работы оно должно восстановить первоначальный уровень громкости (если от него не требуется обратного). Это можно сделать, если перед изменением определить текущий уровень громкости с помощью функции waveOutGetVolume :
Функция waveOutGetVolume
UINT waveOutGetVolume( UINT wDeviceID, // номер устройства вывода LPDWORD lpdwVolume); // текущая громкость
Параметры функции:
wDeviceID
Параметр wDeviceID содержит номер устройства, который может изменяться от 0 и до значения, определенного с помощью функции waveOutGetNumDevs
lpdwVolume
Указатель на переменную размером в двойное слово, в которую будет записано значение, соответствующее текущей громкости для левого и правого каналов. Младшее слово переменной будет содержать громкость для левого канала (или монофонического канала), старшее - для правого. Максимальной громкости соответствует значение 0xffff, минимальной - 0x0000. Промежуточные значения интерпретируются в логарифмическом масштабе
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_NOTSUPPORTED
Функция не поддерживается драйвером
MMSYSERR_NODRIVER
В системе нет нужного драйвера
Управление проигрыванием
Макрокоманда MCIWndPlay включает режим проигрывания для окна MCI, идентификатор которого передается ей в качестве единственного параметра.
Если вам нужно начать проигрывание с заданной позиции, воспользуйтесь макрокомандой MCIWndPlayFrom . Первый параметр этой макрокоманды задает идентификатор окна MCI, второй (размером в двойное слово) - позицию для начала проигрывания. Есть возможность задать конечную позицию. Макрокоманда MCIWndPlayFromTo , имеющая три параметра, аналогична макрокоманде MCIWndPlayFrom , но позволяет через третий параметр задать конечную позицию, при достижении которой проигрывание будет остановлено.
Вы даже можете запустить проигрывние в обратную сторону - от конца к началу файла (если драйвер устройства позволит вам это сделать). Достаточно вызвать макрокоманду MCIWndPlayReverse . Она имеет один параметр (идентификатор окна MCI) и запускает проигрывание от текущей позиции к началу файла.
Макрокоманда MCIWndSetRepeat позволяет включить режим циклического проигрывания. Через первый параметр этой макрокоманде передается идентификатор окна MCI, через второй для включение режима циклического проигрывания нужно передать значение TRUE.
Макрокоманды MCIWndStop и MCIWndPause предназначены для выполнения, соответственно, останова и временного останова проигрывания. Для того чтобы продолжить проигрывание после временного останова, используйте макрокоманду MCIWndResume . Все три макрокоманды имеют только один параметр - идентификатор окна MCI.
Управление устройством CD ROM
3.1.
3.2.
В этой главе мы расскажем вам об использовании устройства чтения CD ROM для проигрывания звуковых компакт-дисков. Приложения Windows работают с этим устройством через интерфейс MCI, которым вы уже умеете пользоваться для записи и воспроизведения wav-файлов. Так как объем книги ограничен, мы не будем подробно рассказывать об использовании всех команд MCI для управления устройством чтения CD ROM, ограничившись только особенностями.
Для чего может потребоваться прослушивание звуковых компакт-дисков при помощи такого дорогостоящего устройства, как компьютер, оснащенный средствами мультимедиа? Ведь стоимость обычного проигрывателя компакт-дисков не превышает нескольких сотен долларов, что просто несравнимо со стоимостью компьютера!
Наиболее очевидные области применения приложений, умеющих проигрывать звуковые компакт-диски - рекламные и демонстрационные ролики с музыкальным и речевым сопровождением, игры, автоматизированные музыкальные центры.
Не вдаваясь в технические подробности, заметим, что данные на компакт-диске записаны вдоль одной гигантской спирали. В первом приближении можно считать, что на музыкальных дисках эта спираль разбита на несколько участков, или дорожек, каждая из которых содержит отдельную звуковую запись (например, музыкальное произведение).
Приложение может устанавливать лазерное устройство чтения в произвольное место спирали, причем драйвер обеспечивает позиционирование в режиме прямого доступа как на начало любой дорожки, так и в произвольную позицию внутри дорожки. К сожалению, процесс позиционирования занимает много времени, около 0,5 секунды, поэтому (а также из-за небольшой скорости передачи, составляющей 150-300 Кбайт в секунду) устройства чтения CD ROM нельзя называть быстродействующими.
Как мы уже говорили в первой главе, устройство чтения CD ROM имеет два звуковых выхода. Один из них обычно расположен на лицевой панели и предназначен для подключения головных телефонов (там же находится и регулятор громкости). Второй выведен на заднюю панель и подключается кабелем к входу звукового адаптера, специально предназначенному для этого. Программное обеспечение микшерского пульта, поставляющееся вместе со звуковым адаптером, позволяет подключать выход устройства чтения CD ROM ко входу усилителя или аналого-цифрового преобразователя, поэтому приложения мультимедиа могут выполнять не только проигрывание звуковых компакт-дисков с прослушиванием через громкоговорители, но и синхронную запись wav-файлов.
Вы можете работать с устройством чтения CD ROM при помощи интерфейса управляющих строк MCI или интерфейса управляющих сообщений.
Управление записью и сохранение данных
Для включения режима записи с текущей позиции предназначена макрокоманда MCIWndRecord . В качестве единственного параметра ей нужно передать идентификатор окна MCI. С помощью этой макрокоманды вы сможете организовать запись звуковых данных (как это сделано в нашем приложении MCIWNDC, которое мы рассмотрим чуть позже).
С помощью окна MCI вы не можете записывать avi-файлы, для этого предназначено окно класса AVICap.
Для сохранения записанных данных можно воспользоваться макрокомандами MCIWndSave или MCIWndSaveDialog .
Макрокоманда MCIWndSave имеет два параметра - идентификатор окна MCI и указатель на текстовую строку, в которой должен находиться путь к файлу. Записанные данные будут сохранены в этом файле.
При вызове макрокоманды MCIWndSaveDialog , имеющий один параметр (идентификатор окна MCI) на экран выводится диалоговая панель, позволяющая пользователю сохранить данные в файле. Это модифицированная диалоговая панель "Save As..." с возможностью предварительного просмотра или прослушивания содержимого файлов мультимедиа. Кстати, эту же панель вы можете вывести и отдельно с помощью функции GetSaveFileNaamePreview , аналогичной функции GetSaveFileName из библиотеки commdlg.dll.
Установка драйвера устройства чтения CD ROM
Устройство чтения CD ROM может использоваться либо для чтения цифровых компакт-дисков, либо для проигрывания звуковых компакт-дисков. И в том, и в другом случае вам необходимо установить драйвер устройства чтения и специальное расширение MS-DOS mscdex.exe.
Процедура установки драйвера зависит от изготовителя устройства и используемого интерфейсного адаптера. Она описана в документации, которая поставляется вместе с устройством. Например, для устройства MITSUMI , подключенного к звуковому адаптеру Sound Galaxy NX Pro, используется драйвер sgcdm.sys, подключаемый в файле config.sys следующим образом:
device=c:\sgnxpro\drivers\sgcdm.sys /D:MITSUMI /P:300 /I:12
Параметр /D определяет имя устройства. Это же самое имя должно быть указано при запуске mscdex.exe. Через параметр /P передается адрес порта ввода/вывода, используемого для работы с устройством, через параметр /I - номер прерывания. Драйверы для других типов CD ROM или интерфейсных адаптеров могут иметь другой набор параметров, однако имя устройства должно задаваться в любом случае.
Расширение MS-DOS, предназначенное для работы с CD ROM, подключается в файле autoexec.bat следующим образом:
c:\dos\mscdex.exe /D:MITSUMI /M:20
Единственный параметр, который должен быть задан обязательно, это параметр /D, определяющий имя устройства. Это имя должно быть задано точно таким же образом, что и в строке параметров драйвера CD ROM. Вы можете загрузить несколько драйверов для нескольких устройств CD ROM, задав каждому устройству свое имя. В этом случае при запуске mscdex.exe следует указать параметр /D несколько раз:
c:\dos\mscdex.exe /D:CDDRV00 /D:CDDRV01 /M:10 /E /V
Параметр /M определяет количество блоков памяти, получаемых программой mscdex.exe для буферизации чтения CD ROM.
Можно потребовать, чтобы программа mscdex.exe была загружена в дополнительную (expanded) память. Для этого надо указать параметр /E.
Для просмотра информации об использовании программой mscdex.exe оперативной памяти следует указать параметр /V.
Параметр /L позволяет принудительно задать букву, которая будет использована для обозначения дискового устройства, соответствующего устройству чтения CD ROM. Например, для того чтобы устройство CD ROM обозначалось буквой Z, можно использовать следующий набор параметров:
c:\dos\mscdex.exe /D:MITSUMI /L:Z
Установка драйвера звукового адаптера
Процедура установки драйвера звукового адаптера, предназначенного для Windows, описана в документации, которая поставляется вместе с адаптером. Тем не менее мы рассмотрим некоторые моменты на примере установки драйвера адаптера Sound Galaxy NX Pro.
После запуска программы установки, которая есть на дистрибутивной дискете, в каталоге sgnxpro/windows появляются следующие файлы:
DISK1 GALAXY DRV MIDIMAP CFG OEMSETUP INF SGAUX DRV SGOPL3 DRV SGPROFM DRV VSGD 386
Файл oemsetup.inf предназначен для установки драйверов с помощью стандартного приложения Control Panel.
Запустите приложение Control Panel и сделайте двойной щелчок левой клавишей мыши по пиктограмме "Drivers". На экране появится диалоговая панель "Drivers", в которой будет отображен список всех установленных драйверов. Нажмите кнопку "Add...". В появившейся диалоговой панели выберите строку "Unlisted or Updated Driver" и нажмите кнопку "OK". Появится диалоговая панель "Install Driver". В этой панели нажмите кнопку "Browse..." и укажите каталог sgnxpro/windows или другой каталог, в котором находятся драйверы звукового адаптера. Затем нажмите кнопку "OK".
На экран будет выведена диалоговая панель "Add Unlisted or Updated Driver" (рис. 1.4), с помощью которой следует добавить нужные драйверы.
Рис. 1.4. Установка драйверов для звукового адаптера
Вы можете добавить по очереди все драйверы из появившегося списка или только некоторые. Для обеспечения возможности работы со звуком следует добавить первые два драйвера из списка, показанного на рис. 1.4.
Если вы приобрели другой звуковой адаптер, названия драйверов изменятся, поэтому при установке следует руководствоваться описанием, которое поставляется вместе с драйверами.
После выбора драйвера на экране появится диалоговая панель настройки параметров (рис. 1.5).
Рис. 1.5. Настройка параметров
С помощью этой диалоговой панели вы должны указать номер прерывания, адрес порта ввода/вывода и номер канала прямого доступа. Эти параметры будут записаны в энергонезависимую память, расположенную в адаптере Sound Galaxy NX Pro. В адаптерах других типов для настройки параметров могут использоваться перемычки или переключатели, расположенные на плате звукового адаптера. Устанавливая параметры, следите за тем, чтобы они не конфликтовали с параметрами другого оборудования компьютера.
Установка звукового адаптера
При установке звукового адаптера в компьютер вам нужно выбрать для него адрес на шине ввода/вывода, номер канала прямого доступа и номер прерывания. При этом нужно внимательно следить за тем, чтобы случайно не выбрать для звукового адаптера адрес, канал прямого доступа или прерывание, занятые другими устройствами компьютера. Конкретные рекомендации по установке зависят от типа звукового адаптера и приведены в документации, которая поставляется вместе с адаптером.
Если на плате звукового адаптера имеется контроллер устройства чтения CD ROM, следует также задать адрес порта ввода/вывода и номер прерывания для этого контроллера.
Ваш звуковой адаптер может также содержать порт для подключения джойстика (игровой порт). Если в компьютере уже есть один игровой порт (в адаптере принтера, последовательного интерфейса или в адаптере "мультипорт"), этот порт перед установкой звукового адаптера следует отключить во избежание возникновения конфликта на шине ввода/вывода.
Большинство звуковых адаптеров имеют встроенный музыкальный синтезатор, снабженный последовательным портом ввода/вывода в стандарте MIDI. К этому порту можно подключить внешний музыкальный синтезатор или музыкальную клавиатуру.
На рис. 1.3 мы схематически показали подключение звукового адаптера к различным устройствам, предназначенным для ввода или вывода звука.
Рис. 1.3. Подключение звукового адаптера
Как правило, в любом звуковом адаптере есть один или два входа для подключения микрофона. По своей конструкции микрофоны бывают электродинамические, электретные и конденсаторные. Первые обеспечивают лучшее качество, но стоят дороже электретных. Покупая звуковой адаптер без микрофона, обязательно поинтересуйтесь у продавца, какой тип микрофона можно использовать вместе с этим адаптером. Некоторые адаптеры имеют два отдельных входа, предназначенных для подключения электродинамического и электретного микрофона, соответственно.
К выходу звуковой платы можно подключать головные телефоны или звуковые колонки.
Мощность встроенного в адаптер усилителя может составлять порядка 4 Вт, что вполне достаточно, если, конечно, не использовать компьютер для озвучивания дискотеки. Если же вам нужна очень большая мощность, придется подключить внешний усилитель.
Если вы приобрели звуковой адаптер вместе с колонками, попробуйте заменить их на более мощные, например, такие как 15АС-201 (не забудьте поинтересоваться величиной сопротивления колонки, которое должно быть 4 или 8 Ом, иначе можно повредить усилитель звукового адаптера). Чем больше размер излучающей поверхности, тем выше качество звучания на низких частотах. К сожалению, качество звучания миниатюрных колонок, которые продаются вместе со звуковыми адаптерами, не может удовлетворить потребности взыскательного любителя музыки. Аналогичное замечание касается и микрофона. Если вам нужно высокое качество, используйте студийный микрофон и 16-разрядный звуковой адаптер.
В звуковом адаптере может быть предусмотрен вход для подключения к такой аппаратуре, как тюнер, проигрыватель звуковых компакт-дисков или магнитофон. На рис. 1.3 этот вход обозначен как "Линия".
Разъем "Порт MIDI/игровой порт" предназначен для подключения джойстика и внешнего музыкального синтезатора или музыкальной клавиатуры.
Для плавной регулировки громкости можно использовать регулятор, расположенный на плате звукового адаптера. Однако лучше воспользоваться электронным микшерским пультом, который есть в адаптере и который управляется программно.
Вы знаете, что в корпусе практически любого персонального компьютера есть динамик, способный издавать звуковые сигналы. Он подключается к основной плате компьютера. Многие программы, в том числе модули инициализации BIOS, операционная система MS-DOS и игровые программы используют этот динамик. Если вы приобрели звуковой адаптер, встроенный динамик вам больше не понадобится. Отключите от него провода, идущие к материнской плате. Один из этих проводов подключен к "земле" компьютера, второй - к выходному усилителю, который находится на основной (материнской) плате компьютера.
Подключите этот второй провод к специальному входу, который есть на звуковом адаптере. Теперь вы сможете регулировать громкость звукового сигнала, так как он будет проходить через усилитель и микшер звукового адаптера.
Теперь о подключении устройства чтения компакт-дисков CD ROM.
CD ROM может подключаться либо к звуковому адаптеру, либо к отдельному адаптеру. Очень распространены, например, устройства CD ROM, имеющие интерфейс SCSI. Контроллер CD ROM, расположенный на плате звукового адаптера, рассчитан, как правило, на подключение только некоторых типов CD ROM, поэтому перед покупкой звукового адаптера или устройства чтения CD ROM поинтересуйтесь, будут ли они совместимы. Лучший способ застраховаться от несовместимости - покупать набор, состоящий из звуковой платы и устройства чтения CD ROM или покупать CD ROM вместе с предназначенным для него отдельным контроллером.
Вместе с устройством чтения CD ROM должны продаваться драйверы, в частности, драйвер для MS-DOS. Специальный драйвер для Windows не требуется, так как Windows имеет собственный драйвер, работающий через расширение MS-DOS, которое Microsoft поставляет в виде резидентной программы mscdex.exe. Эта программа входит в комплект поставки MS-DOS, но для ее работы требуется драйвер, который поставляется только вместе с устройством чтения CD ROM.
На корпусе устройства чтения CD ROM есть разъем для подключения головных телефонов и регулятор громкости. Разъем не предназначен для подключения колонок, так как встроенный в устройство усилитель имеет небольшую выходную мощность.
На задней стенке корпуса есть три разъема. Первый разъем нужен для подключения питания, второй - для широкого интерфейсного кабеля, третий - для подключения специального аналогового входа, который есть на звуковом адаптере (рис. 1.3). Этот аналоговый вход, так же как и вход от микрофона, линии для подключения динамика и внешней линии, подключен к микшеру, встроенному в звуковой адаптер.
Таким образом, обеспечивается возможность как раздельного ввода звуковой информации от перечисленных выше устройств, так и ввода смешанной в миксере информации.Например, вы можете установить в устройство CD ROM обычный звуковой компакт-диск, запустить его проигрывание, а затем записывать музыку с компакт-диска, с наложением речи при помощи микрофона.
Воспроизведение звуковых данных
Для воспроизведения звуковых данных на низком уровне после определения возможностей устройства вывода необходимо открыть устройство. Это можно сделать с помощью функции waveOutOpen .
Функция waveOutOpen
UINT waveOutOpen( LPHWAVEOUT lphWaveOut, // указатель на идентификатор устройства UINT wDeviceID, // номер открываемого устройства LPWAVEFORMAT lpFormat, // указатель на структуру WAVEFORMAT DWORD dwCallback, // адрес функции обратного вызова // или идентификатор окна DWORD dwCallbackInstance, // данные для функции обратного вызова DWORD dwFlags); // режим открытия устройства
Параметры функции:
lphWaveOut
Дальний указатель на переменную типа HWAVEOUT . В эту переменную будет записан идентификатор устройства вывода, который необходим для выполнения всех операций с устройством. Функция waveOutOpen может быть использована для определения возможности воспроизведения звуковых данных заданного формата (в том числе нестандартного), в этом случае параметр lphWaveOut может иметь значение NULL. Дополнительно в параметре dwFlags следует установить флаг WAVE_FORMAT_QUERY
wDeviceID
Через параметр wDeviceID приложение должно передать функции waveOutOpen номер устройства вывода, которое оно собирается открыть или константу WAVE_MAPPER , определенную в файле mmsystem.h.
В первом случае номер устройства может лежать в пределах от нуля до значения, полученного с помощью функции waveOutGetNumDevs. Напомним, что эта функция возвращает количество устройств, способных воспроизводить звуковые данные, записанные с использованием импульсно-кодовой модуляции.
Обычно приложение использует константу WAVE_MAPPER, при этом функция waveOutOpen пытается самостоятельно выбрать и открыть устройство вывода, подходящее для проигрывания звуковых данных указанного формата
lpFormat
Через параметр lpFormat приложение должно передать функции waveOutOpen адрес заполненной структуры WAVEFORMAT . Эта структура и указатели на нее описаны в файле mmsystem.h:
typedef struct waveformat_tag { WORD wFormatTag; // тип формата WORD nChannels; // количество каналов (моно или стерео) DWORD nSamplesPerSec; // частота дискретизации DWORD nAvgBytesPerSec; // скорость потока данных WORD nBlockAlign; // выравнивание блока данных } WAVEFORMAT; typedef WAVEFORMAT *PWAVEFORMAT; typedef WAVEFORMAT NEAR *NPWAVEFORMAT; typedef WAVEFORMAT FAR *LPWAVEFORMAT;
Мы уже рассказывали вам об этой структуре в разделе, посвященном формату wav-файлов. Там вы сможете найти подробное описание полей структуры
dwCallback
Через параметр dwCallback вы можете передать функции waveOutOpen адрес функции обратного вызова. Эту функцию будет вызывать драйвер устройства вывода при возникновении событий, имеющих отношение к проигрыванию блока данных. При использовании функции обратного вызова в параметре dwFlags следует установить флаг CALLBACK_FUNCTION .
Неудобство использования функции обратного вызова заключается в том, что она должна располагаться в фиксированном сегменте dll-библиотеки, так как вызов функции выполняется во время обработки прерывания. Кроме того, если функция обратного вызова использует какие-либо данные, то для их хранения следует либо использовать память из фиксированного сегмента данных, либо заказывать ее из глобальной области памяти с параметрами GMEM_MOVEABLE и GMEM_SHARE с последующей фиксацией при помощи функций GlobalLock и GlobalPageLock. Функция обратного вызова не может использовать никакие функции программного интерфейса Windows за исключением функции PostMessage и функций из dll-библиотеки mmsystem.dll, имеющих отношение к службе времени.
Другой более простой способ извещения приложения о возникновении события заключается в посылке сообщений функции окна. Для этого параметр dwCallback должен содержать идентификатор окна. Кроме этого, в параметре dwFlags следует установить флаг CALLBACK_WINDOW
dwCallbackInstance
Идентификатор данных, который передается в функцию обратного вызова. Не используется совместно с флагом CALLBACK_WINDOW
dwFlags
Вы можете указывать в этом поле следующие флаги:
Флаг | Описание |
WAVE_FORMAT_QUERY | Функция waveOutOpen вызывается только для проверки возможности использования формата звуковых данных, определенного в структуре WAVEFORMAT, адрес которой передается через параметр lpFormat. Этим способом вы можете проверить, способно ли устройство работать с нестандартным форматом, например, с нестандартной частотой дискретизации |
WAVE_ALLOWSYNC | Этот флаг необходимо использовать для открытия синхронного устройства вывода, во время работы которого все приложения блокируются |
CALLBACK_WINDOW | Для извещения о наступлении событий используется окно, идентификатор которого передается через параметр dwCallback |
CALLBACK_FUNCTION | Для извещения о наступлении событий используется функция обратного вызова, адрес которой передается через параметр dwCallback |
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_BADDEVICEID
Указан неправильный номер устройства
MMSYSERR_ALLOCATED
Это устройство уже открыто
MMSYSERR_NOMEM
Для выполнения операции не хватает памяти
WAVERR_BADFORMAT
Указанный формат звуковых данных не поддерживается драйвером устройства вывода
WAVERR_SYNC
Была выполнена попытка открыть синхронное устройство вывода без использования флага WAVE_ALLOWSYNC
Как правило, при проигрывании wav-файлов приложение вызывает функцию waveOutOpen два раза. В первый раз она вызывается для проверки возможности проигрывания звуковых данных заданного формата:
if(waveOutOpen(NULL, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, NULL, 0L, WAVE_FORMAT_QUERY | WAVE_ALLOWSYNC)) { // Формат не поддерживается }
Если указанный формат поддерживается драйвером, приложение может открыть устройство вывода, например, следующим образом:
rc = waveOutOpen(&hWaveOut, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC);
Такая методика позволяет определить возможность работы с нестандартными форматами.
Что же касается структуры WAVEFORMAT, то проще всего заполнить ее непосредственно из заголовка проигрываемого wav-файла, как это мы сделали в приложении WAVE (см. ниже).
После того, как устройство вывода открыто, можно приступать к проигрыванию wav-файла или звуковых данных, взятых из другого источника. Для проигрывания на низком уровне вы должны подготовить и передать драйверу устройства вывода блоки данных, содержащие звуковую информацию. Формат этих данных должен соответствовать указанному при открытии устройства.
Блоки данных, передаваемые драйверу, должны быть заказаны как глобальные с флагами GMEM_MOVEABLE и GMEM_SHARE. Вы можете заказать один такой блок и переписать в него содержимое wav-файла (как мы это сделали в приложении WAVE), либо использовать очередь или массив блоков, отдавая блоки драйверу по мере необходимости.
Перед тем как отдать блок драйверу, его надо подготовить при помощи функции waveOutPrepareHeader .
Функция waveOutPrepareHeader
UINT waveOutPrepareHeader( HWAVEOUT hWaveOut, // идентификатор устройства LPWAVEHDR lpWaveOutHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
lpWaveOutHdr
Через параметр lpWaveOutHdr приложение должно передать функции waveOutPrepareHeader адрес заполненной структуры WAVEHDR , описывающей передаваемый блок данных. Эта структура и указатели на нее описаны в файле mmsystem.h:
typedef struct wavehdr_tag { LPSTR lpData; // адрес блока данных DWORD dwBufferLength; // размер блока данных DWORD dwBytesRecorded; // количество записанных байт // (используется только при записи) DWORD dwUser; // пользовательские данные DWORD dwFlags; // флаги состояния буфера данных DWORD dwLoops; // кратность проигрывания буфера // (используется только при воспроизведении) struct wavehdr_tag far *lpNext; // зарезервировано DWORD reserved; // зарезервировано } WAVEHDR; typedef WAVEHDR *PWAVEHDR; typedef WAVEHDR NEAR *NPWAVEHDR; typedef WAVEHDR FAR *LPWAVEHDR;
Заказав блок памяти функцией GlobalAlloc с флагами GMEM_MOVEABLE и GMEM_SHARE, вы должны зафиксировать его функцией GlobalLock. Полученный в результате фиксирования адрес блока следует записать в поле lpData структуры WAVEHDR. Размер блока нужно записать в поле dwBufferLength.
Заметим, что для указания размера блока памяти используется двойное слово, поэтому вы можете использовать блоки очень большого размера. Однако есть ограничение - блок должен поместиться целиком в физическую память, иначе его будет невозможно зафиксировать. Поэтому при необходимости выполнять проигрывание "долгоиграющих" wav-файлов имеет смысл создать два или большее количество блоков, заполняя их из файла попеременно и отдавая драйверу для проигрывания в асинхронном режиме.
Структура WAVEHDR используется не только для воспроизведения, но и для записи. В этом случае после завершения записи блока в поле dwBytesRecorded будет находиться количество записанных байт звуковых данных. При воспроизведении это поле не используется.
Через поле dwUser приложение может передать функции обратного вызова или обработчику сообщения для данного устройства вывода любую дополнительную информацию
Поле dwFlags после прихода сообщения о событии или передачи управления функции обратного вызова будет содержать информацию о состоянии блока. В этом поле могут быть установлены следующие флаги.
Флаги | Описание |
WHDR_DONE | Работа с буфером данных закончена. Он был успешно проигран или записан, после чего драйвер вернул буфер приложению |
WHDR_BEGINLOOP | Данный буфер является первым в цикле. Флаг используется только при воспроизведении. Если необходимо проиграть в цикле только один блок, он должен быть отмечен и флагом WHDR_BEGINLOOP, и флагом WHDR_ENDLOOP |
WHDR_ENDLOOP | Данный буфер является последним в цикле. Флаг используется только при воспроизведении |
WHDR_PREPARED | Буфер подготовлен для воспроизведения функцией waveOutPrepareHeader или для записи функцией waveInPrepareHeader |
Поля lpNext и reserved зарезервированы и не должны использоваться приложением.
wSize
Поле wSize должно содержать размер структуры WAVEHDR
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_NOMEM
Для выполнения операции не хватает памяти
После того, как блок памяти обработан функцией waveOutPrepareHeader, его можно проиграть, вызвав функцию waveOutWrite .
Функция waveOutWrite
UINT waveOutWrite( HWAVEOUT hWaveOut, // идентификатор устройства LPWAVEHDR lpWaveOutHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
lpWaveOutHdr
Через параметр lpWaveOutHdr приложение должно передать функции адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных
wSize
Поле wSize должно содержать размер структуры WAVEHDR
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_UNPREPARED
Переданный блок данных не был подготовлен функцией waveOutPrepareHeader
Сразу после вызова функции waveOutWrite начинается проигрывание блока.
Если блок будет проигран до конца или если проигрывание блока будет остановлено, функция окна, идентификатор которой был указан при открытии устройства через параметр dwCallback, получит сообщение MM_WOM_DONE .
Через параметр wParam сообщения MM_WOM_DONE передается идентификатор устройства, которое было использовано для проигрывания блока. Параметр lParam содержит адрес структуры WAVEHDR, соответствующей проигранному блоку.
Если для обработки событий используется функция обратного вызова, она получит аналогичное сообщение с кодом WOM_DONE .
После того как приложение получило сообщение MM_WOM_DONE, оно должно передать блок функции waveOutUnprepareHeader, затем разблокировать его функцией GlobalUnlock и освободить (если данный блок памяти больше не нужен) функцией GlobalFree.
Приведем формат вызова функции waveOutUnprepareHeader .
Функция waveOutUnprepareHeader
UINT waveOutUnprepareHeader( HWAVEOUT hWaveOut, // идентификатор устройства LPWAVEHDR lpWaveOutHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
lpWaveOutHdr
Адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных
wSize
Поле wSize должно содержать размер структуры WAVEHDR
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_STILLPLAYING
Указанный блок все еще находится в очереди для проигрывания
После завершения работы с устройством его необходимо закрыть, вызвав функцию waveOutClose . Через единственный параметр этой функции необходимо передать идентификатор закрываемого устройства вывода.
Функция waveOutClose
UINT waveOutClose( HWAVEOUT hWaveOut); // идентификатор устройства
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_STILLPLAYING
Очередь данного устройства еще содержит блоки для проигрывания
компьютеры встречались только на страницах
Еще совсем недавно "говорящие" компьютеры встречались только на страницах научно-фантастических книг и в лабораториях. Компьютер, который умел воспроизводить или записывать звук, рассматривался как экзотика, недоступная для большинства пользователей. Многие даже и не догадывались, что на экране обычного компьютера можно просматривать видеофильмы со звуковым сопровождением.
Звук и видеофильмы представляются в компьютере в виде обычных цифровых данных, поэтому, казалось бы, нет никаких затруднений в их хранении, обработке и выводе. Однако при ближайшем рассмотрении оказывается, что для хранения звука и тем более видеоизображения требуются огромные объемы памяти - порядка десятков и сотен мегабайт. Запись и воспроизведение видеофильмов связаны с обработкой информации в реальном времени, поэтому только современные компьютеры могут справиться с потоком данных, поступающим, например, от видеокамеры.
Прорыв в технологии изготовления запоминающих устройств для компьютеров привел к значительному удешевлению памяти, как оперативной, так и дисковой. Появились лазерные дисковые устройства различных типов, обладающие рекордно низкой стоимостью хранения одного мегабайта данных. Цена процессоров i386, i486 и Pentium упала настолько, что они стали доступны практически всем. Теперь вы сможете приобрести компьютер с процессором Pentium/60 всего за 2000 - 2500 долларов. Очень трудно найти в продаже компьютер с процессором 80286, зато примерно за 700-900 долларов можно приобрести компьютер с процессором i386 или даже i486SX.
Постоянное падение цен на компьютерное оборудование, а также разработка новых программных и аппаратных методов компрессии данных создали уже сейчас все предпосылки для самого широкого внедрения технологии мультимедиа, предполагающей активное использование звука, высококачественных графических изображений и видео. Всего за 50 - 100 долларов вы можете приобрести звуковую плату с миниатюрными колонками и микрофоном, с помощью которой ваш компьютер сможет записывать и воспроизводить звук, а также проигрывать музыкальные файлы в формате MIDI.
Затратив дополнительно не более 160 долларов, вы сможете приобрести устройство чтения компакт-дисков CD ROM, которое откроет для вас окно в мир программных систем нового поколения. Вполне доступно устройство ввода информации от видеокамеры или видеомагнитофона (в зависимости от возможностей такое устройство может стоить от ста до нескольких сотен долларов).
Какие же новые возможности появляются у пользователя или программиста, превратившего свой компьютер в мультимедиа-компьютер, и стоят ли они денег, потраченных на приобретение дополнительного оборудования?
На этот вопрос может быть только один ответ: да, и еще раз да!
Уточним, что же такое мультимедиа. Дословный перевод слова multimedia не слишком благозвучен - многосредность или множество сред. Под средой здесь понимается звук, видео, текст и любые другие данные. Мы не будем переводить слово multimedia, которое пришло в русский язык из английского, широко используется и понятно без перевода.
Под мультимедиа мы будем понимать комплекс аппаратных и программных средств, позволяющих применять персональных компьютер для работы не только с текстом, но и со звуком, графикой, анимацией, а также видео.
Мультимедиа позволяют использовать компьютер новым способом, превращая его, например, в удобный инструмент для работы с базами данных громадных размеров, содержащих не только текстовые данные, но и звук, высококачественные изображения и видеофильмы.
Как разработчик программного обеспечения, вы можете распространять свое приложение на компакт-дисках, предназначенных для устройств чтения CD ROM, при этом размер дисковой памяти, занимаемый одним приложением, может достигать несколько сотен Мбайт! В России уже есть издательства, занимающиеся созданием, тиражированием и распространением таких компакт-дисков. Стоимость изготовления одного компакт-диска не превышает двух долларов. Так как на одном компакт-диске может быть записано порядка 650 Мбайт данных, стоимость хранения информации минимальна и просто не сравнима со стоимостью хранения данных на традиционных магнитных носителях.
Данное обстоятельство уже привело к лавинообразному появлению на зарубежном рынке программного обеспечения, баз данных и целых библиотек, которые поставляются на компакт-дисках по низкой цене.
Можно очень долго перечислять приложения, использующие технологию мультимедиа и записанные на компакт-дисках. Выделим для примера несколько групп таких приложений.
Справочные базы данных и системы поддержки разработчиков программного обеспечения
Примером системы поддержки разработчиков программного обеспечения может служить набор компакт-дисков Microsoft Developer Network CD. Один диск из такого набора стоимостью 50 долларов содержит целый шкаф документации (сотни книг и статей!), тысячи примеров программ, базу знаний, инструментальные средства и полезные утилиты.
На момент создания книги фирма Microsoft подготовила два уровня Microsoft Developer Network CD. Первый уровень - это один компакт-диск Development Library, на котором находится все, перечисленное выше. Второй уровень (Development Platform) состоит из нескольких компакт-дисков, содержащих десятки версий операционной системы Windows и Windows NT, а также средства разработки приложений (SDK и DDK) для Windows и Windows NT.
Без преувеличения можно сказать, что Microsoft Developer Network CD второго уровня содержит все, что нужно разработчику приложений Windows и Windows NT (кроме транслятора Visual C++, который, впрочем, тоже поставляется на компакт-диске).
Аналогичные средства созданы и другими фирмами. Можно упомянуть энциклопедии, выпускаемые фирмой Novell для разработчиков программного обеспечения, администраторов и пользователей сетевых операционных систем, созданных Novell.
Традиционное программное обеспечение
Так как стоимость хранения информации на компакт-дисках значительно меньше стоимости хранения информации на дискетах, а надежность хранения выше, удобнее использовать дистрибутивы программ в виде компакт-дисков. Современные приложения, такие, например, как Borland C++ версии 4.0 или Microsoft Visual C++ версии 1.5, поставляются либо на дискетах, либо на компакт-дисках.
В первом случае вам нужно установить по очереди более двух десятков дискет, что не слишком удобно. Установка программ с компакт-диска выполняется легко и быстро. Вам не нужно беспокоиться о том, что в один прекрасный момент дискеты дистрибутива испортятся, и, соответственно, не нужно делать резервную копию.
В настоящее время на компакт-дисках продается практически любое программное обеспечение, дистрибутив которого занимает больше чем полтора-два десятка дискет. Можно также купить один компакт-диск без напечатанной в виде книг документации, что стоит дешевле. При необходимости вы сможете сами распечатать нужные вам тома документации, так как они есть на компакт-диске в том или ином формате.
Всего за 20-30 долларов вы можете приобрести компакт-диски, содержащие тысячи и десятки тысяч программ MS-DOS, приложений Windows, драйверов и утилит для MS-DOS, Windows и других операционных систем. Предлагаются также компакт-диски, содержащие сотни высококачественных изображений в виде gif-файлов, коллекции звуков, MIDI-файлов с музыкальными произведениями и видеофильмами. Во многих случаях вы можете бесплатно использовать такие изображения, звуки и видеофильмы при создании своего программного обеспечения (то есть если вы включаете файлы, переписанные из таких компакт-дисков, в свое программное обеспечение, вам не требуется платить за это владельцу прав на распространение компакт-дисков, составителю или автору компакт-диска).
Словари и энциклопедии
Технология мультимедиа открывает новые возможности для создания электронных словарей и энциклопедий. Такие словари хранят для каждого слова не только перевод и транскрипцию, но и образец произношения в виде небольшого звукового фрагмента. Огромная емкость компакт-диска позволяет записать на нем также и фотографию, имеющую отношение к любому слову или понятию. Электронные словари и энциклопедии намного удобнее обычных, так как в них легче искать нужную вам информацию и они не занимают много места на рабочем столе (что иногда тоже имеет большое значение).
Картографические системы
На компакт- дисках выпускаются различные картографические системы, от атласа мира до подробных карт сотен городов. Вы можете прослушать гимны стран, названия городов, ознакомиться с подробной картой нужного вам города и получить другую информацию, связанную с той или иной местностью. Картографические системы окажут неоценимую услугу при планировании отпуска или деловой поездки, они очень удобны для туристических бюро и агентств.
Обучающие системы
Эффективность обучения повышается при использовании наглядных иллюстраций. Технология мультимедиа и компакт-диски сделали возможным появление обучающих систем нового поколения, превращающих обучение в игру. Создатель такой системы может иллюстрировать изучаемый материал при помощи видеофильмов со звуковым комментарием, что улучшает его восприятие и снижает утомляемость.
Телеконференции в сети компьютеров
Организация в локальных сетях компьютеров телеконференций - новое направление использования технологии мультимедиа, появившееся в результате внедрения скоростных локальных сетей, недорогих видеокамер, аппаратуры для ввода звука и видеоинформации в персональный компьютер. Видеоизображение и звук поступают с одной или нескольких видеокамер и транслируются по сети. Каждый участник видеоконференции имеет доступ к транслируемым видеоматериалам и при наличии на рабочем месте видеокамеры может сам передавать изображения в сеть.
Игры и развлечения
Технология мультимедиа позволяет приблизить "виртуальную реальность" игры к реальности жизни, которая наполнена звуками и изображениями. Компакт-диски могут хранить файлы игровых программ размером в сотни мегабайт, что создает предпосылки для появления игр, несравнимых со старыми по обилию звуковой и видеоинформации. Наконец, устройство чтения компакт-дисков способно проигрывать музыкальные диски. Вы можете подключить к нему обычные наушники и слушать музыку.
Надеемся, что мы вас убедили и что в самое ближайшее время вы отправитесь приобретать звуковой адаптер и устройство чтения компакт-дисков CD ROM, а может быть и адаптер для записи видеофильмов.
Теперь об этой книге.
Наша книга предназначена в первую очередь для тех, кто собирается самостоятельно создавать мультимедиа-приложения. Поэтому основное внимание мы уделим вопросам использования технологий мультимедиа при разработке новых приложений. Однако, учитывая новизну темы, мы сделаем краткий обзор существующих приложений, поддерживающих технологию мультимедиа.
Из-за ограниченного объема книги мы смогли рассмотреть только самые важные аспекты программирования устройств мультимедиа. Тем из вас, кто собирается разрабатывать мультимедиа-приложения на профессиональном уровне, мы рекомендуем приобрести Microsoft SDK для Windows версии 3.1, в состав которого входит полное описание средств мультимедиа. В продаже есть также изделие Microsoft MDK (Multimedia Development Kit), вместе с которым поставляется средство для создания приложений мультимедиа без программирования. Для работы с видеофильмами вам необходимо приобрести систему Microsoft Video for Windows Development Kit, содержащую необходимую документацию и, что самое главное, полезные утилиты, примеры приложений, библиотеки и include-файлы.
Для работы с книгой вам достаточно иметь компьютер с процессором i386DX33 и оперативной памятью 4 Мбайт, оснащенный звуковым адаптером и, желательно, устройством чтения компакт-дисков CD ROM. Для работы с видеофильмами средствами Video for Windows рекомендуется процессор i486 и акселератор Windows в качестве видеоадаптера, хотя просмотр видеофильмов возможен и на менее мощном компьютере (акселератор Windows, тем не менее, очень желателен).
Что касается программного обеспечения, то для трансляции исходных текстов примеров приложений, не имеющих отношение к Video for Windows, вы можете воспользоваться системами Borland Turbo C++ for Windows версии 3.1 или Borland C++ версий 3.1 или 4.0. Приложения, работающие с Video for Windows, можно транслировать системами Microsoft C++ версии 7.0 или Microsoft Visual C++ версий 1.0 или 1.5. В последнем случае желательно, чтобы в компьютере было установлено по крайней мере 8 Мбайт оперативной памяти (а лучше 16 Мбайт, особенно если вы собираетесь разрабатывать крупные проекты).
В первой, вводной главе мы познакомим вас с системами мультимедиа для Windows, расскажем о способах ввода и представлении звуковой информации в памяти компьютера, расскажем о том, как выбрать и подключить звуковой адаптер, а также сделаем краткий обзор приложений Windows, использующих технологию мультимедиа.
Вторая глава полностью посвящена описанию различных способов, с помощью которых приложения Windows могут записывать и воспроизводить звук. Мы рассмотрим все уровни программного интерфейса, от самого высокого до самого низкого. Помня о том, что примеры сильно облегчают понимание материала, мы включили в эту главу многочисленные примеры несложных приложений, демонстрирующих все способы работы со звуком. Конкретно мы рассмотрели функцию sndPlaySound, позволяющую проигрывать звуковые файлы небольшого размера, два типа интерфейса MCI и функции низкого уровня, выполняющие обращение непосредственно к драйверу звукового адаптера.
Третья глава - о работе с устройством чтения компакт-дисков CD ROM. Из нее вы узнаете о том, как подключить драйвер этого устройства, как использовать устройство для чтения цифровых компакт-дисков и проигрывания музыкальных компакт-дисков. Мы опишем способы управления CD ROM, основанные на применении интерфейса MCI и приведем пример приложения, предназначенного для проигрывания дорожек музыкального компакт-диска. Используя полученные знания, вы сможете создавать приложения, которые проигрывают дорожки компакт-диска во время своей работы в фоновом режиме.
В четвертой главе мы расскажем вам об использовании музыкального синтезатора, расположенного в звуковом адаптере, для проигрывания музыкальных файлов в стандарте MIDI с помощью интерфейса MCI. Вы сможете легко озвучить ваше приложение, даже если оно не рассчитано на работу с устройством чтения компакт-дисков. Музыкальные файлы в стандарте MIDI занимают очень немного места, поэтому ваше "озвученное" приложение не превратится в монстра, пожирающего десятки мегабайт дисковой памяти.
В продаже есть богатые коллекции MIDI-файлов, так что вы всегда сможете выбрать что-нибудь на свой вкус.
Пятая глава, посвященная Microsoft Video for Windows, - о самой передовой технологии мультимедиа для операционной системы Microsoft Windows. Когда мы еще только начинали писать эту книгу, у нас не было планов включать в нее материал о Video for Windows, так как для работы с этой системой требуется достаточно мощный компьютер. Кроме того, Video for Windows - весьма сложная система, заслуживающая отдельной книги. Однако компьютеры постоянно дешевеют, а обзор средств мультимедиа был бы неполным без хотя бы краткого рассказа о Video for Windows. Поэтому мы дополнили книгу пятой главой, которую можно считать введением в Video for Windows. Вы познакомитесь с принципами, положенными в основу этой системы, узнаете ее структуру и назначение отдельных подсистем. Мы научим вас создавать видеофильмы с помощью приложений, входящих в комплект поставки Video for Windows, а также создавать собственные приложения, предназначенные для проигрывания видеофильмов. Таким образом, даже не изучая всей системы в целом, вы сможете использовать технологию Video for Windows в своих приложениях. А главное - вы сможете почувствовать мощь этой технологии, которая, кстати, является стандартной для новых версий операционной системы Windows, таких как Windows NT версии 3.5 и Chicago.
Вместе с этим томом "Библиотеки системного программиста" продается больше дискет, чем обычно. Кроме дискеты с исходными текстами приложений мы подготовили дискеты с файлами небольших видеофильмов, а также дискету с системой Video for Windows Runtime, необходимой для работы с Video for Windows. Поэтому если вы приобрели весь набор дискет, у вас есть все для того чтобы оценить возможности Video for Windows.
Как связаться с нами?
Вы можете присылать ваши отзывы в адрес издательства или через электронную почту по адресу, доступному из сетей GlasNet, Internet, Relcom и т. п.:
frolov@glas.apc.org
Заранее благодарны за ваши замечания и предложения по содержанию этой книги, а также всех остальных книг серий "Библиотека системного программиста" и "Персональный компьютер.Шаг за шагом".
Авторы выражают благодарность:
сотрудникам издательского отдела АО "Диалог-МИФИ" Елене Виноградовой, Олегу Александровичу Голубеву, Наталье Дмитриевой, Оксане Кузьминовой, которые приложили немало усилий для того чтобы книги серий "Библиотека системного программиста" и "Персональный компьютер. Шаг за шагом" вышли в свет;
корректору Виктору Кустову за колоссальные усилия, затраченные на проверку и исправление рукописей наших книг;
системным программистам фирмы Interactive Products Inc. Максиму Синеву и Сергею Ноженко за ценные советы и рекомендации по содержанию книги;
фирме Interactive Products Inc. за предоставленное в наше распоряжение программное обеспечение.
Ввод и вывод звука
Для ввода звука используется устройство с названием аналого-цифровой преобразователь . Оно периодически преобразует аналоговый сигнал, поступающий от микрофона, магнитофона или другого источника сигнала, в последовательность чисел, представляющих собой мгновенные значения амплитуды сигнала (рис. 1.1).
Рис. 1.1. Ввод звука
Этот способ преобразования называется импульсно-кодовой модуляцией . В отечественной литературе вы можете встретить аббревиатуру ИКМ, однако в документации, которая поставляется вместе с SDK для Windows, для обозначения импульсно-кодовой модуляции используется аббревиатура PCM - Pulse Code Modulation .
Насколько часто нужно выполнять преобразование?
Это зависит от ширины спектра входного сигнала. Как известно, человеческое ухо воспринимает звуковые колебания в диапазоне частот примерно от 20 Гц до 16-20 Кгц, поэтому для передачи звука без искажений необходимо обеспечить преобразование сигнала с шириной спектра 20 Кгц. Существует теорема (носящая у нас имя Котельникова), согласно которой частота дискретизации сигнала должна вдвое превышать максимальное значение частоты сигнала. Следовательно, для передачи всего спектра звуковых сигналов, воспринимаемых человеком, необходимо выполнять преобразования аналогового сигнала с частотой не менее 40 Кгц.
Другой вопрос - с какой точностью следует преобразовывать аналоговый сигнал?
Казалось бы, чем точнее - тем лучше. Однако не следует забывать, что чем больше точность, тем больше двоичных разрядов используется для представления амплитуды сигнала и следовательно, тем больше памяти нужно для хранения записанной фонограммы. В хороших цифровых студийных магнитофонах для представления амплитуды сигнала используется не менее 16 разрядов, или 2 байта. Нетрудно подсчитать, что в этом случае при частоте дискретизации 40 Кгц для хранения одной минуты монофонической звуковой информации потребуется примерно 4,5 Мбайт памяти. Для стереофонической записи эту величину следует умножить на два.
Если вам нужно записывать только речь, можно ограничить ширину спектра сигнала величиной примерно 2,2 - 3 Кгц, что соответствует частоте дискретизации 4,4 - 6 Кгц. Кроме того, для представления мгновенной амплитуды сигнала вполне достаточно одного байта памяти. В этом случае для хранения одной минуты речи нужно 257 Кбайт памяти.
Для вывода звука используется цифро-аналоговый преобразователь (рис. 1.2), который может быть одноканальный (монофонический) или двухканальный (стереофонический).
Рис. 1.2. Вывод звука
На вход цифро-аналогового преобразователя поступает цифровой код, полученный от аналого-цифрового преобразователя и записанный в память компьютера. Разумеется, темп поступления кода должен соответствовать частоте дискретизации, в противном случае при воспроизведении неизбежны частотные искажения.
Для повышения производительности звуковой адаптер работает в режиме прямого доступа к памяти с использованием прерываний.
Выбор и подключение звукового адаптера
Для компьютера IBM PC и совместимых с ним создано много звуковых адаптеров. Это Sound Blaster, Sound Galaxy, Sound Vision, Microsoft Sound System и т. д. Они отличаются друг от друга по составу аппаратных средств, возможностям и цене. Как правило, все эти адаптеры предназначены для шины ISA, хотя есть устройства, подключаемые к принтерному или к последовательному порту компьютера. Выпускаются блокнотные компьютеры, оснащенные встроенным звуковым адаптером (и даже встроенным устройством чтения компакт-дисков).
Мы рекомендуем вам приобрести стереофонический адаптер для шины ISA, содержащий дополнительно музыкальный синтезатор, например, адаптер Sound Galaxy NX Pro. Если вы ограничены в средствах, купите 8-битовый адаптер, который обеспечит вполне приемлемое качество звучания. Если же вам нужно студийное качество, воспользуйтесь 16-битовым адаптером.
Особое внимание при покупке обратите на совместимость адаптера с операционной системой Windows версии 3.1. В комплект поставки должны входить драйверы для этой операционной системы. Учтите, что в состав Windows версии 3.1 входят драйверы не для всех звуковых адаптеров (есть драйверы для адаптеров Sound Blaster и AdLib). Другой важный критерий - аппаратная совместимость с адаптером Sound Blaster. Этот адаптер стал своеобразным стандартом для подобных устройств. Совместимость с Sound Blaster нужна для того, чтобы вы смогли использовать адаптер с играми, разработанными для MS-DOS, такими, как DOOM или Wolf3D, или с другими аналогичными программами.
Иногда на плате звукового адаптера устанавливают интерфейс устройства чтения компакт-дисков CD ROM. Если вы планируете приобрести сразу и звуковой адаптер, и устройство чтения CD ROM, лучше остановить свой выбор на одном из комплектов, добавляющих в компьютер возможности мультимедиа. В этот комплект, как правило, входит звуковой адаптер, микрофон, колонки, соединительные кабели, устройство чтения компакт-дисков CD ROM, набор драйверов для MS-DOS и Windows, а также несколько демонстрационных компакт-дисков.
Стоимость такого набора может лежать в пределах 290-400 долларов, что не очень много.
При выборе типа устройства чтения CD ROM следует принимать во внимание формат данных, скорость передачи данных и время позиционирования. Формат данных должен соответствовать стандарту ISO 9660 (можно узнать из документации, которая поставляется вместе с устройством). Устройство чтения с одинарной скоростью обеспечивает скорость передачи данных, равную 150 Кбайт в секунду при среднем времени позиционирования порядка 250 миллисекунд. Есть также устройства с двойной скоростью (300 Кбайт в секунду). Ожидается появление устройств чтения CD ROM с еще большей скоростью передачи данных.
Скорость передачи данных критична для приложений, читающих с компакт-дисков видеофильмы и отображающие их на экране компьютера. Для проигрывания видеофильмов лучше использовать устройства с двойной скоростью, хотя в крайнем случае подойдут и устройства с одинарной скоростью. Если вы работаете с базами данных, записанных на компакт-дисках, основной вклад в задержку работы вносит процесс позиционирования при поиске и выборке информации, поэтому в этом случае двухкратное увеличение скорости передачи данных не приведет к двухкратному увеличению скорости работы.
Загрузка файла или выбор устройства
При создании окна MCI вы можете указать имя устройства или файл мультимедиа, передав указатель на соответствующую текстовую строку через последний параметр функции MCIWndCreate. Можно использовать и другие способы.
Макрокоманда MCIWndOpen позволяет для созданного ранее окна открыть устройство или файл, например:
MCIWndOpen(hwnd, "push.wav", MCIWNDOPENF_NEW);
Через первый параметр передается идентификатор окна MCI, через второй - указатель на строку, содержащую имя устройства или файла. В третьем параметре при создании нового файла нужно указать флаг MCIWNDOPENF_NEW .
Напомним, что закрыть устройство или файл можно с помощью макрокоманды MCIWndClose .
Функция MCIWndOpenDialog позволяет вывести на экран диалоговую панель, с помощью которой пользователь может выбрать файл и загрузить его в окно MCI. В качестве единственного параметра этой функции следует передать идентификатор окна MCI. Функция возвращает значение 0L (размером в двойное слово) в случае успеха или код ошибки.
Для выбора файла можно также использовать функцию GetOpenFileNamePreview , входящую в программный интерфейс Video for Windows и аналогичную функции GetOpenFileName из библиотеки commdlg.dll . Диалоговая панель, появляющаяся на экране при вызове этой функции, содержит окно для предварительного прослушивания или просмотра файла (рис. 5.15).
Новый файл создается макрокомандой MCIWndNew , которой в качестве первого параметра следует передать идентификатор окна MCI, а в качестве второго - указатель на текстовую строку, содержащей имя устройства. Пример использования этой макрокоманды ести в приложении MCIWNDC, которое мы скоро рассмотрим.
Закрытие файла
Если приложение открыло файл функцией mmioOpen, после завершения работы с ним оно должно закрыть этот файл функцией mmioClose .
Функция mmioClose
UINT mmioClose( HMMIO hmmio, // идентификатор открытого файла UINT wFlags); // флаги для операции закрытия файла
Параметры функции:
hmmio
Идентификатор открытого файла, полученный с помощью функции mmioOpen
wFlags
Флаги, определяющие режим закрытия файла. Можно указать флаг MMIO_FHOPEN , при этом функция mmioClose закроет файл, открытый средствами MS-DOS
Возвращаемое значение:
При успехе возвращается нулевое значение. В противном случае - код ошибки
Запись и воспроизведение видео
5.1.
5.2.
5.3.
Эта глава посвящена системе Microsoft Video for Windows версии 1.1 (рис. 5.1), которая поставляется отдельно в качестве расширения для Microsoft Windows и Microsoft Windows for Workgroups. Новые версии операционной системы Windows, например, Windows NT версии3.5, поставляются со встроенной системой Video for Windows Runtime, позволяющей проигрывать avi-файлы и другие мультимедиа-файлы.
Рис. 5.1. Microsoft Video for Windows версии 1.1
Video for Windows предоставляет в распоряжение пользователей новую технологию работы со звуковой информацией, видеоинформацией и другими типами данных. С помощью Video for Windows пользователь может создавать файлы с расширением имени avi, содержащие одновременно несколько потоков данных. Например, в файле может храниться видеофильм со звуковым сопровождением на нескольких национальных языках. Во время воспроизведения пользователь может выбрать нужный ему язык, указав соответствующий поток звуковых данных.
В качестве источника видео можно использовать видеокамеру, подключенную к компьютеру через видеоадаптер. В этом случае для создания avi-файла следует использовать приложение VidCap . Можно также создать видеоролик из отдельных кадров, сохраненных в виде файлов в одном из нескольких наиболее популярных графических форматов, из анимационных файлов fli и flc в формате Autodesk Animation . В составе Video for Windows поставляется приложение Screen Capture, с помощью которого можно создавать демонстрационные ролики, "снимая" изменения на экране компьютера.
Что же касается звукового сопровождения, то есть возможность одновременной записи звука и видео, причем для ввода звука в этом случае используется обычный звуковой адаптер. Можно добавить звук позже в уже готовый avi-файл, "озвучив" его после съемки и монтажа. Для этого следует воспользоваться приложением VidEdit . Просматривать avi-файлы можно с помощью приложения Media Player .
Для создания приложений, работающих с Video for Windows, вам следует приобрести Video for Windows Development Kit версии 1.1 (рис. 5.2), а также транслятор Microsoft C++ версии 7.0 или Microsoft Visual C++ версий 1.0 или 1.5.
Рис. 5.2. Video for Windows Development Kit версии 1.1
В состав Video for Windows Development Kit входит документация в виде гипертекста (Programmer Guide), набор библиотек и include-файлов, большое количество примеров приложений. Вместе с Video for Windows и Video for Windows Development Kit поставляется Video for Windows Runtime , который вы можете распространять вместе с разработанными вами приложениями или созданными вами avi-файлами. Кроме необходимых драйверов и dll-библиотек в состав Video for Windows Runtime входит приложение Media Player.
Запись и воспроизведение звука
2.1.
2.2.
2.3.
2.4.
В этой главе мы расскажем вам о том, как приложения Windows могут записывать и воспроизводить звуковые фрагменты, используя программный интерфейс системы мультимедиа различных уровней. Вы научитесь создавать "звучащие" приложения, и приложения, которые могут записывать звук, познакомитесь со структурой wav-файлов, предназначенных для хранения записанных фонограмм.
Как мы уже говорили, для работы со звуковым адаптером в среде операционной системы Windows вам не потребуется программировать на уровне портов ввода/вывода, прерываний и каналов прямого доступа. Весь необходимый интерфейс (высокого или низкого уровня) предоставляется приложению DLL-библиотекой mmsystem.dll . Эту библиотеку можно рассматривать как расширение Windows для обеспечения возможности работы с мультимедиа.
Библиотека mmsystem.dll поставляется в составе Windows версии 3.1 (версия 3.0 не могла работать с мультимедиа, однако можно было приобрести изделие Microsoft Multimedia Extension, содержащее эту библиотеку и приложения, предназначенные для работы со звуком). Все функции, входящие в библиотеку mmsystem.dll, описаны в файле mmsystem.h , который поставляется со всеми системами разработки приложений для Windows и находится в каталоге include вместе с файлом windows.h.
Что же содержится в библиотеке mmsystem.dll?
В этой библиотеке определены функции двух уровней: функции низкого уровня (Low-Level Functions ) и функции высокого уровня, представляющих собой интерфейс управления средой MCI (Media Control Interface ).
Функции низкого уровня работают непосредственно с драйверами устройств ввода/вывода, такими, как драйверы звукового адаптера, джойстика или устройства ввода/вывода MIDI .
Функции интерфейса MCI работают с драйверами устройств MCI (например, драйверами устройств чтения компакт-дисков или лазерных видеодисков) и вызывают функции низкого уровня.
В любом случае для работы с устройствами мультимедиа приложение должно вызывать ту или иную функцию, определенную в библиотеке mmsystem.dll, вне зависимости от уровня интерфейса.
Что же касается драйверов звуковых устройств, то можно выделить четыре типа таких драйверов.
Драйвер для ввода звука (Waveform Input Driver )
Драйвер для вывода звука (Waveform Output Driver )
Драйвер для ввода музыки в стандарте MIDI (MIDI Input Driver )
Драйвер для вывода музыки в стандарте MIDI (MIDI Output Driver )
Все эти драйверы поставляются вместе со звуковым адаптером и устанавливаются после установки операционной системы Windows. В зависимости от типа звукового адаптера состав драйверов может изменяться (например, могут отсутствовать драйверы для работы с MIDI).
Вы можете также приобрести звуковой драйвер для работы с динамиком, встроенным в корпус компьютера (Sound Driver for PC Speaker ). В комплект поставки входят два файла - speaker.drv и oemsetup.inf . Этот драйвер можно найти в библиотеке дополнительных драйверов для Windows, которая называется Windows Driver Library , или на одной из электронных досок объявлений BBS.
Конечно, качество звучания встроенного динамика не сравнимо с качеством звучания настоящей звуковой системы, но самый главный недостаток этого драйвера заключается в невозможности воспроизведения звука в фоновом режиме. Однако даже небольшие звуковые возможности, полученные практически даром, оживят Windows и сделают эту операционную систему более привлекательной.
Запись с помощью видеоадаптера
Для того чтобы создавать полноценные видеофрагменты, вам следует приобрести специальный видеоадаптер для ввода видеосигнала. В продаже есть два принципиально разных типа таких адаптеров. Простейшие видеоадаптеры, которые мы отнесем к первому типу, позволяют записывать лишь отдельные кадры. В некоторых случаях этого достаточно (например, вы сможете снять "пластилиновый" мультфильм, составив его из множества отдельных кадров). Видеоадаптеры второго типа способны записывать поток видеоинформации в реальном масштабе времени, причем одновременно выполняется компрессия данных.
Приобретая видеоадаптер поинтересуйтесь, сможете ли вы с помощью этого адаптера записывать видео в реальном масштабе времени с компрессией данных, или же вам будет доступна только запись отдельных кадров. Кроме того, очень важно, чтобы вместе с видеоадаптером поставлялся драйвер для Windows версии 3.1, способный работать вместе с Microsoft Video for Windows версии 1.1. Только тогда вы сможете без проблем записывать avi-файлы, подключив к видеоадаптеру лазерный проигрыватель видеодисков, видеокамеру или видеомагнитофон. В противном случае вам придется создавать avi-файл из отдельных кадров, что не всегда приемлимо и отнимает очень много времени.
Для создания avi-файла с помощью видеоадаптера вам следует использовать приложение VidCap , которое поставляется вместе с Video for Windows. Это приложение способно работать в трех режимах: в режиме покадровой записи, в автоматическом режиме и в автоматическом режиме с автоматическим же управлением лазерным проигрывателем видеодисков или видеомагнитофоном.
Первый режим удобен в тех случаях, когда ваш видеоадаптер не способен записывать видео в реальном масштабе времени или когда вам требуется записать только отдельные кадры видео.
Второй режим удобен для записи видеофильмов непосредственно в avi-файл. Учтите, что размер созданного файла может достигать в зависимости от продолжительности записи и метода компрессии десятков и сотен мегабайт, поэтому подготовьте быстродействующий диск соответствующего размера. Важное значение имеет также производительность процессора, поэтому компьютер с процессором i386 едва ли подойдет для записи видео в реальном масштабе времени.
Третий режим доступен в том случае, если источник видеосигнала (лазерный проигрыватель или видеомагнитофон) имеет возможность дистанционного управления от компьютера с использованием интерфейса MCI. В этом режиме приложение VidCap способно управлять устройством и позиционировать носитель данных.
Если вас по каким-либо причинам не устраивают возможности приложения VidCap, вы можете создать собственное при помощи Video for Windows Development Kit на базе окна класса AVICap . Примеры приложений, выполняющих запись видео (которые называются Cap Test и CapCPP ), поставляются с исходными текстами в составе Video for Windows Development Kit.
Запись в avi-файл содержимого области экрана
В состав Video for Windows входит приложение Screen Capture , с помощью которого вы можете создавать демонстрационные и рекламные ролики, записывая в avi-файл изображение любой области экрана.
Запустите приложение Screen Capture. Оно появится на экране в виде пиктограммы. Установите на эту пиктограмму курсор и сделайте щелчок левой клавишей мыши. Появится меню (рис. 5.8).
Рис. 5.8. Меню приложения Screen Capture
Выберите строку "Set Capture File..." и с помощью появившейся диалоговой панели выберите avi-файл, в который будет записан видеоролик. Затем нужно указать область экрана, содержимое которой будет записано в avi-файл. Так как производительность компьютера может оказаться недостаточной для записи крупноформатного видео, в документации рекомендуется использовать область размером не больше чем 320 х 240 пикселов.
Затем выберите строку "Preferences..." и задайте режимы работы приложения (рис. 5.9).
Рис. 5.9. Диалоговая панель "Preferences"
В поле "Frame Rate" укажите частоту кадров. Если вместе с записью видео нужно выполнять запись звуковых данных, включите переключатель "Capture Audio" и укажите нужный формат звуковых данных, нажав на кнопку "Change...". По умолчанию для остановки процесса записи используется клавиша <Esc>. Вы можете заменить ее на другую в поле "Stop Recording Key".
Для запуска процесса записи выберите строку "Capture!" в меню приложения Screen Capture. Начнется запись видео и, возможно, звуковых данных.
Запись в файл
Для записи в файл, открытый при помощи функции mmioOpen, следует использовать функцию mmioWrite . Эта функция позволяет за один вызов записать в файл блок данных размером, большим 64 Кбайт. После записи выполняется перемещение текущей позиции вперед на количество записанных байт.
Функция mmioWrite
LONG mmioWrite( HMMIO hmmio, // идентификатор открытого файла HPSTR hpBuff, // указатель на буфер с данными LONG dwBytes); // размер буфера
Параметры функции:
hmmio
Идентификатор открытого файла, полученный с помощью функции mmioOpen
hpBuff
Указатель типа huge на буфер, содержимое которого будет записано в файл
dwBytes
Размер буфера
Возвращаемое значение:
Возвращается количество записанных байт данных или -1 при возникновении ошибки
Запись звуковых данных
Процесс записи похож на процесс воспроизведения.
Вначале необходимо открыть устройство записи, вызвав функцию waveInOpen :
Функция waveInOpen
UINT waveInOpen( LPHWAVEIN lphWaveIn, // указатель на идентификатор устройства UINT wDeviceID, // номер открываемого устройства LPWAVEFORMAT lpFormat, // указатель на структуру WAVEFORMAT DWORD dwCallback, // адрес функции обратного вызова // или идентификатор окна DWORD dwCallbackInstance, // данные для функции обратного вызова DWORD dwFlags); // режим открытия устройства
Параметры функции:
lphWaveOut
Дальний указатель на переменную типа HWAVEIN . В эту переменную будет записан идентификатор устройства ввода, который необходим для выполнения всех операций с устройством. Функция waveOutOpen может быть использована для определения возможности записи звуковых данных в заданном формате (например, нестандартном), в этом случае параметр lphWaveIn может иметь значение NULL. Дополнительно в параметре dwFlags следует установить флаг WAVE_FORMAT_QUERY
wDeviceID
Через параметр wDeviceID приложение должно передать функции waveInOpen номер устройства ввода, которое оно собирается открыть или константу WAVE_MAPPER , определенную в файле mmsystem.h.
В первом случае номер устройства может лежать в пределах от нуля до значения, полученного с помощью функции waveInGetNumDevs.
Если приложение использует константу WAVE_MAPPER, функция waveInOpen пытается самостоятельно выбрать и открыть устройство вывода, подходящее для записи звуковых данных в указанном формате
lpFormat
Через параметр lpFormat приложение должно передать функции waveInOpen адрес заполненной структуры WAVEFORMAT. Мы уже рассказывали вам об этой структуре в предыдущем разделе.
dwCallback
Через параметр dwCallback вы можете передать функции waveInOpen адрес функции обратного вызова. Эту функцию будет вызывать драйвер устройства ввода при возникновении событий, имеющих отношение к записи блока данных. При использовании функции обратного вызова в параметре dwFlags следует установить флаг CALLBACK_FUNCTION.
Можно использовать другой способ извещения приложения о возникновении события, который заключается в посылке сообщений функции окна. Для этого параметр dwCallback должен содержать идентификатор окна. Кроме этого, в параметре dwFlags следует установить флаг CALLBACK_WINDOW
dwCallbackInstance
Идентификатор данных, который передается в функцию обратного вызова. Не используется совместно с флагом CALLBACK_WINDOW
dwFlags
Вы можете указывать в этом поле следующие флаги:
Флаг | Описание |
WAVE_FORMAT_QUERY | Функция waveInOpen вызывается только для проверки возможности использования формата звуковых данных, определенного в структуре WAVEFORMAT |
WAVE_ALLOWSYNC | Этот флаг необходимо использовать для открытия синхронного устройства ввода, во время работы которого все приложения блокируются |
CALLBACK_WINDOW | Для извещения о наступлении событий используется окно, идентификатор которого передается через параметр dwCallback |
CALLBACK_FUNCTION | Для извещения о наступлении событий используется функция обратного вызова, адрес которой передается через параметр dwCallback |
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_NODRIVER
В системе нет нужного для работы с устройством ввода драйвера
MMSYSERR_BADDEVICEID
Указан неправильный номер устройства
MMSYSERR_ALLOCATED
Это устройство уже открыто
MMSYSERR_NOMEM
Для выполнения операции не хватает памяти
WAVERR_BADFORMAT
Указанный формат звуковых данных не поддерживается драйвером устройства ввода
WAVERR_SYNC
Была выполнена попытка открыть синхронное устройство ввода без использования флага WAVE_ALLOWSYNC
После открытия устройства ввода необходимо подготовить один или несколько блоков памяти, в который (или которые) будет записана введенная звуковая информация. Требования к блокам памяти, используемым для записи, такие же, как и требования к блокам памяти, используемым для воспроизведения. Они должны быть заказаны как глобальные с флагами GMEM_MOVEABLE и GMEM_SHARE.
Вы можете заказать один блок (как мы это сделали в приложении WAVE), либо использовать очередь или массив блоков, отдавая блоки драйверу по мере необходимости, переписывая каждый раз содержимое записанного блока в wav-файл.
Перед тем как отдать блок драйверу, его, как и в процессе воспроизведения, надо подготовить, для чего следует воспользоваться функцией waveInPrepareHeader .
Функция waveInPrepareHeader
UINT waveInPrepareHeader( HWAVEIN hWaveIn, // идентификатор устройства LPWAVEHDR lpWaveInHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
lpWaveInHdr
Через параметр lpWaveInHdr приложение должно передать функции waveInPrepareHeader адрес заполненной структуры WAVEHDR, описывающей блок данных, в который будет записана введенная звуковая информация. Формат этой структуры был описан в предыдущем разделе.
wSize
Поле wSize должно содержать размер структуры WAVEHDR
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_NOMEM
Для выполнения операции не хватает памяти
Подготовленный блок памяти следует передать драйверу устройства ввода, вызвав функцию waveInAddBuffer :
Функция waveInAddBuffer
UINT waveInAddBuffer( HWAVEIN hWaveIn, // идентификатор устройства LPWAVEHDR lpWaveInHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
lpWaveInHdr
Через параметр lpWaveInHdr приложение должно передать функции адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных
wSize
Поле wSize должно содержать размер структуры WAVEHDR
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение.
В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_UNPREPARED
Переданный блок данных не был подготовлен функцией waveOutPrepareHeader
Для того чтобы устройство ввода могло приступить к записи, его надо запустить, вызвав функцию waveInStart :
Функция waveInStart
UINT waveInStart(HWAVEIN hWaveIn); // идентификатор устройства
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Запись будет продолжаться до тех пор, пока не будет записан весь буфер или пока устройство ввода не будет остановлено функцией waveInStop :
Функция waveInStop
UINT waveInStop(HWAVEIN hWaveIn); // идентификатор устройства
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Если блок записан до конца или если запись блока остановлена, функция окна, идентификатор которой был указан при открытии устройства через параметр dwCallback, получит сообщение MM_WIM_DONE .
Через параметр wParam сообщения MM_WIM_DONE передается идентификатор устройства, которое было использовано для записи блока. Параметр lParam содержит адрес структуры WAVEHDR, соответствующей записанному блоку.
Если для обработки событий используется функция обратного вызова, она получит аналогичное сообщение с кодом WIM_DONE .
После того как приложение получило сообщение MM_WIM_DONE, оно должно передать блок функции waveInUnprepareHeader, затем разблокировать его функцией GlobalUnlock и при необходимости освободить функцией GlobalFree.
Приведем формат вызова функции waveInUnprepareHeader .
Функция waveInUnprepareHeader
UINT waveOutUnprepareHeader( HWAVEIN hWaveIn, // идентификатор устройства LPWAVEHDR lpWaveInHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR
Параметры функции:
hWaveIn
Идентификатор устройства вывода, полученный от функции waveInOpen при открытии устройства
lpWaveOutHdr
Адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных
wSize
Параметр wSize должно содержать размер структуры WAVEHDR
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_STILLPLAYING
Указанный блок все еще находится в очереди
После завершения работы с устройством ввода его необходимо закрыть, вызвав функцию waveInClose . Через параметр этой функции необходимо передать идентификатор закрываемого устройства ввода.
Функция waveInClose
UINT waveInClose( HWAVEIN hWaveIn); // идентификатор устройства
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
MMSYSERR_STILLPLAYING
Очередь данного устройства еще содержит блоки для записи
Запуск устройства
Для запуска устройства ввода используется рассмотренная нами ранее функция waveInStart . Если же нужно продолжить работу приостановленного устройства вывода, следует вызвать функцию waveOutRestart :
Функция waveOutRestart
UINT waveOutRestart( HWAVEOUT hWaveOut); // идентификатор устройства вывода
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Знакомство с мультимедиа
1.1.
1.2.
1.3.
1.4.
С тех пор как появились первые компьютеры, программисты и разработчики аппаратуры предпринимали многочисленные попытки научить их работать со звуком. Идея заставить компьютер разговаривать и понимать речь витает в воздухе, однако проблема распознавания речи далека от своего полного решения. Тем не менее, современные персональные компьютеры без особого труда превращают звук в цифры и наоборот.
Компьютеры Macintosh фирмы Apple имеют стандартное устройство, предназначенное для работы со звуком, для IBM PC и совместимых с ним персональных компьютеров нужно приобрести недорогой звуковой адаптер. С помощью встроенного или установленного дополнительно адаптера можно работать с монофонической или стереофонической звуковой информацией. Созданы многочисленные адаптеры, предназначенные для ввода и вывода видеоинформации, поступающей от видеокамеры, видеомагнитофона или аналогичного устройства.