Windows Media Audio Professional 编码解码器功能

Nick Vicars-Harris

Microsoft Corporation

适用于:

Microsoft® Windows Media® Format SDK

Microsoft Windows Media Encoder 9 Series

摘要: 本文解释了 Windows Media Audio Professional 编码解码器如何在必要时将多声道音频内容转换为立体声音频内容。本文还演示了如何创建编码应用程序,从而使得内容作者能够确定如何进行这种转换。

本页内容

简介
缩混为立体声
动态范围控制
代码示例
更多信息

简介

Microsoft® Windows Media® Audio Professional 编码解码器支持高分辨率多声道内容、从多声道到立体声的作者控制缩混,以及动态范围控制。利用本文提供的信息,编程人员可以使用 Windows Media Format 软件开发工具包 (SDK) 创建利用这些功能的编码或呈现应用程序。

多声道播放需要使用 Windows® XP 操作系统。该编码解码器的一个重要功能是在早期版本的 Windows 操作系统上或者在不支持多声道播放的系统配置(例如,只有两个扬声器的系统)上,将多声道缩混为立体声的功能。

本文将详细讲述多声道缩混算法,并说明动态范围控制的运行方式。

本文包括以下主题:

  • 缩混为立体声。讲述了编码应用程序如何能够提供会影响缩混过程的值,并详细讲述了缩混算法。

  • 动态范围控制。讲述了编码应用程序如何能够提供峰值音量和平均音量,以便用于动态范围控制。

  • 代码示例。演示了如何在编码文件中插入和检索相关元数据属性。

缩混为立体声

Windows Media Audio Professional 编码解码器支持多声道编码和解码。如果播放系统支持多声道播放,对于应用程序开发人员来说则无需进一步的操作。多声道播放需要在 Windows XP 操作系统上使用 Windows Media Player 9 Series 或更高版本。如果播放系统不支持多声道播放,则解码过程必须将多声道内容转换为立体声输出。

缩混 指的是获取一定数量的音频内容声道,然后将其重新组织为更少声道结构的过程。本文所讨论的缩混是从六个声道缩混为两个声道。本文讨论的这个六声道音频配置包括两个右声道扬声器、两个左声道扬声器、一个中间声道扬声器,还有一个超低音扬声器。此配置称为 5.1 音频,这是因为有 5 个标准扬声器和一个超低音扬声器。

在下列情况下,需要进行缩混过程:

  • 使用 Windows Media Player 9 Series 之前的 Windows Media Player 版本,或者在 Windows XP 之前的操作系统上播放多声道内容。

  • 使用受支持的播放器和 Windows 版本播放多声道内容,但是 Windows 扬声器配置设置为任何两个扬声器的选项。要设置扬声器设置,请打开“控制面板”,然后单击“声音和音频设备”。

  • 使用 Windows Media Player 9 Series 或更高版本将多声道内容导出到便携设备。

  • 利用 Windows Media Encoder 9 Series 将使用 Windows Media Audio Professional 编码解码器编码的多声道内容转换为使用版本 8 或更高版本编码解码器编码的双声道内容。

缩混过程开始时首先要输入三个值,称为缩混系数。这些值在公式中使用,用来从六个输入声道的每个声道计算出信号级别,然后将这些信号级别进行组合,从而生成两个输出声道。

编码应用程序负责提供一个用户界面,以便内容作者能够指定缩混系数。然后,该应用程序会使用这些系数计算出各个不同的信号级别。最后,该应用程序会向编码文件中插入一个元数据属性。该元数据属性的值是一个以逗号分隔的字符串,其中包含了上述信号级别。

在解码过程中,Windows Media Audio Professional 编码解码器会判断是否需要进行缩混。如果需要进行缩混,它将从编码文件的元数据标头中检索这些信号级别值,并执行缩混。

矩阵缩混

如果播放系统使用下列配置,Windows Media Audio Professional 编码解码器将使用一种特殊的缩混模式:

  • Windows Media 9 Series 或更高版本技术。

  • Windows XP 操作系统。

  • Windows 扬声器配置可支持环绕音响扬声器。

在此配置中,会以一种“矩阵解码器”可以解释的方式,将双声道缩混信号混合到两个物理声道中。如果音频卡要将其输出发送到一个带有“矩阵解码器”的用户音频/视频接收器,该接收器将能够将输入信号重新解码为四个声道(左、右、中间和环绕),以便获得环绕音响体验。

