wince下MP3播放

MP3 播放
音量设置
最近一段时间在修改音量需求变化的时候,让我对在WINCE下对各种音量的设置有了一定的理解,现在将我的理解写到blog,让各位指教。
首先我们来看下,这些声音设置都在注册表:HKEY_CURRENT_USER\ControlPanel\Volume,里面的几个键值都是控制声音的。先解释如下:
Volume: 系统的主音量,范围是 0x0 ~ 0xFFFFFFFF.    
Screen:
屏幕敲击声. 当数值为0(65536)无声,1为柔和,65538为洪亮

Key: 键盘敲击声,数值的意义和Screen相同 .
 Mute:
控制其它静音的选项. 0x04位为1时允许事件声音,0x02允许应用程序声音,0x01允许警告声.需要注意的是,如果不允许应用程序声音,则警告声位也将被忽略.
知道各个键值的意义后,我从最主要的系统的主音量Volume说起。
一、对系统主音量Volume的操作
首先我们来看一段最简单的改变音量的代码:          
DWORD dwVolume = 0xAAAAAAAA;
waveOutSetVolume(0,dwVolume);    
waveOutSetVolume()的第一个参数是设备ID,因为需要更改的是整个系统音量,所以在这里直接取0值即可;第二个参数是需要设置的音量数值,范围是从 0x0 ~ 0xFFFFFFFF
通过waveOutSetVolume()这个API,我们可以很容易的更改系统设备的音量,但这个时候,如果你去查看注册表的Volume的键值是没有变化的,因为它只修改了设备的音量,变化还没有这么快到达注册表。但你可以到控制面板中的音量与声音打开一下,注册表的值也随之改变。(反之,通过对注册表的单独操作对具体音量是起不到作用的)
所以,我对音量的操作,首先对注册表中的Volume进行操作,在用waveOutSetVolume()这个API具体改变音量,这样可以达到一致。具体操作代码如下:
DWORD dwVolume = 0;
CReg* pVolumeReg = NULL;
 pVolumeReg = new CReg( HKEY_CURRENT_USER, TEXT("ControlPanel\\Volume") );
 dwVolume = pVolumeReg->ValueDW(TEXT("Volume"));
。。。。。。。。。。。。。。对音量的具体设置
if( waveOutSetVolume(NULL, dwVolume) != MMSYSERR_NOERROR )           音量设置是否成功
 {
  DBGMSG(ZONE_1, (TEXT("waveOutSetVolume failed, [MainLayere.cpp, SetVolume]")));
 }
  pVolumeReg->SetDW(TEXT("Volume"),dwVolume); 
设置注册表
 delete pVolumeReg;
 pVolumeReg = NULL;   
 上面用到的CReg是一个对注册表操作的类,是我们小组用的一个基类(Walzer:WINCE500目录下搜索CReg可以找到, 其实微软的人也很懒)。这样我们对主音量设置就很完善了。
二、对硬件按键声音(KEY键值)的设置
       尽管waveOutSetVolume()这个API对主音量设置很好用,但这个函数的功能却也是非常有限的,也就是说,它只能更改系统的主音量;如果想修改硬件按键声音或屏幕敲击声,则就无能为力 .
        
有些比较细心的朋友可能会从"控制面板""声音"入手,发现每次在控制面板调节声音,相应的"ControlPanel\Volume"下的键值数值都会变更.但如果是直接修其下的改注册表,却是无论如何都达不到相应的功能的----因为没有通知系统,注册表已经被修改
.
       
如果需要告知系统,注册表已经修改,并请系统依照修改的数值来更改音量,则需要调用微软一个未公开的
API:AudioUpdateFromRegistry().
       
这个API在文档中是无法搜索到,如果需要调用这个函数,可以有两种方法
.
       
一是直接包含"pwinuser.h"文件,然后直接调用
.
       
二是调用coredll.dll,引出该函数并使用
.
        
