Famitracker: DPCM Loop Problem

Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music.

Moderator: Moderators

User avatar
BioMechanical Dude
Formerly AlienX
Posts: 137
Joined: Fri Apr 18, 2014 7:41 am
Location: Bulgaria

Famitracker: DPCM Loop Problem

Post by BioMechanical Dude »

I have encountered an interesting problem in Famitracker, when using DPCM samples. I made a thread about it on the Famitracker forum, but nobody replied, so I'm hoping somebody here can help.

I would like to use some DPCM bass for some of my projects. I want the samples to be really small, so I can have more space for drum and maybe sound effect samples and fit it all in the 16KB space, dedicated to DPCM data. So I made a bunch of Saw Wave samples for different notes. Each one is short and small. The way I would play longer notes is by looping them. And here is where I ran into an issue. When they loop, the samples sound like arpeggios are being used or something. As if there is some silence either in the beginning or the end of the sample, which ruins the clear sounding of the loop. And when creating those samples, I made sure they sound clear, when looping.

When I looked at the waveform in Famitracker, I noticed this:
Image
The compression has changed the beginning and ending of the wave. Is there a way to prevent this from happening? If I try using a sample with one wave cycle, this problem doesn't appear, but the pitch gets changed and every sample sounds like the same note. Perhaps the current length of the samples I'm using isn't right. If so, can someone tell me how long is the selection I've made in the screenshot and will this solve the problem with the looping?

I've included an FTM with one of the samples and the original .wav file.
Attachments
DPCM Loop.zip
(5.71 KiB) Downloaded 605 times
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: Famitracker: DPCM Loop Problem

Post by thefox »

The DPCM sample length (in bytes) has to be of form 16*n+1 (where n=0, 1, 2, ..., 255). Most likely FamiTracker is padding the sample because of that. If you want clean looping samples, I think it's best if you familiarize yourself with how the APU DPCM channel actually works: http://wiki.nesdev.com/w/index.php/APU_DMC

It might be easier to construct the samples manually in a hex editor (or by writing a custom tool) rather than use the WAV import for it. That way you have full control over what APU is doing.

...

EDIT: For example, here's a saw(ish) DPCM wave that loops perfectly at amplitude 32:

Code: Select all

Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  73 73 73 77 73 73 73 77 73 73 73 77 73 00 00 00  ssswssswsssws...
00000010  00                                               .
The 73, 77 section ramps the waveform down, and the 00 section ramps it back up. Of course only a limited range of frequencies can be covered by a single sample, so you'd have to have variations for different frequencies, while keeping the sample length restriction in mind at the same time.

EDIT2: I guess I should've also mentioned this technique developed by blargg that plays saw, pulse and triangle waves in the DPCM channel by playing a 1-byte sample at varying frequencies: http://www.slack.net/~ant/misc/nes-saw/ It requires custom code and IRQs though, so you can't directly use it in FamiTracker.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Famitracker: DPCM Loop Problem

Post by dougeff »

I think a good strategy for DPCM samples is - first in Audacity trim all silence from before and after the sample. Then cut it to about 0.1-0.2 seconds. Then have Famitracker produce a sample that is LARGER than you want. Then save it. Open the sample in a hex editor, and trim it to exactly 16*n + 1 bytes long (where n = 1-255). Then reimport to Famitracker.
Last edited by dougeff on Tue Aug 25, 2015 9:28 am, edited 1 time in total.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Famitracker: DPCM Loop Problem

Post by dougeff »

Well, Here's a low C saw wave, done exactly how I described. I didn't have to edit it in a hex editor, but I have had to do that with other samples.
Attachments
DPCM.zip
(299 Bytes) Downloaded 625 times
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Famitracker: DPCM Loop Problem

Post by dougeff »

Also, you won't be able to play looped Bass DPCM samples and Drum DPCM samples at the same time. You might want to consider Noise channel, or a combination of Noise and Sq/Tr, to simulate a drum sound.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
BioMechanical Dude
Formerly AlienX
Posts: 137
Joined: Fri Apr 18, 2014 7:41 am
Location: Bulgaria

Re: Famitracker: DPCM Loop Problem

Post by BioMechanical Dude »

