[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

New 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

New 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.
 
Top Bottom