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

компьютеры встречались только на страницах

Еще совсем недавно "говорящие" компьютеры встречались только на страницах научно-фантастических книг и в лабораториях. Компьютер, который умел воспроизводить или записывать звук, рассматривался как экзотика, недоступная для большинства пользователей. Многие даже и не догадывались, что на экране обычного компьютера можно просматривать видеофильмы со звуковым сопровождением.
Звук и видеофильмы представляются в компьютере в виде обычных цифровых данных, поэтому, казалось бы, нет никаких затруднений в их хранении, обработке и выводе. Однако при ближайшем рассмотрении оказывается, что для хранения звука и тем более видеоизображения требуются огромные объемы памяти - порядка десятков и сотен мегабайт. Запись и воспроизведение видеофильмов связаны с обработкой информации в реальном времени, поэтому только современные компьютеры могут справиться с потоком данных, поступающим, например, от видеокамеры.
Прорыв в технологии изготовления запоминающих устройств для компьютеров привел к значительному удешевлению памяти, как оперативной, так и дисковой. Появились лазерные дисковые устройства различных типов, обладающие рекордно низкой стоимостью хранения одного мегабайта данных. Цена процессоров i386, i486 и Pentium упала настолько, что они стали доступны практически всем. Теперь вы сможете приобрести компьютер с процессором Pentium/60 всего за 2000 - 2500 долларов. Очень трудно найти в продаже компьютер с процессором 80286, зато примерно за 700-900 долларов можно приобрести компьютер с процессором i386 или даже i486SX.
Постоянное падение цен на компьютерное оборудование, а также разработка новых программных и аппаратных методов компрессии данных создали уже сейчас все предпосылки для самого широкого внедрения технологии мультимедиа, предполагающей активное использование звука, высококачественных графических изображений и видео. Всего за 50 - 100 долларов вы можете приобрести звуковую плату с миниатюрными колонками и микрофоном, с помощью которой ваш компьютер сможет записывать и воспроизводить звук, а также проигрывать музыкальные файлы в формате MIDI.
Затратив дополнительно не более 160 долларов, вы сможете приобрести устройство чтения компакт-дисков CD ROM, которое откроет для вас окно в мир программных систем нового поколения. Вполне доступно устройство ввода информации от видеокамеры или видеомагнитофона (в зависимости от возможностей такое устройство может стоить от ста до нескольких сотен долларов).
Какие же новые возможности появляются у пользователя или программиста, превратившего свой компьютер в мультимедиа-компьютер, и стоят ли они денег, потраченных на приобретение дополнительного оборудования?
На этот вопрос может быть только один ответ: да, и еще раз да!
Уточним, что же такое мультимедиа. Дословный перевод слова multimedia не слишком благозвучен - многосредность или множество сред. Под средой здесь понимается звук, видео, текст и любые другие данные. Мы не будем переводить слово multimedia, которое пришло в русский язык из английского, широко используется и понятно без перевода.
Под мультимедиа мы будем понимать комплекс аппаратных и программных средств, позволяющих применять персональных компьютер для работы не только с текстом, но и со звуком, графикой, анимацией, а также видео.
Мультимедиа позволяют использовать компьютер новым способом, превращая его, например, в удобный инструмент для работы с базами данных громадных размеров, содержащих не только текстовые данные, но и звук, высококачественные изображения и видеофильмы.
Как разработчик программного обеспечения, вы можете распространять свое приложение на компакт-дисках, предназначенных для устройств чтения CD ROM, при этом размер дисковой памяти, занимаемый одним приложением, может достигать несколько сотен Мбайт! В России уже есть издательства, занимающиеся созданием, тиражированием и распространением таких компакт-дисков. Стоимость изготовления одного компакт-диска не превышает двух долларов. Так как на одном компакт-диске может быть записано порядка 650 Мбайт данных, стоимость хранения информации минимальна и просто не сравнима со стоимостью хранения данных на традиционных магнитных носителях.


Данное обстоятельство уже привело к лавинообразному появлению на зарубежном рынке программного обеспечения, баз данных и целых библиотек, которые поставляются на компакт-дисках по низкой цене.
Можно очень долго перечислять приложения, использующие технологию мультимедиа и записанные на компакт-дисках. Выделим для примера несколько групп таких приложений.
Справочные базы данных и системы поддержки разработчиков программного обеспечения
Примером системы поддержки разработчиков программного обеспечения может служить набор компакт-дисков Microsoft Developer Network CD. Один диск из такого набора стоимостью 50 долларов содержит целый шкаф документации (сотни книг и статей!), тысячи примеров программ, базу знаний, инструментальные средства и полезные утилиты.
На момент создания книги фирма Microsoft подготовила два уровня Microsoft Developer Network CD. Первый уровень - это один компакт-диск Development Library, на котором находится все, перечисленное выше. Второй уровень (Development Platform) состоит из нескольких компакт-дисков, содержащих десятки версий операционной системы Windows и Windows NT, а также средства разработки приложений (SDK и DDK) для Windows и Windows NT.
Без преувеличения можно сказать, что Microsoft Developer Network CD второго уровня содержит все, что нужно разработчику приложений Windows и Windows NT (кроме транслятора Visual C++, который, впрочем, тоже поставляется на компакт-диске).
Аналогичные средства созданы и другими фирмами. Можно упомянуть энциклопедии, выпускаемые фирмой Novell для разработчиков программного обеспечения, администраторов и пользователей сетевых операционных систем, созданных Novell.
Традиционное программное обеспечение
Так как стоимость хранения информации на компакт-дисках значительно меньше стоимости хранения информации на дискетах, а надежность хранения выше, удобнее использовать дистрибутивы программ в виде компакт-дисков. Современные приложения, такие, например, как Borland C++ версии 4.0 или Microsoft Visual C++ версии 1.5, поставляются либо на дискетах, либо на компакт-дисках.


В первом случае вам нужно установить по очереди более двух десятков дискет, что не слишком удобно. Установка программ с компакт-диска выполняется легко и быстро. Вам не нужно беспокоиться о том, что в один прекрасный момент дискеты дистрибутива испортятся, и, соответственно, не нужно делать резервную копию.
В настоящее время на компакт-дисках продается практически любое программное обеспечение, дистрибутив которого занимает больше чем полтора-два десятка дискет. Можно также купить один компакт-диск без напечатанной в виде книг документации, что стоит дешевле. При необходимости вы сможете сами распечатать нужные вам тома документации, так как они есть на компакт-диске в том или ином формате.
Всего за 20-30 долларов вы можете приобрести компакт-диски, содержащие тысячи и десятки тысяч программ MS-DOS, приложений Windows, драйверов и утилит для MS-DOS, Windows и других операционных систем. Предлагаются также компакт-диски, содержащие сотни высококачественных изображений в виде gif-файлов, коллекции звуков, MIDI-файлов с музыкальными произведениями и видеофильмами. Во многих случаях вы можете бесплатно использовать такие изображения, звуки и видеофильмы при создании своего программного обеспечения (то есть если вы включаете файлы, переписанные из таких компакт-дисков, в свое программное обеспечение, вам не требуется платить за это владельцу прав на распространение компакт-дисков, составителю или автору компакт-диска).
Словари и энциклопедии
Технология мультимедиа открывает новые возможности для создания электронных словарей и энциклопедий. Такие словари хранят для каждого слова не только перевод и транскрипцию, но и образец произношения в виде небольшого звукового фрагмента. Огромная емкость компакт-диска позволяет записать на нем также и фотографию, имеющую отношение к любому слову или понятию. Электронные словари и энциклопедии намного удобнее обычных, так как в них легче искать нужную вам информацию и они не занимают много места на рабочем столе (что иногда тоже имеет большое значение).


Картографические системы
На компакт- дисках выпускаются различные картографические системы, от атласа мира до подробных карт сотен городов. Вы можете прослушать гимны стран, названия городов, ознакомиться с подробной картой нужного вам города и получить другую информацию, связанную с той или иной местностью. Картографические системы окажут неоценимую услугу при планировании отпуска или деловой поездки, они очень удобны для туристических бюро и агентств.
Обучающие системы
Эффективность обучения повышается при использовании наглядных иллюстраций. Технология мультимедиа и компакт-диски сделали возможным появление обучающих систем нового поколения, превращающих обучение в игру. Создатель такой системы может иллюстрировать изучаемый материал при помощи видеофильмов со звуковым комментарием, что улучшает его восприятие и снижает утомляемость.
Телеконференции в сети компьютеров
Организация в локальных сетях компьютеров телеконференций - новое направление использования технологии мультимедиа, появившееся в результате внедрения скоростных локальных сетей, недорогих видеокамер, аппаратуры для ввода звука и видеоинформации в персональный компьютер. Видеоизображение и звук поступают с одной или нескольких видеокамер и транслируются по сети. Каждый участник видеоконференции имеет доступ к транслируемым видеоматериалам и при наличии на рабочем месте видеокамеры может сам передавать изображения в сеть.
Игры и развлечения
Технология мультимедиа позволяет приблизить "виртуальную реальность" игры к реальности жизни, которая наполнена звуками и изображениями. Компакт-диски могут хранить файлы игровых программ размером в сотни мегабайт, что создает предпосылки для появления игр, несравнимых со старыми по обилию звуковой и видеоинформации. Наконец, устройство чтения компакт-дисков способно проигрывать музыкальные диски. Вы можете подключить к нему обычные наушники и слушать музыку.
Надеемся, что мы вас убедили и что в самое ближайшее время вы отправитесь приобретать звуковой адаптер и устройство чтения компакт-дисков CD ROM, а может быть и адаптер для записи видеофильмов.

Теперь об этой книге.
Наша книга предназначена в первую очередь для тех, кто собирается самостоятельно создавать мультимедиа-приложения. Поэтому основное внимание мы уделим вопросам использования технологий мультимедиа при разработке новых приложений. Однако, учитывая новизну темы, мы сделаем краткий обзор существующих приложений, поддерживающих технологию мультимедиа.
Из-за ограниченного объема книги мы смогли рассмотреть только самые важные аспекты программирования устройств мультимедиа. Тем из вас, кто собирается разрабатывать мультимедиа-приложения на профессиональном уровне, мы рекомендуем приобрести Microsoft SDK для Windows версии 3.1, в состав которого входит полное описание средств мультимедиа. В продаже есть также изделие Microsoft MDK (Multimedia Development Kit), вместе с которым поставляется средство для создания приложений мультимедиа без программирования. Для работы с видеофильмами вам необходимо приобрести систему Microsoft Video for Windows Development Kit, содержащую необходимую документацию и, что самое главное, полезные утилиты, примеры приложений, библиотеки и include-файлы.
Для работы с книгой вам достаточно иметь компьютер с процессором i386DX33 и оперативной памятью 4 Мбайт, оснащенный звуковым адаптером и, желательно, устройством чтения компакт-дисков CD ROM. Для работы с видеофильмами средствами Video for Windows рекомендуется процессор i486 и акселератор Windows в качестве видеоадаптера, хотя просмотр видеофильмов возможен и на менее мощном компьютере (акселератор Windows, тем не менее, очень желателен).
Что касается программного обеспечения, то для трансляции исходных текстов примеров приложений, не имеющих отношение к Video for Windows, вы можете воспользоваться системами Borland Turbo C++ for Windows версии 3.1 или Borland C++ версий 3.1 или 4.0. Приложения, работающие с Video for Windows, можно транслировать системами Microsoft C++ версии 7.0 или Microsoft Visual C++ версий 1.0 или 1.5. В последнем случае желательно, чтобы в компьютере было установлено по крайней мере 8 Мбайт оперативной памяти (а лучше 16 Мбайт, особенно если вы собираетесь разрабатывать крупные проекты).


В первой, вводной главе мы познакомим вас с системами мультимедиа для Windows, расскажем о способах ввода и представлении звуковой информации в памяти компьютера, расскажем о том, как выбрать и подключить звуковой адаптер, а также сделаем краткий обзор приложений Windows, использующих технологию мультимедиа.
Вторая глава полностью посвящена описанию различных способов, с помощью которых приложения Windows могут записывать и воспроизводить звук. Мы рассмотрим все уровни программного интерфейса, от самого высокого до самого низкого. Помня о том, что примеры сильно облегчают понимание материала, мы включили в эту главу многочисленные примеры несложных приложений, демонстрирующих все способы работы со звуком. Конкретно мы рассмотрели функцию sndPlaySound, позволяющую проигрывать звуковые файлы небольшого размера, два типа интерфейса MCI и функции низкого уровня, выполняющие обращение непосредственно к драйверу звукового адаптера.
Третья глава - о работе с устройством чтения компакт-дисков CD ROM. Из нее вы узнаете о том, как подключить драйвер этого устройства, как использовать устройство для чтения цифровых компакт-дисков и проигрывания музыкальных компакт-дисков. Мы опишем способы управления CD ROM, основанные на применении интерфейса MCI и приведем пример приложения, предназначенного для проигрывания дорожек музыкального компакт-диска. Используя полученные знания, вы сможете создавать приложения, которые проигрывают дорожки компакт-диска во время своей работы в фоновом режиме.
В четвертой главе мы расскажем вам об использовании музыкального синтезатора, расположенного в звуковом адаптере, для проигрывания музыкальных файлов в стандарте MIDI с помощью интерфейса MCI. Вы сможете легко озвучить ваше приложение, даже если оно не рассчитано на работу с устройством чтения компакт-дисков. Музыкальные файлы в стандарте MIDI занимают очень немного места, поэтому ваше "озвученное" приложение не превратится в монстра, пожирающего десятки мегабайт дисковой памяти.


В продаже есть богатые коллекции MIDI-файлов, так что вы всегда сможете выбрать что-нибудь на свой вкус.
Пятая глава, посвященная Microsoft Video for Windows, - о самой передовой технологии мультимедиа для операционной системы Microsoft Windows. Когда мы еще только начинали писать эту книгу, у нас не было планов включать в нее материал о Video for Windows, так как для работы с этой системой требуется достаточно мощный компьютер. Кроме того, Video for Windows - весьма сложная система, заслуживающая отдельной книги. Однако компьютеры постоянно дешевеют, а обзор средств мультимедиа был бы неполным без хотя бы краткого рассказа о Video for Windows. Поэтому мы дополнили книгу пятой главой, которую можно считать введением в Video for Windows. Вы познакомитесь с принципами, положенными в основу этой системы, узнаете ее структуру и назначение отдельных подсистем. Мы научим вас создавать видеофильмы с помощью приложений, входящих в комплект поставки Video for Windows, а также создавать собственные приложения, предназначенные для проигрывания видеофильмов. Таким образом, даже не изучая всей системы в целом, вы сможете использовать технологию Video for Windows в своих приложениях. А главное - вы сможете почувствовать мощь этой технологии, которая, кстати, является стандартной для новых версий операционной системы Windows, таких как Windows NT версии 3.5 и Chicago.
Вместе с этим томом "Библиотеки системного программиста" продается больше дискет, чем обычно. Кроме дискеты с исходными текстами приложений мы подготовили дискеты с файлами небольших видеофильмов, а также дискету с системой Video for Windows Runtime, необходимой для работы с Video for Windows. Поэтому если вы приобрели весь набор дискет, у вас есть все для того чтобы оценить возможности Video for Windows.

Как связаться с нами?
Вы можете присылать ваши отзывы в адрес издательства или через электронную почту по адресу, доступному из сетей GlasNet, Internet, Relcom и т. п.:
frolov@glas.apc.org
Заранее благодарны за ваши замечания и предложения по содержанию этой книги, а также всех остальных книг серий "Библиотека системного программиста" и "Персональный компьютер.Шаг за шагом".

Авторы выражают благодарность:
сотрудникам издательского отдела АО "Диалог-МИФИ" Елене Виноградовой, Олегу Александровичу Голубеву, Наталье Дмитриевой, Оксане Кузьминовой, которые приложили немало усилий для того чтобы книги серий "Библиотека системного программиста" и "Персональный компьютер. Шаг за шагом" вышли в свет;
корректору Виктору Кустову за колоссальные усилия, затраченные на проверку и исправление рукописей наших книг;
системным программистам фирмы Interactive Products Inc. Максиму Синеву и Сергею Ноженко за ценные советы и рекомендации по содержанию книги;
фирме Interactive Products Inc. за предоставленное в наше распоряжение программное обеспечение.

Break

Команда break позволяет определить код виртуальной клавиши, предназначенной для прерывания (по умолчанию используется комбинация клавиш ):
break device_id parameter [notify] [wait]
В качестве параметра parameter можно указывать одну из следующих строк:
on virt_key
Для прерывания будет использована клавиша с виртуальным кодом virt_key
off
Действие клавиши прерывания отменяется
Если команда выдана с параметром wait, функция mciSendString вернет управление только после завершения операции. Если пользователь не желает дожидаться завершения длительной операции, он может нажать клавишу, прерывающую выполнение команды.

Capability

С помощью команды capability приложение может определить возможности устройства:
capability device_id parameter [notify] [wait]
В качестве параметра parameter можно указывать одну из следующих строк:
can play
Если драйвер звукового адаптера может проигрывать wav-файлы, в ответ на эту строку он возвратит строку true, а если нет - то false
can record
Если устройство может записывать, возвращается true, в противном случае - false
can save
Используется для определения возможности сохранения записанного звукового фрагмента в wav-файле. Если такая возможность есть, возвращается строка true, в противном случае - false
compound device
Все MCI-устройства можно разделить на простые и составные (compound). Простые устройства, такие как проигрыватель звуковых компакт-дисков или лазерных видеодисков, не работают с файлами. Составные, такие как звуковой адаптер, используют файлы. Поэтому в ответ на эту строку, переданную драйверу звукового адаптера, приложение получит строку true
device type
Для звукового адаптера возвращается строка waveaudio
has audio
Для звукового адаптера возвращается строка true
inputs
Общее количество устройств ввода
outputs
Общее количество устройств вывода
uses files
Для звукового адаптера возвращается строка true, так как он работает с wav-файлами

Чтение из файла

