プログラムコード(melody.c)
/**************** START ****************/
#include < stdio.h >
struct note {
int height;
int length;
int accident;
int height2;
};
int checkscale(int x,int y);
int SumInterval(int q,struct note x[]);
/**************** main ****************/
int main () {
struct note x[30];
int c,i=0,d,m,sum;
char buff[100];
/******** 記入した譜面を構造体に記録する ***************/
while((c=getchar())!='\n') {
while(c!=','){
if (c=='-') {
c=getchar();
buff[1]='\0'; /* reset buff */
buff[0]=c;
d =atoi(buff); /* 自然数に変える */
x[i].height=-d; /* マイナスをつける */
}
else if (c==':') x[i].height=x[i-1].height;
else {
buff[1]='\0';
buff[0]=c;
d =atoi(buff);
x[i].height=d;
}
c=getchar(); /* :をスキップ */
c=getchar();
m=0;
while (c!=':'){
buff[m]=c;
c=getchar();
m++;
}
d =atoi(buff);
x[i].length=d; /*分析に音の長さを使っていないので、省略可 */
c=getchar();
if (c!=',' && c!='\n') {
buff[0]=c;
x[i].accident=buff[0];
}
else {
x[i].accident='0';
}
if (x[i].accident=='s') x[i].height2=x[i].height*10+5;
if (x[i].accident=='x') x[i].height2=x[i].height*10+10;
if (x[i].accident=='b') x[i].height2=x[i].height*10-5;
if (x[i].accident=='d') x[i].height2=x[i].height*10-10;
if (x[i].accident=='0') x[i].height2=x[i].height*10;
i++;
break;
}
}
sum=SumInterval(i,x);
printf("特徴量=%d\n",sum);
}
/********* checkscale ******************/
/*******ド-シ、ミ-ファを通る分、
半音を引く計算をする関数 ****************/
int checkscale(int x,int y){
int sum=0,l;
int scale[16]={0,5,0,0,5,0,0,0,5,0,0,5,0,0,0,5}; /*-8から+8までのスケール。全音を0、半音を5としている*/
if (x>y) {
for (l=y+8;l< x+8;l++){
sum+=scale[l];
}
}
if (x
for (l=x+8;l< y+8;l++){
sum+=scale[l];
}
}
return (sum);
}
/**************** SumInterval *********************/
/*隣り合う音の間隔を計算する関数。*/
int SumInterval(int q,struct note x[]){
int a,b,sum=0,check,i;
for (i=1;i< q;i++){
a=x[i].height2-x[i-1].height2;
b=a<0?0-a:a; /* 絶対値の計算*/
check=checkscale(x[i].height,x[i-1].height);
sum+=(b-check);
}
return (sum);
}
/**************** END ****************/
出力の例
全音を10、半音を5として隣り合う音同士の間隔を数えている。
AからGまでの楽譜を入力したときの出力は以下の通り。
A楽譜:
t11m022@s01612h009:~/sony$ cc melody.c -o melody
t11m022@s01612h009:~/sony$
t11m022@s01612h009:~/sony$ ./melody
-3:4:,-2:4:,-1:4:,0:4:b,-1:4:,-2:4:,-3:4:,:4:,-1:4:,0:4:b,1:4:,2:4:,1:4:,0:4:b,-1:4:,:4:
特徴量=120
B楽譜:
t11m022@s01612h009:~/sony$ ./melody
3:4:,2:4:,1:4:,0:4:b,1:4:,2:4:,3:4:,:4:,1:4:,0:4:b,-1:4:,-2:4:,-1:4:,0:4:b,1:4:,:4:
特徴量=130
C楽譜:
t11m022@s01612h009:~/sony$ ./melody
3:4:,2:4:,1:4:,0:4:,1:4:,2:4:,3:4:,:4:,1:8:,0:4.:,-1:4:,-2:4:,:8:,-1:4.:,0:8:,1:8:
特徴量=120
D楽譜:
t11m022@s01612h009:~/sony$ ./melody
-6:8:,-6:8:,-6:8:,-4:8:,-2:8:,-2:8:,-2:8:,:8:,-5:8:,-5:8:,-5:8:,-3:8:,-2:8:,-2:8:,-2:8:,:8:,:8:,-2:8:,-2:8:,-1:8:b,-1:8:,-1:8:,-1:8:,-1:8:b,-2:4:,0:4:,1:4:,:4:
特徴量=130
E楽譜:
t11m022@s01612h009:~/sony$ ./melody
-6:8.:,-7:16:,-6:8:,-5:8:,-4:8:,-4:8:,-4:4:,-3:8.:,-4:16:,-5:8:,-6:8:,-5:4:,:4:,-3:8:,-5:4:,-5:8:,-4:8:,-4:8:,-3:8:,-3:8:,-4:8:,-4:8:,-5:8:,-5:8:,-6:4:,:4:
特徴量=140
F楽譜:
t11m022@s01612h009:~/sony$ ./melody
-6:2:,-5:4:,-6:8:,-5:8:,-4:4:,-2:4:,-4:4:,:4:,-5:4:,-5:4:,-6:4:,-5:4:,-4:2.:,:4:
特徴量=110
G楽譜:
t11m022@s01612h009:~/sony$ ./melody
-2:8.:,-1:16:,-2:8.:,-1:16:,-2:4:,-4:4:,-4:8.:,-3:16:,-4:8.:,-3:16:,-4:4:,-6:4:,-4:8:,:8:,-6:8.:,-5:16:,-4:8:,:8:,-6:8.:,-5:16:,-4:8.:,-4:16:,-2:8.:,-2:16:,-5:8.:,-4:16:,-5:4:
特徴量=255
t11m022@s01612h009:~/sony$
i)の問題はAとBが異なって、AとCが特徴量が同じになるようにと指示してあったので、これを満たしている。A=C=120 、B=130。
ii)の問題はD,E,FのどれがBと同じ特徴量を出力するかという問題である。
D=130、E=140、F=110なので、Bと同じのはD。
iii)はすみませんが、時間の関係で解いていない。
アルゴリズムの簡単な説明・動作方法
このプログラムでまず入力した楽譜を構造体で整理している。
「:」 や「,」を区きりにして、その間の情報を音の高さheight、長さlength、臨時記号accidentで分けている。
その後、accidentで高さを調整している。自然数で計算できるように、高さを10でかけて、accidentによって+5 (s) , +10 (x) , -5 (b) , -10 (d)を引いたり足したりする。その結果をheight2として保存する。
SumInterval関数では隣り合う音同士の間隔をそのまま足している。例えば、ド(+1)からレ(+2)に移動した旋律なら、|10-20|=10 。その後レ(+2)からミ(+3)のフラットなら、|20-25|=5。 sum=10+5=15となる。
しかし、シとドまたはミとファの間には半音の間隔しかないので、その調整を checkscale関数でしている。
楽譜の-8から+8の音の間隔がscale配列に記録されているので、SumIntervalの計算に「ド・シ、ミ・ファを通った分」5を引いて行く。
これで特徴量として、隣り合う音同士の間隔を足した値を使って楽譜を分析した。
実行環境など
LinuxのUbuntu Ver. 10 , 64bitモードで実行しましたが、C言語のコンパイラがあればどこでも実行できると思います。
コードを.cファイルとして保存して、コンパイルしたら実行できます。