这种模式不会使用内容作者可能已经提供的任何缩混元数据。

这种模式不能用于缩混 7.1 声道内容。包括 7.1 声道音频的内容总是缩混为立体声。

作者控制的缩混

要改变缩混行为,内容作者最多可以提供三个缩混系数。编码应用程序应该提供一个用户界面,以便作者能够输入这些值,如在 Windows Media Encoder 中。然后,该应用程序使用 Windows Media Format SDK 提供的接口,将缩混信息写入文件。

下表对这三个缩混系数进行了说明。

缩混系数

说明

SurroundMix

混合级别,以分贝为单位 (dB),适用于左环绕 (LS) 和右环绕 (RS) 声道。

CenterMix

混合级别(以 dB 为单位),适用于中间 (C) 声道。

LFEMix

混合级别(以 dB 为单位),适用于低频效果 (LFE) 声道。

该应用程序必须强制实施下表中所说明的限制。

参数

SurroundMix 默认值

-3 dB

CenterMix 默认值

-3 dB

LFEMix 默认值

-12 dB

每个系数的最大值

0 dB

每个系数的最小值

-144 dB

缩混算法

要实现作者控制的缩混,编码应用程序必须在编码文件中插入 g_wszFold6To2Channels3 元数据属性。该属性的值是一个逗号分隔的字符串,其中包含了 Windows Media Audio Professional 编码解码器用于从多声道输入派生立体声输出的数字。本节将演示如何从内容作者指定的系数得出这些数字。

下列等式提供了 5.1 到 2.0 缩混的常用形式:

LResult = B * (L + (LinearSurroundMix * LS) + 
                   (LinearCenterMix * C) + 
                   (LinearLFEMix * LFE))
 
RResult = B * (R + (LinearSurroundMix * RS) + 
                   (LinearCenterMix * C) + 
                   (LinearLFEMix * LFE))

LResult 和 RResult 为两个输出声道。L、R、LS、RS、C 和 LFE 为六个输入声道。

每个公式中的三个项目都使用线性系数进行了加权,这些线性系数是从内容作者提供的相对应缩混系数计算而来的。B 为从这些线性系数计算而来的比例系数。该比例系数是必需的,有了该比例系数,整个混合才永远不会得出大于 1 的得数。

下面的这些等式用于计算线性系数:

LinearSurroundMix = 10 ^ (SurroundMix / 20)
LinearCenterMix = 10 ^ (CenterMix / 20)
LinearLFEMix = 10 ^ (LFEMix / 20)

此公式用于计算 B:

B = 1 / (1 + LinearSurroundMix + LinearCenterMix + LinearLFEMix)

操作系统提供的混音器服务 KMixer 需要 log 值乘以 65,536。要提供与此功能的兼容性,我们要计算下列值,编码解码器在缩混过程中要使用这些值:

W = 20 * 65536 * log10(B)
X = 20 * 65536 * log10(LinearSurroundMix * B)
Y = 20 * 65536 * log10(LinearCenterMix * B)
Z = 20 * 65536 * log10(LinearLFEMix *B)

下表显示了这四个值与六个输入声道和两个输出声道有着怎样的关系:

Output

L

R

C

LFE

LS

RS

LResult

W

minusInfinity

Y

Z

X

minusInfinity

RResult

minusInfinity

W

Y

Z

minusInfinity

X

作为缩混属性值的字符串具有下列格式:

W,minusInfinity,minusInfinity,W,Y,Y,Z,Z,X,minusInfinity,minusInfinity,X

编码应用程序可以使用此值用于 minusInfinity:

–2147483648 (0x80000000)

指定缩混属性的 Windows Media Format SDK 常量为 g_wszFold6To2Channels3。本文最后一节显示的示例代码会计算必需值、构造逗号分隔的字符串,并将该属性插入编码文件中。

动态范围控制

有些音频流包含了较大的音频音量范围,用户可能想限制此声音大小。上述情况对于从经典音乐到电影原声带的各种音频源来说都可能存在。

峰值音量和平均音量之间的差别称为动态范围。如果用户启用了动态范围控制,则 Windows Media Audio Professional 编码解码器支持创作峰值音量和平均音量,这些音量将对播放过程中可用的动态范围进行控制。

Windows Media Audio Lossless 编码解码器还支持作者控制的动态范围控制。