Для чтения файла, открытого при помощи функции mmioOpen, следует использовать функцию mmioRead . Эта функция позволяет за один вызов прочитать из файла блок данных размером, большим чем 64 Кбайт. После чтения выполняется перемещение текущей позиции вперед на количество прочитанных байт.
Функция mmioRead
LONG mmioRead( HMMIO hmmio, // идентификатор открытого файла HPSTR hpBuff, // указатель на буфер с данными LONG dwBytes); // размер буфера
Параметры функции:
hmmio
Идентификатор открытого файла, полученный с помощью функции mmioOpen
hpBuff
Указатель типа huge на буфер, в который будут прочитаны данные
dwBytes
Размер буфера
Возвращаемое значение:
Возвращается количество прочитанных байт данных или -1 при возникновении ошибки. При достижении конца файла возвращается нулевое значение

Close

Команда close закрывает устройство и освобождает все связанные с ним ресурсы. Формат команды:
close device_id [notify] [wait]
Необходимо указать идентификатор устройства device_id, использованный при открытии устройства командой open.
Например, для того чтобы закрыть устройство с алиасом nsound, вы можете использовать следующую управляющую строку:
close nsound

Cue

Подготовка для записи или воспроизведения. После выполнения такой подготовки устройство будет готово быстро приступить к выполнению требуемой операции. Эту команду нельзя выдавать во время записи или воспроизведения.
В качестве параметра parameter можно указывать одну из следующих строк:
input
Подготовка для записи
output
Подготовка для воспроизведения

Delete

Удаление сегмента из фрагмента звуковых данных
delete device_id from position [to position] [notify] [wait]
Для этой команды можно указать либо оба параметра (from и to), либо только параметр from.

Драйвер mciavi.drv

Драйвер mciavi.drv предназначен для проигрывания файлов мультимедиа, содержащих звуковые данные и видео (как с поддержкой цветовых палитр, так и в режиме True Color). Звуковые данные могут быть моно- или стереофоническими с частотой дискретизации 44,1 Кгц, 22,05 Кгц или 11,025 Кгц. С помощью драйвера mciavi.drv можно проигрывать avi-файлы, содержащие звук и видео, только видео или только звук.
При воспроизведении видео со звуком возникает проблема синхронизации. К сожалению, производительность компьютера и особенно его видеоподсистемы не всегда оказывается достаточной для воспроизведения всех кадров, записанных в avi-файле, с нужной скоростью. В случае необходимости драйвер mciavi.drv пропускает некоторые кадры. Из-за инерционности зрения качество изображения во многих случаях остается при этом вполне удовлетворительным. Однако перерывы в воспроизведении звуковых данных или пропуски фрагментов звуковых данных недопустимы. Поэтому в процессе пропуска видеокадров драйвер mciavi.drv обеспечивает предварительную выборку звуковых данных и непрерывность воспроизведения звуковой информации.
В дополнение к командам MCI, рассмотренным нами ранее и предназначенным для управления звуковыми устройствами и устройствами чтения компакт-дисков, драйвер mciavi.drv позволяет использовать команды, имеющие отношение к воспроизведению видео. В частности, с помощью этих команд можно указать параметры окна, в котором будет выполняться просмотр видео. Однако, как мы уже говорили, самый простой способ проигрывания avi-файлов заключается в использовании интерфейса окна MCI вместо классического интерфейса MCI.

Другие функции низкого уровня

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

Другие макрокоманды

Перечислим некоторые другие полезные макрокоманды, имеющие отношение к окну MCI.
Макрокоманды MCIWndCanConfig , MCIWndCanEject , MCIWndCanPlay , MCIWndCanRecord , MCIWndCanSave , MCIWndCanWindow позволяют определить, соответственно, возможность конфигурирования, автоматической смены носителя, проигрывания, записи, сохранения и проигрывания в окне. Все эти макрокоманды имеют один параметр - идентификатор окна MCI. Если та или иная возможность поддерживается, соответствующая макрокоманда возвращает значение TRUE, в противном случае FALSE.
В любой момент времени приложение может изменить стиль окна MCI, вызвав макрокоманду MCIWndChangeStyles . Пример испльзования этой макрокоманды есть в приложении MCIWNDC (см. ниже). Для определения текущего стиля окна MCI можно воспользоваться макрокомандой MCIGetStyles .
Несколько макрокоманд предназначены для определения характеристик файла или такого носителя данных, как звуковой компакт-диск, загруженного в окно MCI. С помощью макрокоманд MCIWndGetLength , MCIWndGetStart , MCIWndGetEnd , MCIWndGetPosition , MCIWndGetPositionString приложение может определить, соотетственно, длину файла, начальную позицию, конечную позицию, текущую позицию и текущую позицию в виде текстовой строки.
Приложение может установить скорость проигрывания, громкость и размеры окна MCI, вызвав, соответственно, макрокоманды MCIWndSetSpeed , MCIWndSetVolume , MCIWndSetZoom . Есть макрокоманды, с помощью которых можно определить текущее значение для скорости проигрывания, громкости и размеров окна MCI - MCIWndGetSpeed , MCIWndGetVolume и MCIWndGetZoom .
Есть макрокоманды для изменения формата времени и определени текущего формата времени (MCIWndSetTimeFormat , MCIWndGetTimeFormat , MCIWndUseFrames , MCIWndUseTime ), для обновления информации о позиции при замене носителя данных (MCIWndValidateMedia ), для работы с таймером, палитрами и некоторые другие.
Если же этого обширного списка все же не хватит, то с помощью макрокоманды MCIWndSendString вы сможете передать окну любую команду MCI, лишь бы ее поддерживало используемое устройство. Ответ драйвера на посланную команду можно получить в виде текстовой строки при помощи макрокоманды MCIWndReturnString .

Другие приложения мультимедиа

Приложение World Atlas (атлас мира) поставляется на дискетах или компакт-диске. Его можно использовать как справочник, содержащий многочисленные сведения о различных странах и городах мира. Справочник содержит высококачественные цветные графические изображения карт и мощные средства поиска информации (рис. 1.17).
Другие приложения мультимедиа
Рис. 1.17. Приложение World Atlas
Выбрав страну, вы можете услышать (на английском языке) ее название, а также гимн. База данных содержит сведения о населении городов, средней температуре в разные сезоны года, географические координаты, международный телефонный код. Если вы не знаете, где расположена страна или город, но знаете ее название, нужную вам страну или город нетрудно найти в списке географических названий. После выбора названия вы увидите это место на карте. В базе есть не только политическая карты мира, но и топографическая, на которой обозначен рельеф местности, а также статистические карты.
С помощью World Atlas вы сможете без труда узнать поясное время в любом городе мира, определить расстояние между любыми двумя городами, узнать другие сведения о стране, необходимые для путешественника. Вы можете отметить нужные вам города на карте. Любая карта или диаграмма может быть скопирована в Clipboard и вставлена затем в документ (рис. 1.18).
Другие приложения мультимедиа
Рис. 1.18. Карта Австралии, скопированная из атласа мира
Еще одно интересное приложение, использующее технологию мультимедиа - электронная книга "Guinness Multimedia Disk of Records" (вариант "Книги рекордов Гиннеса"), которая поставляется на компакт-диске (рис. 1.19).
Другие приложения мультимедиа
Рис. 1.19. Приложение Guinness Multimedia Disk of Records
В отличие от обычной книги, где вы можете прочитать запись о рекорде и посмотреть соответствующую фотографию, электронный вариант позволяет вам прослушать звуковую запись. Например, мы узнали, что самое длинное слово в русском языке - "рентгеноэлектрокардиографического". Мы услышали произношение этого слова, а также пример предложения, в котором оно используется, и его перевод на английский язык - "Больше нет рентгеноэлектрокардиографического института".
Увы, больше нет...

В некоторых окнах, содержащих описание рекорда, есть пиктограммы фотоаппарата и головных телефонов. Например, на рис. 1.20 вы видите сведения о самой быстрой птице. Сделав двойной щелчок по пиктограмме с изображением фотоаппарата, вы можете увидеть фотографию птицы. Если же вы щелкните по пиктограмме с изображением головных телефонов, вы услышите голос самой быстрой птицы в мире.

Другие приложения мультимедиа

Рис. 1.20. Пиктограммы для просмотра фотографии и прослушивания звуковой записи

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

  • Audobon's Mammals


  • Многотомное описание животных Северной Америки с цветными фотографиями и записями звуков.

  • Cameron's Fine Art Catalog


  • Работы художников и фотографов со звуковым сопровождением.

  • Compton's Interactive Encyclopedia


  • 26-томная энциклопедия с фотографиями, звуковыми и видеозаписями.

  • Macmillian Dictionary for Children


  • Словарь, предназначенный для обучения детей. Использование средств мультимедиа позволяет значительно улучшить восприятие материала.

  • Mayo Clinic Family Health Book


  • Электронный семейный доктор. Фотографии, видеозаписи и звуковые записи иллюстрируют информацию о том, как сохранить свое здоровье.

    Формат wav-файла

    Данные, имеющие отношение к мультимедиа (звук, видео и т. п.) хранятся в файлах в так называемом RIFF-формате (Resource Interchange File Format - формат файла для обмена ресурсами). Как wav-файлы, содержащие звук, так и avi-файлы, содержащие видеоинформацию, имеют формат RIFF.
    Файл в формате RIFF содержит вложенные фрагменты (chunk's ). Внешний фрагмент состоит из заголовка и области данных (рис. 2.3).
    Формат wav-файла
    Рис. 2.3. Фрагмент "RIFF"
    Первое двойное слово заголовка содержит четырехбуквенный код FOURCC, который идентифицирует данные, хранящиеся во фрагменте. Второе двойное слово заголовка - размер области данных в байтах (без учета размера самого заголовка).
    Область данных имеет переменную длину, однако она должна быть выравнена на границу слова и при необходимости дополнена в конце нулевым байтом до целого числа слов.
    Заметим, что формат RIFF не описывает формат данных. Практически файл в формате RIFF может содержать любые данные для мультимедиа, причем формат данных зависит от типа данных.
    Область, обозначенная на рис. 2.3 как "Данные", может содержать внутри себя другие фрагменты. Для файла, в котором хранятся звуковые данные (wav-файл), эта область содержит идентификатор данных "WAVE", фрагмент формата звуковых данных "fmt " (три символа "fmt" и пробел на конце), а также фрагмент звуковых данных (рис. 2.4). Файл может дополнительно содержать фрагменты других типов, поэтому не следует думать, что заголовок wav-файла имеет фиксированный формат. Например, в файле может присутствовать фрагмент "LIST" или "INFO", содержащий информацию о правах копирования и другую дополнительную информацию. Из-за ограниченного объема книги мы не будем рассматривать форматы других фрагментов, при необходимости вы можете узнать их из документации, которая поставляется в составе Microsoft SDK for Windows 3.1.
    Формат wav-файла
    Рис. 2.4. Формат wav-файла
    Область, обозначенная на рис. 2.4 как "Формат данных", описывает звуковые данные.
    Формат этой области для файлов PCM (записанных с использованием импульсно-кодовой модуляции) соответствует структуре PCMWAVEFORMAT , определенной в файле mmsystem.h следующим образом:

    typedef struct pcmwaveformat_tag { WAVEFORMAT wf; WORD wBitsPerSample; } PCMWAVEFORMAT; typedef PCMWAVEFORMAT *PPCMWAVEFORMAT; typedef PCMWAVEFORMAT NEAR *NPPCMWAVEFORMAT; typedef PCMWAVEFORMAT FAR *LPPCMWAVEFORMAT;

    Структура WAVEFORMAT также описана в файле mmsystem.h:

    typedef struct waveformat_tag { WORD wFormatTag; // тип формата WORD nChannels; // количество каналов (моно или стерео) DWORD nSamplesPerSec; // частота дискретизации DWORD nAvgBytesPerSec; // скорость потока данных WORD nBlockAlign; // выравнивание блока данных } WAVEFORMAT; typedef WAVEFORMAT *PWAVEFORMAT; typedef WAVEFORMAT NEAR *NPWAVEFORMAT; typedef WAVEFORMAT FAR *LPWAVEFORMAT;

    Поле wFormatTag описывает тип формата звуковых данных. Для импульсно-кодовой модуляции PCM, которая поддерживается стандартной библиотекой mmsystem.dll, в этом поле должно находиться значение WAVE_FORMAT_PCM , определенное в файле mmsystem.h:

    #define WAVE_FORMAT_PCM 1

    Поле nChannels содержит количество каналов. В нем могут находиться значения 1 (моно) или 2 (стерео).

    В поле nSamplesPerSec записана частота дискретизации, то есть количество выборок сигнала в секунду. В этом поле могут находиться стандартные значения (11025 Кгц, 22050 Кгц или 44100 Кгц), либо нестандартные значения, такие как 5000 Кгц или 4400 Кгц. Учтите, что не все драйверы звуковых адаптеров могут работать с нестандартными частотами дискретизации.

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


    Точное значение вы можете подсчитать по формуле:

    nAvgBytesPerSec = (nChannels * nSamplesPerSec * wBitsPerSample) / 8

    В поле nBlockAlign находится выравнивание блока в байтах, которое подсчитывается по формуле:

    nBlockAlign = (nChannels * wBitsPerSample) / 8

    Поле wBitsPerSample находится в структуре PCMWAVEFORMAT и содержит дискретность сигнала, то есть количество бит, используемых для представления одной выборки сигнала. Обычно используются значения 8 или 16.

    Что же касается формата самих звуковых данных, то он зависит от количества каналов и от дискретности.

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

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

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

    Диапазон изменения значений выборок сигнала определяется дискретизацией. Для 8-битовых данных он составляет от 0 до 255 (0xff), причем отсутствию сигнала (полной тишине) соответствует значение 128 (0x80). Для 16-битовых данных диапазон изменения составляет от -32768 (-0x8000) до 32767 (0x7fff), отсутствию сигнала соответствует значение0.

    Функции для работы с файлами

    Для чтения или записи wav-файлов вы, конечно, можете использовать стандартные функции или такие функции, как _hread или _hwrite. Однако в библиотеке mmsystem.dll есть более удобные функции, специально предназначенные для работы с файлами в стандарте RIFF. Все эти функции могут работать с блоками памяти большого размера (больше 64 Кбайт), что очень удобно, так как звуковые данные редко помещаются в одном сегменте памяти.

    Функции для работы с RIFF-файлами

    Ваше приложение может работать с RIFF-файлами с использованием обычных функций ввода/вывода или с помощью функций, описанных выше (что удобнее). Дополнительно в библиотеке mmsystem.dll есть функции, сильно облегчающие работу с фрагментами RIFF-файлов. Эти функции помогут вам заполнить четырехбайтовый идентификатор фрагмента, найти в файле нужный фрагмент и установить на него (или за него) текущую позицию файла, а также создать новый фрагмент в новом файле.
    При формировании нового фрагмента удобна функция mmioFOURCC , с помощью которой можно создать четырехбуквенный код фрагмента из отдельных букв.
    Функция mmioFOURCC
    FOURCC mmioFOURCC( CHAR ch0, // первая буква кода CHAR ch1, // вторая буква кода CHAR ch2, // третья буква кода CHAR ch3); // четвертая буква кода
    Параметры функции:
    ch0, ch1, ch2, ch3
    Коды букв, из которых будет составлен четырехбуквенный код
    Возвращаемое значение:
    Возвращается значение четырехбуквенного идентификатора, который можно использовать при формировании нового фрагмента
    Функция mmioFOURCC реализована как макрокоманда, выполняющая упаковку четырех байт в двойное слово:
    #define mmioFOURCC(ch0, ch1, ch2, ch3) \ ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \ ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
    Для формирования, например, идентификатора фрагмента "WAVE" вы можете использовать эту макрокоманда следующим образом:
    FOURCC fourccWaveID; fourccWaveID = mmioFOURCC('W', 'A', 'V', 'E');
    В некоторых случаях может оказаться удобнее формировать четырехбуквенный идентификатор не из отдельных букв, а из строки символов. Для этого можно использовать функцию mmioStringToFOURCC .
    Функция mmioStringToFOURCC
    FOURCC mmioStringToFOURCC( LPCSTR szString, // преобразуемая строка UINT wFlags); // режим преобразования
    Параметры функции:
    szString
    Указатель на преобразуемую строку, закрытую двоичным нулем
    wFlags
    Если указан флаг MMIO_TOUPPER , все буквы строки будут преобразованы в заглавные
    Возвращаемое значение:

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

    Пример использования функции mmioStringToFOURCC:

    FOURCC fourccWaveID; fourccWaveID = mmioStringToFOURCC("wave", MMIO_TOUPPER);

    Для создания нового фрагмента в RIFF-файле удобно использовать функцию mmioCreateChunk . Новый фрагмент создается в текущей позиции файла, открытого с помощью функции mmioOpen.

    Функция mmioCreateChunk

    UINT mmioCreateChunk( HMMIO hmmio, // идентификатор открытого файла LPMMCKINFO lpck, // указатель на структуру MMCKINFO UINT wFlags); // тип фрагмента

    Параметры функции:

    hmmio

    Идентификатор открытого файла, полученный с помощью функции mmioOpen

    lpck

    Указатель на структуру MMCKINFO, содержащую информацию о создаваемом фрагменте

    wFlags

    Если указан флаг MMIO_CREATERIFF , создается фрагмент "RIFF", а если MMIO_CREATELIST - создается фрагмент "LIST"

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки

    Структура MMCKINFO и указатели на нее определены в файле mmsystem.h:

    typedef struct _MMCKINFO { FOURCC ckid; DWORD cksize; FOURCC fccType; DWORD dwDataOffset; DWORD dwFlags; } MMCKINFO; typedef MMCKINFO *PMMCKINFO; typedef MMCKINFO NEAR *NPMMCKINFO; typedef MMCKINFO FAR *LPMMCKINFO;

    Опишем назначение отдельных полей этой структуры.

    Поле Описание
    ckid Код, соответствующий четырехбуквенному идентификатору фрагмента
    cksize Размер фрагмента в байтах без учета идентификатора фрагмента, поля длины фрагмента и дополнительных байтов выравнивания, которые могут находиться в конце фрагмента
    fccType Тип фрагмента
    dwDataOffset Смещение области данных относительно начала файла в байтах
    dwFlags В этом поле может находиться нулевое значение или флаг MMIO_DIRTY, в последнем случае длина фрагмента может быть изменена, поэтому для ее обновления следует вызвать функцию mmioAscend. Флаг MMIO_DIRTY может быть установлен при создании фрагмента функцией mmioCreateChunk
    <


    В приведенном ниже фрагменте кода создается новый файл, подготавливается структура MMCKINFO, а затем создается фрагмент "RIFF", для чего вызывается функция mmioCreateChunk:

    hFile = mmioOpen(szFileName, NULL, MMIO_CREATE | MMIO_READWRITE); if(hFile != NULL) { ck.ckid = MMIO_CREATERIFF; ck.cksize = waveiocbIn.lpWaveHdr->dwBytesRecorded + sizeof(PCMWAVEFORMAT) + 20; ck.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmioCreateChunk(hFile, (LPMMCKINFO)&ck, MMIO_CREATERIFF); }

    Более подробно этот фрагмент кода будет описан позже, когда мы будем рассказывать вам о приложении WAVE, работающим с wav-файлами и звуковым адаптером на низком уровне.

    Для поиска нужного фрагмента внутри RIFF-файла у вас нет необходимости выполнять побайтовое чтение файла и анализ его внутренней структуры. Найти нужный фрагмент и выполнить позиционирование относительно этого фрагмента вам помогут функции mmioDescend и mmioAscend.

    Функция mmioDescend ищет заданный фрагмент начиная с текущей позиции. Если фрагмент найден, текущая позиция устанавливается на область данных. Напомним, что область данных расположена на 8 байт ближе к концу файла от начала фрагмента (рис. 2.3).

    Функция mmioDescend

    UINT mmioDescend( HMMIO hmmio, // идентификатор открытого файла LPMMCKINFO lpck, // указатель на структуру MMCKINFO // для текущего фрагмента LPMMCKINFO lpckParent, // указатель на структуру MMCKINFO // для внешнего фрагмента UINT wFlags); // режим поиска

    Параметры функции:

    hmmio

    Идентификатор открытого файла, полученный с помощью функции mmioOpen

    lpck

    Указатель на структуру MMCKINFO, в которую будет записана информация о текущем фрагменте

    lpckParent

    Указатель на структуру MMCKINFO, описывающую внешний фрагмент, внутри которого выполняется поиск. В качестве внешнего фрагмента могут выступать только фрагменты "RIFF" или "LIST". Этот параметр можно указывать как NULL, если внешний фрагмент отсутствует

    wFlags

    Если указан флаг MMIO_FINDCHUNK , выполняется поиск фрагмента, заданного своим идентификатором, если MMIO_FINDLIST - выполняется поиск фрагмента внутри фрагмента "LIST", если MMIO_FINDRIFF - внутри фрагмента "RIFF".


    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки

    В приведенном ниже фрагменте кода открывается на чтение wav-файл, затем в нем выполняется поиск фрагментов "WAVE" и "fmt ":

    hmmio = mmioOpen((LPSTR)lpszFileName, NULL, MMIO_READ | MMIO_ALLOCBUF); if(!hmmio) return WIOERR_FILEERROR;

    memset(&ckRIFF, 0, sizeof(MMCKINFO)); ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if(mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    memset(&ckFMT, 0, sizeof(MMCKINFO)); ckFMT.ckid = mmioFOURCC('f', 'm', 't', ' '); if(mmioDescend(hmmio, &ckFMT, &ckRIFF, MMIO_FINDCHUNK)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    Функция mmioAscend предназначена для продвижения текущей позиции к началу следующего фрагмента.

    Функция mmioAscend

    UINT mmioAscend( HMMIO hmmio, // идентификатор открытого файла LPMMCKINFO lpck, // указатель на структуру MMCKINFO UINT wFlags); // режим поиска

    Параметры функции:

    hmmio

    Идентификатор открытого файла, полученный с помощью функции mmioOpen

    lpck

    Указатель на структуру MMCKINFO, предварительно заполненную функцией mmioDescend или mmioCreatechunk

    wFlags

    Параметр не используется, необходимо передавать нулевое значение.

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки

    Функции для рисования битовых изображений DIB

    Video for Windows добавляет в Windows программный интерфейс, с помощью которого приложения могут рисовать изображения DIB на экране. Имена этих функций начинаются с DrawDib , что, очевидно, говорит об их назначении. Функция DrawDibDraw может все, что может функция StretchDIBits , однако работает быстрее. Дополнительно при отображении вызывается система ICM, выполняющая восстановление сжатых изображений непосредственно перед выводом на экран. Если исходное изображение имеет формат True Color, а устройство вывода обладает средним или низким цветовым разрешением, функции выполняют рисование с использованием смешивания (dithering) цветов.

    Функция mciSendCommand

    Для передачи управляющих сообщений драйверам устройств мультимедиа используется функция mciSendCommand , описание которой приведено ниже.
    Функция mciSendCommand
    DWORD mciSendCommand( UINT wDeviceID, // идентификатор устройства UINT wMessage, // код сообщения DWORD dwParam1, // флаги команды DWORD dwParam2); // указатель на структуру параметров
    Параметры функции:
    wDeviceID
    Идентификатор устройства, которому посылается сообщение. Для команды MCI_OPEN не используется, так как идентификатор создается в результате выполнения этой команды
    wMessage
    Код сообщения. Соответствует требуемой функции. Список кодов сообщений (команд) для звукового адаптера приведен в следующем разделе
    dwParam1
    Флаги команды. Флаги используются для того чтобы сообщить функции, какие параметры, заданные в структуре параметров, следует использовать при выполнении команды. Если ни один флаг не указан, параметры игнорируются
    dwParam2
    Указатель на структуру параметров. Формат этой структуры зависит от кода сообщения
    Возвращаемое значение:
    Нуль при нормальном завершении или код ошибки. Список кодов ошибок для функции mciSendCommand приведен в приложении 1. С помощью функции mciGetErrorString, рассмотренной нами ранее, вы можете преобразовать код ошибки в текстовую строку, передав ей этот код в качестве параметра
    Формат структуры параметров (блока параметров) и используемые флаги зависят от кода управляющего сообщения, передаваемого функции mciSendCommand через параметр wMessage.

    Функция mciSendString

    Как передать устройству управляющую строку?
    Очень просто - для этого можно воспользоваться функцией mciSendString . Прототип функции mciSendString находится в файле mmsystem.h:
    Функция mciSendString
    DWORD mciSendString( LPCSTR lpstrCommand, // управляющая строка LPSTR lpstrReturnString, // буфер для результата UINT wReturnLength, // размер буфера HANDLE hCallback) // идентификатор окна извещения
    Параметры функции:
    lpstrCommand
    Дальний указатель на текстовую управляющую строку
    lpstrReturnString
    Указатель на буфер, в который будет записан результат выполнения команды (в текстовом виде). Этот параметр можно указать как NULL, если приложение не интересуется результатом выполнения команды
    wReturnLength
    Размер буфера для записи результата выполнения команды
    hCallback
    Идентификатор окна, которое получит извещение (сообщение MM_MCINOTIFY) после того как устройство завершит операцию. Этот параметр можно указать как NULL, если извещение не используется
    Возвращаемое значение:
    Нуль при успешном завершении или код ошибки (в младшем слове возвращаемого значения):
    MCIERR_BAD_CONSTANT
    Указана константа, неправильная для данной команды
    MCIERR_BAD_INTEGER
    Указано значение, неправильное для данной команды
    MCIERR_DUPLICATE_FLAGS
    Двойное определение параметра или значения
    MCIERR_MISSING_COMMAND_STRING
    Не указана управляющая строка
    MCIERR_MISSING_DEVICE_NAME
    Не указано имя устройства, драйвера, файла или алиас
    MCIERR_MISSING_STRING_ARGUMENT
    Не указан обязательный параметр команды
    MCIERR_NEW_REQUIRED_ALIAS
    При использовании параметра new следует указать алиас
    MCIERR_NO_CLOSING_QUOTE
    В команде отсутствуют закрывающие двойные кавычки
    MCIERR_NOTIFY_ON_AUTO_OPEN
    Для устройств, открываемых автоматически, нельзя указывать флаг notify (флаг notify указывает на необходимость генерации извещающего сообщения при завершении выполнения операции, мы расскажем об этом флаге позже)
    MCIERR_PARAM_OVERFLOW
    Строка параметров не помещается в буфер. Необходимо увеличить размер буфера

    MCIERR_PARSER_INTERNAL

    Ошибка в драйвере устройства. Следует заменить драйвер на новый, более поздней версии

    MCIERR_UNRECOGNIZED_KEYWORD

    Драйвер не распознал параметр управляющей строки

    Например, для проигрывания wav-файла вы можете использовать следующую последовательность вызовов функции mciSendString :

    mciSendString( (LPSTR)"open ding.wav type waveaudio alias snd wait", (LPSTR)szBuf, 256, NULL); mciSendString((LPSTR)"play snd wait", (LPSTR)szBuf, 256, NULL); mciSendString((LPSTR)"close snd", (LPSTR)szBuf, 256, NULL);

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

    Функция mciGetErrorString

    UINT mciGetErrorString( DWORD dwError, // код ошибки LPSTR lpstrBuffer, // буфер для записи текстовой строки UINT wLength); // размер буфера

    Параметры функции:

    dwError

    Код ошибки, полученный от функции mciSendString или mciSendCommand (функция mciSendCommand предназначена для передачи управляющих сообщений, она будет рассмотрена позже)

    lpstrBuffer

    Буфер, в который будет записано текстовое описание ошибки

    wLength

    Размер буфера в байтах

    Возвращаемое значение:

    TRUE при успешном завершении или FALSE, если переданному коду ошибки не соответствует ни одно текстовое описание

    Функция MessageBeep

    Ранее, в предыдущих томах "Библиотеки системного программиста", мы упоминали функцию MessageBeep :
    void MessageBeep(UINT uAlert);
    Эта функция как раз и предназначена для выдачи звуковых сигналов. Правда, если в компьютере не установлен драйвер звукового адаптера или драйвер для работы с динамиком, при вызове этой функции можно услышать только короткий звуковой сигнал "бип".
    Если же звуковой драйвер установлен, в зависимости от значения параметра uAlert функция MessageBeep может воспроизводить один из звуковых фрагментов, записанных в wav-файле.
    Обратите внимание, что в файле win.ini имеется раздел [sound], в котором перечислены различные ситуации. Для ситуации может быть указано имя wav-файла, который должен быть воспроизведен при ее возникновении:
    [sounds] SystemAsterisk=chimes.wav,Asterisk SystemHand=ding.wav,Critical Stop SystemDefault=,Default Beep SystemExclamation=ding.wav,Exclamation SystemQuestion=ding.wav,Question SystemExit=bye.wav,Windows Exit SystemStart=,Windows Start
    У вас нет необходимости изменять этот раздел вручную, так как это можно сделать при помощи приложения Control Panel (рис. 1.8).
    Возможные значения параметра uAlert для функции MessageBeep приведены в следующей таблице.

    Значение Описание
    -1 Стандартный звуковой сигнал, который выдается на встроенный в компьютер динамик
    MB_ICONASTERISK Проигрывается wav-файл, определенный в строке SystemAsterisk раздела [sound] файла win.ini
    MB_ICONEXLAMATION Аналогично для строки SystemExclamation
    MB_ICONHAND Аналогично для строки SystemHand
    MB_ICONQUESTION Аналогично для строки SystemQuestion
    MB_OK Аналогично для строки SystemDefault

    Функция MessageBeep пытается проиграть звуковой фрагмент в асинхронном (фоновом) режиме, если это позволяет звуковой драйвер. Если в системе установлен драйвер Sound Driver for PC Speaker, функция MessageBeep возвращает управление только после того, как проигрывание будет закончено. Если же функция не может проиграть нужный фрагмент, будет "исполнен" стандартный системный звук, определенный в строке SystemDefault раздела [sound] файла win.ini. Если же и это невозможно, вы услышите "бип" из встроенного в компьютер динамика.

    Функция sndPlaySound

    А есть ли простой способ проигрывания произвольного wav-файла?
    Есть, и он действительно прост. Этот способ основан на использовании функции sndPlaySound , которая находится в библиотеке mmsystem.dll. Ее прототип определен в файле mmsystem.h:
    BOOL sndPlaySound(LPCSTR lpszSoundFile, UINT wFlags);
    Через параметр lpszSoundFile этой функции можно передать путь к wav-файлу, идентификатор ресурса, содержащего звуковой фрагмент (вы можете записать звуковой фрагмент в ресурсы приложения), или текстовую строку, определенную в разделе [sound] файла win.ini.
    Параметр wFlags определяет способ проигрывания звукового фрагмента. Используются следующие значения (некоторые из них можно комбинировать при помощи операции ИЛИ):

    Значение Описание
    SND_SYNC Синхронный режим работы. Функция sndPlaySound вернет управление только после завершения проигрывания звукового фрагмента
    SND_ASYNC Асинхронный режим работы. Функция вернет управление немедленно, проигрывание звукового фрагмента будет выполняться в фоновом режиме параллельно с работой приложения
    SND_NODEFAULT Если указанный файл не найден, функция "тихо" возвращает управление, не проигрывая никаких звуков. Если же этот флаг не указан, и файл не найден, будет проигран стандартный системный звук, определенный в строке SystemDefault раздела [sound] файла win.ini. А если и это невозможно, функция не будет ничего проигрывать и вернет значение FALSE
    SND_MEMORY Это значение используется для проигрывания звуковых файлов, загруженных в оперативную память, например, из ресурсов приложения
    SND_LOOP Если указано значение SND_ASYNC, проигрывание звукового фрагмента будет зациклено. Для того чтобы остановить проигрывание, необходимо вызвать функцию sndPlaySound, указав ей в качестве параметра lpszSoundFile значение NULL
    SND_NOSTOP При указании этого значения функция проверяет, выполняется ли в настоящий момент проигрывание фрагмента. Если да, функция возвращает значение FALSE

    Во всех случаях, если не указан параметр SND_NOSTOP, функция sndPlaySound возвращает значение TRUE, если выполняется проигрывание, и FALSE - если нет. Учтите, что при использовании функций MessageBeep и sndPlaySound есть ограничение на размер wav-файла - он должен целиком помещаться в физическую память. Поэтому самые простые способы проигрывания звуковых фрагментов хороши только для относительно небольших файлов.

    Info

    С помощью команды info можно получить информацию об MCI-устройстве:
    info device_id parameter [notify] [wait]
    В качестве параметра parameter можно указывать одну из следующих строк:
    product
    Текстовое описание звукового адаптера
    input
    Текстовое описание устройства ввода звуковой информации
    output
    Текстовое описание устройства вывода звуковой информации
    file
    Имя текущего wav-файла

    Инструментальные средства

    В этом разделе мы сделаем небольшой обзор приложений, которые по своему назначению являются инструментальными средствами, предназначенными для работы со звуком. Такие приложения можно приобрести либо в комплекте со звуковым адаптером, либо отдельно.
    Заслуживает внимания приложение WinDAT, которое создано фирмой Voyetra Technologies и поставляется вместе со звуковым адаптером Sound Galaxy (рис. 1.11).
    Инструментальные средства
    Рис. 1.11. Приложение WinDAT
    Это приложение работает со звуковыми файлами в стандарте Windows (с расширением имени wav) и в стандарте Sound Blaster (с расширением имени voc). Оно намного мощнее приложения Sound Recorder.
    В частности, вы можете выделить звуковой фрагмент на осциллограмме при помощи мыши (выделенный фрагмент отображается инвертированием цвета, что видно на рис. 1.11). Выделенный фрагмент может быть скопирован в Clipboard или удален при помощи стандартного меню "Edit". При помощи строки "Mix Paste" меню "Edit" можно наложить фрагмент, записанный в Clipboard, на любой участок фонограммы, указанный при помощи полосы просмотра. Можно вставить паузу или вырезать ненужный участок фонограммы. Все изменения нетрудно отменить, выбрав в меню "Edit" строку "Undo".
    Меню "Transforms" позволяет вносить изменения в выделенный участок фонограммы. Можно, например, изменить громкость, определить расположение и максимальную амплитуду сигнала в выделенном фрагменте.
    В отличие от приложения Sound Recorder, приложение WinDAT при записи позволяет выбрать стереофонический или монофонический режим, а также задать частоту дискретизации, выбрав ее из возможных для данного адаптера значений. Для этого следует воспользоваться строкой "Setup..." меню "Windows" (достаточно странное место для строки "Setup...", не правда ли?).
    Еще одно интересное приложение для работы со звуковыми файлами разработано фирмой IPI (Interactive Products Inc). Оно называется Audio Server (рис. 1.12).
    Инструментальные средства
    Рис. 1.12.
    Приложение Audio Server

    Это приложение может служить звуковым OLE-сервером, при этом оно поддерживает технологию Drag & Drop. С помощью его вы можете записать звуковой фрагмент (воспользовавшись кнопкой с изображением микрофона), а затем вставить этот фрагмент в документ как объект, просто переместив мышью изображение магнитофонной кассеты из окна приложения Audio Server в нужное место документа. При необходимости с помощью меню "Edit" можно вставить фрагмент в Clipboard не как объект, а как звуковой файл.

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

    Другая разработка фирмы IPI - приложение Voice Toolkit, предназначенное для тех, кто занимается решением проблемы распознавания речи (рис. 1.13).

    Инструментальные средства

    Рис. 1.13. Приложение Voice Toolkit

    С помощью Voice Toolkit вы можете не только записывать и воспроизводить монофонические звуковые фрагменты, но и исследовать их характеристики, в первую очередь спектральные. На рис. 1.13 показан звуковой фрагмент и соответствующий ему двухмерный энергетический спектр, позволяющий исследовать распределение мощности сигнала в зависимости от частоты и времени. Спектр может быть цветной или черно-белый, при этом мощность выделяется либо цветом, либо яркостью.

    Однако наиболее впечатляющая возможность приложения Voice Toolkit - отображение трехмерных энергетических спектров (рис. 1.14).

    Инструментальные средства

    Рис. 1.14. Трехмерный энергетический спектр

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

    Так как "рельеф" поверхности может быть достаточно сложным, предусмотрены мощные средства для просмотра отдельных деталей спектра.Вы, например, можете выделить любой трехмерный участок спектра и увеличить его до размеров окна. Есть средства для поворота полученного участка в пространстве (рис. 1.15).

    Инструментальные средства

    Рис. 1.15. Поворот участка спектра в пространстве

    В приложении Voice Toolkit предусмотрен расширяемый набор фильтров для обработки звуковых фрагментов (звуковые эффекты), такие, как эхо, изменение уровня сигнала, проигрывание в обратном направлении, изменение скорости воспроизведения и т. д. (рис. 1.16).

    Инструментальные средства

    Рис. 1.16. Звуковые эффекты

    Интерфейс низкого уровня

    При необходимости иметь непосредственный доступ к буферам, содержащим звуковые данные, приложение должно использовать интерфейс низкого уровня, обеспечиваемый несколькими функциями с префиксом имени wave, например, waveInOpen, waveOutOpen, waveOutWrite, waveAddBuffer и т. д.
    Общая методика использования интерфейса низкого уровня для воспроизведения wav-файлов такова. Сначала выполняется чтение и проверка формата заголовка wav-файла, открытие устройства вывода с указанием конкретного формата звуковых данных. Затем звуковые данные читаются блоками непосредственно из wav-файла, подготавливаются специальной функцией для вывода и затем передаются драйверу устройства вывода. Драйвер выводит их в звуковой адаптер. Приложение имеет полный контроль над процессом воспроизведения, так как оно само должно подготовить блоки данных в оперативной памяти.
    Аналогично выполняется запись звуковых данных. Вначале требуется открыть устройство ввода, указав ему формат звуковых данных. Затем нужно заказать один или несколько блоков памяти и подготовить их для ввода, вызвав специальную функцию. После этого подготовленные блоки нужно по мере необходимости передавать драйверу устройства ввода, который заполнит их записанными звуковыми данными. Для сохранения записанных данных в wav-файле приложение должно сформировать и записать в файл заголовок wav-файла и звуковые данные из подготовленных и заполненных драйвером устройства ввода блоков памяти.
    В отличие от интерфейса MCI, где многие параметры принимаются по умолчанию, интерфейс низкого уровня требует внимательного и тщательного учета всех деталей процесса записи и воспроизведения. В качестве компенсации за дополнительно затраченные усилия вы получаете большую гибкость и возможность работать со звуковыми данными в реальном времени.
    Дальнейшее изложение материала будет происходить по следующему плану.
    Вначале мы расскажем вам о формате wav-файлов, в которых хранятся звуковые данные. Вы узнаете о функциях, предназначенных для работы с такими файлами. Эти функции экспортируются библиотекой mmsystem.dll. Они сильно облегчают работу с файлами на низком уровне. Попутно вы подробно познакомитесь с форматами звуковых данных.
    Затем мы расскажем о том, как определить состав установленных в системе драйверов устройств мультимедиа, а также возможности отдельных драйверов.
    После этого мы перейдем к описанию процесса воспроизведения и записи на низком уровне. В конце раздела мы опишем приложение WAVE, демонстрирующее способы работы со звуковыми данными на низком уровне.

    Интерфейс управляющих сообщений MCI

    Интерфейс управляющих сообщений более удобен для приложений, составленных на языках программирования С и С++. Он предполагает посылку устройствам мультимедиа управляющих сообщений с помощью функции mciSendCommand .
    В качестве одного из параметров этой функции передается двоичный код управляющего сообщения (команды). Символические константы кодов команд определены в файле mmsystem.h. В дополнение к коду команды функции mciSendCommand передается структура параметров, формат которой зависит от кода команды, а также другая дополнительная информация, необходимая для выполнения команды.

    Как правило, большинство приложений, составленных на языках программирования C и C++, управляют устройством чтения CD ROM с помощью интерфейса управляющих сообщений MCI. Напомним, что приложения, использующие этот интерфейс, посылают устройствам мультимедиа управляющие сообщения с помощью функции mciSendCommand .
    Рассмотрим особенности команд MCI, предназначенных для устройства чтения компакт-дисков.


    Использование интерфейса управляющих сообщений для проигрывания mid-файлов аналогично использованию этого интерфейса для проигрывания wav-файлов. Перечислим кратко допустимые коды управляющих сообщений и самые нужные параметры, специфические для драйвера mciseq.drv.
    MCI_OPEN
    Устройство sequencer открывается командой MCI_OPEN с использованием структуры MCI_OPEN_PARMS, определенной в файле mmsystem.h.
    Поле lpstrDeviceType в этой структуре должно содержать указатель на строку имени устройства, или константный идентификатор устройства. Для устройства sequencer вы можете указать имя "sequencer " или константу MCI_DEVTYPE_SEQUENCER .
    Через параметр lpstrElementName передается указатель на путь к проигрываемому mid-файлу.
    MCI_CLOSE
    Эта команда закрывает устройство. Ее необходимо выдавать после завершения работы с устройством.
    MCI_PLAY
    Команда MCI_PLAY предназначена для проигрывания. Для mid-файлов она используется точно таким же образом, что и для wav-файлов.
    MCI_PAUSE
    Команда MCI_PAUSE приостанавливает выполнение операции проигрывания.
    MCI_RESUME
    Эта команда не поддерживается драйвером mciseq.drv . Используйте вместо нее команду MCI_PLAY без указания позиции (для запуска проигрывания с текущей позиции).
    MCI_STOP
    Команда MCI_STOP останавливает выполнение операции проигрывания.
    MCI_SEEK
    Команда MCI_SEEK позволяет выполнять позиционирование в пределах mid-файла.
    MCI_BREAK
    С помощью команды MCI_BREAK указывается виртуальный код клавиши, с помощью которой можно прервать выполнение операции.
    MCI_GETDEVCAPS
    С помощью команды MCI_GETDEVCAPS можно определить возможности устройства.
    MCI_INFO
    С помощью этой команды можно получить информацию об устройстве в виде текстовой строки, такую как описание аппаратуры, имя файла, связанного с устройством.
    MCI_SYSINFO
    С помощью этой команды можно получить системную информацию об устройстве в виде текстовой строки.
    MCI_STATUS
    Команда MCI_STATUS используется для определения текущего состояния устройства.
    MCI_SET
    Команда MCI_SET предназначена для установки режима работы устройства.
    MCI_COPY
    Команда MCI_COPY предназначена для копирования данных в универсальный буфер обмена Clipboard. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT.
    MCI_PASTE
    Команда MCI_PASTE вставляет данные из Clipboard в текущий буфер устройства. Для нее, как и для команды MCI_COPY, используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT.
    MCI_CUT
    Команда MCI_CUT удаляет данные из текущего буфера устройства и копирует их в универсальный буфер обмена Clipboard. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT.
    MCI_DELETE
    Команда MCI_DELETE удаляет данные из текущего буфера устройства без копирования их в Clipboard.
    MCI_LOAD
    Команда MCI_LOAD предназначена для загрузки файла.

    Интерфейс управляющих строк MCI

    MCI представляет собой универсальный, независимый от особенностей аппаратного обеспечения интерфейс, предназначенный для управления устройствами мультимедиа, такими как звуковые и видеоадаптеры, устройства чтения (проигрывания) звуковых компакт-дисков и лазерных видеодисков. В большинстве случаев возможности, предоставляемые этим интерфейсом, удовлетворят потребности любого приложения мультимедиа, предназначенного для записи и воспроизведения звуковой или видеоинформации. Если же приложение должно обрабатывать данные на низком уровне или в реальном времени (задачи редактирования и преобразования wav-файлов, распознавания речи, образов, преобразования речи в реальном времени), оно может воспользоваться низкоуровневым интерфейсом, который мы рассмотрим позже.
    Все функции интерфейса MCI экспортируются библиотекой mmsystem.dll . Эти функции обращаются непосредственно к драйверам устройств ввода/вывода, а также к функциям более низкого уровня, определенным в библиотеке mmsystem.dll.
    Приложения могут использовать два типа программного интерфейса MCI .
    Первый тип называется интерфейс управляющих строк (Command-String Interface ). Он основан на использовании текстовых команд (таких, как open, play, close).
    Второй тип - это интерфейс управляющих сообщений (Command-Message Interface ). Для управления устройствам посылаются сообщения, коды которых определены в файле mmsystem.h через символические константы (например, MCI_OPEN, MCI_PLAY, MCI_CLOSE).
    Интерфейс управляющих строк удобен для использования в системах программирования высокого уровня, так как позволяет быстро получить необходимый результат. Например, для проигрывания звукового файла ding.wav достаточно передать звуковому адаптеру следующую последовательность управляющих строк:
    open ding.wav type waveaudio alias snd wait play snd wait close snd
    Не намного сложнее выглядят управляющие строки (команды) для записи звукового фрагмента в wav-файл или для проигрывания видеофрагмента из avi-файла.
    Для передачи команд используется функция mciSendString , которой в качестве первого параметра передается указатель на строку команды.
    Однако более простое и тесное взаимодействие между приложением, составленным на языке программирования С, и устройством мультимедиа можно достичь при использовании интерфейса управляющих сообщений. Для посылки такого управляющего сообщения приложение должно вызвать функцию mciSendCommand . Второй параметр этой функции содержит код управляющего сообщения.
    Мы рассмотрим основные приемы использования обоих типов интерфейса MCI на примере работы со звуковым адаптером и wav-файлами. В дальнейшем, при описании работы с устройствами других типов, мы ограничимся только особенностями команд MCI этих устройств.

    Для передачи управляющей строки устройству чтения CD ROM вы должны использовать функцию mciSendString . Вам могут потребоваться и другие функции, описанные во второй главе и предназначенные для работы с интерфейсом MCI.


    Для работы с музыкальным синтезатором, входящим в комплект звукового адаптера, используется драйвер mciseq.drv . Свое название он получил от слова sequencer (дословно можно перевести как "устройство задания последовательности"). Именно так в терминологии мультимедиа называется устройство, предназначенное для работы с файлами в стандарте MIDI.
    При работе с mid-файлами на уровне управляющих строк MCI вы можете пользоваться практически всеми командами, рассмотренными нами в разделе, посвященном записи и воспроизведению звука при помощи устройства waveaudio (не поддерживаются команды resume, record и save). Например, следующая последовательность команд выполнит проигрывание файла canyon.mid, который поставляется в составе дистрибутива операционной системы Windows:
    open g:\win\canyon.mid alias music wait play music wait close music
    Если с помощью команды play будет предпринята попытка проиграть mid-файл, не авторизованный для Windows, на экране появится предупреждающее сообщение о том, что при стандартной конфигурации MIDI данный файл может проигрываться неправильно (рис.4.1).
    Интерфейс управляющих строк MCI
    Рис. 4.1. Сообщение о попытке выполнить проигрывание файла MIDI, не авторизованного для Windows
    Драйвер mciseq.drv не поддерживает следующие параметры команды set: audio all off, audio all on, audio left on, audio left off, audio right on, audio right off. Дополнительно вы можете использовать параметр time format song pointer, устанавливающий формат времени в единицах "одна шестнадцатая ноты", параметр tempo, позволяющий задать темп исполнения мелодии, и некоторые другие. Полный перечень вы сможете найти в документации, которая поставляется вместе с Microsoft SDK для Windows 3.1.

    Использование интерфейса управляющих строк

    Работа с устройствами ввода/вывода системы мультимедиа напоминает работу с обычными файлами в том смысле, что вначале вы открываете устройство, затем выполняете с ним те или иные операции, затем закрываете устройство.
    Прежде чем работать с устройством средствами MCI, его следует открыть при помощи команды open. Далее при необходимости можно задать режим работы устройства, послав ему команду set с параметрами. Для включения режима проигрывания или записи используются, соответственно, команды play и record . В любой момент времени можно узнать состояние устройства, если послать ему команду status . После использования устройства его необходимо закрыть при помощи команды close .

    Использование класса окна MCI

    Приложение может создать окно MCI, указав его параметры, управлять им с помощью передачи сообщений через удобный в использовании набор макрокоманд и функций, а также удалить окно MCI. Этих функций и макрокоманд так много, что мы не можем описать их все подробно из-за ограниченного объема нашей книги. Рассмотрим некоторые, самые полезные на наш взгляд, функции и макрокоманды, предназначенные для работы с окном MCI. Полную информацию вы сможете найти в документации, которая поставляется вместе с Video for Windows Development Kit.

    Извещения для родительского окна

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

    Коды управляющих сообщений MCI

    Коды управляющих сообщений делятся на системные (System), обязательные (Required), базовые (Basic) и расширенные (Extended), точно также как и команды, используемые в интерфейсе управляющих строк, рассмотренном нами в предыдущем разделе.
    Приведем список кодов управляющих сообщений (в дальнейшем просто команд), которые используются для управления звуковым адаптером.

    Команда Тип Описание
    MCI_BREAK Системная Назначение виртуального кода клавиши, с помощью которой можно прервать работу устройства
    MCI_SYSINFO - Получение системной информации об устройстве (в виде текстовой строки)
    MCI_GETDEVCAPS Обязательная Определение возможностей устройства
    MCI_CLOSE - Закрытие устройства
    MCI_INFO - Получение текстовой информации об устройстве
    MCI_OPEN - Открытие устройства
    MCI_STАTUS - Определение состояния устройства
    MCI_LOAD Базовая Загрузка данных из файла
    MCI_PAUSE - Пауза при проигрывании
    MCI_PLAY - Включение режима проигрывания
    MCI_RECORD - Включение режима записи
    MCI_RESUME - Продолжение проигрывания после паузы
    MCI_SAVE - Сохранение данных в файле
    MCI_SEEK - Позиционирование
    MCI_SET - Установка режима работы устройства
    MCI_STOP - Останов проигрывания
    MCI_CUE Расширенная Подготовка устройства для проигрывания или записи
    MCI_DELETE - Удаление фрагмента данных

    Как нетрудно заметить, состав и назначение групп управляющих сообщений полностью соответствует составу и назначению групп управляющих строк. Расскажем об использовании наиболее полезных управляющих сообщений (команд MCI).

    Команды для воспроизведения, записи и позиционирования

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

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

    Команды инициализации и завершения работы

    В этой группе всего две команды - open и close, предназначенные, соответственно, для открытия и закрытия устройства ввода/вывода звука.

    Команды MCI

    Какие бывают команды?
    Все команды можно разделить на четыре группы : системные (System), обязательные (Required), базовые (Basic) и расширенные (Extended).
    Системные команды не передаются драйверу устройства, они обрабатываются непосредственно системой MCI.
    Обязательные команды поддерживаются любыми устройствами. Примером таких команд могут послужить строки open и close .
    Некоторые из базовых команд могут не поддерживаться устройством. Например, устройство может только проигрывать дорожки звукового компакт-диска, но не записывать их.
    И, наконец, расширенные команды, которые дополняют возможности базовых команд, могут поддерживаться только некоторыми типами устройств.
    Приведем список команд, которые используются для управления звуковым адаптером.

    Команда Тип Описание
    break Системная Назначение виртуального кода клавиши, с помощью которой можно прервать работу устройства.
    sysinfo - Получение системной информации об устройстве (в виде текстовой строки)
    capability Обязательная Определение возможностей устройства
    close - Закрывание устройства
    info - Получение текстовой информации об устройстве
    open - Открывание устройства
    status - Определение состояния устройства
    load Базовая Загрузка данных из файла
    pause - Пауза при проигрывании
    play - Включение режима проигрывания
    record - Включение режима записи
    resume - Продолжение проигрывания после паузы
    save - Сохранение данных в файле
    seek - Позиционирование
    set - Установка режима работы устройства
    stop - Останов проигрывания
    cue Расширенная Подготовка устройства для проигрывания или записи
    delete - Удаление фрагмента данных

    Далее мы расскажем об использовании этих команд применительно к звуковому адаптеру. Особенности других устройств мультимедиа будут рассмотрены позже.

    Команды установки режима работы

    Команда break не имеет никаких особенностей. Она позволяет определить код виртуальной клавиши, предназначенной для прерывания процесса выполнения команды. По умолчанию используется комбинация клавиш .
    Для команды set можно указывать следующие параметры:
    audio all off
    Отключение звукового выхода
    audio all on
    Включение звукового выхода
    audio left off
    Отключение левого канала
    audio left on
    Включение левого канала
    audio right off
    Отключение правого канала
    audio right on
    Включение правого канала
    door closed
    Загрузка компакт-диска и фиксирование его в устройстве. Этот параметр может поддерживаться не всеми устройствами (так же, как и параметр door open)
    door open
    Извлечение компакт-диска
    time format milliseconds
    В качестве единицы измерения при позиционировании используются миллисекунды. Строку milliseconds можно также указывать как ms
    time format msf
    В качестве единицы измерения при позиционировании используются минуты, секунды и фреймы. В качестве разделителя минут секунд и фреймов используется двоеточие. Этот формат используется по умолчанию
    time format tmsf
    В качестве единицы измерения при позиционировании используются дорожки, минуты, секунды и фреймы

    Компрессия данных

    Для компресии видеоданных можно использовать один из компрессоров , поставляемых вместе с Video for Windows, или приобретенный отдельно.
    Если видеоизображение содержит большие площади однотонного цвета, хорошие результаты получаются при использовании метода компресии Microsoft RLE . Этим методом имеет смысл пользоваться для сжатия видеофильмов, записанных с экрана компьютера при помощи приложения Screen Capture. Особенно хорошие результаты получаются в том случае, если изображение меняется от кадра к кадру не очень сильно.
    Метод компресии Cinepak Codek дает хорошие результаты при необходимости подготовить avi-файл для записи на компакт-диск со скоростью передачи данных 150 Кбайт в секунду (одинарная скорость). Для хранения видеоданных используется формат True Color (в нем для представления цвета используются 24 бита данных). При необходимости воспроизведения в режимах более низкого цветового разрешения выполняется автоматическое преобразование цветов и, если это нужно, создание цветовых палитр. Учтите, что процесс сжатия изображения может длиться часами даже при использовании высокопроизводительного компьютера.
    Более быстро, хотя и не так качественно, как Cinepak Codek, работает метод Microsoft Video 1 . При использовании этого метода вы можете выбрать 8-битовый или 16-битовый формат для храненеия цвета.
    Метод Intel Indeo Video R3.1 хранит изображения в формате True Color. Формат видеоданных совместим с видеоадаптером Smart Video Recorder, позволяющим получать при записи сжатые данные в реальном времени.
    Создавая avi-файл, рекомендуется попробовать несколько методов и остановиться на том, который обеспечивает хорошее качество при приемлимых размерах файла.
    Приложения могут обращаться непосредственно к менеджеру ICM для сжатия и восстановления видеоданных, причем ICM по запросу приложения может вывести восстановленные данные на экран. Как правило, явное обращение к ICM требуется только для приложений, редактирующих видеоданные. Средства записи и воспроизведения видео, предоставляемые окнами AVICap и MCI, выполняют компрессию автоматически в прозрачном для приложения режиме.

    Аналогично используется компрессор звуковых данных ACM. Система ACM может выполнять не только компрессию и восстановление звуковых данных, но также преобразование и фильтрацию (например, с помощью соответствующего фильтра можно добавить эхо). ACM устанавливается между приложением и библиотекой mmsystem.dll и работает с помощью механизма перехвата вызовов этой библиотеки. Для выполнения преобразований формата ACM обращается к драйверам преобразования формата, которые устанавливаются в системе вместе с Video for Windows или дополнительно.

    Пользователь может управлять системой ACM при помощи приложения Sound Mapper , которое доступно из Control Panel после установки Video for Windows (рис. 5.4).

    Компрессия данных

    Рис. 5.4. Настройка параметров приложения Sound Mapper

    В поле Sound Mapper Drivers отображается список установленных драйверов и их приоритет (который можно изменить, воспользовавшись кнопкой "Priority..."). Для работы будет выбран первый подходящий по своим возможностям драйвер, причем выбор будет выполняться с учетом приоритетов.

    В поле Sound Device Preferences можно выбрать устройство для проигрывания или записи (если их несколько).

    Систему ACM можно рассматривать как расширение библиотеки mmsystem.dll, обеспечивающее возможность работы с новыми форматами звуковых данных. Когда приложение пытается открыть устройство, поддерживающее тот или иной формат звуковых данных, ACM либо предоставляет приложению физическое устройство, либо подбирает компрессор или декомпрессор, преобразующий указанный формат в один из форматов, поддерживаемых физическим устройством.

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

    MCI_BREAK

    С помощью команды MCI_BREAK указывается виртуальный код клавиши, предназначенной для прерывания выполнения операции. Для этой команды необходимо использовать следующую структуру блока параметров:
    typedef struct tagMCI_BREAK_PARMS { DWORD dwCallback; int nVirtKey; UINT wReserved0; HWND hwndBreak; UINT wReserved1; } MCI_BREAK_PARMS; typedef MCI_BREAK_PARMS FAR * LPMCI_BREAK_PARMS;
    Поле nVirtKey определяет виртуальный код клавиши прерывания.
    В поле hwndBreak можно указать идентификатор окна, которое должно быть текущим для обеспечения возможности прерывания с помощью заданной клавиши.
    Поля wReserved0 и wReserved1 зарезервированы.
    Для команды MCI_BREAK можно указывать следующие флаги:

    Флаг Описание
    MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
    MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса позиционирования
    MCI_BREAK_KEY Поле nVirtKey содержит виртуальный код клавиши прерывания команды
    MCI_BREAK_HWND Поле hwndBreak содержит идентификатор окна, которое должно быть текущим для обеспечения возможности прерывания команды
    MCI_BREAK_OFF Используется для отключения прерывания

    По умолчанию для прерывания используется комбинация клавиш .

    MCI_CLOSE

    Эта команда закрывает устройство. Ее необходимо выдавать после завершения работы с устройством.
    Для команды MCI_CLOSE используется блок параметров в виде структуры MCI_GENERIC_PARMS , описанной в файле mmsystem.h:
    typedef struct tagMCI_GENERIC_PARMS { DWORD dwCallback; } MCI_GENERIC_PARMS; typedef MCI_GENERIC_PARMS FAR *LPMCI_GENERIC_PARMS;
    Эта упрощенная структура предназначена для тех случаев, когда команде не требуются дополнительные параметры. Поле dwCallback используется также, как и в команде MCI_OPEN.
    В следующем фрагменте кода закрывается устройство с идентификатором, записанным в поле wDeviceID структуры mciOpen:
    MCI_GENERIC_PARMS mcigen; DWORD dwrc; mcigen.dwCallback = 0; dwrc = mciSendCommand(mciOpen.wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen);

    MCI_COPY

    Команда MCI_COPY предназначена для копирования данных в универсальный буфер обмена Clipboard. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT.

    MCI_CUE

    Команда MCI_CUE используется для подготовки устройства к записи или воспроизведению, после которой эти операции выполняются с минимальной задержкой. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS и следующий набор флагов:

    Флаг Описание
    MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
    MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса


    MCI_CUT

    Команда MCI_CUT удаляет данные из текущего буфера устройства и копирует их в универсальный буфер обмена Clipboard. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT.

    MCI_DELETE

    Команда MCI_DELETE удаляет данные из текущего буфера устройства без копирования их в Clipboard. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS.
    Вместе с этой командой при работе со звуковым адаптером можно использовать структуру MCI_WAVE_DELETE_PARMS :
    typedef struct tagMCI_WAVE_DELETE_PARMS { DWORD dwCallback; DWORD dwFrom; DWORD dwTo; } MCI_WAVE_DELETE_PARMS; typedef MCI_WAVE_DELETE_PARMS FAR *LPMCI_WAVE_DELETE_PARMS;
    Поле dwFrom используется для передачи команде начальной позиции для удаления, поле dwTo - для передачи конечной позиции удаления.
    Вместе с командой MCI_DELETE используются перечисленные ниже флаги.

    Флаг Описание
    MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
    MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса
    MCI_FROM Поле dwFrom содержит начальную позицию для удаления
    MCI_TO Поле dwTo содержит конечную позицию для удаления


    MCI_GETDEVCAPS

    С помощью команды MCI_GETDEVCAPS можно определить возможности устройства мультимедиа. Для нее используется блок параметров в формате структуры MCI_GETDEVCAPS_PARMS , определенной в файле mmsystem.h следующим образом:
    typedef struct tagMCI_GETDEVCAPS_PARMS { DWORD dwCallback; DWORD dwReturn; DWORD dwItem; } MCI_GETDEVCAPS_PARMS; typedef MCI_GETDEVCAPS_PARMS FAR * LPMCI_GETDEVCAPS_PARMS;
    В поле dwReturn после возврата из функции mciSendCommand будет записано значение требуемого параметра. Код нужного параметра следует записать в поле dwItem перед вызовом функции mciSendCommand.
    Приведем возможные значения параметра dwItem:

    Значение параметра dwItem Описание
    MCI_GETDEVCAPS_CAN_EJECT Если устройство может выталкивать носитель данных (например, компакт-диск), после возврата из функции mciSendCommand в поле dwReturn будет ненулевое значение TRUE
    MCI_GETDEVCAPS_CAN_PLAY Устройство может проигрывать
    MCI_GETDEVCAPS_CAN_RECORD Устройство может записывать
    MCI_GETDEVCAPS_CAN_SAVE Устройство может сохранять записанные данные в файле
    MCI_GETDEVCAPS_COMPOUND_DEVICE Устройство может работать с файлами
    MCI_GETDEVCAPS_DEVICE_TYPE Требуется определить тип устройства. Для звукового адаптера возвращается константа MCI_DEVTYPE_WAVEFORM_AUDIO
    MCI_GETDEVCAPS_HAS_AUDIO Устройство имеет звуковой выход
    MCI_GETDEVCAPS_HAS_VIDEO Устройство имеет видеовыход
    MCI_GETDEVCAPS_USES_FILES При открытии устройства требуется указывать имя файла
    MCI_WAVE_GETDEVCAPS_INPUT Количество звуковых входов
    MCI_WAVE_GETDEVCAPS_OUTPUT Количество звуковых выходов (каналов)

    Для команды MCI_GETDEVCAPS можно использовать следующий набор флагов:

    Флаг Описание
    MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
    MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса
    MCI_GETDEVCAPS_ITEM Поле dwItem содержит константу, соответствующую определяемому параметру устройства


    MCI_INFO

    С помощью этой команды можно получить информацию об устройстве в виде текстовой строки, такую как описание аппаратуры, имя файла, связанного с устройством.
    Используется блок параметров в формате структуры MCI_INFO_PARMS :
    typedef struct tagMCI_INFO_PARMS { DWORD dwCallback; LPSTR lpstrReturn; DWORD dwRetSize; } MCI_INFO_PARMS; typedef MCI_INFO_PARMS FAR * LPMCI_INFO_PARMS;
    Поле lpstrReturn должно содержать дальний указатель на буфер, в который будет записана строка информации. Размер этого буфера следует передать через поле dwRetSize.
    Приведем набор флагов для команды MCI_INFO:

    Флаг Описание
    MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
    MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса
    MCI_INFO_PRODUCT Требуется получить описание аппаратуры устройства
    MCI_INFO_FILE Требуется получить имя текущего файла, связанного с устройством
    MCI_WAWE_INPUT Имя текущего устройства ввода
    MCI_WAVE_OUTPUT Имя текущего устройства вывода


    MCI_LOAD

    Команда MCI_LOAD предназначена для загрузки файла. Она использует блок параметров в формате структуры MCI_LOAD_PARMS :
    typedef struct tagMCI_LOAD_PARMS { DWORD dwCallback; LPCSTR lpfilename; } MCI_LOAD_PARMS; typedef MCI_LOAD_PARMS FAR * LPMCI_LOAD_PARMS;
    Через поле lpfilename передается указатель на буфер, содержащий путь к файлу.
    Вместе с командой MCI_LOAD можно использовать следующие флаги:

    Флаг Описание
    MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
    MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса
    MCI_LOAD_FILE Поле lpfilename содержит указатель на строку пути к файлу


    MCI_OPEN

    Перед использованием устройства мультимедиа вы должны его открыть, вызвав функцию mciSendCommand и указав ей в качестве второго параметра код команды MCI_OPEN .
    Первый параметр (идентификатор устройства) можно указать как 0, так как пока мы не открыли устройство, ему не присвоен никакой идентификатор.
    Через последний (четвертый) параметр нужно передать функции адрес заполненной структуры MCI_OPEN_PARMS , определенной в файле mmsystem.h:
    typedef struct tagMCI_WAVE_OPEN_PARMS { DWORD dwCallback; UINT wDeviceID; UINT wReserved0; LPCSTR lpstrDeviceType; LPCSTR lpstrElementName; LPCSTR lpstrAlias; } MCI_OPEN_PARMS; typedef MCI_OPEN_PARMS FAR *LPMCI_OPEN_PARMS;
    В этой структуре младшее слово поля dwCallback должно содержать идентификатор окна, которому после выполнения команды будет посылаться извещение в виде сообщения MM_MCINOTIFY . Если извещение не посылается, значение этого поля игнорируется.
    В поле wDeviceID после возвращения из функции mciSendCommand будет записан идентификатор, присвоенный устройству при открытии. Все последующие команды MCI должны ссылаться на этот идентификатор.
    Поле wReserved0 зарезервировано, в него следует записать нулевое значение.
    Поле lpstrDeviceType содержит указатель на строку имени устройства, или константный идентификатор устройства. Для звукового адаптера вы можете указать имя "waveaudio " или константу MCI_DEVTYPE_WAVWFORM_AUDIO .
    Через параметр lpstrElementName передается указатель на путь к файлу, если нужно проиграть звуковой фрагмент, записанный в wav-файле.
    Дополнительно при открытии устройства ему можно назначить алиасное имя, записав в поле lpstrAlias указатель на строку алиасного имени.
    Третий параметр функции mciSendCommand предназначен для флагов, определяющих, какие из полей структуры параметров следует использовать при открытии устройства, а также для флага MCI_WAIT, устанавливающего режим работы функции (с ожиданием или без ожидания). Флаги можно объединять с помощью логической операции ИЛИ.
    Для структуры MCI_OPEN_PARMS определены следующие флаги:

    Флаг Описание
    MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
    MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса открытия устройства
    MCI_OPEN_ALIAS Используется алиасное имя, адрес строки имени должен быть указан в поле lpstrAlias
    MCI_OPEN_SHAREABLE Устройство открывается в режиме совместного использования несколькими приложениями одновременно
    MCI_OPEN_ELEMENT Поле lpstrElementName содержит указатель на строку, в которой находится путь к файлу
    MCI_OPEN_TYPE Поле lpstrDeviceType содержит указатель на строку имени устройства, например, адрес строки "waveaudio"
    MCI_OPEN_TYPE_ID Поле lpstrDeviceType содержит константный идентификатор устройства, например, константу MCI_DEVTYPE_WAVWFORM_AUDIO
    Приведенный ниже фрагмент кода открывает устройство "waveaudio", причем будет открыт файл, адрес пути к которому записан в переменной szFileName:

    MCI_OPEN_PARMS mciOpen; 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);

    После выполнения этого фрагмента в переменную dwrc, будет записан код результата завершения. При успешном завершении в поле wDeviceID структуры mciOpen будет находиться идентификатор открытого устройства.

    Для звукового адаптера вы можете использовать расширенную структуру MCI_WAVE_OPEN_PARMS , также определенную в файле mmsystem.h:

    typedef struct tagMCI_WAVE_OPEN_PARMS { DWORD dwCallback; UINT wDeviceID; UINT wReserved0; LPCSTR lpstrDeviceType; LPCSTR lpstrElementName; LPCSTR lpstrAlias; DWORD dwBufferSeconds; } MCI_WAVE_OPEN_PARMS; typedef MCI_WAVE_OPEN_PARMS FAR *LPMCI_WAVE_OPEN_PARMS;

    По сравнению со структурой MCI_OPEN_PARMS в ней есть дополнительное поле dwBufferSeconds. Это поле определяет размер внутреннего буфера системы MCI для звукового драйвера. Численно размер буфера должен быть равен длительности звучания в секундах.

    Чтобы задействовать это дополнительное поле, следует указать функции mciSendCommand флаг MCI_WAVE_OPEN_BUFFER .

    MCI_PASTE

    Команда MCI_PASTE вставляет данные из Clipboard в текущий буфер устройства. Для нее, как и для команды MCI_COPY, используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT.

    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).

    Система Microsoft Video for Windows

    Рис. 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).

    Система Microsoft Video for Windows

    Рис. 1.22. Версия приложения Media Player для проигрывания avi-файлов

    Изображение появляется в отдельном окне (рис. 1.23). Это окно можно уменьшить до пиктограммы, при этом "кино" не остановится.

    Система Microsoft Video for Windows

    Рис. 1.23. Окно, в котором проигрывается видеоизображение

    Если же у вас очень много avi-файлов, вы можете их каталогизировать и просматривать с помощью приложения Microsoft Media Browser (рис. 1.24).

    Система Microsoft Video for Windows

    Рис. 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).
    Окно MCI
    Рис. 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 поставляется приложение winmix.exe, представляющее собой электронный микшерный пульт, управляющий работой адаптера (рис. 1.6). С другими адаптерами поставляются утилиты аналогичного назначения.
    Панель управления звуковым адаптером
    Рис. 1.6. Управляющая панель
    С помощью этого приложения вы можете устанавливать уровень сигнала до смешивания (набор регуляторов "Mixer Control", регулировать громкость ("Vol"), баланс ("Bal"), тембр для низких и высоких частот ("Bas" и "Trb"), включать и выключать стереофоническое звучание (кнопка "ST"), а также контролировать уровень сигнала по индикатору, расположенному в верхнем левом углу панели. При выключенном переключателе "Lock" можно регулировать уровни сигнала отдельно для каждого канала (в стереофоническом режиме).
    С помощью меню "ADC" можно изменять подключение аналого-цифрового преобразователя (рис. 1.7).
    Панель управления звуковым адаптером
    Рис. 1.7. Диалоговая панель "ADC Select"
    Группа переключателей "ADC Input" позволяет подключить этот преобразователь к микрофону ("Mic"), аналоговому выходу устройства чтения компакт-дисков ("CD") или входной линии ("Line in"). Вы также можете разрешить смешивание сигналов от различных входов (переключатель "Mixer On") или запретить такое смешивание ("Mixer Off").
    Все установленные параметры, включая настройку громкости и тембра, можно сохранить в энергонезависимой памяти звукового адаптера, для чего из меню "Option" приложения следует выбрать строку "Save Settings".
    Отметим, что не все звуковые адаптеры допускают регулировку тембра или могут сохранять параметры в энергонезависимой памяти.

    Pause

    Временный останов при воспроизведении или записи (пауза)
    pause device_id

    Play

    Команда play предназначена для запуска процесса воспроизведения. Она имеет следующий формат:
    play device_id [from position [to position]] [notify] [wait]
    Идентификатор устройства device_id создается при открытии устройства командой open. Например, если для открытия устройства была использована строка
    open c:\windows\ding.wav alias ding
    то в качестве параметра device_id можно использовать алиас ding:
    play ding
    Если не указан параметр from position, проигрывание начинается с текущей позиции. Сразу после открытия текущая позиция устанавливается в начало файла. Параметр to position позволяет указать конечную позицию, при достижении которой проигрывание прекращается. Перед использованием параметров from и to необходимо установить формат для позиционирования при помощи команды set (см. ниже).
    Если задан параметр notify, и при передаче строки в последнем параметре функции mciSendString был указан идентификатор окна для оповещения, после завершения операции проигрывания функция этого окна получит сообщение MM_MCINOTIFY. Обработчик этого сообщения может закрыть устройство или выполнить повторное проигрывание фрагмента, например, с самого начала.
    Указав параметр wait, можно установить псевдосинхронный режим воспроизведения. В этом случае функция mciSendString вернет управление только после завершения операции, однако работа других приложений Windows будет продолжаться во время воспроизведения. Если же этот параметр не указан, функция mciSendString запустит процесс в асинхронном режиме и немедленно вернет управление.

    Подводя итоги

    Системы мультимедиа позволяют интегрировать в одном приложении звук, видео, графические изображения и текстовую информацию. Появление цифровых компакт-дисков открыло реальную возможность создания таких приложений, о которых раньше мы не могли и мечтать. Теперь компьютер может передавать человеку информацию в наиболее удобном для него виде, а именно, с помощью звука, изображений и видео. Все это не только увеличивает привлекательность компьютера как инструмента для выполнения какой-либо работы, но и открывает совершенно новые области для его использования.
    Если рассматривать существующие средства разработки приложений Windows, использующих технологию мультимедиа, можно выделить три уровня таких средств.
    Первый уровень, самый высокий, предназначен для конечных пользователей, создающих приложения без использования программирования. Существуют готовые системы, с помощью которых пользователь может создавать интерактивные приложения мультимедиа, не написав ни одной строчки программного кода. Эти системы удобны, когда нужно быстро получить результат: подготовить демонстрацию или рекламный ролик, создать несложную обучающую систему или базу данных, содержащую звуковую или видеоинформацию.
    Второй уровень предполагает программирование с использованием интерфейса высокого уровня MCI (Media Control Interface - интерфейс управления средой). С помощью этого интерфейса приложение Windows может выдавать команды драйверам устройств ввода/вывода звуковой и видеоинформации, включающие режим записи, воспроизведения, перемотки и т. п. Если ваше приложение должно выполнять запись или воспроизведение звуковых или видеоданных, имеет смысл воспользоваться именно этим уровнем, скрывающим от программиста многие сложные процедуры, необходимые для работы с драйверами устройств мультимедиа.
    Третий, самый низкий, уровень позволяет приложениям получить доступ к буферам, содержащим воспроизводимые или записываемые звуковые или видеоданные, работать с внутренней структурой файлов, содержащих звуковую или видеоинформацию, а также использовать другие дополнительные возможности. К этому уровню обычно обращаются в тех случаях, когда ни один из более высоких уровней не позволяет достичь нужного результата. Как правило, низкий уровень используется для создания инструментальных средств и приложений, обрабатывающих звуковую и видеоинформацию в реальном времени.

    Позиционирование

    Для позиционирования внутри файла, открытого при помощи функции mmioOpen, следует использовать функцию mmioSeek .
    Функция mmioSeek
    LONG mmioSeek( HMMIO hmmio, // идентификатор открытого файла LONG dwOffset, // смещение для текущей позиции int nOrigin); // интерпретация смещения
    Параметры функции:
    hmmio
    Идентификатор открытого файла, полученный с помощью функции mmioOpen
    dwOffset
    Величина смещения в байтах, на которое будет продвинута текущая позиция в файле. Интерпретация этого значения зависит от параметра nOrigin
    nOrigin
    Этот параметр определяет способ использования смещения, заданного параметром dwOffset. Можно использовать константы SEEK_SET (смещение от начала файла), SEEK_CUR (смещение от текущей позиции в файле), SEEK_END (смещение от конца файла).
    Возвращаемое значение:
    Возвращается новое смещение текущей позиции в файле от начала файла (в байтах) или -1 при возникновении ошибки

    Коды ошибок MCI

    MCIERR_BAD_TIME_FORMAT
    Неправильный формат времени
    MCIERR_CANNOT_LOAD_DRIVER
    Невозможно загрузить драйвер
    MCIERR_CANNOT_USE_ALL
    Для этой команды нельзя использовать строку all в качестве имени устройства
    MCIERR_CREATEWINDOW
    Невозможно создать или использовать окно
    MCIERR_DEVICE_LENGTH
    Слишком длинное имя устройства или драйвера (больше 79 символов)
    MCIERR_DEVICE_LOCKED
    Устройство закрыто, через несколько секунд можно попробовать повторить команду
    MCIERR_DEVICE_NOT_INSTALLED
    Указанное устройство не установлено в системе
    MCIERR_DEVICE_NOT_READY
    Драйвер не готов выполнить запрос
    MCIERR_DEVICE_OPEN
    Имя устройства уже используется приложением в качестве алиаса (альтернативного имени)
    MCIERR_DEVICE_ORD_LENGTH
    Слишком длинное имя драйвера (больше 79 символов)
    MCIERR_DEVICE_TYPE_REQUIRED
    Указанный драйвер не найден
    MCIERR_DRIVER
    Ошибка драйвера. Следует установить драйвер более поздней версии
    MCIERR_DRIVER_INTERNAL
    Аналогично предыдущему
    MCIERR_DUPLICATE_ALIAS
    Указанный алиас уже используется в системе
    MCIERR_EXTENSION_NOT_FOUND
    Указанному расширению имени файла не соответствует ни одно устройство, установленное в системе
    MCIERR_EXTRA_CHARACTERS
    Текстовая строка должна быть заключена в двойные кавычки. Если появилась эта ошибка, в строке есть символы, расположенные после закрывающей кавычки
    MCIERR_FILE_NOT_FOUND
    Файл не найден
    MCIERR_FILE_NOT_SAVED
    Файл не сохранен, вероятно, из-за отсутствия свободного места на диске
    MCIERR_FILE_READ
    Ошибка при чтении файла
    MCIERR_FILE_WRITE
    Ошибка при записи файла
    MCIERR_FLAGS_NOT_COMPATIBLE
    Указанные параметры несовместимы
    MCIERR_FILENAME_REQUIRED
    Требуется указать правильное имя файла
    MCIERR_GET_CD
    Не найден нужный файл или драйвер
    MCIERR_HARDWARE
    Ошибка аппаратного обеспечения
    MCIERR_ILLEGAL_FOR_AUTO_OPEN
    Данную команду нельзя выполнить для устройства, открываемого автоматически
    MCIERR_INTERNAL
    Ошибка при инициализации MCI. Необходимо перезапустить операционную систему Windows
    MCIERR_INVALID_DEVICE_ID

    Неправильный идентификатор устройства

    MCIERR_INVALID_DEVICE_NAME

    Указанный драйвер не открыт или не распознан системой MCI

    MCIERR_INVALID_FILE

    Указанный файл невозможно проиграть на выбранном устройстве. Вероятно, файл имеет неправильный или неподдерживаемый формат

    MCIERR_INVALID_SETUP

    Неправильная установка параметров MIDI. Необходимо восстановить оригинальный файл midimap.cfg, расположенный в системном каталоге Windows

    MCIERR_MISSING_INTEGER

    Данной команде необходим параметр в виде целого числа

    MCIERR_MISSING_PARAMETER

    Данной команде необходимо указать параметр

    MCIERR_MULTIPLE

    Возникли ошибки одновременно на нескольких устройствах

    MCIERR_MUST_USE_SHAREABLE

    Указанный драйвер уже используется. Для совместного использования следует указать параметр "shareable"

    MCIERR_NO_ELEMENT_ALLOWED

    Данное устройство не работает с файлами

    MCIERR_NO_INTEGER

    Для указанной команды необходимо использовать параметр в виде целого числа

    MCIERR_NO_WINDOW

    Нет окна для отображения

    MCIERR_NONAPPLICABLE_FUNCTION

    Указанные команды MCI невозможно выполнить в данном порядке

    MCIERR_NULL_PARAMETER_BLOCK

    Был использован нулевой блок параметров

    MCIERR_OUT_OF_MEMORY

    Для выполнения команды слишком мало памяти

    MCIERR_OUTOFRANGE

    Значение параметра лежит вне допустимых границ

    MCIERR_SET_CD

    Указанный драйвер или устройство недоступны, так как приложение не может сменить каталог

    MCIERR_SET_DRIVE

    Указанный драйвер или устройство недоступны, так как приложение не может сменить устройство

    MCIERR_UNNAMED_RESOURCE

    Попытка сохранения в файле без указания имени файла

    MCIERR_UNRECOGNIZED_COMMAND

    Команда не распознана

    MCIERR_UNSUPPORTED_FUNCTION

    Драйвер не поддерживает указанную команду

    MCIERR_SEQ_DIV_INCOMPATIBLE

    Несовместимые форматы указателя "song pointer" и SMPTE

    MCIERR_SEQ_NOMIDIPRESENT

    В системе нет устройств MIDI

    MCIERR_SEQ_PORT_INUSE

    Указанный порт MIDI уже используется

    MCIERR_SEQ_PORT_MAPNODEVICE

    Система отображения MIDI Mapper ссылается на устройство MIDI, которое не установлено в системе


    MCIERR_SEQ_PORT_MISCERROR

    Ошибка порта

    MCIERR_SEQ_PORT_NONEXISTENT

    Указанное устройство MIDI не установлено в системе

    MCIERR_SEQ_PORTUNSPICIFIED

    В системе не установлен текущий порт MIDI

    MCIERR_SEQ_TIMER

    Все таймеры системы мультимедиа задействованы другими приложениями

    MCIERR_WAVE_INPUTSINUSE

    Задействованы все звуковые устройства, которые могут записывать файл в данном формате

    MCIERR_WAVE_INPUTSUNSUITABLE

    В системе не установлено ни одного устройства, которое могло бы записывать файл в данном формате

    MCIERR_WAVE_INPUTUNSPECIFIED

    Необходимо указать любое совместимое устройство записи звука

    MCIERR_WAVE_OUTPUTSINUSE

    Задействованы все звуковые устройства, которые могут проигрывать файл в данном формате

    MCIERR_WAVE_OUTPUTSUNSUITABLE

    В системе не установлено ни одного устройства, которое могло бы проигрывать файл в данном формате

    MCIERR_WAVE_OUTPUTUNSPECIFIED

    Необходимо указать любое совместимое устройство воспроизведения звука

    MCIERR_WAVE_SETINPUTINUSE

    Текущее звуковое устройство уже задействовано

    MCIERR_WAVE_SETINPUTUNSUITABLE

    Устройство, использованное для записи, не может распознать формат данных

    MCIERR_WAVE_SETOUTPUTINUSE

    Текущее звуковое устройство уже задействовано

    MCIERR_WAVE_SETOUTPUTUNSUITABLE

    Устройство, использованное для воспроизведения, не может распознать формат данных

    Коды изготовителей


    Код Изготовитель
    1 Microsoft Corporation
    2 Creative Labs Inc.
    3 Media Vision Inc.
    4 FUJITSU
    20 Artisoft Inc.
    21 TURTLE_BEACH
    22 International Business Machines Corp.
    23 Vocaltec LTD.
    24 ROLAND
    25 Digispeech, Inc.
    26 NEC
    27 ATI
    28 Wang Laboratories, Inc
    29 Tandy Corporation
    30 Voyetra
    31 Antex Electronics
    32 ICL Personal Systems
    33 Intel Corporation
    34 Advanced Gravis
    35 Video Associates Labs, Inc.
    36 InterActive, Inc.
    37 Yamaha Corporation of America
    38 Everex Systems, Inc.
    39 Echo Speech Corporation
    40 Sierra Semiconductor Corp.
    41 Computer Aided Technologies
    42 APPS Software International
    43 DSP Group, Inc.
    44 MicroEngineering Labs
    45 Computer Friends, Inc.
    46 ESS Technology
    47 Audio, Inc.
    48 Motorola, Inc.
    49 Canopus Co., Ltd.
    50 Seiko Epson Corporation
    51 Truevision
    52 Aztech Labs, Inc.
    53 Videologic
    54 SCALACS Inc.
    55 Toshihiko Okuhara, Korg Inc.
    56 Audio Processing Technology


    Коды устройств

    Microsoft


    Код Устройство
    1 Устройство отображения MIDI Mapper
    2 Устройство отображения Wave Mapper
    3 Выходной порт Sound Blaster MIDI
    4 Входной порт Sound Blaster MIDI
    5 Внутренний синтезатор Sound Blaster
    6 Выходное устройство Sound Blaster для воспроизведения звука, записанного методом импульсно-кодовой модуляции PCM
    7 Входное устройство PCM Sound Blaster
    9 Синтезатор, совместимый с устройством Ad Lib
    10 Выходной порт MIDI, совместимый с MPU401
    11 Водной порт MIDI, совместимый с MPU401
    12 Адаптер джойстика
    13 Драйвер PC Speaker, предназначенный для вывода
    14 Устройство ввода PCM MS Audio Board
    15 Устройства вывода PCM MS Audio Board
    16 Синтезатор MS Audio Board Stereo FM
    18 Устройство ввода PCM MS OEM Audio Board
    19 Устройство вывода PCM MS OEM Audio Board
    20 Синтезатор MS OEM Audio Board Stereo FM
    21 Дополнительный порт MS Audio Board Auxiliary Port
    22 Дополнительный порт MS OEM Audio Auxiliary Port

    Creative Labs Inc.


    Код Устройство
    1 Устройство ввода PCM Sound Blaster 1.5
    2 Устройство ввода PCM Sound Blaster 2.0
    3 Устройство ввода PCM Sound Blaster Pro
    4 Устройство ввода PCM Sound Blaster Pro 16
    101 Устройство вывода PCM Sound Blaster 1.5
    102 Устройство вывода PCM Sound Blaster 2.0
    103 Устройство вывода PCM Sound Blaster Pro
    104 Устройство вывода PCM Sound Blaster Pro 16
    201 Выходной порт Sound Blaster MIDI
    202 Входной порт Sound Blaster MIDI
    301 Синтезатор Sound Blaster FM
    302 Синтезатор Sound Blaster Pro Stereo FM
    401 Дополнительный порт для управления устройством чтения компакт-дисков Sound Blaster Pro aux
    402 Дополнительный порт для управления устройством ввода с линии Sound Blaster Pro aux
    403 Дополнительный порт для ввода с микрофона Sound Blaster Pro aux

    Artisoft Inc.

    Код Устройство
    1 Устройство ввода PCM Artisoft Sound Board
    2 Устройство вывода PCM Artisoft Sound Board Waveform

    IBM

    Код Устройство
    1 Дополнительный порт IBM M-Motion
    2 Устройство вывода PCM IBM M-Motion
    3 Устройство ввода PCM IBM M-Motion

    Vocaltec LTD.

    Код Устройство
    1 Устройство вывода PCM Vocaltec
    2 Устройство ввода PCM Vocaltec

    Intel

    Код Устройство
    1 Устройство ввода PCM HID2
    101 Устройство вывода PCM HID2
    401 Дополнительный порт HID2


    Приложение DRVLIST

    Приложение DRVLIST (листинг 2.13) поможет вам исследовать конфигурацию драйверов устройств мультимедиа, установленных в системе. Это приложение формирует в текущем каталоге текстовый файл с именем drvlist.txt и записывает в него конфигурацию драйверов.
    Листинг 2.13 Файл drvlist\drvlist.cpp
    // ---------------------------------------- // Просмотр параметров драйверов // для системы мультимедиа // ----------------------------------------
    #define STRICT #include #include #include #include
    // =========================================== // Функция 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
    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

    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
    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
    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
    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
    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.
    Приложение MCICDPL
    Рис. 3.1. Главное окно приложения MCICDPL
    Исходный текст приложения представлен в листинге 3.1.
    Листинг 3.1. Файл mcicdpl/mcicdpl.cpp
    // ---------------------------------------- // Проигрыватель звуковых компакт-дисков // ----------------------------------------
    #define STRICT #include #include #include #include
    #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 #include
    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.
    Приложение MCITEST
    Рис. 2.1. Главное окно приложения MCITEST
    Окно, расположенное в верхней части экрана, предназначено для ввода командных строк. Это окно представляет собой многострочный редактор текста, поэтому вы можете вводить сразу несколько строк. Введенные команды можно выполнять по отдельности, нажимая клавишу или кнопку "Step", или все вместе (при помощи кнопки "Go!").
    В окне "MCI Output" отображается результат выполнения операции. Если произошла ошибка, ее текстовое описание появляется в окне "Error".
    Если в качестве одного из параметров команды была указана строка notyfy, в окне "Notification" отображается результат, переданный с сообщением MM_MCINOTIFY.
    С помощью меню "File" вы можете сохранять и загружать последовательности команд MCI, проверяя их в работе.

    Приложение MCIWAVER

    В качестве примера использования интерфейса сообщений MCI приведем исходные тексты несложного приложения MCIWAVER, с помощью которого можно записывать и воспроизводить wav-файлы (рис. 2.2).
    Приложение MCIWAVER
    Рис. 2.2. Главное окно приложения MCIWAVER
    Если выбрать из главного меню приложения строку "Record!", включится запись. Вы сможете записать звуковой фрагмент длительностью до 60 секунд (максимальное время записи определяется константой MAXRECORDTIME, вы можете изменить значение этой константы в исходном тексте приложения). Для прерывания процесса записи в любой момент времени можно выбрать из меню строки "Stop!" или "Pause!". Результат записи всегда сохраняется в файле с именем recorded.wav, который создается в текущем каталоге.
    С помощью строки "Open..." меню "File" можно выбрать wav-файл для воспроизведения. Путь к выбранному файлу отобразится в заголовке окна. Для прослушивания загруженного wav-файла воспользуйтесь строкой "Play!". Прослушивание можно прекратить (строка "Stop!") или временно приостановить (строка "Pause!"). Для продолжения прослушивания после временного останова выберите строку "Resume!".
    Полоса просмотра служит для отображения текущей позиции при записи и воспроизведении. Над полосой просмотра выводится текущий режим работы приложения.
    Основной файл приложения MCIWAWER приведен в листинге 2.7.
    Листинг 2.7. Файл mciwaver\mciwaver.cpp
    // ------------------------------------------------ // Приложение MCIWAVE // Проигрывание и запись wav-файлов // с помощью интерфейса сообщений MCI // ------------------------------------------------
    #define STRICT #include #include #include #include #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 #include #include #include #include #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 #include

    #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) и... все!
    Приложение MCIWND
    Рис. 5.10. Окно MCI
    Это окно, когда в него ничего не загружено, имеет стандартный заголовок и три органа управления - кнопку воспроизведения, кнопку доступа к меню и полосу просмотра.
    Запустите загрузочный модуль этого приложения и нажмите кнопку доступа к меню (рис.5.11).
    Приложение MCIWND
    Рис. 5.11. Кнопка доступа к меню
    На экране появится почти стандартная диалоговая панель "Open", с помощью которой вы сможете выбрать файл мультимедиа для просмотра или прослушивания (рис. 5.12).
    Приложение MCIWND
    Рис. 5.12. Диалоговая панель "Open"
    Обратите внимание, что в правом нижнем углу диалоговой панели имеются органы управления (кнопка и полоса просмотра), предназначенные для предварительного просмотра или прослушивания содержимого файла. Это очень удобно, так как при поиске файла не нужно много раз открывать и закрывать диалоговую панель "Open".
    После загрузки wav-файла меню приложения модифицируется (рис. 5.13).
    Приложение MCIWND
    Рис. 5.13. Меню для работы с wav-файлом
    В нем появляется строка "Close", с помощью которой можно закрыть файл, строка "Copy", позволяющая скопировать содержимое файла в Clipboard, а также строка "Command...". Последняя предназначена для передачи устройству произвольной управляющей строки MCI (соответствующая диалоговая панель показана на рис. 5.14).
    Приложение MCIWND
    Рис. 5.14. Диалоговая панель для передачи управляющей строки MCI
    При выборе avi-файлов можно посмотреть их содержимое в небольшом окне (рис. 5.15).
    Приложение MCIWND
    Рис. 5.15. Выбор avi-файла
    Для работы с avi-файлами используется расширенное меню (рис. 5.16).
    Приложение MCIWND
    Рис. 5.16. Меню для работы с avi-файлами
    Строка "View" предназначена для управления размером окна. С помощью строк "Volume" и "Speed" можно регулировать, соответственно, громкость звука и скорость воспроизведения видео. Строка "Configure..." предназначена для установки параметров проигрывателя. При ее выборе на экране появляется диалоговая панель "Video Playback Options" (рис. 5.17).

    Приложение MCIWND

    Рис. 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 #include

    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-файлы.
    Приложение MCIWNDC
    Рис. 5.18. Меню "File" приложения MCIWNDC
    Для проигрывания файлов мультимедиа их следует открыть при помощи строки "Open..." меню "File". При этом на экран будет выведена диалоговая панель "Open" с возможностью предварительного просмотра или прослушивания содержимого файла (рис. 5.19).
    Приложение MCIWNDC
    Рис. 5.19. Диалоговая панель "Open" приложения MCIWNDC
    Меню "Movie" (рис. 5.20) и "Styles" (рис. 5.22) предназначены для управления окном MCI.
    Приложение MCIWNDC
    Рис. 5.20. Меню "Movie" приложения MCIWNDC
    Строки "Play", "Play Reverse", "Record" и "Stop" предназначены, соответственно, для проигрывания, проигрывания в обратном направлении, записи и выполнения останова.
    С помощью строк "Home" и "End" выполняется позиционирование на начало и конец файла. Строки "Step Fwrd" и "Step Back" дают возможность выполнять пошаговое перемещение вперед и назад.
    Выбрав строку "Info...", вы увидите на экране диалоговую панель "Media Info" (рис. 5.21), в которой будет отображено имя устройства, путь к загруженному в окно MCI файлу и размер этого файла.
    Приложение MCIWNDC
    Рис. 5.21. Диалоговая панель "Media Info"
    С помощью строки "Play Bar" меню "Styles" (рис. 5.22) вы можете убрать или возвратить на место органы управления окном MCI.
    Приложение MCIWNDC
    Рис. 5.22. Меню "Styles" приложения MCIWNDC
    Диалоговая панель, используемая при сохранении записанных wav-файлов, содержит средства предварительного просмотра или прослушивания (рис. 5.23).
    Приложение MCIWNDC
    Рис. 5.23. Диалоговая панель "Save As..." приложения MCIWNDC

    Приложение MCIWNDC способно также проигрывать дорожки звуковых компакт-дисков. Внешний вид окна MCI, которое используется для этого, показан на рис. 5.24.

    Приложение MCIWNDC

    Рис. 5.24. Окно MCI для проигрывания дорожек звукового компакт-диска

    Несмотря на обилие возможностей, исходный текст приложения MCIWNDC занимает немного места (листинг 5.4).

    Листинг 5.4. Файл mciwndc/mciwndc.cpp

    // ------------------------------------------------ // Приложение MCIWNDC // Использование класса окна MCIWnd для // проигрывания и записи файлов мультимедиа // ------------------------------------------------

    #define STRICT #include #include #include #include #include #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.
    Приложение MIDIPL
    Рис. 4.2. Приложение MIDIPL
    По своей структуре оно напоминает приложение MCIWAWER, которое может проигрывать и записывать wav-файлы. Поэтому мы рассмотрим только отличия, специфические для работы с устройством sequencer.
    Основной файл исходных текстов приложения MIDIPL приведен в листинге 4.1.
    Листинг 4.1. Файл midipl/midipl.cpp
    // ------------------------------------------------ // Приложение MIDIPL // Проигрывание файлов MIDI // с помощью интерфейса сообщений MCI // ------------------------------------------------
    #define STRICT #include #include #include #include #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 #include #include #include #include #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 #include

    #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 #include
    #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

    Приложение WAVE

    В качестве примера использования интерфейса нижнего уровня для записи и воспроизведения wav-файлов мы представим вам исходные тексты приложения WAVE (рис. 2.5).
    Приложение WAVE
    Рис. 2.5. Главное окно приложения WAVE
    Это приложение по своему внешнему виду и функциям аналогично приложению MCIWAVER, которое было создано с использованием интерфейса MCI.
    Приложение WAVE заказывает два глобальных блока памяти, один из которых используется при записи, другой - при воспроизведении. Исходный текст основного модуля приложения представлен в листинге 2.16.
    Листинг 2.16. Файл wave\wave.cpp
    // ---------------------------------------- // Приложение WAVE // Проигрывание и запись wav-файлов на низком уровне // ----------------------------------------
    #define STRICT #include #include #include #include #pragma hdrstop
    #include "wave.hpp" #include "waveio.hpp"
    // Идентификатор таймера #define BEEP_TIMER 1
    // Идентификатор полосы просмотра #define ID_SCROLL 10
    // Длина полосы просмотра #define SCROLL_SIZE 400
    // Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); BOOL WAVELoad(LPWAVEIOCB lpwiocb);
    // Глобальные переменные HWAVEOUT hWaveOut; WAVEIOCB waveiocbOut; HWAVEIN hWaveIn; WAVEIOCB waveiocbIn; int nMode = MODE_STOP; MMTIME mmtimeIn, mmtimeOut; BOOL fNeedSave = FALSE; BOOL fFileLoaded = FALSE; int nPosition; HWND hScroll; char const szClassName[] = "WaveClass"; char const szWindowTitle[] = "Wave Player/Recorder"; HINSTANCE hInst;
    // ===================================== // Функция WinMain // ===================================== #pragma argsused
    int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения
    if(hPrevInstance) return FALSE;
    if(!InitApp(hInstance)) return FALSE;
    hInst = hInstance;

    hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // размеры и расположение окна CW_USEDEFAULT, 450, 120, 0, 0, hInstance, NULL);

    if(!hwnd) return FALSE;

    ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);

    while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

    // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // =====================================

    BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации

    memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APPICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName = (LPSTR)szClassName;

    aWndClass = RegisterClass(&wc); return (aWndClass != 0); }

    // ===================================== // Функция WndProc // =====================================

    LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; int rc;

    switch (msg) {

    // ------------------------------------------------------------ // WM_CREATE // Создание главного окна приложения // ------------------------------------------------------------ case WM_CREATE: { nMode = MODE_STOP; fNeedSave = FALSE; fFileLoaded = FALSE; hWaveIn = NULL; hWaveOut = NULL;

    // Создаем таймер SetTimer(hwnd, BEEP_TIMER, 100, NULL);

    // Создаем полосу просмотра hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 10, 40, SCROLL_SIZE, 15, hwnd, (HMENU) ID_SCROLL, hInst, NULL);

    // Устанавливаем текущую позицию nPosition = 0;

    // Устанавливаем минимальное и максимальное // значения для полосы просмотра SetScrollRange(hScroll, SB_CTL, 1, SCROLL_SIZE, TRUE);


    // Устанавливаем ползунок SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); return 0; }

    // ------------------------------------------------------------ // WM_PAINT // Рисование в окне // ------------------------------------------------------------ case WM_PAINT: { // Получаем контекст отображения для // рисования во внутренней области окна hdc = BeginPaint(hwnd, &ps);

    // Отображаем текущий режим работы if(nMode == MODE_STOP) TextOut(hdc, 10, 10, "Остановлено", 11); else if(nMode == MODE_RECORDING) TextOut(hdc, 10, 10, "Идет запись...", 14); else if(nMode == MODE_PLAYING) TextOut(hdc, 10, 10, "Идет проигрывание...", 20); else if(nMode == MODE_RECORDINGPAUSED) TextOut(hdc, 10, 10, "Запись остановлена", 18); else if(nMode == MODE_PLAYINGPAUSED) TextOut(hdc, 10, 10, "Проигрывание остановлено", 24); else TextOut(hdc, 10, 10, "Неправильный режим!", 19);

    // Освобождаем контекст отображения EndPaint(hwnd, &ps); return 0; }

    // ------------------------------------------------------------ // WM_COMMAND // Обработка сообщений от меню // ------------------------------------------------------------ case WM_COMMAND: { switch (wParam) { // ------------------------------------------------- // Строка "About" меню "Help" // ------------------------------------------------- case CM_HELPABOUT: { MessageBox(hwnd, "Wave, v.1.0\nWave Player/Recorder\n" "(C) Frolov A.V., 1994", "About Wave", MB_OK | MB_ICONINFORMATION); return 0; }

    // ------------------------------------------------- // Строка "Open" меню "File" // ------------------------------------------------- case CM_FILEOPEN: { // Если файл уже был загружен, возвращаем WAVE в // исходное состояние if(fFileLoaded) { if(hWaveOut) { // Останавливаем устройство вывода rc=waveOutReset(hWaveOut); if(rc) wioOutError(rc);

    // Закрываем устройство вывода rc=waveOutClose(hWaveOut); if(rc) wioOutError(rc); }


    // Освобождаем буфера, предназначенные для вывода GlobalFreePtr(waveiocbOut.lpWaveHdr); GlobalFreePtr(waveiocbOut.lpData); GlobalFreePtr(waveiocbOut.lpFmt); }

    // Загружаем новый файл if(!WAVELoad(&waveiocbOut)) return 0;

    // Устанавливаем флаг загрузки файла fFileLoaded = TRUE; return 0; }

    // ------------------------------------------------- // Строка "Play!" // Проигрывание загруженного wav-файла // ------------------------------------------------- case CM_CTLPLAY: { if(nMode == MODE_STOP) { // Если файл загружен и не проигрывается, // запускаем проигрывание файла if((fFileLoaded == TRUE) && (nMode != MODE_PLAYING)) { // Новый режим nMode = MODE_PLAYING;

    // Перерисовываем окно для отображения строки, // соответствующей новому режиму InvalidateRect(hwnd, NULL, TRUE);

    // Проигрываем файл rc=wioPlay(&waveiocbOut, hwnd); if(rc) wioOutError(rc); } } return 0; }

    // ------------------------------------------------- // Строка "Record!" // Запись wav-файла // ------------------------------------------------- case CM_CTLRECORD: { // Запись возможна только из состояния останова if(nMode == MODE_STOP) { nMode = MODE_RECORDING; InvalidateRect(hwnd, NULL, TRUE);

    // Требуется сохранить записанный файл fNeedSave = TRUE;

    // Запись файла wioRecord(&waveiocbIn, hwnd); } return 0; }

    // ------------------------------------------------- // Строка "Stop!" // Останов проигрывания или записи wav-файла // ------------------------------------------------- case CM_CTLSTOP: { if(nMode == MODE_RECORDING nMode == MODE_RECORDINGPAUSED) { if(hWaveIn) { // Останавливаем запись rc=waveInReset(hWaveIn); if(rc) wioInError(rc);

    // Закрываем устройство записи rc=waveInClose(hWaveIn); if(rc) wioInError(rc); hWaveIn = 0; } }

    else if(nMode == MODE_PLAYING nMode == MODE_PLAYINGPAUSED) { if(hWaveOut) { // Останавливаем проигрывание rc=waveOutReset(hWaveOut); if(rc) wioOutError(rc);

    // Закрываем устройство вывода rc=waveOutClose(hWaveOut); if(rc) wioOutError(rc); hWaveOut = 0; } }


    // Устанавливаем движок в начало полосы просмотра nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

    // Новый режим nMode = MODE_STOP; InvalidateRect(hwnd, NULL, TRUE); return 0; }

    // ------------------------------------------------- // Строка "Pause!" // Временный останов проигрывания или // полный останов записи wav-файла // ------------------------------------------------- case CM_CTLPAUSE: { if(nMode == MODE_RECORDING) { // Останов записи rc=waveInStop(hWaveIn); if(rc) wioInError(rc); nMode = MODE_STOP; }

    else if(nMode == MODE_PLAYING) { // Временный останов проигрывания rc=waveOutPause(hWaveOut); if(rc) wioOutError(rc); nMode = MODE_PLAYINGPAUSED; }

    InvalidateRect(hwnd, NULL, TRUE); return 0; }

    // ------------------------------------------------- // Строка "Resume!" // Продолжение проигрывания после останова // ------------------------------------------------- case CM_CTLRESUME: { if(nMode == MODE_PLAYINGPAUSED) { rc=waveOutRestart(hWaveOut); if(rc) wioOutError(rc); nMode = MODE_PLAYING; InvalidateRect(hwnd, NULL, TRUE); } return 0; }

    // ------------------------------------------------- // Строка "Exit" меню "File" // Завершение работы приложения // ------------------------------------------------- case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; }

    default: return 0; } }

    // ------------------------------------------------------------ // MM_WOM_DONE // Завершение проигрывания блока // ------------------------------------------------------------ case MM_WOM_DONE: { nMode = MODE_STOP; nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); InvalidateRect(hwnd, NULL, TRUE);

    // Удаляем блок из очереди проигрывания waveOutUnprepareHeader((HWAVEOUT)wParam, (LPWAVEHDR)lParam, sizeof(WAVEHDR));

    // Останавливаем и закрываем устройство вывода waveOutReset((HWAVEOUT)wParam); waveOutClose((HWAVEOUT)wParam); hWaveOut = 0; return 0; }

    // ------------------------------------------------------------ // MM_WIM_DATA // Завершение записи блока // ------------------------------------------------------------ case MM_WIM_DATA: { nMode = MODE_STOP; nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); InvalidateRect(hwnd, NULL, TRUE);


    // Удаляем блок из очереди записи waveInUnprepareHeader((HWAVEIN)wParam, (LPWAVEHDR)lParam, sizeof(WAVEHDR));

    // Сохраняем записанный блок в файле wioFileSave("RECORDED.WAV"); fNeedSave = FALSE;

    // Освобождаем буфера, связанные с блоком GlobalFreePtr(waveiocbIn.lpWaveHdr); GlobalFreePtr(waveiocbIn.lpData);

    // Останавливаем и закрываем устройство ввода waveInReset((HWAVEIN)wParam); waveInClose((HWAVEIN)wParam); hWaveIn = 0; return 0; }

    // ------------------------------------------------------------ // WM_TIMER // Сообщение от таймера // ------------------------------------------------------------ case WM_TIMER: { if(nMode == MODE_RECORDING) { // Определяем текущую позицию внутри проигрываемого блока mmtimeIn.wType = TIME_SAMPLES; waveInGetPosition(hWaveIn, (LPMMTIME)&mmtimeIn, sizeof(MMTIME));

    // Вычисляем новое положение движка полосы просмотра nPosition = ((DWORD)SCROLL_SIZE * mmtimeIn.u.sample) / MAXSAMPLES;

    // Ограничиваем пределы изменения текущей // позиции значениями от 1 до SCROLL_SIZE if(nPosition > SCROLL_SIZE) nPosition = SCROLL_SIZE; if(nPosition < 1) nPosition = 1;

    // Устанавливаем ползунок полосы просмотра // в соответствии с новым значением текущей позиции SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); }

    else if(nMode == MODE_PLAYING) { // Определяем текущую позицию внутри записываемого блока mmtimeOut.wType = TIME_SAMPLES; waveOutGetPosition(hWaveOut, (LPMMTIME)&mmtimeOut, sizeof(MMTIME));

    // Вычисляем новое положение движка полосы просмотра nPosition = ((DWORD)SCROLL_SIZE * mmtimeOut.u.sample) / (waveiocbOut.dwDataSize / waveiocbOut.wBytesPerSample);

    // Ограничиваем пределы изменения текущей // позиции значениями от 1 до SCROLL_SIZE if(nPosition > SCROLL_SIZE) nPosition = SCROLL_SIZE; if(nPosition < 1) nPosition = 1;

    // Устанавливаем ползунок полосы просмотра // в соответствии с новым значением текущей позиции SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); } return 0; }

    // ------------------------------------------------------------ // WM_DESTROY // Уничтожение главного окна приложения // ------------------------------------------------------------ case WM_DESTROY: { // Удаляем таймер и полосу просмотра KillTimer(hwnd, BEEP_TIMER); DestroyWindow(hScroll);


    // Если находимся в режиме записи, останавливаем // запись и закрываем устройство ввода if(nMode == MODE_RECORDING nMode == MODE_RECORDINGPAUSED) { if(hWaveIn) { rc=waveInReset(hWaveIn); if(rc) wioInError(rc); rc=waveInClose(hWaveIn); if(rc) wioInError(rc); } }

    // Если запись началась, но еще не закончилась, после // остановки записи удаляем буфера if(fNeedSave) { GlobalFreePtr(waveiocbIn.lpWaveHdr); GlobalFreePtr(waveiocbIn.lpData); }

    else if(fFileLoaded) { // Если находимся в режиме проигрывания, останавливаем // запись и закрываем устройство вывода if(nMode == MODE_PLAYING nMode == MODE_PLAYINGPAUSED) { if(hWaveOut) { rc=waveOutReset(hWaveOut); if(rc) wioOutError(rc);

    rc=waveOutUnprepareHeader(hWaveOut, waveiocbOut.lpWaveHdr, sizeof(WAVEHDR)); if(rc) wioOutError(rc);

    rc=waveOutClose(hWaveOut); if(rc) wioOutError(rc); } }

    nMode = MODE_STOP;

    // Освобождаем буфера GlobalFreePtr(waveiocbOut.lpWaveHdr); GlobalFreePtr(waveiocbOut.lpData); GlobalFreePtr(waveiocbOut.lpFmt); } PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); }

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

    Обработчик сообщения WM_PAINT отображает в верхней части главного окна текстовую строку, соответствующую текущему режиму работы приложения (переменная nMode).

    При выборе из меню "File" строки "Open" приложение проверяет, не был ли раньше загружен файл. Если файл был загружен и устройство вывода открыто, это устройство останавливается функцией waveOutReset и закрывается функцией waveOutClose. После этого освобождаются буфера, которые использовались для вывода.

    Затем вызывается функция WAVELoad, определенная в нашем приложении в файле waveio.cpp (листинг 2.18).


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

    Для проигрывания файла нужно выбрать из главного меню приложения строку "Play!". Соответствующий обработчик проверит текущий режим работы приложения. Если wav-файл загружен, и приложение уже не находится в режиме воспроизведения, вызывается функция wioPlay, выполняющая проигрывание файла. Эта функция также определена в нашем приложении в файле waveio.cpp.

    С помощью строки "Record!" главного меню приложения можно включить режим записи (если приложение находится в состоянии останова). При этом устанавливается флаг fNeedSave, который используется как признак необходимости сохранения записанных звуковых данных в wav-файле. Запись выполняется функцией wioRecord, определенной в файле waveio.cpp.

    В любой момент времени можно остановить запись или воспроизведение, если из главного меню приложения выбрать строку "Stop!". При этом, если приложение находилось в режиме записи, выполняется сброс и закрытие устройства записи, а если в режиме воспроизведения - сброс и закрытие устройства воспроизведения. В любом случае движок полосы просмотра устанавливается в начальное, самое левое, положение, так как после сброса устройства начальная позиция равна нулю.

    Строка "Pause!" главного меню приложения предназначена для останова записи и временного останова воспроизведения. В режиме записи устройство останавливается функцией waveInStop. В режиме воспроизведения вызывается функция waveOutPause, выполняющая временный останов.

    Для продолжения воспроизведения после временного останова из главного меню приложения следует выбрать строку "Resume!". В этом случае будет вызвана функция waveOutRestart, которая возобновит работу устройства вывода.

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


    В режиме записи при достижении конца блока памяти или при останове записи главное окно приложения получит сообщение MM_WIM_DATA. Обработчик этого сообщения также установит полосу просмотра в исходное состояние и удалит блок из очереди записи, вызвав функцию waveInUnprepareHeader. Затем содержимое блока будет сохранено в wav-файле с именем recorded.wav, который будет создан или перезаписан в текущем каталоге. Для записи файла вызывается функция wioFileSave, определенная в файле waveio.cpp.

    Далее освобождаются буфера, использовавшиеся при записи, устройство записи останавливается и закрывается.

    Сообщения, поступающие от таймера, используются для обновления положения движка полосы просмотра в соответствии с текущей позицией устройства записи (в режиме записи) или устройства воспроизведения (в режиме воспроизведения).

    В режиме записи с помощью функции waveInGetPosition определяется текущая позиция в формате TIME_SAMPLES:

    mmtimeIn.wType = TIME_SAMPLES; waveInGetPosition(hWaveIn, (LPMMTIME)&mmtimeIn, sizeof(MMTIME));

    Новое положение движка полосы просмотра в режиме записи определяется на основании текущей позиции и максимального размера буфера записи MAXSAMPLES, заданного как константа:

    nPosition = ((DWORD)SCROLL_SIZE * mmtimeIn.u.sample) / MAXSAMPLES;

    В режиме воспроизведения для вычисления положения движка используется размер загруженного wav-файла:

    nPosition = ((DWORD)SCROLL_SIZE * mmtimeOut.u.sample) / (waveiocbOut.dwDataSize / waveiocbOut.wBytesPerSample);

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

    Файл wave.hpp содержит определения символических имен констант (листинг 2.17).

    Листинг 2.15. Файл wave\wave.hpp

    #define CM_HELPABOUT 301 #define CM_FILEEXIT 302 #define CM_FILEOPEN 303 #define CM_FILESAVEAS 304 #define CM_FILENEW 305


    #define CM_CTLPLAY 401 #define CM_CTLRECORD 402 #define CM_CTLRESUME 403 #define CM_CTLPAUSE 404 #define CM_CTLSTOP 405

    Исходные тексты всех функций, используемых для работы с wav-файлами на низком уровне, определены в файле waveio.cpp (листинг 2.18).

    Листинг 2.18. Файл wave\waveio.cpp

    #define STRICT #include #include #include #include #include #pragma hdrstop #include "waveio.hpp"

    BOOL WAVEPlay(HWND);

    extern WAVEIOCB waveiocbOut; extern WAVEIOCB waveiocbIn; extern HWAVEOUT hWaveOut; extern HWAVEIN hWaveIn; extern int nMode; extern int nPosition;

    //----------------------------------------------------- // WAVELoad // Загрузка wav-файла для проигрывания //----------------------------------------------------- BOOL WAVELoad(LPWAVEIOCB lpwiocb) { BYTE szFileName[256]; OPENFILENAME ofn; int rc; BYTE szBuf[256];

    // Проверяем наличие драйвера, способного выводить // звуковые файлы rc=waveOutGetNumDevs(); if(!rc) { MessageBox(NULL, (LPSTR)"Нет устройств для вывода звуковых файлов", "Wave Error", MB_OK | MB_ICONHAND); return FALSE; }

    // Выбираем wav-файл if(!wioSelectFile(szFileName)) return FALSE;

    // Открываем и загружаем в память выбранный файл rc = wioFileOpen(lpwiocb, (LPSTR)szFileName);

    if(rc == WIOERR_NOERROR) return TRUE; else if(rc == WIOERR_FILEERROR) lstrcpy(szBuf, "Ошибка при открытии файла"); else if(rc == WIOERR_BADFORMAT) lstrcpy(szBuf, "Неправильный или неподдерживаемый формат файла"); else if(rc == WIOERR_NOMEM) lstrcpy(szBuf, "Мало памяти"); else if(rc == WIOERR_READERROR) lstrcpy(szBuf, "Ошибка при чтении"); else lstrcpy(szBuf, "Неизвестная ошибка");

    MessageBox(NULL, (LPSTR)szBuf, "Wave Error", MB_OK | MB_ICONHAND); return FALSE; }

    //----------------------------------------------------- // wioSelectFile // Выбор wav-файла //----------------------------------------------------- BOOL wioSelectFile(LPSTR lpszFileName) { OPENFILENAME ofn;


    char szFile[256]; char szFileTitle[256]; char szFilter[256] = "Wave Files\0*.wav\0Any Files\0*.*\0"; szFile[0] = '\0'; memset(&ofn, 0, sizeof(OPENFILENAME));

    // Инициализируем нужные нам поля ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = NULL; ofn.lpstrFilter = szFilter; ofn.nFilterIndex = 1; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = sizeof(szFileTitle); ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;

    // Выбираем входной файл if (GetOpenFileName(&ofn)) { // Копируем путь к выбранному файлу lstrcpy(lpszFileName, (LPSTR)szFile); return TRUE; } else return FALSE; }

    //--------------------------------------------------------- // wioFileOpen // Открытие и загрузка wav-файла //--------------------------------------------------------- int wioFileOpen(LPWAVEIOCB lpwiocb, LPSTR lpszFileName) { HMMIO hmmio; MMCKINFO ckRIFF, ckFMT; DWORD dwFmtSize;

    // Открываем wav-файл hmmio = mmioOpen((LPSTR)lpszFileName, NULL, MMIO_READ | MMIO_ALLOCBUF); if(!hmmio) return WIOERR_FILEERROR;

    // Ищем фрагмент "WAVE" memset(&ckRIFF, 0, sizeof(MMCKINFO)); ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');

    if(mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    // Ищем фрагмент "fmt " memset(&ckFMT, 0, sizeof(MMCKINFO)); ckFMT.ckid = mmioFOURCC('f', 'm', 't', ' ');

    if(mmioDescend(hmmio, &ckFMT, &ckRIFF, MMIO_FINDCHUNK)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    dwFmtSize = sizeof(PCMWAVEFORMAT); //ckFMT.cksize;

    // Получаем память для загрузки формата звукового файла lpwiocb->lpFmt = (PCMWAVEFORMAT FAR *)GlobalAllocPtr(GPTR, dwFmtSize);

    if(!lpwiocb->lpFmt) { mmioClose(hmmio,0); return WIOERR_NOMEM; }

    // Загружаем формат звукового файла if(mmioRead(hmmio, (HPSTR)lpwiocb->lpFmt, dwFmtSize) != (LONG)dwFmtSize) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_READERROR; }


    // Проверяем формат звукового файла if(lpwiocb->lpFmt->wf.wFormatTag != WAVE_FORMAT_PCM) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    // Проверяем способность драйвера работать с указанным форматом if(waveOutOpen(NULL, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, NULL, 0L, WAVE_FORMAT_QUERY | WAVE_ALLOWSYNC)) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    // Вычисляем количество байт, необходимых для // хранения одной выборки звукового сигнала lpwiocb->wBitsPerSample = lpwiocb->lpFmt->wBitsPerSample; lpwiocb->wBytesPerSample = (waveiocbOut.wBitsPerSample/8) * waveiocbOut.lpFmt->wf.nChannels;

    // Ищем фрагмент "data" mmioAscend(hmmio, &ckFMT, 0); ckFMT.ckid = mmioFOURCC('d', 'a', 't', 'a');

    if(mmioDescend(hmmio, &ckFMT, &ckRIFF, MMIO_FINDCHUNK)) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    // Определяем размер фрагмента сегмента звуковых // данных и его смещение в wav-файле lpwiocb->dwDataSize = ckFMT.cksize; lpwiocb->dwDataOffset = ckFMT.dwDataOffset;

    // Проверяем, что файл не пуст if(lpwiocb->dwDataSize == 0L) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    // Получаем память для заголовка блока lpwiocb->lpWaveHdr = (LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR)); if(!lpwiocb->lpWaveHdr) return WIOERR_NOMEM;

    // Получаем память для звуковых данных lpwiocb->lpData = (HPSTR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, lpwiocb->dwDataSize); if(!lpwiocb->lpData) return WIOERR_NOMEM;

    // Позиционирование на начало звуковых данных mmioSeek(hmmio, SEEK_SET, lpwiocb->dwDataOffset);

    // Читаем звуковые данные mmioRead(hmmio, lpwiocb->lpData, lpwiocb->dwDataSize);

    // Закрываем wav-файл mmioClose(hmmio,0);

    return WIOERR_NOERROR; }

    //--------------------------------------------------------- // wioPlay // Проигрывание загруженного блока звуковых данных //--------------------------------------------------------- int wioPlay(LPWAVEIOCB lpwiocb, HWND hwnd) { WORD rc;


    // Открываем устройство вывода rc = waveOutOpen(&hWaveOut, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC); if(rc) return rc;

    // Заполняем заголовок блока данных lpwiocb->lpWaveHdr->lpData = (LPSTR)lpwiocb->lpData; lpwiocb->lpWaveHdr->dwBufferLength = lpwiocb->dwDataSize; lpwiocb->lpWaveHdr->dwBytesRecorded = 0; lpwiocb->lpWaveHdr->dwFlags = 0; lpwiocb->lpWaveHdr->dwLoops = 0; lpwiocb->lpWaveHdr->dwUser = 0; lpwiocb->lpWaveHdr->lpNext = 0; lpwiocb->lpWaveHdr->reserved = 0;

    // Подготавливаем заголовок для вывода rc = waveOutPrepareHeader(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); if(rc) { GlobalFreePtr(lpwiocb->lpWaveHdr); return rc; }

    // Запускаем проигрывание блока rc = waveOutWrite(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); if(rc) { waveOutUnprepareHeader(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); GlobalFreePtr(lpwiocb->lpWaveHdr); return rc; }

    return 0; }

    //--------------------------------------------------------- // wioRecord // Запись звуковых данных //--------------------------------------------------------- int wioRecord(LPWAVEIOCB lpwiocb, HWND hwnd) { int rc;

    // Проверяем наличие драйвера, способного // выполнять запись звука rc = waveInGetNumDevs(); if(!rc) { MessageBox(NULL, (LPSTR)"Нет устройств для записи звуковых файлов", "Wave Error", MB_OK | MB_ICONHAND); return WIOERR_NODEVICE; }

    // Максимальный размер блока в байтах lpwiocb->dwDataSize = MAXSAMPLES;

    // Получаем память для заголовка блока lpwiocb->lpWaveHdr = (LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR)); if(!lpwiocb->lpWaveHdr) return WIOERR_NOMEM;

    // Получаем память для блока звуковых данных lpwiocb->lpData = (HPSTR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, lpwiocb->dwDataSize); if(!lpwiocb->lpData) return WIOERR_NOMEM;

    // Получаем память для блока формата lpwiocb->lpFmt = (PCMWAVEFORMAT FAR *)GlobalAllocPtr(GPTR, sizeof(WAVEFORMAT));


    if(!lpwiocb->lpFmt) { return WIOERR_NOMEM; }

    // Заполняем блок формата. Наше приложение способно // записывать монофонические файлы в формате WAVE_FORMAT_PCM // с частотой дискретизации 22,05 Кгц lpwiocb->lpFmt->wf.wFormatTag = WAVE_FORMAT_PCM; lpwiocb->lpFmt->wf.nChannels = 1; lpwiocb->lpFmt->wf.nSamplesPerSec = 22050; lpwiocb->lpFmt->wf.nAvgBytesPerSec = 22050; lpwiocb->lpFmt->wf.nBlockAlign = 1;

    // Открываем устройство записи rc=waveInOpen(&hWaveIn, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC); if(rc) { wioInError(rc); return WIOERR_BADFORMAT; }

    // Заполняем заголовок блока lpwiocb->lpWaveHdr->lpData = (LPSTR)lpwiocb->lpData; lpwiocb->lpWaveHdr->dwBufferLength = lpwiocb->dwDataSize; lpwiocb->lpWaveHdr->dwFlags = 0L; lpwiocb->lpWaveHdr->dwLoops = 0L; lpwiocb->lpWaveHdr->dwUser = 0L;

    // Подготавливаем блок для записи rc = waveInPrepareHeader(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); if(rc) { wioInError(rc); GlobalFreePtr(lpwiocb->lpWaveHdr); return WIOERR_BADFORMAT; }

    // Передаем блок устройству записи rc = waveInAddBuffer(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); if(rc) { wioInError(rc); waveInUnprepareHeader(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR)); GlobalFreePtr(lpwiocb->lpWaveHdr); GlobalFreePtr(lpwiocb->lpFmt); GlobalFreePtr(lpwiocb->lpData); return WIOERR_ERROR; }

    // Запускаем запись rc = waveInStart(hWaveIn); if(rc) wioInError(rc);

    return TRUE; }

    //--------------------------------------------------------- // wioFileSave // Сохранение записанного звука в wav-файле //--------------------------------------------------------- BOOL wioFileSave(LPSTR szFileName) { DWORD dwDataSize; char szdata[] = "data"; HMMIO hFile; MMCKINFO ck; WORD wBitsPerSample = 8; char szfmt[] = "fmt "; DWORD dwFmtSize = sizeof(PCMWAVEFORMAT);

    // Создаем новый файл или перезаписываем существующий hFile = mmioOpen(szFileName, NULL, MMIO_CREATE | MMIO_READWRITE); if(hFile != NULL) { // Создаем заголовок wav-файла ck.ckid = MMIO_CREATERIFF; ck.cksize = waveiocbIn.lpWaveHdr->dwBytesRecorded + sizeof(PCMWAVEFORMAT) + 20; ck.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmioCreateChunk(hFile, (LPMMCKINFO)&ck, MMIO_CREATERIFF);


    // Записываем фрагмент "fmt " mmioWrite(hFile, (HPSTR)szfmt, 4); mmioWrite(hFile, (HPSTR)&dwFmtSize, sizeof(DWORD));

    mmioWrite(hFile, (HPSTR)waveiocbIn.lpFmt, sizeof(WAVEFORMAT)); mmioWrite(hFile, (HPSTR)&wBitsPerSample, sizeof(WORD));

    mmioWrite(hFile, (HPSTR)szdata, 4); dwDataSize = waveiocbIn.lpWaveHdr->dwBytesRecorded; mmioWrite(hFile, (HPSTR)&dwDataSize, sizeof(DWORD));

    // Записываем данные mmioWrite(hFile, (HPSTR)waveiocbIn.lpData, waveiocbIn.lpWaveHdr->dwBytesRecorded);

    // Закрываем файл mmioClose(hFile, 0); return TRUE; } return FALSE; }

    //--------------------------------------------------------- // wioOutError // Вывод сообщения об ошибке в процессе проигрывания //--------------------------------------------------------- void wioOutError(int rc) { BYTE szBuf[MAXERRORLENGTH];

    if(waveOutGetErrorText(rc, (LPSTR)szBuf, MAXERRORLENGTH) == MMSYSERR_BADERRNUM) { lstrcpy(szBuf, "Unknown Error"); } MessageBox(NULL, (LPSTR)szBuf, "Wave Error", MB_OK | MB_ICONHAND); }

    //--------------------------------------------------------- // wioInError // Вывод сообщения об ошибке в процессе записи //--------------------------------------------------------- void wioInError(int rc) { BYTE szBuf[MAXERRORLENGTH];

    if(waveInGetErrorText(rc, (LPSTR)szBuf, MAXERRORLENGTH) == MMSYSERR_BADERRNUM) { lstrcpy(szBuf, "Unknown Error"); } MessageBox(NULL, (LPSTR)szBuf, "Wave Error", MB_OK | MB_ICONHAND); }

    Функция WAVLoad предназначена для выбора и загрузки проигрываемого wav-файла.

    В самом начале своей работы она определяет наличие драйвера, способного воспроизводить звуковую информацию, для чего вызывает функцию waveOutGetNumDevs. Если нужный драйвер есть, вызывается функция wioSelectFile, предоставляющая пользователю возможность выбрать файл при помощи стандартной диалоговой панели "Open". Выбранный файл открывается и загружается в память функцией wioFileOpen.

    Функция wioSelectFile не имеет никаких особенностей.


    Для выбора файла в ней используется функция GetOpenFileName из библиотеки commdlg.dll. Путь к выбранному файлу копируется в буфер, адрес которого передается функции wioSelectFile в качестве единственного параметра.

    Функция wioFileOpen выполняет всю работу по загрузке и анализу wav-файла.

    Для открытия файла используется функция mmioOpen. При этом файл открывается на чтение с использованием буферизации, для чего в последнем параметре функции указаны флаги MMIO_READ и MMIO_ALLOCBUF.

    Далее в wav-файле ищутся фрагменты "WAVE" и "fmt ", для чего используется функция mmioDescend:

    memset(&ckRIFF, 0, sizeof(MMCKINFO)); ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if(mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    memset(&ckFMT, 0, sizeof(MMCKINFO)); ckFMT.ckid = mmioFOURCC('f', 'm', 't', ' '); if(mmioDescend(hmmio, &ckFMT, &ckRIFF, MMIO_FINDCHUNK)) { mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    После этого приложение заказывает память для структуры PCMWAVEFORMAT, которая находится во фрагменте "fmt " и содержит сведения о формате wav-файла. Загрузка данных в структуру выполняется функцией mmioRead:

    if(mmioRead(hmmio, (HPSTR)lpwiocb->lpFmt, dwFmtSize) != (LONG)dwFmtSize) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_READERROR; }

    Далее проверяется формат звукового файла: поле wFormatTag структуры WAVEFORMAT должно содержать значение WAVE_FORMAT_PCM.

    Для того чтобы определить, может ли установленный в системе драйвер звукового адаптера работать с форматом загруженного файла, вызывается функция waveOutOpen с флагом WAVE_FORMAT_QUERY:

    if(waveOutOpen(NULL, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, NULL, 0L, WAVE_FORMAT_QUERY | WAVE_ALLOWSYNC)) { GlobalFreePtr(lpwiocb->lpFmt); mmioClose(hmmio,0); return WIOERR_BADFORMAT; }

    Так как пользователь может загрузить wav-файл любого формата, как монофонический, так и стереофонический, с использованием 8 или 16 битов для представления одной выборки сигнала, приложение должно динамически определять количество байт памяти, необходимых для хранения одной выборки сигнала.


    После анализа формата наше приложение ищет фрагмент "data", в котором находятся звуковые данные. Для поиска используется функция mmioDescend. После определения размера фрагмента данных приложение заказывает память для заголовка блока и самого блока, пользуясь макрокомандой GlobalAllocPtr.

    Перед чтением звуковых данных в заказанный буфер выполняется позиционирование на начало звуковых данных, для чего используется функция mmioSeek. Чтение данных выполняется функцией mmioRead. Сразу после чтения файл закрывается функцией mmioClose, так как он нам больше не нужен.

    Функция wioPlay проигрывает загруженный блок. Адрес блока и его заголовка передаются функции через первый параметр. Через второй параметр функция wioPlay получает идентификатор окна, в которое после завершения проигрывания поступит сообщение MM_WOM_DONE.

    Прежде всего функция wioPlay открывает устройство вывода, вызывая для этого функцию waveOutOpen:

    rc = waveOutOpen(&hWaveOut, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC);

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

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

    Затем заголовок блока подготавливается для вывода при помощи функции waveOutPrepareHeader:

    rc = waveOutPrepareHeader(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR));

    Для запуска процесса проигрывания вызывается функция waveOutWrite:

    rc = waveOutWrite(hWaveOut, lpwiocb->lpWaveHdr, sizeof(WAVEHDR));

    Функция wioRecord запускает процесс записи.

    В начале своей работы она проверяет, установлен ли в системе драйвер, способный выполнять запись звука, вызывая для этого функцию waveInGetNumDevs. Далее заказывается память для заголовка блока данных и самого блока данных. Так как приложение WAVE может записывать только монофонические wav-файлы с 8-битовым представлением звуковых данных, размер буфера для записи численно равен константе MAXSAMPLES (максимальный размер буфера в выборках сигнала).


    Затем функция wioRecord заказывает память для блока формата и заполняет этот блок. Для простоты мы использовали только один формат, а именно монофонический 8-битовый формат с частотой дискретизации 22,05 Кгц.

    После заполнения блока формата функция открывает устройство записи, заполняет заголовок блока. Заполненный заголовок подготавливается для записи, для чего вызывается функция waveInPrepareHeader.

    Далее блок передается устройству записи функцией waveInAddBuffer:

    rc = waveInAddBuffer(hWaveIn, lpwiocb->lpWaveHdr, sizeof(WAVEHDR));

    Для запуска записи вызывается функция waveInStart:

    rc = waveInStart(hWaveIn);

    Функция wioFileSave сохраняет содержимое буфера записи в wav-файле.

    Для создания нового или перезаписи существующего файла он открывается функцией mmioOpen с флагами MMIO_CREATE и MMIO_READWRITE.

    Далее создается заголовок файла при помощи функции mmioCreateChunk:

    ck.cksize = waveiocbIn.lpWaveHdr->dwBytesRecorded + sizeof(PCMWAVEFORMAT) + 20; ck.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmioCreateChunk(hFile, (LPMMCKINFO)&ck, MMIO_CREATERIFF);

    При этом в заголовке указывается длина с учетом объема записанных звуковых данных.

    После этого с помощью функции mmioWrite в файл записывается фрагмент "fmt " и содержимое буфера звуковых данных. Файл закрывается функцией mmioClose.

    Для обработки ошибок в процессе воспроизведения используется функция wioOutError, которая с помощью функции waveOutGetErrorText преобразует код ошибки, передаваемый через параметр, в текстовое сообщение и выводит его на экран. Аналогичные действия выполняются при записи функцией wioInError, преобразующей код ошибки в текстовую строку при помощи функции waveInGetErrorText.

    Файл waveio.hpp (листинг 2.19) содержит определение структуры WAVEIOCB и указателей на нее, а также определения констант и прототипы функций. Структура WAVEIOCB используется функциями записи и проигрывания блоков звуковых данных.

    Листинг 2.19. Файл wave\waveio.hpp

    #include #include


    typedef struct tagWAVEIOCB { DWORD dwDataSize; DWORD dwDataOffset; PCMWAVEFORMAT FAR *lpFmt; LPWAVEHDR lpWaveHdr; HPSTR lpData; WORD wBitsPerSample; WORD wBytesPerSample;

    } WAVEIOCB, *PWAVEIOCB, FAR *LPWAVEIOCB;

    #define WIOERR_BASE (100) #define WIOERR_NOERROR (0) #define WIOERR_ERROR (WIOERR_BASE+1) #define WIOERR_BADHANDLE (WIOERR_BASE+2) #define WIOERR_BADFLAGS (WIOERR_BASE+3) #define WIOERR_BADPARAM (WIOERR_BASE+4) #define WIOERR_BADSIZE (WIOERR_BASE+5) #define WIOERR_FILEERROR (WIOERR_BASE+6) #define WIOERR_NOMEM (WIOERR_BASE+7) #define WIOERR_BADFILE (WIOERR_BASE+8) #define WIOERR_NODEVICE (WIOERR_BASE+9) #define WIOERR_BADFORMAT (WIOERR_BASE+10) #define WIOERR_ALLOCATED (WIOERR_BASE+11) #define WIOERR_NOTSUPPORTED (WIOERR_BASE+12) #define WIOERR_READERROR (WIOERR_BASE+13)

    #define MAXSAMPLES 1024000L

    #define MODE_STOP 0 #define MODE_PLAYING 1 #define MODE_RECORDING 2 #define MODE_PLAYINGPAUSED 3 #define MODE_RECORDINGPAUSED 4

    BOOL wioSelectFile(LPSTR); int wioFileOpen(LPWAVEIOCB, LPSTR); int wioPlay(LPWAVEIOCB, HWND); int wioRecord(LPWAVEIOCB, HWND); BOOL wioFileSave(LPSTR szFileName); void wioOutError(int rc); void wioInError(int rc);

    Файл определения ресурсов приложения WAVE представлен в листинге 2.20.

    Листинг 2.20. Файл wave\wave.rc

    #include "wave.hpp" APPICON ICON "wave.ico" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open...", CM_FILEOPEN MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END

    MENUITEM "&Play!", CM_CTLPLAY MENUITEM "&Stop!", CM_CTLSTOP MENUITEM "Resu&me!", CM_CTLRESUME MENUITEM "P&ause!", CM_CTLPAUSE MENUITEM "&Record!", CM_CTLRECORD

    POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END

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

    Листинг 2.21. Файл wave\wave.def

    NAME WAVE DESCRIPTION 'Приложение WAVE, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 10240 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple

    Программы, работающие со звуком и видео

    Для работы со звуковыми адаптерами существует большое количество программ, рассчитанных на работу в среде MS-DOS, Windows и других операционных систем. Прежде чем приступать к созданию собственных программ, полезно познакомиться с тем, что уже сделано в этом направлении. Отметим, что наибольшей популярностью пользуются мультимедиа-приложения, разработанные для операционной системы Windows, хотя есть интересные программы MS-DOS, работающие со звуком и анимацией. Следующие версии Windows, такие, как Chicago, будут иметь очень мощные встроенные средства мультимедиа, поэтому, на наш взгляд, в первую очередь имеет смысл научиться создавать мультимедиа-приложения для Windows.
    В составе операционной системы Windows версии 3.1 поставляются драйверы, dll-библиотеки и некоторые приложения, предназначенные для работы со звуком. Дополнительно можно приобрести такое изделие, как Microsoft Video for Windows версии 1.1, которое можно считать средством для создания систем мультимедиа второго поколения.
    Нельзя сказать, что создавая программы MS-DOS, программист неизбежно столкнется с необходимостью программирования звукового адаптера на уровне портов, каналов прямого доступа и обработки прерываний. Для звуковых плат поставляются специальные средства разработки, содержащие драйверы в виде резидентных программ, значительно облегчающие программирование в среде MS-DOS.
    В качестве примера можно привести такой программный продукт, как Sound Galaxy Software Developer Kit. В него входит набор драйверов для MS-DOS, библиотеки функций языка программирования С для работы с этими драйверами в среде MS-DOS, а также подробное описание особенностей аппаратуры. К сожалению, в комплекте поставки нет ни одного примера программы, работающей на уровне портов ввода/вывода звукового адаптера, что необходимо, если ваша программа работает не в среде MS-DOS или Windows.
    Приложения Windows находятся в лучшем положении, так как они могут пользоваться высокоуровневым или низкоуровневым интерфейсом, который обеспечивается мультимедиа-расширением Windows. Само это расширение работает со звуковым адаптером через драйвер, который поставляется вместе с адаптером. Поэтому приложение Windows в значительной мере изолировано от звукового адаптера и его аппаратных особенностей, работая на уровне унифицированного интерфейса.
    К описанию этого интерфейса мы вернемся позже, а сейчас расскажем о некоторых приложениях, созданных для Windows с использованием технологии мультимедиа.

    Проигрывание MIDI-файлов

    4.1.
    4.2.
    4.3.
    В этой главе мы кратко расскажем вам о том, как приложения Windows могут проигрывать музыкальные файлы формата MIDI при помощи средств интерфейса MCI.
    Стандарт MIDI (Musical Instrument Digital Interface - цифровой интерфейс музыкальных инструментов) был разработан давно, в 1982 году. В рамках этого стандарта определены электрические и логические спецификации (уровни сигналов, временные диаграммы и коммуникационный протокол) для подключения таких музыкальных инструментов, как синтезаторы и музыкальные клавиатуры друг к другу и к компьютеру.
    Для электрического подключения используется последовательный интерфейс типа "токовая петля" со скоростью передачи данных 31250 бит в секунду. Данные передаются байтами с одним стартовым и одним стоповым битом. Подробная спецификация разъема приведена в документации на звуковой адаптер (если адаптер оснащен портом для подключения внешних устройств MIDI). Отметим, что устройства MIDI могут подключаться цепочкой, для чего на корпусе устройства обычно предусмотрены три разъема. Входной разъем обозначается MIDI In , выходной - MIDI Out . Разъем MIDI Thru предназначен для организации цепочки, он является выходным и дублирует сигналы с разъема MIDI In. Некоторые устройства, например, простые музыкальные клавиатуры, могут иметь только один входной или выходной разъем. В звуковом адаптере Sound Galaxy NX Pro предусмотрено подключение входного и выходного сигнала, причем оба они подключаются к одному разъему.
    Как правило, все звуковые адаптеры оборудованы музыкальным синтезатором. Синтезаторы могут иметь базовый или расширенный уровень. Синтезатор базового уровня содержит в себе голоса трех мелодичных и трех ударных инструментов. Уровень полифонии такого синтезатора равен 6 для мелодичных и 3 для ударных инструментов. Это означает, что синтезатор базового уровня может играть одновременно 6 нот на мелодичных инструментах и 3 ноты на ударных. Синтезаторы расширенного уровня содержат в себе 9 мелодичных и 8 ударных инструментов при уровне полифонии, равном 16.

    Хорошие синтезаторы содержат в себе цифровые записи (образцы) голосов музыкальных инструментов, которые модифицируются для получения нужной высоты тона и нужного уровня выходного сигнала. Более простые модели синтезируют звучание программным способом, что, однако, отдаляет качество звучания от идеала.

    Для управления синтезатором, подключенным через порт MIDI, используется механизм сообщений MIDI. Сообщения могут иметь длину от одного до нескольких байт. Они передают такую информацию, как номер нажатой или отпущенной клавиши музыкальной клавиатуры или номер одного из 16 логических каналов, через которые осуществляется управление синтезатором.

    Файлы в стандарте MIDI имеют расширение имени .mid и содержат заголовок и сообщения для музыкального синтезатора. Используется также стандарт RIFF. Файлы, содержащие сообщения MIDI и соответствующие стандарту RIFF, обычно создаются с расширением имени rmi. Если приложение Windows будет проигрывать такие файлы при помощи интерфейса MCI, ему не нужно знать внутренний формат файлов.

    Но есть одна тонкость, связанная с использованием логических каналов и кодов инструментов.

    В спецификации MIDI определены 16 логических каналов, предназначенных для адресации 16 логических синтезаторов. Каналы с номерами 13...16 используются синтезаторами базового уровня, каналы с номерами 1...10 - синтезаторами расширенного уровня. Каналы 11 и 12 не используются. Файлы MIDI содержат сообщения, предназначенные для разных каналов и для разных инструментов. Проблема заключается в том, что не все синтезаторы имеют одинаковое распределение каналов и инструментов. Самый простой способ уйти от трудностей, связанных с распределением каналов - приобретать mid-файлы, созданные специально для работы в среде Windows (авторизованные для Windows). В магазинах есть компакт-диски с многими сотнями таких файлов, причем в большинстве случаев вы можете свободно продавать эти файлы в составе своего программного обеспечения.

    В нашей книге мы не будем рассматривать вопросы создания mid-файлов.Во-первых, есть много готовых, которые можно купить в магазине. Во-вторых, для создания mid-файлов требуется музыкальная клавиатура, соответствующее программное обеспечение и, разумеется, некоторые музыкальные способности. Кроме того, нам надо еще оставить место в книге для рассказа о новейшем направлении в мультимедиа - системе Microsoft Video for Windows.

    А сейчас рассмотрим особенности использования интерфейса MCI для проигрывания файлов MIDI.

    Работа с окном MCI

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

    Record

    Эта команда запускает запись звукового фрагмента.
    В качестве необязательного параметра parameter можно указывать одну из следующих строк:
    insert
    Новые данные будут добавлены к ранее записанным
    from position
    Определение начальной позиции для записи. Если начальная позиция не задана, данные будут вставлены начиная с текущей позиции. Если используются параметры from или to, необходимо задать формат времени командой set time format
    to position
    Определение конечной позиции при записи. Если конечная позиция не задана, запись будет продолжаться до тех пор, пока не будет выдана команда stop или pause
    overwrite
    Новые данные должны заместить записанные ранее

    Resume

    Продолжение воспроизведения или записи после временного останова по команде pause.
    resume device_id [notify] [wait]

    Самые простые способы воспроизведения звука

    В самом простейшем случае приложение должно уметь выдавать звуковые сигналы или проигрывать небольшие звуковые сообщения при появлении каких-либо непредвиденных событий, когда нужно привлечь внимание пользователя. Для этого можно воспользоваться функциями MessageBeep и sndPlaySound .

    Save

    Сохранение записанного звукового фрагмента в файле
    save device_id [filename] [notify] [wait]
    Параметр filename задает путь к файлу, в который должен быть записан звуковой фрагмент

    Сброс устройства

    Функции waveInReset и waveOutReset выполняют, соответственно, останов устройства ввода или вывода и сброс текущей позиции для устройства в 0.
    Функция waveInReset
    UINT waveInReset( HWAVEIN hWaveIn); // идентификатор устройства ввода
    Параметры функции:
    hWaveIn
    Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства
    Возвращаемое значение:
    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
    MMSYSERR_INVALHANDLE
    Указан неправильный идентификатор устройства
    Функция waveOutReset
    UINT waveOutReset ( HWAVEOUT hWaveOut); // идентификатор устройства вывода
    Параметры функции:
    hWaveOut
    Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
    Возвращаемое значение:
    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
    MMSYSERR_INVALHANDLE
    Указан неправильный идентификатор устройства

    Seek

    Позиционирование с последующим остановом. Перед использованием этой команды необходимо задать формат времени командой set time format.
    seek device_id parameter [notify] [wait]
    В качестве необязательного параметра parameter можно указывать одну из следующих строк:
    to position
    Позиционирование в указанное место фрагмента
    to start
    Позиционирование в начало
    to end
    Позиционирование в конец

    Set

    Команда set в зависимости от параметров позволяет выполнять установку различных режимов работы.
    set device_id parameter [notify] [wait]
    В качестве параметра parameter можно указывать одну из следующих строк (за один раз можно указывать сразу несколько параметров):
    alignment int
    Установка выравнивания блока данных относительно начала данных звукового фрагмента, переданного драйверу звукового адаптера
    any input
    Использование любого устройства ввода, поддерживающего текущий формат при записи. Этот режим включен по умолчанию
    any output
    Использование любого устройства вывода, поддерживающего текущий формат при воспроизведении. Этот режим включен по умолчанию
    audio all off
    Отключение звукового выхода
    audio all on
    Включение звукового выхода
    audio left off
    Отключение левого канала
    audio left on
    Включение левого канала
    audio right off
    Отключение правого канала
    audio right on
    Включение правого канала
    bitspersample bit_count
    Установка количества бит для представления выборки сигнала. Параметр bit_count задает количество бит (8 или 16)
    bytespersec byte_rate
    Установка частоты дискретизации при записи или воспроизведении. Параметр byte_rate задает частоту (байты в секунду)
    channels channel_count
    Установка количества каналов для записи или воспроизведения (1 - монофонический режим, 2 - стереофонический режим)
    format tag tag
    Установка типа формата
    format tag pcm
    Установка формата PCM (импульсно-кодовая модуляция)
    input int
    Выбор канала для ввода
    output int
    Выбор канала для вывода
    samplepersec int
    Установка скорости записи или воспроизведения
    time format bytes
    В качестве единицы измерения при позиционировании используются байты блока звуковых данных
    time format milliseconds
    В качестве единицы измерения при позиционировании используются миллисекунды. Строку milliseconds можно также указывать как ms
    time format samples
    В качестве единицы измерения при позиционировании используются выборки сигнала

    Системы распознавания речи

    Несмотря на сложность задачи распознавания речи, в этом направлении есть определенные достижения. Все существующие системы можно разделить на две группы. К первой группе относятся системы распознавания, которые обучаются пользователем. Пользователь сам формирует словарь, причем система настраивается на произношение конкретного человека. Вторая группа систем содержит фиксированный словарь и не нуждается в предварительном обучении или настройке на конкретного пользователя.
    Примером системы, относящейся к первой группе, может послужить приложение Voice Mouse, созданное фирмой IPI (рис. 1.25).
    Системы распознавания речи
    Рис. 1.25. Приложение Voice Mouse
    Это приложение добавляет в Windows голосовое управление, что позволяет выполнять некоторые функции, не прикасаясь к мыши или клавиатуре. Во время своей работы Voice Mouse анализирует структуру меню активного приложения, формируя в своем окне список команд, входящих в меню. Этот список можно расширить произвольным образом. Для каждой команды можно определить звуковую последовательность, при распознавании которой данная команда будет запущена на выполнение, а также действия, которые нужно выполнить.
    Для редактирования команд используется диалоговая панель "Command Editor" (рис.1.26).
    Системы распознавания речи
    Рис. 1.26. Редактор команд
    С помощью кнопки "Train..." можно обучить Voice Mouse распознавать команду, повторив в микрофон соответствующее этой команде слово два раза.
    С помощью кнопки "Edit..." можно назначить действие, которое должно быть выполнено при распознавании команды. В качестве действия можно задать либо запуск произвольного приложения, либо имитацию ввода с клавиатуры произвольной последовательности символов. Например, исходный текст приложения, показанный на рис. 1.25, был "набран" без использования клавиатуры, мы просто надиктовали его в микрофон, предварительно обучив систему Voice Mouse таким словам, как WinMain и HINSTANCE.
    Аналогичная система создана фирмой Cylogic Software и называется Voice User. Среди возможных областей ее применения - помощь пользователям-инвалидам.
    К системам распознавания речи второй группы можно отнести IBM Personal Dictation System, разработанную фирмой IBM Personal Software Products. Эта система имеет словарь размером 32 тыс. слов и способна работать с английским, немецким, французским, итальянским и испанскими языками. Аналогичные системы создаются и в России.

    Сообщение MM_MCINOTIFY

    Немного о сообщении MM_MCINOTIFY .
    Как мы уже говорили, приложение может передать функции mciSendString через последний параметр идентификатор окна. Если команда MCI выдана с параметром notify, после ее завершения функция окна получит сообщение MM_MCINOTIFY. Это сообщение - извещение о завершении (удачном или нет) процесса выполнения команды.
    Через параметр wParam сообщения MM_MCINOTIFY функция окна получает код извещения, по которому можно судить о результатах выполнения команды. Возможны следующие значения (описанные в файле mmsystem.h):

    Значение Описание
    MCI_NOTIFY_ABORTED Устройство получило такую команду, в результате которой не будет получено извещение о завершении выполнения предыдущей команды. Если новая команда прерывает выполнение текущей команды и также требует извещения, функция окна получит сообщение MCI_NOTIFY_ABORTED (но не MCI_NOTIFY_SUPERSEDED)
    MCI_NOTIFY_SUCCESSFUL Успешное завершение команды
    MCI_NOTIFY_SUPERSEDED Устройство получило еще одну команду, так же требующую извещения, в результате чего извещение от первой команды не будет получено
    MCI_NOTIFY_FAILURE В устройстве произошла ошибка во время выполнения команды

    Параметр lParam содержит идентификатор устройства, приславшего извещение.
    В случае успешного завершения обработчик сообщения MM_MCINOTIFY должен вернуть нулевое значение, при ошибке - соответствующий код ошибки MCI.
    Далее мы перейдем к изучению отдельных групп команд, предназначенных для работы со звуковым адаптером.

    Создание avi-файла из файлов анимации

    С помощью строки "Insert..." меню "File" вы можете вставить flc - или fli -файлы анимации в формате Autodesk Animation , подготовленные, например, с помощью таких программ, как 3D-Studio или Autodesk Animator. Для этого в списке "List Files of Type" диалоговой панели "Insert File" выберите строку "Autodesk Animation" и укажите нужный файл. Он будет преобразован в формат avi.

    Создание и редактирование avi-файлов

    Перед тем как приступить к изучению программного интерфейса Video for Windows (вернее, его небольшой части, касающейся окна MCI) неплохо было бы научиться создавать и редактировать avi-файлы, пользуясь утилитами, входящими в состав Video for Windows и Video for Windows Development Kit. Существует несколько способов создания avi-файлов, и не все они связаны с использованием видеокамеры и адаптера для ввода изображения. Даже если у вас нет оборудования для ввода в компьютер видео, все равно вы можете создавать мультфильмы и демонстрационные ролики.

    Создание окна

    Для создания окна MCI проще всего воспользоваться функцией MCIWndCreate .
    Функция MCIWndCreate
    HWND MCIWndCreate( HWND hwndParent, // идентификатор родительского окна HINSTANCE hInstance, // идентификатор приложения DWORD dwStyle, // стиль окна LPSTR szFile); // имя устройства или путь к файлу
    Параметры функции:
    hwndParent
    Через этот параметр приложение передает функции идентификатор родительского окна, то есть окна, создавшего окно MCI. Если родительского окна нет, в качестве этого параметра можно указать NULL
    hInstance
    Идентификатор приложения, полученных через параметры функции WinMain или LibMain (для DLL-библиотеки)
    dwStyle
    Стиль создаваемого окна. Можно указывать стили, стандартные для функции CreateWindow, а также дополнительные, список которых приведен ниже. Если стандратные стили не указаны (что допустимо), то если есть родительское окно, используются стили WS_CHILD, WS_BORDER, иWS_VISIBLE. Если же параметр hwndParent указан как NULL, используются стили WS_OVERLAPPEDWINDOW и WS_VISIBLE. Для создания невидимого окна следует использовать один из стандартных стилей, например, WS_CHILD
    szFile
    Указатель на текстовую строку, содержащую имя устройства (например, "cdaudio") или путь к файлу
    Возвращаемое значение:
    Идентификатор созданного окна при успешном завершении или NULL при ошибке
    Привдем список дополнительных стилей, которые можно использовать при создании окна MCI.

    Стиль Описание
    MCIWNDF_NOAUTOSIZEWINDOW Размер окна не изменяется при изменении размера изображения
    MCIWNDF_NOAUTOSIZEMOVIE При изменении размеров окна не следует выполнять масштабирование изображения для полного заполнения внутренней области окна
    MCIWNDF_NOPLAYBAR Если задан этот стиль, не отображается полоса просмотра
    MCIWNDF_NOMENU Не отображается кнопка для доступа к меню
    MCIWNDF_RECORD Отображается кнопка записи, в меню добавляется строка "New"
    MCIWNDF_NOERRORDLG При возникновении ошибки на экран не выводится диалогоая панель с описанием этой ошибки. Приложение может получить описание самой последней возникшей ошибки при помощи функции MCIWndGetError
    MCIWNDF_NOTIFYMODE При изменении режима родительское окно получит извещающее сообщение MCIWNDM_NOTIFYMODE
    MCIWNDF_NOTIFYPOS При изменении текущей позиции приложение получит извещающее сообщение MCIWNDM_NOTIFYPOS
    MCIWNDF_NOTIFYMEDIA При замене носителя данных (например, звукового компакт-диска) приложение получит извещающее сообщение MCIWNDM_NOTIFYMEDIA
    MCIWNDF_NOTIFYSIZE Родительское окно получит извещающее сообщение MCIWNDM_NOTIFYSIZE при изменении размера окна MCI
    MCIWNDF_NOTIFYERROR При возникновении ошибки родительское окно получит сообщение MCIWNDM_NOTIFYERROR
    MCIWNDF_NOTIFYALL Окно MCI будет извещать родительское окно в случае возникновения любых событий
    MCIWNDF_SHOWNAME В заголовке окна будет отображаться имя устройства или путь к файлу
    MCIWNDF_SHOWPOS В заголовке окна будет отображаться текущая позиция
    MCIWNDF_SHOWMODE В заголовке окна будет отображаться текущий режим работы
    MCIWNDF_SHOWALL Будут использованы все возможности окна MCI (то есть все органы управления, отображение информации в заголовке и т. д.)

    Другой способ создания окна MCI заключается в регистрации класса окна MCIWND_WINDOW_CLASS функцией MCIWndRegisterClass , не имеющей парамеров, и создании на базе этого класса окна функцией CreateWindow . В случае успеха функция MCIWndRegisterClass возвращает значение FALSE.

    Создание видео из отдельных кадров

    Подготовьте несколько десятков или сотен (а может быть, тысяч?) bmp-файлов, содержащих отдельные кадры мультфильма. Имена файлов должны оканчиваться их порядковым номером, например, pic001.bmp, pic002.bmp, ..., pic867.bmp и т. д.. Разумеется, размеры и цветовое разрешение всех кадров должны быть одинаковые, а что же касается содержимого, тут можно полностью положиться на ваш вкус. Если кадры должны сменяться, например, десять раз в секунду, то для создания десятисекундного мультфильма вам нужно подготовить "всего" сто рисунков.
    Запустите приложение VidEdit , которое поставляется в составе Video for Windows (рис.5.5).
    Создание видео из отдельных кадров
    Рис. 5.5. Редактор avi-файлов VidEdit
    С помощью меню "File" вы можете загрузить для редактирования готовый avi-файл (строка "Open...") или создать новый (строка "New"). Для создания avi-файла из последовательности bmp-файлов выберите из этого меню строку "Insert...". В списке "List Files of Type" появившейся диалоговой панели "Insert File" выберите строку "DIB Sequence".
    Замените в поле "File Name" шаблон *.dib на *.bmp и выберите первый файл из подготовленной вами последовательности. Укажите формат файла как "DIB Sequence" и нажмите кнопку "OK". Последовательность bmp-файлов будет преобразована в avi-файл.
    Сохраните созданный файл, выбрав из меню "File" строку "Save As...". На экране появится диалоговая панель "Save Video File", напоминающая стандартную диалоговую панель "Save As" из библиотеки commdlg.dll. Единственное существенное отличие заключается в том, что при помощи кнопки "Compression Options..." вы можете задать алгоритм сжатия, который будет использован при сохранении avi-файла (рис. 5.6).
    Создание видео из отдельных кадров
    Рис. 5.6. Диалоговая панель для выбора алгоритма сжатия
    Для начала используйте параметры, указанные на рис. 5.6. При необходимости вы можете изменить скорость воспроизведения видео, выбрав в меню "Video" строку "Synchronize...". На экране появится диалоговая панель "Synchronize", предназначенная для выполнения синхронизации видео и звука (рис. 5.7).
    Создание видео из отдельных кадров
    Рис. 5.7. Диалоговая панель "Synchronize"
    Изменив значение частоты кадров в поле "Video Speed", вы измените скорость воспроизведения видео.
    Последовательность bmp-файлов (а также отедльные bmp-файлы) можно вставлять в любое место видео. Кроме графических файлов в формате DIB, редактор VidEdit позволяет вставлять отдельные изображения в форматах PC Paintbrush, TIFF, TGA, GIF и других популярных графических форматов.

    Справочные команды

    Эта группа команд предназначена для получения различной справочной информации об устройстве. Перед использованием справочных команд следует открыть устройство командой open.

    Команда sysinfo не имеет особенностей. В качестве имени устройства для этой команды следует указывать строку cdaudio, даже если при открытии был использован алиас.
    Для команды info можно указывать только параметр product.
    С помощью команды capability с параметром can eject вы можете узнать, имеет ли устройство CD ROM возможность автоматического извлечения компакт-дисков. Вы можете также использовать и другие параметры: can play, can record, can save, compound device, device type, has audio, has video, uses files.
    Для определения текущего состояния CD ROM следует использовать команду status . Вы можете указать следующие параметры:
    current track
    Номер текущей дорожки
    length
    Общая длина
    length track track_number
    Длина заданной дорожки
    media present
    Если в устройство вставлен компакт-диск, возвращается строка true
    mode
    Текущий режим работы: not ready (не готов), playing (проигрывание), stopped (останов), recording (запись), seeking (позиционирование)
    number of tracks
    Количество дорожек
    position
    Текущая позиция
    position track track_number
    Текущая позиция на заданной дорожке
    ready
    Если устройство готово, возвращается строка true
    start position
    Начальная позиция
    time format
    Текущий формат времени

    Средства для работы с avi-файлами

    Файлы avi имеют довольно сложную внутреннюю структуру и состоят из большого числа вложенных фрагментов. Структура avi-файла приведена в документации, которая поставляется вместе с Video for Windows Development Kit. Мы не будем ее описывать, так как в большинстве случаев вам не потребуется выполнять непосредственное чтение или запись avi-файлов. Все функции, необходимые для работы с avi-файлами, имеются в библиотеке avifile.dll.
    Заметим, что в отличие от, например, wav-файла, avi-файл может содержать несколько потоков данных, обрабатываемых одновременно. Все данные хранятся в виде блоков (кадров), причем для обеспечения непрерывности воспроизведения звуковых данных последние чередуются с видеоданными. Как правило, для сокращения объема avi-файла используются различные методы компресии, поэтому звуковые данные и видеоданные обычно хранятся в сжатом виде. Таким образом, сложность внутренней структуры avi-файла сильно затрудняет работу с ним на низком уровне без использования функций библиотеки avifile.dll.
    Функции, предназначенные для работы с avi- и wav-файлами, определенные в библиотеке avifile.dll, позволяют работать на уровне потоков данных и кадров. Вы можете получать информацию об avi-файле и о потоках данных, читать и писать потоки данных, получать отдельные кадры, причем при необходимости эти данные будут автоматически разжаты или сжаты. Есть средства для копирования потоков из разных файлов, позиционирования внутри потока, создания временных потоков в оперативной памяти. Вы можете скопировать весь avi-файл или любую его часть в Clipboard.
    В распоряжении приложений есть функции GetOpenFileNamePreview и GetSaveFileNamePreview , аналогичные по своему назначению функциям GetOpenFileName и GetSaveFileName библиотеки commdlg.dll, но дополнительно обеспечивающие возможность предварительного просмотра avi-файлов или прослушивания wav-файлов. Мы используем функцию GetOpenFileNamePreview в приложении MCIWNDC, исходные тексты которого приведены в этой главе.

    Стандартные приложения Windows

    Первое, что вы можете сделать после установки звукового адаптера и драйвера для него - это "озвучить" Windows. Запустите приложение Control Panel и выберите для работы пиктограмму "Sound". На экране появится диалоговая панель "Sound" (рис. 1.8).
    Стандартные приложения Windows
    Рис. 1.8. Диалоговая панель "Sound" в Windows for Workgroups версии 3.11
    Из списка "Events" вы можете выбрать то или иное событие в системе, назначив ему соответствующий звуковой фрагмент из файла с расширением имени wav. Если произойдет событие, например, запуск или завершение работы Windows, критическая ошибка и т. п., звуковой адаптер проиграет соответствующий звуковой файл. Несколько таких файлов поставляются в комплекте с операционными системами Windows или Windows for Workgroups. Для прослушивания подключенного фрагмента можно воспользоваться кнопкой "Test".
    Для отключения фрагмента выберите строку "". Вы можете отключить "музыкальное сопровождение" Windows, если переведете переключатель "Enable System Sounds" в выключенное состояние.
    Если вас не удовлетворяет набор звуковых wav-файлов, поставляемых в составе Windows, с помощью стандартного приложения Sound Recorder (рис. 1.9) можно записать свои файлы (а также прослушать и отредактировать записанные ранее).
    Стандартные приложения Windows
    Рис. 1.9. Приложение Sound Recorder
    Для прослушивания wav-файла загрузите его с помощью строки "Open..." меню "File" и нажмите на среднюю кнопку. Вы можете "перематывать" звуковую запись в любом направлении с помощью полосы просмотра или левых двух кнопок, а также временно останавливать ее, нажимая вторую кнопку справа. В центре окна приложения для наглядности отображается осциллограмма звукового сигнала.
    Кнопка с нарисованным на ней микрофоном включает режим записи. Записанную фонограмму можно сохранить в файле при помощи строки "Save as..." меню "File".
    Что еще, кроме записи и воспроизведения звуковых файлов, можно делать при помощи приложения Sound Recorder?

    Меню "Effects" позволяет выполнять различные операции над загруженным или записанным звуковым файлом. Можно увеличивать или уменьшать громкость, скорость воспроизведения, добавлять эхо или перевернуть звуковой файл для проигрывания из конца в начало.

    Меню "Edit" также предоставляет вам некоторые возможности для редактирования звукового файла. Вы можете объединять звуковые файлы или накладывать их друг на друга, удалять ненужные фрагменты, и, что самое интересное, копировать содержимое звукового файла в универсальный буфер обмена Clipboard. Зачем это нужно?

    Современные приложения, такие как Microsoft Write, Microsoft Word for Windows версий 2.0 и 6.0, Microsoft Excel и т. п. позволяют вставлять в документ из Clipboard не только текст и графические изображения, но и звук! Разумеется, звук нельзя распечатать на принтере, но зато можно добавить звуковые комментарии в текстовый документ или электронную таблицу. Звук будет записан в Clipboard в виде OLE-объекта. Такой объект можно впоследствии не только прослушивать, но и редактировать.

    Стандартные приложения Windows В результате вставки звука из Clipboard в документе появится пиктограмма в виде микрофона. Если сделать по этой пиктограмме двойной щелчок левой клавишей мыши, вы услышите звук, вставленный ранее в документ.
    Несмотря на то, что звуковые комментарии увеличивают размер файла, содержащего документ, они удобнее и нагляднее обычных, в виде напечатанных слов. Кроме того, звуковые комментарии вносят минимальные изменения в документ (вместо ссылок и сносок добавляется одна или несколько пиктограмм).

    Еще одно приложение, которое работает со звуком и поставляется вместе с Windows, называется Media Player (рис. 1.10).

    Стандартные приложения Windows

    Рис. 1.10. Приложение Media Player

    С помощью этого приложения вы можете проигрывать звуковые wav-файлы, файлы в стандарте MIDI (mid- и rmi-файлы), а также звуковые компакт диски. Можно также записывать в Clipboard звуковые фрагменты в виде OLE-объектов приложения Media Player. Для проигрывания MIDI-файлов и звуковых компакт-дисков необходимо установить соответствующие драйверы: MCI MIDI Sequencer, MIDI Mapper (которые входят в дистрибутив Windows и устанавливаются по умолчанию), а также драйвер, специфический для звукового адаптера (поставляется вместе с адаптером).


    Например, вместе с адаптером Sound Galaxy NX Pro поставляется драйвер Galaxy NX-Pro FM Synth, который можно использовать для проигрывания MIDI-файлов. Вопросы, связанные с MIDI, будут рассмотрены нами позже в отдельной главе.

    Приложение Media Player можно использовать и для проигрывания звуковых компакт-дисков. Для этого из меню "Device" следует выбрать строку "CD Audio". Однако следует убедиться, что в системе установлен драйвер MCI CD Audio. Если такого драйвера нет (а по умолчанию он не устанавливается), его следует добавить при помощи приложения Control Panel. Драйвер MCI CD Audio входит в комплект поставки операционной системы Windows. Кроме того, перед запуском Windows следует установить драйвер устройства чтения компакт-дисков (поставляется вместе с устройством), и запустить резидентную программу mscdex.exe (поставляется вместе с MS-DOS).

    Звуковые компакт-диски содержат отдельные дорожки (треки), каждый из которых обычно соответствует одной музыкальной записи. С помощью Media Player вы можете проигрывать их последовательно или в произвольном порядке.

    Если включить электронный микшер, аналоговый выход проигрывателя компакт-дисков будет подключен ко входу аналого-цифрового преобразователя звукового адаптера, что позволит переписать музыкальный фрагмент в wav-файл. Для записи wav-файла можно воспользоваться приложением Sound Recorder. Не пытайтесь переписать весь компакт-диск, так как для этого придется создать wav-файл огромного размера (некоторые приложения, предназначенные для записи wav-файлов, могут накладывать ограничения на размер файла).

    На этом мы завершим обзор стандартных приложений Windows, предназначенных для работы со звуком. Более подробное описание вы сможете найти в руководстве пользователя Windows. Подводя итоги, заметим, что сервис, предоставляемый этими приложениями, нельзя назвать очень мощным. Такие приложения, как Sound Recorder и Media Player, позволяют записать, прослушать и отредактировать звуковой wav-файл, вставить звук в документ в виде OLE-объекта, проиграть MIDI-файлы и звуковые компакт-диски.Но это и все, что с их помощью можно сделать.

    К настоящему моменту времени разными фирмами создано большое количество приложений Windows, способных работать со звуком и анимацией. Такие приложения реализуют технологию мультимедиа первого поколения, в которой основной упор делался на использование звука и простейшей анимации (второе поколение предполагает использование видео, совмещенного со звуком, этот подход реализован, например, в системе Microsoft Video for Windows, о которой мы еще расскажем). Рассмотрим кратко возможности приложений мультимедиа, разработанных различными фирмами.

    Status

    Команда status позволяет определить текущее состояние устройства.
    status device_id parameter [notify] [wait]
    В качестве параметра parameter можно указывать одну из следующих строк:
    alignment
    Выравнивание блока данных в байтах
    bitspersample
    Количество байт на одну выборку сигнала
    bytespersec
    Скорость проигрывания или записи, байт в секунду
    channels
    Количество каналов, 1 - моно, 2 - стерео
    current track
    Номер текущей дорожки. Для звукового адаптера всегда равно 1
    format tag
    Тег формата
    input
    Устройство ввода
    length
    Общая длина звукового фрагмента
    length track track_number
    Длина фрагмента, соответствующая заданной дорожке
    level
    Текущий уровень звукового сигнала
    media present
    Признак присутствия носителя (среды). Для звукового адаптера всегда равно true
    mode
    Текущий режим работы: not ready (не готов), playing (проигрывание), stopped (останов), recording (запись), seeking (позиционирование)
    number of tracks
    Количество дорожек. Для звукового адаптера всегда равно 1
    output
    Устройство вывода
    position
    Текущая позиция
    position track track_number
    Текущая позиция на заданной дорожке. Для звукового адаптера всегда равно 0
    ready
    Если устройство готово, возвращается строка true
    samplespersec
    Количество выборок сигнала в секунду при проигрывании или записи (частота дискретизации)
    start position
    Начальная позиция
    time format
    Текущий формат времени

    Sysinfo

    Команда sysinfo предназначена для получения системной информации об MCI-устройстве:
    sysinfo device_id parameter [notify] [wait]
    В качестве параметра parameter можно указывать одну из следующих строк:
    installname
    Имя, использованное в файле system.ini при установке драйвера устройства
    quantity
    Количество MCI-устройств типа device_id, установленных в системе и указанных в файле system.ini. Если в качестве device_id указать строку all, будет подсчитано общее количество установленных в системе MCI-драйверов
    quantity open
    Количество открытых MCI-устройств типа device_id, установленных в системе и указанных в файле system.ini
    name index
    Имя устройства MCI, номер которого задан строкой index. Первому устройству соответствует строка 1
    name index open
    Имя открытого устройства MCI, номер которого задан строкой index

    Текстовое описание ошибки

    Когда мы рассказывали об использовании функции mciSendString , то упоминали функцию mciGetErrorString, с помощью которой можно преобразовать код ошибки в текстовое описание в виде строки символов. Аналогичная возможность есть и у приложений, работающих со звуковым адаптером на низком уровне. Для выполнения такого преобразования приложение может воспользоваться функцией waveInGetErrorText (для устройства ввода) и waveOutGetErrorText (для устройства вывода).
    Приведем описание функции waveInGetErrorText:
    Функция waveInGetErrorText
    UINT waveInGetErrorText( UINT wError, // код ошибки LPSTR lpstrBuffer, // буфер для записи текстовой строки UINT wLength); // размер буфера
    Параметры функции:
    wError
    Код ошибки, полученный от функций низкого уровня
    lpstrBuffer
    Буфер, в который будет записано текстовое описание ошибки
    wLength
    Размер буфера в байтах. В файле mmsystem.h определена константа MAXERRORLENGTH , которая соответствует размеру самого длинного сообщения об ошибке
    Возвращаемое значение:
    Функция возвращает нулевое значение при успешном завершении или значение MMSYSERR_BADERRNUM , если переданному коду ошибки не соответствует ни одно текстовое описание
    Функция waveOutGetErrorText используется аналогично функции waveInGetErrorText:
    Функция waveOutGetErrorText
    UINT waveOutGetErrorText( UINT wError, // код ошибки LPSTR lpstrBuffer, // буфер для записи текстовой строки UINT wLength); // размер буфера
    Параметры функции:
    Аналогичны параметрам функции waveInGetErrorText
    Возвращаемое значение:
    Аналогично функции waveInGetErrorText

    Удаление окна

    Если окно MCI больше не нужно, его можно удалить макрокомандой MCIWndDestroy . Идентификатор удаляемого окна MCI передается этой функции в качестве единственного параметра:
    #define MCIWndDestroy(hwnd) (VOID)MCIWndSM(hwnd, WM_CLOSE, 0, 0)
    Макро MCIWndSM определяется как функция SendMessage (см. файл mciwnd.h, поставляемый вместе с Video for Windows Development Kit и включаемый в файл vfw.h).
    Для того чтобы закрыть устройство, открытое ранее в окне MCI без удаления окна, используйте макрокоманду MCIWndClose, передав ей идентификатор окна.

    Управление громкостью

    Ваше приложение может управлять громкостью сигнала при его воспроизведении. Для установки громкости следует использовать функцию waveOutSetVolume :
    Функция waveOutSetVolume
    UINT waveOutSetVolume( UINT wDeviceID, // номер устройства вывода DWORD dwVolume); // громкость
    Параметры функции:
    wDeviceID
    Параметр wDeviceID служит для выбора устройства. Заметим, что для функции waveOutSetVolume нужно указывать не идентификатор открытого устройства, а номер устройства, который может изменяться от 0 и до значения, определенного с помощью функции waveOutGetNumDevs. Если известен только идентификатор открытого устройства, номер этого устройства можно получить, вызвав функцию waveOutGetID, рассмотренную нами ранее
    dwVolume
    Младшее слово параметра dwVolume задает громкость для левого канала (или единственного монофонического канала), старшее - для правого. Максимальной громкости соответствует значение 0xffff, минимальной - 0x0000. Промежуточные значения интерпретируются в логарифмическом масштабе
    Возвращаемое значение:
    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
    MMSYSERR_INVALHANDLE
    Указан неправильный идентификатор устройства
    MMSYSERR_NOTSUPPORTED
    Функция не поддерживается драйвером
    MMSYSERR_NODRIVER
    В системе нет нужного драйвера
    Как правило, вместе со звуковым адаптером проставляется приложение, выполняющее функции единой управляющей панели, с помощью которой можно регулировать громкость и тембр звука для разных каналов и устройств. Если ваше приложение изменяет громкость, перед завершением своей работы оно должно восстановить первоначальный уровень громкости (если от него не требуется обратного). Это можно сделать, если перед изменением определить текущий уровень громкости с помощью функции waveOutGetVolume :
    Функция waveOutGetVolume
    UINT waveOutGetVolume( UINT wDeviceID, // номер устройства вывода LPDWORD lpdwVolume); // текущая громкость
    Параметры функции:
    wDeviceID
    Параметр wDeviceID содержит номер устройства, который может изменяться от 0 и до значения, определенного с помощью функции waveOutGetNumDevs
    lpdwVolume
    Указатель на переменную размером в двойное слово, в которую будет записано значение, соответствующее текущей громкости для левого и правого каналов. Младшее слово переменной будет содержать громкость для левого канала (или монофонического канала), старшее - для правого. Максимальной громкости соответствует значение 0xffff, минимальной - 0x0000. Промежуточные значения интерпретируются в логарифмическом масштабе
    Возвращаемое значение:
    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
    MMSYSERR_INVALHANDLE
    Указан неправильный идентификатор устройства
    MMSYSERR_NOTSUPPORTED
    Функция не поддерживается драйвером
    MMSYSERR_NODRIVER
    В системе нет нужного драйвера

    Управление проигрыванием

    Макрокоманда MCIWndPlay включает режим проигрывания для окна MCI, идентификатор которого передается ей в качестве единственного параметра.
    Если вам нужно начать проигрывание с заданной позиции, воспользуйтесь макрокомандой MCIWndPlayFrom . Первый параметр этой макрокоманды задает идентификатор окна MCI, второй (размером в двойное слово) - позицию для начала проигрывания. Есть возможность задать конечную позицию. Макрокоманда MCIWndPlayFromTo , имеющая три параметра, аналогична макрокоманде MCIWndPlayFrom , но позволяет через третий параметр задать конечную позицию, при достижении которой проигрывание будет остановлено.
    Вы даже можете запустить проигрывние в обратную сторону - от конца к началу файла (если драйвер устройства позволит вам это сделать). Достаточно вызвать макрокоманду MCIWndPlayReverse . Она имеет один параметр (идентификатор окна MCI) и запускает проигрывание от текущей позиции к началу файла.
    Макрокоманда MCIWndSetRepeat позволяет включить режим циклического проигрывания. Через первый параметр этой макрокоманде передается идентификатор окна MCI, через второй для включение режима циклического проигрывания нужно передать значение TRUE.
    Макрокоманды MCIWndStop и MCIWndPause предназначены для выполнения, соответственно, останова и временного останова проигрывания. Для того чтобы продолжить проигрывание после временного останова, используйте макрокоманду MCIWndResume . Все три макрокоманды имеют только один параметр - идентификатор окна MCI.

    Управление устройством CD ROM

    3.1.
    3.2.
    В этой главе мы расскажем вам об использовании устройства чтения CD ROM для проигрывания звуковых компакт-дисков. Приложения Windows работают с этим устройством через интерфейс MCI, которым вы уже умеете пользоваться для записи и воспроизведения wav-файлов. Так как объем книги ограничен, мы не будем подробно рассказывать об использовании всех команд MCI для управления устройством чтения CD ROM, ограничившись только особенностями.
    Для чего может потребоваться прослушивание звуковых компакт-дисков при помощи такого дорогостоящего устройства, как компьютер, оснащенный средствами мультимедиа? Ведь стоимость обычного проигрывателя компакт-дисков не превышает нескольких сотен долларов, что просто несравнимо со стоимостью компьютера!
    Наиболее очевидные области применения приложений, умеющих проигрывать звуковые компакт-диски - рекламные и демонстрационные ролики с музыкальным и речевым сопровождением, игры, автоматизированные музыкальные центры.
    Не вдаваясь в технические подробности, заметим, что данные на компакт-диске записаны вдоль одной гигантской спирали. В первом приближении можно считать, что на музыкальных дисках эта спираль разбита на несколько участков, или дорожек, каждая из которых содержит отдельную звуковую запись (например, музыкальное произведение).
    Приложение может устанавливать лазерное устройство чтения в произвольное место спирали, причем драйвер обеспечивает позиционирование в режиме прямого доступа как на начало любой дорожки, так и в произвольную позицию внутри дорожки. К сожалению, процесс позиционирования занимает много времени, около 0,5 секунды, поэтому (а также из-за небольшой скорости передачи, составляющей 150-300 Кбайт в секунду) устройства чтения CD ROM нельзя называть быстродействующими.
    Как мы уже говорили в первой главе, устройство чтения CD ROM имеет два звуковых выхода. Один из них обычно расположен на лицевой панели и предназначен для подключения головных телефонов (там же находится и регулятор громкости). Второй выведен на заднюю панель и подключается кабелем к входу звукового адаптера, специально предназначенному для этого. Программное обеспечение микшерского пульта, поставляющееся вместе со звуковым адаптером, позволяет подключать выход устройства чтения CD ROM ко входу усилителя или аналого-цифрового преобразователя, поэтому приложения мультимедиа могут выполнять не только проигрывание звуковых компакт-дисков с прослушиванием через громкоговорители, но и синхронную запись wav-файлов.
    Вы можете работать с устройством чтения CD ROM при помощи интерфейса управляющих строк MCI или интерфейса управляющих сообщений.

    Управление записью и сохранение данных

    Для включения режима записи с текущей позиции предназначена макрокоманда MCIWndRecord . В качестве единственного параметра ей нужно передать идентификатор окна MCI. С помощью этой макрокоманды вы сможете организовать запись звуковых данных (как это сделано в нашем приложении MCIWNDC, которое мы рассмотрим чуть позже).
    С помощью окна MCI вы не можете записывать avi-файлы, для этого предназначено окно класса AVICap.
    Для сохранения записанных данных можно воспользоваться макрокомандами MCIWndSave или MCIWndSaveDialog .
    Макрокоманда MCIWndSave имеет два параметра - идентификатор окна MCI и указатель на текстовую строку, в которой должен находиться путь к файлу. Записанные данные будут сохранены в этом файле.
    При вызове макрокоманды MCIWndSaveDialog , имеющий один параметр (идентификатор окна MCI) на экран выводится диалоговая панель, позволяющая пользователю сохранить данные в файле. Это модифицированная диалоговая панель "Save As..." с возможностью предварительного просмотра или прослушивания содержимого файлов мультимедиа. Кстати, эту же панель вы можете вывести и отдельно с помощью функции GetSaveFileNaamePreview , аналогичной функции GetSaveFileName из библиотеки commdlg.dll.

    Установка драйвера устройства чтения CD ROM

    Устройство чтения CD ROM может использоваться либо для чтения цифровых компакт-дисков, либо для проигрывания звуковых компакт-дисков. И в том, и в другом случае вам необходимо установить драйвер устройства чтения и специальное расширение MS-DOS mscdex.exe.
    Процедура установки драйвера зависит от изготовителя устройства и используемого интерфейсного адаптера. Она описана в документации, которая поставляется вместе с устройством. Например, для устройства MITSUMI , подключенного к звуковому адаптеру Sound Galaxy NX Pro, используется драйвер sgcdm.sys, подключаемый в файле config.sys следующим образом:
    device=c:\sgnxpro\drivers\sgcdm.sys /D:MITSUMI /P:300 /I:12
    Параметр /D определяет имя устройства. Это же самое имя должно быть указано при запуске mscdex.exe. Через параметр /P передается адрес порта ввода/вывода, используемого для работы с устройством, через параметр /I - номер прерывания. Драйверы для других типов CD ROM или интерфейсных адаптеров могут иметь другой набор параметров, однако имя устройства должно задаваться в любом случае.
    Расширение MS-DOS, предназначенное для работы с CD ROM, подключается в файле autoexec.bat следующим образом:
    c:\dos\mscdex.exe /D:MITSUMI /M:20
    Единственный параметр, который должен быть задан обязательно, это параметр /D, определяющий имя устройства. Это имя должно быть задано точно таким же образом, что и в строке параметров драйвера CD ROM. Вы можете загрузить несколько драйверов для нескольких устройств CD ROM, задав каждому устройству свое имя. В этом случае при запуске mscdex.exe следует указать параметр /D несколько раз:
    c:\dos\mscdex.exe /D:CDDRV00 /D:CDDRV01 /M:10 /E /V
    Параметр /M определяет количество блоков памяти, получаемых программой mscdex.exe для буферизации чтения CD ROM.
    Можно потребовать, чтобы программа mscdex.exe была загружена в дополнительную (expanded) память. Для этого надо указать параметр /E.
    Для просмотра информации об использовании программой mscdex.exe оперативной памяти следует указать параметр /V.
    Параметр /L позволяет принудительно задать букву, которая будет использована для обозначения дискового устройства, соответствующего устройству чтения CD ROM. Например, для того чтобы устройство CD ROM обозначалось буквой Z, можно использовать следующий набор параметров:
    c:\dos\mscdex.exe /D:MITSUMI /L:Z

    Установка драйвера звукового адаптера

    Процедура установки драйвера звукового адаптера, предназначенного для Windows, описана в документации, которая поставляется вместе с адаптером. Тем не менее мы рассмотрим некоторые моменты на примере установки драйвера адаптера Sound Galaxy NX Pro.
    После запуска программы установки, которая есть на дистрибутивной дискете, в каталоге sgnxpro/windows появляются следующие файлы:
    DISK1 GALAXY DRV MIDIMAP CFG OEMSETUP INF SGAUX DRV SGOPL3 DRV SGPROFM DRV VSGD 386
    Файл oemsetup.inf предназначен для установки драйверов с помощью стандартного приложения Control Panel.
    Запустите приложение Control Panel и сделайте двойной щелчок левой клавишей мыши по пиктограмме "Drivers". На экране появится диалоговая панель "Drivers", в которой будет отображен список всех установленных драйверов. Нажмите кнопку "Add...". В появившейся диалоговой панели выберите строку "Unlisted or Updated Driver" и нажмите кнопку "OK". Появится диалоговая панель "Install Driver". В этой панели нажмите кнопку "Browse..." и укажите каталог sgnxpro/windows или другой каталог, в котором находятся драйверы звукового адаптера. Затем нажмите кнопку "OK".
    На экран будет выведена диалоговая панель "Add Unlisted or Updated Driver" (рис. 1.4), с помощью которой следует добавить нужные драйверы.
    Установка драйвера звукового адаптера
    Рис. 1.4. Установка драйверов для звукового адаптера
    Вы можете добавить по очереди все драйверы из появившегося списка или только некоторые. Для обеспечения возможности работы со звуком следует добавить первые два драйвера из списка, показанного на рис. 1.4.
    Если вы приобрели другой звуковой адаптер, названия драйверов изменятся, поэтому при установке следует руководствоваться описанием, которое поставляется вместе с драйверами.
    После выбора драйвера на экране появится диалоговая панель настройки параметров (рис. 1.5).
    Установка драйвера звукового адаптера
    Рис. 1.5. Настройка параметров
    С помощью этой диалоговой панели вы должны указать номер прерывания, адрес порта ввода/вывода и номер канала прямого доступа. Эти параметры будут записаны в энергонезависимую память, расположенную в адаптере Sound Galaxy NX Pro. В адаптерах других типов для настройки параметров могут использоваться перемычки или переключатели, расположенные на плате звукового адаптера. Устанавливая параметры, следите за тем, чтобы они не конфликтовали с параметрами другого оборудования компьютера.

    Установка звукового адаптера

    При установке звукового адаптера в компьютер вам нужно выбрать для него адрес на шине ввода/вывода, номер канала прямого доступа и номер прерывания. При этом нужно внимательно следить за тем, чтобы случайно не выбрать для звукового адаптера адрес, канал прямого доступа или прерывание, занятые другими устройствами компьютера. Конкретные рекомендации по установке зависят от типа звукового адаптера и приведены в документации, которая поставляется вместе с адаптером.
    Если на плате звукового адаптера имеется контроллер устройства чтения CD ROM, следует также задать адрес порта ввода/вывода и номер прерывания для этого контроллера.
    Ваш звуковой адаптер может также содержать порт для подключения джойстика (игровой порт). Если в компьютере уже есть один игровой порт (в адаптере принтера, последовательного интерфейса или в адаптере "мультипорт"), этот порт перед установкой звукового адаптера следует отключить во избежание возникновения конфликта на шине ввода/вывода.
    Большинство звуковых адаптеров имеют встроенный музыкальный синтезатор, снабженный последовательным портом ввода/вывода в стандарте MIDI. К этому порту можно подключить внешний музыкальный синтезатор или музыкальную клавиатуру.
    На рис. 1.3 мы схематически показали подключение звукового адаптера к различным устройствам, предназначенным для ввода или вывода звука.
    Установка звукового адаптера
    Рис. 1.3. Подключение звукового адаптера
    Как правило, в любом звуковом адаптере есть один или два входа для подключения микрофона. По своей конструкции микрофоны бывают электродинамические, электретные и конденсаторные. Первые обеспечивают лучшее качество, но стоят дороже электретных. Покупая звуковой адаптер без микрофона, обязательно поинтересуйтесь у продавца, какой тип микрофона можно использовать вместе с этим адаптером. Некоторые адаптеры имеют два отдельных входа, предназначенных для подключения электродинамического и электретного микрофона, соответственно.
    К выходу звуковой платы можно подключать головные телефоны или звуковые колонки.
    Мощность встроенного в адаптер усилителя может составлять порядка 4 Вт, что вполне достаточно, если, конечно, не использовать компьютер для озвучивания дискотеки. Если же вам нужна очень большая мощность, придется подключить внешний усилитель.

    Если вы приобрели звуковой адаптер вместе с колонками, попробуйте заменить их на более мощные, например, такие как 15АС-201 (не забудьте поинтересоваться величиной сопротивления колонки, которое должно быть 4 или 8 Ом, иначе можно повредить усилитель звукового адаптера). Чем больше размер излучающей поверхности, тем выше качество звучания на низких частотах. К сожалению, качество звучания миниатюрных колонок, которые продаются вместе со звуковыми адаптерами, не может удовлетворить потребности взыскательного любителя музыки. Аналогичное замечание касается и микрофона. Если вам нужно высокое качество, используйте студийный микрофон и 16-разрядный звуковой адаптер.

    В звуковом адаптере может быть предусмотрен вход для подключения к такой аппаратуре, как тюнер, проигрыватель звуковых компакт-дисков или магнитофон. На рис. 1.3 этот вход обозначен как "Линия".

    Разъем "Порт MIDI/игровой порт" предназначен для подключения джойстика и внешнего музыкального синтезатора или музыкальной клавиатуры.

    Для плавной регулировки громкости можно использовать регулятор, расположенный на плате звукового адаптера. Однако лучше воспользоваться электронным микшерским пультом, который есть в адаптере и который управляется программно.

    Вы знаете, что в корпусе практически любого персонального компьютера есть динамик, способный издавать звуковые сигналы. Он подключается к основной плате компьютера. Многие программы, в том числе модули инициализации BIOS, операционная система MS-DOS и игровые программы используют этот динамик. Если вы приобрели звуковой адаптер, встроенный динамик вам больше не понадобится. Отключите от него провода, идущие к материнской плате. Один из этих проводов подключен к "земле" компьютера, второй - к выходному усилителю, который находится на основной (материнской) плате компьютера.


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

    Теперь о подключении устройства чтения компакт-дисков CD ROM.

    CD ROM может подключаться либо к звуковому адаптеру, либо к отдельному адаптеру. Очень распространены, например, устройства CD ROM, имеющие интерфейс SCSI. Контроллер CD ROM, расположенный на плате звукового адаптера, рассчитан, как правило, на подключение только некоторых типов CD ROM, поэтому перед покупкой звукового адаптера или устройства чтения CD ROM поинтересуйтесь, будут ли они совместимы. Лучший способ застраховаться от несовместимости - покупать набор, состоящий из звуковой платы и устройства чтения CD ROM или покупать CD ROM вместе с предназначенным для него отдельным контроллером.

    Вместе с устройством чтения CD ROM должны продаваться драйверы, в частности, драйвер для MS-DOS. Специальный драйвер для Windows не требуется, так как Windows имеет собственный драйвер, работающий через расширение MS-DOS, которое Microsoft поставляет в виде резидентной программы mscdex.exe. Эта программа входит в комплект поставки MS-DOS, но для ее работы требуется драйвер, который поставляется только вместе с устройством чтения CD ROM.

    На корпусе устройства чтения CD ROM есть разъем для подключения головных телефонов и регулятор громкости. Разъем не предназначен для подключения колонок, так как встроенный в устройство усилитель имеет небольшую выходную мощность.

    На задней стенке корпуса есть три разъема. Первый разъем нужен для подключения питания, второй - для широкого интерфейсного кабеля, третий - для подключения специального аналогового входа, который есть на звуковом адаптере (рис. 1.3). Этот аналоговый вход, так же как и вход от микрофона, линии для подключения динамика и внешней линии, подключен к микшеру, встроенному в звуковой адаптер.

    Таким образом, обеспечивается возможность как раздельного ввода звуковой информации от перечисленных выше устройств, так и ввода смешанной в миксере информации.Например, вы можете установить в устройство CD ROM обычный звуковой компакт-диск, запустить его проигрывание, а затем записывать музыку с компакт-диска, с наложением речи при помощи микрофона.

    Воспроизведение звуковых данных

    Для воспроизведения звуковых данных на низком уровне после определения возможностей устройства вывода необходимо открыть устройство. Это можно сделать с помощью функции waveOutOpen .
    Функция waveOutOpen
    UINT waveOutOpen( LPHWAVEOUT lphWaveOut, // указатель на идентификатор устройства UINT wDeviceID, // номер открываемого устройства LPWAVEFORMAT lpFormat, // указатель на структуру WAVEFORMAT DWORD dwCallback, // адрес функции обратного вызова // или идентификатор окна DWORD dwCallbackInstance, // данные для функции обратного вызова DWORD dwFlags); // режим открытия устройства
    Параметры функции:
    lphWaveOut
    Дальний указатель на переменную типа HWAVEOUT . В эту переменную будет записан идентификатор устройства вывода, который необходим для выполнения всех операций с устройством. Функция waveOutOpen может быть использована для определения возможности воспроизведения звуковых данных заданного формата (в том числе нестандартного), в этом случае параметр lphWaveOut может иметь значение NULL. Дополнительно в параметре dwFlags следует установить флаг WAVE_FORMAT_QUERY
    wDeviceID
    Через параметр wDeviceID приложение должно передать функции waveOutOpen номер устройства вывода, которое оно собирается открыть или константу WAVE_MAPPER , определенную в файле mmsystem.h.
    В первом случае номер устройства может лежать в пределах от нуля до значения, полученного с помощью функции waveOutGetNumDevs. Напомним, что эта функция возвращает количество устройств, способных воспроизводить звуковые данные, записанные с использованием импульсно-кодовой модуляции.
    Обычно приложение использует константу WAVE_MAPPER, при этом функция waveOutOpen пытается самостоятельно выбрать и открыть устройство вывода, подходящее для проигрывания звуковых данных указанного формата
    lpFormat
    Через параметр lpFormat приложение должно передать функции waveOutOpen адрес заполненной структуры WAVEFORMAT . Эта структура и указатели на нее описаны в файле mmsystem.h:
    typedef struct waveformat_tag { WORD wFormatTag; // тип формата WORD nChannels; // количество каналов (моно или стерео) DWORD nSamplesPerSec; // частота дискретизации DWORD nAvgBytesPerSec; // скорость потока данных WORD nBlockAlign; // выравнивание блока данных } WAVEFORMAT; typedef WAVEFORMAT *PWAVEFORMAT; typedef WAVEFORMAT NEAR *NPWAVEFORMAT; typedef WAVEFORMAT FAR *LPWAVEFORMAT;

    Мы уже рассказывали вам об этой структуре в разделе, посвященном формату wav-файлов. Там вы сможете найти подробное описание полей структуры

    dwCallback

    Через параметр dwCallback вы можете передать функции waveOutOpen адрес функции обратного вызова. Эту функцию будет вызывать драйвер устройства вывода при возникновении событий, имеющих отношение к проигрыванию блока данных. При использовании функции обратного вызова в параметре dwFlags следует установить флаг CALLBACK_FUNCTION .

    Неудобство использования функции обратного вызова заключается в том, что она должна располагаться в фиксированном сегменте dll-библиотеки, так как вызов функции выполняется во время обработки прерывания. Кроме того, если функция обратного вызова использует какие-либо данные, то для их хранения следует либо использовать память из фиксированного сегмента данных, либо заказывать ее из глобальной области памяти с параметрами GMEM_MOVEABLE и GMEM_SHARE с последующей фиксацией при помощи функций GlobalLock и GlobalPageLock. Функция обратного вызова не может использовать никакие функции программного интерфейса Windows за исключением функции PostMessage и функций из dll-библиотеки mmsystem.dll, имеющих отношение к службе времени.

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

    dwCallbackInstance

    Идентификатор данных, который передается в функцию обратного вызова. Не используется совместно с флагом CALLBACK_WINDOW

    dwFlags

    Вы можете указывать в этом поле следующие флаги:

    Флаг Описание
    WAVE_FORMAT_QUERY Функция waveOutOpen вызывается только для проверки возможности использования формата звуковых данных, определенного в структуре WAVEFORMAT, адрес которой передается через параметр lpFormat. Этим способом вы можете проверить, способно ли устройство работать с нестандартным форматом, например, с нестандартной частотой дискретизации
    WAVE_ALLOWSYNC Этот флаг необходимо использовать для открытия синхронного устройства вывода, во время работы которого все приложения блокируются
    CALLBACK_WINDOW Для извещения о наступлении событий используется окно, идентификатор которого передается через параметр dwCallback
    CALLBACK_FUNCTION Для извещения о наступлении событий используется функция обратного вызова, адрес которой передается через параметр dwCallback
    <


    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_BADDEVICEID

    Указан неправильный номер устройства

    MMSYSERR_ALLOCATED

    Это устройство уже открыто

    MMSYSERR_NOMEM

    Для выполнения операции не хватает памяти

    WAVERR_BADFORMAT

    Указанный формат звуковых данных не поддерживается драйвером устройства вывода

    WAVERR_SYNC

    Была выполнена попытка открыть синхронное устройство вывода без использования флага WAVE_ALLOWSYNC

    Как правило, при проигрывании wav-файлов приложение вызывает функцию waveOutOpen два раза. В первый раз она вызывается для проверки возможности проигрывания звуковых данных заданного формата:

    if(waveOutOpen(NULL, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, NULL, 0L, WAVE_FORMAT_QUERY | WAVE_ALLOWSYNC)) { // Формат не поддерживается }

    Если указанный формат поддерживается драйвером, приложение может открыть устройство вывода, например, следующим образом:

    rc = waveOutOpen(&hWaveOut, WAVE_MAPPER, (WAVEFORMAT FAR *)lpwiocb->lpFmt, (UINT)hwnd, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC);

    Такая методика позволяет определить возможность работы с нестандартными форматами.

    Что же касается структуры WAVEFORMAT, то проще всего заполнить ее непосредственно из заголовка проигрываемого wav-файла, как это мы сделали в приложении WAVE (см. ниже).

    После того, как устройство вывода открыто, можно приступать к проигрыванию wav-файла или звуковых данных, взятых из другого источника. Для проигрывания на низком уровне вы должны подготовить и передать драйверу устройства вывода блоки данных, содержащие звуковую информацию. Формат этих данных должен соответствовать указанному при открытии устройства.

    Блоки данных, передаваемые драйверу, должны быть заказаны как глобальные с флагами GMEM_MOVEABLE и GMEM_SHARE. Вы можете заказать один такой блок и переписать в него содержимое wav-файла (как мы это сделали в приложении WAVE), либо использовать очередь или массив блоков, отдавая блоки драйверу по мере необходимости.


    Перед тем как отдать блок драйверу, его надо подготовить при помощи функции waveOutPrepareHeader .

    Функция waveOutPrepareHeader

    UINT waveOutPrepareHeader( HWAVEOUT hWaveOut, // идентификатор устройства LPWAVEHDR lpWaveOutHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR

    Параметры функции:

    hWaveOut

    Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства

    lpWaveOutHdr

    Через параметр lpWaveOutHdr приложение должно передать функции waveOutPrepareHeader адрес заполненной структуры WAVEHDR , описывающей передаваемый блок данных. Эта структура и указатели на нее описаны в файле mmsystem.h:

    typedef struct wavehdr_tag { LPSTR lpData; // адрес блока данных DWORD dwBufferLength; // размер блока данных DWORD dwBytesRecorded; // количество записанных байт // (используется только при записи) DWORD dwUser; // пользовательские данные DWORD dwFlags; // флаги состояния буфера данных DWORD dwLoops; // кратность проигрывания буфера // (используется только при воспроизведении) struct wavehdr_tag far *lpNext; // зарезервировано DWORD reserved; // зарезервировано } WAVEHDR; typedef WAVEHDR *PWAVEHDR; typedef WAVEHDR NEAR *NPWAVEHDR; typedef WAVEHDR FAR *LPWAVEHDR;

    Заказав блок памяти функцией GlobalAlloc с флагами GMEM_MOVEABLE и GMEM_SHARE, вы должны зафиксировать его функцией GlobalLock. Полученный в результате фиксирования адрес блока следует записать в поле lpData структуры WAVEHDR. Размер блока нужно записать в поле dwBufferLength.

    Заметим, что для указания размера блока памяти используется двойное слово, поэтому вы можете использовать блоки очень большого размера. Однако есть ограничение - блок должен поместиться целиком в физическую память, иначе его будет невозможно зафиксировать. Поэтому при необходимости выполнять проигрывание "долгоиграющих" wav-файлов имеет смысл создать два или большее количество блоков, заполняя их из файла попеременно и отдавая драйверу для проигрывания в асинхронном режиме.


    Структура WAVEHDR используется не только для воспроизведения, но и для записи. В этом случае после завершения записи блока в поле dwBytesRecorded будет находиться количество записанных байт звуковых данных. При воспроизведении это поле не используется.

    Через поле dwUser приложение может передать функции обратного вызова или обработчику сообщения для данного устройства вывода любую дополнительную информацию

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

    Флаги Описание
    WHDR_DONE Работа с буфером данных закончена. Он был успешно проигран или записан, после чего драйвер вернул буфер приложению
    WHDR_BEGINLOOP Данный буфер является первым в цикле. Флаг используется только при воспроизведении. Если необходимо проиграть в цикле только один блок, он должен быть отмечен и флагом WHDR_BEGINLOOP, и флагом WHDR_ENDLOOP
    WHDR_ENDLOOP Данный буфер является последним в цикле. Флаг используется только при воспроизведении
    WHDR_PREPARED Буфер подготовлен для воспроизведения функцией waveOutPrepareHeader или для записи функцией waveInPrepareHeader
    Приложение может указать драйверу, что блок необходимо проиграть несколько раз подряд. Для этого следует заполнить поле dwLoops, указав в нем, сколько раз нужно проиграть буфер.

    Поля lpNext и reserved зарезервированы и не должны использоваться приложением.

    wSize

    Поле wSize должно содержать размер структуры WAVEHDR

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_NOMEM

    Для выполнения операции не хватает памяти

    После того, как блок памяти обработан функцией waveOutPrepareHeader, его можно проиграть, вызвав функцию waveOutWrite .

    Функция waveOutWrite

    UINT waveOutWrite( HWAVEOUT hWaveOut, // идентификатор устройства LPWAVEHDR lpWaveOutHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR


    Параметры функции:

    hWaveOut

    Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства

    lpWaveOutHdr

    Через параметр lpWaveOutHdr приложение должно передать функции адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных

    wSize

    Поле wSize должно содержать размер структуры WAVEHDR

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_UNPREPARED

    Переданный блок данных не был подготовлен функцией waveOutPrepareHeader

    Сразу после вызова функции waveOutWrite начинается проигрывание блока.

    Если блок будет проигран до конца или если проигрывание блока будет остановлено, функция окна, идентификатор которой был указан при открытии устройства через параметр dwCallback, получит сообщение MM_WOM_DONE .

    Через параметр wParam сообщения MM_WOM_DONE передается идентификатор устройства, которое было использовано для проигрывания блока. Параметр lParam содержит адрес структуры WAVEHDR, соответствующей проигранному блоку.

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

    После того как приложение получило сообщение MM_WOM_DONE, оно должно передать блок функции waveOutUnprepareHeader, затем разблокировать его функцией GlobalUnlock и освободить (если данный блок памяти больше не нужен) функцией GlobalFree.

    Приведем формат вызова функции waveOutUnprepareHeader .

    Функция waveOutUnprepareHeader

    UINT waveOutUnprepareHeader( HWAVEOUT hWaveOut, // идентификатор устройства LPWAVEHDR lpWaveOutHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR

    Параметры функции:

    hWaveOut

    Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства

    lpWaveOutHdr

    Адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных

    wSize

    Поле wSize должно содержать размер структуры WAVEHDR


    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_STILLPLAYING

    Указанный блок все еще находится в очереди для проигрывания

    После завершения работы с устройством его необходимо закрыть, вызвав функцию waveOutClose . Через единственный параметр этой функции необходимо передать идентификатор закрываемого устройства вывода.

    Функция waveOutClose

    UINT waveOutClose( HWAVEOUT hWaveOut); // идентификатор устройства

    Параметры функции:

    hWaveOut

    Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_STILLPLAYING

    Очередь данного устройства еще содержит блоки для проигрывания

    Ввод и вывод звука

    Для ввода звука используется устройство с названием аналого-цифровой преобразователь . Оно периодически преобразует аналоговый сигнал, поступающий от микрофона, магнитофона или другого источника сигнала, в последовательность чисел, представляющих собой мгновенные значения амплитуды сигнала (рис. 1.1).
    Ввод и вывод звука
    Рис. 1.1. Ввод звука
    Этот способ преобразования называется импульсно-кодовой модуляцией . В отечественной литературе вы можете встретить аббревиатуру ИКМ, однако в документации, которая поставляется вместе с SDK для Windows, для обозначения импульсно-кодовой модуляции используется аббревиатура PCM - Pulse Code Modulation .
    Насколько часто нужно выполнять преобразование?
    Это зависит от ширины спектра входного сигнала. Как известно, человеческое ухо воспринимает звуковые колебания в диапазоне частот примерно от 20 Гц до 16-20 Кгц, поэтому для передачи звука без искажений необходимо обеспечить преобразование сигнала с шириной спектра 20 Кгц. Существует теорема (носящая у нас имя Котельникова), согласно которой частота дискретизации сигнала должна вдвое превышать максимальное значение частоты сигнала. Следовательно, для передачи всего спектра звуковых сигналов, воспринимаемых человеком, необходимо выполнять преобразования аналогового сигнала с частотой не менее 40 Кгц.
    Другой вопрос - с какой точностью следует преобразовывать аналоговый сигнал?
    Казалось бы, чем точнее - тем лучше. Однако не следует забывать, что чем больше точность, тем больше двоичных разрядов используется для представления амплитуды сигнала и следовательно, тем больше памяти нужно для хранения записанной фонограммы. В хороших цифровых студийных магнитофонах для представления амплитуды сигнала используется не менее 16 разрядов, или 2 байта. Нетрудно подсчитать, что в этом случае при частоте дискретизации 40 Кгц для хранения одной минуты монофонической звуковой информации потребуется примерно 4,5 Мбайт памяти. Для стереофонической записи эту величину следует умножить на два.
    Если вам нужно записывать только речь, можно ограничить ширину спектра сигнала величиной примерно 2,2 - 3 Кгц, что соответствует частоте дискретизации 4,4 - 6 Кгц. Кроме того, для представления мгновенной амплитуды сигнала вполне достаточно одного байта памяти. В этом случае для хранения одной минуты речи нужно 257 Кбайт памяти.
    Для вывода звука используется цифро-аналоговый преобразователь (рис. 1.2), который может быть одноканальный (монофонический) или двухканальный (стереофонический).
    Ввод и вывод звука
    Рис. 1.2. Вывод звука
    На вход цифро-аналогового преобразователя поступает цифровой код, полученный от аналого-цифрового преобразователя и записанный в память компьютера. Разумеется, темп поступления кода должен соответствовать частоте дискретизации, в противном случае при воспроизведении неизбежны частотные искажения.
    Для повышения производительности звуковой адаптер работает в режиме прямого доступа к памяти с использованием прерываний.

    Выбор и подключение звукового адаптера

    Для компьютера IBM PC и совместимых с ним создано много звуковых адаптеров. Это Sound Blaster, Sound Galaxy, Sound Vision, Microsoft Sound System и т. д. Они отличаются друг от друга по составу аппаратных средств, возможностям и цене. Как правило, все эти адаптеры предназначены для шины ISA, хотя есть устройства, подключаемые к принтерному или к последовательному порту компьютера. Выпускаются блокнотные компьютеры, оснащенные встроенным звуковым адаптером (и даже встроенным устройством чтения компакт-дисков).
    Мы рекомендуем вам приобрести стереофонический адаптер для шины ISA, содержащий дополнительно музыкальный синтезатор, например, адаптер Sound Galaxy NX Pro. Если вы ограничены в средствах, купите 8-битовый адаптер, который обеспечит вполне приемлемое качество звучания. Если же вам нужно студийное качество, воспользуйтесь 16-битовым адаптером.
    Особое внимание при покупке обратите на совместимость адаптера с операционной системой Windows версии 3.1. В комплект поставки должны входить драйверы для этой операционной системы. Учтите, что в состав Windows версии 3.1 входят драйверы не для всех звуковых адаптеров (есть драйверы для адаптеров Sound Blaster и AdLib). Другой важный критерий - аппаратная совместимость с адаптером Sound Blaster. Этот адаптер стал своеобразным стандартом для подобных устройств. Совместимость с Sound Blaster нужна для того, чтобы вы смогли использовать адаптер с играми, разработанными для MS-DOS, такими, как DOOM или Wolf3D, или с другими аналогичными программами.
    Иногда на плате звукового адаптера устанавливают интерфейс устройства чтения компакт-дисков CD ROM. Если вы планируете приобрести сразу и звуковой адаптер, и устройство чтения CD ROM, лучше остановить свой выбор на одном из комплектов, добавляющих в компьютер возможности мультимедиа. В этот комплект, как правило, входит звуковой адаптер, микрофон, колонки, соединительные кабели, устройство чтения компакт-дисков CD ROM, набор драйверов для MS-DOS и Windows, а также несколько демонстрационных компакт-дисков.
    Стоимость такого набора может лежать в пределах 290-400 долларов, что не очень много.

    При выборе типа устройства чтения CD ROM следует принимать во внимание формат данных, скорость передачи данных и время позиционирования. Формат данных должен соответствовать стандарту ISO 9660 (можно узнать из документации, которая поставляется вместе с устройством). Устройство чтения с одинарной скоростью обеспечивает скорость передачи данных, равную 150 Кбайт в секунду при среднем времени позиционирования порядка 250 миллисекунд. Есть также устройства с двойной скоростью (300 Кбайт в секунду). Ожидается появление устройств чтения CD ROM с еще большей скоростью передачи данных.

    Скорость передачи данных критична для приложений, читающих с компакт-дисков видеофильмы и отображающие их на экране компьютера. Для проигрывания видеофильмов лучше использовать устройства с двойной скоростью, хотя в крайнем случае подойдут и устройства с одинарной скоростью. Если вы работаете с базами данных, записанных на компакт-дисках, основной вклад в задержку работы вносит процесс позиционирования при поиске и выборке информации, поэтому в этом случае двухкратное увеличение скорости передачи данных не приведет к двухкратному увеличению скорости работы.

    Загрузка файла или выбор устройства

    При создании окна MCI вы можете указать имя устройства или файл мультимедиа, передав указатель на соответствующую текстовую строку через последний параметр функции MCIWndCreate. Можно использовать и другие способы.
    Макрокоманда MCIWndOpen позволяет для созданного ранее окна открыть устройство или файл, например:
    MCIWndOpen(hwnd, "push.wav", MCIWNDOPENF_NEW);
    Через первый параметр передается идентификатор окна MCI, через второй - указатель на строку, содержащую имя устройства или файла. В третьем параметре при создании нового файла нужно указать флаг MCIWNDOPENF_NEW .
    Напомним, что закрыть устройство или файл можно с помощью макрокоманды MCIWndClose .
    Функция MCIWndOpenDialog позволяет вывести на экран диалоговую панель, с помощью которой пользователь может выбрать файл и загрузить его в окно MCI. В качестве единственного параметра этой функции следует передать идентификатор окна MCI. Функция возвращает значение 0L (размером в двойное слово) в случае успеха или код ошибки.
    Для выбора файла можно также использовать функцию GetOpenFileNamePreview , входящую в программный интерфейс Video for Windows и аналогичную функции GetOpenFileName из библиотеки commdlg.dll . Диалоговая панель, появляющаяся на экране при вызове этой функции, содержит окно для предварительного прослушивания или просмотра файла (рис. 5.15).
    Новый файл создается макрокомандой MCIWndNew , которой в качестве первого параметра следует передать идентификатор окна MCI, а в качестве второго - указатель на текстовую строку, содержащей имя устройства. Пример использования этой макрокоманды ести в приложении MCIWNDC, которое мы скоро рассмотрим.

    Закрытие файла

    Если приложение открыло файл функцией mmioOpen, после завершения работы с ним оно должно закрыть этот файл функцией mmioClose .
    Функция mmioClose
    UINT mmioClose( HMMIO hmmio, // идентификатор открытого файла UINT wFlags); // флаги для операции закрытия файла
    Параметры функции:
    hmmio
    Идентификатор открытого файла, полученный с помощью функции mmioOpen
    wFlags
    Флаги, определяющие режим закрытия файла. Можно указать флаг MMIO_FHOPEN , при этом функция mmioClose закроет файл, открытый средствами MS-DOS
    Возвращаемое значение:
    При успехе возвращается нулевое значение. В противном случае - код ошибки

    Запись и воспроизведение видео

    5.1.
    5.2.
    5.3.
    Эта глава посвящена системе Microsoft Video for Windows версии 1.1 (рис. 5.1), которая поставляется отдельно в качестве расширения для Microsoft Windows и Microsoft Windows for Workgroups. Новые версии операционной системы Windows, например, Windows NT версии3.5, поставляются со встроенной системой Video for Windows Runtime, позволяющей проигрывать avi-файлы и другие мультимедиа-файлы.
    Запись и воспроизведение видео
    Рис. 5.1. Microsoft Video for Windows версии 1.1
    Video for Windows предоставляет в распоряжение пользователей новую технологию работы со звуковой информацией, видеоинформацией и другими типами данных. С помощью Video for Windows пользователь может создавать файлы с расширением имени avi, содержащие одновременно несколько потоков данных. Например, в файле может храниться видеофильм со звуковым сопровождением на нескольких национальных языках. Во время воспроизведения пользователь может выбрать нужный ему язык, указав соответствующий поток звуковых данных.
    В качестве источника видео можно использовать видеокамеру, подключенную к компьютеру через видеоадаптер. В этом случае для создания avi-файла следует использовать приложение VidCap . Можно также создать видеоролик из отдельных кадров, сохраненных в виде файлов в одном из нескольких наиболее популярных графических форматов, из анимационных файлов fli и flc в формате Autodesk Animation . В составе Video for Windows поставляется приложение Screen Capture, с помощью которого можно создавать демонстрационные ролики, "снимая" изменения на экране компьютера.
    Что же касается звукового сопровождения, то есть возможность одновременной записи звука и видео, причем для ввода звука в этом случае используется обычный звуковой адаптер. Можно добавить звук позже в уже готовый avi-файл, "озвучив" его после съемки и монтажа. Для этого следует воспользоваться приложением VidEdit . Просматривать avi-файлы можно с помощью приложения Media Player .
    Для создания приложений, работающих с Video for Windows, вам следует приобрести Video for Windows Development Kit версии 1.1 (рис. 5.2), а также транслятор Microsoft C++ версии 7.0 или Microsoft Visual C++ версий 1.0 или 1.5.
    Запись и воспроизведение видео
    Рис. 5.2. Video for Windows Development Kit версии 1.1
    В состав Video for Windows Development Kit входит документация в виде гипертекста (Programmer Guide), набор библиотек и include-файлов, большое количество примеров приложений. Вместе с Video for Windows и Video for Windows Development Kit поставляется Video for Windows Runtime , который вы можете распространять вместе с разработанными вами приложениями или созданными вами avi-файлами. Кроме необходимых драйверов и dll-библиотек в состав Video for Windows Runtime входит приложение Media Player.

    Запись и воспроизведение звука

    2.1.
    2.2.
    2.3.
    2.4.
    В этой главе мы расскажем вам о том, как приложения Windows могут записывать и воспроизводить звуковые фрагменты, используя программный интерфейс системы мультимедиа различных уровней. Вы научитесь создавать "звучащие" приложения, и приложения, которые могут записывать звук, познакомитесь со структурой wav-файлов, предназначенных для хранения записанных фонограмм.
    Как мы уже говорили, для работы со звуковым адаптером в среде операционной системы Windows вам не потребуется программировать на уровне портов ввода/вывода, прерываний и каналов прямого доступа. Весь необходимый интерфейс (высокого или низкого уровня) предоставляется приложению DLL-библиотекой mmsystem.dll . Эту библиотеку можно рассматривать как расширение Windows для обеспечения возможности работы с мультимедиа.
    Библиотека mmsystem.dll поставляется в составе Windows версии 3.1 (версия 3.0 не могла работать с мультимедиа, однако можно было приобрести изделие Microsoft Multimedia Extension, содержащее эту библиотеку и приложения, предназначенные для работы со звуком). Все функции, входящие в библиотеку mmsystem.dll, описаны в файле mmsystem.h , который поставляется со всеми системами разработки приложений для Windows и находится в каталоге include вместе с файлом windows.h.
    Что же содержится в библиотеке mmsystem.dll?
    В этой библиотеке определены функции двух уровней: функции низкого уровня (Low-Level Functions ) и функции высокого уровня, представляющих собой интерфейс управления средой MCI (Media Control Interface ).
    Функции низкого уровня работают непосредственно с драйверами устройств ввода/вывода, такими, как драйверы звукового адаптера, джойстика или устройства ввода/вывода MIDI .
    Функции интерфейса MCI работают с драйверами устройств MCI (например, драйверами устройств чтения компакт-дисков или лазерных видеодисков) и вызывают функции низкого уровня.
    В любом случае для работы с устройствами мультимедиа приложение должно вызывать ту или иную функцию, определенную в библиотеке mmsystem.dll, вне зависимости от уровня интерфейса.

    Что же касается драйверов звуковых устройств, то можно выделить четыре типа таких драйверов.

    Драйвер для ввода звука (Waveform Input Driver )

    Драйвер для вывода звука (Waveform Output Driver )

    Драйвер для ввода музыки в стандарте MIDI (MIDI Input Driver )

    Драйвер для вывода музыки в стандарте MIDI (MIDI Output Driver )

    Все эти драйверы поставляются вместе со звуковым адаптером и устанавливаются после установки операционной системы Windows. В зависимости от типа звукового адаптера состав драйверов может изменяться (например, могут отсутствовать драйверы для работы с MIDI).

    Вы можете также приобрести звуковой драйвер для работы с динамиком, встроенным в корпус компьютера (Sound Driver for PC Speaker ). В комплект поставки входят два файла - speaker.drv и oemsetup.inf . Этот драйвер можно найти в библиотеке дополнительных драйверов для Windows, которая называется Windows Driver Library , или на одной из электронных досок объявлений BBS.

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

    Запись с помощью видеоадаптера

    Для того чтобы создавать полноценные видеофрагменты, вам следует приобрести специальный видеоадаптер для ввода видеосигнала. В продаже есть два принципиально разных типа таких адаптеров. Простейшие видеоадаптеры, которые мы отнесем к первому типу, позволяют записывать лишь отдельные кадры. В некоторых случаях этого достаточно (например, вы сможете снять "пластилиновый" мультфильм, составив его из множества отдельных кадров). Видеоадаптеры второго типа способны записывать поток видеоинформации в реальном масштабе времени, причем одновременно выполняется компрессия данных.
    Приобретая видеоадаптер поинтересуйтесь, сможете ли вы с помощью этого адаптера записывать видео в реальном масштабе времени с компрессией данных, или же вам будет доступна только запись отдельных кадров. Кроме того, очень важно, чтобы вместе с видеоадаптером поставлялся драйвер для Windows версии 3.1, способный работать вместе с Microsoft Video for Windows версии 1.1. Только тогда вы сможете без проблем записывать avi-файлы, подключив к видеоадаптеру лазерный проигрыватель видеодисков, видеокамеру или видеомагнитофон. В противном случае вам придется создавать avi-файл из отдельных кадров, что не всегда приемлимо и отнимает очень много времени.
    Для создания avi-файла с помощью видеоадаптера вам следует использовать приложение VidCap , которое поставляется вместе с Video for Windows. Это приложение способно работать в трех режимах: в режиме покадровой записи, в автоматическом режиме и в автоматическом режиме с автоматическим же управлением лазерным проигрывателем видеодисков или видеомагнитофоном.
    Первый режим удобен в тех случаях, когда ваш видеоадаптер не способен записывать видео в реальном масштабе времени или когда вам требуется записать только отдельные кадры видео.
    Второй режим удобен для записи видеофильмов непосредственно в avi-файл. Учтите, что размер созданного файла может достигать в зависимости от продолжительности записи и метода компрессии десятков и сотен мегабайт, поэтому подготовьте быстродействующий диск соответствующего размера. Важное значение имеет также производительность процессора, поэтому компьютер с процессором i386 едва ли подойдет для записи видео в реальном масштабе времени.
    Третий режим доступен в том случае, если источник видеосигнала (лазерный проигрыватель или видеомагнитофон) имеет возможность дистанционного управления от компьютера с использованием интерфейса MCI. В этом режиме приложение VidCap способно управлять устройством и позиционировать носитель данных.
    Если вас по каким-либо причинам не устраивают возможности приложения VidCap, вы можете создать собственное при помощи Video for Windows Development Kit на базе окна класса AVICap . Примеры приложений, выполняющих запись видео (которые называются Cap Test и CapCPP ), поставляются с исходными текстами в составе Video for Windows Development Kit.

    Запись в avi-файл содержимого области экрана

    В состав Video for Windows входит приложение Screen Capture , с помощью которого вы можете создавать демонстрационные и рекламные ролики, записывая в avi-файл изображение любой области экрана.
    Запустите приложение Screen Capture. Оно появится на экране в виде пиктограммы. Установите на эту пиктограмму курсор и сделайте щелчок левой клавишей мыши. Появится меню (рис. 5.8).
    Запись в avi-файл содержимого области экрана
    Рис. 5.8. Меню приложения Screen Capture
    Выберите строку "Set Capture File..." и с помощью появившейся диалоговой панели выберите avi-файл, в который будет записан видеоролик. Затем нужно указать область экрана, содержимое которой будет записано в avi-файл. Так как производительность компьютера может оказаться недостаточной для записи крупноформатного видео, в документации рекомендуется использовать область размером не больше чем 320 х 240 пикселов.
    Затем выберите строку "Preferences..." и задайте режимы работы приложения (рис. 5.9).
    Запись в avi-файл содержимого области экрана
    Рис. 5.9. Диалоговая панель "Preferences"
    В поле "Frame Rate" укажите частоту кадров. Если вместе с записью видео нужно выполнять запись звуковых данных, включите переключатель "Capture Audio" и укажите нужный формат звуковых данных, нажав на кнопку "Change...". По умолчанию для остановки процесса записи используется клавиша . Вы можете заменить ее на другую в поле "Stop Recording Key".
    Для запуска процесса записи выберите строку "Capture!" в меню приложения Screen Capture. Начнется запись видео и, возможно, звуковых данных.

    Запись в файл

    Для записи в файл, открытый при помощи функции mmioOpen, следует использовать функцию mmioWrite . Эта функция позволяет за один вызов записать в файл блок данных размером, большим 64 Кбайт. После записи выполняется перемещение текущей позиции вперед на количество записанных байт.
    Функция mmioWrite
    LONG mmioWrite( HMMIO hmmio, // идентификатор открытого файла HPSTR hpBuff, // указатель на буфер с данными LONG dwBytes); // размер буфера
    Параметры функции:
    hmmio
    Идентификатор открытого файла, полученный с помощью функции mmioOpen
    hpBuff
    Указатель типа huge на буфер, содержимое которого будет записано в файл
    dwBytes
    Размер буфера
    Возвращаемое значение:
    Возвращается количество записанных байт данных или -1 при возникновении ошибки

    Запись звуковых данных

    Процесс записи похож на процесс воспроизведения.
    Вначале необходимо открыть устройство записи, вызвав функцию waveInOpen :
    Функция waveInOpen
    UINT waveInOpen( LPHWAVEIN lphWaveIn, // указатель на идентификатор устройства UINT wDeviceID, // номер открываемого устройства LPWAVEFORMAT lpFormat, // указатель на структуру WAVEFORMAT DWORD dwCallback, // адрес функции обратного вызова // или идентификатор окна DWORD dwCallbackInstance, // данные для функции обратного вызова DWORD dwFlags); // режим открытия устройства
    Параметры функции:
    lphWaveOut
    Дальний указатель на переменную типа HWAVEIN . В эту переменную будет записан идентификатор устройства ввода, который необходим для выполнения всех операций с устройством. Функция waveOutOpen может быть использована для определения возможности записи звуковых данных в заданном формате (например, нестандартном), в этом случае параметр lphWaveIn может иметь значение NULL. Дополнительно в параметре dwFlags следует установить флаг WAVE_FORMAT_QUERY
    wDeviceID
    Через параметр wDeviceID приложение должно передать функции waveInOpen номер устройства ввода, которое оно собирается открыть или константу WAVE_MAPPER , определенную в файле mmsystem.h.
    В первом случае номер устройства может лежать в пределах от нуля до значения, полученного с помощью функции waveInGetNumDevs.
    Если приложение использует константу WAVE_MAPPER, функция waveInOpen пытается самостоятельно выбрать и открыть устройство вывода, подходящее для записи звуковых данных в указанном формате
    lpFormat
    Через параметр lpFormat приложение должно передать функции waveInOpen адрес заполненной структуры WAVEFORMAT. Мы уже рассказывали вам об этой структуре в предыдущем разделе.
    dwCallback
    Через параметр dwCallback вы можете передать функции waveInOpen адрес функции обратного вызова. Эту функцию будет вызывать драйвер устройства ввода при возникновении событий, имеющих отношение к записи блока данных. При использовании функции обратного вызова в параметре dwFlags следует установить флаг CALLBACK_FUNCTION.

    Можно использовать другой способ извещения приложения о возникновении события, который заключается в посылке сообщений функции окна. Для этого параметр dwCallback должен содержать идентификатор окна. Кроме этого, в параметре dwFlags следует установить флаг CALLBACK_WINDOW

    dwCallbackInstance

    Идентификатор данных, который передается в функцию обратного вызова. Не используется совместно с флагом CALLBACK_WINDOW

    dwFlags

    Вы можете указывать в этом поле следующие флаги:

    Флаг Описание
    WAVE_FORMAT_QUERY Функция waveInOpen вызывается только для проверки возможности использования формата звуковых данных, определенного в структуре WAVEFORMAT
    WAVE_ALLOWSYNC Этот флаг необходимо использовать для открытия синхронного устройства ввода, во время работы которого все приложения блокируются
    CALLBACK_WINDOW Для извещения о наступлении событий используется окно, идентификатор которого передается через параметр dwCallback
    CALLBACK_FUNCTION Для извещения о наступлении событий используется функция обратного вызова, адрес которой передается через параметр dwCallback
    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_NODRIVER

    В системе нет нужного для работы с устройством ввода драйвера

    MMSYSERR_BADDEVICEID

    Указан неправильный номер устройства

    MMSYSERR_ALLOCATED

    Это устройство уже открыто

    MMSYSERR_NOMEM

    Для выполнения операции не хватает памяти

    WAVERR_BADFORMAT

    Указанный формат звуковых данных не поддерживается драйвером устройства ввода

    WAVERR_SYNC

    Была выполнена попытка открыть синхронное устройство ввода без использования флага WAVE_ALLOWSYNC

    После открытия устройства ввода необходимо подготовить один или несколько блоков памяти, в который (или которые) будет записана введенная звуковая информация. Требования к блокам памяти, используемым для записи, такие же, как и требования к блокам памяти, используемым для воспроизведения. Они должны быть заказаны как глобальные с флагами GMEM_MOVEABLE и GMEM_SHARE.


    Вы можете заказать один блок (как мы это сделали в приложении WAVE), либо использовать очередь или массив блоков, отдавая блоки драйверу по мере необходимости, переписывая каждый раз содержимое записанного блока в wav-файл.

    Перед тем как отдать блок драйверу, его, как и в процессе воспроизведения, надо подготовить, для чего следует воспользоваться функцией waveInPrepareHeader .

    Функция waveInPrepareHeader

    UINT waveInPrepareHeader( HWAVEIN hWaveIn, // идентификатор устройства LPWAVEHDR lpWaveInHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR

    Параметры функции:

    hWaveIn

    Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства

    lpWaveInHdr

    Через параметр lpWaveInHdr приложение должно передать функции waveInPrepareHeader адрес заполненной структуры WAVEHDR, описывающей блок данных, в который будет записана введенная звуковая информация. Формат этой структуры был описан в предыдущем разделе.

    wSize

    Поле wSize должно содержать размер структуры WAVEHDR

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_NOMEM

    Для выполнения операции не хватает памяти

    Подготовленный блок памяти следует передать драйверу устройства ввода, вызвав функцию waveInAddBuffer :

    Функция waveInAddBuffer

    UINT waveInAddBuffer( HWAVEIN hWaveIn, // идентификатор устройства LPWAVEHDR lpWaveInHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR

    Параметры функции:

    hWaveIn

    Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства

    lpWaveInHdr

    Через параметр lpWaveInHdr приложение должно передать функции адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных

    wSize

    Поле wSize должно содержать размер структуры WAVEHDR

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение.


    В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_UNPREPARED

    Переданный блок данных не был подготовлен функцией waveOutPrepareHeader

    Для того чтобы устройство ввода могло приступить к записи, его надо запустить, вызвав функцию waveInStart :

    Функция waveInStart

    UINT waveInStart(HWAVEIN hWaveIn); // идентификатор устройства

    Параметры функции:

    hWaveIn

    Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    Запись будет продолжаться до тех пор, пока не будет записан весь буфер или пока устройство ввода не будет остановлено функцией waveInStop :

    Функция waveInStop

    UINT waveInStop(HWAVEIN hWaveIn); // идентификатор устройства

    Параметры функции:

    hWaveIn

    Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    Если блок записан до конца или если запись блока остановлена, функция окна, идентификатор которой был указан при открытии устройства через параметр dwCallback, получит сообщение MM_WIM_DONE .

    Через параметр wParam сообщения MM_WIM_DONE передается идентификатор устройства, которое было использовано для записи блока. Параметр lParam содержит адрес структуры WAVEHDR, соответствующей записанному блоку.

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

    После того как приложение получило сообщение MM_WIM_DONE, оно должно передать блок функции waveInUnprepareHeader, затем разблокировать его функцией GlobalUnlock и при необходимости освободить функцией GlobalFree.

    Приведем формат вызова функции waveInUnprepareHeader .


    Функция waveInUnprepareHeader

    UINT waveOutUnprepareHeader( HWAVEIN hWaveIn, // идентификатор устройства LPWAVEHDR lpWaveInHdr, // указатель на структуру WAVEHDR UINT wSize); // размер структуры WAVEHDR

    Параметры функции:

    hWaveIn

    Идентификатор устройства вывода, полученный от функции waveInOpen при открытии устройства

    lpWaveOutHdr

    Адрес заполненной структуры WAVEHDR, которая соответствует подготовленному блоку данных

    wSize

    Параметр wSize должно содержать размер структуры WAVEHDR

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_STILLPLAYING

    Указанный блок все еще находится в очереди

    После завершения работы с устройством ввода его необходимо закрыть, вызвав функцию waveInClose . Через параметр этой функции необходимо передать идентификатор закрываемого устройства ввода.

    Функция waveInClose

    UINT waveInClose( HWAVEIN hWaveIn); // идентификатор устройства

    Параметры функции:

    hWaveIn

    Идентификатор устройства ввода, полученный от функции waveInOpen при открытии устройства

    Возвращаемое значение:

    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:

    MMSYSERR_INVALHANDLE

    Указан неправильный идентификатор устройства

    MMSYSERR_STILLPLAYING

    Очередь данного устройства еще содержит блоки для записи

    Запуск устройства

    Для запуска устройства ввода используется рассмотренная нами ранее функция waveInStart . Если же нужно продолжить работу приостановленного устройства вывода, следует вызвать функцию waveOutRestart :
    Функция waveOutRestart
    UINT waveOutRestart( HWAVEOUT hWaveOut); // идентификатор устройства вывода
    Параметры функции:
    hWaveOut
    Идентификатор устройства вывода, полученный от функции waveOutOpen при открытии устройства
    Возвращаемое значение:
    При нормальном завершении возвращается нулевое значение. В противном случае возвращается код ошибки:
    MMSYSERR_INVALHANDLE
    Указан неправильный идентификатор устройства

    Знакомство с мультимедиа

    1.1.
    1.2.
    1.3.
    1.4.
    С тех пор как появились первые компьютеры, программисты и разработчики аппаратуры предпринимали многочисленные попытки научить их работать со звуком. Идея заставить компьютер разговаривать и понимать речь витает в воздухе, однако проблема распознавания речи далека от своего полного решения. Тем не менее, современные персональные компьютеры без особого труда превращают звук в цифры и наоборот.
    Компьютеры Macintosh фирмы Apple имеют стандартное устройство, предназначенное для работы со звуком, для IBM PC и совместимых с ним персональных компьютеров нужно приобрести недорогой звуковой адаптер. С помощью встроенного или установленного дополнительно адаптера можно работать с монофонической или стереофонической звуковой информацией. Созданы многочисленные адаптеры, предназначенные для ввода и вывода видеоинформации, поступающей от видеокамеры, видеомагнитофона или аналогичного устройства.

    

        Сайт: Аннимация - Видео - Графика