博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Audio Driver 架构
阅读量:2400 次
发布时间:2019-05-10

本文共 14495 字,大约阅读时间需要 48 分钟。

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

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

This function initializes the WAV I/O device.

This function deinitializes the WAV I/O device.

This function opens the WAV I/O device.

This function closes the WAV I/O device.

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

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

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

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

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是驱动程序被加载的顺序

 

 

 

 

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

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

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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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;

 

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

}  

转载地址:http://cqjob.baihongyu.com/

你可能感兴趣的文章
关于纠结的recycle pool的设置
查看>>
清华梦的粉碎读后感--论理想主义者王垠
查看>>
增量数据丢失的原因分析(三)
查看>>
生活中的优化和向往(r11笔记第72天)
查看>>
最近的几个技术问题总结和答疑(七)
查看>>
使用sysbench压力测试MySQL(一)(r11笔记第3天)
查看>>
容灾切换中的数据库宕机问题简单分析(一)
查看>>
几年前的一次答疑解惑
查看>>
MySQL RR隔离级别的更新冲突策略
查看>>
MySQL索引条件下推的简单测试
查看>>
通过SQL解读财富的分配(二)
查看>>
一个MySQL死锁问题的复现
查看>>
数据迁移中的几个问题总结
查看>>
心理学中的效应简单解读(r12笔记第24天)
查看>>
mysqldump简单解析
查看>>
Oracle备库无法连接主库的问题分析
查看>>
最近一周的学习计划
查看>>
MySQL备份和恢复工具图谱
查看>>
从零开始搭建Nginx和Tomcat的web集群环境
查看>>
关于技术文档
查看>>