Audio Driver 架构

Audio Driver 架构

在WINCE中 Audio Driver 架构支持两种驱动模式

即独立型的unified audio model (UAM)驱动 和 分层式的MDD and PDD mode驱动,(不论是UAM或者MDD/PDD都是流接口驱动)。

其架构还支持的audio compression manager (ACM)驱动,例如codecs, converters, and filters等器件


1)         UAM

UAM支持标准波形驱动接口(standard wave driver interfaces),过去的波形驱动和采样驱动是由MDD和PDD模型组成。MDD模型执行了驱动的独立硬件部分以及输出到驱动接口的中间设备。PDD模型提供了驱动依赖硬件的执行部分

The following illustration shows the UAM stack.:


Using MDD and PDD, the previous model had the following limitations:

·            No support for multiple streams

·            No multiple devices on one driver

·            No reliable support for looping

·            Poor support for streaming

OEM商可以围绕这些限制来移植自己的MDD或者写入他们自己的完整驱动来输出到合适的接口到中间设备

UAM实现了对WAV和Microsoft DirectSound®音频API的高效支持。它还使得编写一个能有效支持WAV和DirectSound的驱动程序成为可能。

 

在我们的WM8753 音频Driver中即使用了UAM这种驱动模式,它也是一种流接口驱动,故只需编写驱动中WAVE和MIXER这两部分,然后使用流接口函数调用即可。

 

2).音频MDD和PDD

 

编写音频驱动我们可以选择UAM架构,或者直接执行流接口(stream interface),我们使用由微软提供的MDD库-Wavemdd.lib。这个库通过DDSI来执行流接口功能。如果使用了Wavemdd.lib,则必须一个PDD库来执行音频DDSI的功能。这个库被称为Wavepdd,lib, 这两个库编译连接后就形成了我们的音频驱动,通常被为Wavedev,dll。

在系统程序文件中可能缺少器件的功能导致音频器件的很多功能无法被使用,为了解决这个问题DeviceIOControl 就变得很重要了。DeviceIOControl是流接口的一部分,其允许任意功能能够通过WAV_IOControl功能来正确得操作硬件。例如:当命令器件准备录音时,中间设备会使用WAV_IOControl发送WIDM_PREPARE消息给音频驱动。

由于音频驱动完全依赖DeviceIOControl功能消息,所以执行剩余得流接口就相对简单了,

特别像WAV_Read, WAV_Seek, and WAV_Write功能仅仅是返回不变的值

下图显示了使用 MDD 库的音频驱动程序的交互

 

以下列表显示了音频驱动程序的 DDSI 函数

 

PDD_AudioDeinitialize
 This function turns off and disconnects the audio device.
 
PDD_AudioGetInterruptType
 This function determines the cause of the audio interrupt and returns the current device status.
 
PDD_AudioInitialize
 This function initializes the audio device for operation. 
 
PDD_AudioMessage
 This function sends messages from user applications to the audio driver's platform-dependent driver (PDD) layer.
 
PDD_AudioPowerHandler
 This function is responsible for managing the audio hardware during POWER_UP and POWER_DOWN notifications.
 
PDD_WaveProc
 This function sends messages to the audio driver's PDD layer.
 

PDD和MDD都依靠调用DDSI函数来实现相互通信,所以若采用分层式来编写驱动,只需找到微软提供的MDD,然后根据其DDSI来编写PDD层即可。

 

对于流驱动,×××_Open/×××_Close/×××_IoControl等就是ddi;如果不是流驱动,它的ddi不具有上述形式;

 

以我们的driver为例,在程序中,我们可以在C:/WINCE500/PLATFORM/C340/Src/Drivers/audio/IIS/wm8753/wavemain.cpp中找到以下对应点

Programming element

Description

WAV_IOControl

This function is the device I/O control routine for the WAV I/O device.

WAV_Init

This function initializes the WAV I/O device.

WAV_Deinit

This function deinitializes the WAV I/O device.

