Для передачи управляющей строки устройству чтения CD ROM вы должны использовать функцию mciSendString . Вам могут потребоваться и другие функции, описанные во второй главе и предназначенные для работы с интерфейсом MCI.
Как правило, большинство приложений, составленных на языках программирования C и C++, управляют устройством чтения CD ROM с помощью интерфейса управляющих сообщений MCI. Напомним, что приложения, использующие этот интерфейс, посылают устройствам мультимедиа управляющие сообщения с помощью функции mciSendCommand .
Рассмотрим особенности команд MCI, предназначенных для устройства чтения компакт-дисков.
3.1. Интерфейс управляющих строк MCI
3.2. Интерфейс управляющих сообщений MCI
В этой главе мы расскажем вам об использовании устройства чтения CD ROM для проигрывания звуковых компакт-дисков. Приложения Windows работают с этим устройством через интерфейс MCI, которым вы уже умеете пользоваться для записи и воспроизведения wav-файлов. Так как объем книги ограничен, мы не будем подробно рассказывать об использовании всех команд MCI для управления устройством чтения CD ROM, ограничившись только особенностями.
Для чего может потребоваться прослушивание звуковых компакт-дисков при помощи такого дорогостоящего устройства, как компьютер, оснащенный средствами мультимедиа? Ведь стоимость обычного проигрывателя компакт-дисков не превышает нескольких сотен долларов, что просто несравнимо со стоимостью компьютера!
Наиболее очевидные области применения приложений, умеющих проигрывать звуковые компакт-диски - рекламные и демонстрационные ролики с музыкальным и речевым сопровождением, игры, автоматизированные музыкальные центры.
Не вдаваясь в технические подробности, заметим, что данные на компакт-диске записаны вдоль одной гигантской спирали. В первом приближении можно считать, что на музыкальных дисках эта спираль разбита на несколько участков, или дорожек, каждая из которых содержит отдельную звуковую запись (например, музыкальное произведение).
Приложение может устанавливать лазерное устройство чтения в произвольное место спирали, причем драйвер обеспечивает позиционирование в режиме прямого доступа как на начало любой дорожки, так и в произвольную позицию внутри дорожки. К сожалению, процесс позиционирования занимает много времени, около 0,5 секунды, поэтому (а также из-за небольшой скорости передачи, составляющей 150-300 Кбайт в секунду) устройства чтения CD ROM нельзя называть быстродействующими.
Как мы уже говорили в первой главе, устройство чтения CD ROM имеет два звуковых выхода. Один из них обычно расположен на лицевой панели и предназначен для подключения головных телефонов (там же находится и регулятор громкости). Второй выведен на заднюю панель и подключается кабелем к входу звукового адаптера, специально предназначенному для этого. Программное обеспечение микшерского пульта, поставляющееся вместе со звуковым адаптером, позволяет подключать выход устройства чтения CD ROM ко входу усилителя или аналого-цифрового преобразователя, поэтому приложения мультимедиа могут выполнять не только проигрывание звуковых компакт-дисков с прослушиванием через громкоговорители, но и синхронную запись wav-файлов.
Вы можете работать с устройством чтения CD ROM при помощи интерфейса управляющих строк MCI или интерфейса управляющих сообщений.
С помощью команд этой группы можно выполнять проигрывание дорожек звукового компакт-диска, временный или полный останов, позиционирование и т. д.
Команда break не имеет никаких особенностей. Она позволяет определить код виртуальной клавиши, предназначенной для прерывания процесса выполнения команды. По умолчанию используется комбинация клавиш <Control+Break>.
Для команды set можно указывать следующие параметры: audio all off
Отключение звукового выхода audio all on
Включение звукового выхода audio left off
Отключение левого канала audio left on
Включение левого канала audio right off
Отключение правого канала audio right on
Включение правого канала door closed
Загрузка компакт-диска и фиксирование его в устройстве. Этот параметр может поддерживаться не всеми устройствами (так же, как и параметр door open) door open
Извлечение компакт-диска time format milliseconds
В качестве единицы измерения при позиционировании используются миллисекунды. Строку milliseconds можно также указывать как ms time format msf
В качестве единицы измерения при позиционировании используются минуты, секунды и фреймы. В качестве разделителя минут секунд и фреймов используется двоеточие. Этот формат используется по умолчанию time format tmsf
В качестве единицы измерения при позиционировании используются дорожки, минуты, секунды и фреймы
С помощью команды MCI_BREAK указывается виртуальный код клавиши, с помощью которой можно прервать выполнение операции. По умолчанию используется комбинация клавиш <Control+Break>.
Эта команда закрывает устройство. Она также не имеет никаких особенностей.
С помощью команды MCI_GETDEVCAPS можно определить возможности устройства чтения компакт-дисков. Для нее используется блок параметров в формате структуры MCI_GETDEVCAPS_PARMS , определенной в файле mmsystem.h следующим образом: typedef struct tagMCI_GETDEVCAPS_PARMS { DWORD dwCallback; DWORD dwReturn; DWORD dwItem; } MCI_GETDEVCAPS_PARMS; typedef MCI_GETDEVCAPS_PARMS FAR * LPMCI_GETDEVCAPS_PARMS;
В поле dwReturn после возврата из функции mciSendCommand будет записано значение требуемого параметра. Код нужного параметра следует записать в поле dwItem перед вызовом функции mciSendCommand.
Приведем возможные значения параметра dwItem:
Значение параметра dwItem | Описание |
MCI_GETDEVCAPS_CAN_EJECT | Если устройство может выталкивать компакт-диск, после возврата из функции mciSendCommand в поле dwReturn будет ненулевое значение TRUE |
MCI_GETDEVCAPS_CAN_PLAY | Устройство может проигрывать |
MCI_GETDEVCAPS_CAN_RECORD | Устройство может записывать |
MCI_GETDEVCAPS_CAN_SAVE | Устройство может сохранять записанные данные в файле |
MCI_GETDEVCAPS_COMPOUND_DEVICE | Устройство может работать с файлами |
MCI_GETDEVCAPS_DEVICE_TYPE | Требуется определить тип устройства |
MCI_GETDEVCAPS_HAS_AUDIO | Устройство имеет звуковой выход |
MCI_GETDEVCAPS_HAS_VIDEO | Устройство имеет видеовыход |
MCI_GETDEVCAPS_USES_FILES | При открытии устройства требуется указывать имя файла |
С помощью этой команды можно получить информацию об устройстве чтения CD ROM в виде текстовой строки.
Используется блок параметров в формате структуры MCI_INFO_PARMS : typedef struct tagMCI_INFO_PARMS { DWORD dwCallback; LPSTR lpstrReturn; DWORD dwRetSize; } MCI_INFO_PARMS; typedef MCI_INFO_PARMS FAR * LPMCI_INFO_PARMS;
Поле lpstrReturn должно содержать дальний указатель на буфер, в который будет записана строка информации. Размер этого буфера следует передать через поле dwRetSize.
Приведем набор флагов для команды MCI_INFO, допустимых к использованию при работе с устройством чтения компакт-дисков:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса |
MCI_INFO_PRODUCT | Требуется получить описание аппаратуры устройства |
Команда MCI_OPEN не имеет никаких дополнительных возможностей и используется как обычно. Приложение должно подготовить структуру MCI_OPEN_PARMS и передать ее адрес через четвертый параметр функции mciSendCommand.
Поле lpstrDeviceType структуры MCI_OPEN_PARMS содержит указатель на строку имени устройства, или константный идентификатор устройства. Для устройства чтения CD ROM вы можете указать имя "cdaudio " или константу MCI_DEVTYPE_CD_AUDIO .
Параметр lpstrElementName не используется, так как устройство чтения компакт-дисков не работает с файлами.
Приведенный ниже фрагмент кода открывает устройство чтения компакт-дисков: MCIOpen.lpstrDeviceType = (LPSTR)MCI_DEVTYPE_CD_AUDIO; dwrc = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&MCIOpen);
После выполнения этого фрагмента в переменную dwrc будет записан код результата завершения. При успешном завершении в поле wDeviceID структуры mciOpen будет находиться идентификатор открытого устройства.
Команда MCI_PAUSE останавливает выполнение операции проигрывания звукового компакт-диска и действует точно так же, как и команда MCI_STOP.
Команда MCI_PLAY не имеет расширений для устройства чтения CD ROM.
Команда MCI_SEEK позволяет выполнять позиционирование. Она не имеет расширений, специально предназначенных для устройства чтения CD ROM.
Команда MCI_SET предназначена для установки режима работы устройства. Вместе с этой командой используется блок параметров в формате структуры MCI_SET_PARMS : typedef struct tagMCI_SET_PARMS { DWORD dwCallback; DWORD dwTimeFormat; DWORD dwAudio; } MCI_SET_PARMS; typedef MCI_SET_PARMS FAR *LPMCI_SET_PARMS;
Поле dwTimeFormat определяет формат времени для устройства, поле dwAudio определяет выходной канал.
Приведем список флагов, которые используются вместе с командой MCI_SET:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса |
MCI_SET_AUDIO | Включение или выключение каналов, используется вместе с флагами MCI_SET_ON и MCI_SET_OFF. Поле dwAudio содержит номера канала. Дополнительно можно указать следующие константы:MCI_SET_AUDIO_ALL все каналыMCI_SET_AUDIO_LEFT левый каналMCI_SET_AUDIO_RIGHT правый канал |
MCI_SET_DOOR_CLOSED | По этой команде устройство защелкивает компакт-диск |
MCI_SET_DOOR_OPEN | Освобождение носителя данных |
MCI_SET_VIDEO | Включение или выключение видеосигнала, используется вместе с флагами MCI_SET_ON и MCI_SET_OFF |
MCI_SET_TIME_FORMAT | Установить формат времени. Используется вместе со следующими константами:MCI_FORMAT_MSF минуты, секунды, фреймы;MCI_FORMAT_MILLISECONDS в миллисекундах;MCI_FORMAT_TMSF треки, минуты, секунды, фреймы |
MCI_SET_ON | Включение заданного канала |
MCI_SET_OFF | Выключение заданного канала |
При использовании формата времени MCI_FORMAT_MSF старший байт старшего слова поля dwTimeFormat не используется, младший содержит номер фрейма. Старший байт младшего слова содержит секунды, младший - минуты. Формат времени MCI_FORMAT_TMSF аналогичный, за исключением того, что старший байт старшего слова содержит номер дорожки.
Команда MCI_STATUS используется для определения текущего состояния устройства.
Формат соответствующего блока параметров описывается структурой MCI_STATUS_PARMS : typedef struct tagMCI_STATUS_PARMS { DWORD dwCallback; DWORD dwReturn; DWORD dwItem; DWORD dwTrack; } MCI_STATUS_PARMS; typedef MCI_STATUS_PARMS FAR * LPMCI_STATUS_PARMS;
Через поле dwReturn передается возвращаемая информация. Вид запрашиваемой информации определяется содержимым поля dwItem. Для устройства чтения компакт-дисков в поле dwTrack можно указать размер или номер дорожки.
Приведем возможные значения параметра dwItem:
Значение параметра dwItem | Описание получаемой информации |
MCI_STATUS_CURRENT_TRACK | Номер текущей дорожки |
MCI_STATUS_LENGTH | Общий размер всех дорожек компакт-диска |
MCI_STATUS_MODE | Текущий режим устройства. Может иметь следующие значения:MCI_MODE_NOT_READY не готово;MCI_MODE_PAUSE пауза;MCI_MODE_PLAY проигрывание;MCI_MODE_STOP останов;MCI_MODE_OPEN открывание;MCI_MODE_RECORD запись;MCI_MODE_SEEK позиционирование |
MCI_STATUS_NUMBER_OF_TRACKS | Общее количество дорожек, которые можно проиграть |
MCI_STATUS_POSITION | Текущая позиция |
MCI_STATUS_READY | Если устройство готово, возвращается значение TRUE, в противном случае - FALSE |
MCI_STATUS_TIME_FORMAT | Текущий формат времени. Может иметь следующие значения:MCI_FORMAT_MILLISECONDS MCI_FORMAT_MSF MCI_FORMAT_TMSF |
MCI_STATUS_START | Начальная позиция |
MCI_STATUS_TRACK | В поле dwTrack записывается либо начальная позиция заданной дорожки (если дополнительно используется MCI_STATUS_POSITION), либо размер дорожки (если дополнительно используется MCI_STATUS_LENGTH) |
MCI_STATUS_MEDIA_PRESENT | Возвращается TRUE, если компакт-диск вставлен в устройство |
Команда MCI_STOP останавливает выполнение проигрывания компакт-диска.
С помощью этой команды можно получить системную информацию об устройстве в виде текстовой строки. Команда MCI_SYSINFO не имеет расширений для устройства чтения компакт-дисков.
Перед началом работы с устройством вы должны его открыть, передав управляющую строку open . При этом вы должны указать имя устройства как cdaudio (можно использовать алиас): open cdaudio alias cd wait
Так как операция открытия устройства чтения CD ROM может выполняться в течении нескольких секунд, имеет смысл перед продолжением работы приложения дождаться ее завершения, указав параметр wait.
При открытии драйвера CD ROM можно указать параметр shareable , в этом случае устройством смогут пользоваться одновременно несколько приложений (если они все откроют устройство с параметром shareable).
Драйвер устройства CD ROM не работает с файлами, поэтому в управляющей строке open путь к файлу не указывается.
Команда close особенностей не имеет. В качестве параметра вы должны указать имя устройства cdaudio или алиас (альтернативное имя), если устройство было открыто с использованием алиаса: close cd
Временный останов (пауза). Для устройства чтения CD ROM эта команда работает как команда полного останова stop, при этом команда продолжения работы после временного останова resume не поддерживается pause device_id
Команда play предназначена для запуска проигрывания. Она имеет следующий формат: play device_id [from position [to position]] [notify] [wait]
Если не указан параметр from position, проигрывание начинается с текущей позиции. Сразу после открытия текущая позиция устанавливается в начало первой дорожки компакт-диска. Параметр to position позволяет указать конечную позицию, при достижении которого проигрывание прекращается. Перед использованием параметров from и to необходимо установить формат для позиционирования при помощи команды set.
В качестве примера приведем последовательность команд, с помощью которой выполняется проигрывание 11-ой дорожки звукового компакт-диска: open cdaudio alias cd wait set cd time format tmsf wait play cd from 11 to 12 wait close cd
Вы можете попробовать эту последовательность команд с помощью приложения MCITEST из Microsoft SDK for Windows 3.1. Загрузочный модуль приложения MCITEST есть на дискете, которая продается вместе с книгой.
Если вам нужно указать позицию внутри дорожки, используйте более полный формат времени с указанием минут и секунд. Например, следующая команда выполнит проигрывание фрагмента 11-ой дорожки, начало которого отстоит на 10 секунд от начала дорожки, а длительность составляет 5 секунд: play cd from 11:0:10 to 11:0:15 wait
Если вы будете разрабатывать проигрыватель звуковых компакт-дисков, то можете взять за основу приложение MCICDPL (рис. 3.1), которое работает с устройством чтения CD-ROM при помощи управляющих сообщений MCI.
Исходный текст приложения представлен в листинге 3.1.
Листинг 3.1. Файл mcicdpl/mcicdpl.cpp // ---------------------------------------- // Проигрыватель звуковых компакт-дисков // ---------------------------------------- #define STRICT #include <windows.h> #include <mmsystem.h> #include <mem.h> #include <stdlib.h> #include "mcicdpl.hpp" #define CD_EMPTY 0 #define CD_READY 1 #define CD_PLAYING 2 #define CD_PAUSED 3 // Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); void mciwioError(DWORD dwrc); void Play(HWND hwnd, UINT nTrack); // Имя класса окна char const szClassName[] = "MCICDP"; // Заголовок окна char const szWindowTitle[] = "MCI CD Player"; HINSTANCE hInst; DWORD dwrc; UINT nTimerID; MCI_OPEN_PARMS MCIOpen; MCI_SET_PARMS MCISet; MCI_STATUS_PARMS MCIStatus; MCI_PLAY_PARMS MCIPlay; BOOL bMediaPresent = FALSE; BOOL bPaused = FALSE; UINT nMode = 0; UINT nCurTrack = 0; UINT nTrackCnt = 0; HWND hwndCurTrack = NULL; // ===================================== // Функция 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; // Открываем устройство чтения компакт-дисков MCIOpen.lpstrDeviceType = (LPSTR)MCI_DEVTYPE_CD_AUDIO; dwrc = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&MCIOpen); if(dwrc) { mciwioError(dwrc); return -1; } // Устанавливаем формат времени MCISet.dwTimeFormat = MCI_FORMAT_TMSF; dwrc = mciSendCommand(MCIOpen.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&MCISet); if(dwrc) { mciwioError(dwrc); return -1; } // Создаем диалоговую панель вместо главного окна hwnd = CreateDialog(hInstance, szClassName, 0, NULL); // Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE; // Определяем идентификатор поля, которое используется // для отображения номера текущей дорожки hwndCurTrack = GetDlgItem(hwnd, IDT_CURTRACK); // Рисуем главное окно ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // Запускаем цикл обработки сообщений while(GetMessage(&msg, NULL, 0, 0)) { if((hwnd == 0) || (!IsDialogMessage(hwnd, &msg))) DispatchMessage(&msg); } return msg.wParam; } // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // ===================================== BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна memset(&wc, 0, sizeof(wc)); wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APPICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = (LPSTR)NULL; wc.lpszClassName = (LPSTR)szClassName; // Регистрация класса aWndClass = RegisterClass(&wc); return (aWndClass != 0); } // ===================================== // Функция WndProc // ===================================== LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { // --------------------------------------- // Обработчик сообщения WM_CREATE // --------------------------------------- case WM_CREATE: { // Создаем таймер, который нужен для периодического // определения состояния устройства чтения CD nTimerID = SetTimer(hwnd, 1, 1000, NULL); return 0; } // --------------------------------------- // Обработчик сообщения WM_COMMAND // --------------------------------------- case WM_COMMAND: { switch(wParam) { // Запуск режима проигрывания case IDB_PLAY: { // Если в проигрывателе есть компакт-диск, // запускаем проигрывание if(bMediaPresent) Play(hwnd, 1); return 0; } // Останов проигрывания case IDB_STOP: { if(bMediaPresent) { bPaused = FALSE; nCurTrack = 0; mciSendCommand(MCIOpen.wDeviceID, MCI_STOP, NULL, NULL); } return 0; } // Временный останов проигрывания case IDB_PAUSE: { if(bMediaPresent) { if(!bPaused) { bPaused = TRUE; mciSendCommand(MCIOpen.wDeviceID, MCI_PAUSE, NULL, NULL); } } return 0; } // Продолжение проигрывания после // временного останова case IDB_RESUME: { if(bMediaPresent) { if(bPaused) { bPaused = FALSE; MCIPlay.dwCallback = (DWORD)hwnd; mciSendCommand(MCIOpen.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&MCIPlay); } } return 0; } // Позиционирование на следующую дорожку case IDB_NEXT: { if(bMediaPresent) { UINT nNewTrack; // Если текущая дорожка - последняя, // начинаем проигрывание с первой дорожки. // Если нет - проигрываем следующую дорожку if(nCurTrack == nTrackCnt) nNewTrack = 1; else nNewTrack = nCurTrack + 1; Play(hwnd, nNewTrack); } return 0; } // Позиционирование на предыдущую дорожку case IDB_PREV: { if(bMediaPresent) { UINT nNewTrack; // Если текущая дорожка - первая, // проигрываем последнюю дорожку if(nCurTrack <= 1) nNewTrack = nTrackCnt; else nNewTrack = nCurTrack - 1; Play(hwnd, nNewTrack); } return 0; } // Завершаем работу приложения case IDOK: case IDCANCEL: { SendMessage(hwnd, WM_CLOSE, 0, 0L); return 0; } // Выполняем команду извлечения диска из // устройства чтения case IDB_EJECT: { mciSendCommand(MCIOpen.wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, NULL); return 0; } } } // --------------------------------------- // Обработчик сообщения WM_TIMER // --------------------------------------- case WM_TIMER: { UINT nCurMode; // Если окно свернуто в пиктограмму, ничего не делаем, // чтобы не снижать производительность системы if(IsIconic(hwnd)) return 0; // Определяем текущее состояние проигрывателя CD MCIStatus.dwItem = MCI_STATUS_MODE; mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&MCIStatus); // Проверяем, готово ли устройство чтения к работе if((MCIStatus.dwReturn == MCI_MODE_NOT_READY) || (MCIStatus.dwReturn == MCI_MODE_OPEN)) { // Устройство не готово nCurMode = CD_EMPTY; } else if((MCIStatus.dwReturn == MCI_MODE_STOP) && bPaused) { // Устройство остановлено nCurMode = CD_PAUSED; } else if(MCIStatus.dwReturn == MCI_MODE_PLAY) { // Устройство находится в режиме проигрывания nCurMode = CD_PLAYING; } else { // Устройство готово nCurMode = CD_READY; } // Если с момента последней проверки произошло // изменение режима, записываем код нового режима if(nMode != nCurMode) { nMode = nCurMode; } // Проверяем, вставлен ли компакт-диск MCIStatus.dwItem = MCI_STATUS_MEDIA_PRESENT; mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&MCIStatus); // Если компакт-диск вставлен, определяем // количество звуковых дорожек if((!bMediaPresent) && MCIStatus.dwReturn) { bMediaPresent = TRUE; bPaused = FALSE; nCurTrack = 0; MCIStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&MCIStatus); nTrackCnt = MCIStatus.dwReturn; } // Если компакт-диск не вставлен, сбрасываем // номер текущей дорожке в поле диалоговой панели else if((bMediaPresent) && !MCIStatus.dwReturn) { bMediaPresent = FALSE; bPaused = FALSE; SetWindowText(hwndCurTrack, (LPSTR)""); } // Если приложение находится в режиме проигрывания, // определяем номер текущей дорожки if(nCurMode == CD_PLAYING) { // Определяем текущую позицию MCIStatus.dwItem = MCI_STATUS_POSITION; mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&MCIStatus); // Если номер дорожки изменился, отображаем новое // значение в соответствующем поле диалоговой панели if(nCurTrack != (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn)) { BYTE szBuf[20]; nCurTrack = (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn); SetWindowText(hwndCurTrack, itoa(nCurTrack, szBuf, 10)); } } return 0; } // --------------------------------------- // Обработчик сообщения MM_MCINOTIFY // --------------------------------------- case MM_MCINOTIFY: { if(wParam == MCI_NOTIFY_SUCCESSFUL) { if(bMediaPresent) Play(hwnd, 1); } return 0; } // --------------------------------------- // Обработчик сообщения WM_CLOSE // --------------------------------------- case WM_CLOSE: { DestroyWindow(hwnd); return 0; } // --------------------------------------- // Обработчик сообщения WM_DESTROY // --------------------------------------- case WM_DESTROY: { // Закрываем устройство чтения компакт-дисков dwrc = mciSendCommand(MCIOpen.wDeviceID, MCI_CLOSE, NULL, NULL); if(dwrc) mciwioError(dwrc); // Уничтожаем таймер KillTimer(hwnd, nTimerID); PostQuitMessage(0); return 0; } } return DefDlgProc(hwnd, msg, wParam, lParam); } //----------------------------------------------------- // mciwioError // Обработка ошибок //----------------------------------------------------- void mciwioError(DWORD dwrc) { BYTE szBuf[MAXERRORLENGTH]; if(mciGetErrorString(dwrc, (LPSTR)szBuf, MAXERRORLENGTH)) MessageBox(NULL, szBuf, "MCIWAVE Error", MB_ICONEXCLAMATION); else MessageBox(NULL, "Неизвестная ошибка", "MCIWAVE Error", MB_ICONEXCLAMATION); } //----------------------------------------------------- // Play // Запуск проигрывания дорожки //----------------------------------------------------- void Play(HWND hwnd, UINT nTrack) { bPaused = FALSE; MCIPlay.dwCallback = (DWORD)hwnd; MCIPlay.dwFrom = MCI_MAKE_TMSF(nTrack, 0, 0, 0); dwrc = mciSendCommand(MCIOpen.wDeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD)(LPVOID)&MCIPlay); if(dwrc) { mciwioError(dwrc); return; } }
Особенностью данного приложения является отсутствие главного окна - его роль выполняет диалоговая панель.
Сразу после запуска приложение пытается открыть устройство чтения компакт-дисков, и если в системе нет соответствующего драйвера, приложение завершает свою работу с сообщением об ошибке.
Далее устанавливается формат времени MCI_FORMAT_TMSF, так как приложение будет выполнять позиционирование по дорожкам компакт-диска.
Далее с помощью функции CreateDialog создается диалоговая панель, при этом указывается зарегистрированный приложением класс окна szClassName (строка "MCICDP"): hwnd = CreateDialog(hInstance, szClassName, 0, NULL);
Для того чтобы функция окна могла получать сообщения от диалоговой панели, в описании шаблона диалоговой панели используется оператор CLASS : CLASS "MCICDP"
В шаблоне предусмотрен статический орган управления, который имеет идентификатор IDT_CURTRACK и используется для отображения номера текущего трека. Перед запуском цикла обработки сообщений приложение определяет его идентификатор и сохраняет в переменной hwndCurTrack: hwndCurTrack = GetDlgItem(hwnd, IDT_CURTRACK);
Затем диалоговая панель отображается на экране и запускается цикл обработки сообщений, в котором вызывается функция IsDialogMessage: while(GetMessage(&msg, NULL, 0, 0)) { if((hwnd == 0) || (!IsDialogMessage(hwnd, &msg))) DispatchMessage(&msg); }
Во время обработки сообщения WM_CREATE создается таймер с периодом 1 секунда. Этот таймер будет использоваться для определения текущего состояния устройства чтения компакт-дисков.
Если нажать на кнопку "Play", функция окна получит сообщение WM_COMMAND с параметром IDB_PLAY. При этом приложение проверит состояние флага bMediaPresent (наличие компакт-диска в устройстве) и, если этот флаг установлен, запустит проигрывание первой дорожки. Содержимое флага bMediaPresent периодически обновляется в соответствии с действительным состоянием устройства обработчиком сообщений таймера.
Кнопка "Stop" позволяет остановить процесс проигрывания. При этом устройству посылается команда MCI_STOP. Алогично, кнопка "Pause" выполняет временный останов, соответствующий обработчик посылает управляющее сообщение с кодом MCI_PAUSE. Для продолжения проигрывания после временного останова используется команда MCI_PLAY, для которой не задается начальная позиция (команда MCI_RESUME не поддерживается драйвером устройства чтения CD ROM). В этом случае проигрывание возобновляется с текущей позиции, то есть с прерванного места.
Для выполнения операции позиционирования на следующую или предыдущую дорожку вычисляется номер следующей дорожки исходя из номера текущей дорожки. Если новый номер дорожки меньше 1 или больше максимального, выполняется переход, соответственно, на последнюю или первую дорожку компакт-диска.
Обработчик сообщения таймера проверяет, не находится ли окно приложения (диалоговая панель) в свернутом виде. Если пользователь свернул окно в пиктограмму, нет смысла определять текущее состояние устройства, поэтому для увеличения общей производительности системы обработчик сообщения таймера в этом случае просто возвращает управление.
Код текущего состояния устройства записывается в переменную nMode. Далее обработчик сообщения WM_TIMER с помощью команды MCI_STATUS проверяет, вставлен ли в устройство компакт-диск. Если диск вставлен, определяется количество дорожек. определенное значение сохраняется в переменной nTrackCnt.
Номер текущей дорожки также определяется каждый раз при обработке сообщения таймера (при условии, что компакт-диск вставлен в устройство и устройство находится в режиме проигрывания). Если этот номер изменился, новое значение отображается в статическом органе управления диалоговой панели с идентификатором hwndCurTrack: if(nCurTrack != (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn)) { BYTE szBuf[20]; nCurTrack = (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn); SetWindowText(hwndCurTrack, itoa(nCurTrack, szBuf, 10)); }
Макрокоманда MCI_TMSF_TRACK извлекает номер дорожки из значения, возвращенного командой MCI_STATUS с параметром MCI_STATUS_POSITION.
В файле mmsystem.h определены и другие макрокоманды, которые используются аналогичным образом для получения других полей: MCI_TMSF_FRAME , MCI_TMSF_MINUTE , MCI_TMSF_SECOND , MCI_MSF_FRAME , MCI_MSF_MINUTE , MCI_MSF_SECOND . Можно сделать и обратные преобразования. Например, можно использовать макрокоманду MCI_MAKE_TMSF для упаковки в двойное слово номера дорожки, минут, секунд и номера фрейма: dwFormat = MCI_MAKE_TMSF(track, min, sec, frame);
В нашем приложении предусмотрена обработка сообщения MM_MCINOTIFY . Это сообщение используется для того чтобы "зациклить" проигрывание компакт-диска. После того как команда проигрывания будет выполнена до конца (то есть после того как будет завершено проигрывание последней дорожки компакт-диска), функция окна приложения получит сообщение MM_MCINOTIFY с параметром MCI_NOTIFY_SUCCESSFUL. Обработчик этого сообщения выглядит очень просто - он запускает проигрывание заново с первой дорожки: case MM_MCINOTIFY: { if(wParam == MCI_NOTIFY_SUCCESSFUL) { if(bMediaPresent) Play(hwnd, 1); } return 0; }
При завершении работы приложения обработчик сообщения WM_DESTROY закрывает устройство чтения компакт-дисков и уничтожает таймер.
Функция Play, определенная в нашем приложении, запускает проигрывание компакт-диска начиная с заданной дорожки. В ней для формирования позиции используется макрокоманда MCI_MAKE_TMSF : MCIPlay.dwFrom = MCI_MAKE_TMSF(nTrack, 0, 0, 0);
Все параметры, кроме первого, содержат нулевые значения, поэтому проигрывание будет запущено с самого начала дорожки, имеющей номер nTrack.
Файл mcicdpl.hpp (листинг 3.2) содержит определения констант, используемых в приложении.
Листинг 3.2. Файл mcicdpl/mcicdpl.hpp #define IDT_CURTRACK 200 #define IDB_STOP 101 #define IDB_PAUSE 102 #define IDB_RESUME 103 #define IDB_NEXT 104 #define IDB_PREV 105 #define IDB_EJECT 106 #define IDB_PLAY 100
Файл описания ресурсов приложения представлен в листинге 3.3. Он содержит определение пиктограммы и диалоговой панели, выступающей в роли главного окна приложения.
Листинг 3.3. Файл mcicdpl/mcicdpl.rc #include "g:\tcwin\include\windows.h" #include "mcicdpl.hpp" APPICON ICON "mcicdpl.ico" MCICDP DIALOG 45, 20, 153, 57 STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX CLASS "MCICDP" CAPTION "Compact Disk Player" BEGIN PUSHBUTTON "Play", IDB_PLAY, 84, 11, 31, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP PUSHBUTTON "Stop", IDB_STOP, 118, 11, 31, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP PUSHBUTTON "Pause", IDB_PAUSE, 84, 26, 31, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP PUSHBUTTON "Resume", IDB_RESUME, 118, 26, 31, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP PUSHBUTTON ">>I", IDB_NEXT, 6, 26, 31, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP PUSHBUTTON "I<<", IDB_PREV, 40, 26, 31, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP PUSHBUTTON "Eject", IDB_EJECT, 6, 41, 65, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP DEFPUSHBUTTON "Exit", IDOK, 84, 41, 65, 11, WS_CHILD | WS_VISIBLE | WS_TABSTOP LTEXT "Track:", -1, 12, 7, 35, 8, WS_CHILD | WS_VISIBLE | WS_GROUP LTEXT "00", IDT_CURTRACK, 35, 7, 16, 8, WS_CHILD | WS_VISIBLE | WS_GROUP CONTROL "", -1, "static", SS_BLACKFRAME | WS_CHILD | WS_VISIBLE, 6, 4, 65, 15 END
Файл определения модуля приложения MCICDPL представлен в листинге 3.4.
Листинг 3.4. Файл mcicdpl/mcicdpl.def NAME MCICDPL DESCRIPTION 'Приложение MCICDPL, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8194 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple
Позиционирование с последующим остановом. Перед использованием этой команды необходимо задать формат времени командой set time format seek device_id parameter [notify] [wait]
В качестве необязательного параметра parameter можно указывать одну из следующих строк: to position
Позиционирование в заданное место компакт-диска to start
Позиционирование в начало to end
Позиционирование в конец
Команда 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
Текущий формат времени
Останов проигрывания stop device_id