18 June 2013, 16:18 | #1 |
Registered User
Join Date: Sep 2009
Location: Norway
Posts: 1,711
|
Generating an accurate Paula period table
Hi,
I'm creating a small, portable MOD player in C and I want to generate the period table (36 notes, spanned over 16 finetunes, padded with a zero before the new finetune, totalling in 592 values) but all the routines I've found on the net seem to be -1/+1 off on certain periods. The reason I want to do this is because the table is 1184 bytes long. It's not that big but it'll shave off some bytes for very small packed intros. I found periodtable.bas by Lars Hamre (?) but converting it to C makes rounding errors. I've tried to use floor(val + 0.5) and what not, nothing seems to work. I've tried for hours to change variables. One time I managed to get some of the red values correct, but then some other ones went off. I thought at first that maybe the periods must not be even values, but there are both odd and even values in the original table. Here's the table I want to generate: http://pastebin.com/6xVK2msR Here's PERIODTA.BAS: http://pastebin.com/3GcVZFG8 Here's the C code that generates the table: http://pastebin.com/2KTibxBk Here's the (wrongly) generated period table from the C code: http://pastebin.com/fSRqDZRm Using my C-converted code, the first 36 notes with no finetune added is like this: Code:
856,808,763,720,679,641,605,571,539,509,480,453, 428,404,381,360,340,321,303,286,270,254,240,227, 214,202,191,180,170,160,151,143,135,127,120,113,0, Code:
856,808,762,720,678,640,604,570,538,508,480,453, 428,404,381,360,339,320,302,285,269,254,240,226, 214,202,190,180,170,160,151,143,135,127,120,113,0, Some helpers on the constants in the QuickBasic file: fclk = 4433618.75#*1.6 this is PAL color clock carrier * 1.6, which yields 7093790Hz which is the Amiga PAL CPU clock. fr=258.9731 * b 258.9731 is a hand-rounded value from (7093790 / (2 * (856 * 16)) which yields 258.9730578271028. 856 is the mid-C period value. Last edited by 8bitbubsy; 18 June 2013 at 17:46. |
18 June 2013, 18:14 | #2 |
Unregistered User
Join Date: Sep 2012
Location: Copenhagen / DK
Age: 43
Posts: 4,190
|
The only thing I spot is on line 21 where you do an (int) cast. This is the same as a floor, so I'd add 0.5 to that line:
Code:
mt_PeriodTable[(i * 37) + j] = (int)(0.5+(1.0 / fr / pclk) / 16.0); |
18 June 2013, 18:17 | #3 |
move.l #$c0ff33,throat
Join Date: Dec 2005
Location: Berlin/Joymoney
Posts: 6,863
|
The way I did it for one of my 4k intros was to generate a table and then in a 2nd pass I corrected the wrong values (5 or 6 IIRC) with another small lookup table. I think I never managed to create a 100% correct period table using code only, the 2nd LUT quite annoyed me but since it was very very small in the end I didn't care much about it.
|
18 June 2013, 18:59 | #4 |
Puttymoon inhabitant
|
Sorry, but the thread title made me laugh
|
18 June 2013, 19:01 | #5 |
Registered User
Join Date: Sep 2009
Location: Norway
Posts: 1,711
|
Yeah, I thought about having a 1-bit shift table to prevent errors, but if there is a formula that works instead then I'd much prefer that. I guess the original period table was fixed by hand and ear, since the period accuracy is not spot on. In fact, it's pretty bad.
Anyways, here's the replayer I made if anyone's interested (I converted the PT2.3a replayer from asm to C, it's very accurate): http://pastebin.com/5dPR3yiL I added some bugfixes too. There's not many sanity checks on the loaded MOD tho', this player assumes you have a valid ProTracker module. For a more "safe" replayer, check out playptmod, which I also coded. It sure is misleading, but at least it's posted on an Amiga forum. Last edited by 8bitbubsy; 18 June 2013 at 19:45. |
18 June 2013, 19:12 | #6 |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
I think the differences you're seeing are the results of an incorrect implementation of software floating point math in the BASIC interpreter he used.
If you trust his math to be correct, then just rounding each value to the nearest integer will probably give a better result than his original table, but just using a pre-computed table might save lots of space on certain platforms, since you want it to be really portable and as small as possible. BTW, was 2.3 the one everyone used back in the day? What things are better in the 3.x versions? More player features, or just a bigger and better editor? Last edited by Leffmann; 18 June 2013 at 19:26. |
18 June 2013, 19:48 | #7 |
move.l #$c0ff33,throat
Join Date: Dec 2005
Location: Berlin/Joymoney
Posts: 6,863
|
3.xx series used a hires screen so more things were visible on screen. It also had some better sampling features and some other improvements but 2.xx series was the most stable. AFAIR most musicians back then didn't like the 3.xx series much back then and kept using 2.xx versions.
|
18 June 2013, 20:00 | #8 |
Registered User
Join Date: Jan 2012
Location: USA
Posts: 372
|
I think I'd trust your C code (along with the suggestion that 0.5 be added before the INT cast) before I'd trust a basic interpreter.
|
18 June 2013, 22:41 | #9 |
Unregistered User
Join Date: Sep 2012
Location: Copenhagen / DK
Age: 43
Posts: 4,190
|
When multiplying floats in a loop, round off errors can quickly become significant.
To test your results, I put this into MATLAB: Code:
f = zeros(1,36); for i = 1:36 f(i) = 2 ^ ((i - 1) / 12); end round(856 ./ f) |
19 June 2013, 08:12 | #10 |
Registered User
Join Date: Sep 2009
Location: Norway
Posts: 1,711
|
Ok, so I assume it's an error in the BASIC interpreter's floating point that Lars (?) used... Or maybe PERIODTA.BAS actually generates a more correct table, like the C code, but that he edited the "errors" by hand to match the older tables found in Ultimate Soundtracker.
Ultimate Soundtracker's table is also wrong (and identic to the ProTracker one, but without finetune shifting), which dates back from 1987 (!): Code:
dc.w 856,808,762,720,678,640 dc.w 604,570,538,508,480,453 dc.w 428,404,381,360,339,320 dc.w 302,285,269,254,240,226 dc.w 214,202,190,180,170,160 dc.w 151,143,135,127,120,113 dc.w 000 Anyways, the table *must* be like the one used in ProTracker, because I am looking up and comparing values, to get an index (which is the note ). These hard-coded period values are inside the MOD pattern data as well. With the "correct" table, I get wrong notes (not by a cent, but by a lot!). Last edited by 8bitbubsy; 19 June 2013 at 08:20. |
19 June 2013, 10:06 | #11 |
Unregistered User
Join Date: Sep 2012
Location: Copenhagen / DK
Age: 43
Posts: 4,190
|
Maybe you could use some kind of simple compression scheme on the original numbers?
You could also just store the difference from one number to the next. Then you could get away with using bytes for storage (and a word for the first value in every tuning), ending up with 555 bytes. You could even pack the data as no values would need more than 6 bits, ending up with 420 bytes + the space needed for a very simple unpacker. |
19 June 2013, 11:11 | #12 | |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 6,999
|
Quote:
|
|
19 June 2013, 13:30 | #13 |
Registered User
Join Date: Mar 2012
Location: Norfolk, UK
Posts: 1,153
|
Has anyone actually tried running the BASIC programme in AmigaBASIC?
Looks like a great project, by the way - I've downloaded it and may well port it to run on one of my FPGA projects in due course. |
19 June 2013, 14:08 | #14 |
Unregistered User
Join Date: Sep 2012
Location: Copenhagen / DK
Age: 43
Posts: 4,190
|
|
19 June 2013, 18:22 | #15 |
Registered User
Join Date: Mar 2012
Location: Norfolk, UK
Posts: 1,153
|
|
19 June 2013, 18:35 | #16 |
Registered User
Join Date: Jan 2012
Location: USA
Posts: 372
|
It's the algorithm itself that is the problem. Like demolition pointed out, the fr=fr*a in the loop is liable to introduce large errors. The frequency really needs to be recomputed each step by computing the appropriate power of the 12th root of 2 and multiplying that with the base frequency.
I ran this instead on a TRS-80 Color Computer emulator. Code:
10 NTSC = 3579545 20 PAL = 3546895 30 LGTH = 16 40 BASEFREQ = 258.9731 50 FOR I = 0 to 35 60 FREQ = BASEFREQ*(2↑(I/12)) 70 D1 = INT(NTSC/FREQ/LGTH + 0.5) 80 D2 = INT(PAL/FREQ/LGTH + 0.5) 90 PRINT D1;D2 100 NEXT I One thing that hasn't been discussed is temperament. The old values might might actually sound better for certain music and it's possible they were chosen for that reason. |
19 June 2013, 20:56 | #17 | |
Unregistered User
Join Date: Sep 2012
Location: Copenhagen / DK
Age: 43
Posts: 4,190
|
Quote:
It could be temperament adjustments, but then you'd have to choose a base as it would sound wrong if you then made music with another base and it seems weird if Protracker would be designed mainly for music in A for example. |
|
19 June 2013, 21:09 | #18 | |
Registered User
Join Date: Jan 2012
Location: USA
Posts: 372
|
Quote:
Well I was thinking it might have been done to mask obvious beats when two notes are played simultaneously. The notes of chords in even temperament aren't perfect ratios of one another so beats are inevitable. Other temperaments are sometimes used to reduce these beats. The correct temperament depends on the objective, of course. EDIT: Of course the computed values aren't even temperament either since they're rational approximations and 2↑(1/12) is not rational. I wonder what rounding/approximating does to the relationships between notes when chords are played. Maybe there should be an Amiga temperament! Last edited by mc6809e; 19 June 2013 at 21:21. |
|
19 June 2013, 21:43 | #19 |
Registered User
Join Date: Sep 2009
Location: Norway
Posts: 1,711
|
Is it possible to do some BASIC (interpreter with errors in the floating-point calcs) work to find some of the "inaccurate" factor/variable values, and use that in the C calculation instead? Using inaccurate constants to simulate the process. It will of course not help for the dynamically non-const calculated values, but maybe there's a way around that too...
EDIT: Removed some non-sense babbling from this post. I was wrong. EDIT2: Now I managed to make a "differentiate" table, halving the size of the period table section in the EXE. The generated table is 100% accurate with the original one, I compared each and every value in a loop. The reason I have 14 extras zeroes at the end of the table is to prevent access violation on arpeggios on high notes with +7 finetuned samples. PT ignored this, you can imagine what kind of weird glitches you'd get. New version of MOD player: http://pastebin.com/5dPR3yiL Last edited by 8bitbubsy; 14 September 2013 at 19:10. |
08 July 2013, 17:38 | #20 |
Moderator
Join Date: Nov 2004
Location: Eksjö / Sweden
Posts: 5,604
|
Here's the code from 1Klång which calculates it realtime to save 10 bytes (i.e. no table at all). D1 contains note 0-11 << 16 + octave 0-n on entry. I might use this in P61_Init actually to save some horrible includes and compile options
Code:
_baseC =54229 _baseA =64489 _octBase=_baseC move.w #_octBase,d0 swap d1 bra.s .jmpin .octl2: mulu #61858,d0 ;exponential: 1/(2^(1/12))*65536 swap d0 .jmpin: DBF d1,.octl2 swap d1 ;!hi word NOT clear. d1=octave addq.w #4,d1 ;shift 4 more (prec) lsr.w d1,d0 ;shift down by #octaves. move.w d0,_prds(a2) ;prd |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Looking for the most accurate Paula audio emulation | craig64 | support.Other | 69 | 23 July 2023 19:36 |
What is the most accurate speed test for WinUAE? | Steve | support.OtherUAE | 5 | 04 December 2012 18:04 |
Calculating percentages in assembly aka bpm and period | h0ffman | Coders. Asm / Hardware | 8 | 16 September 2012 18:49 |
Software for generating screenshots and videos | Edi (FZ2D) | Retrogaming General Discussion | 5 | 08 April 2010 23:34 |
How accurate is the emulation? | manicx | support.WinUAE | 26 | 07 July 2003 08:35 |
|
|