WAV_Open

This function opens the WAV I/O device.

WAV_Close

This function closes the WAV I/O device.

WAV_Read

This function is the read routine for the WAV I/O device driver.

WAV_Write

This function is the write routine for the WAV I/O device.

WAV_Seek

This function is the seek routine for the WAV I/O device.

WAV_PowerUp

This function notifies the WAV I/O device that the system is leaving the suspend state.

WAV_PowerDown

This function turns off the WAV I/O device

 

在注册表中还要建立驱动程序的入口点,这样设备管理器才能识别和管理这个驱动

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Audio]

   "Prefix"="WAV"

 "Dll"="s3c2440a_iis_wm8753.dll"

   "Index"=dword:1

   "Order"=dword:0

此外,注册表还能储存额外的信息,这些信息可以在驱动运行之后被使用到,DLL项是设备管理器在加载驱动时需要的DLL名称;Prefix代表了设备前缀;Order是驱动程序被加载的顺序

 

 

 

 

WIDM_GETDEVCAPS

This message is used to request a waveform input driver to return the capabilities of a specified device.

WIDM_GETNUMDEVS

This message is used to request a waveform input driver to return the number of devices that it supports.

WIDM_GETPOS

This message is used to request a stream input driver to return the current input position within a waveform. The input position is relative to the first recorded sample of the waveform.

WIDM_OPEN

This message is used to request a waveform input driver to open a stream of a specified device.

WIDM_PREPARE

This message is used to request a waveform input driver to prepare a system-exclusive data buffer for input.

WIDM_RESET

This message is used to request a waveform input driver to stop recording and return all buffers in the input queue to the caller.

WIDM_START

This message is used to request a waveform input driver to begin recording.

WIDM_STOP

This message is used to request a waveform input driver to stop recording.

WIDM_UNPREPARE

This message is used to request a waveform input driver to undo the buffer preparation that was performed in response to a WIDM_PREPARE message.

 

下表中列出了各个输出driver消息

Programming element

Description

WODM_BREAKLOOP

This message is used to request a waveform output driver to break an output loop that was created with a WODM_WRITE message.

WODM_CLOSE

This message is used to request a waveform output driver to close a specified stream that was previously opened with a WODM_OPEN message.

WODM_GETDEVCAPS

This message is used to request a waveform output driver to return the capabilities of a specified device.

WODM_GETNUMDEVS

This message is used to request a waveform output driver to return the number of device instances that it supports.

WODM_GETPITCH

This message is used to request a waveform output driver to return the specified device's current pitch multiplier value.

WODM_GETPLAYBACKRATE

This message is to request a waveform output driver to return the current playback rate multiplier value for the specified device.

WODM_GETPOS

This message is used to return the current position within a stream. The position is relative to the beginning of the waveform.

WODM_GETVOLUME

This message is used to request a waveform output driver to return the current volume level setting for the specified device or stream.

WODM_OPEN

This message is used to request a waveform output driver to open a stream on the specified device.

WODM_PAUSE

This message is used to request a waveform output driver to pause playback of a waveform.

WODM_PREPARE

This message is used to request a waveform output driver to prepare a system-exclusive data buffer for output.

WODM_RESET

This message is used to request a waveform output driver to stop sending output data and return all output buffers to the list.

WODM_RESTART

This message is used to request a waveform output driver to continue playback of a waveform after playback has been paused with WODM_PAUSE.

WODM_SETPITCH

This message is used to request a waveform output driver to set the specified device's pitch multiplier value.

WODM_SETPLAYBACKRATE

This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device.

WODM_SETVOLUME

This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device.

WODM_UNPREPARE

This message is used to request a waveform output driver to remove the buffer preparation performed in response to WODM_PREPARE.

WODM_WRITE

This message is used to request a waveform output driver to write a waveform data block to the specified device.

 

 

以我们的WM8753音频Driver 为例,整个驱动里包含了以下重要功能的子驱动:

Devctxt.cpp

