[4.5.6] Implementing DPCM Sample Playback

CutterCross

Active member
Alright, this has been a long time coming. And now that Byte-Off is over and I had the chance to implement this in 4.5.6, (and actually wrote some notes as I went this time,) here we go. I'm not doing a full tutorial on how to make and use DPCM samples within the context of FamiTracker or FamiStudio, as there are plenty of other tutorials for that. This will be strictly in the context of importing music files with DPCM samples into NESmaker projects.



IMPORTANT:

DPCM sample playback will not be for everyone. Each project is going to have their own needs with memory management in the static bank ($1F) and you will have to make compromises. You will also need to have some understanding on the quirks of DPCM and how to monitor your bank data with Shiru's NES Space Checker.



Quirks of Using DPCM Samples:

For UNROM 512 / Mapper#30, DPCM sample data must be stored in the static bank ($1F). This means it will share the same area in ROM with your initialization, main game loop, NMI, and whatever else needs to be loaded into the CPU memory window at all times. A default NESmaker project gives you around 2.7 KB to work with, but you'll have to plan if it's worth sacrificing potential space for custom routines in exchange for DPCM sample space. Other projects will vary on available sample space. You should also keep in mind the size of your samples when viewing them in FamiTracker's Instrument Editor. It will tell you the exact sizes of each sample.

Because of the issue with sample size and available ROM space in the static bank, anyone outsourcing a FamiTracker user for their game's music will have to communicate more and plan things out regarding your game's specific ROM limitations.

Playback of the DMC will lower the volume of the Triangle and Noise channels in a difficult to control manner. Keep this in mind when doing volume mixing / adjustments.

GGsound can occasionally play a sample at a pitch lower than intended on original NES hardware. This is not an issue with emulators to my knowledge, and is a pretty rare circumstance, but it's important to note.



How to Actually Implement DPCM Playback:

With all that out of the way, let's get to actually enabling sample playback. We'll assume you already have your FT txt file ready and you've accounted for everything above.



1. BACK UP YOUR ENTIRE NESMAKER FOLDER FOR DPCM PROJECTS. Just so nothing goes wrong with your non-DPCM projects, you'll want to make 2 separate folders for both DPCM and non-DPCM projects.

2. Uncomment FEATURE_DPCM at the top of ggsound.inc, and change the value it represents from 0 to 1.
Code:
;Comment out these equates for features you do not wish to use.
FEATURE_DPCM = 1
FEATURE_ARPEGGIOS = 1

3. Add these variables to Zero Page RAM (Project -> Project Settings -> Zero Page RAM):
Code:
	base_address_dpcm_sample_table 			;; 2 bytes
	base_address_dpcm_note_to_sample_index 		;; 2 bytes
	base_address_dpcm_note_to_sample_length 	;; 2 bytes
	base_address_dpcm_note_to_loop_pitch_index 	;; 2 bytes
	apu_dpcm_state 					;; 1 byte

4. Also in Zero Page RAM, sound_param_word_3 is misspelled. Fix Joe's mistake and rename it correctly.

5. Uncomment the following lines in Initialization.asm:
Code:
	lda #<dpcm_list
   	sta sound_param_word_3
    	lda #>dpcm_list
    	sta sound_param_word_3+1

6. To make sure the DMC stops playing sample data along with the rest of the 2A03 channels, add this in Bank1B.asm under "jsr sound_stop" (line 8):
Code:
	LDA #$00
	STA $4010
	STA $4011

7. Add those same lines of code at the end of the StopSound macro (StopSound.asm):
Code:
	LDA #$00
	STA $4010
	STA $4011



Importing Your Music File and DPCM Sample Table:

Now that we've got all the initialization stuff out of the way, we can actually import our music and sample table.



1. Use GGsound's official ft_txt_to_asm converter to convert your FT txt file into an asm file. It will spit out both the converted asm file and the DPCM sample table asm file.

2. In Base.asm, include your DPCM sample table asm file right before the Reset vector:
Code:
;3. Handle bank assignments
	.include SCR_ASSIGN_BANKS
	