对音频内容进行编码之后,Windows Media Audio Professional 编码解码器会计算该编码内容的峰值音量和平均音量。它会在文件的元数据中两次插入这些值:作为只读参考值和作为读写目标值。参考值会保留内容的原始值。如果用户启用了动态范围控制,则在解码过程中这四个值都会用到。因为目标值可以更改,所以利用编码应用程序,内容作者能够指定新值,以便在用户启用了动态范围控制的情况下调整播放。

编码应用程序应该显示参考值,以便内容作者能够选择合适的目标值。它应该对峰值目标值和平均目标值都强制使用最大值 0 dB 和最小值 -90 dB。

Windows Media Format SDK 为这四个属性提供了下列常量:

  • g_wszWMWMADRCPeakReference

  • g_wszWMWMADRCAverageReference

  • g_wszWMWMADRCPeakTarget

  • g_wszWMWMADRCAverageTarget

编码应用程序应该向内容作者明确表明,不建议设置平均目标值。调整这个平均值不会影响高低声音差别,而会降低或者大大提高整体平均音量,从而导致播放过程中不希望出现的失真。

播放应用程序通过使用输出设置 g_wszDynamicRangeControl 来启用动态范围控制。该应用程序使用 Reader 对象的 IWMReaderAdvanced2::SetOutputSetting 方法来配置该设置。值为零(默认值)指定不更改动态范围。值为 1 指定中等级别的动态范围压缩。值为 2 指定高级别的动态范围压缩。

注 在 Windows Media Player 9 Series 以及更高版本中,Quiet Mode 的用户界面选项决定着动态范围控制。设置 OffMedium differenceLittle difference 分别与 g_wszDynamicRangeControl 值 0、1 和 2 相对应。

下表说明了 g_wszDynamicRangeControl 设置在播放过程中产生的效果。

设置

目标值

最终提交的音频的范围

0

任何目标值。

与原始内容的范围相同。

1

目标值等于参考值。

保持平均水平,峰值限制为平均值 +12 dB。

2

目标值等于参考值。

保持平均水平,峰值限制为平均值 +6 dB。

1

指定了目标值。

平均水平设置为目标平均值,峰值限制为目标峰值。

2

指定了目标值。

平均水平设置为目标平均值,峰值限制为目标平均值和目标峰值的平均值。

如果您要使用 Windows Media Format SDK 构建编码应用程序,则应该提供一个用户界面,以便内容作者能够指定这些目标值。然后,该应用程序应该根据需要修改 g_wszWMWMADRCPeakTargetg_wszWMWMADRCAverageTarget 元数据属性。您还应该提供一个使得用户能够指定要应用的动态范围控制程度的用户界面。

代码示例

下面的代码示例演示了如何使用 Windows Media Format 9 Series SDK 或更高版本来写入和读取多声道音频。

写入多声道输出

下面的示例代码演示了如何将缩混数据写入一个多声道 ASF 文件:

//Some useful defines, although not all are used in this code example
#define  DEFAULT_SURROUND_MIX   -3
#define  DEFAULT_CENTER_MIX     -3
#define  DEFAULT_LFE_MIX        -12

#define INVALID_AUDIO_VALUE 1

#define     MINUS_INFINITY      0x80000000 // –2147483648
#define     PLUS_INFNITY        0x7FFFFFFF // 2147483647
#define     KMIXER_LOG_CONSTANT1    20.0
#define     KMIXER_LOG_CONSTANT2    65536.0
#define     OUT_FORMAT L"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\0"

