Мультимедиа для Windows

         

Главное окно приложения MCIWAVER



Рис. 2.2. Главное окно приложения MCIWAVER

Если выбрать из главного меню приложения строку "Record!", включится запись. Вы сможете записать звуковой фрагмент длительностью до 60 секунд (максимальное время записи определяется константой MAXRECORDTIME, вы можете изменить значение этой константы в исходном тексте приложения). Для прерывания процесса записи в любой момент времени можно выбрать из меню строки "Stop!" или "Pause!". Результат записи всегда сохраняется в файле с именем recorded.wav, который создается в текущем каталоге.

С помощью строки "Open..." меню "File" можно выбрать wav-файл для воспроизведения. Путь к выбранному файлу отобразится в заголовке окна. Для прослушивания загруженного wav-файла воспользуйтесь строкой "Play!". Прослушивание можно прекратить (строка "Stop!") или временно приостановить (строка "Pause!"). Для продолжения прослушивания после временного останова выберите строку "Resume!".

Полоса просмотра служит для отображения текущей позиции при записи и воспроизведении. Над полосой просмотра выводится текущий режим работы приложения.

Основной файл приложения MCIWAWER приведен в листинге 2.7.

Листинг 2.7. Файл mciwaver\mciwaver.cpp // ------------------------------------------------ // Приложение MCIWAVE // Проигрывание и запись wav-файлов // с помощью интерфейса сообщений MCI // ------------------------------------------------ #define STRICT #include <windows.h> #include <windowsx.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop #include "mciwave.hpp" #include "mciwavio.hpp" // Идентификатор таймера #define BEEP_TIMER 1 // Идентификатор полосы просмотра #define ID_SCROLL 10 // Длина полосы просмотра #define SCROLL_SIZE 400


