Files
2026-04-11 20:18:08 +02:00

462 lines
15 KiB
PHP
Executable File

<?php
/* this generetes the data tables used by NinjaTracker+ */
function output_values($label, $arr, $prefix = 'dw', $bounds = 16, $padding = 24)
{
$str = str_pad($label, $padding) . '= *'."\n";
$i = 0;
foreach($arr as $e)
{
if($i === $bounds)
{
$str .= "\n";
$i = 0;
}
if($i === 0)
{
$str .= str_repeat(' ', $padding).$prefix.' ';
}
else
{
$str .= ',';
}
$str .= $e;
$i++;
}
return $str;
}
/* volume conversion table ------------------------------------------------------------------- */
$volumes = [];
for($i = 0; $i <= 64; $i++)
{
$volumes[] = min(255, $i * 4);
}
$volumes[] = 255;
echo output_values('volume_lookup', $volumes)."\n\n";
/* Finetune Table ----------------------------------------------------------------------------
*
* Protracker MODs store durations (aka periods).
* Note sliding modify the duration (add/subtract).
* For the tracker player on the GS, we want one table of words so that we can map each duration to an ensoniq freq hi/lo setting.
*
* The formula to convert an Amiga duration into ensoniq freq hi/lo registers is:
*
* Amiga:
* The sample rate in Hertz can calculated as:
* 7093789.2 / (period * 2) for a PAL Amiga
* 7159090.5 / (period * 2) for a NTSC Amiga
* The period is what is stored in the MOD for the note.
* Taken from here taken from ftp://ftp.modland.com/pub/documents/format_documentation/Protracker%202.3A%20&%20miscellaneous%20info%20(.mod).txt
*
* IIGS:
* The formula for IIGS sound from IIGS Technote 37: http://www.1000bit.it/support/manuali/apple/technotes/iigs/tn.iigs.037.html
* freqOffset = ((32*Sample rate in Hertz)/1645)
*
* Example:
* period = 214;
* amiga_freq = 7093789.2 / (period * 2);
* gs_frequency_old = floor(32 / 1645 * (1 / (($period - 2) * 2.79365E-07))); // Ninjatracker
* gs_frequence_new = round((32 * amiga_freq) / 1645); // Ninjatracker+
*
*
* These are original Protracker values for the duration and every possible finetune.
* Taken from the file pt.c out of http://ftp.kameli.net/pub/fit/once_upon/once_upon-src.zip
*/
$pt_durations_finetune = [
[ // tuning 0
1712,1616,1525,1440,1357,1281,1209,1141,1077,1017, 961, 907, /* octave 0 */
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, /* octave 1 */
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, /* octave 2 */
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, /* octave 3 */
107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57, /* octave 4 */
53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, /* octave 5 */
],
[ // tuning 1
1700,1604,1514,1429,1349,1273,1202,1134,1070,1010,954,900,
850,802,757,715,674,637,601,567,535,505,477,450,
425,401,379,357,337,318,300,284,268,253,239,225,
213,201,189,179,169,159,150,142,134,126,119,113,
106,100,94,89,84,79,75,70,66,63,59,56,
53,50,47,44,42,39,37,35,33,31,29,28,28
],
[ // tuning 2
1688,1593,1503,1419,1339,1264,1193,1126,1063,1003,947,894,
844,796,752,709,670,632,597,563,532,502,474,447,
422,398,376,355,335,316,298,282,266,251,237,224,
211,199,188,177,167,158,149,141,133,125,118,112,
106,100,94,88,83,79,74,70,66,62,59,55,
53,50,47,44,41,39,37,35,33,31,29,28,28
],
[ // tuning 3
1676,1582,1493,1409,1330,1255,1185,1118,1055,996,940,887,
838,791,746,704,665,628,592,559,528,498,470,444,
419,395,373,352,332,314,296,280,264,249,235,222,
209,198,187,176,166,157,148,140,132,125,118,111,
105,99,93,88,83,78,74,70,66,62,59,55,
52,49,46,44,41,39,37,34,32,31,29,27,27
],
[ // tuning 4
1664,1570,1482,1399,1320,1246,1176,1110,1048,989,933,881,
832,785,741,699,660,623,588,555,524,495,467,441,
416,392,370,350,330,312,294,278,262,247,233,220,
208,196,185,175,165,156,147,139,131,124,117,110,
104,98,92,87,82,78,73,69,65,62,58,55,
52,49,46,43,41,39,36,34,32,31,29,27,27
],
[ // tuning 5
1652,1559,1471,1389,1311,1237,1168,1102,1040,982,927,875,
826,779,736,694,655,619,584,551,520,491,463,437,
413,390,368,347,328,309,292,276,260,245,232,219,
206,195,184,174,164,155,146,138,130,123,116,109,
103,97,92,87,82,77,73,69,65,61,58,54,
51,48,46,43,41,38,36,34,32,30,29,27,27
],
[ // tuning 6
1640,1548,1461,1379,1301,1228,1159,1094,1033,975,920,868,
820,774,730,689,651,614,580,547,516,487,460,434,
410,387,365,345,325,307,290,274,258,244,230,217,
205,193,183,172,163,154,145,137,129,122,115,109,
102,96,91,86,81,77,72,68,64,61,57,54,
51,48,45,43,40,38,36,34,32,30,28,27,27
],
[ // tuning 7
1628,1536,1450,1368,1292,1219,1151,1086,1025,968,913,862,
814,768,725,684,646,610,575,543,513,484,457,431,
407,384,363,342,323,305,288,272,256,242,228,216,
204,192,181,171,161,152,144,136,128,121,114,108,
102,96,90,85,80,76,72,68,64,60,57,54,
51,48,45,42,40,38,36,34,32,30,28,27,27
],
[ // tuning -8
1814,1712,1616,1525,1439,1358,1282,1210,1142,1078,1018,960,
907,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,106,100,95,90,85,80,75,71,67,63,60,
56,53,50,47,44,42,40,37,35,33,31,30,30
],
[ // tuning -7
1800,1699,1603,1513,1428,1348,1272,1201,1133,1070,1010,953,
900,850,802,757,715,675,636,601,567,535,505,477,
450,425,401,379,357,337,318,300,284,268,253,238,
225,212,200,189,179,169,159,150,142,134,126,119,
112,106,100,94,89,84,79,75,71,67,63,59,
56,53,50,47,44,42,39,37,35,33,31,30,30
],
[ // tuning -6
1788,1687,1592,1503,1409,1339,1264,1193,1126,1063,1003,947,
894,844,796,752,709,670,632,597,563,532,502,474,
447,422,398,376,355,335,316,298,282,266,251,237,
223,211,199,188,177,167,158,149,141,133,125,118,
111,105,99,93,88,83,78,74,70,66,62,59,
56,52,49,46,44,41,39,37,35,33,31,29,29
],
[ // tuning -5
1774,1674,1580,1491,1408,1328,1254,1184,1117,1054,995,939,
887,838,791,746,704,665,628,592,559,528,498,470,
444,419,395,373,352,332,314,296,280,264,249,235,
222,209,198,187,176,166,157,148,140,132,125,118,
111,104,98,93,88,83,78,74,69,66,62,58,
56,52,49,46,44,41,39,37,34,33,31,29,29
],
[ // tuning -4
1762,1663,1569,1481,1398,1320,1245,1175,1109,1047,988,933,
881,832,785,741,699,660,623,588,555,524,494,467,
441,416,392,370,350,330,312,294,278,262,247,233,
220,208,196,185,175,165,156,147,139,131,123,117,
110,104,98,92,87,82,77,73,69,65,61,58,
55,52,49,46,43,41,38,36,34,32,30,29,29
],
[ // tuning -3
1750,1651,1559,1471,1388,1311,1237,1167,1102,1040,982,927,
875,826,779,736,694,655,619,584,551,520,491,463,
437,413,390,368,347,328,309,292,276,260,245,232,
219,206,195,184,174,164,155,146,138,130,123,116,
109,103,97,92,87,82,77,73,69,65,61,58,
55,51,48,46,43,41,38,36,34,32,30,29,29
],
[ // tuning -2
1736,1638,1546,1459,1377,1300,1227,1158,1093,1032,974,919,
868,820,774,730,689,651,614,580,547,516,487,460,
434,410,387,365,345,325,307,290,274,258,244,230,
217,205,193,183,172,163,154,145,137,129,122,115,
108,102,96,92,86,82,77,72,68,64,61,57,
54,51,48,46,43,40,38,36,34,32,30,28,28
],
[ // tuning -1
1724,1627,1535,1449,1368,1291,1219,1150,1086,1025,967,913,
862,814,768,725,684,646,610,575,543,513,484,457,
431,407,384,363,342,323,305,288,272,256,242,228,
216,203,192,181,171,161,152,144,136,128,121,114,
108,102,96,91,85,81,76,72,68,64,60,57,
54,51,48,45,42,40,38,36,34,32,30,28,28
],
];
/* find the highest and lowest duration */
$max_duration = 0;
$min_duration = 999999;
foreach($pt_durations_finetune as $octave)
{
foreach($octave as $duration)
{
$max_duration = max($max_duration, $duration);
$min_duration = min($min_duration, $duration);
}
}
/* for every value of duration, calculate a value for the GS freq hi and lo DOC registers */
$frequencies = [];
for($duration = $max_duration; $duration >= $min_duration; $duration--)
{
/* Ninjatracker */
//$gs_frequency = floor(32 / 1645 * (1 / (($duration - 2) * 2.79365E-07)));
/* Ninjatrackerplus */
$amiga_freq = 7093789.2 / ($duration * 2);
$gs_frequency = round((32 * $amiga_freq) / 1645);
$durations[] = $duration;
$frequencies[] = $gs_frequency;
}
echo str_pad('max_finetune_ptr', 24) . '= '.(count($frequencies) * 2 - 2)."\n";
echo output_values('freq_finetune', $frequencies)."\n\n";
/* for every finetuned set of octaves, find the duration of each note in the frequency table
so that we have, for every note, a pointer into the frequency table */
echo str_pad('note_freq_ptrs', 24) . '= *'."\n";
foreach($pt_durations_finetune as $finetune_index => $octave)
{
$ptr_into_freq = [0]; /* note 0 is only a dummy */
foreach($octave as $duration)
{
$pos = array_search($duration, $durations);
if($pos === false)
{
die('Duration (period) not found: '.$duration);
}
$ptr = $pos * 2;
$ptr_into_freq[] = $ptr;
}
$label = 'note_freq_ptrs_'.dechex($finetune_index);
echo output_values($label, $ptr_into_freq)."\n";
$labels[$finetune_index] = $label;
}
echo str_pad('max_freq_ptr', 24) . '= '.(count($ptr_into_freq) * 2 - 2)."\n";
echo str_pad('note_freq_ptrs_offset', 24) . '= *'."\n";
foreach($pt_durations_finetune as $finetune_index => $octave)
{
echo str_repeat(' ', 24) . 'dw '.$labels[$finetune_index].'-note_freq_ptrs'."\n";
}
echo "\n";
/*
Nostalgia - The original Basic program from 1994
1 REM Make Finetune Table for Soundsmith Player
5 DIM A(3 * 12),B(3 * 12),C(3 * 12)
7 PRINT CHR$ (4)"opendeath": PRINT CHR$ (4)"writedeath"
10 DATA 858,808,762,720,678,640,604,570,538,508,480,453
20 DATA 428,404,381,360,339,320,302,285,269,254,240,226
30 DATA 214,202,190,180,170,160,151,143,135,127,120,113
35 FOR I = 1 TO 36: READ A(I): NEXT
40 K = 1:D = A(1): PRINT " dw ";
50 C = C + 1
55 REM (D+4) oder (D-2)
70 X = INT (32 / 1645 * (1 / ((D + 4) * 2.79365E - 07))): PRINT X;: IF C < 12 THEN PRINT ",";: GOTO 90
80 C = 0: PRINT : PRINT " dw ";
90 IF K < 37 THEN IF D = A(K) THEN B(K) = D2:C(K) = X:K = K + 1
100 D2 = D2 + 2:D = D - 1
110 IF D > 90 THEN 50
115 C = 12: FOR I = 1 TO 36:C = C + 1: IF C > 11 THEN C = 0: PRINT : PRINT " dw ";
116 PRINT B(I);: IF C < 11 THEN PRINT ",";
117 NEXT
118 C = 12: FOR I = 1 TO 36:C = C + 1: IF C > 11 THEN C = 0: PRINT : PRINT " dw ";
119 PRINT C(I);: IF C < 11 THEN PRINT ",";
120 NEXT
130 PRINT CHR$ (4)"close"
*/
/* --------------------------------------------------------------------------------------------------------------------- */
/* waveforms for vibrato and tremolo */
$table_length = 2 * 64 * 16;
$offset_sine_tbl = 0;
$offset_ramp_tbl = $offset_sine_tbl + $table_length;
$offset_square_tbl = $offset_ramp_tbl + $table_length;
echo str_pad('waveforms', 24).'dw '.$offset_sine_tbl.','.$offset_ramp_tbl.','.$offset_square_tbl.','.$offset_square_tbl.' ;random=square in protracker!'."\n\n";
$max_x = 64;
/* sine waveforms for vibrato and tremolo */
echo str_pad('sine_tbl', 24).'= *'."\n";
for($amplitude = 0; $amplitude < 16; $amplitude++)
{
$list = [];
for($x = 0; $x < $max_x; $x++)
{
$y = 2 * ((int) round($amplitude * sin($x / $max_x * 2 * M_PI)));
$word = ($y >= 0) ? $y : (65536 + $y);
$word_str = $word < 10 ? $word : '$'.dechex($word);
$list[] = $word_str;
}
echo str_repeat(' ', 24).'dw '.implode(',', $list)."\n";
}
echo "\n";
/* ramp down waveforms for vibrato and tremolo */
echo str_pad('ramp_tbl', 24).'= *'."\n";
for($amplitude = 0; $amplitude < 16; $amplitude++)
{
$list = [];
for($x = 0; $x < $max_x; $x++)
{
$y = 2 * (round($amplitude * ($max_x / 2 - $x) / ($max_x / 2)));
$word = abs(($y >= 0) ? $y : (65536 + $y));
$word_str = $word < 10 ? $word : '$'.dechex($word);
$list[] = $word_str;
}
echo str_repeat(' ', 24).'dw '.implode(',', $list)."\n";
}
echo "\n";
/* square waveforms for vibrato and tremolo */
echo str_pad('square_tbl', 24).'= *'."\n";
for($amplitude = 0; $amplitude < 16; $amplitude++)
{
$list = [];
for($x = 0; $x < $max_x; $x++)
{
$y = 2 * $amplitude * ($x < ($max_x / 2) ? 1 : -1);
$word = ($y >= 0) ? $y : (65536 + $y);
$word_str = $word < 10 ? $word : '$'.dechex($word);
$list[] = $word_str;
}
echo str_repeat(' ', 24).'dw '.implode(',', $list)."\n";
}
echo "\n";
/* --------------------------------------------------------------------------------------------------------------------- */
/* pattern break pointers */
$pointers = [];
for($i = 0; $i <= 255; $i++)
{
$h = str_pad(dechex($i), 2, '0', STR_PAD_LEFT);
$x = hexdec(substr($h,0,1));
$y = hexdec(substr($h,1,1));
if($x <= 6 && $y <= 9)
{
$p = $x * 10 + $y;
if($x === 6 && $y >= 4)
{
$p = 0; /* only values from 00 to 63 needed */
}
$pointers[] = $p;
}
else
{
$pointers[] = 0;
}
}
echo output_values('pattern_break_ptrs', $pointers, 'dfb')."\n\n";
/* --------------------------------------------------------------------------------------------------------------------- */
/* beats per minute
*
* The value of 125 means a 50 Hz interrupt.
* The value of 150 means a 60 Hz interrupt.
*
* According to the formula in the IIGS hardware reference:
* output_freq = SR / (2 ^ (17 + RES)) * FHL
* with: SR = 894886 Hz / (OSC + 2)
*
* In our case, the interrupt uses a 256 byte wave, so RES = 0.
* We have 32 oscillators enabled, so OSC = 32.
* Our interrupt should be by default 50 Hz, so FHL = 249.
*
* Now we need values for every parameter of the set speed command.
*/
$factor = 34 * 131072 / 894886; /* from the formula above */
$values = [];
for($bpm_value = 32; $bpm_value <= 255; $bpm_value++)
{
$output_freq = $bpm_value * 50 / 125;
$FHL = $factor * $output_freq;
$values[] = round($FHL);
}
echo output_values('bpm_to_freq', $values)."\n\n";
/* --------------------------------------------------------------------------------------------------------------------- */
/* quick lookup tables for registers */
foreach([32 => 'freqhi', 64 => 'vol', 128 => 'ptr', 160 => 'ctl', 192 => 'siz'] as $start => $label_part)
{
$values = [];
for($i = 0; $i < 30; $i += 2)
{
$values[] = $start + $i;
}
echo output_values('reg_tbl_'.$label_part, $values)."\n";
}
echo "\n";