MCI_PAUSE
Команда MCI_PAUSE приостанавливает выполнение операции записи или воспроизведения. Она используется совместно с блоком параметров MCI_GENERIC_PARMS , который был рассмотрен выше.
Флаги для этой команды:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса |
Пример использования команды MCI_PAUSE:
MCI_GENERIC_PARMS mcigen; dwrc = mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen);
MCI_PLAY
Команда MCI_PLAY , как это видно из ее названия, предназначена для проигрывания файлов. Для этой команды в файле mmsystem.h определена структура блока параметров MCI_PLAY_PARMS :
typedef struct tagMCI_PLAY_PARMS { DWORD dwCallback; DWORD dwFrom; DWORD dwTo; } MCI_PLAY_PARMS; typedef MCI_PLAY_PARMS FAR *LPMCI_PLAY_PARMS;
В структуре параметров можно указать начальную и конечную позиции для проигрывания. Начальная позиция задается в поле dwFrom, конечная - в поле dwTo. Перед использованием начальной позиции следует установить формат времени при помощи команды MCI_SET_TIME_FORMAT, которую мы рассмотрим позже. Формат времени определяет единицу измерения для позиции, например, миллисекунды, байты, выборки сигнала или кадры.
Для структуры MCI_PLAY_PARMS можно указывать следующие флаги:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса проигрывания |
MCI_FROM | Поле dwFrom содержит начальную позицию для проигрывания |
MCI_TO | Поле dwTo содержит конечную позицию для проигрывания |
В приведенном ниже фрагменте запускается проигрывание с текущей позиции:
MCI_PLAY_PARMS mciPlayParms; DWORD dwrc; mciPlayParms.dwCallback = (DWORD)hwnd; dwrc=mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&mciPlayParms);
Для изменения текущей позиции можно воспользоваться командой MCI_SEEK.
При завершении проигрывания окно с идентификатором hwnd получит сообщение MM_MCINOTIFY.
MCI_RECORD
Команда MCI_RECORD позволяет выполнить запись в существующий или новый файл. Если при открытии устройства вы указали имя файла, будет выполняться запись в существующий файл. Для записи в новый файл нужно использовать имя нулевой длины. Результат записи в этом случае можно сохранить в файле при помощи команды MCI_SAVE.
Приведем формат блока параметров для команды MCI_RECORD:
typedef struct tagMCI_RECORD_PARMS { DWORD dwCallback; DWORD dwFrom; DWORD dwTo; } MCI_RECORD_PARMS; typedef MCI_RECORD_PARMS FAR *LPMCI_RECORD_PARMS;
Параметры dwFrom и dwTo задают, соответственно, начальную и конечную позицию для записи.
Для структуры MCI_RECORD_PARMS можно указывать следующие флаги:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса записи |
MCI_RECORD_INSERT | Необходимо вставить новую запись в уже существующие данные |
MCI_RECORD_OWERWRITE | Новая запись должна заместить существующие данные |
MCI_FROM | Поле dwFrom содержит начальную позицию для записи |
MCI_TO | Поле dwTo содержит конечную позицию для записи |
Если при выдаче команды MCI_RECORD вы не указали конечную позицию записи, запись будет продолжаться до тех пор, пока приложение не выдаст команду MCI_STOP или пока не будет израсходовано все свободное место на диске.
В приведенном ниже фрагменте кода запускается запись, которая будет продолжаться до достижения позиции dwMSec (запись может быть также остановлена раньше при помощи команды MCI_STOP):
MCI_RECORD_PARMS mciRecordParms; mciRecordParms.dwTo = dwMSec; mciRecordParms.dwCallback = (DWORD)hwnd; dwrc=mciSendCommand(wInDeviceID, MCI_RECORD, MCI_NOTIFY | MCI_TO, (DWORD)(LPVOID)&mciRecordParms);
После завершения процесса записи окно с идентификатором hwnd получит сообщение MM_MCINOTIFY.
MCI_RESUME
Эта команда отменяет действие команды MCI_PAUSE, при этом приостановленная операция будет продолжена. Для команды MCI_RESUME используется блок параметров MCI_GENERIC_PARMS .
Флаги для этой команды:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса |
Пример использования команды MCI_RESUME:
MCI_GENERIC_PARMS mcigen; dwrc = mciSendCommand(wDeviceID, MCI_RESUME, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen);
MCI_SAVE
Команда MCI_SAVE позволяет сохранить результат записи в файле. Для нее используется блок параметров MCI_SAVE_PARMS :
typedef struct tagMCI_SAVE_PARMS { DWORD dwCallback; LPCSTR lpfilename; } MCI_SAVE_PARMS; typedef MCI_SAVE_PARMS FAR * LPMCI_SAVE_PARMS;
Поле lpfilename должно содержать указатель на путь к файлу.
Флаги для этой команды:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса сохранения |
MCI_SAVE_FILE | Поле lpfilename содержит путь к файлу, в котором необходимо сохранить результат записи |
В следующем фрагменте кода выполняется сохранение записанных данных в файле с именем recorded.wav, который будет создан в текущем каталоге:
MCI_SAVE_PARMS mciSave; mciSave.lpfilename = "recorded.wav"; dwrc=mciSendCommand(wInDeviceID, MCI_SAVE, MCI_SAVE_FILE | MCI_WAIT, (DWORD)(LPVOID)&mciSave);
MCI_SEEK
Команда MCI_SEEK позволяет выполнять позиционирование в пределах файла. Для этой команды используется блок параметров MCI_SEEK_PARMS :
typedef struct tagMCI_SEEK_PARMS { DWORD dwCallback; DWORD dwTo; } MCI_SEEK_PARMS; typedef MCI_SEEK_PARMS FAR *LPMCI_SEEK_PARMS;
Поле dwTo задает новую позицию в единицах, установленных командой MCI_SET_TIME_FORMAT.
Флаги для этой команды:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса позиционирования |
MCI_SEEK_TO_START | Позиционирование на начало |
MCI_SEEK_TO_END | Позиционирование в конец |
MCI_SEEK_TO | Позиция определяется содержимым поля dwTo |
MCI_SET
Команда 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 определяет выходной канал.
Для звуковых устройств можно использовать другую структуру:
typedef struct tagMCI_WAVE_SET_PARMS { DWORD dwCallback; DWORD dwTimeFormat; DWORD dwAudio; UINT wInput; UINT wReserved0; UINT wOutput; UINT wReserved1; UINT wFormatTag; UINT wReserved2; UINT nChannels; UINT wReserved3; DWORD nSamplesPerSec; DWORD nAvgBytesPerSec; UINT nBlockAlign; UINT wReserved4; UINT wBitsPerSample; UINT wReserved5; } MCI_WAVE_SET_PARMS; typedef MCI_WAVE_SET_PARMS FAR * LPMCI_WAVE_SET_PARMS;
В этой структуре поле wInput определяет номер канала для записи, wOutput - номер канала для воспроизведения. Поле wFormatTag используется для определения формата звуковых данных. С помощью поля nChannels можно указать количество каналов - 1 (моно) или 2 (стерео). Поле nSamplesPerSec предназначено для задания частоты дискретизации (количество выборок сигнала в секунду). Поле nAvgBytesPerSec содержит скорость передачи данных (байты в секунду). С помощью поля nBlockAlign можно задать выравнивание блока, а с помощью поля wBitsPerSample - количество бит, используемых для представления одной выборки (8 или 16). Остальные поля зарезервированы.
Приведем список флагов, которые используются вместе с командой 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_ON | Включение заданного канала |
MCI_SET_OFF | Выключение заданного канала |
MCI_WAVE_INPUT | Установка канала для записи. Номер канала должен быть указан в поле wInput структуры MCI_WAVE_SET_PARAMS |
MCI_WAVE_OUTPUT | Установка канала для воспроизведения. Номер канала должен быть указан в поле wInput структуры MCI_WAVE_SET_PARAMS |
MCI_WAVE_SET_ANYINPUT | При записи следует использовать любое устройство, совместимое с заданным форматом данных |
MCI_WAVE_SET_ANYOUTPUT | При воспроизведении следует использовать любое устройство, совместимое с заданным форматом данных |
MCI_WAVE_SET_AVGBYTESPERSEC | Установить скорость потока данных при записи и воспроизведении из поля nAvgBytesPerSec |
MCI_WAVE_SET_BITSPERSAMPLE | Установить количество бит, используемых для представления одной выборки сигнала из поля wBitsPerSample |
MCI_WAVE_SET_BLOCKALIGN | Установить выравнивание блока из поля nBlockAlign |
MCI_WAVE_SET_CHANNELS | Поле nChannels содержит номер канала |
MCI_WAVE_SET_FORMATTAG | Установить формат из поля wFormatTag |
MCI_WAVE_SET_SAMPLESPERSEC | Установить частоту выборки из поля nSamplesPerSec |
MCI_WAVE_SET_TIME_FORMAT | Установить формат времени. Используется вместе со следующими константами:MCI_FORMAT_BYTES в байтах;MCI_FORMAT_MILLISECONDS в миллисекундах;MCI_FORMAT_SAMPLES в выборках сигнала |
MCI_STATUS
Команда 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_BYTES MCI_FORMAT_FRAMES MCI_FORMAT_HMS MCI_FORMAT_MILLISECONDS MCI_FORMAT_MSF MCI_FORMAT_SAMPLES MCI_FORMAT_TMSF |
MCI_STATUS_START | Начальная позиция |
MCI_STATUS_TRACK | В поле dwTrack записывается либо начальная позиция заданной дорожки (если дополнительно используется MCI_STATUS_POSITION), либо размер дорожки (если дополнительно используется MCI_STATUS_LENGTH) |
MCI_STATUS_MEDIA_PRESENT | Возвращается TRUE, если носитель данных вставлен в устройство |
MCI_WAVE_INPUT | Устройство, используемое для записи |
MCI_WAVE_OUTPUT | Устройство, используемое для воспроизведения |
MCI_WAVE_STATUS_AVGBYTESPERSEC | Скорость потока данных при записи и воспроизведении, байты в секунду |
MCI_WAVE_STATUS_BITSPERSAMPLE | Количество бит, используемых для представления одной выборки сигнала |
MCI_WAVE_STATUS_BLOCKALIGN | Текущее выравнивание блока |
MCI_WAVE_STATUS_CHANNELS | Количество каналов |
MCI_WAVE_FORMATTAG | Тег формата, используемого для записи, воспроизведения или сохранения данных в файле |
MCI_WAVE_STATUS_LEVEL | Текущий уровень записи или воспроизведения, используется 8- или 16-битовое значение в зависимости от формата данных. Младшее слово содержит уровень для монофонической записи или уровень правого канала для стереофонической записи. Уровень левого канала передается через старшее слово |
MCI_WAVE_STATUS_SAMPLESPERSEC | Скорость выборки сигнала (частота дискретизации) |
Приведем также список флагов для команды MCI_STATUS:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса |
MCI_STATUS_ITEM | Поле wItem содержит код получаемой информации |
mciStatus.dwItem = MCI_STATUS_LENGTH; dwrc = mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&mciStatus);
MCI_STOP
Команда MCI_STOP останавливает выполнение операции записи или воспроизведения, после чего освобождает все буфера, которые были использованы для операции. Эта команда использует блок параметров MCI_GENERIC_PARMS .
Флаги для этой команды:
Флаг | Описание |
MCI_NOTIFY | Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY |
MCI_WAIT | Функция mciSendCommand вернет управление только после завершения процесса |
Пример использования команды MCI_STOP:
MCI_GENERIC_PARMS mcigen; dwrc = mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen);
MCI_SYSINFO
С помощью этой команды можно получить системную информацию об устройстве в виде текстовой строки. Используется блок параметров в формате структуры MCI_SYSINFO_PARMS :
typedef struct tagMCI_SYSINFO_PARMS { DWORD dwCallback; LPSTR lpstrReturn; DWORD dwRetSize; DWORD dwNumber; UINT wDeviceType; UINT wReserved0; } MCI_SYSINFO_PARMS; typedef MCI_SYSINFO_PARMS FAR * LPMCI_SYSINFO_PARMS;
Поле lpstrReturn должно содержать дальний указатель на буфер, в который будет записана строка системной информации. Размер этого буфера следует передать через поле dwRetSize.
Поле dwNumber определяет положение устройства в таблице устройств MCI или в списке открытых устройств, если установлен флаг MCI_SYSINFO_OPEN. Поле wDeviceType определяет тип устройства. Поле wReserved0 зарезервировано.
Приведем набор флагов для команды MCI_INFO:
Флаг | Описание |
MCI_SYSINFO_INSTALLNAME | Требуется получить имя, которое было использовано в файле win.ini при установке драйвера устройства |
MCI_SYSINFO_NAME | Требуется получить имя устройства, соответствующего устройству с номером, заданным в поле dwNumber |
MCI_SYSINFO_OPEN | Определить имя открытого устройства или количество открытых устройств |
MCI_SYSINFO_QUANTITY | Определить количество устройств заданного типа, перечисленных в разделе [mci] файла system.ini. Если дополнительно установлен флаг MCI_SYSINFO_OPEN, возвращается количество открытых устройств |
MCIWNDM_NOTIFYERROR
Сообщение MCIWNDM_NOTIFYERROR передается родительскому окну при возникновении ошибки. Параметр wParam содержит идентификатор окна MCI, параметр lParam - указатель на текстовую строку с описанием ошибки.
MCIWNDM_NOTIFYMEDIA
При изменении носителя данных (устройства или файла) родительское окно получает сообщение MCIWNDM_NOTIFYMEDIA . Параметр lParam содержит указатель на текстовую строку с именем файла или устройства. Если устройство или файл закрыты, этот параметр имеет значение NULL. Параметр wParam не используется.
MCIWNDM_NOTIFYMODE
При изменении режима работы родительское окно получает сообщение MCIWNDM_NOTIFYMODE , причем код нового режима находится в параметре lParam. Параметр wParam не используется.
MCIWNDM_NOTIFYPOS
Сообщение MCIWNDM_NOTIFYPOS передается родительскому окну при изменении текущей позиции. Новая позиция находится в параметре lParam. Параметр wParam не используется.
MCIWNDM_NOTIFYSIZE
Если пользователь изменил размер окна MCI, родительское окно получает сообщение MCIWNDM_NOTIFYSIZE . Параметр wParam содержит идентификатор окна MCI, параметр lParam не используется и равен нулю.
Система Microsoft Video for Windows
Система Microsoft Video for Windows открыла новые области применения компьютера, обеспечив запись, хранение, редактирование и воспроизведение видеофильмов (рис. 1.21).
Рис. 1.21. Воспроизведение видеофильма в окне Windows
Одновременно и синхронно с видеоизображением записывается звук, поэтому теперь на экране компьютера можно посмотреть полноценное кино со звуковым сопровождением.
Вы спросите: а зачем? Для записи и просмотра видео есть видеокамеры, видеомагнитофоны, видеоплееры и телевизоры. Для чего впутывать в это хорошо освоенное дело компьютеры? К тому же стоимость компьютера не сравнима со стоимостью видеомагнитофона. Особенно такого, который способен воспроизводить видео с приемлемым качеством.
Согласны, технологию Video for Windows пока еще невыгодно использовать для записи обычных фильмов. Если вы желаете иметь дома коллекцию фильмов, купите себе видеоплеер, телевизор, кассеты с фильмами и забудьте про компьютеры и Video for Windows.
Однако иногда возникает необходимость создания баз данных и обучающих систем, в которых хранится не только текстовая и графическая информация, но и видеофрагменты.
Компьютер поможет выбрать нужный видеофрагмент. Вам не придется искать кассету, перематывать ленту, не отрываясь глядеть на счетчик метража. В некоторых случаях, особенно если вам нужно работать с большим количеством коротких видеофрагментов, использование компьютера значительно ускорит доступ к информации.
Вы, наверное, знаете, что в продаже есть видеокассеты с учебными записями. Все вы смотрели в школе учебные фильмы по физике, химии и другим наукам. Технология Video for Windows позволяет создавать обучающие системы нового, более высокого уровня. Эффективность обучения в этих системах достигается не только за счет использования звуковых и видеофрагментов (что доступно и в обычных учебных фильмах), но и за счет интерактивного взаимодействия ученика с обучающей системой (что может обеспечить только компьютер).
Видеофильмы хранятся в компьютере в виде файлов с расширением имени avi (файлы в стандарте AVI).
Эти файлы могут содержать не только видео, но и также многоканальное звуковое сопровождение (например, на разных языках), текстовую и любую другую информацию.
Для проигрывания avi-файлов можно использовать специальную версию приложения Media Player, которая поставляется вместе с Video for Windows и Video for Windows Runtime (рис. 1.22).
Рис. 1.22. Версия приложения Media Player для проигрывания avi-файлов
Изображение появляется в отдельном окне (рис. 1.23). Это окно можно уменьшить до пиктограммы, при этом "кино" не остановится.
Рис. 1.23. Окно, в котором проигрывается видеоизображение
Если же у вас очень много avi-файлов, вы можете их каталогизировать и просматривать с помощью приложения Microsoft Media Browser (рис. 1.24).
Рис. 1.24. Приложение Microsoft Media Browser
Вы можете создать несколько коллекций видеофильмов и сохранить их в виде файла на диске. В дальнейшем при помощи кнопки "Add Collection..." можно указать путь к каталогу, в котором находятся файлы коллекций, при этом список "Collections" будет содержать названия коллекций видеофрагментов, а список "Files" - названия файлов, входящих в состав выбранной коллекции. Для каждого avi-файла, входящего в коллекцию, вы можете подготовить краткое описание, которое будет отображаться в окне "File Information" вместе с первым кадром фрагмента. В этом же окне отображается имя файла и его размер.
Обратите внимание, что размер файла, выбранного на рис. 1.24, составляет около 37 Мбайт. Время проигрывания этого файла - примерно 4,5 минуты, следовательно, для хранения одной минуты видео нужно примерно 8 Мбайт памяти (при использовании данного размера изображения, цветового разрешения и метода компрессии изображения). Поэтому в качестве памяти для хранения avi-файлов используются цифровые компакт-диски, магнитооптические лазерные диски с перезаписью, лазерные диски с однократной записью (WORM), и магнитные диски большой емкости (порядка 1 Гбайт).
В продаже есть компакт-диски с записанными на них видеофильмами, базами данных, содержащими видеофрагменты и другие аналогичные информационные системы.
Что вам нужно для того чтобы самостоятельно записывать avi-файлы?
В дополнение к звуковому адаптеру вам необходимо приобрести адаптер для ввода видеоизображения. Этот адаптер должен продаваться вместе с драйвером для Windows. Желательно также, чтобы он мог выполнять аппаратную компрессию изображения в реальном времени. Это значительно сократит требования к объему диска, на который будет записываться полученный avi-файл. Стоимость такого адаптера может составлять порядка нескольких сотен долларов.
Источником видеосигнала может служить видеокамера, видеоплеер, телевизор или лазерный проигрыватель видеодисков.
Ну и конечно, необходимо приобрести Microsoft Video for Windows версии 1.1 или более поздней версии. В его состав входит приложение VidCap, с помощью которого вы сможете создать avi-файл из видеосигнала, поступающего в адаптер ввода изображения. Следует также установить драйвер для Windows, который поставляется вместе с адаптером.
Записанное изображение можно отредактировать, воспользовавшись приложением VidEdit. При этом avi-файл можно сжать, использовав один из методов компрессии.
Есть также способы создания видеофильмов из отдельных файлов, содержащих изображения кадров фильма в формате DIB, из файлов анимаций. Можно создать фильм, записывая изменения содержимого произвольного участка экрана компьютера, что удобно, например, для демонстрации работы программ. Все эти способы создания видеофильмов мы опишем в пятой главе.
Обзор Video for Windows
Video for Windows содержит DLL-библиотеки и драйверы, обеспечивающие интерфейс между приложениями и устройствами мультимедиа, предназначенными для работы cо звуковыми данными и видеоданными.
Приложения, проигрывающие файлы мультимедиа (в том числе avi-файлы), могут воспользоваться драйвером mciavi.drv , предоставляющим в распоряжение приложения высокоуровневый интерфейс MCI. Однако более предпочтительно использование интерфейса, который обеспечивается библиотекой msvideo.dll . Эта библиотека является надстройкой над интерфейсом MCI, значительно упрощающей его использование. Она нужна не только для проигрывания, но и для записи. В этой главе мы приведем исходные тексты приложений, работающих через интерфейс библиотеки msvideo.dll.
Вызывая функции интерфейса Video for Windows, приложения не имеют дело с внутренней структурой avi-файлов, так как библиотека avifile.dll обеспечивает им всю необходимую поддержку.
Приложения, предназначенные для записи звуковых данных и видеоданных, могут воспользоваться удобным высокоуровневым интерфейсом, предоставляемым библиотекой avicap.dll . Создавая приложения для записи видео, вам не придется заботиться о внутренней структуре avi-файлов, о компрессии (сжатии) данных при записи, об интерфейсе с драйверами устройства (или устройств) записи. При необходимости, вы, тем не менее, можете воспользоваться интерфейсом более низкого уровня, который обеспечивается библиотекой avifile.dll.
Редакторы файлов мультимедиа могут использовать библиотеки avifile.dll и msvideo.dll для доступа к данным и средствам отображения, а также системам компресии данных.
В составе Video for Windows имеются две системы компресии.
Первая из них - устанавливаемый менеджер компресии ICM (Installable Compression Manager ) предназначен для сжатия и восстановления видеоданных в реальном времени. Он может восстанавливать сжатые данные динамически во время проигрывания avi-файла. При создании новых avi-файлов можно использовать различные алгоритмы компресии. Собственно компрессия и восстановление выполняются драйверами компресии. Несколько таких драйверов поставляется в составе Video for Windows. Имеется возможность создания новых драйверов компресии при помощи Video for Windows Development Kit.
Вторая система - звуковой менеджер компресии ACM (Audio Compression Manager ). Он выполняет компрессию и восстановление звуковых данных в реальном времени. Вы можете использовать для компресии звуковых данных алгоритмы, реализуемые готовыми драйверами, или созданными вами с помощью Video for Windows Development Kit.
Окно класса AVICap
Для создания приложений, записывающих видео, лучше всего воспользоваться классом окна AVICap , определенном в библиотеке avicap.dll . Создав окно на базе класса AVICap, приложение получит в свое распоряжение простой интерфейс для записи видео и звуковых данных в avi-файл, для предварительного просмотра видео и выполнения других операций.
В классе AVICap предусмотрены средства динамического переключения устройств записи видео и звука, что удобно в тех случаях, когда возможно поочередное использование нескольких таких устройств, установленных в компьютере. Приложение может создать avi-файл, предназначенный для записи, скопировать содержимое одного avi-файла в другой, установить частоту кадров, вывести на экран диалоговую панель, с помощью которой пользователь сможет задать формат записи. Есть средства для работы с палитрами и универсальным буфером обмена Clipboard.
Для записи звука класс окна AVICap пользуется срествами библиотеки mmsystem.dll, подробно рассмотренными нами ранее.
Окно MCI
Библиотека msvideo.dll содержит определение класса окна MCI, на базе которого можно очень легко создавать приложения, управляющие устройствами для воспроизведения видео, воспроизведения и записи звуковых данных, воспроизведения MIDI, а также приложения, управляющие устройствами чтения компакт-дисков.
Окно MCI создать также просто, как и обычное окно. Более того, для его создания можно использовать привычную вам функцию CreateWindow , хотя есть и специальная функция MCIWndCreate . При создании окна вы можете использовать в дополнение к стандартным стилям окна некоторые другие. Например, вы можете указать, что окно MCI должно иметь набор органов управления для запуска проигрывания, записи, доступа к меню и полосу просмотра (рис.5.3).
Рис. 5.3. Окно MCI
Зная идентификатор окна MCI, вы можете управлять окном, используя набор макрокоманд, таких, как MCIWndRecord (включение режима записи) и MCIWndPlay (включение режима воспроизведения). Есть также средства для передачи окну обычных команд или командных строк MCI.
Вы можете создать окно MCI как перекрывающееся (overlapped) или дочернее (child), расположив его в любом месте экрана или родительского окна.
В большинстве случаев для проигрывания файлов мультимедиа возможности окна MCI более чем достаточны. Поэтому в нашей книге основное внимание мы уделим именно окну MCI.
Open
Управляющая строка open посылается перед началом работы для открытия устройства. Эта строка имеет следующий формат:
open device [type device_name] [alias alias] [buffer size] [notify] [wait]
Параметры, указанные в квадратных скобках, необязательные.
В качестве параметра device можно указывать имя драйвера устройства, имя самого устройства или имя звукового файла (можно также указать полный путь к файлу). Так как имя драйвера зависит от устройства, лучше пользоваться именем устройства или именем файла. Для звукового адаптера можно указать устройство waveaudio :
open waveaudio
Это устройство обслуживается драйвером mciwave.drv, входящим в состав операционной системы Windows 3.1. Интерфейс управляющих строк MCI непригоден для работы с драйвером Sound Driver for PC Speaker , поэтому, если в системе установлен только такой драйвер, пользуйтесь функциями MessageBeep или sndPlaySound, рассмотренными нами ранее.
Если при открытии устройства указывается путь к файлу, тип устройства определяется по расширению имени с использованием раздела [mci extensions] файла win.ini:
[mci extensions] wav=waveaudio mid=sequencer rmi=sequencer avi=AVIVideo
Поэтому следующая командная строка приведет к открытию устройства waveaudio:
open c:\wave\bye.wav
Если через параметр device передается имя файла, можно указать тип устройства при помощи параметра type device_name. Например:
open c:\wave\bye.wav type waveaudio
Это позволит использовать имена файлов с нестандартными расширениями. Например, вы можете переименовать файл bye.wav в файл bye.snd, при этом несмотря на то, что в разделе [mci extensions] файла win.ini расширение snd не описано, результат выполнения следующей команды будет правильный:
open c:\wave\bye.snd type waveaudio
Вы можете также указать алиас (альтернативное имя) для работы с устройством, использовав параметр alias:
open c:\wave\bye.wav alias sound
Параметр buffersize size задает размер буфера, который используется драйвером звукового адаптера (в секундах звучания).
Если указан параметр notify, и при передаче строки в последнем параметре функции mciSendString был указан идентификатор окна для оповещения, после того как устройство будет открыто, функция этого окна получит сообщение MM_MCINOTIFY.
С помощью команды open можно открыть устройство не только на воспроизведение, но и на запись. При этом в качестве параметра device нужно указать строку new. Следует также указать алиас. В качестве примера приведем последовательность команд, выполняющих запись:
open new type waveaudio alias nsound wait record nsound
Для остановки записи следует выдать команду stop. Для сохранения записанного фрагмента в wav-файле нужно использовать команду save (команда close закрывает устройство, она будет описана ниже):
stop nsound wait save nsound newsound.wav wait close nsound
Если указан параметр wait, функция mciSendString вернет управление только после завершения операции. Заметим, что параметры notify и wait используются практически со всеми управляющими строками.
Определение номера устройства по идентификатору
Если вы открыли устройство ввода или вывода с указанием константы WAVE_MAPPER, функция waveInOpen (или waveOutOpen, если открывается устройство вывода) может использовать любое подходящее устройство, установленное в системе. Для определения номера выбранного устройства по идентификатору, полученному от функций waveInOpen или waveOutOpen, можно использовать, соответственно, функцию waveInGetID или waveOutGetID.
Приведем описание функции waveInGetID :
Функция waveInGetID
UINT waveInGetID( HWAVEIN hWaveIn, // идентификатор устройства ввода UINT FAR* lpwDeviceID); // адрес переменной для записи // номера устройства
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
lpwDeviceID
Указатель на слово в памяти, в которое будет записан номер устройства, соответствующий идентификатору hWaveIn
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Функция waveOutGetID используется аналогично:
Функция waveOutGetID
UINT waveOutGetID( HWAVEOUT hWaveOut, // идентификатор устройства вывода UINT FAR* lpwDeviceID); // адрес переменной для записи // номера устройства
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
lpwDeviceID
Указатель на слово в памяти, в которое будет записан номер устройства, соответствующий идентификатору hWaveOut
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Определение текущей позиции
Приложение может определить текущую позицию в блоке при записи или воспроизведении, вызвав функцию waveInGetPosition или waveOutGetPosition, соответственно.
Приведем описание функции waveInGetPosition :
Функция waveInGetPosition
UINT waveInGetPosition( HWAVEIN hWaveIn, // идентификатор устройства ввода LPMMTIME lpInfo, // указатель на структуру MMTIME UNIT wSize); // размер структуры MMTIME
Параметры функции:
hWaveIn
Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
lpInfo
Указатель на структуру MMTIME . В нее будет записана информация о текущей позиции. Эта структура определена в файле mmsystem.h следующим образом:
typedef struct mmtime_tag { UINT wType; // формат времени union { DWORD ms; // миллисекунды DWORD sample; // выборки DWORD cb; // счетчик байт struct { // формат SMPTE BYTE hour; // часы BYTE min; // минуты BYTE sec; // секунды BYTE frame; // фреймы BYTE fps; // фреймы в секунду BYTE dummy; // байт для выравнивания } smpte; struct { // формат MIDI DWORD songptrpos; // указатель позиции в мелодии } midi; } u; } MMTIME; typedef MMTIME *PMMTIME; typedef MMTIME NEAR *NPMMTIME; typedef MMTIME FAR *LPMMTIME;
Перед вызовом функции waveInGetPosition необходимо записать в поле wType нужный формат времени. Можно использовать следующие значения:
Значение | Описание |
TIME_MS | Время измеряется в миллисекундах, при этом в объединении u следует использовать поле ms |
TIME_SAMPLES | Время измеряется в выборках сигнала, при этом в объединении u следует использовать поле sample |
TIME_BYTES | Для измерения времени выполняется подсчет байтов данных, в объединении u следует использовать поле cb |
TIME_SMPTE | Время измеряется в так называемом формате SMPTE (Society of Motion Picture and Television Engineers), при этом в объединении u следует использовать структуру smpte. Для поля fps возможны значения 24, 25, 29 или 30 фреймов (кадров) в секунду |
TIME_MIDI | Время измеряется в формате MIDI (Musical Instruments Digital Interface), при этом в объединении u следует использовать структуру midi |
wSize
Размер структуры MMTIME в байтах
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
После вызова функции waveInGetPosition приложение должно проверить содержимое поля wType. Если устройство не может выдать информацию о текущей позиции в затребованном формате, оно может предоставить сведения о текущей позиции в другом формате.
Для определения текущей позиции устройства вывода следует использовать функцию waveOutGetPosition :
Функция waveOutGetPosition
UINT waveOutGetPosition( HWAVEOUT hWaveOut, // идентификатор устройства вывода LPMMTIME lpInfo, // указатель на структуру MMTIME UNIT wSize); // размер структуры MMTIME
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
lpInfo
Указатель на структуру MMTIME. В нее будет записана информация о текущей позиции для устройства вывода.
wSize
Размер структуры MMTIME в байтах
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Определение возможностей звуковых устройств мультимедиа
Теперь, когда вы познакомились со структурой wav-файлов, нашей следующей задачей будет освоение приемов работы со звуковыми устройствами на низком уровне. И начнем мы с определения количества и возможностей звуковых устройств, установленных в системе.
В системе могут быть установлены устройства для записи и воспроизведения звука методом импульсно-кодовой модуляции PCM (waveform audio), устройства для записи и проигрывания музыкальных MIDI-файлов, дополнительные (auxiliary) устройства, такие, как проигрыватель звуковых компакт-дисков и другие.
Библиотека mmsystem.dll содержит набор функций, с помощью которых приложение может определить состав устройств и их возможности.
Функция waveOutGetNumDevs , не имеющая параметров, возвращает количество устройств, способных воспроизводить звуковые данные, записанные с использованием импульсно-кодовой модуляции. Аналогично, функция waveInGetNumDevs возвращает количество устройств, способных записывать такие данные.
Количество устройств, пригодных для записи и воспроизведения MIDI-файлов, можно узнать при помощи, соответственно, функций midiOutGetNumDevs и midiInGetNumDevs .
Для определения количества дополнительных устройств предназначена функция auxGetNumDevs .
Все перечисленные функции не имеют параметров и возвращают значение типа UINT (количество установленных в системе устройств того или иного типа).
Для определения возможностей устройств используются функции auxGetDevCaps (возможности дополнительных устройств), midiInGetDevCaps (возможности устройств записи в формате MIDI), midiOutGetDevCaps (возможности устройств воспроизведения в формате MIDI), waveInGetDevCaps (возможности устройств записи данных методом импульсно-кодовой модуляции), waveOutGetDevCaps (возможности устройств вывода данных, записанных методом импульсно-кодовой модуляции).
В качестве первого параметра всем перечисленным функциям следует указать идентификатор устройства, который может изменяться от нуля (для первого устройства) и до значения, полученного от таких функций, как waveInGetNumDevs и auxGetNumDevs .
Второй параметр является дальним указателем на структуру, формат которой зависит от типа устройства. Это может быть структура AUXCAPS (дополнительное устройство), MIDIINCAPS (устройство ввода данных MIDI), MIDIOUTCAPS (устройство вывода данных MIDI), WAVEINCAPS (устройство ввода методом импульсно-кодовой модуляции), WAVEOUTCAPS (устройство вывода данных, записанных методом импульсно-кодовой модуляции).
Третий параметр - размер соответствующей структуры в байтах.
Все эти структуры и указатели на них определены в файле mmsystem.h.
Структура AUXCAPS выглядит следующим образом:
typedef struct auxcaps_tag { UINT wMid; // код изготовителя драйвера UINT wPid; // код устройства VERSION vDriverVersion; // версия драйвера char szPname[MAXPNAMELEN]; // название устройства UINT wTechnology; // тип устройства DWORD dwSupport; // поддерживаемые функции } AUXCAPS; typedef AUXCAPS *PAUXCAPS; typedef AUXCAPS NEAR *NPAUXCAPS; typedef AUXCAPS FAR *LPAUXCAPS;
Поля wMid, wPid, vDriverVersion и szPname определены во всех структурах, используемых для определения возможностей устройств мультимедиа.
В поле wMid находится код изготовителя драйвера для устройства (список кодов некоторых фирм-изготовителей есть в приложении 2).
Поле wPid содержит код устройства, назначенный изготовителем (приложение 3).
Старший байт поля vDriverVersion содержит верхний (major) номер версии драйвера устройства, младший - нижний (minor) номер версии драйвера устройства.
В поле szPname располагается описание устройства в виде текстовой строки.
Поле wTechnology специфично для структуры AUXCAPS. В нем могут быть установлены флаги AUXCAPS_CDAUDIO (имеется звуковой вход от внутреннего устройства проигрывания компакт-дисков) и AUXCAPS_AUXIN (предусмотрен звуковой вход от входной линии, расположенной на плате звукового адаптера).
Поле dwSupport может содержать флаги AUXCAPS_VOLUME (есть возможность регулировки громкости) и AUXCAPS_LRVOLUME (есть возможность раздельной регулировки громкости для левого и правого каналов).
Структура MIDIINCAPS содержит только те поля, которые являются общими для всех структур, предназначенных для определения возможностей устройств мультимедиа:
typedef struct midiincaps_tag { UINT wMid; UINT wPid; VERSION vDriverVersion; char szPname[MAXPNAMELEN]; } MIDIINCAPS; typedef MIDIINCAPS *PMIDIINCAPS; typedef MIDIINCAPS NEAR *NPMIDIINCAPS; typedef MIDIINCAPS FAR *LPMIDIINCAPS;
Структура MIDIOUTCAPS дополнительно содержит поля wTechnology (тип устройства), wVoices (количество голосов для встроенного синтезатора), wNotes (количество нот для встроенного синтезатора), wChannelMask (количество каналов для встроенного синтезатора) и dwSupport (поддерживаемые функции):
typedef struct midioutcaps_tag { UINT wMid; UINT wPid; VERSION vDriverVersion; char szPname[MAXPNAMELEN]; UINT wTechnology; UINT wVoices; UINT wNotes; UINT wChannelMask; DWORD dwSupport; } MIDIOUTCAPS; typedef MIDIOUTCAPS *PMIDIOUTCAPS; typedef MIDIOUTCAPS NEAR *NPMIDIOUTCAPS; typedef MIDIOUTCAPS FAR *LPMIDIOUTCAPS;
В поле wTechnology могут находиться значения MOD_MIDIPORT (устройство является аппаратным портом MIDI), MOD_SQSYNTH (устройство является синтезатором с выходным сигналом прямоугольной формы), MOD_FMSYNTH (FM-синтезатор, то есть синтезатор с частотной модуляцией), MOD_MAPPER (устройство отображения Microsoft MIDI Mapper ).
На данном этапе для нас наибольший интерес представляют структуры WAVEINCAPS и WAVEOUTCAPS, предназначенные, соответственно, для определения возможностей устройств ввода и вывода звуковых сигналов с использованием импульсно-кодовой модуляции.
Структура WAVEINCAPS определена следующим образом:
typedef struct waveincaps_tag { UINT wMid; UINT wPid; VERSION vDriverVersion; char szPname[MAXPNAMELEN]; DWORD dwFormats; UINT wChannels; } WAVEINCAPS; typedef WAVEINCAPS *PWAVEINCAPS; typedef WAVEINCAPS NEAR *NPWAVEINCAPS; typedef WAVEINCAPS FAR *LPWAVEINCAPS;
В поле wChannels находится количество каналов (1 - моно, 2 - стерео).
В поле dwFormats могут располагаться флаги, соответствующие стандартным форматам звуковых данных, с которыми может работать устройство.
Флаги объединены при помощи логической операции ИЛИ. Для них в файле mmsystem.h определены символические константы:
Константа | Частота дискретизации, количество каналов (моно, стерео) и количество бит для представления выборки сигнала |
WAVE_FORMAT_1M08 | 11.025 Кгц, моно, 8 бит |
WAVE_FORMAT_1S08 | 11.025 Кгц, стерео, 8 бит |
WAVE_FORMAT_1M16 | 11.025 Кгц, моно, 16 бит |
WAVE_FORMAT_1S16 | 11.025 Кгц, стерео, 16 бит |
WAVE_FORMAT_2M08 | 22.05 Кгц, моно, 8 бит |
WAVE_FORMAT_2S08 | 22.05 Кгц, стерео, 8 бит |
WAVE_FORMAT_2M16 | 22.05 Кгц, моно, 16 бит |
WAVE_FORMAT_2S16 | 22.05 Кгц, стерео, 16 бит |
WAVE_FORMAT_4M08 | 44.1 Кгц, моно, 8 бит |
WAVE_FORMAT_4S08 | 44.1 Кгц, стерео, 8 бит |
WAVE_FORMAT_4M16 | 44.1 Кгц, моно, 16 бит |
WAVE_FORMAT_4S16 | 44.1 Кгц, стерео, 16 бит |
typedef struct waveoutcaps_tag { UINT wMid; UINT wPid; VERSION vDriverVersion; char szPname[MAXPNAMELEN]; DWORD dwFormats; UINT wChannels; DWORD dwSupport; } WAVEOUTCAPS; typedef WAVEOUTCAPS *PWAVEOUTCAPS; typedef WAVEOUTCAPS NEAR *NPWAVEOUTCAPS; typedef WAVEOUTCAPS FAR *LPWAVEOUTCAPS;
В этой структуре поля dwFormats и wChannels имеют такое же назначение, что и в только что рассмотренной нами структуре WAVEINCAPS.
Поле dwSupport содержит флаги, соответствующие различным возможностям устройства вывода. Символические константы для них определены в файле mmsystem.h:
Константа | Описание |
WAVECAPS_PITCH | Изменение высоты тона |
WAVECAPS_PLAYBACKRATE | Изменение скорости проигрывания |
WAVECAPS_SYNC | Драйвер устройства вывода работает в синхронном режиме (во время проигрывания работа приложений приостанавливается) |
WAVECAPS_VOLUME | Управление громкостью |
WAVECAPS_LRVOLUME | Раздельное управление громкостью для левого и правого каналов |
Останов устройства
Для останова устройства ввода используется функция waveInStop, которая была рассмотрена нами в разделе, посвященному записи звука на низком уровне. Для временного останова работы устройства вывода следует использовать функцию waveOutPause :
Функция waveOutPause
UINT waveOutPause( HWAVEOUT hWaveOut); // идентификатор устройства вывода
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Если требуется прервать вывод, выполняемый в цикле, используйте функцию waveOutBreakLoop :
Функция waveOutBreakLoop
UINT waveOutBreakLoop( HWAVEOUT hWaveOut); // идентификатор устройства вывода
Параметры функции:
hWaveOut
Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
Возвращаемое значение:
При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
MMSYSERR_INVALHANDLE
Указан неправильный идентификатор устройства
Открытие файла
Для открытия файла предназначена функция mmioOpen , прототип которой есть в файле mmsystem.h. Эта функция может открывать файл для буферизованного или небуферизованного ввода, файл в оперативной памяти. Она может работать с файлами, уже открытыми средствами MS-DOS или использовать дополнительные функции для выполнения нестандартных процедур ввода/вывода. Из-за ограниченного объема книги мы сможем рассмотреть только основные возможности функции mmioOpen, более подробное описание вы сможете найти в документации, которая поставляется вместе с Microsoft SDK.
Функция mmioOpen
HMMIO mmioOpen( LPSTR szFilename, // путь к файлу LPMMIOINFO lpmmioinfo, // указатель на структуру MMIOINFO DWORD dwOpenFlags); // флаги для операции открытия
Параметры функции:
szFilename
Дальний указатель на текстовую строку, содержащую путь к открываемому файлу
lpmmioinfo
Указатель на структуру MMIOINFO, которая содержит дополнительные параметры для операции открытия файла. Может быть задан как NULL
dwOpenFlags
Флаги, определяющие режим открытия файла
Возвращаемое значение:
При успехе возвращается идентификатор открытого файла. Этот идентификатор можно использовать только в функциях с префиксом имени mmio. В случае ошибки возвращается значение NULL. Код ошибки можно определить из поля wErrorRet структуры MMIOINFO
Формат структуры MMIOINFO описан в файле mmsystem.h:
typedef struct _MMIOINFO { // Поля общего назначения DWORD dwFlags; // общий флаг состояния FOURCC fccIOProc; // код идентификации // процедуры ввода/вывода LPMMIOPROC pIOProc; // указатель на процедуру ввода/вывода UINT wErrorRet; // код завершения HTASK htask; // идентификатор локальной процедуры // ввода/вывода // Поля для буферизованного ввода/вывода LONG cchBuffer; // размер буфера или 0L HPSTR pchBuffer; // начало буфера или NULL HPSTR pchNext; // указатель на следующий байт для // чтения или записи HPSTR pchEndRead; // указатель на последний прочитанный // байт HPSTR pchEndWrite;// указатель на последний // записанный байт LONG lBufOffset; // дисковое смещение начала буфера // Поля для процедур ввода/вывода LONG lDiskOffset; // дисковое смещение для следующей // операции чтения или записи DWORD adwInfo[3]; // дополнительные данные для типа MMIOPROC // Прочие поля DWORD dwReserved1; // зарезервировано DWORD dwReserved2; // зарезервировано HMMIO hmmio; // идентификатор открытого файла } MMIOINFO; typedef MMIOINFO *PMMIOINFO; typedef MMIOINFO NEAR *NPMMIOINFO; typedef MMIOINFO FAR *LPMMIOINFO;
Структура MMIOINFO позволяет задать многочисленные способы работы с файлами. Можно использовать файлы в памяти, можно определить собственную процедуру для выполнения нестандартного ввода или вывода или работать с идентификаторами файлов, открытых средствами MS-DOS. В простых случаях вы можете указать второй параметр функции mmioOpen как NULL и не использовать структуру MMIOINFO вообще:
hmmio = mmioOpen((LPSTR)lpszFileName, NULL, MMIO_READ | MMIO_ALLOCBUF);
Последний параметр функции mmioOpen предназначен для определения режима открытия файла в виде логической комбинации ИЛИ отдельных флагов. Приведем список флагов.
Флаг | Описание режима открытия файла |
MMIO_READ | Чтение |
MMIO_WRITE | Запись |
MMIO_READWRITE | Чтение и запись |
MMIO_CREATE | Создание нового файла. Если файл с таким именем уже есть, он обрезается до нулевой длины |
MMIO_DELETE | Удаление файла. Если удаление выполнено без ошибок, возвращается значение TRUE, в противном случае - FALSE |
MMIO_PARSE | Создание текстовой строки, содержащей полный путь к файлу на базе пути, переданного функции через параметр szFilename. Результат помещается обратно в буфер szFilename |
MMIO_EXIST | Определяется, существует ли указанный файл, и если существует, для него создается текстовая строка, содержащая полный путь к файлу |
MMIO_ALLOCBUF | Файл будет открыт для буферизованного ввода/вывода. По умолчанию буфер имеет размер 8 Кбайт. Приложение может изменить размер буфера, указав его в поле cchBuffer в структуре MMIOINFO |
MMIO_COMPAT | Файл будет открыт в режиме совместимости. В этом режиме он может быть открыт несколько раз |
MMIO_EXCLUSIVE | Файл будет открыт в монопольном режиме |
MMIO_DENYWRITE | Другим приложениям запрещено открывать файл на запись |
MMIO_DENYREAD | Другим приложениям запрещено открывать файл на чтение |
MMIO_DENYNONE | Другие приложения могут открывать файл и на запись, и на чтение |
MMIO_GETTEMP | Создание текстовой строки для открытия временного файла. Текстовая строка будет записана в буфер, адрес которого передается через первый параметр. Открытие файла не выполняется |
hFile = mmioOpen(szFileName, NULL, MMIO_CREATE | MMIO_READWRITE);
Открытие и закрытие устройства CD ROM
Перед началом работы с устройством вы должны его открыть, передав управляющую строку open . При этом вы должны указать имя устройства как cdaudio (можно использовать алиас):
open cdaudio alias cd wait
Так как операция открытия устройства чтения CD ROM может выполняться в течении нескольких секунд, имеет смысл перед продолжением работы приложения дождаться ее завершения, указав параметр wait.
При открытии драйвера CD ROM можно указать параметр shareable , в этом случае устройством смогут пользоваться одновременно несколько приложений (если они все откроют устройство с параметром shareable).
Драйвер устройства CD ROM не работает с файлами, поэтому в управляющей строке open путь к файлу не указывается.
Команда close особенностей не имеет. В качестве параметра вы должны указать имя устройства cdaudio или алиас (альтернативное имя), если устройство было открыто с использованием алиаса:
close cd
Озвучивание видео
Вы можете записать звук одновременно с записью видео, или добавить его позже при помощи приложения VidEdit. Для добавления звука в "немое" видео откройте avi-файл из приложения VidEdit и выберите из меню "File" строку "Insert...". В списке "List Files of Type" появившейся диалоговой панели "Insert File" выберите строку "Microsoft Waveform". Далее укажите wav-файл, содержащий добавляемый звук.
Все! Файл озвучен и его нужно сохранить. Полезно также перед озвучиванием сделать копию исходного файла.
В некоторых случаях вам может потребоваться выполнить синхронизацию видео и звукового сопровождения. Для выполнения синхронизации используйте диалоговую панель "Synchronize" (рис. 5.7). В этой диалоговой панели вы можете задать скорость проигрывания видео и смещение звуковых данных относительно начала фильма в миллисекундах (в поле Audio Offset).
Панель управления звуковым адаптером
В комплекте утилит для адаптера Sound Galaxy NX Pro поставляется приложение winmix.exe, представляющее собой электронный микшерный пульт, управляющий работой адаптера (рис. 1.6). С другими адаптерами поставляются утилиты аналогичного назначения.
Рис. 1.6. Управляющая панель
С помощью этого приложения вы можете устанавливать уровень сигнала до смешивания (набор регуляторов "Mixer Control", регулировать громкость ("Vol"), баланс ("Bal"), тембр для низких и высоких частот ("Bas" и "Trb"), включать и выключать стереофоническое звучание (кнопка "ST"), а также контролировать уровень сигнала по индикатору, расположенному в верхнем левом углу панели. При выключенном переключателе "Lock" можно регулировать уровни сигнала отдельно для каждого канала (в стереофоническом режиме).
С помощью меню "ADC" можно изменять подключение аналого-цифрового преобразователя (рис. 1.7).
Рис. 1.7. Диалоговая панель "ADC Select"
Группа переключателей "ADC Input" позволяет подключить этот преобразователь к микрофону ("Mic"), аналоговому выходу устройства чтения компакт-дисков ("CD") или входной линии ("Line in"). Вы также можете разрешить смешивание сигналов от различных входов (переключатель "Mixer On") или запретить такое смешивание ("Mixer Off").
Все установленные параметры, включая настройку громкости и тембра, можно сохранить в энергонезависимой памяти звукового адаптера, для чего из меню "Option" приложения следует выбрать строку "Save Settings".
Отметим, что не все звуковые адаптеры допускают регулировку тембра или могут сохранять параметры в энергонезависимой памяти.
Pause
Временный останов при воспроизведении или записи (пауза)
pause device_id
Play
Команда play предназначена для запуска процесса воспроизведения. Она имеет следующий формат:
play device_id [from position [to position]] [notify] [wait]
Идентификатор устройства device_id создается при открытии устройства командой open. Например, если для открытия устройства была использована строка
open c:\windows\ding.wav alias ding
то в качестве параметра device_id можно использовать алиас ding:
play ding
Если не указан параметр from position, проигрывание начинается с текущей позиции. Сразу после открытия текущая позиция устанавливается в начало файла. Параметр to position позволяет указать конечную позицию, при достижении которой проигрывание прекращается. Перед использованием параметров from и to необходимо установить формат для позиционирования при помощи команды set (см. ниже).
Если задан параметр notify, и при передаче строки в последнем параметре функции mciSendString был указан идентификатор окна для оповещения, после завершения операции проигрывания функция этого окна получит сообщение MM_MCINOTIFY. Обработчик этого сообщения может закрыть устройство или выполнить повторное проигрывание фрагмента, например, с самого начала.
Указав параметр wait, можно установить псевдосинхронный режим воспроизведения. В этом случае функция mciSendString вернет управление только после завершения операции, однако работа других приложений Windows будет продолжаться во время воспроизведения. Если же этот параметр не указан, функция mciSendString запустит процесс в асинхронном режиме и немедленно вернет управление.
Подводя итоги
Системы мультимедиа позволяют интегрировать в одном приложении звук, видео, графические изображения и текстовую информацию. Появление цифровых компакт-дисков открыло реальную возможность создания таких приложений, о которых раньше мы не могли и мечтать. Теперь компьютер может передавать человеку информацию в наиболее удобном для него виде, а именно, с помощью звука, изображений и видео. Все это не только увеличивает привлекательность компьютера как инструмента для выполнения какой-либо работы, но и открывает совершенно новые области для его использования.
Если рассматривать существующие средства разработки приложений Windows, использующих технологию мультимедиа, можно выделить три уровня таких средств.
Первый уровень, самый высокий, предназначен для конечных пользователей, создающих приложения без использования программирования. Существуют готовые системы, с помощью которых пользователь может создавать интерактивные приложения мультимедиа, не написав ни одной строчки программного кода. Эти системы удобны, когда нужно быстро получить результат: подготовить демонстрацию или рекламный ролик, создать несложную обучающую систему или базу данных, содержащую звуковую или видеоинформацию.
Второй уровень предполагает программирование с использованием интерфейса высокого уровня MCI (Media Control Interface - интерфейс управления средой). С помощью этого интерфейса приложение Windows может выдавать команды драйверам устройств ввода/вывода звуковой и видеоинформации, включающие режим записи, воспроизведения, перемотки и т. п. Если ваше приложение должно выполнять запись или воспроизведение звуковых или видеоданных, имеет смысл воспользоваться именно этим уровнем, скрывающим от программиста многие сложные процедуры, необходимые для работы с драйверами устройств мультимедиа.
Третий, самый низкий, уровень позволяет приложениям получить доступ к буферам, содержащим воспроизводимые или записываемые звуковые или видеоданные, работать с внутренней структурой файлов, содержащих звуковую или видеоинформацию, а также использовать другие дополнительные возможности. К этому уровню обычно обращаются в тех случаях, когда ни один из более высоких уровней не позволяет достичь нужного результата. Как правило, низкий уровень используется для создания инструментальных средств и приложений, обрабатывающих звуковую и видеоинформацию в реальном времени.
Позиционирование
Для позиционирования внутри файла, открытого при помощи функции mmioOpen, следует использовать функцию mmioSeek .
Функция mmioSeek
LONG mmioSeek( HMMIO hmmio, // идентификатор открытого файла LONG dwOffset, // смещение для текущей позиции int nOrigin); // интерпретация смещения
Параметры функции:
hmmio
Идентификатор открытого файла, полученный с помощью функции mmioOpen
dwOffset
Величина смещения в байтах, на которое будет продвинута текущая позиция в файле. Интерпретация этого значения зависит от параметра nOrigin
nOrigin
Этот параметр определяет способ использования смещения, заданного параметром dwOffset. Можно использовать константы SEEK_SET (смещение от начала файла), SEEK_CUR (смещение от текущей позиции в файле), SEEK_END (смещение от конца файла).
Возвращаемое значение:
Возвращается новое смещение текущей позиции в файле от начала файла (в байтах) или -1 при возникновении ошибки
Коды ошибок MCI
MCIERR_BAD_TIME_FORMAT
Неправильный формат времени
MCIERR_CANNOT_LOAD_DRIVER
Невозможно загрузить драйвер
MCIERR_CANNOT_USE_ALL
Для этой команды нельзя использовать строку all в качестве имени устройства
MCIERR_CREATEWINDOW
Невозможно создать или использовать окно
MCIERR_DEVICE_LENGTH
Слишком длинное имя устройства или драйвера (больше 79 символов)
MCIERR_DEVICE_LOCKED
Устройство закрыто, через несколько секунд можно попробовать повторить команду
MCIERR_DEVICE_NOT_INSTALLED
Указанное устройство не установлено в системе
MCIERR_DEVICE_NOT_READY
Драйвер не готов выполнить запрос
MCIERR_DEVICE_OPEN
Имя устройства уже используется приложением в качестве алиаса (альтернативного имени)
MCIERR_DEVICE_ORD_LENGTH
Слишком длинное имя драйвера (больше 79 символов)
MCIERR_DEVICE_TYPE_REQUIRED
Указанный драйвер не найден
MCIERR_DRIVER
Ошибка драйвера. Следует установить драйвер более поздней версии
MCIERR_DRIVER_INTERNAL
Аналогично предыдущему
MCIERR_DUPLICATE_ALIAS
Указанный алиас уже используется в системе
MCIERR_EXTENSION_NOT_FOUND
Указанному расширению имени файла не соответствует ни одно устройство, установленное в системе
MCIERR_EXTRA_CHARACTERS
Текстовая строка должна быть заключена в двойные кавычки. Если появилась эта ошибка, в строке есть символы, расположенные после закрывающей кавычки
MCIERR_FILE_NOT_FOUND
Файл не найден
MCIERR_FILE_NOT_SAVED
Файл не сохранен, вероятно, из-за отсутствия свободного места на диске
MCIERR_FILE_READ
Ошибка при чтении файла
MCIERR_FILE_WRITE
Ошибка при записи файла
MCIERR_FLAGS_NOT_COMPATIBLE
Указанные параметры несовместимы
MCIERR_FILENAME_REQUIRED
Требуется указать правильное имя файла
MCIERR_GET_CD
Не найден нужный файл или драйвер
MCIERR_HARDWARE
Ошибка аппаратного обеспечения
MCIERR_ILLEGAL_FOR_AUTO_OPEN
Данную команду нельзя выполнить для устройства, открываемого автоматически
MCIERR_INTERNAL
Ошибка при инициализации MCI. Необходимо перезапустить операционную систему Windows
MCIERR_INVALID_DEVICE_ID
Неправильный идентификатор устройства
MCIERR_INVALID_DEVICE_NAME
Указанный драйвер не открыт или не распознан системой MCI
MCIERR_INVALID_FILE
Указанный файл невозможно проиграть на выбранном устройстве. Вероятно, файл имеет неправильный или неподдерживаемый формат
MCIERR_INVALID_SETUP
Неправильная установка параметров MIDI. Необходимо восстановить оригинальный файл midimap.cfg, расположенный в системном каталоге Windows
MCIERR_MISSING_INTEGER
Данной команде необходим параметр в виде целого числа
MCIERR_MISSING_PARAMETER
Данной команде необходимо указать параметр
MCIERR_MULTIPLE
Возникли ошибки одновременно на нескольких устройствах
MCIERR_MUST_USE_SHAREABLE
Указанный драйвер уже используется. Для совместного использования следует указать параметр "shareable"
MCIERR_NO_ELEMENT_ALLOWED
Данное устройство не работает с файлами
MCIERR_NO_INTEGER
Для указанной команды необходимо использовать параметр в виде целого числа
MCIERR_NO_WINDOW
Нет окна для отображения
MCIERR_NONAPPLICABLE_FUNCTION
Указанные команды MCI невозможно выполнить в данном порядке
MCIERR_NULL_PARAMETER_BLOCK
Был использован нулевой блок параметров
MCIERR_OUT_OF_MEMORY
Для выполнения команды слишком мало памяти
MCIERR_OUTOFRANGE
Значение параметра лежит вне допустимых границ
MCIERR_SET_CD
Указанный драйвер или устройство недоступны, так как приложение не может сменить каталог
MCIERR_SET_DRIVE
Указанный драйвер или устройство недоступны, так как приложение не может сменить устройство
MCIERR_UNNAMED_RESOURCE
Попытка сохранения в файле без указания имени файла
MCIERR_UNRECOGNIZED_COMMAND
Команда не распознана
MCIERR_UNSUPPORTED_FUNCTION
Драйвер не поддерживает указанную команду
MCIERR_SEQ_DIV_INCOMPATIBLE
Несовместимые форматы указателя "song pointer" и SMPTE
MCIERR_SEQ_NOMIDIPRESENT
В системе нет устройств MIDI
MCIERR_SEQ_PORT_INUSE
Указанный порт MIDI уже используется
MCIERR_SEQ_PORT_MAPNODEVICE
Система отображения MIDI Mapper ссылается на устройство MIDI, которое не установлено в системе
MCIERR_SEQ_PORT_MISCERROR
Ошибка порта
MCIERR_SEQ_PORT_NONEXISTENT
Указанное устройство MIDI не установлено в системе
MCIERR_SEQ_PORTUNSPICIFIED
В системе не установлен текущий порт MIDI
MCIERR_SEQ_TIMER
Все таймеры системы мультимедиа задействованы другими приложениями
MCIERR_WAVE_INPUTSINUSE
Задействованы все звуковые устройства, которые могут записывать файл в данном формате
MCIERR_WAVE_INPUTSUNSUITABLE
В системе не установлено ни одного устройства, которое могло бы записывать файл в данном формате
MCIERR_WAVE_INPUTUNSPECIFIED
Необходимо указать любое совместимое устройство записи звука
MCIERR_WAVE_OUTPUTSINUSE
Задействованы все звуковые устройства, которые могут проигрывать файл в данном формате
MCIERR_WAVE_OUTPUTSUNSUITABLE
В системе не установлено ни одного устройства, которое могло бы проигрывать файл в данном формате
MCIERR_WAVE_OUTPUTUNSPECIFIED
Необходимо указать любое совместимое устройство воспроизведения звука
MCIERR_WAVE_SETINPUTINUSE
Текущее звуковое устройство уже задействовано
MCIERR_WAVE_SETINPUTUNSUITABLE
Устройство, использованное для записи, не может распознать формат данных
MCIERR_WAVE_SETOUTPUTINUSE
Текущее звуковое устройство уже задействовано
MCIERR_WAVE_SETOUTPUTUNSUITABLE
Устройство, использованное для воспроизведения, не может распознать формат данных
Коды изготовителей
Код | Изготовитель |
1 | Microsoft Corporation |
2 | Creative Labs Inc. |
3 | Media Vision Inc. |
4 | FUJITSU |
20 | Artisoft Inc. |
21 | TURTLE_BEACH |
22 | International Business Machines Corp. |
23 | Vocaltec LTD. |
24 | ROLAND |
25 | Digispeech, Inc. |
26 | NEC |
27 | ATI |
28 | Wang Laboratories, Inc |
29 | Tandy Corporation |
30 | Voyetra |
31 | Antex Electronics |
32 | ICL Personal Systems |
33 | Intel Corporation |
34 | Advanced Gravis |
35 | Video Associates Labs, Inc. |
36 | InterActive, Inc. |
37 | Yamaha Corporation of America |
38 | Everex Systems, Inc. |
39 | Echo Speech Corporation |
40 | Sierra Semiconductor Corp. |
41 | Computer Aided Technologies |
42 | APPS Software International |
43 | DSP Group, Inc. |
44 | MicroEngineering Labs |
45 | Computer Friends, Inc. |
46 | ESS Technology |
47 | Audio, Inc. |
48 | Motorola, Inc. |
49 | Canopus Co., Ltd. |
50 | Seiko Epson Corporation |
51 | Truevision |
52 | Aztech Labs, Inc. |
53 | Videologic |
54 | SCALACS Inc. |
55 | Toshihiko Okuhara, Korg Inc. |
56 | Audio Processing Technology |
Коды устройств
Microsoft
Код | Устройство |
1 | Устройство отображения MIDI Mapper |
2 | Устройство отображения Wave Mapper |
3 | Выходной порт Sound Blaster MIDI |
4 | Входной порт Sound Blaster MIDI |
5 | Внутренний синтезатор Sound Blaster |
6 | Выходное устройство Sound Blaster для воспроизведения звука, записанного методом импульсно-кодовой модуляции PCM |
7 | Входное устройство PCM Sound Blaster |
9 | Синтезатор, совместимый с устройством Ad Lib |
10 | Выходной порт MIDI, совместимый с MPU401 |
11 | Водной порт MIDI, совместимый с MPU401 |
12 | Адаптер джойстика |
13 | Драйвер PC Speaker, предназначенный для вывода |
14 | Устройство ввода PCM MS Audio Board |
15 | Устройства вывода PCM MS Audio Board |
16 | Синтезатор MS Audio Board Stereo FM |
18 | Устройство ввода PCM MS OEM Audio Board |
19 | Устройство вывода PCM MS OEM Audio Board |
20 | Синтезатор MS OEM Audio Board Stereo FM |
21 | Дополнительный порт MS Audio Board Auxiliary Port |
22 | Дополнительный порт MS OEM Audio Auxiliary Port |
Creative Labs Inc.
Код | Устройство |
1 | Устройство ввода PCM Sound Blaster 1.5 |
2 | Устройство ввода PCM Sound Blaster 2.0 |
3 | Устройство ввода PCM Sound Blaster Pro |
4 | Устройство ввода PCM Sound Blaster Pro 16 |
101 | Устройство вывода PCM Sound Blaster 1.5 |
102 | Устройство вывода PCM Sound Blaster 2.0 |
103 | Устройство вывода PCM Sound Blaster Pro |
104 | Устройство вывода PCM Sound Blaster Pro 16 |
201 | Выходной порт Sound Blaster MIDI |
202 | Входной порт Sound Blaster MIDI |
301 | Синтезатор Sound Blaster FM |
302 | Синтезатор Sound Blaster Pro Stereo FM |
401 | Дополнительный порт для управления устройством чтения компакт-дисков Sound Blaster Pro aux |
402 | Дополнительный порт для управления устройством ввода с линии Sound Blaster Pro aux |
403 | Дополнительный порт для ввода с микрофона Sound Blaster Pro aux |
Artisoft Inc.
Код | Устройство |
1 | Устройство ввода PCM Artisoft Sound Board |
2 | Устройство вывода PCM Artisoft Sound Board Waveform |
IBM
Код | Устройство |
1 | Дополнительный порт IBM M-Motion |
2 | Устройство вывода PCM IBM M-Motion |
3 | Устройство ввода PCM IBM M-Motion |
Vocaltec LTD.
Код | Устройство |
1 | Устройство вывода PCM Vocaltec |
2 | Устройство ввода PCM Vocaltec |
Intel
Код | Устройство |
1 | Устройство ввода PCM HID2 |
101 | Устройство вывода PCM HID2 |
401 | Дополнительный порт HID2 |
Приложение DRVLIST
Приложение DRVLIST (листинг 2.13) поможет вам исследовать конфигурацию драйверов устройств мультимедиа, установленных в системе. Это приложение формирует в текущем каталоге текстовый файл с именем drvlist.txt и записывает в него конфигурацию драйверов.
Листинг 2.13 Файл drvlist\drvlist.cpp
// ---------------------------------------- // Просмотр параметров драйверов // для системы мультимедиа // ----------------------------------------
#define STRICT #include <windows.h> #include <mmsystem.h> #include <stdio.h> #include <string.h>
// =========================================== // Функция WinMain // ===========================================
#pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { FILE *out; // файл для вывода int i; // рабочий счетчик char buf[512]; // рабочий буфер
UINT nMMSystemVersion;
UINT nNumInDevs, nNumOutDevs; UINT nNumAuxDevs; UINT nNumMidiInDevs, nNumMidiOutDevs; UINT nNumJoyDevs;
WAVEOUTCAPS wcapsOutCaps; WAVEINCAPS wcapsInCaps; AUXCAPS auxcaps; MIDIINCAPS midicapsInCaps; MIDIOUTCAPS midicapsOutCaps; JOYCAPS joycaps; TIMECAPS timecaps;
DWORD dwFmt, dwSup; UINT wTech;
// Открываем выходной файл для вывода // текста потоком if ((out = fopen("drvlist.txt", "wt")) == NULL) { MessageBox(NULL, "Не могу открыть файл drvlist.txt", "Ошибка", MB_OK | MB_ICONSTOP); return 1; }
// Выводим заголовок файла fputs("* ================================= *\n", out); fputs("* DRVLIST, (C) Frolov A.V., 1994 *\n", out); fputs("* ================================= *\n", out);
nMMSystemVersion = mmsystemGetVersion(); wsprintf(buf, "\nВерсия mmsystem.dll: %d.%d\n\n", HIBYTE(nMMSystemVersion), LOBYTE(nMMSystemVersion));
// Выводим строку в файл fputs(buf, out);
nNumInDevs = waveInGetNumDevs(); nNumOutDevs = waveOutGetNumDevs(); nNumAuxDevs = auxGetNumDevs(); nNumMidiInDevs = midiInGetNumDevs(); nNumMidiOutDevs = midiOutGetNumDevs(); nNumJoyDevs = joyGetNumDevs();
for(i=0; i<nNumOutDevs; i++) { waveOutGetDevCaps(i, &wcapsOutCaps, sizeof(WAVEOUTCAPS));
wsprintf(buf, "\n%s, v. %X, " "wMid=%d, wPid=%d, wChannels=%d\n", (LPSTR)wcapsOutCaps.szPname, wcapsOutCaps.vDriverVersion, wcapsOutCaps.wMid, wcapsOutCaps.wPid, wcapsOutCaps.wChannels);
dwFmt = wcapsOutCaps.dwFormats; dwSup = wcapsOutCaps.dwSupport;
if(dwFmt & WAVE_FORMAT_1M08) strcat(buf, " 11025 КГц, моно,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_1S08) strcat(buf, "11025 КГц, стерео,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_1M16) strcat(buf, "11025 КГц, моно,\t16 бит\n"); if(dwFmt & WAVE_FORMAT_1S16) strcat(buf, "11025 КГц, стерео,\t16 бит\n");
if(dwFmt & WAVE_FORMAT_2M08) strcat(buf, "22050 КГц, моно,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_2S08) strcat(buf, "22050 КГц, стерео,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_2M16) strcat(buf, "22050 КГц, моно,\t16 бит\n"); if(dwFmt & WAVE_FORMAT_2S16) strcat(buf, "22050 КГц, стерео,\t16 бит\n");
if(dwFmt & WAVE_FORMAT_4M08) strcat(buf, "44100 КГц, моно,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_4S08) strcat(buf, "44100 КГц, стерео,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_4M16) strcat(buf, "44100 КГц, моно,\t16 бит\n"); if(dwFmt & WAVE_FORMAT_4S16) strcat(buf, "44100 КГц, стерео,\t16 бит\n");
if(dwSup & WAVECAPS_PITCH) strcat(buf, "Регулировка высоты тона\n"); if(dwSup & WAVECAPS_PLAYBACKRATE) strcat(buf, "Регулировка скорости воспроизведения\n"); if(dwSup & WAVECAPS_SYNC) strcat(buf, "Синхронный драйвер\n"); if(dwSup & WAVECAPS_VOLUME) strcat(buf, "Регулировка громкости\n"); if(dwSup & WAVECAPS_LRVOLUME) strcat(buf, "Раздельная регулировка громкости\n");
// Выводим строку в файл fputs(buf, out); }
for(i=0; i<nNumInDevs; i++) { waveInGetDevCaps(i, &wcapsInCaps, sizeof(WAVEINCAPS)); wsprintf(buf, "\n%s, v. %X, " "wMid=%d, wPid=%d, wChannels=%d\n", (LPSTR)wcapsInCaps.szPname, wcapsInCaps.vDriverVersion, wcapsInCaps.wMid, wcapsInCaps.wPid, wcapsInCaps.wChannels);
dwFmt = wcapsInCaps.dwFormats;
if(dwFmt & WAVE_FORMAT_1M08) strcat(buf, "11025 КГц, моно,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_1S08) strcat(buf, "11025 КГц, стерео,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_1M16) strcat(buf, "11025 КГц, моно,\t16 бит\n"); if(dwFmt & WAVE_FORMAT_1S16) strcat(buf, "11025 КГц, стерео,\t16 бит\n");
if(dwFmt & WAVE_FORMAT_2M08) strcat(buf, "22050 КГц, моно,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_2S08) strcat(buf, "22050 КГц, стерео,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_2M16) strcat(buf, "22050 КГц, моно,\t16 бит\n"); if(dwFmt & WAVE_FORMAT_2S16) strcat(buf, "22050 КГц, стерео,\t16 бит\n");
if(dwFmt & WAVE_FORMAT_4M08) strcat(buf, "44100 КГц, моно,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_4S08) strcat(buf, "44100 КГц, стерео,\t8 бит\n"); if(dwFmt & WAVE_FORMAT_4M16) strcat(buf, "44100 КГц, моно,\t16 бит\n"); if(dwFmt & WAVE_FORMAT_4S16) strcat(buf, "44100 КГц, стерео,\t16 бит\n");
// Выводим строку в файл fputs(buf, out); }
for(i=0; i<nNumAuxDevs; i++) { auxGetDevCaps(i, &auxcaps, sizeof(AUXCAPS));
wsprintf(buf, "\n%s, v. %X, wMid=%d, wPid=%d\n", (LPSTR)auxcaps.szPname, auxcaps.vDriverVersion, auxcaps.wMid, auxcaps.wPid);
wTech = auxcaps.wTechnology; dwSup = auxcaps.dwSupport;
if(wTech & AUXCAPS_CDAUDIO) strcat(buf, " Звуковой выход для внутреннего CD-ROM\n"); if(wTech & AUXCAPS_AUXIN) strcat(buf, "Ввод с линии\n");
if(dwSup & AUXCAPS_VOLUME) strcat(buf, "Регулировка громкости\n"); if(dwSup & AUXCAPS_LRVOLUME) strcat(buf, "Раздельная регулировка громкости\n");
// Выводим строку в файл fputs(buf, out); }
for(i=0; i<nNumMidiInDevs; i++) { midiInGetDevCaps(i, &midicapsInCaps, sizeof(MIDIINCAPS));
wsprintf(buf, "\n%s, v. %X, wMid=%d, wPid=%d\n", (LPSTR)midicapsInCaps.szPname, midicapsInCaps.vDriverVersion, midicapsInCaps.wMid, midicapsInCaps.wPid);
// Выводим строку в файл fputs(buf, out); }
for(i=0; i<nNumMidiOutDevs; i++) { midiOutGetDevCaps(i, &midicapsOutCaps, sizeof(MIDIOUTCAPS));
wsprintf(buf, "\n%s, v. %X, wMid=%d, wPid=%d\n", (LPSTR)midicapsOutCaps.szPname, midicapsOutCaps.vDriverVersion, midicapsOutCaps.wMid, midicapsOutCaps.wPid);
// Выводим строку в файл fputs(buf, out); }
for(i=0; i<nNumJoyDevs; i++) { joyGetDevCaps(i, &joycaps, sizeof(JOYCAPS));
wsprintf(buf, "\n%s, wMid=%d, wPid=%d\n", (LPSTR)joycaps.szPname, joycaps.wMid, joycaps.wPid);
// Выводим строку в файл fputs(buf, out); }
timeGetDevCaps(&timecaps, sizeof(TIMECAPS));
wsprintf(buf, "\nТаймер: wPeriodMin=%u, wPeriodMax=%u\n", (UINT)timecaps.wPeriodMin, (UINT)timecaps.wPeriodMax);
// Выводим строку в файл fputs(buf, out);
// Закрываем файл fclose(out);
MessageBox(NULL, "Список драйверов записан " "в файл drvlist.txt", "DRVLIST", MB_OK);
return 0; }
Приложение создает выходной файл и открывает его на запись с помощью стандартной функции fopen.
Затем вызывается функция mmsystemGetVersion, не имеющая параметров. Она возвращает слово, содержащее версию библиотеки mmsystem.dll. Старший байт этого слова содержит верхний (major) номер версии, младший - нижний (minor). Определенный с помощью этой функции номер версии библиотеки mmsystem.dll преобразуется в текстовую строку (при помощи функции wsprintf) и записывается в выходной файл функцией fputs.
Далее приложение определяет количество устройств мультимедиа, вызывая функции waveInGetNumDevs, waveOutGetNumDevs, auxGetNumDevs, midiInGetNumDevs, midiOutGetNumDevs. Приложение вызывает также функцию joyGetNumDevs , которая возвращает количество джойстиков, установленных в системе.
Затем приложение вызывает в нескольких циклах функции определения возможностей для каждого устройства, преобразуя результат в текстовые строки и записывая их в выходной файл.
Перед завершением работы приложение определяет возможности таймера, который тоже относится к устройствам мультимедиа.
Для этого вызывается функция timeGetDevCaps . Соответствующая структура TIMECAPS и указатели на нее определены в файле mmsystem.h следующим образом:
typedef struct timecaps_tag { UINT wPeriodMin; UINT wPeriodMax; } TIMECAPS; typedef TIMECAPS *PTIMECAPS; typedef TIMECAPS NEAR *NPTIMECAPS; typedef TIMECAPS FAR *LPTIMECAPS;
Поле wPeriodMin определяет минимальное значение периода, которое может использовать таймер, поле wPeriodMax - максимальное значение периода таймера (в миллисекундах). Эти параметры таймера могут быть различными не только для различных систем, но и для различных режимов работы операционной системы Windows (стандартном или расширенном).
Файл описания ресурсов содержит определение пиктограммы (листинг 2.14).
Листинг 2.14. Файл drvlist\drvlist.rc
APPICON ICON "drvlist.ico"
Файл определения модуля для приложения DRVLIST приведен в листинге 2.15.
Листинг 2.15. Файл drvlist\drvlist.def
NAME DRVLIST DESCRIPTION 'Приложение DRVLIST, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 5120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple
Приведем образец выходного файла, полученный с помощью приложения DRVLIST, запущенного на компьютере, оснащенном аппаратурой Sound Galaxy NX-Pro мультимедиа Upgrade Kit:
* ================================= * * DRVLIST, (C) Frolov A.V., 1994 * * ================================= * Версия mmsystem.dll: 1.1
Galaxy Wave-Out, v. 204, wMid=2, wPid=103, wChannels=2 11025 КГц, моно, 8 бит 11025 КГц, стерео, 8 бит 22050 КГц, моно, 8 бит 22050 КГц, стерео, 8 бит 44100 КГц, моно, 8 бит Регулировка громкости Раздельная регулировка громкости
Galaxy Wave-In, v. 204, wMid=2, wPid=3, wChannels=2 11025 КГц, моно, 8 бит 11025 КГц, стерео, 8 бит 22050 КГц, моно, 8 бит 22050 КГц, стерео, 8 бит 44100 КГц, моно, 8 бит
Sound Galaxy CD Audio, v. 101, wMid=2, wPid=401 Звуковой выход для внутреннего CD-ROM Регулировка громкости Раздельная регулировка громкости
Sound Galaxy Line In, v. 101, wMid=2, wPid=402 Ввод с линии Регулировка громкости Раздельная регулировка громкости
Sound Galaxy Microphone, v. 101, wMid=2, wPid=403 Регулировка громкости
Galaxy MIDI-In Port, v. 204, wMid=2, wPid=202 Sound Galaxy OPL3 FM, v. 100, wMid=7, wPid=32 Galaxy MIDI-Out Port, v. 204, wMid=2, wPid=201 Sound Galaxy NX-Pro FM Synth, v. 101, wMid=2, wPid=250 Sound Galaxy OPL3 FM, v. 100, wMid=7, wPid=32 Таймер: wPeriodMin=1, wPeriodMax=65535
Из содержимого файла видно, что в операционной системе Windows используется библиотека mmsystem.dll версии 1.1.
Для вывода звука, записанного с помощью импульсно-кодовой модуляции, используется устройство Galaxy Wave-Out, которое может работать со стандартными частотами дискретизации 11025, 22050 и 44100 Кгц (в действительности устройство Sound Galaxy NX-Pro может работать и с нестандартными значениями частоты дискретизации, однако функция waveOutGetDevCaps на дает возможности определить это). Устройство вывода имеет два канала (то есть способно выводить стереофонический сигнал), причем возможна раздельная регулировка громкости в каждом канале. Для представления одной выборки сигнала используется 8 бит.
В системе установлен также драйвер устройства ввода звуковой информации Galaxy Wave-In, который также является 8-битовым стереофоническим устройством.
Непосредственно на плате звукового адаптера Sound Galaxy NX-Pro имеется интерфейс устройства чтения компакт дисков. В системе установлен драйвер Sound Galaxy CD Audio, позволяющий проигрывать звуковые компакт-диски, а также драйверы других устройств, таких, как устройство ввода сигнала с линии, микрофона и музыкального синтезатора.
Приложение MCICDPL
Если вы будете разрабатывать проигрыватель звуковых компакт-дисков, то можете взять за основу приложение MCICDPL (рис. 3.1), которое работает с устройством чтения CD-ROM при помощи управляющих сообщений MCI.
Рис. 3.1. Главное окно приложения MCICDPL
Исходный текст приложения представлен в листинге 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
Приложение MCISTRVW
Приложение MCISTRVW (листинг 2.4) демонстрирует использование строчного интерфейса MCI для воспроизведения звукового файла с именем kaas.wav, расположенного в текущем каталоге (файл kaas.wav есть в каталоге mcistrvw на дискете, которая продается вместе с книгой). Это простейшее приложение не создает ни одного окна и, следовательно, не обрабатывает сообщения.
Листинг 2.4. Файл mcistrvw\mcistrvw.cpp
// ----------------------------------------------------- // Приложение MCISTRVW // Демонстрирует использование командных строк MCI // ----------------------------------------------------- #define STRICT #include <windows.h> #include <mmsystem.h>
void mciwioError(DWORD dwrc);
#pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { DWORD dwrc; BYTE szBuf[256], szBuf1[256];
// Открываем файл kaas.wav dwrc = mciSendString( (LPSTR)"open kaas.wav type waveaudio alias patr wait", (LPSTR)szBuf, 256, NULL); if(dwrc) mciwioError(dwrc);
// Получаем имя устройства, под которым оно установлено // в файле system.ini dwrc = mciSendString((LPSTR)"sysinfo patr installname wait", (LPSTR)szBuf, 256, NULL); if(dwrc) mciwioError(dwrc);
lstrcat(szBuf, (LPSTR)"\n");
// Добавляем к нему текстовое описание аппаратуры dwrc = mciSendString((LPSTR)"info patr product wait", (LPSTR)szBuf1, 256, NULL); if(dwrc) mciwioError(dwrc); lstrcat(szBuf, szBuf1);
// Выводим на экран полученную информацию об устройстве MessageBox(NULL, szBuf, "MCISTRWV", MB_ICONINFORMATION);
// Запускаем проигрывание в синхронном режиме dwrc = mciSendString((LPSTR)"play patr wait", (LPSTR)szBuf, 256, NULL); if(dwrc) mciwioError(dwrc);
// После завершения проигрывания закрываем устройство dwrc = mciSendString((LPSTR)"close patr", (LPSTR)szBuf, 256, NULL); if(dwrc) mciwioError(dwrc);
return 0; }
// ----------------------------------------------------- // Функция mciwioError // Выводит текстовое описание ошибки // ----------------------------------------------------- void mciwioError(DWORD dwrc) { BYTE szBuf[MAXERRORLENGTH];
// Если коду ошибки, переданному через параметр dwrc // соответствует текстовое описание, выводим его на экран if(mciGetErrorString(dwrc, (LPSTR)szBuf, MAXERRORLENGTH)) MessageBox(NULL, szBuf, "MCISTRVW Error", MB_ICONEXCLAMATION);
// В противном случае выводим сообщение о том, что это // неизвестная ошибка else MessageBox(NULL, "Неизвестная ошибка", "MCISTRVW Error", MB_ICONEXCLAMATION); }
Сразу после запуска приложение открывает устройство waveaudio с файлом kaas.wav, передавая ему следующую команду:
open kaas.wav type waveaudio alias patr wait
При этом устройству назначается алиас patr. Так как задан параметр wait, работа приложения будет продолжена только после завершения процесса открытия.
После открытия устройства приложение получает от него некоторую справочную информацию, и затем выводит ее на экран с помощью функции MessageBox.
Для проигрывания выдается следующая команда (используется алиас, назначенный при открытии устройства):
play patr wait
Работа приложения MCISTRVW приостанавливается до завершения процесса воспроизведения, однако при этом другие приложения могут работать.
Перед завершением своей работы приложение закрывает устройство:
close patr
Каждый раз после выдачи команды приложение проверяет код возврата функции mciSendString. Если он не равен нулю, вызывается обработчик ошибок (функция mciwioError), задача которого заключается в выводе текстового описания ошибки на экран. Для преобразования кода ошибки в текстовое сообщение используется функция mciGetErrorString.
Файл ресурсов приложения MCISTRVW приведен в листинге 2.5.
Листинг 2.5. Файл mcistrvw\mcistrvw.def
APPICON ICON "mcistrwv.ico"
Файл определения модуля вы сможете найти в листинге 2.6.
Листинг 2.6. Файл mcistrvw\mcistrvw.def
NAME MCISTRVW DESCRIPTION 'Приложение MCISTRVW, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple
Приложение MCITEST
В составе системы разработки приложений Microsoft SDK в качестве примера поставляются исходные тексты приложения MCITEST (загрузочный модуль этого приложения есть в каталоге SDK\MCITEST на дискете, которая продается вместе с книгой). Приложение MCITEST (рис. 2.1) удобно использовать для изучения строчного интерфейса MCI и для отладки последовательностей строчных команд MCI.
Рис. 2.1. Главное окно приложения MCITEST
Окно, расположенное в верхней части экрана, предназначено для ввода командных строк. Это окно представляет собой многострочный редактор текста, поэтому вы можете вводить сразу несколько строк. Введенные команды можно выполнять по отдельности, нажимая клавишу <Enter> или кнопку "Step", или все вместе (при помощи кнопки "Go!").
В окне "MCI Output" отображается результат выполнения операции. Если произошла ошибка, ее текстовое описание появляется в окне "Error".
Если в качестве одного из параметров команды была указана строка notyfy, в окне "Notification" отображается результат, переданный с сообщением MM_MCINOTIFY.
С помощью меню "File" вы можете сохранять и загружать последовательности команд MCI, проверяя их в работе.
Приложение MCIWAVER
В качестве примера использования интерфейса сообщений MCI приведем исходные тексты несложного приложения MCIWAVER, с помощью которого можно записывать и воспроизводить wav-файлы (рис. 2.2).
Рис. 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
Приложение MCIWND
Приложение MCIWND создает окно MCI (рис. 5.10) и... все!
Рис. 5.10. Окно MCI
Это окно, когда в него ничего не загружено, имеет стандартный заголовок и три органа управления - кнопку воспроизведения, кнопку доступа к меню и полосу просмотра.
Запустите загрузочный модуль этого приложения и нажмите кнопку доступа к меню (рис.5.11).
Рис. 5.11. Кнопка доступа к меню
На экране появится почти стандартная диалоговая панель "Open", с помощью которой вы сможете выбрать файл мультимедиа для просмотра или прослушивания (рис. 5.12).
Рис. 5.12. Диалоговая панель "Open"
Обратите внимание, что в правом нижнем углу диалоговой панели имеются органы управления (кнопка и полоса просмотра), предназначенные для предварительного просмотра или прослушивания содержимого файла. Это очень удобно, так как при поиске файла не нужно много раз открывать и закрывать диалоговую панель "Open".
После загрузки wav-файла меню приложения модифицируется (рис. 5.13).
Рис. 5.13. Меню для работы с wav-файлом
В нем появляется строка "Close", с помощью которой можно закрыть файл, строка "Copy", позволяющая скопировать содержимое файла в Clipboard, а также строка "Command...". Последняя предназначена для передачи устройству произвольной управляющей строки MCI (соответствующая диалоговая панель показана на рис. 5.14).
Рис. 5.14. Диалоговая панель для передачи управляющей строки MCI
При выборе avi-файлов можно посмотреть их содержимое в небольшом окне (рис. 5.15).
Рис. 5.15. Выбор avi-файла
Для работы с avi-файлами используется расширенное меню (рис. 5.16).
Рис. 5.16. Меню для работы с avi-файлами
Строка "View" предназначена для управления размером окна. С помощью строк "Volume" и "Speed" можно регулировать, соответственно, громкость звука и скорость воспроизведения видео. Строка "Configure..." предназначена для установки параметров проигрывателя. При ее выборе на экране появляется диалоговая панель "Video Playback Options" (рис. 5.17).
Рис. 5.17. Диалоговая панель "Video Playback Options"
В поле " Video Mode" вы можете включить режим отображения видео в окне (переключатель "Window") или на полном экране видеомонитора (переключатель "Full Screen"). Переключатель "Zoom by 2" позволяет уменьшить размер окна в два раза.
Если включен переключатель "Skip video frames if behind", для обеспечения непрерывности звука при необходимости будут пропускаться видеокадры.
Таким образом, приложение MCIWND выполняет довольно много функций. Вы, возможно, будете удивлены тем, что исходный текст приложения занимает не более двух страниц (листинг 5.1).
Листинг 5.1. Файл mciwnd/mciwnd.c
#include <windows.h> #include <vfw.h>
static char szAppName[]="MCIWnd"; static HWND hwnd;
// ===================================== // Функция WinMain // ===================================== int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; WORD wVersion;
// Проверяем версию Video for Windows wVersion = HIWORD(VideoForWindowsVersion()); if(wVersion < 0x010a) { MessageBox(NULL, "Используйте Video for Windows" " версии 1.1 или более поздней версии", "MCIWnd Error", MB_OK | MB_ICONHAND); return FALSE; }
// Создаем окно класса MCIWND hwnd = MCIWndCreate(NULL, hInstance, MCIWNDF_SHOWNAME | MCIWNDF_SHOWMODE | WS_OVERLAPPEDWINDOW | WS_VISIBLE, NULL);
if(hwnd == NULL) return -1;
// Устанавливаем заголовок окна SetWindowText(hwnd, szAppName);
// Запускаем цикл обработки сообщений while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg);
if(!IsWindow(hwnd)) PostQuitMessage(0); } return msg.wParam; }
Исходный текст приложения подготовлен для трансляторов Microsoft C++ версии 7.0 или Microsoft Visual C++ версий 1.0 или 1.5, так как поставляющаяся в составе Video for Windows Development Kit библиотека vfw.lib совместима именно с этими трансляторами.
После файла windows.h в исходный текст необходимо включить файл vfw.h , который поставляется вместе с Video for Windows Development Kit. Он содержит определения всех необходимых констант, структур данных и прототипы функций.
В самом начале работы приложение вызывает функцию VideoForWindowsVersion , возвращающую в старшем слове версию Video for Windows:
wVersion = HIWORD(VideoForWindowsVersion());
Версия Video for Windows должна быть не ниже 1.1.
Далее приложение создает окно MCI, вызывая для этого функцию MCIWndCreate :
hwnd = MCIWndCreate(NULL, hInstance, MCIWNDF_SHOWNAME | MCIWNDF_SHOWMODE | WS_OVERLAPPEDWINDOW | WS_VISIBLE, NULL);
Функция MCIWndCreate позволяет определить обычные стили окна, такие как WS_OVERLAPPEDWINDOW и WS_VISIBLE, а также специфические для окна MCI - MCIWNDF_SHOWNAME и MCIWNDF_SHOWMODE. Позже мы рассмотрим подробнее эту функцию и дополнительные стили окна.
После создания окна запускается цикл обработки сообщений, который имеет одну особенность - в нем периодически вызывается функция IsWindow , которая определена в стандартном программном интерфейсе Windows:
if(!IsWindow(hwnd)) PostQuitMessage(0);
Эта функция проверяет идентификатор окна, передаваемого ей через параметр. Если этот идентификатор правильный, возвращается TRUE. После того как пользователь уничтожит окно MCI, его идентификатор станет недействительным. В этом случае функция IsWindow вернет значение FALSE и будет вызвана функция PostQuitMessage, в результате чего работа приложения завершится.
Вот и все! По сложности исходного текста это приложение напоминает наши первые приложения из 10 тома "Библиотеки системного программиста", однако выполняемые им функции во много раз сложнее. Все дело тут, разумеется, в реализации класса окна MCI.
Файл описания ресурсов приведен в листинге 5.2.
Листинг 5.2. Файл mciwnd/mciwnd.rc
AppIcon ICON mciwnd.ico
Файл определения модуля для приложения MCIWND не имеет никаких особенностей (листинг 5.3).
Листинг 5.3. Файл mciwnd/mciwnd.def
NAME MCIWNDPL DESCRIPTION 'Приложение MCIWND, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8196 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple
Теперь о том, как из исходного текста создать загрузочный модуль приложения MCIWND.
При использовании транслятора Microsoft C++ версии 7.0 вы можете воспользоваться файлом makefile , представленном в листинге 5.3.
Листинг 5.3. Файл mciwnd/makefile
NAME = mciwnd OBJ = mciwnd.obj LIBS = libw slibcew vfw
!if "$(DEBUG)" == "NO" DEF = CLOPT = MASMOPT = LINKOPT = !else DEF = -DDEBUG CLOPT = -Zid MASMOPT = -Zi LINKOPT = /CO/LI !endif
CC = cl -c -W3 -AS -Zp -G2sw -Oxas $(DEF) $(CLOPT) -DWIN31 ASM = masm -Mx $(MASMOPT) LINK= link /NOE/NOD/LI/MAP/AL:16/ONERROR:NOEXE $(LINKOPT) RC = rc .c.obj: $(CC) $*.c .asm.obj: $(ASM) $*; goal: $(NAME).exe $(NAME).exe: $(OBJ) $(NAME).res $(NAME).def makefile $(LINK) $(OBJ), $(NAME), $(NAME),$(LIBS), $(NAME).def $(RC) -31 $(NAME).res -mapsym $(NAME).map $(NAME).res: $(NAME).rc $(NAME).ico $(RC) -r $(NAME).rc clean: del $(NAME).exe del *.res del *.obj del *.map del *.sym del *.pdb copy: copy $(NAME).exe ..\..\bin copy $(NAME).sym ..\..\bin depend: mv makefile makefile.old sed "/^# START Dependencies/,/^# END Dependencies/D" makefile.old > makefile del makefile.old echo # START Dependencies >> makefile includes -l *.c *.asm >> makefile echo # END Dependencies >> makefile
Особенностью транслятора Microsoft C++ версии 7.0 (а также версии 8.0, входящей в состав Visual C++, и трансляторов более ранних версий, запускаемых в среде MS-DOS) является использование переменных среды. Для правильной установки переменных среды подготовьте bat-файл, содежащий следующие команды (предполагается, что транслятор установлен в каталоге g:msvc, а Video for Windows Development Kit - в каталоге g:\vfwdk):
@echo off set TOOLROOTDIR=G:\MSVC set PATH=G:\MSVC\BIN;%PATH% set INCLUDE=G:\MSVC\INCLUDE;g:\vfwdk\inc;%INCLUDE% set LIB=G:\MSVC\LIB;g:\vfwdk\lib;g:\windev\lib;%LIB% set INIT=G:\MSVC;%INIT%
Находясь в среде MS-DOS, сделайте текущим каталог, содержащий все файлы приложения MCIWND, и, после запуска приведенного выше пакетного файла, запустите программу nmake без параметров. В результате будет создан загрузочный модуль приложения.
Для того чтобы загрузочный модуль не содержал отладочной информации, добавьте в начало файла makefile следующую строку:
DEBUG = NO
Можно также запустить программу nmake с параметром DEBUG=NO:
nmake DEBUG=NO
Намного удобнее работать в среде Visual C++. Вместе с исходными текстами приложения MCIWND на дискете поставляется файл проекта mciwnd.mak, предназначенный для системы Visual C++.
Запустите Visual C++ и из меню "Project" выберите строку "Open...". С помощью появившейся диалоговой панели откройте файл проекта mciwnd.mak. Нажмите на самую левую кнопку в полосе инструментов. Появится список файлов, имеющих отношение к проекту. Выберите файл исходного текста. Возможно, что при этом вместо русских букв в окне редактирования вы увидите нечто, не поддающееся прочтению. В этом случае следует установить для редактора шрифт с русскими буквами.
Для изменения шрифта сделайте текущим окно редактирования и выберите в меню "Options" строку "Font...". На экране появится диалоговая панель "Font". Выберите в ней подходящий шрифт, имеющий русские буквы, и нажмите кнопку "Use as Default Font". При этом для всех создаваемыех вновь окон будет использоваться выбранный вами шрифт. Для завершения работы с диалоговой панелью нажмите кнопку "OK".
Не забудьте добавить пути к каталогам vfwdk\inc, wfwdk\lib и windev\lib с помощью диалоговой панели "Directories", которую можно вызвать, если в меню "Options" выбрать строку "Directories...".
После всех этих подготовительных действий выберите из меню "Project" строку "Build MCIWND.EXE". Будет запущен процесс создания загрузочного модуля (в фоновом режиме). После завершения этого процесса можно запустить приложение, выбрав из этого же меню строку "Execute MCIWND.EXE", или перейти в режим отладки, воспользовавшись меню "Debug".
Приложение MCIWNDC
Теперь нашей задачей будет создание такого приложения, которое выполняет управление окном MCI с помощью перечисленных выше макрокоманд.
Приложение MCIWNDC (рис. 5.18) может проигрывать avi- и wav-файлы, файлы в стандарте MIDI, а также дорожки звуковых компакт-дисков. Кроме этого, приложение может записывать wav-файлы.
Рис. 5.18. Меню "File" приложения MCIWNDC
Для проигрывания файлов мультимедиа их следует открыть при помощи строки "Open..." меню "File". При этом на экран будет выведена диалоговая панель "Open" с возможностью предварительного просмотра или прослушивания содержимого файла (рис. 5.19).
Рис. 5.19. Диалоговая панель "Open" приложения MCIWNDC
Меню "Movie" (рис. 5.20) и "Styles" (рис. 5.22) предназначены для управления окном MCI.
Рис. 5.20. Меню "Movie" приложения MCIWNDC
Строки "Play", "Play Reverse", "Record" и "Stop" предназначены, соответственно, для проигрывания, проигрывания в обратном направлении, записи и выполнения останова.
С помощью строк "Home" и "End" выполняется позиционирование на начало и конец файла. Строки "Step Fwrd" и "Step Back" дают возможность выполнять пошаговое перемещение вперед и назад.
Выбрав строку "Info...", вы увидите на экране диалоговую панель "Media Info" (рис. 5.21), в которой будет отображено имя устройства, путь к загруженному в окно MCI файлу и размер этого файла.
Рис. 5.21. Диалоговая панель "Media Info"
С помощью строки "Play Bar" меню "Styles" (рис. 5.22) вы можете убрать или возвратить на место органы управления окном MCI.
Рис. 5.22. Меню "Styles" приложения MCIWNDC
Диалоговая панель, используемая при сохранении записанных wav-файлов, содержит средства предварительного просмотра или прослушивания (рис. 5.23).
Рис. 5.23. Диалоговая панель "Save As..." приложения MCIWNDC
Приложение MCIWNDC способно также проигрывать дорожки звуковых компакт-дисков. Внешний вид окна MCI, которое используется для этого, показан на рис. 5.24.
Рис. 5.24. Окно MCI для проигрывания дорожек звукового компакт-диска
Несмотря на обилие возможностей, исходный текст приложения MCIWNDC занимает немного места (листинг 5.4).
Листинг 5.4. Файл mciwndc/mciwndc.cpp
// ------------------------------------------------ // Приложение MCIWNDC // Использование класса окна MCIWnd для // проигрывания и записи файлов мультимедиа // ------------------------------------------------
#define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <memory.h> #include <vfw.h> #include "mciwndc.h"
// Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); BOOL mciwndSelectFile(LPSTR lpszFileName);
// Глобальные переменные char const szClassName[] = "MCIWNDCClass"; char const szMovieClass[] = MCIWND_WINDOW_CLASS; char const szWindowTitle[] = "MCIWnd Player & Recorder"; HINSTANCE hInst; HWND hwndMovie = NULL;
// ===================================== // Функция WinMain // ===================================== int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения WORD wVersion; // версия Video for Windows
if(hPrevInstance) return FALSE;
// Проверяем версию Video for Windows wVersion = HIWORD(VideoForWindowsVersion()); if(wVersion < 0x010a) { MessageBox(NULL, "Используйте Video for Windows" " версии 1.1 или более поздней версии", "MCIWnd Error", MB_OK | MB_ICONHAND); return FALSE; }
if(!InitApp(hInstance)) return FALSE;
hInst = hInstance;
hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // размеры и расположение окна CW_USEDEFAULT, 400, 350, 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) { switch (msg) { // ------------------------------------------------------------ // WM_INITMENU // Инициализация меню // ------------------------------------------------------------ case WM_INITMENU: { // Определяем стиль окна MCI WORD wStyles = MCIWndGetStyles(hwndMovie);
// Если окно MCI имеет панель управления, // отмечаем строку "Play Bar" меню "Styles" CheckMenuItem(GetMenu(hwnd), CM_STPBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? MF_UNCHECKED : MF_CHECKED); return 0; }
// ------------------------------------------------------------ // WM_COMMAND // Обработка сообщений от меню // ------------------------------------------------------------ case WM_COMMAND: { switch (wParam) { // ------------------------------------------------- // Строка "About" меню "Help" // ------------------------------------------------- case CM_HELPABOUT: { MessageBox(hwnd, "MCIWnd Player & Recorder, v.1.0\n" "(C) Frolov A.V., 1994", "About MCIWNDC", MB_OK | MB_ICONINFORMATION); return 0; }
// ------------------------------------------------- // Строка "New Waveaudio" меню "File" // ------------------------------------------------- case CM_FILENEW: { // Если окно MCI уже было создано, удаляем его if(hwndMovie) MCIWndDestroy(hwndMovie);
// Создаем новое окно для записи звука, // открываем драйвер устройства waveaudio hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"waveaudio");
// Создаем новый файл MCIWndNew(hwndMovie, "waveaudio"); return 0; }
// ------------------------------------------------- // Строка "Save Waveaudio As..." меню "File" // ------------------------------------------------- case CM_FILESAVEAS: { // Создаем диалоговую панель "Save As..." для // сохранения файла MCIWndSaveDialog(hwndMovie); return 0; }
// ------------------------------------------------- // Строка "CD Audio" меню "File" // ------------------------------------------------- case CM_FILECDAUDIO: { // Если окно MCI уже было создано, удаляем его if(hwndMovie) MCIWndDestroy(hwndMovie);
// Создаем новое окно для проигрывания CD, // открываем драйвер устройства cdaudio hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"cdaudio"); return 0; }
// ------------------------------------------------- // Строка "Open" меню "File" // ------------------------------------------------- case CM_FILEOPEN: { char szBuff[256];
// Если окно MCI уже было создано, удаляем его if(hwndMovie) MCIWndDestroy(hwndMovie);
// Выбираем файл для загрузки в окно MCI if(mciwndSelectFile(szBuff)) { // Создаем окно MCI hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)szBuff); } return 0; }
// ------------------------------------------------- // Строка "Close" меню "File" // ------------------------------------------------- case CM_FILECLOSE: { // Удаляем окно MCI MCIWndDestroy(hwndMovie); hwndMovie = NULL; return 0; }
// ------------------------------------------------- // Меню "Movie" // ------------------------------------------------- case CM_MVIPLAY: // "Play" { // Проигрывание MCIWndPlay(hwndMovie); return 0; }
case CM_MVIRPLAY: // "Play Reverse" { // Проигрывание в обратном направлении MCIWndPlayReverse(hwndMovie); return 0; }
case CM_MVISTOP: // "Stop" { // Останов MCIWndStop(hwndMovie); return 0; }
case CM_MVIRECORD: // "Record" { // Запись MCIWndRecord(hwndMovie); return 0; }
case CM_MVIHOME: // "Home" { // Позиционирование в начало MCIWndHome(hwndMovie); return 0; }
case CM_MVIEND: // "End" { // Позиционирование в конец MCIWndEnd(hwndMovie); return 0; }
case CM_MVISTEP: // "Step Fwrd" { // Шаг вперед MCIWndStep(hwndMovie, 1); return 0; }
case CM_MVIRSTEP: // "Step Back" { // Шаг назад MCIWndStep(hwndMovie, -1); return 0; }
case CM_MVIINFO: // "Info..." { char szBuff[512], szBuff1[256]; long dwSize;
// Если окно MCI создано, выводим информацию // о загруженном в него файле if(hwndMovie) { // Имя устройства MCIWndGetDevice(hwndMovie, (LPSTR)szBuff, 512); lstrcat(szBuff, (LPSTR)"\n");
// Путь к файлу или имя устройства MCIWndGetFileName(hwndMovie, (LPSTR)szBuff1, 256); lstrcat(szBuff, (LPSTR)szBuff1); lstrcat(szBuff, (LPSTR)"\n");
// Размер файла dwSize = MCIWndGetLength(hwndMovie); wsprintf(szBuff1, "Size: %ld ", (long)dwSize); lstrcat(szBuff, (LPSTR)szBuff1);
// Формат времени MCIWndGetTimeFormat(hwndMovie, (LPSTR)szBuff1, 256); lstrcat(szBuff, (LPSTR)szBuff1);
MessageBox(hwnd, szBuff, "Media Info", MB_OK | MB_ICONINFORMATION); } return 0; } // ------------------------------------------------- // Меню "Styles" // ------------------------------------------------- case CM_STPBAR: // "Play Bar" { // Определяем стили окна MCI WORD wStyles = MCIWndGetStyles(hwndMovie);
// Инвертируем состояние стиля MCIWNDF_NOPLAYBAR MCIWndChangeStyles(hwndMovie, MCIWNDF_NOPLAYBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? 0 : MCIWNDF_NOPLAYBAR); return 0; }
// ------------------------------------------------- // Строка "Exit" меню "File" // Завершение работы приложения // ------------------------------------------------- case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; } default: return 0; } }
// Отслеживаем изменения в системной палитре case WM_PALETTECHANGED: { SendMessage(hwndMovie, msg, wParam, lParam); break; } case WM_QUERYNEWPALETTE: { return SendMessage(hwndMovie, msg, wParam, lParam); }
// ------------------------------------------------------------ // WM_DESTROY // Уничтожение главного окна приложения // ------------------------------------------------------------ case WM_DESTROY: { PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); }
//----------------------------------------------------- // mciwndSelectFile // Выбор файла //----------------------------------------------------- BOOL mciwndSelectFile(LPSTR lpszFileName) { OPENFILENAME ofn;
char szFile[256]; char szFileTitle[256]; char szFilter[256] = "Video Files\0*.avi\0" "Waveaudio Files\0*.wav\0" "MIDI Files\0*.mid;*.rmi\0" "Any 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 (GetOpenFileNamePreview(&ofn)) { // Копируем путь к выбранному файлу lstrcpy(lpszFileName, (LPSTR)szFile); return TRUE; } else return FALSE; }
После проверки версии Video for Windows функция WinMain создает обычным образом главное окно приложения MCIWNDC и запускает цикл обработки сообщений. Эта часть приложения не имеет каких-либо особенностей.
При выборе строки "New Waveaudio" из меню "File" проверяется содержимое глобальной переменной hwndMovie, в которой хранится идентификатор окна MCI. Сразу после запуска приложения эта переменная содержит нулевое значение.
Если же окно MCI было создано ранее, оно удаляется при помощи макрокоманды MCIWndDestroy:
if(hwndMovie) MCIWndDestroy(hwndMovie);
Далее создается окно MCI, причем одновременно открывается устройство waveaudio:
hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"waveaudio");
Создаваемое при этом окно MCI является видимым, дочерним и имеет рамку. Среди органов управления присутствует кнопка записи, так как указан стиль окна MCIWNDF_RECORD.
Затем создается новый файл, для чего используется макрокоманда MCIWndNew:
MCIWndNew(hwndMovie, "waveaudio");
Сохранение записанных звуковых данных выполняется очень просто - с помощью макрокоманды MCIWndSaveDialog, позволяющей пользователю выбрать путь и имя файла:
MCIWndSaveDialog(hwndMovie);
При выборе строки "CD Audio" в меню "File" окно MCI создается следующим образом:
hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"cdaudio");
Обратите внимание, что указан стиль MCIWNDF_RECORD. Так как драйвер устройства чтения компакт-дисков не поддерживает (увы!) операцию записи, среди органов управления окна MCI кнопка записи так и не появится. Поэтому при создании собственного приложения для проигрывания компакт-дисков средствами окна MCI вам не нужно указывать этот стиль.
Если приложение MCIWNDC используется для проигрывания файла, окно MCI создается при выборе строки "Open..." в меню "File". В этом случае приложение вызывает функцию mciwndSelectFile, которая позволяет пользователю выбрать файл и записывает путь к файлу в переменную szBuff. Далее окно MCI создается следующим образом:
hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)szBuff);
При создании окна MCI указан стиль MCIWNDF_RECORD, однако кнопка записи появится только при загрузке wav-файла, так как запись средствами окна MCI возможна только для устройства "waveaudio".
При выборе из меню "File" строки "Close" окно MCI удаляется функцией MCIWndDestroy.
Обработка сообщений от меню "Movie" сводится в основном к вызову соответствующей макрокоманды. Например, при выборе из этого меню строки "Play" вызывается макрокоманда MCIWndPlay:
case CM_MVIPLAY: // "Play" { MCIWndPlay(hwndMovie); return 0; }
Аналогичным образом обрабатываются остальные команды, за исключением команд позиционирования на один шаг вперед и один шаг назад:
case CM_MVISTEP: // "Step Fwrd" { MCIWndStep(hwndMovie, 1); return 0; } case CM_MVIRSTEP: // "Step Back" { MCIWndStep(hwndMovie, -1); return 0; }
Второй параметр макрокоманды MCIWndStep указывает величину шага в миллисекундах или кадрах (в зависимости от текущего формата времени), причем отрицательным значениям соответствует позиционирование в обратном направлении.
При выборе из меню "Movie" строки "Info..." приложение определяет и выводит на экран информацию об используемом устройстве и загруженном файле.
Для определения имени устройства используется макрокоманда MCIWndGetDevice :
MCIWndGetDevice(hwndMovie, (LPSTR)szBuff, 512);
Через первый параметр этой макрокоманде передается идентификатор окна MCI. Второй параметр - указатель на буфер, в который следует записать имя устройства. Третий параметр - размер буфера.
Путь к загруженному устройству или имя устройства (если файл не используется) определяется с помощью макрокоманды MCIWndGetFileName аналогичным образом:
MCIWndGetFileName(hwndMovie, (LPSTR)szBuff1, 256);
Размер файла вычисляется макрокомандой MCIWndGetLength , возвращающей значение в формате двойного слова:
dwSize = MCIWndGetLength(hwndMovie);
Единица измерения размера зависит от текущего формата времени, который определяется в виде текстовой строки при помощи макрокоманды MCIWndGetTimeFormat :
MCIWndGetTimeFormat(hwndMovie, (LPSTR)szBuff1, 256);
Теперь о меню "Styles".
При выборе из этого меню строки "Play Bar" приложение определяет текущий стили окна, и затем инвертирует стиль MCIWNDF_NOPLAYBAR:
case CM_STPBAR: // "Play Bar" { WORD wStyles = MCIWndGetStyles(hwndMovie); MCIWndChangeStyles(hwndMovie, MCIWNDF_NOPLAYBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? 0 : MCIWNDF_NOPLAYBAR); return 0; }
Правильную отметку строки "Play Bar" в меню обеспечивает обработчик сообщения WM_INITMENU:
case WM_INITMENU: { WORD wStyles = MCIWndGetStyles(hwndMovie); CheckMenuItem(GetMenu(hwnd), CM_STPBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? MF_UNCHECKED : MF_CHECKED); return 0; }
Этот обработчик отмечает или нет строку "Play Bar" меню "Styles" в зависимости от того, имеет ли окно MCI стиль MCIWNDF_NOPLAYBAR, или нет.
Обработка сообщений об изменении системной палитры WM_PALETTECHANGED и о необходимости реализации палитры WM_QUERYNEWPALETTE заключается в непосредственной передаче соответствующих сообщений окну MCI, поэтому выполняется очень просто:
case WM_PALETTECHANGED: { SendMessage(hwndMovie, msg, wParam, lParam); break; } case WM_QUERYNEWPALETTE: { return SendMessage(hwndMovie, msg, wParam, lParam); }
Файл mciwndc.h (листинг 5.5) содержит определения констант, используемых в приложении MCIWNDC.
Листинг 5.5. Файл mciwndc/mciwndc.h
#define CM_HELPABOUT 301 #define CM_FILEEXIT 302 #define CM_FILEOPEN 303 #define CM_FILECLOSE 304 #define CM_FILENEW 305 #define CM_FILESAVEAS 306 #define CM_FILECDAUDIO 307
#define CM_MVIPLAY 401 #define CM_MVIRPLAY 402 #define CM_MVIHOME 403 #define CM_MVIEND 404 #define CM_MVISTEP 405 #define CM_MVIRSTEP 406 #define CM_MVISTOP 407 #define CM_MVIRECORD 408 #define CM_MVIINFO 409
#define CM_STPBAR 501
Файл определения ресурсов приложения представлен в листинге 5.6.
Листинг 5.6. Файл mciwndc/mciwndc.rc
#include "mciwndc.h" AppIcon ICON mciwndc.ico APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open...", CM_FILEOPEN MENUITEM "&Close", CM_FILECLOSE MENUITEM SEPARATOR MENUITEM "&New Waveaudio", CM_FILENEW MENUITEM "Save Waveaudio &As...", CM_FILESAVEAS MENUITEM SEPARATOR MENUITEM "CD Audio", CM_FILECDAUDIO MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END
POPUP "&Movie" BEGIN MENUITEM "&Play", CM_MVIPLAY MENUITEM "Play &Reverse", CM_MVIRPLAY MENUITEM "R&ecord", CM_MVIRECORD MENUITEM "&Stop", CM_MVISTOP MENUITEM SEPARATOR MENUITEM "&Home", CM_MVIHOME MENUITEM "&End", CM_MVIEND MENUITEM SEPARATOR MENUITEM "Step &Fwrd", CM_MVISTEP MENUITEM "Step &Back", CM_MVIRSTEP MENUITEM SEPARATOR MENUITEM "&Info...", CM_MVIINFO END POPUP "St&yles" BEGIN MENUITEM "&Play Bar", CM_STPBAR END
POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END
Файл определения модуля приложения MCIWNDC представлен в листинге 5.7.
Листинг 5.7. Файл mciwndc/mciwndc.def
NAME MCIWNDC DESCRIPTION 'Приложение MCIWNDC, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 10240 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple
Для трансляции приложения в среде MS-DOS системой Microsoft C++ версии 7.0 или 8.0 (входящий в Visual C++ версии 1.0) вы можете использовать makefile, представленный в листинге 5.8.
Листинг 5.8. Файл mciwndc/makefile
NAME = mciwndc OBJ = mciwndc.obj LIBS = libw slibcew commdlg vfw
!if "$(DEBUG)" == "NO" DEF = CLOPT = MASMOPT = LINKOPT = !else DEF = -DDEBUG CLOPT = -Zid MASMOPT = -Zi LINKOPT = /CO/LI !endif
CC = cl -c -W3 -AS -Zp -G2sw -Oxas $(DEF) $(CLOPT) -DWIN31 ASM = masm -Mx $(MASMOPT) LINK= link /NOE/NOD/LI/MAP/AL:16/ONERROR:NOEXE $(LINKOPT) RC = rc .c.obj: $(CC) $*.c .asm.obj: $(ASM) $*; goal: $(NAME).exe $(NAME).exe: $(OBJ) $(NAME).res $(NAME).def makefile $(LINK) $(OBJ), $(NAME), $(NAME),$(LIBS), $(NAME).def $(RC) -31 $(NAME).res -mapsym $(NAME).map $(NAME).res: $(NAME).rc $(RC) -r $(NAME).rc clean: del $(NAME).exe del *.res del *.obj del *.map del *.sym del *.pdb copy: copy $(NAME).exe ..\..\bin copy $(NAME).sym ..\..\bin depend: mv makefile makefile.old sed "/^# START Dependencies/,/^# END Dependencies/D" makefile.old > makefile del makefile.old echo # START Dependencies >> makefile includes -l *.c *.asm >> makefile echo # END Dependencies >> makefile
Приложение MIDIPL
Приложение MIDIPL (рис. 4.2) демонстрирует способы использования некоторых функций MCI для проигрывания файлов MIDI.
Рис. 4.2. Приложение MIDIPL
По своей структуре оно напоминает приложение MCIWAWER, которое может проигрывать и записывать wav-файлы. Поэтому мы рассмотрим только отличия, специфические для работы с устройством sequencer.
Основной файл исходных текстов приложения MIDIPL приведен в листинге 4.1.
Листинг 4.1. Файл midipl/midipl.cpp
// ------------------------------------------------ // Приложение MIDIPL // Проигрывание файлов MIDI // с помощью интерфейса сообщений MCI // ------------------------------------------------
#define STRICT #include <windows.h> #include <windowsx.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop
#include "midipl.hpp" #include "midiio.hpp"
// Идентификатор таймера #define BEEP_TIMER 1
// Идентификатор полосы просмотра #define ID_SCROLL 10
// Длина полосы просмотра #define SCROLL_SIZE 400
// Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
// Глобальные переменные int nMode = MODE_STOP; MMTIME mmtimeOut; BOOL fFileLoaded = FALSE; int nPosition; HWND hScroll; UINT wOutDeviceID; BYTE szFileName[128]; DWORD dwFileSize;
char const szClassName[] = "MCIMIDIClass"; char const szWindowTitle[] = "MIDI Player"; 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;
// Создаем таймер 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_PLAYING) TextOut(hdc, 10, 10, "Идет проигрывание...", 20); 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, "MIDI Player, v.1.0\n" "(C) Frolov A.V., 1994", "About MIDIPL", MB_OK | MB_ICONINFORMATION); return 0; }
// ------------------------------------------------- // Строка "Open" меню "File" // ------------------------------------------------- case CM_FILEOPEN: { char szTitle[256];
// Загружаем новый файл if(!mcimidiSelectFile(szFileName)) return 0;
// Отображаем в заголовке окна путь к файлу lstrcpy(szTitle, szWindowTitle); lstrcat(szTitle, " - "); lstrcat(szTitle, szFileName); SetWindowText(hwnd, szTitle);
// Если было запущено воспроизведение, // останавливаем его и закрываем устройство вывода if(wOutDeviceID) { mcimidiStop(wOutDeviceID); mcimidiClose(wOutDeviceID); wOutDeviceID = 0;
// Новый режим nMode = MODE_STOP;
// Перерисовываем окно для отображения строки, // соответствующей новому режиму InvalidateRect(hwnd, NULL, TRUE); }
// Устанавливаем движок в начало полосы просмотра nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);
// Устанавливаем флаг загрузки файла fFileLoaded = TRUE; return 0; }
// ------------------------------------------------- // Строка "Play!" // Проигрывание загруженного файла MIDI // ------------------------------------------------- case CM_CTLPLAY: { // Если файл загружен и не проигрывается, // запускаем проигрывание файла if((fFileLoaded == TRUE) && (nMode == MODE_STOP)) { // Новый режим nMode = MODE_PLAYING;
// Перерисовываем окно для отображения строки, // соответствующей новому режиму InvalidateRect(hwnd, NULL, TRUE);
// Если устройство не было открыто раньше, // открываем его if(!wOutDeviceID) wOutDeviceID = mcimidiOpen((LPSTR)szFileName);
// Проигрываем файл mcimidiPlay(hwnd, wOutDeviceID); } return 0; }
// ------------------------------------------------- // Строка "Stop!" // Останов проигрывания или записи файла MIDI // ------------------------------------------------- case CM_CTLSTOP: { if(nMode == MODE_PLAYING nMode == MODE_PLAYINGPAUSED) { // Останавливаем проигрывание mcimidiStop(wOutDeviceID); }
// Устанавливаем движок в начало полосы просмотра nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);
// Новый режим nMode = MODE_STOP; InvalidateRect(hwnd, NULL, TRUE); return 0; }
// ------------------------------------------------- // Строка "Pause!" // Временный останов проигрывания // ------------------------------------------------- case CM_CTLPAUSE: { if(nMode == MODE_PLAYING) { // Временный останов проигрывания mcimidiPause(wOutDeviceID); nMode = MODE_PLAYINGPAUSED; }
InvalidateRect(hwnd, NULL, TRUE); return 0; }
// ------------------------------------------------- // Строка "Resume!" // Продолжение проигрывания после останова // ------------------------------------------------- case CM_CTLRESUME: { if(nMode == MODE_PLAYINGPAUSED) { // Продолжаем проигрывание mcimidiPlayCurrent(hwnd, 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(nMode == MODE_PLAYING) { if(wOutDeviceID) { mcimidiStop(wOutDeviceID); mcimidiClose(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_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(fFileLoaded) { if(nMode == MODE_PLAYING nMode == MODE_PLAYINGPAUSED) { mcimidiStop(wOutDeviceID); mcimidiClose(wOutDeviceID); } nMode = MODE_STOP; } PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); }
Обратим ваше внимание на то, как приложение MIDIPL выполняет продолжение проигрывания после временного останова. Так как драйвер mciseq.drv не поддерживает команду MCI_RESUME, для продолжения проигрывания используется команда MCI_PLAY без указания начальной позиции. Эта команда выдается функцией mcimidiPlayCurrent, вызываемой для продолжения проигрывания с текущего места.
Определения констант для приложения MIDIPL находятся в файле midipl.hpp (листинг 4.2).
Листинг 4.2. Файл midipl/midipl.hpp
#define CM_HELPABOUT 301 #define CM_FILEEXIT 302 #define CM_FILEOPEN 303 #define CM_CTLPLAY 401 #define CM_CTLRESUME 402 #define CM_CTLPAUSE 403 #define CM_CTLSTOP 404
Файл midiio. cpp содержит определение функций, предназначенных для работы с интерфейсом MCI (листинг 4.3).
Листинг 4.3. Файл midipl/midiio.cpp
#define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop
#include "midiio.hpp"
// Глобальные переменные extern int nMode; extern int nPosition; extern DWORD dwFileSize;
//----------------------------------------------------- // mcimidiOpen // Открытие устройства вывода //-----------------------------------------------------
UINT mcimidiOpen(LPSTR szFileName) { MCI_OPEN_PARMS mciOpen; MCI_STATUS_PARMS mciStatus; DWORD dwrc; DWORD dwFlags;
// Готовим блок параметров mciOpen.lpstrDeviceType= (LPSTR)"sequencer"; 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) { mcimidiError(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) { mcimidiError(dwrc); return 0; }
// Сохраняем длительность звучания в глобальной // переменной и возвращаем идентификатор устройства вывода dwFileSize = mciStatus.dwReturn; return mciOpen.wDeviceID; } }
//----------------------------------------------------- // mcimidiPlay // Проигрывание загруженного файла MIDI //----------------------------------------------------- DWORD mcimidiPlay(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; }
//----------------------------------------------------- // mcimidiPlayCurrent // Проигрывание загруженного файла MIDI // с текущей позиции //----------------------------------------------------- DWORD mcimidiPlayCurrent(HWND hwnd, UINT wDeviceID) { MCI_PLAY_PARMS mciPlayParms; DWORD dwrc;
// Идентификатор окна, функция которого получит // сообщение MM_MCINOTIFY mciPlayParms.dwCallback = (DWORD)hwnd;
// Запускаем проигрывание dwrc=mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&mciPlayParms);
return dwrc; }
//----------------------------------------------------- // mcimidiStop // Останов проигрывания загруженного файла MIDI //----------------------------------------------------- DWORD mcimidiStop(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc;
dwrc = mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mcimidiError(dwrc); }
return dwrc; }
//----------------------------------------------------- // mcimidiPause // Временный останов проигрывания загруженного файла MIDI //----------------------------------------------------- DWORD mcimidiPause(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc;
dwrc = mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mcimidiError(dwrc); }
return dwrc; }
//----------------------------------------------------- // mcimidiSelectFile // Выбор файла MIDI //----------------------------------------------------- BOOL mcimidiSelectFile(LPSTR lpszFileName) { OPENFILENAME ofn;
char szFile[256]; char szFileTitle[256]; char szFilter[256] = "MIDI Files\0*.mid;*.rmi\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; }
//----------------------------------------------------- // mcimidiClose // Закрытие устройства вывода //----------------------------------------------------- void mcimidiClose(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc;
dwrc = mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mcimidiError(dwrc); return; } }
//----------------------------------------------------- // mcimidiError // Обработка ошибок //----------------------------------------------------- void mcimidiError(DWORD dwrc) { BYTE szBuf[MAXERRORLENGTH];
if(mciGetErrorString(dwrc, (LPSTR)szBuf, MAXERRORLENGTH)) MessageBox(NULL, szBuf, "MIDIPL Error", MB_ICONEXCLAMATION); else MessageBox(NULL, "Неизвестная ошибка", "MIDIPL Error", MB_ICONEXCLAMATION); }
Функция mcimidiOpen предназначена для открытия устройства sequencer. При подготовке блока параметров в поле lpstrDeviceType структуры mciOpen указано имя устройства:
mciOpen.lpstrDeviceType= (LPSTR)"sequencer";
Функция mcimidiPlayCurrent предназначена для проигрывания с текущей позиции. В отличие от функции mcimidiPlay в ней не выполняется позиционирование на начало.
Файл midiio.hpp (листинг 4.4) содержит определения констант и прототипы функций для файла midiio.cpp.
Листинг 4.4. Файл midipl/midiio.hpp
#include <windows.h> #include <mmsystem.h>
#define MODE_STOP 0 #define MODE_PLAYING 1 #define MODE_PLAYINGPAUSED 2
UINT mcimidiOpen(LPSTR szFileName); BOOL mcimidiSelectFile(LPSTR lpszFileName); void mcimidiClose(UINT wDeviceID); DWORD mcimidiPlay(HWND hwnd, UINT wDeviceID); DWORD mcimidiPlayCurrent(HWND hwnd, UINT wDeviceID); void mcimidiError(DWORD dwrc); DWORD mcimidiStop(UINT wDeviceID); DWORD mcimidiPause(UINT wDeviceID); DWORD mcimidiResume(UINT wDeviceID);
Файл определения ресурсов midipl.rc приведен в листинге 4.5.
Листинг 4.5. Файл midipl/midipl.rc
#include "midipl.hpp" APPICON ICON "midipl.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
POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END
Файл определения модуля для приложения MIDIPL приведен в листинге 4.6.
Листинг 4.6. Файл midipl/midipl.def
NAME MIDIPL DESCRIPTION 'Приложение MIDIPL, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 10240 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple
Приложение SNDPLAY
Наше первое приложение SNDPLAY, имеющее зачатки мультимедиа, предназначено для демонстрации различных способов работы с функцией sndPlaySound (листинг 2.1).
Листинг 2.1. Файл sndplay\sndplay.cpp
// ---------------------------------------- // Использование функций // MessageBeep и sndPlaySound // ---------------------------------------- #define STRICT #include <windows.h> #include <mmsystem.h>
#pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HANDLE hWaveRes, hRes; LPSTR lpRes; BOOL rc; HFILE hf; DWORD dwFileSize; HGLOBAL hWave; char huge *lpBuf;
// Проигрываем звук, соответствующий строке // SystemQuestion раздела [sound] файла win.ini MessageBeep(MB_ICONQUESTION);
MessageBox(NULL, "Начнем, что ли?", "SndPlay", MB_OK | MB_ICONQUESTION);
// Проигрываем файл sndplay.snd в синхронном режиме rc = sndPlaySound((LPSTR)"sndplay.wav", SND_SYNC); if(!rc) { MessageBeep(MB_ICONHAND); MessageBox(NULL, "Не могу проиграть файл sndplay.wav", "SndPlay", MB_OK | MB_ICONHAND); return -1; }
// Загружаем звуковой фрагмент из ресурсов приложения // и проигрываем его
// Находим нужный ресурс hWaveRes = FindResource(hInstance, "APP_SOUND", "WAVE"); if(hWaveRes) { // Загружаем ресурс в память hRes = LoadResource(hInstance, (HRSRC)hWaveRes); if(hRes) { // Фиксируем ресурс в памяти, получая // указатель на данные lpRes = (LPSTR)LockResource(hRes); if(lpRes) { // Проигрываем звук в цикле rc = sndPlaySound(lpRes, SND_MEMORY | SND_ASYNC | SND_LOOP);
MessageBox(NULL, "Для завершения нажмите кнопку OK", "SndPlay", MB_OK | MB_ICONINFORMATION);
// Останавливаем проигрывание sndPlaySound(NULL, 0);
// Расфиксируем и освобождаем ресурс UnlockResource(hRes); FreeResource(hRes);
// Загружаем звуковой фрагмент непосредственно из // wav-файла в память и проигрываем его
// Открываем wav-файл hf = _lopen((LPSTR)"uff.wav", OF_READ);
// Определяем размер файла dwFileSize = _llseek(hf, 0l, 2); _llseek(hf, 0l, 0);
// Заказываем глобальный блок памяти, // размер которого равен длине файла hWave = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwFileSize);
// Фиксируем блок памяти lpBuf = (char huge *)GlobalLock(hWave); if(lpBuf != NULL) { // Читаем файл в полученный блок памяти _hread(hf, lpBuf, dwFileSize);
// Проигрываем звуковой фрагмент, загруженный в память rc = sndPlaySound((LPCSTR)lpBuf, SND_MEMORY | SND_SYNC); if(!rc) { MessageBeep(MB_ICONHAND); MessageBox(NULL, "Не могу проиграть файл uff.wav", "SndPlay", MB_OK | MB_ICONHAND); }
// Расфиксируем и освобождаем память GlobalUnlock(hWave); GlobalFree(hWave);
// Закрываем файл _lclose(hf); } } } } return 0; }
Приложение не имеет главного окна и функции окна. Сразу после запуска приложение SNDPLAY вызывает функцию MessageBeep, с помощью которой проигрывается звук, соответствующий строке SystemQuestion раздела [sound] файла win.ini.
Затем приложение вызывает функцию sndPlaySound для проигрывания файла sndplay.wav в синхронном режиме:
rc = sndPlaySound((LPSTR)"sndplay.wav", SND_SYNC);
Как только этот файл будет проигран, функция sndPlaySound вернет управление и работа приложения будет продолжена.
Далее приложение загружает звуковой фрагмент из ресурсов и проигрывает его асинхронно в циклическом режиме. При этом на экран выводится диалоговая панель с сообщением о то, что для прекращения циклического проигрывания следует нажать кнопку OK. Методика работы с ресурсами была описана нами в 12 томе "Библиотеки системного программиста". После поиска и фиксирования ресурса адрес соответствующего блока памяти передается в качестве первого параметра функции sndPlaySound:
rc = sndPlaySound(lpRes, SND_MEMORY | SND_ASYNC | SND_LOOP);
Так как звуковой файл находится в памяти, во втором параметре этой функции необходимо указать флаги SND_MEMORY и SND_ASYNC. Для циклического проигрывания следует также указать флаг SND_LOOP.
Для прерывания циклического проигрывания функция sndPlaySound вызывается с нулевыми параметрами:
sndPlaySound(NULL, 0);
В некоторых случаях может оказаться удобным проигрывать звуковой фрагмент, загруженный в память не из ресурсов приложения, а непосредственно из wav-файла. Финальная часть приложения SNDPLAY демонстрирует этот способ.
Вначале приложение открывает wav-файл с помощью функции _lopen и определяет его размер, вызывая функцию _llseek :
hf = _lopen((LPSTR)"uff.wav", OF_READ); dwFileSize = _llseek(hf, 0l, 2); _llseek(hf, 0l, 0);
Далее приложение заказывает глобальный блок памяти такого размера, чтобы в нем мог поместиться весь wav-файл. Блок фиксируется в памяти:
hWave = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwFileSize); lpBuf = (char huge *)GlobalLock(hWave);
Теперь, если передать адрес этого блока функции sndPlaySound, можно проиграть его в асинхронном или синхронном режиме. Так как мы освобождаем память сразу после возврата управления из функции sndPlaySound, для простоты мы выбрали синхронный режим:
rc = sndPlaySound((LPCSTR)lpBuf, SND_MEMORY | SND_SYNC);
Только после того, как проигрывание закончено, можно расфиксировать и освободить память с образом wav-файла:
GlobalUnlock(hWave); GlobalFree(hWave);
Если освободить память до момента окончания проигрывания, Windows перейдет в нестабильное состояние, требующее полного перезапуска.
Файл ресурсов приложения (листинг 2.2) содержит описание ресурса типа WAVE (можно использовать любой другой нестандартный тип ресурса):
Листинг 2.2. Файл sndplay\sndplay.rc
APP_SOUND WAVE loop.wav APPICON ICON "sndplay.ico"
Файл определения модуля приложения SNDPLAY приведен в листинге 2.3.
Листинг 2.3. Файл sndplay\sndplay.def
NAME SNDPLAY DESCRIPTION 'Приложение SNDPLAY, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 5120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple