Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /storage/content/49/145849/famitracker.com/public_html/forum/classes/dbHandler.php on line 29 FamiTracker
Login:
Menu:
Post: Author:
FamiTracker > General > Bug Reports & Feature Requests > DPCM sample length Owner: rainwarrior New post
Page 1 of 1 Sort:  
DPCM sample length Posted: 2011-07-12 17:37  (Last Edited: 2011-07-13 07:51) Reply | Quote
rainwarrior

Avatar

Member for: 4150 days
Location: Canada
Status: Offline

#19847
I was looking at an example TechEmporium posted of an attempt to make a triangle wave as a DPCM sample, and I was noticing some disparity between different players as to what the sample length is before looping.

Most of the documention I can find seems to suggest it's a multiple of 16 bytes + 1 byte. What I seem to be seeing when this sample loops is a multiple of 16 bytes + 1 sample (1/8th of a byte). I've attached a WAV output from FamiTracker 0.3.7b to show this. It's a pretty subtle widening of the bottom of the wave once per loop, hard to notice without visually looking at it.

In contrast, NotSo Fatso reads and uses an entire byte. Generally after a few loops, the counter ends up flattening itself against 0 for 8 samples. This makes a big audible difference.

I don't have original hardware to verify the correct behaviour with, but several emulators I tried seem to use the whole byte. You might want to create verification/padding to make sure samples meet the length requirement (16n+1).

If the extra byte is required, then the triangle sample isn't actually meant to work correctly, but it might be more informative if it worked the same way in FamiTracker as it will elsewhere.

Edit: see my second post below for a more detailed description of what I believe is happening.


Attachments:
requiem_for_a_journey.ftm (17 Kb)
looping_glitch.wav (6 Kb)
Posted: 2011-07-13 04:22  (Last Edited: 2011-07-13 04:42) Reply | Quote
TechEmporium

Avatar

Member for: 4485 days
Status: Offline

#19872
This might just be my usual (& often incorrect) rambling, but what if, instead of 16 bytes + 1 byte, JSR accidentally programmed the minimum DPCM file size as 16 bytes + 1 bit (or worse, 16 bits + 1 bit)? This could explain why FamiTracker, NSFLive & VirtuaNSF are able to play such short samples when other players can't, but I can't readily find anything in JSR's code to suggest this.

The samples that were used (TriangleX.dmc, TriangleY.dmc & TriangleZ.dmc) are, indeed, 16 B in size (just below the 16.125 B mark). If FamiTracker's NSF driver (& VirtuaNSF's) can play this sample when others shouldn't, you've discovered quite the bug.

Luckily, the only triangle-wave sample that doesn't fit this problem is c-sharp.dmc (which is only 976 B).

_______________________
Technology: the one thing that's hated & cursed at by all engineers, technologists, scientists & technicians!

(Lousy modern technology! )
Posted: 2011-07-13 04:58  (Last Edited: 2011-07-14 04:34) Reply | Quote
rainwarrior

Avatar

Member for: 4150 days
Location: Canada
Status: Offline

#19874
The +1 sample thing isn't necessarily intentional. I don't know if it's the first bit of the last byte, or if it's something else, but there's an extra sample cycle of 0 or silence going on at the loop point, and you can see it in the WAV.

Edit: I suspect this is what's happening in FamiTracker when the sample loops.

Sample cycle 1. The second last byte (16 of 17) gets loaded into the shift register. Its first bit is played, and the sample buffer is flagged for reload. (Bit count is now 7.)

Sample cycle 2. The last byte (17 of 17) gets loaded into the sample buffer. Reload() is called, resetting the load address, resetting the bit count to 8 and flagging sample buffer to reload. The second bit of the second last byte is played, but bit count is now 7 again.

Sample cycle 3. The first byte (1 of 17) is loaded into the sample buffer, replacing the last byte which has never been used. The shift register continues with data from byte 16.

etc.

So, that second last byte actually gets used for 9 samples in the shift register instead of 8 (the extra sample being a 0), and the very last byte is ignored.

At least, that's what I think is happening. I guess the fix is to get rid of the Reload() and just reset the Address/LengthCounter.

Posted: 2011-07-13 05:53  (Last Edited: 2011-07-13 06:04) Reply | Quote
TechEmporium

Avatar

Member for: 4485 days
Status: Offline

#19875
You know; you might be right on that, considering that byte 17 should be an EOF marker for the sample's file. Without that EOF marker, the system wouldn't know when to loop the sample.

Now, if this model is correct, this makes the actual sample size of my triangle samples 15 B of data + 1 B for an EOF marker (which clearly violates the minimum sample size requirements of the 2A03, according to the docs).

