DPCM ripping and play back? (Gauntlet 2 & others)

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
DPCM ripping and play back? (Gauntlet 2 & others)
by on (#67011)
My idea isn't fully fleshed out yet, but I want to rip DPCM sound from games and play them back on a microcontroller.

I did some fooling around with the 18.exe tool from pineight.com and got some sounds. I was able to pull out the the drum sample and some slashing sound from Ninja Gaiden but I had problems with Gauntlet 2. I can make out the potion noise and some really garbled voice samples but it sounds nothing like it does on real hardware or an emulator. I played with the bitrate and found that 21.3k seems to have the proper pitch. I used a low pass filter and played with the EQ but it's still unintelligible. I figured it might be 7-bit PCM but I shouldn't hear anything recognizable after ripping it as 1-bit DPCM if it were, right?
So does anyone know how Gauntlet 2 encodes it's samples?

Also, playback is as simple as incrementing/decrementing a 6-bit DAC at one of the 16 playback speeds, isn't it? Otherwise I've totally missed something.

Thanks!

EDIT: I've got the Ninja Gaiden slash sample playing back on my dac setup. The speed isn't right and the resistors are mismatched, but all things considered, it sounds pretty ok.
Now I just need "Red warrior shot the food!". Can anyone help?

by on (#67014)
I know some of the sounds would pause the game, but I could be thinking of the first Gauntlet. It probably is done with the $4011 register. It was kinda low quality, I think you would get better results by recording sounds from the arcade version, resample to the best DPCM rate, then convert to 8-bit, then to 1-bit.

If you want to try that, get BridgeM1 (or something similar), it's a frontend for the M1 sound emulator.

But yeah, DPCM would be really easy to play on a microcontroller.

by on (#67016)
Well, it turns out it's PCM. I should have ruled that out before I posted. :oops:
When I use audacity and import it as 8bit unsigned, little endian it doesn't sound quite right. I know the NES uses 7-bit but isn't it stored as 7-bits packed in a byte?
Also, what's a good way to find the start, end and sample rate of specific samples? Could I disassemble and look for writes to a specific register?

I'm really fond of the NES gauntlet 2 sounds and would like to reproduce them specifically.

I didn't realize how simple some audio formats are. I banged out functional playback code in record time.

by on (#67019)
Maybe it's 4-bit PCM, or other bit resolution. I guess ADPCM could be possible, but I doubt it's that. In an emu you can put a breakpoint on writes to $4011, then see what it did before writing.

by on (#67024)
I ripped a couple of samples out of Gauntlet 2 awhile back and got them playing with a custom player instead of using the original code. They are 4 bit PCM samples.

by on (#67025)
Memblers wrote:
Maybe it's 4-bit PCM, or other bit resolution. I guess ADPCM could be possible, but I doubt it's that. In an emu you can put a breakpoint on writes to $4011, then see what it did before writing.

Good call! It looks like that's exactly what it does.
Code:
LDA ($D1),Y @ $9253 = #$98
BEQ $F494
INY
STY $00D0 = #$01
AND #$F0
LSR
STA $4011 = #$FF

Code:
LDY #$00
LDA ($D1),Y @ $9252 = #$78
AND #$0F
ASL
ASL
ASL
STA $4011 = #$FF


Now I can just clone this code on my microcontroller and reap the benefits.

by on (#67039)
I got "Welcome green(?)" to play now. It's extremely static filled and muddy but I chalk it up to paring 2.2k and 1k resistors instead of 2k.

Thanks guys.

by on (#67059)
Image Image Image

You could try extracting .wav and looking at the result in Audacity to make sure your decoding algorithm is correct.

by on (#67084)
tepples wrote:
Image Image Image

You could try extracting .wav and looking at the result in Audacity to make sure your decoding algorithm is correct.

Stingray, or gate, dice? ...Skate, or, die. :)
Sir tepples, I had found that thread and used the script. It works wonders, should be stickied!
I had been opening a hex dump in notepad++ and using a macro to insert a zero every other character, then importing it in a hex editor. I knew I should have used a script but I've only written one before that accesses files.

I think the playback is correct. Here's that section:
Code:
ISR(TIMER2_OVF_vect) {
  temp = table[i];
 
  if (shift_count == 0){
    PORTD = temp & 0b11110000;
  }else{
    PORTD = (temp << 4) & 0b11110000;
    i++;
    shift_count = 0;
  }
  shift_count++;
 
  TCNT2=66;
  if(i==2325){
    i=0;
  }
}


I'm working off of borrowed code. I'm going to change the counter to "compare" instead of "overflow". Hopefully that will make the speed more precise.
I don't get why TCNT2 wants 66. My thinking is this: 16MHz(clock)/32(counter prescale)= 500,000/5265hz= 95; 256-95=161. That way the counter is loaded with 161 and counts 95 before it overflows. Ohwell.

by on (#67090)
I'm a moron. shift_count never gets read as 0 because it is inced after it gets set to 0. Thanks for making me check my work tepples.
I got compare mode to work though. It's not very well documented on arduinos. You have to read the atmega*8 datasheet and guess what the arduino will call the variables.

It's all fixed and working now.