// Длительность записи в миллисекундах #define MAXRECORDTIME 60000L // Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); // Глобальные переменные int nMode = MODE_STOP; MMTIME mmtimeIn, mmtimeOut; BOOL fFileLoaded = FALSE; int nPosition; HWND hScroll; UINT wOutDeviceID; UINT wInDeviceID; BYTE szFileName[128]; DWORD dwFileSize; char const szClassName[] = "MCIWaveClass"; char const szWindowTitle[] = "MCIWaver"; 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; fFileLoaded = FALSE; wOutDeviceID = 0; wInDeviceID = 0; // Создаем таймер 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, "MCIWaver, v.1.0\n" "(C) Frolov A.V., 1994", "About MCIWaver", MB_OK | MB_ICONINFORMATION); return 0; } // ------------------------------------------------- // Строка "Open" меню "File" // ------------------------------------------------- case CM_FILEOPEN: { char szTitle[256]; // Загружаем новый файл if(!mciwioSelectFile(szFileName)) return 0; // Отображаем в заголовке окна путь к файлу lstrcpy(szTitle, szWindowTitle); lstrcat(szTitle, " - "); lstrcat(szTitle, szFileName); SetWindowText(hwnd, szTitle); // Если было запущено воспроизведение, // останавливаем его и закрываем устройство вывода if(wOutDeviceID) { mciwioStop(wOutDeviceID); mciwioClose(wOutDeviceID); wOutDeviceID = 0; // Новый режим nMode = MODE_STOP; } // Устанавливаем движок в начало полосы просмотра nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); // Устанавливаем флаг загрузки файла fFileLoaded = TRUE; return 0; } // ------------------------------------------------- // Строка "Play!" // Проигрывание загруженного wav-файла // ------------------------------------------------- case CM_CTLPLAY: { // Если файл загружен и не проигрывается, // запускаем проигрывание файла if((fFileLoaded == TRUE) && (nMode == MODE_STOP)) { // Новый режим nMode = MODE_PLAYING; // Перерисовываем окно для отображения строки, // соответствующей новому режиму InvalidateRect(hwnd, NULL, TRUE); // Открываем устройство wOutDeviceID = mciwioOpen((LPSTR)szFileName); // Проигрываем файл mciwioPlay(hwnd, wOutDeviceID); } return 0; } // ------------------------------------------------- // Строка "Record!" // Запись wav-файла // ------------------------------------------------- case CM_CTLRECORD: { // Запись возможна только из состояния останова if(nMode == MODE_STOP) { nMode = MODE_RECORDING; InvalidateRect(hwnd, NULL, TRUE); // Запись файла wInDeviceID = mciwioRecord(hwnd, MAXRECORDTIME); } return 0; } // ------------------------------------------------- // Строка "Stop!" // Останов проигрывания или записи wav-файла // ------------------------------------------------- case CM_CTLSTOP: { if(nMode == MODE_RECORDING || nMode == MODE_RECORDINGPAUSED) { // Останавливаем запись mciwioStop(wInDeviceID); } else if(nMode == MODE_PLAYING || nMode == MODE_PLAYINGPAUSED) { // Останавливаем проигрывание mciwioStop(wOutDeviceID); } // Устанавливаем движок в начало полосы просмотра 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) { // Останов записи mciwioStop(wInDeviceID); } else if(nMode == MODE_PLAYING) { // Временный останов проигрывания mciwioPause(wOutDeviceID); nMode = MODE_PLAYINGPAUSED; } InvalidateRect(hwnd, NULL, TRUE); return 0; } // ------------------------------------------------- // Строка "Resume!" // Продолжение проигрывания после останова // ------------------------------------------------- case CM_CTLRESUME: { if(nMode == MODE_PLAYINGPAUSED) { // Продолжаем проигрывание mciwioResume(wOutDeviceID); nMode = MODE_PLAYING; InvalidateRect(hwnd, NULL, TRUE); } return 0; } // ------------------------------------------------- // Строка "Exit" меню "File" // Завершение работы приложения // ------------------------------------------------- case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; } default: return 0; } } // ------------------------------------------------------------ // MM_MCINOTIFY // ------------------------------------------------------------ case MM_MCINOTIFY: { // Если находились в режиме записи, сохраняем файл if(wInDeviceID) { MCI_SAVE_PARMS mciSave; MCI_GENERIC_PARMS mcigen; DWORD dwrc; // Имя файла, в котором будет сохранен звуковой фрагмент mciSave.lpfilename = "recorded.wav"; dwrc=mciSendCommand(wInDeviceID, MCI_SAVE, MCI_SAVE_FILE | MCI_WAIT, (DWORD)(LPVOID)&mciSave); if(dwrc) { mciwioError(dwrc); } // Закрываем устройство записи dwrc = mciSendCommand(wInDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mciwioError(dwrc); } wInDeviceID = 0; } // Если находились в режиме воспроизведения, останавливаем // и закрываем устройство вывода else if(wOutDeviceID) { mciwioStop(wOutDeviceID); mciwioClose(wOutDeviceID); wOutDeviceID=0; } nMode = MODE_STOP; InvalidateRect(hwnd, NULL, TRUE); return 0; } // ------------------------------------------------------------ // WM_TIMER // Сообщение от таймера // ------------------------------------------------------------ case WM_TIMER: { MCI_STATUS_PARMS mciStatus; DWORD dwPos; // Режим записи if(nMode == MODE_RECORDING) { // Определяем текущую позицию внутри блока mciStatus.dwItem = MCI_STATUS_POSITION; mciSendCommand(wInDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatus); dwPos = mciStatus.dwReturn; // Вычисляем новое положение движка полосы просмотра nPosition = ((DWORD)SCROLL_SIZE * dwPos) / MAXRECORDTIME; // Ограничиваем пределы изменения текущей // позиции значениями от 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) { // Определяем текущую позицию внутри блока mciStatus.dwItem = MCI_STATUS_POSITION; mciSendCommand(wOutDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatus); dwPos = mciStatus.dwReturn; // Вычисляем новое положение движка полосы просмотра nPosition = ((DWORD)SCROLL_SIZE * dwPos) / dwFileSize; // Ограничиваем пределы изменения текущей // позиции значениями от 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) { mciwioStop(wInDeviceID); mciwioClose(wInDeviceID); } else if(fFileLoaded) { // Если находимся в режиме проигрывания, останавливаем // запись и закрываем устройство вывода if(nMode == MODE_PLAYING || nMode == MODE_PLAYINGPAUSED) { mciwioStop(wOutDeviceID); mciwioClose(wOutDeviceID); } nMode = MODE_STOP; } PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); }