;;;; Include DPCM sample table
		
	.include "Sound\track_dpcm.asm"		;; GGsound uses track_dpcm as the name for its default DPCM sample table,
						;; So that's the naming convention I stuck with for my projects.
						;; But you can name it whatever you want.
								
;4 The Reset
	.include SCR_RESET

3. In NESmaker, right-click on Sound and left-click "Load Converted FamiTracker .asm" and import your converted asm music file.



And that should do it! Sample at your own risk.

*EDIT: In Bank1B.asm and StopSound.asm, you want to set #$00 to BOTH $4010 and $4011 to properly reset the DMC load counter back to 0, which will restore the proper volume balance of the Triangle and Noise channels.
 

TakuikaNinja

Active member
Nice! Can't wait to play around with this in my own game!

I do remember there was a bug in the ft_txt_to_asm python script which prevented looping samples from working.
Here's the fix CutterCross shared with me -> Line 642: Change << 4 to << 6.
Maybe we should get FunctionalForm to compile the fixed version into an executable file for convenience.

By the way, how would you set up a double controller read to work around the DMC channel corrupting the controller input?
 

CutterCross

Active member
I knew I forgot something. In doHandleInputReads.asm you'll want to replace the main "DO GamePadCheck" with this. This was originally for 4.1.5 but it works fine in 4.5.6. Special credit to Kasumi for the main logic and Dale Coop for this 2 player variant:

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DO GamePadCheck
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;; Kasumi's Double-Read Routine for DPCM ;;;;;;;;;;;;;;;;;;;;;;   
;;;;;;;;;;;;;;;;;;;;; Modified for 2 controllers by Dale Coop ;;;;;;;;;;;;;;;;;;;;;



GamePadCheck:
    LDA gamepad        ;backup values from last frame
    STA temp

    LDA gamepad2    ;backup values from last frame
    STA temp1
    
    LDA #$01
    STA $4016
    LSR
    STA $4016
    
    LDA #$01
    STA $4017
    LSR
    STA $4017

    LDA #$80
    STA gamepad
    STA gamepad2
    STA temp2
    STA temp3    

    
ReadControllerBytesLoop:
    LDA $4017
    AND #%00000011
    CMP #%00000001
    ROR gamepad2
    
    LDA $4016
    AND #%00000011
    CMP #%00000001
    ROR gamepad
    BCC ReadControllerBytesLoop
    
    LDA #$01
    STA $4016
    LSR
    STA $4016
    
    LDA #$01
    STA $4017
    LSR
    STA $4017
    
ReadControllerBytesLoop2:
    LDA $4017
    AND #%00000011
    CMP #%00000001
    ROR temp3
    
    LDA $4016
    AND #%00000011
    CMP #%00000001
    ROR temp2
    BCC ReadControllerBytesLoop2
    
    LDA temp2
    CMP gamepad
    BEQ ReadController1DpcmMatch
    LDA temp
    STA gamepad
    ReadController1DpcmMatch:

    LDA temp3
    CMP gamepad2
    BEQ ReadController2DpcmMatch
    LDA temp1
    STA gamepad2
    ReadController2DpcmMatch:

	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;END GamePadCheck
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 

Logana

Well-known member
Dose this also readable note effects and if so dose it take any extra data to store the note effects
 

CutterCross

Active member
Logana said:
Dose this also readable note effects and if so dose it take any extra data to store the note effects

The only effect GGsound supports is Bxx, and it must be used in all used channels, on the same line, on unique note patterns, and can only be implemented with GGsound's official ft_txt_to_asm converter. This does not add any extra support for effects, only the ability to play back DPCM samples.
 

Sukotto42

Member
You have really outlined a lot of stuff here that helps me bigtime, so I have only one real question.
It appears that I began Adventure Module with a basically completely full $1F bank. Am I out of luck trying to use DPCM in Adventure Module without some advanced trickery? Using 4.5.9, so I don't know what has changed since this post last left it.
 

CutterCross