第一种方法比较不稳定,因为有一些人的sdk中没有这个pwinuser.h文件,所以程序找不到。我还是推荐用第二种方法,直接调用该API(就象我们组长说的暴力调用API,这个方法真的还是不错的。)
    其具体代码如下: 
 typedef void (WINAPI *DLL_AUDIOUPDATEFROMREGISTRY)();定义一个新类型指针,指向 WINAPI
    DLL_AUDIOUPDATEFROMREGISTRY Dll_AudioUpdateFromRegistry = NULL; 
   HINSTANCE hCoreDll = LoadLibrary(TEXT("coredll.dll")); 
   if (hCoreDll) 
 { 
     Dll_AudioUpdateFromRegistry = (DLL_AUDIOUPDATEFROMREGISTRY)GetProcAddress(hCll, _T("AudioUpdateFromRegistry"));   
调用该
API
  if (Dll_AudioUpdateFromRegistry) 
 { 
 (Dll_AudioUpdateFromRegistry)(); 
   } 
       else
{
           return FALSE;
       }
       FreeLibrary(hCoreDll); 
   } 
   else
{
       return FALSE;
   }
   return TRUE;
这样通过修改注册表Key键值,然后通过调用该方法就能实现对按键声音的设置。
ScreenMute的设置也可以用该方法,至此,对WINCE各种音量的设置就基本掌握了。三、MP3音量设置
Volume  -1000…….0    Balance  -1000……..1000
IBasicAudio   *m_pBA
 
BOOL CItsMp3Play::SetVolume(LONG lVolume, LONG lBalance)  //mp3音量设置,非系统音量
{
if(m_pBA == NULL)
  {
        return FALSE;
  }
if(lVolume<MIN_VOLUME&&lVolume>MAX_VOLUME&&lBalance<MIN_BALANCE&&lBalance>MAX_BALANCE)
    {
        return FALSE;
    }
    m_pBA->put_Volume(lVolume);
    m_pBA->put_Balance(lBalance);
         return TRUE;
}
四.解决wince420不能设置左右声道音量的问题
   wince420中不能使用WaveOutSetVolume分别设置左右声道音量,这是因为在wince底层的wav解码里面只使用了参数的低16位,并没有将高低位分开来设置左右声道增益。因此我跟踪调试进入底层,找出设置系统音量的函数!
还有一点是需要说明的,在很多应用中使用的声音芯片都是低成本的,一般不支持左右声道音量调节功能,因此我们只有在wav播放中设置左右声道数据的增益,从而达到左右声道音量分别控制的效果。
默认系统是一个双声道系统。
首先看一下PLATFORM\SMDK2410\DRIVERS\WAVEDEV\Output.cpp文件:
WaveStreamContext::Render之后的数据就是直接发送到外部声音芯片的数据,他根据参数以及标志位选择OutputStreamContextXXX::Render2XXX表示双声道S单声道Mbit位是8位还是16位。
         以双声道OutputStreamContextS16::Render2为例,我的BSP里面的代码如下:
 