При создании главного окна приложения обработчиком сообщения WM_CREATE выполняется инициализация глобальных переменных. Устанавливается текущий режим работы nMode (останов), флаг загрузки файла для проигрывания fFileLoaded, в переменные, соответствующие идентификаторам устройств ввода и вывода (wOutDeviceID и wInDeviceID) записываются нулевые значения.

Положение движка на полосе просмотра, отражающее текущую позицию при записи и воспроизведении, определяется по сообщениям таймера. На этапе инициализации создается таймер, который посылает сообщения каждую десятую долю секунды. Кроме того, создается полоса просмотра, устанавливается текущая позиция, диапазон изменений значений и начальное положение движка.

Во время обработки сообщения WM_PAINT в верхней части окна отображается текущий режим работы, для чего используется функция TextOut.

Когда вы выбираете wav-файл для воспроизведения, функция окна получает сообщение WM_COMMAND с параметром wParam, равным значению CM_FILEOPEN. Соответствующий обработчик загружает файл, вызывая функцию mciwioSelectFile. Эта функция определена в нашем приложении, ее исходный текст находится в файле mciwaveio.cpp (листинг 2.9). С помощью функции SetWindowText путь к файлу отображается в заголовке окна приложения.

Если в момент выбора нового файла приложение находилось в режиме проигрывания, следует остановить и закрыть устройство вывода. Для этого мы используем функции mciwioStop и mciwioClose, соответственно. После того, как устройство вывода закрыто, мы сбрасываем содержимое переменной wOutDeviceID (идентификатор устройства вывода). Кроме того, в переменную nMode записываем код нового состояния (останов).

В завершении выполняется установка движка полосы просмотра в начальное положение. В переменную fFileLoaded записывается значение TRUE (загружен файл для воспроизведения).

После выбора строки "Play!" проверяется текущий режим и флаг загрузки файла. Если файл загружен, и приложение находится в состоянии останова, можно начинать воспроизведение. В переменную nMode записывается константа MODE_PLAYING (воспроизведение), открывается устройство вывода (функция mciwioOpen) и запускается воспроизведение (функция mciwioPlay). Для того чтобы название нового режима было отображено на экране, выполняется перерисовка окна (функция InvalidateRect). Исходные тексты функций mciwioOpen и mciwioPlay находятся в файле mciwaveio.cpp (листинг 2.9).

Запись также можно запустить только из состояния останова. Для записи вызывается функция mciwioRecord (ее исходный текст также находится в листинге 2.9), которой в качестве второго параметра передается максимальная длительность записи в миллисекундах.

При выполнении команды останова (строка "Stop!" в меню приложения) анализируется текущий режим работы. Если приложение находится в режиме записи, вызывается функция mciwioStop (останов устройства), причем в качестве параметра ей передается идентификатор устройства ввода. Если же приложение находится в состоянии воспроизведения, вызывается эта же функция, но в качестве параметра ей передается идентификатор устройства вывода. Далее движок полосы просмотра устанавливается в начальное положение, а переменную nMode записывается код состояния останова.

В ответ на команду временного останова (строка "Pause!") выполняется полный останов записи или временный останов воспроизведения. В последнем случае вызывается функция mciwioPause, которой в качестве параметра передается идентификатор устройства вывода. В переменную nMode записывается значение константы MODE_PLAYINGPAUSED, которое обозначает временный останов воспроизведения.

Для продолжения воспроизведения после временного останова вызывается функция mciwioResume, которой в качестве параметра передается идентификатор устройства вывода.

Функция окна приложения обрабатывает сообщение MM_MCINOTIFY, поступающее после завершения записи или воспроизведения. Обработчик этого сообщения сохраняет записанные данные в том случае, если было открыто устройство ввода. Для этого он вызывает функцию mciSendCommand, передавая с ее помощью устройству ввода команду MCI_SAVE_FILE: mciSave.lpfilename = "recorded.wav"; dwrc=mciSendCommand(wInDeviceID, MCI_SAVE, MCI_SAVE_FILE | MCI_WAIT, (DWORD)(LPVOID)&mciSave);

После сохранения устройство ввода закрывается, в переменную wInDeviceID записывается нулевое значение: dwrc = mciSendCommand(wInDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mciwioError(dwrc); } wInDeviceID = 0;

