VR
Virtual Reality On-line   Журнал
Новости   |     Журнал    |    Хаkер    |     Магазин   |   Проекты
[   Вход    ]
[Kарта сайтa]

[ Download  ]
[  Конкурс  ]
[ Анекдоты  ]
[  Ссылки   ]
[  Реклама  ]
[ Почтальон ]
[ О проекте ]






TopList
Программирование звука.
Преобразование форматов данных
:
Logo

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

Все описываемые сегодня функции относятся к АСМ драйверам. Если в компе есть звуковуха и она проинсталлирована, то драйверы присутствуют. Только заголовочных файлов для них нет. Вместе с сегодняшними исходниками идёт этот заголовочный файл для 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.
    • ACM_METRIC_DRIVER_SUPPORT - флаги устройства, поддерживаемых возможностей.
    • ACM_METRIC_DRIVER_PRIORITY - приоритет драйвера.
  • pMetric - сюда будет записан результат запроса.

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

  Для С++

  MMRESULT acmDriverEnum(
    ACMDRIVERENUMCB fnCallback,	
    DWORD dwInstance,	
    DWORD fdwEnum	
   );	

  Для Delphi

   function acmDriverEnum(
    fnCallback : ACMDRIVERENUMCB; 
    dwInstance : DWORD; 
    fdwEnum : DWORD
   ) : MMRESULT; stdcall;

Кратенько рассмотрим парметры, потому что всё равно в моём примере я обхожусь без неё. И всё же знать её не помешает:

  • fnCallback - адрес функции, которой будет передаватся информация о драйверах
  • dwInstance - произвольный параметр
  • Флаги перечисления. Могут быть:
    • ACM_DRIVERENUMF_NOLOCAL - не перечислять локальные драйверы.
    • ACM_DRIVERENUMF_DISABLED - будут перечислятся даже запрещённые драйверы.

Функция обратного вызова должна выгляеть следующим образом:

  Для С++

   bool Имя_Функции(
    HACMDRIVERID hadid,
    DWORD dwInstance,
    DWORD fdwSupport);

  Для Delphi

   function Имя_Функции(
    hadid : HACMDRIVERID; 
    dwInstance : DWORD; 
    fdwSupport : DWORD
    ) : BOOL; stdcall;

  • hadid - идентификатор АСМ драйвера
  • dwInstance - дополнительный праметр
  • fdwSupport - флаги указывающие на назначение драйвера:
    • ACMDRIVERDETAILS_SUPPORTF_CODEC - означает, что этот драйвер относится к кодекам.
    • ACMDRIVERDETAILS_SUPPORTF_CONVERTER - позволяет конвертировать данные.
    • ACMDRIVERDETAILS_SUPPORTF_FILTER - драйвер для фильтрации данных.
    • ACMDRIVERDETAILS_SUPPORTF_HARDWARE - это аппаратный драйвер.
    • ACMDRIVERDETAILS_SUPPORTF_ASYNC - драйвер может работать в асинхронном режиме.
    • ACMDRIVERDETAILS_SUPPORTF_LOCAL - это локальный драйвер.
    • ACMDRIVERDETAILS_SUPPORTF_DISABLED - драйвер отключён.

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

  Для С++

   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 выглядит следующим образом:

  Для С++

  typedef struct {  
    DWORD                   cbStruct;  
    DWORD                   fdwStyle; 
    HWND                    hwndOwner; 
    LPWAVEFORMATEX          pwfx; 
    DWORD                   cbwfx; 
    LPCSTR                  pszTitle; 
    char szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS]; 
    char szFormat[ACMFORMATDETAILS_FORMAT_CHARS]; 
    LPSTR                   pszName; 
    DWORD                   cchName; 
    DWORD                   fdwEnum; 

    LPWAVEFORMATEX          pwfxEnum; 
    HINSTANCE               hInstance; 
    LPCSTR                  pszTemplateName; 
    LPARAM                  lCustData; 
    ACMFORMATCHOOSEHOOKPROC pfnHook; 
  } ACMFORMATCHOOSE; 

  Для Delphi

   TACMFORMATCHOOSEA = packed record
    cbStruct        : DWORD;
    fdwStyle        : DWORD;
    hwndOwner       : HWND;
    pwfx            : PWAVEFORMATEX;
    cbwfx           : DWORD;
    pszTitle        : LPCSTR;
    szFormatTag : array[0..ACMFORMATTAGDETAILS_FORMATTAG_CHARS-1] of char;
    szFormat    : array[0..ACMFORMATDETAILS_FORMAT_CHARS-1] of char;
    pszName         : LPSTR;
    cchName         : DWORD;
    fdwEnum         : DWORD;
    pwfxEnum        : PWAVEFORMATEX;
    hInstance       : THandle;
    pszTemplateName : LPCSTR;
    lCustData       : LPARAM;
    pfnHook         : ACMFORMATCHOOSEHOOKPROCA;
   end;

