/*平均律になるべく近いレジスタ値が何セントずれているか計算する。1半音は100セント。1オクターブは1200セント。*/
#include <stdio.h>
#include <math.h>
#include <string.h>
const double LA=440.0;
const double CPU=1.789773e+6;
struct REG
{
double cent;
double f;
}G[0x801];
char N[16];
char *H[12]={"A ","A+","B ","C ","C+","D ","D+","E ","F ","F+","G ","G+"};
double FamFreq(int reg)
{
return CPU/(16.0*(1.0+reg));
}
double get12EqTemperament(int key)
{
return LA*pow(2.0,(key-48)/12.0);
}
double Cent(double f)
{
return 1200.0*log(f/LA)/log(2.0);
}
char* getNoteText(int key)
{
strcpy(N,H[key%12]);
N[2]='0'+(key+9)/12;
N[3]=0;
return N;
}
int getNearest(double cent)
{
int r,ret;
double min=1e9;
for(r=0;r<=0x800;r++)
{
if(fabs(G[r].cent-cent)<min)
{
ret=r;
min=fabs(G[r].cent-cent);
}
}
return (ret==0x800)?0:ret;
}
int main(int argc, char* argv[])
{
double f,cent;
int reg,key;
for(reg=0;reg<=0x800;reg++)
{
f=FamFreq(reg);
cent=Cent(f);
G[reg].cent=cent;
G[reg].f=f;
}
for(key=0;key<88;key++)
{
f=get12EqTemperament(key);
cent=Cent(f);
reg=getNearest(cent);
if(reg>=8)
printf("%s = %fHz , reg = 0x%4.4x = %fHz = %+6.3f cent\n",getNoteText(key),f,reg,G[reg].f,G[reg].cent-cent);
}
return 0;
}
/*これ以上の精度は無理。大抵のファミコンソフトはこの固定値を持っていると思われる。*/
/*
8bitのアセンブリ言語のプログラミングでは、
高速化のためにLo, Hi 別テーブルに置く。
https://wiki.nesdev.com/w/index.php/APU_period_table
*/
/*
セント、対数、指数についての式変形はここが簡潔でわかりやすい。
http://d.hatena.ne.jp/keyword/%A5%BB%A5%F3%A5%C8
*/