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篇 声音支持相关推荐

  1. ADI Blackfin DSP处理器-BF533的开发详解70:NES 红白机模拟器(含源码)

    硬件准备 ADSP-EDU-BF533:BF533开发板 AD-HP530ICE:ADI DSP仿真器 软件准备 Visual DSP++软件 硬件链接 代码实现功能 代码实现了 NES 游戏模拟器在 ...

  2. fc安卓模拟器_安利一款手机上的红白机模拟器

    戳上面的蓝字关注我哦! 使用平台:安卓 软件简介: NES.emu是一款任天堂红白机(NES.FC)模拟器,软件支持横竖屏.自动保存游戏进度.按键自定义等功能,还可以自行编辑作弊文件,小编为大家带来的 ...

  3. 撸一个VS Code插件——红白机模拟器 支持手柄 支持保存

    分享我自己写的VS Code红白机模拟器 前言 我曾经利用 jense 这个库封装了一个vue组件的nes模拟器:nes-vue: Vue 3 的NES(FC)模拟器组件 (gitee.com),最近 ...

  4. 使用C++实现FC红白机模拟器 Cartridge 与 Mapper(实现篇)

    (继上篇:原理篇,下:实现篇) 2. Cartridge 与 Mapper的实现 首先我们在QT中创建两个类,Cartridge 与 Mapper类: Cartridge 类负责加载和解析ROM,因为 ...

  5. 使用C++实现FC红白机模拟器 Cartridge 与 Mapper(原理篇)

    1. 认识nes文件 我们既然是模拟,就不可能使用实体的卡带硬件.那我们如何获取游戏文件呢?好在已经有人为我们准备好了(心怀感恩). .nes文件是NES(FC)的rom文件,关于它的来龙去脉这里就不 ...

  6. iCade红白机手柄 随时随地重温美好时光

    我们总是希望一些经典能够完美重现,所以当发现超级玛丽.拳皇这样的经典游戏的复刻版出现在应用程序列表中时,总是会先在心里内牛满面地感谢这些老牌游戏厂商一番,然后欢天喜地速度下载开玩,但是很快你就伤心地哭 ...

  7. 使用c++模拟红白机——概论篇(一)

    任天堂的红白机系列的游戏应该是大家的童年了,红白机,又称FC,随着计算机技术的不断发展,现在市场上基本已经淘汰了红白机系列的硬件设备了.我偶尔的一个突发奇想,想要在体验一下红白机游戏的乐趣,于是乎我想 ...

  8. 【历史上的今天】10 月 18 日:Internet Explorer 7 正式发布;全球首家网络银行开业;“美版红白机” NES 诞生

    整理 | 王启隆 透过「历史上的今天」,从过去看未来,从现在亦可以改变未来. 今天是 2022 年 10 月 18 日,在 100 年前的今天,英国 BBC 广播电视台成立:BBC 是世界上最大的新闻 ...

  9. 红白机原理(零)前言

    今天来聊聊 FC 游戏机,FC 的意思就是 Family Computer,虽然如今渐渐落寞被淘汰,但在当年的确是风靡全球,不负 Family Computer 这名字. FC 大家应该基本知道吧,e ...

最新文章

  1. Java 基础------16进制转2进制
  2. Spring MVC中注解 @ModelAttribute
  3. 【Java面试题】汽水瓶问题
  4. jar包打补丁 jar -uf_maven项目引入本地jar包的方法
  5. 和你谈谈数据分析报告
  6. 使用微软的 ilasm 和 ildasm 对. net程序进行编译和反编译
  7. request获取各种路径记录
  8. bootstrap table列宽设置无效解决
  9. UVa1592 数据库(摘)
  10. Aerospike 使用场景
  11. 进程间通信之管道与有名管道
  12. XOCDE构建提示Command /usr/bin/codesign failed with exit code 1的解决办法
  13. linux服务器生成密钥后无法登陆,securecrt用密钥安全登陆服务器
  14. 边框颜色和背景色之间的关系
  15. 使用代理服务器打不开网页_代理服务器:信息安全表象下的另一面
  16. Python画玫瑰花源码
  17. DDR SDRAM的内部结构Cell Structure
  18. android studio视频路径,Android studio相关设置及实现存在于工程目录中的视频播放...
  19. CPM、CPC、CPA、CPS、CPL、CPR 是什么意思 -解析互联网广告术语
  20. 用互联网对接传统行业,改良还是颠覆?

热门文章

  1. jsp房屋租赁管理系统房屋管理系统JSP网上租房系统JSP房产信息网站房屋租赁系统房屋
  2. 九三学社邬玉良:破解大数据之患
  3. 调通sina33m下的GC0308(分色排版)V1.0
  4. 15款macbook pro A1502内存升级(8G升16G)
  5. 软件工程技术--第二章 可行性研究
  6. docker拉取远程私库的镜像_Docker入门-搭建docker私有仓库
  7. linux 解压rar格式的文件怎么打开,linux服务器怎么解压rar格式的文件
  8. 介绍Netsuite系统优势及中国合作商情况!
  9. m3u8转换到mp4 python_python-将爬取到的m3u8合并为mp4
  10. matlab tril a 1,matlab习题