Если сообщение MM_MCINOTIFY пришло при воспроизведении, устройство вывода останавливается и закрывается. В переменную wOutDeviceID записывается нулевое значение.

В любом случае обработчик сообщения MM_MCINOTIFY переводит приложение в режим останова.

Обработчик сообщения WM_TIMER предназначен для определения и отображения текущей позиции в режимах записи и воспроизведения.

В режиме записи текущая позиция определяется при помощи сообщения MCI_STATUS: mciStatus.dwItem = MCI_STATUS_POSITION; mciSendCommand(wInDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatus); dwPos = mciStatus.dwReturn;

По умолчанию для звукового устройства ввода/вывода устанавливается формат времени в миллисекундах. Поэтому в переменную dwPos будет записана текущая позиция в миллисекундах.

Далее обработчик вычисляет положение движка полосы просмотра, исходя из диапазона изменения значений полосы просмотра SCROLL_SIZE, максимального времени записи MAXRECORDTIME и текущей позиции dwPos: nPosition = ((DWORD)SCROLL_SIZE * dwPos) / MAXRECORDTIME;

Затем движок устанавливается в новое положение при помощи функции SetScrollPos.

В режиме воспроизведения новое положение движка просмотра вычисляется исходя из размера файла dwFileSize: nPosition = ((DWORD)SCROLL_SIZE * dwPos) / dwFileSize;

Размер файла в миллисекундах записывается в глобальную переменную dwFileSize функцией mciwioOpen при открытии файла.

При завершении работы приложения удаляются таймер и полоса просмотра. Далее, если приложение находилось в режиме записи, устройство ввода останавливается и закрывается. Аналогичные действия выполняются с устройством вывода в режиме воспроизведения.

Константы для работы с меню определены в файле mciwave.hpp (листинг 2.8).

Листинг 2.8. Файл mciwaver\mciwave.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

Файл mciwaveio.cpp (листинг 2.9) содержит функции для работы с интерфейсом сообщений MCI.