PBYTE OutputStreamContextS16::Render2(PBYTE pBuffer, PBYTE pBufferEnd, PBYTE pBufferLast)
{
    LONG CurrT = m_CurrT;
    LONG DeltaT = m_DeltaT;
    LONG CurrSamp0 = m_CurrSamp[0];
    LONG CurrSamp1 = m_CurrSamp[1];
    LONG PrevSamp0 = m_PrevSamp[0];
    LONG PrevSamp1 = m_PrevSamp[1];
    PBYTE pCurrData = m_lpCurrData;
    PBYTE pCurrDataEnd = m_lpCurrDataEnd;
    LONG fxpGain = m_fxpGain;//修改过后,表示两个声道,原文件中只有一个
         LONG fxpGain1 = m_fxpGain1;
    LONG OutSamp0;
    LONG OutSamp1;
 
    while (pBuffer < pBufferEnd)
    {
        while (CurrT >= 0x10000)
        {
            if (pCurrData>=pCurrDataEnd)
            {
                goto Exit;
            }
            CurrT -= 0x10000;
            PrevSamp0 = CurrSamp0;
            PrevSamp1 = CurrSamp1;
            PPCM_SAMPLE pSampleSrc = (PPCM_SAMPLE)pCurrData;
            CurrSamp0 = (LONG)pSampleSrc->s16.sample_left;
            CurrSamp1 = (LONG)pSampleSrc->s16.sample_right;
            pCurrData+=4;
        }
        //在这个地方将左右声道数据增益分别设置,这样输出的数据音量增益就会不同,当然我们耳朵听的声音大小就不一样了
OutSamp0 = PrevSamp0 + (((CurrSamp0 - PrevSamp0) * CurrT) >> 16);
        OutSamp0 = (OutSamp0 * fxpGain) >> VOLSHIFT; 
        OutSamp1 = PrevSamp1 + (((CurrSamp1 - PrevSamp1) * CurrT) >> 16);
        OutSamp1 = (OutSamp1 * fxpGain1) >> VOLSHIFT;
        CurrT += DeltaT;
        if (pBuffer < pBufferLast)
        {
            OutSamp0 += ((HWSAMPLE *)pBuffer)[0];
            OutSamp1 += ((HWSAMPLE *)pBuffer)[1];
#if USE_MIX_SATURATE
            // Handle saturation
            if (OutSamp0>AUDIO_SAMPLE_MAX)
            {
                OutSamp0=AUDIO_SAMPLE_MAX;
            }
            else if (OutSamp0<AUDIO_SAMPLE_MIN)
            {
                OutSamp0=AUDIO_SAMPLE_MIN;
            }
            if (OutSamp1>AUDIO_SAMPLE_MAX)
            {
                OutSamp1=AUDIO_SAMPLE_MAX;
            }
            else if (OutSamp1<AUDIO_SAMPLE_MIN)
            {
                OutSamp1=AUDIO_SAMPLE_MIN;
            }
#endif
        }
        ((HWSAMPLE *)pBuffer)[0] = (HWSAMPLE)OutSamp0;
        ((HWSAMPLE *)pBuffer)[1] = (HWSAMPLE)OutSamp1;
 
        pBuffer += 2*sizeof(HWSAMPLE);
    }
    Exit:
    m_dwByteCount += (pCurrData - m_lpCurrData);
    m_lpCurrData = pCurrData;
    m_CurrT = CurrT;
    m_PrevSamp[0] = PrevSamp0;
    m_PrevSamp[1] = PrevSamp1;
    m_CurrSamp[0] = CurrSamp0;
    m_CurrSamp[1] = CurrSamp1;
    return pBuffer;
}
         知道了左右声道增益设置方法,哪么我们就去找m_fxpGain这个参数,这是原文件中的参数,当然我们可以给他添加一个后缀,比如m_fxpGainRm_fxpGainL。这样在阅读的时候也要方便一些,我这里就直接使用我试验时修改的代码。我发现m_fxGain参数是在StreamContext这个类里面定义的!在PLATFORM\SMDK2410\DRIVERS\WAVEDEV\Strmctxt.h文件中可以找到,在这个类里面我们可以看到这样两个函数:
DWORD SetGain(DWORD dwGain)
    {
        m_dwGain = dwGain&0xFFFFFFFF;
        GainChange();
        return MMSYSERR_NOERROR;
    }
这个是设置音量调用的函数。在原文件中m_dwGain = dwGain&0xFFFF;这样一来就把WaveOutSetVolume传进来的32位音量参数与成16位有效了,哪么我们传进来的高16位的音量就没有作用了,我们要做的就是把高16位的功能启动起来!
virtual void GainChange()
    {
             DWORD m_dwGainL = (m_dwGain >> 16) & 0xffff;
                   DWORD m_dwGainR = m_dwGain & 0xffff;
        m_fxpGain = MapGain(m_dwGainR);
                   m_fxpGain1 = MapGain(m_dwGainL);
    }
