C# WavePlayer / WaveOutWrite

在本文中主要调用WinMm库中导出的waveOutWrite完成

“波形数据块”的播放,当然,播放“波形数据块”的办法很多

如“MCI”、“BASS”、“WMP”、“DirectX Sound”等、

不过为什么我一定要用waveOutWrite呢?可能有人曾经想

“音频解码器”是怎么开发的,那么我可以简单的说一下、

“音频解码器”比如mp3的mpeg audio,我们都知道它是一

压缩格式音频,在Win32中如果需要传送声音到音频设备大概

三种方式,waveOutWrite、mdiStreamOut、DirectXSound

举个例子,假设我们需要让自己的浏览器程序静音那么我们

只需要拦截上面的几个函数,当然DirectXSound稍微麻烦些

需要拦截掉CreateSoundBuffer,DirectSoundCreate两个函数

如果mp3格式的音频文件需要被音频设备输出,那么必须把它

转换成被Win32可以直接转送到音频设备的“波形数据块”,不

直接把mp3音频数据丢到声卡,你会听到“无限的噪音” 这是

音频设备不知道,你传送给它的是什么数据


那么我现在说明如果通过WinMm/Wave传送“波形数据块”到声

卡是怎样的一个过程、首先waveOutOpen打开音频输出设备,

waveOutPrepareHeader准备音频波形数据块头,waveOutWrite

写入波形数据块到音频设备、然后最后在进行判断是否播放完了

        public WavePlayer()
        {
            if (waveOutGetNumDevs() <= 0)
            {
                throw new Exception("Cannot find the your computer audio output device."); // 无法找到您电脑的音频输出设备
            }

            sWaveFormat.nSamplesPerSec = 44100; // 波形采样
            sWaveFormat.nAvgBytesPerSec = 176400; // 平均传输率
            sWaveFormat.wFormatTag = 1; // 波形格式
            sWaveFormat.nChannels = 2; // 声道
            sWaveFormat.wBitsPerSample = 16; // 采样位深
            sWaveFormat.nBlockAlign = 2 * 16 / 8; // 块对齐
            sWaveFormat.cbSize = 16; // 结构尺寸

            pfnDevNotify = DeviceNotify;
            GCHandle.Alloc(pfnDevNotify, GCHandleType.Normal);
        }

首先你需要检查系统中有多少“音频设备”,如果没有音频输出设备

自然需要抛出一个值表示错误,然后定义“波形数据块格式”,这里

很关键它定了你输出到设备的“波数据形块”音频质量,当然你在

上述代码中肯定会很纠结,为什么nBlockAlign / 块对齐会是,

2 * 16 / 8, 那么我需注明一下该项的值是这样计算的,

采样位深 * 声道 / sizeof(byte) 在上面是快对齐的值是4,但是我特

意写这样的代码主要是方便理解

        public void Load(byte[] buffer, int loop)
        {
            if (hWaveOut != IntPtr.Zero && waveOutReset(hWaveOut) != MMSYSERR_NOERROR 
                && waveOutClose(hWaveOut) != MMSYSERR_NOERROR)
            {
                hWaveOut = IntPtr.Zero;
                throw new Exception("Unable to turn off the audio output device."); // 无法关闭音频输出设备
            }
            if (waveOutOpen(ref hWaveOut, -1, ref sWaveFormat, pfnDevNotify,
                NULL, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
            {
                throw new Exception("Could not open audio output device."); // 无法打开音频输出设备
            }
            sWaveHdr.lpData = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
            sWaveHdr.dwBufferLength = buffer.Length;
            if (waveOutPrepareHeader(hWaveOut, ref sWaveHdr, 32) != MMSYSERR_NOERROR)
            {
                throw new Exception("Unable to prepare the waveform data block used to play."); // 无法准备用于播放的波形数据块
            }
            sWaveHdr.dwLoops = loop;
            sWaveHdr.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP | WHDR_PREPARED;
            mDepArgs[0] = buffer;
            mDepArgs[1] = loop;
        }

“waveOutOpen / 打开波形音频输出设备”,你打开多少个音频输出

设备则可以同时播放多少个声音、当然一般来说是不会那么做 因为

那则可能成就杂音,如果是游戏开发的话则有必要这么做、当然为

了逼格通常是使用DirectXSound,或者调用3D引擎提供的音频输出

具体可以参考ETV3D的引擎、不否认那引擎效果还是很不错的

“waveOutPrepareHeader / 准备音频波形数据块头”这是输出音频

数据的第二个关键性操作,它的作用是把需要提交给音频设备的

“波形块数据”放入缓冲区内,WAVEHDR的dwLoops成员它是决定

你传送到音频输出设备的播放次数,一般来说-1即INFINITE则是无

限次播放,当然根本不存在真正意义上的无限,但是即便只有一秒

钟循环几十亿次也是很久了、

        public void Play()
        {
            if (PlayStatus == PlayStatus.Pause)
            {
                if (waveOutRestart(hWaveOut) != MMSYSERR_NOERROR)
                {
                    throw new Exception("Unable to block the output waveform data to the device."); // 无法输出波形数据块到设备
                }
            }
            else if (PlayStatus == PlayStatus.Stop)
            {
                Load((byte[])mDepArgs[0], (int)mDepArgs[1]);
            }
            if (PlayStatus != PlayStatus.Pause && PlayStatus != PlayStatus.Playing
                && waveOutWrite(hWaveOut, ref sWaveHdr, 32) != MMSYSERR_NOERROR)
            {
                throw new Exception("Unable to block the output waveform data to the device."); // 无法输出波形数据块到设备
            }
            PlayStatus = PlayStatus.Playing;
        }
最后通过“ waveOutWrite / 写入波形 数据块到音频设备”,一旦写入

则开始播放声音、在这里你可以想象到“音频插件”的一种开发方式,

在输出到音频设备前重新处理“波形块数据”令输出的声音,更加清

晰优秀,当然这一块不是我们这些屌丝可以接触到的,想想就好了 

当然在本代码中挂的“音频设备消息回调”判断,而不是通过

“waveOutUnPrepareHeader / 未准备音频波形数据块头”判断音频

设备是否播放完了“波形数据块”,当然你也可以那么做 但是对于我

写的这个代码而言我个体认为是没有必要,因为不需要读一段“波形

数据块”在要求音频设备播放、如果你需要试试做“音频断续播放”可

以去试试、没事研究研究

在WavePlayer.cs中我调用了一些外部的API函数,我简单的说说他

们的各自是做什么的、因为写代码我不是很喜欢写注释 相信看过我

博文的差不多应该都知道吧、

waveOutOpen 打开波形音频输出设备

waveOutPrepareHeader 准备音频波形数据块头

waveOutWrite 写入波形数据块到音频设备

waveOutPause 暂停音频输出设备的播放

waveOutRestart 恢复音频输出设备的播放

waveOutReset 重置音频输出设备

waveOutClose 关闭音频输出设备

waveOutSetVolume 设置音频输出设备的音量

waveOutGetVolume 获取音频输出设备的音量

waveOutGetNumDevs 获取音频输出设备的数量

示例代码:http://pan.baidu.com/s/1jHp9X8i

需要注意,在压缩包中包含了一个wav格式的文件,差不多

50mb与源代码打包下来差不多也有30mb左右、

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值