Complexity of putting music into a game

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Complexity of putting music into a game
by on (#157515)
I haven't really done anything with music yet, apart from the first two or three Nerdy Nights music tutorials. So I wanted to ask:

Assuming that I have a soundtrack, i.e. it is known what notes are put into what APU channel for what amount of time: How complex is the actual code for this?

I was under the impression that you simply read the values from a constant array and then put them into the port addresses. A simple loop that reads the APU value and the number of frames this sound is supposed to be on. And then you set the value to the APU port, start the counter and when the counter is 0, you set the next music value. And this you do with all three channels.

Pseudo code:
Code:
OncePerFrame:

if StopMusic
    WriteToApu(SquareWavePort1, 0)
    WriteToApu(SquareWavePort2, 0)
    Timer = 0
    ArrayIndex = 0
else if Timer = 0
    MusicData1 = ReadNextArrayValue
    MusicData2 = ReadNextArrayValue
    Timer = ReadNextArrayValue

    if ArrayIndex = ArraySize
        ArrayIndex = 0

    WriteToApu(SquareWavePort1, MusicData1)
    WriteToApu(SquareWavePort1, MusicData2)
else
    Timer = Timer - 1


But I've read several times that people write their own sound drivers etc. So, is this really that complicated? Do I have to implement an elaborated music function if I want to do music with a game?
Re: Complexity of putting music into a game
by on (#157518)
I think you have the general idea...

You have an array of note frequencies.

You have an a way to do note lengths... I do it by counting frames, decreasing the length each frame until it's zero, fetch the next note. Or silent the note by inserting a zero in either the volume register or in the frequency registers (or both...redundant)

But, if you leave it there, the music will sound robotic and dull. Like Atari 2600 or arcades from the 70s. Adding volume envelopes and pitch bends (vibrato) and ways to mimic drums will make a big difference in sound quality.

You're going to want a way to have multiple songs, also.

I think at minimum you'll want to be able to set the volume of each note.
Re: Complexity of putting music into a game
by on (#157521)
dougeff wrote:
Adding volume envelopes and pitch bends (vibrato) and ways to mimic drums will make a big difference in sound quality.
I think at minimum you'll want to be able to set the volume of each note.

But aren't those all just values for the APU ports as well?

I mean, as far as I remember, each wave has two important ports. Two bits for the volume, 11 bits for the notes, two bits for the wave length or whatever. And one for timing that isn't really used in the tutorial.

So, the volume and every change in pitch etc., isn't this just encoded in the actual music data (the array) and gets sent to the APU 1:1? And the code logic is doing exactly nothing at all except for copying bytes?

So, I really don't understand why people talk about sound engines and all that stuff. Do the sound functions in games include music-related logic themselves?

Because, as I said, I would have simply encoded everything, from the note to the volume to the pitch, into a APU-compatible data array and the PlaySound function would just iterate through the array and put everything into the APU as-is (plus every third value would be a timer value that goes into a counting variable.)
The function wouldn't know anything about drums and pitch. The drums would be part of the music as encoded in the array.

Or am I misunderstanding something here?

dougeff wrote:
You're going to want a way to have multiple songs, also.

Do you mean multiple songs at once or separately? Because if you mean separately, then sure: I would just call my function with another array.

Also: The above pseudo code would of course be called four times per frame, each time for a different channel, so that each channel has its own data array.
Re: Complexity of putting music into a game
by on (#157522)
DRW wrote:
dougeff wrote:
Adding volume envelopes and pitch bends (vibrato) and ways to mimic drums will make a big difference in sound quality.
I think at minimum you'll want to be able to set the volume of each note.

But aren't those all just values for the APU ports as well?

They're not literal values ready for poking into the APU ports. They're values that are combined into values for the APU ports. For example, the volume from the musical phrase, which determines how loud or quiet a particular note is, has to be combined with the volume from the instrument's envelope, which determines how loud or quiet a particular part of a note is. And the pitch from the musical phrase is usually stored as a number of semitones above low A or low C or something, which has to be looked up through a lookup table and combined with the pitch modification from the instrument's envelope.

Quote:
So, the volume and every change in pitch etc., isn't this just encoded in the actual music data (the array) and gets sent to the APU 1:1? And the code logic is doing exactly nothing at all except for copying bytes?

What you're describing is a register log, as might be found in VGM files. But that's not how music in NES games works. If they worked that way, music players would be a lot bigger, reducing the amount of space used in the ROM for other things such as graphics or level maps or alternatively increasing the cost of replicating the cartridge. A "music engine" is the audio equivalent of using metatiles for your graphics rather than storing a level as a big .bmp file.

Quote:
So, I really don't understand why people talk about sound engines and all that stuff. Do the sound functions in games include music-related logic themselves?

Yes.
Re: Complexity of putting music into a game
by on (#157523)
Alright. The metatile comparison makes sense, I guess.

Would you say that it pays off to do the whole Nerdy Nights tutorial on NES music? Does it give a good summary of all the stuff?
Re: Complexity of putting music into a game
by on (#157525)
Metal Slime doesn't cover everything (I think he quit before talking about DMC channels), but you might be able to get a working sound engine from reading these tutorials. I definitely read them before I started working on my music stuff...

I also use this program, written by snowbro...
http://nesdev.com/sndtest.zip

and just play around with sound registers, to test out...what frequency of noise channel makes a good cymbal sound, for example.
Re: Complexity of putting music into a game
by on (#157526)
For the actual music, I'll need someone else anyway. (After all, I also know how to create a graphics file for the NES and how to load and save meta tiles. But this doesn't mean I can draw a good-looking character.) But it might help me with understanding how the whole stuff works together.
Re: Complexity of putting music into a game
by on (#157536)
DRW wrote:
Assuming that I have a soundtrack, i.e. it is known what notes are put into what APU channel for what amount of time: How complex is the actual code for this?


The code for my music player is about 1000 lines of assembly, and took me about a week to write.

Famitone2 is about 1500 lines. I don't know how long Shiru spent on it.

If you've never worked with the APU before, expect to spend a lot of time figuring things out. If you just want to get it working, you might as well use Famitone2, since it's ready-made and relatively easy to use.

If you're getting someone else to make the music, make sure they know the capabilities of the music driver before they start. Don't just give them carte blanche to do whatever they want in Famitracker.
Re: Complexity of putting music into a game
by on (#157538)
How much space is generally used for DPCM samples if a game uses them?
Re: Complexity of putting music into a game
by on (#157540)
I use a few small samples for sound effects. They run about 200-300 ($100ish) bytes each. That's at a high sample rate.
Re: Complexity of putting music into a game
by on (#157542)
rainwarrior wrote:
The code for my music player is about 1000 lines of assembly, and took me about a week to write.

Wow! More complex than I thought.

rainwarrior wrote:
If you're getting someone else to make the music, make sure they know the capabilities of the music driver before they start. Don't just give them carte blanche to do whatever they want in Famitracker.

Sure. I will go through that Nerdy Nights tutorial first, then contact that person again.


While we're at it: Is there any place that explains how to convert and include sample sounds into NES games? If I have enough room left in the end, I want to include one or two speech samples. Something that the heroine says to her opponents or something that the evil girl says to her.
Re: Complexity of putting music into a game
by on (#157544)
darryl.revok wrote:
How much space is generally used for DPCM samples if a game uses them?

I think the majority of games use no samples. They eat up a LOT of space for what you get, compared to spending that space on code, etc.

When they're in a fixed bank, there's a hard limit of 16k, but since fixed bank space is at a premium it's usually less than 8k dedicated to samples.

Some mappers have a bank at C000-DFFF (e.g. MMC3 or FME-7) which makes it really flexible; you have 8k of dedicated space times as many banks as you want to allocate for samples. The most I've ever seen is 4 8k sample banks, used in Gimmick. (32k)
Re: Complexity of putting music into a game
by on (#157545)
DRW wrote:
Is there any place that explains how to convert and include sample sounds into NES games? If I have enough room left in the end, I want to include one or two speech samples. Something that the heroine says to her opponents or something that the evil girl says to her.

RushJet1 made a DMC sample tool: http://forums.famitracker.com/viewtopic.php?t=95

That's really the best thing I've seen for making the samples.
Re: Complexity of putting music into a game
by on (#157549)
Ninja Gaiden has two samples: bass drum (~256 bytes) and snare (~512 bytes). I think those give pretty decent value compared to the space they use.
Re: Complexity of putting music into a game
by on (#157550)
rainwarrior wrote:
RushJet1 made a DMC sample tool: http://forums.famitracker.com/viewtopic.php?t=95

That's really the best thing I've seen for making the samples.

O.k., thanks. I'll have a look.

But is there also a tutorial or something similar that explains how to use samples for the NES? The Nerdy Nights tutorial only covers the four regular sound channels.
Re: Complexity of putting music into a game
by on (#157553)
Here's a sample of DPCM code...

Code:
   lda #$0f   ;sample rate
   sta $4010   ;0f is highest rate, 33144

   lda #$00   ;starting address, must be c000-ffc0
         ;00 = c000
         ;fc = ff00

   sta $4012   ;11aa aaaa aa00 0000

   lda #$0e   ;length of sample
   sta $4013   ;LLLL LLLL 0001, 0e => e1 bytes

   lda #$1f   ;write of 0001000 triggers sample
   sta $4015


and...

Code:
ldx #$0f   ;turn off last sample
   stx $4015
Re: Complexity of putting music into a game
by on (#157555)
rainwarrior wrote:
The most I've ever seen is 4 8k sample banks, used in Gimmick. (32k)

Just curious, did they use DPCM to make the detuned bass synth in Sophia, or is it actually two oscillators slightly detuned like a Moog baseline?

According to what I was reading, Sunsoft used DPCM a lot to make cool bass sounds. I was also reading though that it works poorly for high pitched sounds, so you might not be satisfied with the results with a female voice.
Re: Complexity of putting music into a game
by on (#157556)
Sunsoft had so frequently used the DPCM channel for a string or electric bass that we have come to colloquially refer to it here as "sunsoft bass".

The DPCM channel reconstruction is effectively an infinite lowpass filter. ("infinite" in the sense that the corner frequency is 0; for every pair of frequencies f and 2f, f can be represented twice as loud as 2f).
(Obviously at some point the channel saturates. At highest, that's frequencies below 258 Hz. Anyway, I'm ignoring that for now)

This encoding does an adequate job as long as its input is sufficiently bandlimited (say maybe 1kHz from lowest frequency you want to store to highest). The more bandwidth, the worse DPCM is at encoding it.

As an example, here's the first 7 seconds from David Byrne's My Fair Lady on The Wired CD (CC:Sampling+ license). First 7 seconds are post-filtering, second 7 seconds are additionally after encoding and decoding. I've applied a very strong bandpass to retain just the frequency band from 500Hz to 1500Hz.