Active member
You have really outlined a lot of stuff here that helps me bigtime, so I have only one real question.
It appears that I began Adventure Module with a basically completely full $1F bank. Am I out of luck trying to use DPCM in Adventure Module without some advanced trickery? Using 4.5.9, so I don't know what has changed since this post last left it.
DPCM samples can only be accessed through addresses $C000-$FFFF. [Your static bank $1F is always mapped to those addresses.] If bank $1F is full, you can't add samples. That's the problem with implementing sample playback in an already bloated engine like NESmaker's, because the engine packs bank $1F to the brim, making long samples [or many samples] infeasible without finding ways to free up a lot of space. This ROM space issue is the main reason why NESmaker's implementation of GGsound disables sample playback to begin with.
 

Sukotto42

Member
This all makes sense to me. Sad to not be able to use DPCM, as I can't imagine with my limited skills at this point that I will figure out any way to switch enough things out of that bank to make the size samples I need. Then again, I didn't have any of these skills at all a month ago, lol. First, I will try to get them to a lot less than what they are to see if it's even feasible. I have three, kick snare and stick click, but they are all around 1K each as they are currently.
 

Sukotto42

Member
Alright! Months later, getting back to it. Today, I went through all the steps outlined very carefully. I had already cleared a bunch of space from $1F and converted the FTM to TXT, to the two separate ASMs. I shaved a bunch of filesize off the samples as well. It appears the samples are loaded in the $1F bank now, but they aren't being played back with the songs.
 

TakuikaNinja

Active member
Did you use looping samples by any chance? The official converter has a bug which doesn't set the correct bit for looping. Use the forked converter linked in the resources tab of the forums instead.
 

Knietfeld

Member
@TakuikaNinja, thank you for commenting on that! My looping samples weren't working and I was about to dive into ggsound's code and try to force my samples to loop but now I don't have to!
 

Sukotto42

Member
Did you use looping samples by any chance? The official converter has a bug which doesn't set the correct bit for looping. Use the forked converter linked in the resources tab of the forums instead.
@TakuikaNinja They aren't looping samples. They are also currently playing at what seems like random times and pitches, not fully sure what's happening here yet. I used the converter in the resources tab.
 

Sukotto42

Member
@Sukotto42 have you checked your FamiTracker txt export with FTM Checker? I just wanna make sure it's not a problem with the music first.
Oh, HEY. Look at that. I've got 6 Warnings, one error, and 44 notices :ROFLMAO:
*hack hack hack*

Down to 2 Warnings (a DPCM and a Bxx warning) and 4 Optimizations (four unused instruments that I still plan on using). Going to split and try again.
*hack split split*

Done. It's still just as unpredictable and messy. Hmm.

Also, checking the APU viewer in Mesen it seems to be treating some of the samples it's unpredictably triggering as if they were looped samples, which none of them are.
 
Last edited:

TakuikaNinja

Active member
Triple check if you've followed all the steps outlined. You could've easily missed adding certain variables or bits of code.
Also, some of the variables use two bytes instead of one.
 

Sukotto42

Member
All steps here have been followed exactly as outlined, checked yet another time before I made my last post to be sure. Also checked to be sure the correct scripts are pointed to in project settings, as well. Just not sure what’s going wrong.
 

TakuikaNinja

Active member
Did you remember to do steps 4 & 5?
And if you've been using seperate files for the edited scripts, are you using the correct scripts in the the script settings?
I can verify that there are no issues with the music itself and I got it to work on ggsound's demo project.
 

Sukotto42

Member
Did you remember to do steps 4 & 5?
And if you've been using seperate files for the edited scripts, are you using the correct scripts in the the script settings?
I can verify that there are no issues with the music itself and I got it to work on ggsound's demo project.
@TakuikaNinja Thank you so much for reminding me to check ALL of the things one more time. I had a different Initialization script set in my project settings than the one I had edited!

I am happy to report my samples are working. I am elated!!

I hope my boneheadedness can assist others that haven't checked the project settings!
 
Top Bottom