Rip PCM samples from Roms?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Rip PCM samples from Roms?
by on (#63626)
Hello!

First post! Long time lurker and noob. I'm curious if theres any software out there that extracts samples from game boy roms?

The way Ive done it is that Ive imported the rom as raw audio into an audio editor (sony sound forge, audacity etc..) and located the samples.
The downside is that I'm not sure how to determine what sample rate the samples are supposed to be in. I usually set it to somewhere between 4000Hz - 8000Hz and 8-bit unsigned.

It doesn't work that well with all games, often the samples sound extremely bad and almost unrecognizable.


Anyone have any ideas?

Thanks in advance!

// catsup

by on (#63627)
Game Boy waveforms are 4-bit unsigned, big endian within each nibble, and usually 16 bytes (32 samples) long. A couple old NES games used this same format, for which I made a ripper a long time ago when REing Skate or Die 2.

by on (#63628)
You can always try decoding the entire rom as a series of 4-bit samples, and seeing if it sounds any better. (upper 4 bits first)

by on (#63629)
Thanks for your quick response!

The reason I'm setting it to 8-bits is that sound forge doesn't seem to go any lower.

tepples wrote:
A couple old NES games used this same format, for which I made a ripper a long time ago when REing Skate or Die 2.


Stupid question maybe but is this ripper exclusively for NES games or would it work with any file containing that specific data?

by on (#63630)
If you treat the ROM as an 8-bit unsigned sample, you'll be able to hear any 4-bit samples just fine. You'll miss every other sample, but that just has the effect of halving the sampling rate. Try playing it at 44.1 kHz to locate samples, then experiment with lower rates for each sample you find. Once you do that, you can do some more advanced extraction to get the missing samples that treating it as 8-bit skips.

by on (#63637)
catsup wrote:
Stupid question maybe but is this ripper exclusively for NES games or would it work with any file containing that specific data?

It decodes all the nibbles in a whole file to one big .wav file. Want it?

by on (#63640)
tepples wrote:
catsup wrote:
Stupid question maybe but is this ripper exclusively for NES games or would it work with any file containing that specific data?

It decodes all the nibbles in a whole file to one big .wav file. Want it?


Yes please! :D

by on (#63641)
System requirements: 2.6 snakes on a plane
Save the following as nibbledecode.py
Code:
#!/usr/bin/env python
"""

nibbledecode
Use this to convert the samples in e.g. Skate or Die 2 for NES or
any of several Game Boy games to wave format for other apps to use.

In Windows, save it as nibbledecode.py and drag raw files onto it.

Limitations: Master System/Game Gear nonlinear samples are not
supported.

(See copyright notice in versionMsg below.)


"""
from __future__ import with_statement, division
from contextlib import closing

usageMsg = "usage: %prog [options] infile.4bl [outfile.wav]"
descriptionMsg = """Expands 4-bit-per-sample linear PCM to an 8-bit wave file."""
versionMsg = """%prog 0.01
Copyright 2010 Damian Yerrick
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.  This file is offered as-is,
without any warranty.
"""

def expand4bit(src, little=False, signed=False):
    import array
    dst = array.array('B')
    xorData = 0x88 if signed else 0x08
    for byte in src:
        byte = ord(byte)
        s1 = (byte & 0xF0)
        s2 = (byte & 0x0F) << 4
        if little:
            s2, s1 = s1, s2
        dst.append(s1 ^ xorData)
        dst.append(s2 ^ xorData)
    return dst.tostring()

def main(argv=None):
    from optparse import OptionParser
    import wave

    if argv is None:
        import sys
        argv = sys.argv
   
    parser = OptionParser(usage=usageMsg, description=descriptionMsg,
                          version=versionMsg)
    parser.add_option('-l', '--little', dest='little',
                      default=False, action='store_true',
                      help='use low nibble first')
    parser.add_option('-s', '--signed', dest='signed',
                      default=False, action='store_true',
                      help='decode samples as signed nibbles')
    (options, args) = parser.parse_args(argv[1:])
    if len(args) not in (1, 2):
        parser.error("wrong number of arguments; try with --help")

    with open(args[0], 'rb') as infp:
        data = infp.read()
    data = expand4bit(data, little=options.little, signed=options.signed)
    outfilename = args[1] if len(args) > 1 else args[0] + '.wav'
    with closing(wave.open(outfilename, 'wb')) as outfp:
        outfp.setnchannels(1)
        outfp.setframerate(8372)
        outfp.setsampwidth(1)
        outfp.writeframes(data)

if __name__=='__main__':
    main()

by on (#63652)
tepples you're my hero. It worked great! Thanks!

by on (#64317)
Hate to be a pain but I tried the script with Skate or Die 2 and all I heard was a "Skate or Die" voice over. Are you supposed to hear the whole title music that way?

by on (#64322)
I think it rips JUST the samples. Not all the music.

by on (#64324)
Oh. I thought the whole title music was a sample.

by on (#64325)
iirc it is.....but they backed it using some fancy code to make it all musical.