开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 2207|回复: 1
收起左侧

[分享] C# WavePlayer / WaveOutWrite

[复制链接]
结帖率:33% (3/9)
发表于 2015-12-18 11:46:30 | 显示全部楼层 |阅读模式   四川省成都市
本帖最后由 Modify 于 2015-12-18 11:47 编辑

在本文中主要调用WinMm库中导出的waveOutWrite完成
“波形数据块”的播放,当然,播放“波形数据块”的办法很多
如“MCI”、“BASS”、“WMP”、“DirectX Sound”等、
不过为什么我一定要用waveOutWrite呢?可能有人曾经想过
“音频解码器”是怎么开发的,那么我可以简单的说一下、
“音频解码器”比如mp3的mpeg audio,我们都知道它是一种
压缩格式音频,在Win32中如果需要传送声音到音频设备大概
三种方式,waveOutWrite、mdiStreamOut、DirectXSound
举个例子,假设我们需要让自己的浏览器程序静音那么我们
只需要拦截上面的几个函数,当然DirectXSound稍微麻烦些
需要拦截掉CreateSoundBuffer,DirectSoundCreate两个函数
如果mp3格式的音频文件需要被音频设备输出,那么必须把它
转换成被Win32可以直接转送到音频设备的“波形数据块”,不
然直接把mp3音频数据丢到声卡,你会听到“无限的噪音” 这是
音频设备不知道,你传送给它的是什么数据
原帖出处:http://blog.csdn.net/u012395622/article/details/50349964

那么我现在说明如果通过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 获取音频输出设备的数量
需要注意,在压缩包中包含了一个wav格式的文件,差不多
50mb与源代码打包下来差不多也有30mb左右、

结帖率:0% (0/2)

签到天数: 5 天

发表于 2023-12-4 23:24:30 | 显示全部楼层   四川省凉山彝族自治州
是易语言源代码吗
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 诚聘英才| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 800073686,邮箱:800073686@b.qq.com
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表