器件关联——包含了音频流的创造,删除,打开,关闭,格式等功能

Hwctxt.cpp

硬件关联——包含了基本的硬件功能在各个状态的全局配置

I2citf.cpp

I2C传输配置

I2S.cpp

I2S传输配置

Input.cpp

负责输入音频流

Output.cpp

负责输出音频流

Midinote.cpp

负责输出MIDI

Midistrm.cpp

负责MIDI的开关以及控制

Mixerdrv.cpp

系统软件混音

RTcodecComm.cpp

Wm8753的所有功能配置,以及初始化设置

Strmctxt.cpp

负责所有音频流的增益,buffer请求等功能以及对Devctxt的控制

Wavemain.cpp

包含了所有的流接口函数

 

 

按照UAM的定义, wm8753的driver主要由mixer和wave两部分组成;

由于mixer只实现一些基本的混音功能,其主要由Mixerdrv.cpp承担,还包括Midistrm.cpp,Input.cpp,Output.cpp的部分功能。而其他基本都是wave功能

Wavemain.cpp基于整个驱动的最上层,其中,流接口函数做到了以下控制:

 

Wav_init ——> hwctxt    RTcodecComm

                       I2citf.,

I2S

 

Wav_deinit               devctxt                   

Wav_Powerup            hwctxt

Wav_powerdown          mixerdrv

                        strmctxt

 

Wav_IOControl——> Wav_open/close——> 所有  

若要移植一个音频驱动到另一个器件,一般只需更改Hwctxt.cpp,I2citf.cpp,I2S.cpp,RTcodecComm.cpp 即可

 

四.以下是WM8753的WAV_IOControl调用说明:

  extern "C" BOOL WAV_IOControl(DWORD  dwOpenData,

                   DWORD  dwCode,

                   PBYTE  pBufIn,

                   DWORD  dwLenIn,

                   PBYTE  pBufOut,

DWORD  dwLenOut,

 

                   PDWORD pdwActualOut)

