APU period table: Difference between revisions
From NESdev Wiki
Jump to navigationJump to search
Rainwarrior (talk | contribs) m (APU category) |
(→Table generator: attempt 1 to improve py3 compatibility (requested by dougeff)) |
||
Line 44: | Line 44: | ||
# This file is offered as-is, without any warranty. | # This file is offered as-is, without any warranty. | ||
# | # | ||
from __future__ import with_statement, division | from __future__ import with_statement, division, print_function | ||
import sys | import sys | ||
Line 87: | Line 87: | ||
def main(argv): | def main(argv): | ||
if len(argv) >= 2 and argv[1] in ('/?', '-?', '-h', '--help'): | if len(argv) >= 2 and argv[1] in ('/?', '-?', '-h', '--help'): | ||
print "usage: %s TABLENAME FILENAME" % argv[0] | print("usage: %s TABLENAME FILENAME" % argv[0]) | ||
print "known tables:", ' '.join(sorted(tableNames)) | print("known tables:", ' '.join(sorted(tableNames))) | ||
elif len(argv) < 3: | elif len(argv) < 3: | ||
print "mktables: too few arguments; try %s --help" % argv[0] | print("mktables: too few arguments; try %s --help" % argv[0]) | ||
elif argv[1] in tableNames: | elif argv[1] in tableNames: | ||
tableNames[argv[1]](argv[2]) | tableNames[argv[1]](argv[2]) | ||
else: | else: | ||
print "mktables: no such table %s; try %s --help" % (argv[1], argv[0]) | print("mktables: no such table %s; try %s --help" % (argv[1], argv[0])) | ||
if __name__=='__main__': | if __name__=='__main__': |
Revision as of 05:11, 15 December 2015
APU Pulse and APU Triangle use "period" values to set the pitch of the note. But some people might not know the piano key frequencies or how to convert them to periods for the NES. Fortunately, this has been done for you.
Lookup table
Here's a lookup table from note numbers to the values to write to the pulse and triangle period registers. For the triangle channel, the first value corresponds to the lowest key on a standard piano (an A). The pulse waves sound one octave higher.
; NTSC period table generated by mktables.py .export periodTableLo, periodTableHi .segment "RODATA" periodTableLo: .byt $f1,$7f,$13,$ad,$4d,$f3,$9d,$4c,$00,$b8,$74,$34 .byt $f8,$bf,$89,$56,$26,$f9,$ce,$a6,$80,$5c,$3a,$1a .byt $fb,$df,$c4,$ab,$93,$7c,$67,$52,$3f,$2d,$1c,$0c .byt $fd,$ef,$e1,$d5,$c9,$bd,$b3,$a9,$9f,$96,$8e,$86 .byt $7e,$77,$70,$6a,$64,$5e,$59,$54,$4f,$4b,$46,$42 .byt $3f,$3b,$38,$34,$31,$2f,$2c,$29,$27,$25,$23,$21 .byt $1f,$1d,$1b,$1a,$18,$17,$15,$14 periodTableHi: .byt $07,$07,$07,$06,$06,$05,$05,$05,$05,$04,$04,$04 .byt $03,$03,$03,$03,$03,$02,$02,$02,$02,$02,$02,$02 .byt $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01 .byt $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byt $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byt $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byt $00,$00,$00,$00,$00,$00,$00,$00
Table generator
This Python program generated the above lookup table. You can use it to make a table for a PAL NES, which has a different CPU clock rate.
#!/usr/bin/env python # # Lookup table generator for note periods # Copyright 2010 Damian Yerrick # # Copying and distribution of this file, with or without # modification, are permitted in any medium without royalty # provided the copyright notice and this notice are preserved. # This file is offered as-is, without any warranty. # from __future__ import with_statement, division, print_function import sys lowestFreq = 55.0 ntscOctaveBase = 39375000.0/(22 * 16 * lowestFreq) palOctaveBase = 266017125.0/(10 * 16 * 16 * lowestFreq) maxNote = 80 def makePeriodTable(filename, pal=False): semitone = 2.0**(1./12) octaveBase = palOctaveBase if pal else ntscOctaveBase relFreqs = [(1 << (i // 12)) * semitone**(i % 12) for i in xrange(maxNote)] periods = [int(round(octaveBase / freq)) - 1 for freq in relFreqs] systemName = "PAL" if pal else "NTSC" with open(filename, 'wt') as outfp: outfp.write("""; %s period table generated by mktables.py .export periodTableLo, periodTableHi .segment "RODATA" periodTableLo:\n""" % systemName) for i in range(0, maxNote, 12): outfp.write(' .byt ' + ','.join('$%02x' % (i % 256) for i in periods[i:i + 12]) + '\n') outfp.write('periodTableHi:\n') for i in range(0, maxNote, 12): outfp.write(' .byt ' + ','.join('$%02x' % (i >> 8) for i in periods[i:i + 12]) + '\n') def makePALPeriodTable(filename): return makePeriodTable(filename, pal=True) tableNames = { 'period': makePeriodTable, 'palperiod': makePALPeriodTable } def main(argv): if len(argv) >= 2 and argv[1] in ('/?', '-?', '-h', '--help'): print("usage: %s TABLENAME FILENAME" % argv[0]) print("known tables:", ' '.join(sorted(tableNames))) elif len(argv) < 3: print("mktables: too few arguments; try %s --help" % argv[0]) elif argv[1] in tableNames: tableNames[argv[1]](argv[2]) else: print("mktables: no such table %s; try %s --help" % (argv[1], argv[0])) if __name__=='__main__': main(sys.argv)
The following are exercises for the reader:
- Adapt to FDS audio and other mapper sound chips by changing
ntscOctaveBase
and the formula forperiods
- Hint:
fdsOctaveBase = lowestFreq * 65536 * 64 * 22.0 / 39375000.0
andoctaveBase * freq
- Hint:
- Adapt to other musical tuning systems by changing the formula for
relFreqs
- Adapt to assemblers other than ca65