Thanks! That explained a lot, though I still have a question. When I asked about the selection, I didn't ask about file size, but time. So, how long would 1 byte from a sample play at rate 15? According to the wiki, 8 bits, played at rate 15 are equal to 4 scanline lengths. What does it mean by that? 4 scanlines(in which case, I'd have to find out the time it takes for the TV to draw a scanline), 4 pixels, what exactly?

Also, creating the samples in a hex editor is a pretty cool idea, but figuring out how to make a sample play an exact frequency is a pain in the ass and it also wouldn't work for some other samples I want to use. So, what I need to do right now is edit the original wave file, so that the lenght and playback rate match the ones, that can be used by the DPCM(for instance, have the file size match the formula and have the playback rate roughly around 33144.94 Hz). And for that I would need the time it takes to play a given portion of the sample. If those things are impossible, then I need to know what kind of algorithm does Famitracker use to compress the files, so that I can make the proper adjustments.
dougeff wrote:I think a good strategy for DPCM samples is - first in Audacity trim all silence from before and after the sample. Then cut it to about 0.1-0.2 seconds. Then have Famitracker produce a sample that is LARGER than you want. Then save it. Open the sample in a hex editor, and trim it to exactly 16*n + 1 bytes long (where n = 1-255). Then reimport to Famitracker.
This is a pretty interesting idea, but the example you've made doesn't really work. It still has that problem. It's not as noticeable, but it's there. So, sorry, but it doesn't do it for me. :(
dougeff wrote:Also, you won't be able to play looped Bass DPCM samples and Drum DPCM samples at the same time.
I know. I never said I'll have them play at the same time, though I have an interesting idea, that could simulate that. Once this problem's taken care of and I finish the track, I will post it, so you can see for yourself.
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: Famitracker: DPCM Loop Problem

Post by thefox »

AlienX wrote:Thanks! That explained a lot, though I still have a question. When I asked about the selection, I didn't ask about file size, but time. So, how long would 1 byte from a sample play at rate 15? According to the wiki, 8 bits, played at rate 15 are equal to 4 scanline lengths. What does it mean by that? 4 scanlines(in which case, I'd have to find out the time it takes for the TV to draw a scanline), 4 pixels, what exactly?
The scanline lengths aren't really useful for you. They're just there for people who might want to use the DPCM IRQ as a makeshift scanline IRQ.

If you're playing at rate 15, the frequency of playback (of 1-bit samples) is 1789773/54 Hz ~= 33143.94 Hz (check the table in wiki). Every byte contains 8 1-bit samples, so divide by 8 to get the frequency of how often sample bytes are fetched.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: Famitracker: DPCM Loop Problem

Post by lidnariq »

AlienX wrote:So, how long would 1 byte from a sample play at rate 15?
The sample rates are stated on the DMC page in two different ways:
1- The "Rate Index" in the functional description, which gives the period (in CPU clocks) from bit to bit; multiply these numbers by 8 to get the number of CPU clocks to emit a whole byte.
2- The "Pitch table", which is the raw sample rates for each. (It's the same information, just 1789773 Hz divided by the above table). Divide these numbers by 8 to get the bytes/second.

To directly answer the question, the highest sample rate consumes bytes at ≈4143 bytes/second. (546875 ÷ 132 by definition)
how to make a sample play an exact frequency is a pain in the ass and it also wouldn't work for some other samples I want to use.
Unfortunately, you're really constrained by the DMC hardware for "chip" looping samples. You basically can only get pitches that are the existing pre-provided sample rates divided by some integer.

I made a simple perl script some years ago that made triangle waves (included dmc files) for all periods in this manner, for divisors of 2 up through 127, and a reference table (included html file) of which triangle at which rate produces which pitch:
Attachments
all-chip-dmc-triangle-waves.7z
(5.04 KiB) Downloaded 636 times
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: Famitracker: DPCM Loop Problem

Post by thefox »

lidnariq wrote:I made a simple perl script some years ago that made triangle waves (included dmc files) for all periods in this manner, for divisors of 2 up through 127, and a reference table (included html file) of which triangle at which rate produces which pitch:
Cool, I was just about to do the same thing. :)
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Famitracker: DPCM Loop Problem