Листинг 2.9. Файл mciwaver\mciwavio.cpp #define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop #include "mciwavio.hpp" // Глобальные переменные extern int nMode; extern int nPosition; extern DWORD dwFileSize; //----------------------------------------------------- // mciwioOpen // Открытие устройства вывода //----------------------------------------------------- UINT mciwioOpen(LPSTR szFileName) { MCI_OPEN_PARMS mciOpen; MCI_STATUS_PARMS mciStatus; DWORD dwrc; DWORD dwFlags; // Готовим блок параметров mciOpen.lpstrDeviceType= (LPSTR)"waveaudio"; mciOpen.lpstrElementName = (LPSTR)szFileName; mciOpen.dwCallback = 0; mciOpen.wDeviceID = 0; mciOpen.wReserved0 = 0; mciOpen.lpstrAlias = NULL; // Устанавливаем флаги dwFlags = MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_WAIT; // Открываем устройство dwrc = mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD)(LPVOID)&mciOpen); if(dwrc) { mciwioError(dwrc); return 0; } // Если устройство открыто успешно, определяем // длительность звучания в миллисекундах else { mciStatus.dwItem = MCI_STATUS_LENGTH; dwrc = mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&mciStatus); if(dwrc) { mciwioError(dwrc); return 0; } // Сохраняем длительность звучания в глобальной // переменной и возвращаем идентификатор устройства вывода dwFileSize = mciStatus.dwReturn; return mciOpen.wDeviceID; } } //----------------------------------------------------- // mciwioPlay // Проигрывание загруженного wav-файла //----------------------------------------------------- DWORD mciwioPlay(HWND hwnd, UINT wDeviceID) { MCI_PLAY_PARMS mciPlayParms; DWORD dwrc; // Позиционирование на начало фрагмента dwrc = mciSendCommand(wDeviceID, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, NULL); // Идентификатор окна, функция которого получит // сообщение MM_MCINOTIFY mciPlayParms.dwCallback = (DWORD)hwnd; // Запускаем проигрывание dwrc=mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&mciPlayParms); return dwrc; } //----------------------------------------------------- // mciwioStop // Останов проигрывания загруженного wav-файла //----------------------------------------------------- DWORD mciwioStop(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc; dwrc = mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mciwioError(dwrc); } return dwrc; } //----------------------------------------------------- // mciwioResume // Проигрывание после временного останова //----------------------------------------------------- DWORD mciwioResume(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc; dwrc = mciSendCommand(wDeviceID, MCI_RESUME, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mciwioError(dwrc); } return dwrc; } //----------------------------------------------------- // mciwioPause // Временный останов проигрывания загруженного wav-файла //----------------------------------------------------- DWORD mciwioPause(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc; dwrc = mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mciwioError(dwrc); } return dwrc; } //----------------------------------------------------- // mciwioSelectFile // Выбор wav-файла //----------------------------------------------------- BOOL mciwioSelectFile(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; } //----------------------------------------------------- // mciwioClose // Закрытие устройства вывода //----------------------------------------------------- void mciwioClose(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc; dwrc = mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mciwioError(dwrc); return; } } //----------------------------------------------------- // mciwioError // Обработка ошибок //----------------------------------------------------- void mciwioError(DWORD dwrc) { BYTE szBuf[MAXERRORLENGTH]; if(mciGetErrorString(dwrc, (LPSTR)szBuf, MAXERRORLENGTH)) MessageBox(NULL, szBuf, "MCIWAVER Error", MB_ICONEXCLAMATION); else MessageBox(NULL, "Неизвестная ошибка", "MCIWAVER Error", MB_ICONEXCLAMATION); } //----------------------------------------------------- // mciwioRecord // Запись wav-файла //----------------------------------------------------- WORD mciwioRecord(HWND hwnd, DWORD dwMSec) { MCI_RECORD_PARMS mciRecordParms; MCI_OPEN_PARMS mciOpen; DWORD dwrc; DWORD dwFlags; WORD wInDeviceID; // Готовим блок параметров // для команды открытия устройства mciOpen.lpstrDeviceType = (LPSTR)"waveaudio"; mciOpen.lpstrElementName = (LPSTR)""; mciOpen.dwCallback = 0; mciOpen.wDeviceID = 0; mciOpen.wReserved0 = 0; mciOpen.lpstrAlias = NULL; // Устанавливаем флаги dwFlags = MCI_OPEN_TYPE| MCI_OPEN_ELEMENT | MCI_WAIT; // Открываем устройство ввода dwrc = mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD)(LPVOID)&mciOpen); if(dwrc) { mciwioError(dwrc); return 0; } else { // В случае успеха сохраняем идентификатор // устройства ввода в глобальной переменной wInDeviceID = mciOpen.wDeviceID; } // Готовим блок параметров для команды записи mciRecordParms.dwTo = dwMSec; mciRecordParms.dwCallback = (DWORD)hwnd; // Запускаем запись dwrc=mciSendCommand(wInDeviceID, MCI_RECORD, MCI_NOTIFY | MCI_TO, (DWORD)(LPVOID)&mciRecordParms); if(dwrc) { mciwioError(dwrc); return 0; } return wInDeviceID; }

Функция mciwioOpen открывает устройство вывода, посылая ему сообщение MCI_OPEN. В блоке параметров мы указываем тип устройства ("waveaudio") и путь к wav-файлу szFileName. Так как тип устройства указан в виде текстовой строки, мы используем флаг MCI_OPEN_TYPE. Необходимо также указать флаг MCI_OPEN_ELEMENT, так как устройство будет работать с файлом. Для того чтобы функция вернула управление только после открытия устройства, используется флаг MCI_WAIT.

Если при открытии устройства произошла ошибка, функция mciSendCommand вернет ненулевой код ошибки. Мы передаем этот код функции mciwioError, которая выводит текстовое описание ошибки на экран.

После успешного открытия устройства вывода определяется размер файла (длительность звучания файла в миллисекундах). Для этого используется сообщение с кодом MCI_STATUS. Полученный размер сохраняется в глобальной переменной dwFileSize. Он будет использован для вычисления положения движка полосы просмотра, соответствующего текущей позиции при воспроизведении.

Функция mciwioPlay предназначена для проигрывания загруженного файла с самого начала. Перед запуском воспроизведения текущая позиция устанавливается на начало, для чего используется сообщение MCI_SEEK с флагом MCI_SEEK_TO_START: dwrc = mciSendCommand(wDeviceID, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, NULL);