// Name: SixToTwoFoldDown
// Desc: This is the essential fold-down algorithm
HRESULT SixToTwoFoldDown(  long lSurroundMix,
                           long lCenterMix,
                           long lLFEMix,
                           LPWSTR pwszTable,
                           DWORD dwLen )
{
    if( NULL == pwszTable )
    {
        return( E_POINTER );
    }

    double  dLinearSurroundMix, dLinearCenterMix, dLinearLFEMix;
    long    W, X, Y, Z;
    double  dbB;

    // Calculate coefficients 
    dLinearSurroundMix = pow( 10.0, ( (double) lSurroundMix ) / 
                              KMIXER_LOG_CONSTANT1 );
    dLinearCenterMix = pow( 10.0, ( (double) lCenterMix ) / 
                            KMIXER_LOG_CONSTANT1 );
    dLinearLFEMix = pow( 10.0, ( (double) lLFEMix ) / 
                         KMIXER_LOG_CONSTANT1 );

    dbB = 1.0 / ( 1.0 + dLinearSurroundMix + dLinearCenterMix + 
                  dLinearLFEMix );

    // KMixer requires log values scaled by 65,536
    W = (long) ( KMIXER_LOG_CONSTANT1 * KMIXER_LOG_CONSTANT2 * 
                 log10( dbB ) );
    X = (long) ( KMIXER_LOG_CONSTANT1 * KMIXER_LOG_CONSTANT2 * 
                 log10( dLinearSurroundMix * dbB ) );
    Y = (long) ( KMIXER_LOG_CONSTANT1 * KMIXER_LOG_CONSTANT2 * 
                 log10( dLinearCenterMix * dbB ) );
    Z = (long) ( KMIXER_LOG_CONSTANT1 * KMIXER_LOG_CONSTANT2 * 
                 log10( dLinearLFEMix * dbB ) );

    // Print out into a string.
    _snwprintf( pwszTable, dwLen-1, 
                OUT_FORMAT , //L"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\0"
                W, (long) MINUS_INFINITY,    // L values
                (long) MINUS_INFINITY, W,    // R values
                Y, Y,                        // C values
                Z, Z,                        // LFE values
                X, (long) MINUS_INFINITY,    // LS values
               (long) MINUS_INFINITY, X );   // RS values

    *(pwszTable + dwLen - 1 ) = L'\0';

    return S_OK;
}

// Name: SetFoldDownProperty 
// Desc: Sets the fold-down property string on the specified ASF stream
HRESULT SetFoldDownProperty ( long lSurroundMix, long lCenterMix, long lLFEMix, WORD wStreamNum, IWMHeaderInfo* pHeader)
{

    HRESULT hr;
    // Wide character buffer to hold the property strings
    WCHAR   wszTable[MAX_PATH];
    memset( wszTable, 0, sizeof(WCHAR) * MAX_PATH );
           
    // Variables used in SetAttribute
    WMT_ATTR_DATATYPE datatype = WMT_TYPE_STRING;
    WORD cbLen = 0;
    BYTE* pData = NULL;

    // Check for valid input
    if ( ( lSurroundMix <= 0 && lSurroundMix >= -144) && 
        ( lCenterMix <= 0 && lCenterMix >= -144) &&

        ( lLFEMix <= 0 && lLFEMix >= -144))
    {
          
            // Calculate the fold-down values and write them as a string
            // into the buffer provided. 
            // In this example, we only do 5.1 to 2 fold-down.
            hr = SixToTwoFoldDown( lSurroundMix, lCenterMix, lLFEMix, 
                                    wszTable, MAX_PATH );
            if(SUCCEEDED(hr))
            {
                cbLen = sizeof(WCHAR) * ( wcslen( pwszAttribValue ) + 1);
                pData = (BYTE*) wszTable;

                // Now set the attribute for the stream in the ASF file
                hr = pHeader->SetAttribute( wStreamNum, //zero-based
                                            g_wszFold6To2Channels3,
                                            datatype,
                                            pData,
                                            cbLen );
            }
    }

    else
    {
            hr = E_INVALIDARG;
    }

    return hr;
}
 

读取多声道音频

要使用 Reader 对象读取或播放多声道音频,请按以下顺序执行下面三个步骤:

  1. 调用 IWMReaderAdvanced2::SetOutputSetting 两次,第一次用来将 g_wszEnableDiscreteOutput 设置为 TRUE,第二次用来将 g_wszSpeakerConfig 设置为 DSSPEAKER_5POINT1(在 DirectX SDK 的 dsound.h 中定义)。

  2. Reader 对象获取受支持的多声道媒体类型。

  3. 在输出上设置多声道媒体类型。

这些步骤在下面的代码示例中进行了演示。Windows Media Format SDK 中的 audioplayer 示例应用程序使用 waveOut API 来呈现已经由 Reader 对象进行了分析和解码的音频。要为 audioplayer 添加多声道音频支持,请执行下列步骤:

  1. 在 audioplay.cpp 的最前面添加下面几行代码。

    #include "atlbase.h" //for CComPtr and CComQIPtr
    

#include <dsound.h> //For the DSSPEAKER_5POINT1 value #include <mmreg.h> //For WAVEFORMATEXTENSIBLE (if you use it)

  1. 在 audioplay.cpp 中,在 RetrieveAndDisplayAttributes 调用之后向 Open 函数中插入下面几行代码。

    //Set up multichannel playback
    