{

 

 

    _try

    {

   switch (dwCode)

       {

        case IOCTL_MIX_MESSAGE:

        return HandleMixerMessage((PMMDRV_MESSAGE_PARAMS)pBufIn, (DWORD *)pBufOut);//调用混音消息

 

        case IOCTL_WAV_MESSAGE:

         return HandleWaveMessage((PMMDRV_MESSAGE_PARAMS)pBufIn, (DWORD *)pBufOut);//调用音频消息

           

        //以下为电源管理功能

        case IOCTL_POWER_CAPABILITIES:

        case IOCTL_POWER_SET:

        case IOCTL_POWER_GET:

            return g_pHWContext->IOControl

            (dwOpenData, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut);

                               

     

        }

 

    }

 

BOOL HandleWaveMessage(PMMDRV_MESSAGE_PARAMS pParams, DWORD *pdwResult) //管理音频消息

{

    //  set the error code to be no error first

    SetLastError(MMSYSERR_NOERROR);

 

    UINT uMsg = pParams->uMsg;

    UINT uDeviceId = pParams->uDeviceId;

    DWORD dwParam1 = pParams->dwParam1;

    DWORD dwParam2 = pParams->dwParam2;

    DWORD dwUser   = pParams->dwUser;

    StreamContext *pStreamContext = (StreamContext *)dwUser;

 

    DWORD dwRet;

 

    g_pHWContext->Lock();

 

      // catch exceptions inside device lock, otherwise device will remain locked!

    _try

    {

      

    switch (uMsg)

    {

    case WODM_GETNUMDEVS: //This message is used to request a waveform output driver to return the capabilities of a specified device.

        {

            dwRet = g_pHWContext->GetNumOutputDevices();

            break;

        }

 

    case WIDM_GETNUMDEVS:// This message is used to request a waveform input driver to return the number of devices that it supports.

        {

            dwRet = g_pHWContext->GetNumInputDevices();

            break;

        }

 

    case WODM_GETDEVCAPS:// This message is used to request a waveform output driver to return the capabilities of a specified device.

        {

            DeviceContext *pDeviceContext;

            UINT NumDevs = g_pHWContext->GetNumOutputDevices();

 

            if (pStreamContext)

            {

                pDeviceContext=pStreamContext->GetDeviceContext();

            }

            else

            {

                pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);

            }

 

            dwRet = pDeviceContext->GetDevCaps((PVOID)dwParam1,dwParam2);

            break;

        }

 

 

    case WIDM_GETDEVCAPS:// This message is used to request a waveform input driver to return the capabilities of a specified device.

        {

           
DeviceContext *pDeviceContext;

http://www.bbfar.com/article/2e/590.html

 

            UINT NumDevs = g_pHWContext->GetNumInputDevices();

 

            if (pStreamContext)

            {

                pDeviceContext=pStreamContext->GetDeviceContext();

            }

            else

            {

                pDeviceContext = g_pHWContext->GetInputDeviceContext(uDeviceId);

            }

 

            dwRet = pDeviceContext->GetDevCaps((PVOID)dwParam1,dwParam2);

            break;

        }

 

    case WODM_GETEXTDEVCAPS:

        {

            DeviceContext *pDeviceContext;

            UINT NumDevs = g_pHWContext->GetNumOutputDevices();

 

            if (pStreamContext)

            {

                pDeviceContext=pStreamContext->GetDeviceContext();

            }

            else

            {

                pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);

            }

 

            dwRet = pDeviceContext->GetExtDevCaps((PVOID)dwParam1,dwParam2);

            break;

        }

 

    case WODM_OPEN:// This message is used to request a waveform output driver to open a stream on the specified device.

        {

            // DEBUGMSG(1, (TEXT("WODM_OPEN/r/n"));

            DeviceContext *pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);

            dwRet = pDeviceContext->OpenStream((LPWAVEOPENDESC)dwParam1, dwParam2, (StreamContext **)dwUser);

            break;

        }

 

    case WIDM_OPEN:// This message is used to request a waveform input driver to open a stream of a specified device.

        {

            // DEBUGMSG(1, (TEXT("WIDM_OPEN/r/n"));

            DeviceContext *pDeviceContext = g_pHWContext->GetInputDeviceContext(uDeviceId);

            dwRet = pDeviceContext->OpenStream((LPWAVEOPENDESC)dwParam1, dwParam2, (StreamContext **)dwUser);

            break;

        }

 

    case WODM_CLOSE:

    case WIDM_CLOSE:

        {

            // DEBUGMSG(1, (TEXT("WIDM_CLOSE/WODM_CLOSE/r/n"));

            dwRet = pStreamContext->Close();

 

            // Release stream context here, rather than inside StreamContext::Close, so that if someone

            // (like CMidiStream) has subclassed Close there's no chance that the object will get released

            // out from under them.

            if (dwRet==MMSYSERR_NOERROR)

            {

                pStreamContext->Release();

}

 

            break;

        }

 

    case WODM_RESTART:

    case WIDM_START:

        {

            dwRet = pStreamContext->Run();

            break;

        }

 

    case WODM_PAUSE:

    case WIDM_STOP:

        {

            dwRet = pStreamContext->Stop();

            break;

        }

 

    case WODM_GETPOS:

    case WIDM_GETPOS:

        {

            dwRet = pStreamContext->GetPos((PMMTIME)dwParam1);

            break;

        }

 

    case WODM_RESET:

    case WIDM_RESET:

        {

            dwRet = pStreamContext->Reset();

            break;

        }

 

    case WODM_WRITE:

    case WIDM_ADDBUFFER:

        {

            // DEBUGMSG(1, (TEXT("WODM_WRITE/WIDM_ADDBUFFER, Buffer=0x%x/r/n"),dwParam1);

            dwRet = pStreamContext->QueueBuffer((LPWAVEHDR)dwParam1);

            break;

        }

 

    case WODM_GETVOLUME:// This message is used to request a waveform output driver to return the current volume level setting for the specified device or stream.

        {

            PULONG pdwGain = (PULONG)dwParam1;

 

            if (pStreamContext)

            {

                *pdwGain = pStreamContext->GetGain();

            }

            else

            {

#ifdef USE_HW_GAIN_WODM_SETGETVOLUME

                // Handle device gain in hardware

                *pdwGain = g_pHWContext->GetOutputGain();

#else

                // Handle device gain in software

                DeviceContext *pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);

                *pdwGain = pDeviceContext->GetGain();

#endif

            }

            dwRet = MMSYSERR_NOERROR;

            break;

        }

 

    case WODM_SETVOLUME:// This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device.

        {

            LONG dwGain = dwParam1;

            if (pStreamContext)

            {

                dwRet = pStreamContext->SetGain(dwGain);

            }

            else

            {

#ifdef USE_HW_GAIN_WODM_SETGETVOLUME

                // Handle device gain in hardware

                dwRet = g_pHWContext->SetOutputGain(dwGain);

#else

// Handle device gain in software

 

                DeviceContext *pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);

                dwRet = pDeviceContext->SetGain(dwGain);

#endif

            }

            break;

        }

 

    case WODM_BREAKLOOP:// This message is used to request a waveform output driver to break an output loop that was created with a WODM_WRITE message.

        {

            dwRet = pStreamContext->BreakLoop();

            break;

        }

 

    case WODM_SETPLAYBACKRATE:// This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device.         {

            WaveStreamContext *pWaveStream = (WaveStreamContext *)dwUser;

            dwRet = pWaveStream->SetRate(dwParam1);

            break;

        }

 

    case WODM_GETPLAYBACKRATE:// This message is to request a waveform output driver to return the current playback rate multiplier value for the specified device.

        {

            WaveStreamContext *pWaveStream = (WaveStreamContext *)dwUser;

            dwRet = pWaveStream->GetRate((DWORD *)dwParam1);

            break;

        }

 

    case MM_WOM_SETSECONDARYGAINCLASS:

        {

            dwRet = pStreamContext->SetSecondaryGainClass(dwParam1);

            break;

        }

 

    case MM_WOM_SETSECONDARYGAINLIMIT:

        {

            DeviceContext *pDeviceContext;

            if (pStreamContext)

            {

                pDeviceContext = pStreamContext->GetDeviceContext();

            }

            else

            {

                pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);

            }

            dwRet = pDeviceContext->SetSecondaryGainLimit(dwParam1,dwParam2);

            break;

        }

 

    case MM_MOM_MIDIMESSAGE:

        {

            CMidiStream *pMidiStream = (CMidiStream *)dwUser;

            dwRet = pMidiStream->MidiMessage(dwParam1);

            break;

        }

 

           // For Debug Register

    case WPDM_PRIVATE_WRITE_CODEC:

    case WPDM_PRIVATE_READ_CODEC:

         //{

           //    dwRet=g_pHWContext->Private_AudioMessage(pParams->uMsg, pParams->dwParam1, pParams->dwParam2);

           //     break;

               //}

 

// unsupported messages

    case WODM_GETPITCH:

    case WODM_SETPITCH:

    case WODM_PREPARE:

    case WODM_UNPREPARE:

    case WIDM_PREPARE:

    case WIDM_UNPREPARE:

    default:

        dwRet  = MMSYSERR_NOTSUPPORTED;

        break;

    }

   

    }

    _except (GetExceptionCode() == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)

    {

        ERRORMSG(1, (TEXT("Access violation in HandleWaveMessage!!!!/r/n")));

        SetLastError(E_FAIL);

    }   

   

    g_pHWContext->Unlock();

     // Pass the return code back via pBufOut

    if (pdwResult)

    {

        *pdwResult = dwRet;

    }

 

    return(TRUE);

}  

你可能感兴趣的:(WinCE,audio,output,playback,function,input,stream)