Далее готовится блок параметров для сообщения MCI_PLAY. В поле dwCallback записывается идентификатор главного окна приложения (из глобальной переменной hwnd). Это окно получит извещение о завершении проигрывания в виде сообщения MM_MCINOTIFY.

Функция mciwioStop посылает устройству, идентификатор которого передается ей через параметр wDeviceID, сообщение MCI_STOP.

Функция mciwioPause предназначена для временного останова проигрывания wav-файла. Она посылает устройству, идентификатор которого передается ей через параметр wDeviceID, сообщение MCI_PAUSE.

С помощью функции mciwioResume можно возобновить прерванный функцией mciwioPause процесс записи или воспроизведения. Эта функция посылает устройству сообщение MCI_RESUME.

Функция mciwioSelectFile предназначена для выбора wav-файла. Она пользуется стандартной диалоговой панелью выбора файла и функцией GetOpenFileName, определенной в DLL-библиотеке commdlg.dll. Мы уже пользовались этой функцией в предыдущих томах "Библиотеки системного программиста". Путь к выбранному файлу копируется в буфер, адрес которого передается функции mciwioSelectFile в качестве единственного параметра.

Функция mciwioClose закрывает устройство ввода или вывода, посылая ему сообщение MCI_CLOSE.

Запись файла инициируется функцией mciwioRecord. В качестве первого параметра этой функции передается идентификатор окна, которое получит сообщение MM_MCINOTIFY после завершения процесса записи. Второй параметр определяет максимальную длительность записи в миллисекундах.

Перед началом записи открывается устройство ввода "waveaudio", причем в качестве имени файла используется пустая строка: mciOpen.lpstrDeviceType = (LPSTR)"waveaudio"; mciOpen.lpstrElementName = (LPSTR)""; mciOpen.dwCallback = 0; mciOpen.wDeviceID = 0; mciOpen.wReserved0 = 0; mciOpen.lpstrAlias = NULL; dwFlags = MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_WAIT; dwrc = mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD)(LPVOID)&mciOpen); if(dwrc) { mciwioError(dwrc); return 0; } else { wInDeviceID = mciOpen.wDeviceID; }

Далее готовится блок параметров для записи и посылается сообщение MCI_RECORD: mciRecordParms.dwTo = dwMSec; mciRecordParms.dwCallback = (DWORD)hwnd; dwrc=mciSendCommand(wInDeviceID, MCI_RECORD, MCI_NOTIFY | MCI_TO, (DWORD)(LPVOID)&mciRecordParms);

При этом указывается конечная позиция для записи dwTo и идентификатор окна, которое получит извещение о завершении процесса записи dwCallback. Для того чтобы перечисленные параметры были приняты во внимание, устанавливаются флаги MCI_TO и MCI_NOTIFY.

Константы и прототипы функций для файла mciwavio.cpp находятся в файле mciwaveio.hpp (листинг 2.10).

Листинг 2.10. Файл mciwaver\mciwavio.hpp #include <windows.h> #include <mmsystem.h> #define MODE_STOP 0 #define MODE_PLAYING 1 #define MODE_RECORDING 2 #define MODE_PLAYINGPAUSED 3 #define MODE_RECORDINGPAUSED 4 UINT mciwioOpen(LPSTR szFileName); BOOL mciwioSelectFile(LPSTR lpszFileName); void mciwioClose(UINT wDeviceID); DWORD mciwioPlay(HWND hwnd, UINT wDeviceID); void mciwioError(DWORD dwrc); DWORD mciwioStop(UINT wDeviceID); DWORD mciwioPause(UINT wDeviceID); DWORD mciwioResume(UINT wDeviceID); WORD mciwioRecord(HWND hwnd, DWORD dwMSec);

Файл описания ресурсов (листинг 2.11) содержит определение пиктограммы и шаблон меню.

Листинг 2.11. Файл mciwaver\mciwaver.rc #include "mciwave.hpp" APPICON ICON "mciwaver.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

Файл определения модуля для приложения MCIWAVER приведен в листинге 2.12.

Листинг 2.12. Файл mciwaver\mciwaver.def NAME MCIWAVER DESCRIPTION 'Приложение MCIWAVER, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 10240 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple



Содержание    Назад    Вперед