nes 红白机模拟器 第6篇 声音支持
InfoNES 源码中并没有包含 linux 的声音支持。
但提供 wince 和 win 的工程,文件,通过分析,win 的 DirectSound 发声,在使用 linux ALSA 实现。
先使用 DirectSound 模仿写一个 播放 wav 的程序。
为了简单,我这里使用 vc++ 6.0 (vs2015 实在太大了,电脑装上太卡)。
新建一个 mfc exe 项目,基于对话框。放一个按钮,双击添加事件。
添加头文件引用
#include <mmsystem.h>
#pragma comment(lib,"Winmm.lib")
点击 开始播放 事件
void CWavDlg::OnButtonPlay()
{
// TODO: Add your control notification handler code here
PlaySound(_T("1.wav"), NULL, SND_NOWAIT);
}
在 debug 目录,放一个 1.wav 生成可执行文件,点 开始播放, 果然可以播放出来。(win 的东西就是这么简单实用)。
分析 InfoNES_Sound_Win.cpp
类初始化
1 DIRSOUND::DIRSOUND(HWND hwnd) 2 { 3 DWORD ret; 4 WORD x; 5 6 // init variables 7 iCnt = Loops * 3 / 4; // loops:20 iCnt:20*3/4 = 15 8 9 for ( x = 0;x < ds_NUMCHANNELS; x++ ) // ds_NUMCHANNELS = 8 10 { 11 lpdsb[x] = NULL; // DirectSoundBuffer lpdsb[ds_NUMCHANNELS]; 8个 初始化为 NULL 12 } 13 14 // init DirectSound 创建一个 DirectSound 里面有 8个 DirectSoundBuffer 15 ret = DirectSoundCreate(NULL, &lpdirsnd, NULL); 16 17 if (ret != DS_OK) 18 { 19 InfoNES_MessageBox( "Sound Card is needed to execute this application." ); 20 exit(-1); 21 } 22 23 // set cooperative level 24 #if 1 25 //设置属性不重要 26 ret = lpdirsnd->SetCooperativeLevel(hwnd, DSSCL_PRIORITY); 27 #else 28 ret = lpdirsnd->SetCooperativeLevel( hwnd, DSSCL_NORMAL ); 29 #endif 30 31 if ( ret != DS_OK ) 32 { 33 InfoNES_MessageBox( "SetCooperativeLevel() Failed." ); 34 exit(-1); 35 } 36 }
SoundOpen
1 WORD DIRSOUND::AllocChannel(void) 2 { 3 WORD x; 4 5 //判断 lpdsb 找到一个 为空的 这里应该返回0 6 for (x=0;x<ds_NUMCHANNELS;x++) 7 { 8 if (lpdsb[x] == NULL) 9 { 10 break; 11 } 12 } 13 14 if ( x == ds_NUMCHANNELS ) 15 { 16 /* No available channel */ 17 InfoNES_MessageBox( "AllocChannel() Failed." ); 18 exit(-1); 19 } 20 21 return (x); 22 } 23 24 void DIRSOUND::CreateBuffer(WORD channel) 25 { 26 DSBUFFERDESC dsbdesc; //SoundBuffer 描述 27 PCMWAVEFORMAT pcmwf; //wav fmt 格式描述 28 HRESULT hr; 29 30 //清0 31 memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT)); 32 //pcm 格式 33 pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; 34 //1个声道 35 pcmwf.wf.nChannels = ds_CHANSPERSAMPLE; 36 //采样率 44100 37 pcmwf.wf.nSamplesPerSec = ds_SAMPLERATE; 38 //对齐 采样率 / 8 * 声道数 = 44100 / 8 * 1 = 5512.5 39 pcmwf.wf.nBlockAlign = ds_CHANSPERSAMPLE * ds_BITSPERSAMPLE / 8; 40 //缓存区大小 44100*5512.5 = 243101250 41 pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign; 42 //8位 声音 43 pcmwf.wBitsPerSample = ds_BITSPERSAMPLE; 44 45 //清0 46 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 47 dsbdesc.dwSize = sizeof(DSBUFFERDESC); 48 dsbdesc.dwFlags = 0; 49 //缓存大小 735 * 15 = 11025 50 dsbdesc.dwBufferBytes = len[channel]*Loops; 51 dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; 52 53 hr = lpdirsnd->CreateSoundBuffer(&dsbdesc, &lpdsb[channel], NULL); 54 55 if (hr != DS_OK) 56 { 57 InfoNES_MessageBox( "CreateSoundBuffer() Failed." ); 58 exit(-1); 59 } 60 } 61 62 //samples_per_sync = 735 sample_rate = 44100 63 BOOL DIRSOUND::SoundOpen(int samples_per_sync, int sample_rate) 64 { 65 //ch 1 WORD unsigned short 类型 , 创建一个 通道 , 返回 第0 个 SoundBuffer 66 ch1 = AllocChannel(); 67 68 /** 69 * 参数定义 70 * BYTE *sound[ds_NUMCHANNELS]; 71 * DWORD len[ds_NUMCHANNELS]; 72 */ 73 //申请了一个 735 大小的 Byte 74 sound[ch1] = new BYTE[ samples_per_sync ]; 75 //记录了 大小 735 76 len[ch1] = samples_per_sync; 77 78 if ( sound[ch1] == NULL ) 79 { 80 InfoNES_MessageBox( "new BYTE[] Failed." ); 81 exit(-1); 82 } 83 84 //创建缓存区 85 CreateBuffer( ch1 ); 86 87 /* Clear buffer */ 88 FillMemory( sound[ch1], len[ch1], 0 ); 89 //执行15次 90 for ( int i = 0; i < Loops; i++ ) 91 SoundOutput( len[ch1], sound[ch1] ); 92 93 /* Begin to play sound */ 94 Start( ch1, TRUE ); 95 96 return TRUE; 97 }
SoundOutput
1 //初始化时 执行 samples:735 wave:NULL 2 BOOL DIRSOUND::SoundOutput(int samples, BYTE *wave) 3 { 4 /* Buffering sound data */ 5 //将 wave 复制到 sound 6 CopyMemory( sound[ ch1 ], wave, samples ); 7 8 /* Copying to sound data buffer */ 9 FillBuffer( ch1 ); 10 11 /* Play if Counter reaches buffer edge */ 12 //初始化时 iCnt:15 Loops:20 13 if ( Loops == ++iCnt ) 14 { 15 iCnt = 0; 16 } 17 //这里 iCnt = 16 18 return TRUE; 19 } 20 void DIRSOUND::FillBuffer( WORD channel ) 21 { 22 LPVOID write1; 23 DWORD length1; 24 LPVOID write2; 25 DWORD length2; 26 HRESULT hr; 27 28 //得到要写入的地址 29 hr = lpdsb[channel]->Lock( iCnt * len[channel], len[channel], &write1, &length1, &write2, &length2, 0 ); 30 31 //如果返回DSERR_BUFFERLOST,释放并重试锁定 32 if (hr == DSERR_BUFFERLOST) 33 { 34 lpdsb[channel]->Restore(); 35 36 hr = lpdsb[channel]->Lock( iCnt * len[channel], len[channel], &write1, &length1, &write2, &length2, 0 ); 37 } 38 39 if (hr != DS_OK) 40 { 41 InfoNES_MessageBox( "Lock() Failed." ); 42 exit(-1); 43 } 44 45 //写入数据 46 CopyMemory( write1, sound[channel], length1 ); 47 48 if (write2 != NULL) 49 { 50 CopyMemory(write2, sound[channel] + length1, length2); 51 } 52 //解锁 53 hr = lpdsb[channel]->Unlock(write1, length1, write2, length2); 54 55 if (hr != DS_OK) 56 { 57 InfoNES_MessageBox( "Unlock() Failed." ); 58 exit(-1); 59 } 60 }
Play
1 //初始化时 ch1 重复播放 2 void DIRSOUND::Start(WORD channel, BOOL looping) 3 { 4 HRESULT hr; 5 6 hr = lpdsb[channel]->Play( 0, 0, looping == TRUE ? DSBPLAY_LOOPING : 0 ); 7 8 if ( hr != DS_OK ) 9 { 10 InfoNES_MessageBox( "Play() Failed." ); 11 exit(-1); 12 } 13 }
播放调用
1 void InfoNES_SoundOutput( int samples, BYTE *wave1, BYTE *wave2, BYTE *wave3, BYTE *wave4, BYTE *wave5 ) 2 { 3 //rec_freq = 735 4 BYTE wave[ rec_freq ]; 5 //取了 wave1~5 的平均值 6 for ( int i = 0; i < rec_freq; i++) 7 { 8 wave[i] = ( wave1[i] + wave2[i] + wave3[i] + wave4[i] + wave5[i] ) / 5; 9 } 10 #if 1 11 if (!lpSndDevice->SoundOutput( samples, wave ) ) 12 #else 13 if (!lpSndDevice->SoundOutput( samples, wave3 ) ) 14 #endif 15 { 16 InfoNES_MessageBox( "SoundOutput() Failed." ); 17 exit(0); 18 } 19 }
最后总结得到几个有用的参数:
声道数 1
采样率 44100
采样位数 8
每次播放块大小(NES APU 每次生成一块)735
更新 2018-11-04
已移值到 alsa-lib 支持,播放正常,已更新至 github 。 可以在 置顶博文中找地址。
转载于:https://www.cnblogs.com/ningci/p/6863510.html
nes 红白机模拟器 第6篇 声音支持相关推荐
- ADI Blackfin DSP处理器-BF533的开发详解70:NES 红白机模拟器(含源码)
硬件准备 ADSP-EDU-BF533:BF533开发板 AD-HP530ICE:ADI DSP仿真器 软件准备 Visual DSP++软件 硬件链接 代码实现功能 代码实现了 NES 游戏模拟器在 ...
- fc安卓模拟器_安利一款手机上的红白机模拟器
戳上面的蓝字关注我哦! 使用平台:安卓 软件简介: NES.emu是一款任天堂红白机(NES.FC)模拟器,软件支持横竖屏.自动保存游戏进度.按键自定义等功能,还可以自行编辑作弊文件,小编为大家带来的 ...
- 撸一个VS Code插件——红白机模拟器 支持手柄 支持保存
分享我自己写的VS Code红白机模拟器 前言 我曾经利用 jense 这个库封装了一个vue组件的nes模拟器:nes-vue: Vue 3 的NES(FC)模拟器组件 (gitee.com),最近 ...
- 使用C++实现FC红白机模拟器 Cartridge 与 Mapper(实现篇)
(继上篇:原理篇,下:实现篇) 2. Cartridge 与 Mapper的实现 首先我们在QT中创建两个类,Cartridge 与 Mapper类: Cartridge 类负责加载和解析ROM,因为 ...
- 使用C++实现FC红白机模拟器 Cartridge 与 Mapper(原理篇)
1. 认识nes文件 我们既然是模拟,就不可能使用实体的卡带硬件.那我们如何获取游戏文件呢?好在已经有人为我们准备好了(心怀感恩). .nes文件是NES(FC)的rom文件,关于它的来龙去脉这里就不 ...
- iCade红白机手柄 随时随地重温美好时光
我们总是希望一些经典能够完美重现,所以当发现超级玛丽.拳皇这样的经典游戏的复刻版出现在应用程序列表中时,总是会先在心里内牛满面地感谢这些老牌游戏厂商一番,然后欢天喜地速度下载开玩,但是很快你就伤心地哭 ...
- 使用c++模拟红白机——概论篇(一)
任天堂的红白机系列的游戏应该是大家的童年了,红白机,又称FC,随着计算机技术的不断发展,现在市场上基本已经淘汰了红白机系列的硬件设备了.我偶尔的一个突发奇想,想要在体验一下红白机游戏的乐趣,于是乎我想 ...
- 【历史上的今天】10 月 18 日:Internet Explorer 7 正式发布;全球首家网络银行开业;“美版红白机” NES 诞生
整理 | 王启隆 透过「历史上的今天」,从过去看未来,从现在亦可以改变未来. 今天是 2022 年 10 月 18 日,在 100 年前的今天,英国 BBC 广播电视台成立:BBC 是世界上最大的新闻 ...
- 红白机原理(零)前言
今天来聊聊 FC 游戏机,FC 的意思就是 Family Computer,虽然如今渐渐落寞被淘汰,但在当年的确是风靡全球,不负 Family Computer 这名字. FC 大家应该基本知道吧,e ...
最新文章
- Java 基础------16进制转2进制
- Spring MVC中注解 @ModelAttribute
- 【Java面试题】汽水瓶问题
- jar包打补丁 jar -uf_maven项目引入本地jar包的方法
- 和你谈谈数据分析报告
- 使用微软的 ilasm 和 ildasm 对. net程序进行编译和反编译
- request获取各种路径记录
- bootstrap table列宽设置无效解决
- UVa1592 数据库(摘)
- Aerospike 使用场景
- 进程间通信之管道与有名管道
- XOCDE构建提示Command /usr/bin/codesign failed with exit code 1的解决办法
- linux服务器生成密钥后无法登陆,securecrt用密钥安全登陆服务器
- 边框颜色和背景色之间的关系
- 使用代理服务器打不开网页_代理服务器:信息安全表象下的另一面
- Python画玫瑰花源码
- DDR SDRAM的内部结构Cell Structure
- android studio视频路径,Android studio相关设置及实现存在于工程目录中的视频播放...
- CPM、CPC、CPA、CPS、CPL、CPR 是什么意思 -解析互联网广告术语
- 用互联网对接传统行业,改良还是颠覆?
热门文章
- jsp房屋租赁管理系统房屋管理系统JSP网上租房系统JSP房产信息网站房屋租赁系统房屋
- 九三学社邬玉良:破解大数据之患
- 调通sina33m下的GC0308(分色排版)V1.0
- 15款macbook pro A1502内存升级(8G升16G)
- 软件工程技术--第二章 可行性研究
- docker拉取远程私库的镜像_Docker入门-搭建docker私有仓库
- linux 解压rar格式的文件怎么打开,linux服务器怎么解压rar格式的文件
- 介绍Netsuite系统优势及中国合作商情况!
- m3u8转换到mp4 python_python-将爬取到的m3u8合并为mp4
- matlab tril a 1,matlab习题