And if we compare FamiTracker to NotSo Fatso, Blargg's engine or even FlashNSF, we hear in the other players that the triangle DPCM sample plays continuously until the EOF marker is reached (resulting in a period of 0 volume before the loop restarts). This isn't heard in FamiTracker; the EOF marker is detected with a slight click before looping. It's almost as though other players actually compensate for the sample's being below minimum size (by playing a null sample once the bit count reaches 8 & the sample buffer's flagged to reload byte 1 of 16).

Your example looks like it suits a sample that meets the minimum file size (16 B data & 1 B EOF, a total of 17 B).

I think this is what's happening with my triangle samples; the samples are 15 B data & 1 B EOF, a total of 16 B (below minimum).

Sample cycle 1. The third last byte (15 of 16) gets loaded into the shift register. Its second bit is played & the sample buffer is flagged for reload (bit count is now 6).

Sample cycle 2. The second last byte (16 of 16,) containing an EOF marker, gets loaded into the shift register (loading the value 0x00 or a different value that doesn't yield audible intelligence). Its first bit is played as a null & the sample buffer is flagged for reload (bit count is now 7). Since EOF was reached prematurely, the sample buffer continues to step through its cycles as normal.

Sample cycle 3. The last byte (17 of 16, blank data outside of the sample's actual size,) gets loaded into the sample buffer. Reload() is called, resetting the load address, resetting the bit count to 8 & flagging sample buffer to reload. The second bit of the second last byte is played (as 0x00 or a different value that doesn't yield audible intelligence,) but bit count is now 7 again.

Sample cycle 4. The first byte (1 of 16) is loaded into the sample buffer, replacing the last byte which has never been used. The shift register continues with data from byte 15.

Etc.

This could mean two things; either FamiTracker is explicitly programmed for a minimum size of 16 B (i.e.: 15 B data + 1 EOF,) or for 16.125 B (16 B data + 1 b EOF,) playing a faulty sample properly when it shouldn't.

_______________________
Technology: the one thing that's hated & cursed at by all engineers, technologists, scientists & technicians!

(Lousy modern technology! )
Posted: 2011-07-13 06:13 Reply | Quote
rainwarrior

Avatar

Member for: 4150 days
Location: Canada
Status: Offline

#19876
Why would there be an EOF marker? (There isn't.)

Looping is not part of the sample data, it's something you choose to do or not do when you play it back.

The minimum sample size is 1 byte. The next smallest is 17, the next after that 33, and so on of the form 16n+1 up to 4081. FamiTracker doesn't seem to enforce these sizes from the UI side, but the playback engine does. In-between sizes will be rounded down (or rounded up 1 if a multiple of 16, resulting in one byte of random data).

When looping, FamiTracker misses the last byte, and inserts an extra 0 bit. When not looping I think it correctly plays out the last byte.

Posted: 2011-07-13 06:48  (Last Edited: 2011-07-13 06:55) Reply | Quote
TechEmporium

Avatar

Member for: 4485 days
Status: Offline

#19877
Actually, all files would technically have an EOF marker of some kind to define which point the file runs out of valid data. (& in a DMC file, the EOF condition is met at the point when the volume of the actual WAV file abruptly changes from whatever level to absolute 0).

I understand that looping isn't part of the sample data (i.e.: the data is contained in an array, is pulled through to the sample buffer by a set of load opcodes found in some subroutines of the program & can be halted/repeated through the use of a software interrupt). I also understand that one can really never know the point at which the data actually ends unless you adhere to the 16n+1 equation.

But here's essentially what I was describing; your model, normal conditions where a 17 B sample plays in a player like NotSo Fatso (contained in 17 memory locations or offsets):

00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF --

I just put random values & a --, but I'm sure you get the idea; 17 bytes where 16 bytes is sample data & the 17th byte (a value that I wouldn't know,) is the ending value that is used by whatever interrupt or subroutine to reset the load address.

Now, with my bad sample, I'd model the same scenario as such:

00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE xx --

Where there's only 15 bytes of intelligence, the 1 byte that would otherwise translate to the DMC file's actual EOF point (where a null is actually played) & the 17th byte (not part of the actual sample itself) that is the ending value used to reset the load address.

I'm sorry if you still don't understand what I'm trying to say, but I'm definitely not arguing with you; I may still have misinterpreted you, though.

As for the UI, this could also explain why Mex was able to create an entire song with nothing but multiple samples of varying sizes (beyond the maximum tolerable amount of samples an NSF can contain).

_______________________
Technology: the one thing that's hated & cursed at by all engineers, technologists, scientists & technicians!

(Lousy modern technology! )
Posted: 2011-07-13 07:19  (Last Edited: 2011-07-13 07:32) Reply | Quote
rainwarrior

Avatar

Member for: 4150 days
Location: Canada
Status: Offline

#19880
There is no "EOF marker" in a sample file. You can determine where a file ends by asking the operating system for its size, or several other ways.

I don't know if you're thinking of the macro EOF from cstdio.h, which is a default value that gets returned from fgetc if you try to read past the end of a file, but when you're reading a binary file this is not a reliable indicator of the file's end. It is not part of the file, and you would never load it into memory as part of the sample. Look at FamiTracker's code for loading sample files, and you will see that it just finds the file's size and reads it in as a single block.

There are some file formats that may mark an end of a list or something with a special value, but this is not the case for our DPCM samples files, which are just raw data.

There is no ending value in our sample data. The load address is reset when the length counter runs out and looping has been requested. The length of a sample is stored and controlled separately from the sample data itself.

And yes, your 16 byte sample is coming up as a 17 byte sample on playback; that 17th byte is something "random"; whatever happens to be there in the NSF (or in memory, in the case of FamiTracker). In most cases this is probably a zero. This is not because of a minimum size, though, this is because of the rounding I described earlier.

Posted: 2011-07-14 02:54 Reply | Quote
TechEmporium

Avatar

Member for: 4485 days
Status: Offline

#19930
Hmm... You know what; I've just learned something new. Thanks for clearing up my erroneous EOF rambling; I actually was thinking along the lines of cstdio.h & stdlib.h.

Now, about the rounding-off of sample sizes; I wonder how this wasn't really caught earlier by others. If FamiTracker's rounding up the size of the sample when it's a multiple of 16, that 1 extra byte of random data is fed (which it technically shouldn't have been). Wouldn't the safer method be, in the case of a looped sample, to always round the size down to the nearest multiple of 17, regardless of size?

Then again, depending on the sample in use, a significant amount of data may be lost through that method.

_______________________
Technology: the one thing that's hated & cursed at by all engineers, technologists, scientists & technicians!

(Lousy modern technology! )
Page 1 of 1 Sort: