About DPCM Samples

CutterCross

Active member
I was just wondering, what EXACTLY are the issues that come up with using DPCM samples? Is it purely a ROM space issue, or are there other problems that arise in the engine itself when using them?
 

RadJunk

Administrator
Staff member
One of the problems with DPCM samples is that it they can corrupt controller reads. Trying to explain this, or how to fix it, or expect users to be able to dig into the scripts to fix it, or manage it correctly from the input...that's asking a lot. It's less work to just disable it, and then allow advanced users to add support.
 

CutterCross

Active member
Oh I see, that explains why some games like Battletoads only opted for DPCM samples on the title screen and pause screen and not in actual gameplay (aside from the punching/kicking sfx). Though I'm curious on how games like Journey to Silius pulled off constantly playing samples in gameplay.

Of course, DPCM samples aren't necessary to make good music, but it's a fun challenge to see just how much you can get out of the stock 2A03 chip. I'll probably still do some experiments with samples when NESmaker releases, just to see what all the issues are for myself.
 

Kasumi

New member
The easy way:
Each frame:
1. Read each button's state and store them all temporary RAM
2. Read each button's state again and compare them to the value in temporary RAM
3. If the values don't match, use the buttons from last frame as this frame's buttons.
An equally easy way:
1. Read each button's state and store them all temporary RAM
2. Read each button's state again and compare them to the value in temporary RAM
3. If the values don't match, go back to 1. (This will get slightly more accurate input, but eats more CPU time. It can also effectively be made into an infinite loop by trolly hardware hooked up to the controller port.)
This topic describes a way (some ways?) to avoid most problems with exactly 3 reads: http://forums.nesdev.com/viewtopic.php?f=2&t=14197

But even ignoring the controller corruption, DPCM samples are inconvenient to use. They need to be in a very specific place in memory, and always present there. (One shouldn't bankswitch that area while a sample is playing.) For Mapper 30, where they need to be can't be bankswitched at all and is an area you'd want to put most of your engine code. Samples can end up competing for space with the NES Maker engine.
 

RadJunk

Administrator
Staff member
Kasumi - yeah. There are...lots of little issues. So it's much easier to say, "natively, it doesn't support a method for this...consider it an advanced feature" for anyone who wants to futz around at the code level. :)
 

CutterCross

Active member
Kasumi said:
The easy way:
Each frame:
1. Read each button's state and store them all temporary RAM
2. Read each button's state again and compare them to the value in temporary RAM
3. If the values don't match, use the buttons from last frame as this frame's buttons.
An equally easy way:
1. Read each button's state and store them all temporary RAM
2. Read each button's state again and compare them to the value in temporary RAM
3. If the values don't match, go back to 1. (This will get slightly more accurate input, but eats more CPU time. It can also effectively be made into an infinite loop by trolly hardware hooked up to the controller port.)
This topic describes a way (some ways?) to avoid most problems with exactly 3 reads: http://forums.nesdev.com/viewtopic.php?f=2&t=14197

But even ignoring the controller corruption, DPCM samples are inconvenient to use. They need to be in a very specific place in memory, and always present there. (One shouldn't bankswitch that area while a sample is playing.) For Mapper 30, where they need to be can't be bankswitched at all and is an area you'd want to put most of your engine code. Samples can end up competing for space with the NES Maker engine.
Thanks for the info Kasumi!
 

CutterCross

Active member
Oh, I also have another question regarding samples. Are inputs more easily corrupted when the DPCM channel is playing more samples?

Like, say the DPCM channel is always playing a sample at any given time (AKA the DPCM channel is always on, and is never turned off). In that scenario, would input data ALWAYS be corrupted and therefore be impossible to read inputs? Can input data only be read correctly during a gap when a sample ISN'T playing?
 

Kasumi

New member
CutterCross - If a sample is playing at all, input corruption is always possible. But it's not specifically playing the sample that causes the corruption. Playback only relies on the thing that causes the corruption.

If you're familiar with streaming services like youtube: A portion of the video loads. When you've watched most of the portion that loaded, more of the video is sent to you. And when you've watched most of that, more of the video is sent to you.

Sample playback works similarly. A portion of the sample data is loaded into a buffer by the hardware to play back later. When that portion is done playing, the next portion of the sample data is loaded.

If the read the hardware does that loads more sample data into the buffer happens at exactly the same time as the software reads the "controller register", the value given to the software will be incorrect. (But "at exactly the same time" is very specific. It's basically a 50/50 shot whether the read will be wrong even if it's as close to the sample read as is possible for the CPU to be at that point in time.)

High quality samples mean the buffer needs to be filled more often which increases the likelihood of the two things happening at the same time. It's like how a 1080p 60FPS stereo video would request data more often than a 240p mono video on youtube.

So input data can be read correctly while a sample is playing, but not when the buffer is getting filled (well, even that has a 50/50 shot of no effect). Since the buffer is not getting filled all the time, input data won't always be corrupted when playing back samples.

Anyway, this is just information. I could probably just provide joypad code that's DPCM safe and you wouldn't have to worry about the specifics. It's a one time change.

I see the harder bit as where the sample data needs to be and the implications of that. The engine growing could get in the way of someone's samples that used to fit. A user's samples growing could crash into the engine. So it's an "over time" change rather than a "one time" change.
 

CutterCross

Active member
Kasumi said:
CutterCross - If a sample is playing at all, input corruption is always possible. But it's not specifically playing the sample that causes the corruption. Playback only relies on the thing that causes the corruption.

If you're familiar with streaming services like youtube: A portion of the video loads. When you've watched most of the portion that loaded, more of the video is sent to you. And when you've watched most of that, more of the video is sent to you.

Sample playback works similarly. A portion of the sample data is loaded into a buffer by the hardware to play back later. When that portion is done playing, the next portion of the sample data is loaded.

If the read the hardware does that loads more sample data into the buffer happens at exactly the same time as the software reads the "controller register", the value given to the software will be incorrect. (But "at exactly the same time" is very specific. It's basically a 50/50 shot whether the read will be wrong even if it's as close to the sample read as is possible for the CPU to be at that point in time.)

High quality samples mean the buffer needs to be filled more often which increases the likelihood of the two things happening at the same time. It's like how a 1080p 60FPS stereo video would request data more often than a 240p mono video on youtube.

So input data can be read correctly while a sample is playing, but not when the buffer is getting filled (well, even that has a 50/50 shot of no effect). Since the buffer is not getting filled all the time, input data won't always be corrupted when playing back samples.

Anyway, this is just information. I could probably just provide joypad code that's DPCM safe and you wouldn't have to worry about the specifics. It's a one time change.

I see the harder bit as where the sample data needs to be and the implications of that. The engine growing could get in the way of someone's samples that used to fit. A user's samples growing could crash into the engine. So it's an "over time" change rather than a "one time" change.
Okay, that explains a lot! Thanks!

So, what if I had a very small sample that constantly loops to make the sound last longer? Does the buffer need to constantly refill the same sample data over and over again, or is it a one-time thing at the start of sample playback?

I guess another question I'm asking is, how much sample data can the buffer hold at one time?
 

Mihoshi20

Member
There and interesting forum thread I stumbled across on NESDEV titled "DPCM generates extra $4016 read pulse" that talks about DPCM and it's relation to the controller inputs you might find interesting. http://forums.nesdev.com/viewtopic.php?t=4116

There's also this little nugget of info on the NESDEV wiki. "DPCM Safety using Repeated Reads: If your code is intended to be used with APU DMC playback, this code will need to be altered. The NES occasionally glitches the controller port twice in a row if sample playback is enabled, and games using samples need to work around this. For example, Super Mario Bros. 3 reads each controller's data at least two times each frame. First it reads it as normal, then it reads it again. If the two results differ, it does the procedure all over. Because repeated rereads can take a long time, another way is to just use the previous frame's button press data if the results differ: "

I think the disabling DPCM capabilities for the engine and letting advanced users deal with it was the overall right choice given the tool's market and scope as nice as it would be to have the feature and once the tool is release to the public, a clever user may work the functionality back in that doesn't cause issues with controller input and release release the code for others to use.
 

Kasumi

New member
CutterCross - From my understanding (because this is where we get to the end of my knowledge) the buffer can only hold a single byte regardless of anything. So yes, it must constantly refill sample data all the time regardless of if it's the same sample because samples you'd want are larger than a byte.

Rather than change your samples to avoid it, you change your joypad read subroutine to avoid it. It's a simple, one time change.
Here's the one from Indivisible (NESASM syntax rather than asm6). This is "the easy way" I described in my post.
Code:
	lda <p1curstate
	sta <p1oldstate

	lda #$01
	sta <p1curstate ;initialize the buffer with a flag
	
	sta $4016;Strobe joypads
	lsr a
	sta $4016
p1and2loop:
	lda $4016
	and #%00000011
	cmp #$01
	rol <p1curstate
	bcc p1and2loop ;loop if the flag wasn't shifted out yet
	
	lda #$01
	sta <reserved0 ;initialize the buffer with a flag
	
	sta $4016;Strobe joypads
	lsr a
	sta $4016
p1testloop:
	lda $4016
	and #%00000011
	cmp #$01
	rol <reserved0
	bcc p1testloop;loop if the flag wasn't shifted out yet
	
	lda <reserved0
	cmp <p1curstate
	beq joypad1matched
	lda <p1oldstate
	sta <p1curstate
joypad1matched:
This is what the Super Mario Bros. 3 method looks like (more or less. Mario 3 would use the second read's result to compare to the third's and the third's to compare to the fourth's rather than starting again with two new reads). (The "equally easy way" I described in my post.)
Code:
	lda <p1curstate
	sta <p1oldstate
joypad1didnotmatch:
	lda #$01
	sta <p1curstate ;initialize the buffer with a flag
	
	sta $4016;Strobe joypads
	lsr a
	sta $4016
p1and2loop:
	lda $4016
	and #%00000011
	cmp #$01
	rol <p1curstate
	bcc p1and2loop ;loop if the flag wasn't shifted out yet
	
	lda #$01
	sta <reserved0 ;initialize the buffer with a flag
	
	sta $4016;Strobe joypads
	lsr a
	sta $4016
p1testloop:
	lda $4016
	and #%00000011
	cmp #$01
	rol <reserved0
	bcc p1testloop;loop if the flag wasn't shifted out yet
	
	lda <reserved0
	cmp <p1curstate
	bne joypad1didnotmatch

Remove the last 13 instructions and you have a "regular" controller read routine.
Code:
	lda <p1curstate
	sta <p1oldstate

	lda #$01
	sta <p1curstate ;initialize the buffer with a flag
	
	sta $4016;Strobe joypads
	lsr a
	sta $4016
p1and2loop:
	lda $4016
	and #%00000011
	cmp #$01
	rol <p1curstate
	bcc p1and2loop ;loop if the flag wasn't shifted out yet
Though from what I can remember NES Maker stores the buttons reversed of what's here. ("A" is the rightmost bit rather than the leftmost.)
 

CutterCross

Active member
Kasumi, you've been a great help! The assembly routines don't actually look too difficult (for someone like me who's never written assembly code in his life).
Mihoshi20 said:
There and interesting forum thread I stumbled across on NESDEV titled "DPCM generates extra $4016 read pulse" that talks about DPCM and it's relation to the controller inputs you might find interesting. http://forums.nesdev.com/viewtopic.php?t=4116

There's also this little nugget of info on the NESDEV wiki. "DPCM Safety using Repeated Reads: If your code is intended to be used with APU DMC playback, this code will need to be altered. The NES occasionally glitches the controller port twice in a row if sample playback is enabled, and games using samples need to work around this. For example, Super Mario Bros. 3 reads each controller's data at least two times each frame. First it reads it as normal, then it reads it again. If the two results differ, it does the procedure all over. Because repeated rereads can take a long time, another way is to just use the previous frame's button press data if the results differ: "

I think the disabling DPCM capabilities for the engine and letting advanced users deal with it was the overall right choice given the tool's market and scope as nice as it would be to have the feature and once the tool is release to the public, a clever user may work the functionality back in that doesn't cause issues with controller input and release release the code for others to use.
I also think it was a good idea to disable DPCM for newbies just as long as it's simple for craftier users to enable it back again. After all, you shouldn't exclude others from innovating if they know the means to do so, whether it involves DPCM samples or anything else the NES is capable of. If nobody was innovating, we probably wouldn't see anything notable coming from NESmaker. It would kinda be like Unity, a very capable game engine but is really only known for cheap shovelware-type games, despite being capable of so much more than that.

Thankfully, there's a lot of dedicated people here, so were bound to see some awesome releases coming from NESmaker!
 

Kasumi

New member
I've attached a specifically NES Maker joypad routine that allows for what Super Mario Bros. 3 does. But don't use it! Heh. I've left it only for reference.
I made the original text small, since there's no strike through or similar on this forum.
It's tied to the same define that GGSound uses to enable the DPCM features. If DPCM is enabled in GGSound, you get a routine that attempts to avoid controller corruption. If DPCM is not enabled in GGSound, you get a regular controller read routine.

No need to swap out files, just the define. (Which you'd do anyway to enable/disable the feature within GGSound.) It goes in GameEngineData\Routines\System (Replace what's there.)

Edit: Oh. Since NES Maker seems to read the controller in the NMI anyway, it could actually do a much simpler method of avoiding this. https://forums.nesdev.com/viewtopic.php?f=2&t=14319&start=15#p172188 The sprite DMA puts you on a specific side of the 50/50 coin, that code makes sure all the reads are on the side that doesn't get hit by corruption.

Provided that works (and I haven't tested it), there doesn't even need to be a different controller read routine to avoid the issue. (One does have to keep on top of it to make sure to avoid page boundary crossing.)

Regardless! There seem to be about 2,000 bytes you can use for samples. (But as the NES Maker Engine grows, that space gets smaller and smaller. Be smart. Be careful. Be prepared for the engine to grow.)

To add your DPCM data, you'd open GameEngineData\MainASM.asm, and directly below the line with .include "Routines\System\AssignBanks.asm"
You'd .include your DPCM data file that GGSound exports.

The define for DPCM in GGSound seems to already be enabled in NES Maker. But in case that changes (and none of the rest of this does) for some reason, in sound/ggsound.inc you'd add
FEATURE_DPCM = 1
right above
FEATURE_ARPEGGIOS = 1

At least... in theory! If it doesn't work, yell at me and I'll try to help.

Edit again!: Since NES Maker does read the controller in the NMI (and before other kinds of updates are done) I've realized the attached thing is actually pretty unsafe to just drop in. You'd have to change the NMI update order and while that's a thing I know how to swing, it's also something that would have to be done for every release.
 

Attachments

  • GamePadCheck.zip
    740 bytes · Views: 196

CutterCross

Active member
Kasumi said:
I've attached a specifically NES Maker joypad routine that allows for what Super Mario Bros. 3 does.

It's tied to the same define that GGSound uses to enable the DPCM features. If DPCM is enabled in GGSound, you get a routine that attempts to avoid controller corruption. If DPCM is not enabled in GGSound, you get a regular controller read routine.

No need to swap out files, just the define. (Which you'd do anyway to enable/disable the feature within GGSound.)
Kasumi, you are AWESOME! I think you just saved all of us several hours of work. (Granted, I won't be able to test it out until August when NESmaker releases, but it's incredible nonetheless!)
 

Kasumi

New member
Well, I'm not that awesome. I realized the code would have done bad things to NES Maker. There's a limited time to do certain kinds of graphical updates. NES Maker reads the controller before all such updates are done. The SMB3 method of reading controllers in this context could take enough time to make certain graphical updates happen outside the time when they're allowed.

The non Super Mario Bros 3. method (read both, but take the previous frame if they don't match) would be a little better. (Since the time used would be fixed.)

I know how to fix it (you'd move jsr GamePadCheck to right above skipNMUstuff:) but the file the fix needs to go in is not as isolated. (MainASM.asm is much more likely to change in updates such that just replacing the file would break it.)

Edit: If you wanna get your hands really dirty, you could also just try this: https://forums.nesdev.com/viewtopic.php?f=2&t=14319&start=15#p172188 Since NES Maker reads the controller very near to a sprite DMA currently anyway, that's a method that could be tried. (But I haven't tested it.) You'd have to modify it to read the buttons as NES Maker does.
 

Mihoshi20

Member
Kasumi said:
Well, I'm not that awesome. I realized the code would have done bad things to NES Maker. There's a limited time to do certain kinds of graphical updates. NES Maker reads the controller before all such updates are done. The SMB3 method of reading controllers in this context could take enough time to make certain graphical updates happen outside the time when they're allowed.

The non Super Mario Bros 3. method (read both, but take the previous frame if they don't match) would be a little better. (Since the time used would be fixed.)

I know how to fix it (you'd move jsr GamePadCheck to right above skipNMUstuff:) but the file the fix needs to go in is not as isolated. (MainASM.asm is much more likely to change in updates such that just replacing the file would break it.)

Edit: If you wanna get your hands really dirty, you could also just try this: https://forums.nesdev.com/viewtopic.php?f=2&t=14319&start=15#p172188 Since NES Maker reads the controller very near to a sprite DMA currently anyway, that's a method that could be tried. (But I haven't tested it.) You'd have to modify it to read the buttons as NES Maker does.

This is mostly why I'm not focusing on major projects until after August when I'm more confident that the code will at least be a bit more stable or in a more finalize state as far as MainASM and primary areas of the code not designed to be edited by the general user. In 'feature lock' if you will. Currently I'm opting more to work on smaller projects to get used to the tool and it's functions and changes during the beta period so if a major part of the tool changes and I have to start over, it's not as big of a deal.
 

CutterCross

Active member
Mihoshi20 said:
Kasumi said:
Well, I'm not that awesome. I realized the code would have done bad things to NES Maker. There's a limited time to do certain kinds of graphical updates. NES Maker reads the controller before all such updates are done. The SMB3 method of reading controllers in this context could take enough time to make certain graphical updates happen outside the time when they're allowed.

The non Super Mario Bros 3. method (read both, but take the previous frame if they don't match) would be a little better. (Since the time used would be fixed.)

I know how to fix it (you'd move jsr GamePadCheck to right above skipNMUstuff:) but the file the fix needs to go in is not as isolated. (MainASM.asm is much more likely to change in updates such that just replacing the file would break it.)

Edit: If you wanna get your hands really dirty, you could also just try this: https://forums.nesdev.com/viewtopic.php?f=2&t=14319&start=15#p172188 Since NES Maker reads the controller very near to a sprite DMA currently anyway, that's a method that could be tried. (But I haven't tested it.) You'd have to modify it to read the buttons as NES Maker does.

This is mostly why I'm not focusing on major projects until after August when I'm more confident that the code will at least be a bit more stable or in a more finalize state as far as MainASM and primary areas of the code not designed to be edited by the general user. In 'feature lock' if you will. Currently I'm opting more to work on smaller projects to get used to the tool and it's functions and changes during the beta period so if a major part of the tool changes and I have to start over, it's not as big of a deal.
Yeah, I understand that. You never know what sort of things will change in the future, but it doesn't hurts to start planning things out early. I know that I probably won't use DPCM samples in most of my games anyway, but it's good to experiment to see what you can get away with.

If anyone wants to see the music I've experimented samples with, you can check it out here:


Version with DPCM samples:

YouTube: https://www.youtube.com/watch?v=ryEhzFigTAc
SoundCloud: https://soundcloud.com/paul-haas-752206774/ascending-the-tower-the-tower-of-turmoil-famitracker-2a03

Version without DPCM samples:

YouTube: https://www.youtube.com/watch?v=hRWg7aD0Mig
SoundCloud: https://soundcloud.com/paul-haas-752206774/ascending-the-tower-no-dpcm-the-tower-of-turmoil-famitracker-2a03


I know that it's more impractical to use samples in an actual NES game, so that's why I also a version without samples, just in case it doesn't work out in the end.
 

kimmysawi

New member
CutterCross said:
Kasumi said:
CutterCross - If a sample is playing at all, input corruption is always possible. But it's not specifically playing the sample that causes the corruption. Playback only relies on the thing that causes the corruption.

If you're familiar with streaming services like youtube: A portion of the video loads. When you've watched most of the portion that loaded, more of the video is sent to you. And when you've watched most of that, more of the video is sent to you.

Sample playback works similarly. A portion of the sample data is loaded into a buffer by the hardware to play back later. When that portion is done playing, the next portion of the sample data is loaded.

If the read the hardware does that loads more sample data into the buffer happens at exactly the same time as the software reads the "controller register", the value given to the software will be incorrect. (But "at exactly the same time" is very specific. It's basically a 50/50 shot whether the read will be wrong even if it's as close to the sample read as is possible for the CPU to be at that point in time.)

High quality samples mean the buffer needs to be filled more often which increases the likelihood of the two things happening at the same time. It's like how a 1080p 60FPS stereo video would request data more often than a 240p mono video on youtube. visit phenq review here

So input data can be read correctly while a sample is playing, but not when the buffer is getting filled (well, even that has a 50/50 shot of no effect). Since the buffer is not getting filled all the time, input data won't always be corrupted when playing back samples.

Anyway, this is just information. I could probably just provide joypad code that's DPCM safe and you wouldn't have to worry about the specifics. It's a one time change.

I see the harder bit as where the sample data needs to be and the implications of that. The engine growing could get in the way of someone's samples that used to fit. A user's samples growing could crash into the engine. So it's an "over time" change rather than a "one time" change.
Okay, that explains a lot! Thanks!

So, what if I had a very small sample that constantly loops to make the sound last longer? Does the buffer need to constantly refill the same sample data over and over again, or is it a one-time thing at the start of sample playback?

I guess another question I'm asking is, how much sample data can the buffer hold at one time?

It can beat the items and can effect easily those blast
 
This is very disappointing to hear, it would have been a lot better if you left it enabled but made a warning pop up or something about the potential problems it may cause.

Are there any plans on DPCM support in the future ? maybe developing some smart way of handling it to work around those problems?
 

darkhog

New member
Yeah, sad that it doesn't have support for DPCM samples, it would be a great enhancement to music and perhaps sfx. I understand why you did it as I know NES limitations pretty well, but still I'm disappointed. If you find a way to do it, either with GGSound or by employing different sound engine (or writing your own!), please do so.
 
Top Bottom