BOOL fEnableDiscreteOutput = TRUE; DWORD dwSpeakerConfig = DSSPEAKER_5POINT1;

CComQIPtr<IWMReaderAdvanced2, &IID_IWMReaderAdvanced2> pReaderAdvanced2(m_pReader); if(! pReaderAdvanced2) break; //Make the required settings on the Reader Object hr = pReaderAdvanced2->SetOutputSetting(0, g_wszEnableDiscreteOutput, WMT_TYPE_BOOL, (BYTE *)&fEnableDiscreteOutput, sizeof( BOOL ) ); if(FAILED(hr)) break;

hr = pReaderAdvanced2->SetOutputSetting(0, g_wszSpeakerConfig, WMT_TYPE_DWORD, (BYTE *)&dwSpeakerConfig, sizeof( DWORD ) ); if(FAILED(hr)) break;

// Dynamic range control can also be set using SetOutputSetting, // although we don't show that here.

// Get the various formats supported by the audio output. // In this example, to keep things simple, we only handle // audio-only files with a single stream. In other words, we // assume that there is one audio output and that its number is zero. DWORD dwAudioOutput = 0; DWORD formats = 0; hr = m_pReader->GetOutputFormatCount(dwAudioOutput, &formats); if(FAILED(hr)) break;

// Multichannel formats, if available, are returned first for(int j = 0; j < formats;j++) { CComPtr<IWMOutputMediaProps> pProps; hr = m_pReader->GetOutputFormat(dwAudioOutput, j, &pProps); if(FAILED(hr)) break;

WM_MEDIA_TYPE* pNativeType = NULL;
DWORD cbFormat = 0;
hr = pProps-&gt;GetMediaType( NULL, &amp;cbFormat );
if(FAILED(hr)) break;     

pNativeType = (WM_MEDIA_TYPE *)new BYTE[ cbFormat ];
if( NULL == pNativeType )
{
    printf( "Not enough core\n" );
    hr = E_OUTOFMEMORY;
}

hr = pProps-&gt;GetMediaType( pNativeType, &amp;cbFormat );
if( hr != S_OK )
{
    printf( "Failed getting the media type\n");
    return hr;
}
//  This works for WAVEFORMATEXTENSIBLE formats as long
//  as we are only looking at the WAVEFORMATEX members.
WAVEFORMATEX* pWFX = (WAVEFORMATEX*) pNativeType-&gt;pbFormat;
if(pWFX-&gt;nChannels == 6)
{
    // We have found a six-channel output supported for
    // this file. If we were going to examine all the contents
    // of the structure, we would need to cast 
    // pNativeType-&gt;pbFormat to WAVEFORMATEXTENSIBLE. In this
    // example, we just set the first multichannel type
    // we find. The format block has been correctly 
    // allocated, so we just pass it to SetMediaType.
    hr = pProps-&gt;SetMediaType(pNativeType);
    if( hr != S_OK )
    {
        printf( "Failed to set output props\n");
        return hr;
    }

    hr = m_pReader-&gt;SetOutputProps(0, pProps);
    if( hr != S_OK )
    {
        printf( "Failed to set output props\n");
        return hr;
    }
    delete pNativeType;
    break;

} // end if

delete pNativeType;

} // end for

  1. GetAudioOutput 方法调用向下移动到 Open 方法的底部,紧挨“return hr”一行的前面。此方法会设置 WAVEFORMATEX 结构,该结构将在该应用程序后面部分的 waveOutOpen 调用中使用。

如果该读取器对象设置的格式类型为 WAVEFORMATEXTENSIBLEwFormatTag 则设置为 WAVE_FORMAT_EXTENSIBLEcbSize 设置为 22。尽管 waveOutOpen 函数使用的指针为 WAVEFORMATEX,该函数还是会检测这两个值,并正确处理 WAVEFORMATEXTENSIBLE 结构中存在的其他数据。

更多信息

要更多了解 Windows Media Player 处理动态范围信息的方式,请参阅 Microsoft 网站上的文章“Dynamic Range Control in Windows Media 9 Series”。

有关 Windows XP 中多声道音频的更多信息,请参阅 Microsoft 网站上的文章“Multiple Channel Audio Data and WAVE Files”。

转到原英文页面