在这个函数中调用MapGain将我们传送进来的数据转换成音量增益,这里我们可以不管他,因为音量增益是一个指数型增长的数据,所以在这里需要转换,这样在设置音量的时候我们才能感觉到他是直线型增长的!
         在这个函数中就已经可以看出,已经将两个声道的音量分离开了,也就是在前面提到的Render中所使用的音量增益。
         这个参数是在PLATFORM\SMDK2410\DRIVERS\WAVEDEV\WaveMain.cpp里面的WAV_IOControl传进来的,
case WODM_SETVOLUME:
        {   UINT NumDevs = g_pHWContext->GetNumOutputDevices();
            LONG dwGain = dwParam1;
            if (pStreamContext)
            {
                dwRet = pStreamContext->SetGain(dwGain);
            }
            else
            {
     DeviceContext *pDeviceContext= _pHWContext->GetOutputDeviceContext(uDeviceId);
                dwRet = pDeviceContext->SetGain(dwGain);
            }
            break;
        }
在这里面有两种情况,一个是设置整个系统的音量,一个是设置流的音量,这样我们前面所说的联系起来,就能很容易的了解到系统音量设置的整个流程了!
         设置之后当然是没有改变注册表中音量的值的。
在实现左右声道音量设置的时候我发现了一个奇怪的现象,那就我们虽然能设置左右声道音量增益,但是没有办法稳定的实现在一个声道播放声音,例如:我们播放一个音频,声音设置为单声道,就是说将32位参数中的高16位或者低16设为0,连续播放多次这个音频,理论上来说是一个喇叭有声音,一个喇叭没有声音!
         在测试中我发现,两个声道的音量交换,就好像是我们开始设置的是0xFFFF0000,播放几次之后,就变成了0xFFFF了,左右声道的音量交换了。
         我用示波器看了一下数据和左右声道选择线的波形,在s3c2410中,是使用的IIS总线,左右声道选择线是LRCLK,我发现他启动几个周期之后,才有数据的传送,但是具体启动了几个周期之后才开始传送数据的是不确定的,所有才会有左右声道交换的情况!恰好2410LRCLK又是不能控制的!
         如果有人解决了也遇到了同样的情况,欢迎与我讨论。
 
<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
阅读(2022) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPI(Serial Peripheral Interface)是一种用于设备间通信的串行总线协议。在Windows CE(Wince)操作系统中,SPI驱动是一种控制和管理SPI接口设备的软件模块。下面是关于Wince下SPI驱动的回答: Spi驱动是Wince操作系统中用于控制和管理SPI设备的驱动程序。SPI总线是一种串行通信协议,常用于嵌入式系统中,通过该驱动程序可以实现与其他设备的高速通信。 Wince下的SPI驱动提供了对SPI接口的底层控制和管理功能。它允许操作系统与连接在SPI总线上的外设进行数据交换。SPI驱动负责配置和初始化SPI总线、传输数据、进行中断处理等操作。 Wince下的SPI驱动由操作系统提供,并且可以通过SPI函数库进行调用。应用程序可以通过调用特定的API函数与SPI驱动进行交互,并实现与SPI设备的通信。SPI驱动提供了一组API函数,用于设置SPI总线的工作模式、传输数据、控制引脚等。应用程序可以通过这些函数来完成对SPI设备的操作。 SPI驱动在Wince中的使用具有很高的灵活性和可扩展性。可以通过配置SPI驱动参数来适配不同的SPI设备。同时,SPI驱动还支持中断处理,可以实现实时响应和高效的数据传输。 总之,Wince下的SPI驱动是一种用于控制和管理SPI接口设备的软件模块,它提供了与SPI设备进行高速通信的功能,并具有灵活性和可扩展性,可以满足各种不同的应用需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值