apple2_mockingboard/freq.c

386 lines
16 KiB
C

/*
Frequency To/From Mockingboard Tones
by Michael Pohoreski
GitHub: https://github.com/Michaelangel007/apple2_mockingboard
The Mockingboard document at:
* ftp://ftp.apple.asimov.net/pub/apple_II/documentation/hardware/sound/Mockingboard%20Docs.pdf
has several incorrect values:
APPENDIX E
Course & Fine
Note Octave Frequency Decimal Hex. Notes
A 0 27.500 9 20 09 14 <-- should be 90F Exact 27.5 Hz -- bottom key on Piano
A# 0 29.135 8 145 08 91 <-- should be 88D
B 0 30.868 8 22 08 16 <-- should be 812
C 1 32.703 7 163 07 A3 <-- should be 79E
C# 1 34.648 7 53 07 35 <-- should be 731
D 1 36.708 6 205 06 CD <-- should be 6C9
D# 1 38.891 6 108 06 6C <-- should be 668
E 1 41.203 6 15 06 0F <-- should be 60C
F 1 43.654 5 184 05 B8 <-- should be 5B5
F# 1 46.249 5 102 05 66 <-- should be 563
G 1 48.999 5 24 05 18 <-- should be 516
G# 1 51.913 4 207 04 CF <-- should be 4CD
A 1 55.000 4 138 04 8A <-- should be 488 Exact 55.0 Hz
A# 1 58.270 4 73 04 49 <-- should be 447
B 1 61.735 4 11 04 0B <-- should be 409
C 2 65.406 3 209 03 D1 <-- should be 3CF
C# 2 69.296 3 154 03 9A <-- should be 398
D 2 73.416 3 102 03 66 <-- should be 365
D# 2 77.782 3 54 03 36 <-- should be 335
E 2 82.406 3 7 03 07 <-- should be 306
F 2 87.308 2 220 02 DC <-- should be 2DB
F# 2 92.498 2 179 02 B3 <-- should be 2B2
G 2 97.998 2 140 02 8C <-- should be 28B
G# 3 103.826 2 103 02 67 <-- should be 266
A 3 110.000 2 69 02 45 <-- should be 244 Exact 110.0 Hz
A# 3 116.540 2 36 02 24 <-- should be 223
B 3 123.470 2 5 02 05
C 3 130.812 1 232 01 E8
C# 3 138.592 1 205 01 CD <-- should be 1CC
D 3 146.832 1 179 01 B3 <-- should be 1C2
D# 3 155.564 1 155 01 9B <-- should be 19A
E 3 164.812 1 131 01 83
F 3 174.616 1 110 01 6E <-- should be 16D
F# 3 184.996 1 89 01 59
G 3 195.996 1 70 01 46 <-- should be 145
G# 3 207.652 1 51 01 33
A 3 220.000 1 34 01 22
A# 3 233.080 1 18 01 12
B 3 246.940 1 2 01 02
C 4 261.624 0 244 00 F4 -- Middle C4 --
C# 4 277.184 0 230 00 E6
D 4 293.664 0 217 00 D9
D# 4 311.128 0 205 00 CD
E 4 329.624 0 193 00 C1 <-- 329.628 Hz
F 4 349.232 0 183 00 B7 <-- 349.228 Hz
F# 4 369.992 0 172 00 AC
G 4 391.992 0 163 00 A3
G# 4 415.304 0 153 00 99 <-- should be 9A
A 4 440.000 0 145 00 91 Exact 440 Hz -- Concert Tuning A440
A# 4 466.160 0 137 00 89
B 4 493.880 0 129 00 81
C 5 523.248 0 122 00 7A
C# 5 554.368 0 115 00 73
D 5 587.328 0 108 00 6C <-- should be 6D
D# 5 622.256 0 102 00 66 <-- should be 67
E 5 659.248 0 96 00 60 <-- should be 61, and 659.255 Hz
F 5 698.464 0 91 00 58 <-- Should be 5B
F# 5 739.984 0 86 00 56
G 5 783.984 0 81 00 51
G# 5 830.608 0 76 00 4C <-- should be 4D
A 5 880.000 0 72 00 48 Exact 880 Hz
A# 5 932.320 0 68 00 44 <-- 932.328 Hz
B 5 987.760 0 64 00 40 <-- should be 41, and 987.767 Hz
C 6 1046.496 0 61 00 3D -- every frequency
C# 6 1108.736 0 57 00 39 <-- should be 3A -- from this point
D 6 1174.656 0 54 00 36 -- forward is slightly
D# 6 1244.512 0 51 00 33 -- off except for 'A'
E 6 1318.496 0 48 00 30
F 6 1396.928 0 45 00 2D <-- should be 2E, and 1396.913 Hz
F# 6 1479.968 0 43 00 2B
G 6 1567.968 0 40 00 2B <-- Should be 29
G# 6 1661.216 0 38 00 26
A 6 1760.000 0 36 00 24 Exact 1760 Hz
A# 6 1864.640 0 34 00 22
B 6 1975.520 0 32 00 20
C 7 2092.992 0 30 00 1E 2093.005 Hz
C# 7 2217.472 0 28 00 1C <-- should be 1D
D 7 2349.312 0 27 00 1B
D# 7 2489.024 0 25 00 19 <-- should be 1A
E 7 2636.992 0 24 00 18
F 7 2793.856 0 22 00 16 <-- should be 17
F# 7 2959.936 0 21 00 15 <-- should be 16
G 7 3135.936 0 20 00 14
G# 7 3322.432 0 19 00 13
A 7 3520.000 0 18 00 12 Exact 3520 Hz
A# 7 3729.280 0 17 00 11
B 7 3951.040 0 16 00 10
C 8 4185.984 0 15 00 0F -- top key on Piano
C# 8 4434.944 0 14 00 0E
D 8 4698.624 0 13 00 0D
D# 8 4978.048 0 12 00 0C
E 8 5273.984 0 12 00 0C 5274.041 Hz
F 8 5587.712 0 11 00 0B
F# 8 5919.872 0 10 00 0A
G 8 6271.872 0 10 00 0A
G# 8 6644.864 0 9 00 09
A 8 7040.000 0 9 00 09 Exact 7040 Hz
A# 8 7458.560 0 8 00 08
B 8 7902.080 0 8 00 08
Here are the correct frequencies and tones
along with the relative error:
Note Frequency Tone Decimal reproduced Relative Error
A0 27.500 Hz -> $090F ( 9 15) ~ 27.503 Hz (+0.012%)
A0# 29.135 Hz -> $088D ( 8 141) ~ 29.137 Hz (+0.005%)
B0 30.868 Hz -> $0812 ( 8 18) ~ 30.871 Hz (+0.012%)
C1 32.703 Hz -> $079E ( 7 158) ~ 32.708 Hz (+0.014%)
C1# 34.648 Hz -> $0731 ( 7 49) ~ 34.644 Hz (-0.010%)
D1 36.708 Hz -> $06C9 ( 6 201) ~ 36.719 Hz (+0.029%)
D1# 38.891 Hz -> $0668 ( 6 104) ~ 38.890 Hz (-0.001%)
E1 41.203 Hz -> $060C ( 6 12) ~ 41.202 Hz (-0.004%)
F1 43.654 Hz -> $05B5 ( 5 181) ~ 43.655 Hz (+0.004%)
F1# 46.249 Hz -> $0563 ( 5 99) ~ 46.251 Hz (+0.004%)
G1 48.999 Hz -> $0516 ( 5 22) ~ 48.986 Hz (-0.027%)
G1# 51.913 Hz -> $04CD ( 4 205) ~ 51.896 Hz (-0.033%)
A1 55.000 Hz -> $0488 ( 4 136) ~ 54.983 Hz (-0.031%)
A1# 58.270 Hz -> $0447 ( 4 71) ~ 58.247 Hz (-0.041%)
B1 61.735 Hz -> $0409 ( 4 9) ~ 61.743 Hz (+0.012%)
C2 65.406 Hz -> $03CF ( 3 207) ~ 65.416 Hz (+0.014%)
C2# 69.296 Hz -> $0398 ( 3 152) ~ 69.326 Hz (+0.044%)
D2 73.416 Hz -> $0365 ( 3 101) ~ 73.395 Hz (-0.029%)
D2# 77.782 Hz -> $0334 ( 3 52) ~ 77.781 Hz (-0.001%)
E2 82.407 Hz -> $0306 ( 3 6) ~ 82.403 Hz (-0.004%)
F2 87.307 Hz -> $02DB ( 2 219) ~ 87.251 Hz (-0.065%)
F2# 92.499 Hz -> $02B2 ( 2 178) ~ 92.435 Hz (-0.069%)
G2 97.999 Hz -> $028B ( 2 139) ~ 97.973 Hz (-0.027%)
G2# 103.826 Hz -> $0266 ( 2 102) ~ 103.877 Hz (+0.049%)
A2 110.000 Hz -> $0244 ( 2 68) ~ 109.966 Hz (-0.031%)
A2# 116.541 Hz -> $0223 ( 2 35) ~ 116.600 Hz (+0.051%)
B2 123.471 Hz -> $0205 ( 2 5) ~ 123.366 Hz (-0.085%)
C3 130.813 Hz -> $01E8 ( 1 232) ~ 130.697 Hz (-0.088%)
C3# 138.591 Hz -> $01CC ( 1 204) ~ 138.653 Hz (+0.044%)
D3 146.832 Hz -> $01B2 ( 1 178) ~ 146.959 Hz (+0.086%)
D3# 155.563 Hz -> $019A ( 1 154) ~ 155.562 Hz (-0.001%)
E3 164.814 Hz -> $0183 ( 1 131) ~ 164.807 Hz (-0.004%)
F3 174.614 Hz -> $016D ( 1 109) ~ 174.740 Hz (+0.072%)
F3# 184.997 Hz -> $0159 ( 1 89) ~ 184.870 Hz (-0.069%)
G3 195.998 Hz -> $0145 ( 1 69) ~ 196.247 Hz (+0.127%)
G3# 207.652 Hz -> $0133 ( 1 51) ~ 207.753 Hz (+0.049%)
A3 220.000 Hz -> $0122 ( 1 34) ~ 219.932 Hz (-0.031%)
A3# 233.082 Hz -> $0112 ( 1 18) ~ 232.775 Hz (-0.132%)
B3 246.942 Hz -> $0102 ( 1 2) ~ 247.210 Hz (+0.109%)
C4 261.626 Hz -> $00F4 ( 0 244) ~ 261.395 Hz (-0.088%)
C4# 277.183 Hz -> $00E6 ( 0 230) ~ 277.306 Hz (+0.044%)
D4 293.665 Hz -> $00D9 ( 0 217) ~ 293.918 Hz (+0.086%)
D4# 311.127 Hz -> $00CD ( 0 205) ~ 311.123 Hz (-0.001%)
E4 329.628 Hz -> $00C1 ( 0 193) ~ 330.468 Hz (+0.255%)
F4 349.228 Hz -> $00B7 ( 0 183) ~ 348.526 Hz (-0.201%)
F4# 369.994 Hz -> $00AC ( 0 172) ~ 370.816 Hz (+0.222%)
G4 391.995 Hz -> $00A3 ( 0 163) ~ 391.290 Hz (-0.180%)
G4# 415.305 Hz -> $009A ( 0 154) ~ 414.158 Hz (-0.276%)
A4 440.000 Hz -> $0091 ( 0 145) ~ 439.864 Hz (-0.031%)
A4# 466.164 Hz -> $0089 ( 0 137) ~ 465.549 Hz (-0.132%)
B4 493.883 Hz -> $0081 ( 0 129) ~ 494.421 Hz (+0.109%)
C5 523.251 Hz -> $007A ( 0 122) ~ 522.789 Hz (-0.088%)
C5# 554.365 Hz -> $0073 ( 0 115) ~ 554.611 Hz (+0.044%)
D5 587.330 Hz -> $006D ( 0 109) ~ 585.140 Hz (-0.373%)
D5# 622.254 Hz -> $0067 ( 0 103) ~ 619.226 Hz (-0.487%)
E5 659.255 Hz -> $0061 ( 0 97) ~ 657.529 Hz (-0.262%)
F5 698.456 Hz -> $005B ( 0 91) ~ 700.882 Hz (+0.347%)
F5# 739.989 Hz -> $0056 ( 0 86) ~ 741.631 Hz (+0.222%)
G5 783.991 Hz -> $0051 ( 0 81) ~ 787.411 Hz (+0.436%)
G5# 830.609 Hz -> $004D ( 0 77) ~ 828.315 Hz (-0.276%)
A5 880.000 Hz -> $0048 ( 0 72) ~ 885.837 Hz (+0.663%)
A5# 932.328 Hz -> $0044 ( 0 68) ~ 937.945 Hz (+0.603%)
B5 987.767 Hz -> $0041 ( 0 65) ~ 981.235 Hz (-0.661%)
C6 1046.502 Hz -> $003D ( 0 61) ~1045.578 Hz (-0.088%)
C6# 1108.731 Hz -> $003A ( 0 58) ~1099.660 Hz (-0.818%)
D6 1174.659 Hz -> $0036 ( 0 54) ~1181.116 Hz (+0.550%)
D6# 1244.508 Hz -> $0033 ( 0 51) ~1250.594 Hz (+0.489%)
E6 1318.510 Hz -> $0030 ( 0 48) ~1328.756 Hz (+0.777%)
F6 1396.913 Hz -> $002E ( 0 46) ~1386.528 Hz (-0.743%)
F6# 1479.978 Hz -> $002B ( 0 43) ~1483.262 Hz (+0.222%)
G6 1567.982 Hz -> $0029 ( 0 41) ~1555.616 Hz (-0.789%)
G6# 1661.219 Hz -> $0026 ( 0 38) ~1678.428 Hz (+1.036%)
A6 1760.000 Hz -> $0024 ( 0 36) ~1771.674 Hz (+0.663%)
A6# 1864.655 Hz -> $0022 ( 0 34) ~1875.890 Hz (+0.603%)
B6 1975.533 Hz -> $0020 ( 0 32) ~1993.133 Hz (+0.891%)
C7 2093.005 Hz -> $001E ( 0 30) ~2126.009 Hz (+1.577%)
C7# 2217.461 Hz -> $001D ( 0 29) ~2199.320 Hz (-0.818%)
D7 2349.318 Hz -> $001B ( 0 27) ~2362.232 Hz (+0.550%)
D7# 2489.016 Hz -> $001A ( 0 26) ~2453.087 Hz (-1.443%)
E7 2637.020 Hz -> $0018 ( 0 24) ~2657.511 Hz (+0.777%)
F7 2793.826 Hz -> $0017 ( 0 23) ~2773.055 Hz (-0.743%)
F7# 2959.955 Hz -> $0016 ( 0 22) ~2899.103 Hz (-2.056%)
G7 3135.963 Hz -> $0014 ( 0 20) ~3189.014 Hz (+1.692%)
G7# 3322.438 Hz -> $0013 ( 0 19) ~3356.856 Hz (+1.036%)
A7 3520.000 Hz -> $0012 ( 0 18) ~3543.348 Hz (+0.663%)
A7# 3729.310 Hz -> $0011 ( 0 17) ~3751.781 Hz (+0.603%)
B7 3951.066 Hz -> $0010 ( 0 16) ~3986.267 Hz (+0.891%)
C8 4186.009 Hz -> $000F ( 0 15) ~4252.018 Hz (+1.577%)
C8# 4434.922 Hz -> $000E ( 0 14) ~4555.734 Hz (+2.724%)
D8 4698.636 Hz -> $000E ( 0 14) ~4555.734 Hz (-3.041%)
D8# 4978.032 Hz -> $000D ( 0 13) ~4906.175 Hz (-1.443%)
E8 5274.041 Hz -> $000C ( 0 12) ~5315.023 Hz (+0.777%)
F8 5587.652 Hz -> $000B ( 0 11) ~5798.206 Hz (+3.768%)
F8# 5919.911 Hz -> $000B ( 0 11) ~5798.206 Hz (-2.056%)
G8 6271.927 Hz -> $000A ( 0 10) ~6378.027 Hz (+1.692%)
G8# 6644.875 Hz -> $000A ( 0 10) ~6378.027 Hz (-4.016%)
Reference:
* https://en.wikipedia.org/wiki/Piano_key_frequencies
* http://www.sciencebuddies.org/Fi les/3443/5/Phys_img024.jpg
*/
#include <stdio.h>
#include <math.h>
#define DEBUG 0
// Every 64th cycle is stretched to be a 65th cycle
//const double _1MHz_NTSC = 14318180.0 / 14.0; // 1,022,727 Hz
const double _1MHz_NTSC = 1020484.32; // Avg Hz, Table 3.1, Page 3-5, UtA2E, Jim Sather
//const double _1MHz_PAL = 14250000.0 / 14.0;
//const double A2_FREQ = 1023000.0; // common incorrect value
/*
Mockingboard:
Tone Freq = A2 Clock Freq / ((256*Course + Fine) * 16)
6502:
Master = 14.31818 MHz
NTSC Cycle = 14.31818 / 14 = 1.0227271429... MHz = 1022727 Hz
PAL Cycle = 14.25 / 14 = 1.0178571429... MHz = 1017857 Hz
n = Floor( 1,022,727 / freq )
Course = Floor( n / 4096 )
Fine = (n - (4096*Course)) / 16
*/
// Convert Mockingboard Tone Period into Frequency
// ========================================================================
double MockingboardGetFreq( int tone_period )
{
// / 256 = >> 8
// * 4096 = << 12
// int n = ((tone_period & 0xFF00) << 4) + ((tone_period & 0x00FF) << 4);
return _1MHz_NTSC / (double) (tone_period << 4);
}
// Convert Frequency into Mockingboard Tone Period
// ========================================================================
int MockingboardGetTone( double freq_hz )
{
const int n = (int) (_1MHz_NTSC / freq_hz);
const int hi = n / 4096;
const int lo = (n - (hi * 4096)) / 16;
// See if Lo-1, Lo, or Lo+1 has better absolute error
const int tone = hi*256 + lo;
double freq_a = MockingboardGetFreq( tone-1 );
double freq_b = MockingboardGetFreq( tone );
double freq_c = MockingboardGetFreq( tone+1 );
double abs_err_a = fabs(freq_a - freq_hz) / freq_hz * 100.0;
double abs_err_b = fabs(freq_b - freq_hz) / freq_hz * 100.0;
double abs_err_c = fabs(freq_c - freq_hz) / freq_hz * 100.0;
#if DEBUG // absolute error
printf( "%8.3f Hz (%9.6f) : %8.3f Hz (%9.6f) : %8.3f Hz (%9.6f) *** "
, freq_a, abs_err_a
, freq_b, abs_err_b
, freq_c, abs_err_c
);
#endif // DEBUG
if (abs_err_a < abs_err_b) return tone-1;
if (abs_err_b < abs_err_c) return tone;
/* */ return tone+1;
}
// ========================================================================
int main( const int nArg, const char *aArg[] )
{
(void) nArg;
// 0freq = asm low byte
// 1freq = asm high byte
int is_asm = 1*(aArg[0][0] == '0') +
2*(aArg[0][0] == '1') ;
const char *names[ 12 ] =
{
"A ", // 2^( 0/12)
"A#", // 2^( 1/12)
"B ", // 2^( 2/12)
"C ", // 2^( 3/12)
"C#", // 2^( 4/12)
"D ", // 2^( 5/12)
"D#", // 2^( 6/12)
"E ", // 2^( 7/12)
"F ", // 2^( 8/12)
"F#", // 2^( 9/12)
"G ", // 2^(10/12)
"G#" // 2^(11/12)
};
double halfstep_freq[12];
for( int half_step = 0; half_step < 12; half_step++ )
halfstep_freq[ half_step ] = pow( 2.0, (half_step / 12.0) );
if( !is_asm )
printf( "Note Frequency Tone Decimal reproduced Relative Error\n" );
for( int octave = 0; octave < 8; octave++ )
{
double octave_freq = 440.0 * pow( 2.0, (octave - 4.0));
for( int half_step = 0; half_step < 12; half_step++ )
{
double actual = octave_freq * halfstep_freq[ half_step ];
int tone = MockingboardGetTone( actual );
double measure = MockingboardGetFreq( tone );
double rel_err = (measure - actual) / actual * 100.0;
if( is_asm )
printf( "%*s.byte $%02X ; %c%d%c %8.3f Hz\n"
, 8, " "
,(tone >> ((is_asm-1) * 8)) & 0xFF
, names[ half_step ][0]
, octave + (half_step > 2)
, names[ half_step ][1]
, actual
);
else
printf( "%c%d%c %8.3f Hz -> $%04X (%3d %3d) ~%8.3f Hz (%+5.3f%%)\n"
, names[ half_step ][0]
, octave + (half_step > 2)
, names[ half_step ][1]
, actual
, tone
, (tone >> 8) & 0xFF
, (tone >> 0) & 0xFF
, measure
, rel_err
);
if( half_step == 2 )
printf( "\n" );
}
}
printf( "\n" );
return 0;
}