Рассмотрим подробненько параметры структуры:

  • cbStruct - размер структуры ACMFORMATCHOOSEA.
  • 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 (если установлен соответствующий конвертор), может уменьшить результирующие данные в несколько раз. Это необходимо учитывать при преобразовании и правильно расчитывать размеры потоков.

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

  1. Открывается поток. Для этого используется функция acmStreamOpen
  2. Подготавливается два блока данных. Размер исходного выбирется по желанию. Размер результирующего можешь выбрать или подсчитать, но лучше узнать с помощью функции acmStreamSize.
  3. Заполняется структура ACMSTREAMHEADER. В неё заносятся данные о блоках.
  4. Структура ACMSTREAMHEADER подготавливается с помощью acmStreamPrepareHeader.
  5. Исходный блок заполняется данными и происходит конвертация с помощью acmStreamConvert. Полученные данные сохраняются.
  6. По окончанию преобразования освобождаем все выделенные данные с помощью acmStreamUnprepareHeader и acmStreamClose.

А теперь переходим к рассмотрению функций. Начнём с acmStreamOpen, которая открывает поток для начала преобразования данных:

  Для С++

   MMRESULT acmStreamOpen(
    LPHACMSTREAM phas,	
    HACMDRIVER had,	
    LPWAVEFORMATEX pwfxSrc,	
    LPWAVEFORMATEX pwfxDst,	
    LPWAVEFILTER pwfltr,	
    DWORD dwCallback,	
    DWORD dwInstance,	
    DWORD fdwOpen	
   );	

  Для Delphi

   function acmStreamOpen(
    phas : PHACMSTREAM; 
    had : HACMDRIVER; 
    const pwfxSrc : TWAVEFORMATEX; 
    const pwfxdst : TWAVEFORMATEX; 
    pwfltr : PWAVEFILTER; 
    dwCallback : DWORD; 
    dwInstance : DWORD; 
    fdwOpen : DWORD
   ) : MMRESULT; stdcall;

Как всегда, подробненько посмотрим на параметры:

  • 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 - зарезервировано, должно быть равно нулю.

Теперь структура ACMSTREAMHEADER:

  Для С++

   typedef struct {  
    DWORD  cbStruct;  
    DWORD  fdwStatus; 
    DWORD  dwUser; 
    LPBYTE pbSrc; 
    DWORD  cbSrcLength; 
    DWORD  cbSrcLengthUsed; 
    DWORD  dwSrcUser; 
    LPBYTE pbDst; 
    DWORD  cbDstLength; 
    DWORD  cbDstLengthUsed; 
    DWORD  dwDstUser; 
    DWORD  dwReservedDriver[10]; 
   } ACMSTREAMHEADER; 
 
  Для Delphi

   PACMSTREAMHEADER = ^TACMSTREAMHEADER;
   TACMSTREAMHEADER = packed record
    cbStruct         : DWORD;
    fdwStatus        : DWORD;
    dwUser           : DWORD;
    pbSrc            : PBYTE;
    cbSrcLength      : DWORD;
    cbSrcLengthUsed  : DWORD;
    dwSrcUser        : DWORD;
    pbDst            : PBYTE;
    cbDstLength      : DWORD;
    cbDstLengthUsed  : DWORD;
    dwDstUser        : DWORD;
    dwReservedDriver : array [0..9] of DWORD;
   end;

  • cbStruct - размер структуры.
  • 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 - зарезервировано, должно быть ровно нулю.

Вот и всё. На этом заканчиваем обзор функций. Качай исходник и смотри, как это всё работает. Исходник не фантан (написан на быструю руку), но главное, что ясно, как работает преобразование. Юзай и учись.

 Исходники примера забирай здесь


Design by FMk group ©
Copyright©: Horrific aka Флёнов Михаил ©