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

         

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 бит
Структура WAVEOUTCAPS используется для определения возможностей устройств вывода звуковых сигналов с использованием импульсно-кодовой модуляции:

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 поставляется приложение win­mix.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