Post by dougeff »

My sample sounds ok to me, except for a very small higher pitch click at the end of each loop.

Here's the output wav file.
Attachments
saw1.gif
saw1.gif (8.7 KiB) Viewed 17408 times
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Famitracker: DPCM Loop Problem

Post by dougeff »

I looked at your sample...

I think lower and longer and louder original samples sound better looped. The sample you have is too short and too quiet. Quiet, you get alot of artifact noise from the DMC sampling process. Short, you get more artifacts and misaligned peaks in the looping process, and you get a loop buzz (I don't know the technical term for this, but if you loop ANY sample short enough you get weird high pitch buzz, the shorter the loop the higher pitch the buzz).

Lower notes work better, because it's easier to match the peaks up in a loop without clipping (repeated clicks).

NES technology was just not designed to produce great looped samples. I was able to produce a less stuttered / noisy sample from your wav with a lot of modification (doubling it's length, boosting its volume, editing in hex editor), but it's still pretty noisy. Sounds a bit like a buzzing bee.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Famitracker: DPCM Loop Problem

Post by rainwarrior »

You can make a very limited scale with just a 17 byte looping sample, but unfortunately it is a bit detuned compared to the A440 scale usually used by programs like Famitracker.

Example: viewtopic.php?p=92494#p92494

The Immortal did this in a few tracks, it mostly just limited itself to a few droney bass notes that didn't sound too badly detuned.

If you want other pitches you need to use longer samples. To cover the pop/blip at the loop point, you can crossfade the waveform with its original phase just before the loop; this creates a phased/layered sound variation just before the loop, but it could be a lot less objectional than the pop/blip.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Famitracker: DPCM Loop Problem

Post by rainwarrior »

lidnariq wrote:I made a simple perl script some years ago that made triangle waves (included dmc files) for all periods in this manner, for divisors of 2 up through 127, and a reference table (included html file) of which triangle at which rate produces which pitch:
Here's those samples stuck into a Famitracker file, in case someone wants to play with it. I had to split it into 2 instruments (4 octaves each) because there's a limit on sample entries per instrument. There's a lot of redundant notes, but I believe it always favours the one with the highest samplerate.

Here's also the very hastily written python program that parses the HTML file into entries for Famitracker's text import, in case someone wants to regenerate it in a different manner.

Code: Select all

notes = { "C-":0, "C#":1, "D-":2, "D#":3, "E-":4, "F-":5, "F#":6, "G-":7, "G#":8, "A-":9, "A#":10, "B-":11 }
sample = 0
lines = open("makeall.html","rt").readlines()
for line in lines[1:]:
    pitches = 0
    p = 0
    p = line.find("<td",p+1)
    p = line.find("<td",p+1)
    p = line.find("<td",p+1)
    p = line.find("<td",p+1)
    for n in range(16):
        if line[p+3] == ">":
            note_name = line[p+4:p+6]
            octave = int(line[p+6])-1
            inst = 0
            if octave >= 4:
                inst = 1
            note_number = notes[note_name]
            print("KEYDPCM   %d   %d  %2d    %2d  %2d   1     0  -1" %
                  (inst,octave,note_number,sample,n))
            pitches += 1
        p = line.find("<td",p+1)
    if pitches > 0:
        sample += 1
Attachments
lidnariq_triangles_split.ftm
(19.78 KiB) Downloaded 583 times
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Famitracker: DPCM Loop Problem

Post by dougeff »

That's cool. I was thinking about this exact thing recently.

My issues with it
-there are quite a lot of samples (53), some seem to be unused.
-some of the samples are large (1000+ bytes), and maybe not even used.

But sounds good. Thanks.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Famitracker: DPCM Loop Problem

Post by rainwarrior »

Well, lidnariq's goal was to provide every possible usable sample that evenly divides the available sample lengths.

My goal was to collate as many of those as possible into a Famitracker toy you could play with as a more tactile demonstration of what it can do.

Eliminating notes and redundancies to find a practical smaller set is yet another goal, which would require someone to first spend some time figuring out what they actually need for their specific musical case, and even more time figuring out how to build the Famitracker instrument definitions that implement this smaller set. ;)
Post Reply