Сегодня я расскажу, как можно преобразовывать данные из одного формата в другой. Например, ты хочешь сократь размер звукового файла, за счёт понижения дискретизации, для этого тебе понадобится сегодняшние функции. Так что готовся, инфа очень интересная и в инете почти не освещённая.
Все описываемые сегодня функции относятся к АСМ драйверам. Если в компе есть звуковуха и она проинсталлирована, то драйверы присутствуют. Только заголовочных файлов для них нет. Вместе с сегодняшними исходниками идёт этот заголовочный файл для Delphi и называется он msacm.pas. Так что исходник тебе придётся качать в любом случае (он как всегда не большой).
При преобразовании данных используется ACM-Manager, который делает доступ к данным универсальным. Давай перейдём к рассмотрению функций этого менеджера и в процессе познакомимся с его работой.
Первая функция самоя простая - acmGetVersion - которая возвращает версию менеджера. Функция выглядит так:
Для С++
DWORD acmGetVersion(VOID);
Для Delphi
function acmGetVersion : DWORD; stdcall;
Никаких параметров. Функция только возвращает два байта. Номер возвращается в формате ВВННСССС.
ВВ - верхний номер версии.
HH - нижний номер версии.
СССС - номер сборки
Если номер сборки отличен от нуля, то это отладочная версия. Если он равен нулю, то это уже окончательная версия.
Для получения информации о параметрах АСМ менеджера используется функция acmMetrics
Для С++
MMRESULT acmMetrics(
HACMOBJ hao,
UINT uMetric,
LPVOID pMetric
);
Для Delphi
function acmMetrics(
hao : HACMOBJ;
uMetric : UINT;
var pMetric
) : MMRESULT;
Вот параметры функции:
hao - объект, о котором надо получить инфу. Ножет быть равен 0.
uMetric - этот параметр может принимать значения:
ACM_METRIC_COUNT_DRIVERS - количество АСМ драйверов установленных в системе.
ACM_METRIC_COUNT_CODECS - количество установленных кодеков.
ACM_METRIC_COUNT_CONVERTERS - количество установленных конверторов из одного формата в другой.
ACM_METRIC_COUNT_FILTERS - количество установленных фильтров.
ACM_METRIC_COUNT_DISABLED - количество отключённых драйверов.
ACM_METRIC_COUNT_HARDWARE - количество аппаратных драйверов.
ACM_METRIC_COUNT_LOCAL_DRIVERS - количество локальных АСМ драйверов установленных в системе.
ACM_METRIC_COUNT_LOCAL_CODECS - количество локальных установленных кодеков.
ACM_METRIC_COUNT_LOCAL_CONVERTERS - количество установленных локальных конверторов из одного формата в другой.
ACM_METRIC_COUNT_LOCAL_FILTERS - количество локальных установленных фильтров.
ACM_METRIC_COUNT_LOCAL_DISABLED - количество отключённых локальных драйверов.
ACM_METRIC_HARDWARE_WAVE_INPUT - идентификатор драйвера устройства ввода, с которым связан ACM драйвер.
ACM_METRIC_HARDWARE_WAVE_OUTPUT - идентификатор драйвера устройства вывода, с которым связан ACM драйвер.
ACM_METRIC_MAX_SIZE_FORMAT - максимальный размер структуры WAVEFORMATEX.
ACM_METRIC_MAX_SIZE_FILTER - максимальный размер структуры WAVEFILTER.
Для С++
MMRESULT acmDriverOpen(
LPHACMDRIVER phad, //Сюда будет помещён указатель на экземпляр драйвера
HACMDRIVERID hadid,//Идентификатор открываемого драйвера
DWORD fdwOpen //Зарезервировано, должно быть 0
);
Для Delphi
function acmDriverOpen(
var phad : HACMDRIVER; //Сюда будет помещён указатель экземпляра драйвера
hadid : HACMDRIVERID; //Идентификатор открываемого драйвера
fdwOpen : DWORD //Зарезервировано, должно быть 0
) : MMRESULT; stdcall;
Для закрытия драйвера используется функция acmDriverClose:
Для С++
MMRESULT acmDriverClose(
HACMDRIVER had, //Указатель на устройство полученный при открытии
DWORD fdwClose //Зарезервировано. Должен быть ноль
);
Для Delphi
function acmDriverClose(
had : HACMDRIVER; //Указатель на устройство полученный при открытии
fdwClose : DWORD //Зарезервировано. Должен быть ноль
) : MMRESULT; stdcall;
Чтобы получить дополнительную информацию о драйвере можно использовать функцию acmDriverDetails:
Для С++
MMRESULT acmDriverDetails(
HACMDRIVERID hadid, //Указатель на устройство полученный при открытии
LPACMDRIVERDETAILS padd,//Структура, куда будет записан результат
DWORD fdwDetails //Флаги указывающие на получаемую инфу
);
Для Delphi
function acmDriverDetails(
hadid : HACMDRIVERID; //Указатель на устройство полученный при открытии
var padd : TACMDRIVERDETAILS; //Структура, куда будет записан результат
fdwDetails : DWORD //Флаги указывающие на получаемую инфу
) : MMRESULT; stdcall;
Я не буду больше тратить время на общие функции. Этой инфы тебе будет достаточно для начального понимания и самостоятельного изучения. Возможно в будущем я вернусь к этой теме.
Сейчас я перейду к тем функциям, которые нам понадобятся для конвертации данных. Первая функция выводит на экран стандартное диалоговое окно для выбора звукового формата, в который ты хочешь конвертировать данные:
Для С++
MMRESULT acmFormatChoose(
LPACMFORMATCHOOSE pfmtc //Указатель на струтуру ACMFORMATCHOOSE
);
Для Delphi
function acmFormatChoose(
var pafmtc : TACMFORMATCHOOSE //Указатель на струтуру ACMFORMATCHOOSE
) : MMRESULT; stdcall;
Струстура ACMFORMATCHOOSE выглядит следующим образом:
fdwStyle - флаги, определяющие стиль и возможности окна. Может принимать значения:
ACMFORMATCHOOSE_STYLEF_SHOWHELP - у окна будет кнопка "Справка" ("Help").
ACMFORMATCHOOSE_STYLEF_ENABLEHOOK - поле pfnHook содержит адрес функции-ловушки сообщений. Этой функции будет передаваться сообщение MM_ACM_FORMATCHOOSE.
ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATE - окно будет выглядеть на основе шаблона, указанного в полях hInstance и pszTemplateName.
ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATEHANDLE - в поле hInstance находится адрес шаблона. Поле pszTemplateName игнорируется.
ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT - в pwfx находится информация, которая будет использоватся в качестве значения по умолчанию.
ACMFORMATCHOOSE_STYLEF_CONTEXTHELP - будет присутствовать кнопка контекстной подсказки.
hwndOwner - идентификатор родительского окна для создаваемого окна.
pwfx - Структура WAVEFORMATEX, в которой находится значение по умолчанию.
cbwfx - размер структуры WAVEFORMATEX из параметра pwfx.
pszTitle - заголовок окна выбора.
szFormatTag - здесь будет возвращатся строка с описанием типа фармата.
szFormat - здесь будет возвращатся строка с описанием фармата.
pszName - будет возвращатся строка содержащая имя выбранного формата. Желательно, чтобы строка была больше 128 байт.
cchName - Длина поля pszName.
fdwEnum - флаги указывающие на то, что должно содержатся в списке выбора. Могут быть следующие значения:
ACM_FORMATENUMF_WFORMATTAG - в список включаются только форматы, с типом указаном в pwfxEnum.
ACM_FORMATENUMF_NCHANNELS - в список включаются только форматы, содержащие такое же количество каналов, как и в структуре WAVEFORMATEX указаной в pwfxEnum.
ACM_FORMATENUMF_NSAMPLESPERSEC - в список включаются только форматы, содержащие такое же количество выборок в секунду (nSamplesPerSec), как и в структуре WAVEFORMATEX указаной в pwfxEnum.
ACM_FORMATENUMF_WBITSPERSAMPLE - в список включаются только форматы, содержащие такое же количество байт в выборке, как и в структуре WAVEFORMATEX указаной в pwfxEnum.
ACM_FORMATENUMF_CONVERТ - в список включаются только форматы, которые можно получить из указанного в pwfxEnum.
ACM_FORMATENUMF_SUGGEST - в список включаются все форматы, которые можно получить из указаного в pwfxEnum. (я не понял разницы с предыдущим параметром).
ACM_FORMATENUMF_HARDWARE - в список включаются форматы поддерживаемые аппаратно.
ACM_FORMATENUMF_INPUT - в список включаются форматы поддерживаемые при записи звука.
ACM_FORMATENUMF_OUTPUT - в список включаются форматы поддерживаемые при воспроизведении звука.
pwfxEnum - структура WAVEFORMATEX. Её назначение зависит от предыдущего параметра и описано там же.
hInstance - указатель на память содержащую шаблон диалога.
pszTemplateName - имя шаблона.
lCustData - произвольный параметр.
pfnHook - адрес функции лвушки.
В принципе, существуют все необходимые функции для того, чтобы самому узнать все форматы и сделать подобное окно. Но их описание займёт очень много времени и я не хочу сейчас останавливаться на этом. Статья и так затянулась. Так что переходим к функциям необходимым непосредственно для преобразования данных.
Для преобразования используются потоки. Ты наверно понимаешь, что исходные данные и конечные могут отличатся в размере. Например, если ты преобразовываешь WAV формат в MP3 (если установлен соответствующий конвертор), может уменьшить результирующие данные в несколько раз. Это необходимо учитывать при преобразовании и правильно расчитывать размеры потоков.
Давай сначала рассмотрим алгоритм преобразования, а потом перейдём к рассмотрению необходимых функций.
Открывается поток. Для этого используется функция acmStreamOpen
Подготавливается два блока данных. Размер исходного выбирется по желанию. Размер результирующего можешь выбрать или подсчитать, но лучше узнать с помощью функции acmStreamSize.
Заполняется структура ACMSTREAMHEADER. В неё заносятся данные о блоках.
Структура ACMSTREAMHEADER подготавливается с помощью acmStreamPrepareHeader.
Исходный блок заполняется данными и происходит конвертация с помощью acmStreamConvert. Полученные данные сохраняются.
По окончанию преобразования освобождаем все выделенные данные с помощью acmStreamUnprepareHeader и acmStreamClose.
А теперь переходим к рассмотрению функций. Начнём с acmStreamOpen, которая открывает поток для начала преобразования данных:
phas - сюда будет помещён результат - указатель на поток.
had - драйвер используемый при преобразовании. Если указать ноль, то будет использоваться драйвер по умолчанию.
pwfxSrc - структура WAVEFORMATEX в которой содержится информация о исходных данных.
pwfxDst - структура WAVEFORMATEX в которой содержится информация о результирующих данных.
pwfltr - структура WAVEFILTER описывает фильтр, с помощью которого будет происходить преобразование. Если ты производишь не фильтрацию, то сюда нужно засандолить ноль.
dwCallback - функция обратного вызова.
dwInstance - произвольный параметр.
fdwOpen - флаги открытия потока. Могут быть следующие:
ACM_STREAMOPENF_QUERY - проверяется возможность открытия. Реально ничего не открывается, только проверка.
ACM_STREAMOPENF_ASYNC - открытие происходит в фоновом (асинхронном) режиме
ACM_STREAMOPENF_NONREALTIME - производить нормальное преобразование. Если параметр не указать, то преобразование будет происходить в реальном времени и качество результата может оказатся немного хуже.
CALLBACK_EVENT - обратный вызов будет происходить через объект событие.
CALLBACK_FUNCTION - обратный вызов будет происходить через функцию.
CALLBACK_WINDOW - обратный вызов будет происходить через оконные сообщения.
Сразу скажу как закрыть поток:
Для С++
MMRESULT acmStreamClose(
HACMSTREAM has,
DWORD fdwClose
);
Для Delphi
function acmStreamClose(
has : HACMSTREAM;
fdwClose : DWORD
) : MMRESULT; stdcall;
has - указатель на уткрытый поток, который был получен при открытии.
fdwClose - зарезервировано
Внимание: При закрытии драйвера очередь преобразования должна быть пуста.
Следующая функция - acmStreamSize, с помощью которой можно узнать необходимый размер результирующего потока.
Для С++
MMRESULT acmStreamSize(
HACMSTREAM has,
DWORD cbInput,
LPDWORD pdwOutputBytes,
DWORD fdwSize
);
Для Delphi
function acmStreamSize(
has : HACMSTREAM;
cbInput : DWORD;
var pdwOutputByte : DWORD;
fdwSize : DWORD
) : MMRESULT; stdcall;
has - Указатель на открытый поток.
cbInput - размер данных исходного или конечного потока (зависит от флагов).
pdwOutputBytes - адрес переменной, куда будет записан результат.
fdwSize - флаги:
ACM_STREAMSIZEF_SOURCE - поле cbInput должно содержать размер исходных данных, а в pdwOutputByte будет записан размер результирующих данных.
ACM_STREAMSIZEF_DESTINATION - поле cbInput должно содержать размер результирующих данных, а в pdwOutputByte будет записан размер исходных данных.
Теперь переходим к подготовке заголовка:
Для С++
MMRESULT acmStreamPrepareHeader(
HACMSTREAM has,
LPACMSTREAMHEADER pash,
DWORD fdwPrepare
);
Для Delphi
function acmStreamPrepareHeader(
has : HACMSTREAM;
var pash : TACMSTREAMHEADER;
fdwPrepare : DWORD
) : MMRESULT; stdcall;
has - Указатель на открытый поток.
pash - Адрес стрктуры ACMSTREAMHEADER. Она должна быть уже заполнена. Всё, что не заполнено, должно быть равно нулю.
fdwPrepare - зарезервировано, должно быть равно нулю.
fdwStatus - флаги состояния преобразования (используются системой).
ACMSTREAMHEADER_STATUSF_DONE - указывает на то, что преобразование окончено.
ACMSTREAMHEADER_STATUSF_PREPARED - блок данных подготовлен.
ACMSTREAMHEADER_STATUSF_INQUEUE - блок находится в очереди преобразования.
dwUser - дополнительные произвольные данные.
pdSrc - указатель на исходные данные.
cbSrcLength - длина исходного блока данных.
cbSrcLengthUsed - после преобразования здесь будет количество преобразованных данных.
dwSrcUser - произвольные данные.
pdDst - указатель на результирующие данные.
cbDstLength - размер памяти под результирующие данные.
cbDstLengthUsed - размер преобразованных данных.
dwDstUser - произвольные данные.
dwReselvedDriver - зарезервировано. Никогда не изменяй эти данные.
Теперь про уничтожение данных. Как я уже говорил, для этого используется функция acmStreamUnprepareHeader:
Для С++
MMRESULT acmStreamUnprepareHeader(
HACMSTREAM has,
LPACMSTREAMHEADER pash,
DWORD fdwUnprepare
);
Для Delphi
function acmStreamUnprepareHeader(
has : HACMSTREAM;
var pash : TACMSTREAMHEADER;
fdwUnprepare : DWORD
) : MMRESULT; stdcall;
has - Указатель на открытый поток.
pash - Адрес стрктуры ACMSTREAMHEADER.
fdwUnprepare - зарезервировано, должно быть равно нулю.
Ну а теперь сама функция преобразования данных:
Для С++
MMRESULT acmStreamConvert(
HACMSTREAM has,
LPACMSTREAMHEADER pash,
DWORD fdwConvert
);
Для Delphi
function acmStreamConvert(
has : HACMSTREAM;
var pash : TACMSTREAMHEADER;
fdwConvert : DWORD
) : MMRESULT; stdcall;
has - Указатель на открытый поток.
pash - Адрес стрктуры ACMSTREAMHEADER.
fdwConvert - флаги преобразования:
ACM_STREAMCONVERTF_BLOCKALIGN - производить выравнивание (желательно использовать всегда).
ACM_STREAMCONVERTF_START - всегда указывается для первого блока данных.
ACM_STREAMCONVERTF_END - всегда указывается для последнего блока данных.
Есть ещё одна функция, которая останавливает преобразование данных - acmStreamClose:
Для С++
MMRESULT acmStreamReset(
HACMSTREAM has,
DWORD fdwReset
);
Для Delphi
function acmStreamReset(
has : HACMSTREAM;
fdwReset : DWORD
) : MMRESULT; stdcall;
has - Указатель на открытый поток.
fdwClose - зарезервировано, должно быть ровно нулю.
Вот и всё. На этом заканчиваем обзор функций. Качай исходник и смотри, как это всё работает. Исходник не фантан (написан на быструю руку), но главное, что ясно, как работает преобразование. Юзай и учись.