通過前面的教程基本已經(jīng)能玩游戲了群嗤,但是有音樂才算得上完整,下面介紹 NES 的 APU
1. 簡介
APU 和 PPU 一樣托享,也是比較復(fù)雜的芯片,和 PPU 比起來簡單一些浸赫,但是比 CPU 復(fù)雜闰围,畢竟不具備通用性
APU 有 5 通道,2 個(gè)方波既峡,1 個(gè)三角波羡榴,1 個(gè)噪聲,1 個(gè) DMC
注:DMC 全稱為 delta modulation channel运敢,它用來產(chǎn)生方波校仑,三角波,噪聲產(chǎn)生不了的聲音传惠,聲音信息提前存入 rom 中迄沫,不考慮精度的情況下,可以生成任意波形卦方,比如鼓聲這種復(fù)雜的聲音羊瘩,就得用 DMC
NES 有 4 bit DAC,故電壓范圍為 0 - 15盼砍,但是 DMC 除外尘吗,它有 7 bit,范圍為 0 - 127
注:DAC 叫 “數(shù)模轉(zhuǎn)換器”浇坐, 作用是把數(shù)字量轉(zhuǎn)化為模擬量(電壓)睬捶,音頻信號(hào)就是典型的模擬信號(hào),其電壓隨時(shí)間變化近刘,所以通過 DAC擒贸,可以通過數(shù)字的方式生成音頻信號(hào)臀晃。
APU 寄存器分布如下:
通道 | 地址 | 操作 |
---|---|---|
方波1 (pulse1) | 0x4000 - 0x4003 | w |
方波2 (pulse2) | 0x4004 - 0x4007 | w |
三角波 (triangle) | 0x4008 - 0x400B | w |
噪聲 (noise) | 0x400C - 0x400F | w |
DMC | 0x4010 - 0x4013 | w |
狀態(tài)寄存器 | 0x4015 | rw |
幀計(jì)數(shù)器 | 0x4017 | w |
2. 時(shí)鐘
聲音有長有短,頻率也在時(shí)刻變化酗宋,這些都需要時(shí)鐘來提供积仗,APU 有兩個(gè)時(shí)鐘:
- 基本時(shí)鐘(APU 周期):CPU clock / 2
用于控制波形頻率 - 幀計(jì)數(shù)器:240Hz
用于控制波形持續(xù)時(shí)間
3. 狀態(tài)寄存器
由于 APU 有多個(gè)通道,所以提供了專門的 狀態(tài)寄存器 用于控制通道使能和讀取通道相關(guān)信息
-
寫 0x4015
BIT 作用 0 使能方波 1 1 使能方波 2 2 使能三角波 3 使能噪聲 4 使能 DMC 5 - 6 - 7 - -
讀 0x4015
BIT 作用 0 方波 1 長度計(jì)數(shù)器不為 0 1 方波 2 長度計(jì)數(shù)器不為 0 2 三角波長度計(jì)數(shù)器不為 0 3 噪聲長度計(jì)數(shù)器不為 0 4 DMC 長度計(jì)數(shù)器不為 0 5 - 6 幀中斷 7 DMC 中斷
讀 0x4015 后蜕猫,會(huì)清除 幀中斷 標(biāo)志
4. 幀計(jì)數(shù)器
幀計(jì)數(shù)器 位于地址 0x4017寂曹,用來驅(qū)動(dòng)各通道的長度,包絡(luò)等單元回右。該寄存器只用了 2 個(gè) bit隆圆,分別控制中斷使能與步進(jìn)方式:
BIT | 作用 |
---|---|
0 | 0:4 步模式,1:5 步模式 |
1 | 中斷禁止標(biāo)志翔烁,0:使能中斷渺氧,1:禁用中斷 |
2 - 7 | - |
這里肯定有人不理解什么是 4 步 5 步模式,還記得前面時(shí)鐘部分講到有 240Hz 的時(shí)鐘嗎蹬屹?該時(shí)鐘每周期會(huì)步進(jìn)一次侣背,如下:
4 步模式 | 5 步模式 | 功能 |
---|---|---|
- - - f | - - - - - | 產(chǎn)生中斷 |
- l - l | - l - - l | 驅(qū)動(dòng)長度計(jì)數(shù)器(Length counter)和掃描單元(Sweep) |
e e e e | e e e - e | 驅(qū)動(dòng)包絡(luò)(Envelope)與線性計(jì)數(shù)器(Linear counter) |
注:長度計(jì)數(shù)器,包絡(luò)等概念一會(huì)再講
比如慨默,如果當(dāng)前為 4 步模式贩耐,則驅(qū)動(dòng)包絡(luò)與線性計(jì)數(shù)器的頻率為 240Hz,產(chǎn)生中斷的頻率為 60Hz厦取,驅(qū)動(dòng)長度計(jì)數(shù)器和掃描單元的頻率為 120Hz
用代碼實(shí)現(xiàn)也很簡單潮太,用 switch case 就行:
// processFrameCounter 調(diào)用頻率為 240Hz
private processFrameCounter(): void {
if (this.mode === 0) { // 4 Step mode
switch (this.frameCounter % 4) {
case 0:
this.processEnvelopeAndLinearCounter();
break;
case 1:
this.processLengthCounterAndSweep();
this.processEnvelopeAndLinearCounter();
break;
case 2:
this.processEnvelopeAndLinearCounter();
break;
case 3:
this.triggerIRQ();
this.processLengthCounterAndSweep();
this.processEnvelopeAndLinearCounter();
break;
}
} else { // 5 Step mode
switch (this.frameCounter % 5) {
case 0:
this.processEnvelopeAndLinearCounter();
break;
case 1:
this.processLengthCounterAndSweep();
this.processEnvelopeAndLinearCounter();
break;
case 2:
this.processEnvelopeAndLinearCounter();
break;
case 3:
break;
case 4:
this.processLengthCounterAndSweep();
this.processEnvelopeAndLinearCounter();
break;
}
}
}
5. 單元
前面已經(jīng)見過了長度計(jì)數(shù)器(Length counter),掃描單元(Sweep)虾攻,包絡(luò)(Envelope)铡买,線性計(jì)數(shù)器(Linear counter),每個(gè)通道都包含部分上述單元霎箍,每通道的單元都可以由該通道的寄存器控制奇钞,單元列表如下:
通道 | 單元 |
---|---|
方波1 (pulse1) | Timer, length counter, envelope, sweep |
方波2 (pulse2) | Timer, length counter, envelope, sweep |
三角波 (triangle) | Timer, length counter, linear counter |
噪聲 (noise) | Timer, length counter, envelope, linear feedback shift register |
DMC | Timer, memory reader, sample buffer, output unit |
- Timer
每個(gè)通道都有,它使用基本時(shí)鐘(CPU clock / 2)漂坏,用于控制波形頻率 - Length counter(長度計(jì)數(shù)器)
除 DMC 外其他通道都有蛇券,用于控制波形持續(xù)時(shí)間 - Envelope(包絡(luò))
只有方波和噪聲通道有,用于控制音量隨時(shí)間的變化的情況樊拓,比如車離你越來越遠(yuǎn)纠亚,音量越來越小的場景 - Sweep(掃描單元)
只有方波通道有,用于控制聲音頻率隨時(shí)間變化筋夏,可以想象下汽車車速越來越快時(shí)發(fā)動(dòng)機(jī)聲音越來越尖的場景 - Linear counter (線性計(jì)數(shù)器)
只有三角波通道有蒂胞,與 Length counter 一樣,也用來控制音頻持續(xù)時(shí)間条篷∑妫肯定有人會(huì)問這樣功能不就重復(fù)了嗎蛤织。其實(shí)結(jié)合前面的 4 步與 5 步序列我們可以發(fā)現(xiàn),Linear counter 一個(gè)周期處理 4 次鸿染,Length counter 一個(gè)周期只處理 2 次指蚜,這樣 Linear counter 的精度就是 Length counter 的 2 倍,可以做更高精度的定時(shí) - linear feedback shift register(線性反饋移位寄存器)
只有噪聲通道有涨椒,用來發(fā)生偽隨機(jī)數(shù)摊鸡,以此來產(chǎn)生噪聲 - Memory reader(內(nèi)存讀取單元)
只有 DMC 通道有,讀取總線上編碼好的數(shù)據(jù)到 sample buffer - Sample buffer(采樣緩沖)
只有 DMC 通道有蚕冬,緩沖 DMC 數(shù)據(jù) - Output unit(輸出單元)
只有 DMC 通道有免猾,用于生成音量數(shù)據(jù)
6. 混音器
復(fù)雜的音樂是有各t種音色組合起來的,APU 5 個(gè)通道充分運(yùn)用才能發(fā)出動(dòng)聽的音樂囤热×蕴幔混音器就是用來組合 5 個(gè)通道音頻整合后輸出一個(gè)信號(hào)的東西
下列公式會(huì)將 5 個(gè)通道聲音整合后輸出為一個(gè)范圍 0 ~ 1.0 的信號(hào):
output = pulse_out + tnd_out
95.88
pulse_out = ------------------------------------
(8128 / (pulse1 + pulse2)) + 100
159.79
tnd_out = -------------------------------------------------------------
1
----------------------------------------------------- + 100
(triangle / 8227) + (noise / 12241) + (dmc / 22638)
具體參考 http://wiki.nesdev.com/w/index.php/APU_Mixer
在模擬的時(shí)候混音器有兩種實(shí)現(xiàn)方式:查表和直接計(jì)算,直接計(jì)算代碼最簡單:
output = pulse_out + tnd_out
pulse_out = 0.00752 * (pulse1 + pulse2)
tnd_out = 0.00851 * triangle + 0.00494 * noise + 0.00335 * dmc