============================================================ == == == FDS Sound == == == == by Disch == == == ============================================================ Release 1 - 07/14/2004 *************************************** *************************************** ** Introduction: *************************************** *************************************** This doc is to help those NSF Player / NES Emu authors who are discouraged by the utter lack of English docs on the workings of FDS sound. Often times... the best option for authors is to examine other existing emus' source and base their own code off of it. However... other people's source is often unclear, and it's generally more fun to build your own code anyway. Note that I have never: - Heard real FDS sound... or even seen a real FDS system in real life - Comprehensively read any FDS docs But I have: - Thoroughly examined existing emulators' sources (mainly VirtuaNES) - Reproduced FDS sound emulation in my own NSF player (without just copy/pasting from other sources) Therefore, this doc is by no means an accurate technical source for FDS sound. But hopefully it will be enough to get decent FDS support in your emu/player. Since no recent/decent technical documents have been translated, this doc will hopefully be the next best thing for those of us who don't know Japanese. If you do know Japanese, I recommend checking out Norix's FDS sound doc. NESdev has a copy available for download: http://nesdev.parodius.com/FDSSOUND_v1.1.txt Pretty much everything in this doc is based off the FDS Sound source in VirtuaNES (also written by Norix). If anyone out there is willing to translate the above linked doc, it would be much appreciated. In the meantime, hopefully this doc will suffice. *************************************** *************************************** ** Brief Intro to FDS Sound *************************************** *************************************** The FDS Sound channel has a configurable waveform. The waveform has 64 ($40) steps... each step having 64 ($40) levels of output. The playback of this waveform will be called the "main unit" later in the doc. There are also $21 different volume levels for the wave, $20 being full volume, and $00 being silent. Volume appears to be linear. On top of this main volume, there is also a "master volume" for the FDS, which has only 4 possible values (more on this in the register descriptions). For clarity sake, whenever this doc refers to "volume", it's referring to the $21 level main volume. The 4 step volume will always be referred to as the "master volume" There is also a volume envelope unit, which is able to raise/lower the volume, as well as a sweep envelope unit and frequency modulation unit (sometimes also called LFO or Effector unit). Each unit has its own function, however they all seem to be interdependent at one point or another. There are 2 64-entry tables needed for FDS sound emulation. One holds the custom waveform (accessable via $4040 through $407F), the other holds the custom frequency modulation table used by the Modulation unit (accessable via $4088). For more info on the latter, see the $4088 register description and the Modulation Unit description section. *************************************** *************************************** ** Sound Registers *************************************** *************************************** -- $4040 to $407F -- bits: ..ww wwww (Read / Write) w = waveform These registers control the customizable waveform. $4040 controls the first step in the wave, $4041 is the 2nd step, etc. Writes to these registers are ignored unless Write Mode is turned on (see register $4089). If Write Mode is on, the high 2 bits of the written value are dropped, and the low 6 bits are the step in the waveform. When read, the expected 6-bit waveform is returned, and bit 6 ($40) always is on. These registers appear to be readable regardless of whether or not Write Mode is active. -- $4080 -- bits: dmvv vvvv (Write Only) d = Volume Envelope Disable (1 = disabled, 0 = enabled). m = Volume Envelope Mode (1 = increase volume gain, 0 = decrease volume gain) v = Volume and Volume Gain [or] Volume Envelope Speed If 'd' is set, Volume envelope is disabled and 'v' sets the Volume Envelope Gain as well as the current output volume. In the case that 'd' is set and 'v' is greater than $20, the output volume gets clipped at $20 (max volume), although the Volume Envelope Gain *still* gets set to the written value. If 'd' is clear, volume envelope is enabled and 'v' sets the volume envelope speed. Volume Envelope Gain and Output volume are unaffected in this case. -- $4082 -- bits: ffff ffff (Write Only) f = Low 8 bits of the main unit's frequency -- $4083 -- bits: me.. ffff (Write Only) m = Main Unit disable (1 = disabled) e = Envelope disable (1 = disabled) f = High 4 bits of the main unit's frequency If 'm' is set, the main unit is disabled and the channel is inaudible. However, Volume Envelope, Sweep Envelope, and Modulation units may still be active. If 'e' is set, both Volume Envelope and Sweep Envelope units are disabled (despite their own individual enable toggles). Modulation unit seems to be unaffected by this bit. 'f' holds the upper 4 bits of the main unit frequency (combined with register $4082, you have the full 12-bit frequency value). If the frequency is zero, the Main unit is disabled (channel silent). -- $4084 -- bits: dmss ssss (Write Only) d = Sweep Envelope Disable (1 = disabled) m = Sweep Envelope Mode (1 = increase sweep gain, 0 = decrease sweep gain) s = Sweep Gain [or] Sweep Envelope Speed If 'd' is set, Sweep envelope is disabled and 's' sets the Sweep Gain. If 'd' is clear, Sweep envelope is enabled, 's' becomes the Sweep Envelope Speed, and Sweep Gain is unaffected. -- $4085 -- bits: .bbb bbbb (Write Only) b = Sweep Bias (7 bit signed) This register sets the Sweep Bias to the value supplied by 'b'. Note that this value is signed... so $7F would be -1, $3F would be 63. The "Sweep Bias" is a 7 bit signed value used by the Modulation unit in calculating the frequency bend. When the Sweep Bias is negative... the Modulation unit will be bending the frequency down, and when it's positive the Modulation unit will be bending the frequency up. For more info on this see the Modulation Unit and Frequency Calculation sections. Note that any write to this register also resets the Modulation Unit's address to zero. This address is used by the Modulation Unit when looking up entries written to the Modulation table (via $40880). For more details see the Modulation Unit section. -- $4086 -- bits: ffff ffff (Write Only) f = Low 8 bits of Modulation unit's frequency -- $4087 -- bits: d... ffff (Write Only) d = Modulation Disable (1 = disabled) f = High 4 bits of Modulation Unit's frequency. 'f' combines with the value written to $4086 to make the 12-bit frequency for the Modulation unit. If the frequency is zero, the Modulation Unit is disabled -- $4088 -- bits: .... .mmm (Write Only) m = Modulation input Writing to this register puts the value written at the END of the modulation table **twice**, and pushes each entry already in the table 2 places to the front. The first 2 entries of the Modulation table are pushed out and lost. For a graphical example: old, old <--- ModTable_0 <--- ModTable_1 <--- ... <--- ModTable_63 <--- 'm', 'm' -- $4089 -- bits: w... ..vv (Write Only) w = Wave Write Mode (1 = wave write mode) v = "Master Volume" control If 'w' is set... the Main Unit is disabled (channel is silenced), but the custom wave-form becomes writable via $4040 through $407F. If 'w' is cleared... Wave Write Mode is off, and writes to $4040-$407F are ignored (but the channel can once again be audible). 'v' controls the 2-bit 'Master Volume': 0 = FDS plays at 30/30 (100%) volume 1 = FDS plays at 20/30 (~67%) volume 2 = FDS plays at 15/30 ( 50%) volume 3 = FDS plays at 12/30 ( 40%) volume Note this volume change takes place alone side the normal output volume (the $21 step volume). -- $408A -- bits: ssss ssss (Write Only) s = Envelope Speed This value alters the speed of Volume Envelope and Sweep Envelope (along with their own individual timers). If this value is zero, Volume and Sweep Envelope are disabled. In my experience... NSFs which write to this register are rare. So I don't think this register should be inited with $00 at system startup, since that would disable Envelops (and since most NSFs don't write to this reg, they would permanently stay off). So, in my NSF player, I default this value to $FF, which seems to work (and sounds about how it should). However I make no claim as to the accuracy of this. -- $4090 -- (Read Only) This register returns the current Volume Gain level ($00-$3F). Bit 6 ($40) appears to always be on, as well. -- $4092 -- (Read Only) This register returns the current Sweep Gain level ($00-$3F). Bit 6 ($40) appears to always be on, as well. *************************************** *************************************** ** Frequency Calculation and Timing *************************************** *************************************** The 4 different areas of the FDS Sound channel (Volume Envelope, Sweep Envelope, Modulation Unit, and Main Unit) are clocked at independent rates. This section covers how to calculate the number of clocks per second (in Hz) from the various values written to the registers. -------------------------- -- Volume Envelope Unit: -------------------------- Hz = (232 * 960) / (Envelope_Speed * (Volume_Envelope_Speed + 1)) Hz = how many times per second a clock occurs Envelope_Speed = value written to $408A (if zero, Envelope is disabled) Volume_Envelope_Speed = value set by writes to $4080 When I was working with this formula to fit in my NSF player, I was converting the formula to work with NES CPU cycles (~1798772 a second). This involved taking the reciprical of the above formula and dividing by the NES frequency. This produced a somewhat simpler formula: Cyc = 8 * Envelope_Speed * (Volume_Envelope_Speed + 1) Cyc = Number of NES CPU cycles that need to pass for 1 clock to occur For this alternate formula in Hertz: Hz = NES / (8 * Envelope_Speed * (Volume_Envelope_Speed + 1)) NES = NTSC NES CPU frequency (1789772.7272) Since all the other NES channels revolve around the CPU clock rate, I find this second formula to be more likely, since it also uses the NES clock as the base. However, that is purely speculation on my part. In any case... the 2 formulas produce very similar results. If you work the conversion yourself... you end up simplifying: 1789772.7272 / (232 * 960) = ~8.036 -------------------------- -- Sweep Envelope Unit: -------------------------- Hz = (232 * 960) / (Envelope_Speed * (Sweep_Envelope_Speed + 1)) ... or alternatively ... Cyc = 8 * Envelope_Speed * (Sweep_Envelope_Speed + 1) Hz = NES / (8 * Envelope_Speed * (Sweep_Envelope_Speed + 1)) Sweep_Envelope_Speed = value set by writes to $4084 -------------------------- -- Modulation Unit: -------------------------- Hz = NES * ModFreq / 65536 ModFreq = 12-bit Modulation Frequency value set by $4086/$4087 Note that this frequency is how many times per second the modulation unit gets clocked... not how many times it goes through the entire Frequency Modulation Table (which would be this value / 64, since there are 64 entries in the table) -------------------------- -- Main Unit: -------------------------- Hz = NES * ( (Freq + Mod) / 65536 ) Freq = 12-bit Frequency set by $4082/$4083 Mod = Frequency change based on the Modulation unit (see next section) Note again that this is the how many times per second the main unit gets clocked (taking 1 step further in the table). The entire table is 64 entries, so the frequency of it playing the entire wave would be this value / 64. *************************************** *************************************** ** What each unit does in a clock *************************************** *************************************** Now that we know how often clocks occur... we need to know what each unit does on 1 clock. -------------------------- -- Volume Envelope Unit: -------------------------- Every clock... the Volume Envelope Unit alters the Volume_Gain depending on which we're in. Increase Mode: If Volume Gain is less than $20, it's increased by 1 Decrease Mode: If Volume Gain is greater than 0, it's decreased by 1 The mode is determined by bit 6 of $4080. Note that despite Volume Gain in only increased if less than $20... it still can be greater than $20 (up to $3F) if set that way through a write to $4080. Also on a clock.. the output volume is set to Volume Gain... or $20... whichever is less (so while Volume Gain may be higher than $20, the actual output volume caps at $20). -------------------------- -- Sweep Envelope Unit: -------------------------- Sweep Envelope unit behaves just like the Volume Envelope, only it alters Sweep Gain instead of Volume Gain. The Envelope Unit never pushes Sweep Gain above $20, but it still can get above $20 if set that way via $4084. Increase/Decrease mode is determined by bit 6 of $4084 Sweep Gain is used when calculating the Frequency change in the Modulation Unit: -------------------------- -- Modulation Unit: -------------------------- The Modulation Unit, when clocked, takes 1 step through the Modulation Table (set by writes to $4088). The Sweep Bias is adjusted based on the 3-bit value in the table: 0: 0 1: +1 2: +2 3: +4 4: 0 (On a value of 4, the Sweep Bias is reset to 0) 5: -4 6: -2 7: -1 The address of the Modulation unit is incremented so that next clock it will use the next 3-bit value in the table. This address wraps at 64 and can be reset to zero by any write to $4085. After the Sweep Bias is adjusted, it wraps to fit within a signed 7-bit value. That is... if it goes greater than 63, it wraps around to -64, and if it goes below -64, it wraps to 63. The Modulation Unit works by altering the Frequency of the Main Unit by a value calculated from the Sweep Gain and Sweep Bias values. The logic for this calculation eludes me... so I'll just throw out the formula in the form of pseudo-code: temp = Sweep_Bias * Sweep_Gain; if( temp & 0x0F ) { temp /= 16; if( Sweep_Bias < 0 ) temp -= 1; else temp += 2; } else temp /= 16; if( temp > 193 ) temp -= 258; // not a typo... for some reason the wraps if( temp < -64 ) temp += 256; // are inconsistent Mod = Freq * temp / 64; In this code, Freq is the 12-bit MAIN UNIT frequency, and Mod is the amount that frequency is altered (to bend the playback frequency in either direction). This generated 'Mod' value is used in the frequency calculation of the main unit (given earlier): Hz = NES * ( (Freq + Mod) / 65536 ) If at any time the Modulation unit is off, 'Mod' is zero. Otherwise 'Mod' is the above calculated value. If Freq + Mod produces a number less than or equal to zero, the channel is presumably silenced (<0 Hz output isn't really possible/audible). -------------------------- -- Main Unit: -------------------------- When the main Unit is clocked... the next entry in the 64 entry waveform table (accessed via regs $4040-$407F) is output. Once the 64'th sample is played ($407F), the table restarts ($4040 is played) and it continues to loop. *************************************** *************************************** ** Unit Activity *************************************** *************************************** Since there are many factors that could disable a unit... I figured I should put in an overview section to cover all the needed requirements for the channel to be active. Remember that each unit can be active regardless of the activitiy of other units. For example... even though the main unit is off and the channel is silent, this does not mean the Volume Envelope or Modulation units are inactive. If any of the supplied conditions are false... the unit is inactive and will not be clocked. All conditions must be true for the unit to be active. Volume Envelope Unit: - Volume Envelope must be enabled (bit 7 of $4080 must be off) - Envelope Speed must be nonzero (set by $408A) - Envelope must be enabled (bit 6 of $4083 must be off) Sweep Envelope Unit: - Sweep Envelope must be enabled (bit 7 of $4084 must be off) - Envelope Speed must be nonzero (set by $408A) - Envelope must be enabled (bit 6 of $4083 must be off) Modulation Unit: - Modulation must be enabled (bit 7 of $4087 must be off) - Modulation frequency must be non-zero (set by $4086/$4087) Main Unit: - Main Unit must be enabled (bit 7 of $4083 must be off) - Main Unit Frequency must be non-zero (set by $4082/$4083) - 'Freq + Mod' must be greater than zero (see Frequency Calculation section) - Write Mode must be off (bit 7 of $4089 must be off) *************************************** *************************************** ** Conclusion *************************************** *************************************** That's pretty much it. Hopefully this will help aspiring NES emu authors in the FDS department. I know I was really discouraged when the only decent FDS Sound doc I could find was in Japanese without a translation. Hopefully this will shed some light on the subject so that people won't suffer the same problems I did. *************************************** *************************************** ** Version History *************************************** *************************************** -- Release 1 (07/14/2004) First Release.