TOP > n.s.研究所 > 講義 > マイコン・サイエンス講座 > 講座内容

講義

講座内容
第3回 デジタル制御2:音階を使って、音楽をつくる

文責:林 叔克 (2008年12月25日) カテゴリ:2008年講座内容 (タグ:

音階と周波数

音は空気の振動です。この振動は空気を伝達し、鼓膜まで到達します。空気の振動の周波数が、大きければ大きいほど、高い音に聞こえます。音楽は、ドレミファ・・の音階がベースになっていますが、これは人間が任意に選んだ音の周波数です。ドからドまで、周波数が10 倍ちがいます。この周波数の範囲を12 に分割することで、音階ができます。


Figure:周波数と音の高低

AVRマイコンからブザーへの出力

ブザーはPORD の4 番目のピンに接続されています。PD4 を「出力」に設定してから、この端子に[1] と [0] の信号を交互に送ることで、ブザーの膜が振動し、音が鳴ります。


Figure:AVR マイコンとブザーとの接続

AVR マイコンとブザーとの接続

#include <avr/io.h>
int main()
{
	long i;
	DDRD= 0x10; //PORTD の4 番ピンを出力に使う
	for(;;){
		for(i=0;i<200;i++){
			PORTD= 0x10;
		}
		for(i=0;i<200;i++){
			PORTD= 0x00;
		}
	}
	return 0;
}

音階と周波数

音階と周波数のテーブルに従って、音階をつくりましょう。周波数を、波ひとつあたりにどれだけの時間がかかるかの周期にかえて、 1周期の時間をつくります。for ループを10 回繰り返すのにどれぐらいの時間がかかるのかを調べます。 for ループの繰り返しの回数を調節することで、音階をつくります。

例えば、ドの音を作ろうと思えば、ドの周波数は261H zなので、1秒間に空気が261 回振動すれば、ドの音に聞こえます。つまり、マイコンから1秒間に261 回、振動する波を出せばよいことになります。これは時間で言うと、一つの波の周期が3.8 ミリ秒になります。マイコンから「1」を出力する時間を1.9 ミリ秒、マイコンから「0」を出力する時間を1.9 ミリ秒にし、[1] と[0] の出力を繰り返せば、1 秒間に261回振動する波がつくれます。この[1] と[0] でつくられた波の信号が、ブザーに送られ金属板が振動します。この振動が空気を振動させ、最終的には、ヒトの耳の鼓膜が振動し、音が聞こえます。

  • 課題1 ドの音をつくり、3 秒間鳴らすプログラムを書こう
  • 課題2 12 音階をプログラミングしよう
  • 課題3 自分の好きな音楽を鳴らそう
音階コード周波数周期(msec)
ドC2613.8
レD2933.4
ミE3293.0
ファF3492.9
ソG3912.6
ラA4402.3
シB4942.0
ドC5231.9

音楽は、ドレミファ・・の音階をもとに、それぞれの音をどれほどの長さで鳴らすかでつくられます。1章節という区切りに、全音符、2 分音符(図15.3)というように、1 章節の中の音の長さを分割し、音を鳴らす長さを表します。マイコンで音楽をつくる場合は、単純にそれぞれの音を鳴らす長さを指定します。例えば、ドの音を0.5 秒間鳴らし、次にレの音を1 秒間鳴らすというようにして、音楽をつくっていきます。


Figure:章節と音符


Figure:ドの音を鳴らす時間を調節する

音階と関数

音階をつくったあとに、音楽を鳴らしたいわけですが、ドレミドレ・・という順番で「ドのfor loop」の次に「レのfor loop」というように並べていっては、大変です。そこでドの関数、レの関数というようにそれぞれの音の関数をつくり、ドレミファソラシドの音階をつくります。次に楽譜に合わせて、それぞれの関数を呼び出す方法がよい。具体的な関数のつくり方ですが、ドの音をC という名前の関数で表し、レの音をD という名前の関数で表していきます。そして、main 関数の中でC(200); D(100); E(50); というように音階の関数を順番に呼び出し、音楽をつくることを考えます。() の中の引数は、ある波長を何回、繰り返すのか、つまり各音符を鳴らす時間の長さに相当します。プログラムの構造を15.5 に表します。


Figure:音階関数の作り方

ドという関数をつくる

#include <avr/io.h>
void C(int k); //ドの関数の宣言を行なう
int main() //main 関数の中で音階を並べる
{
	DDRD= 0x10; //PORTD の4 番ピンを出力に使う
	C(200); //200 という値を関数C に出力し、ドの音をある時間間隔で出力する
	return 0;
}
//ドの関数をつくる
void C(int k){ //kを読み込む
	int m,i; //関数内で使う変数を宣言する
	for(m=0;m<k;m++){ //k (この場合、k=200) 回、ドの波長を繰り返し、ある時間間隔で音を鳴らす
		for(i=0;i<200;i++){ //ドの一波長をつくる
			PORTD= 0x10;
		}
		for(i=0;i<200;i++){
		PORTD= 0x00;
		}
	}
}

音には、高い音、低い音があり、それぞれで周期が違います。したがって同じ時間間隔で、低い音、高い音を鳴らそうとすると、それぞれの波長に合わせて、何回、特定の波長を繰り返すかを計算しないといけません。そこで例えば、上のドをつくる関数内に「出力したい音の時間間隔」を「ドの1波長を何回繰り返すか」に変換する式をつくり、計算された値で、for loop をまわす回数を設定し、ドの1波長を何回繰り返すかを設定します。こうして音の波長に合わせて、音の鳴る時間を設定することができます。


Figure:音を鳴らす長さのそろえ方

参考に音階の関数を使った「崖の上のポニョ」(O&S 作成) のプログラムをのせておきます。

「崖の上のポニョ」のmain 関数部分

#include<avr/io.h>
void KARA(int k);
void DO(int k);
void RE(int k);
void MI(int k);
void FA(int k);
void SO(int k);
void RA(int k);
void SI1(int k);
void SI(int k);
void DO2(int k);
void RE2(int k);
int main(){
long i,j;
DDRD =0x10;
for(;;){
	DO2(360); RA(160); FA(300); DO(90); KARA(5); DO(90); KARA(5); DO(90); RE(110);
	FA(130); SI1(190); RE(200); DO2(400);
	RA(170); SI1(170); SO(150); KARA(5); SO(150); SI1(150); RA(170); FA(300); RA(150);
	SO(150); RE(130); MI(150); FA(150); SO(620);
	DO2(360); RA(160); FA(300); DO(90); KARA(5); DO(90); KARA(5); DO(90); RE(110);
	FA(130); SI1(190); RE2(200); DO2(400);
	RA(180); SI1(170); SO(160); KARA(5); SO(160); SI1(160); RA(180); FA(160); KARA(40);
	RA(180); SO(160); MI(160); KARA(40); FA(300); KARA(80);
}
return 0;
}

void KARA(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<248;i++){
			PORTD=0x00;
		}
		for(i=0;i<248;i++){
			PORTD=0x00;
		}
	}
}
void DO(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<248;i++){
			PORTD=0x10;
		}
		for(i=0;i<248;i++){
			PORTD=0x00;
		}
	}
}
void RE(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<217;i++){
			PORTD=0x10;
		}
		for(i=0;i<217;i++){
			PORTD=0x00;
		}
	}
}
void MI(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<195;i++){
			PORTD=0x10;
		}
		for(i=0;i<195;i++){
			PORTD=0x00;
		}
	}
}
void FA(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<180;i++){
			PORTD=0x10;
		}
		for(i=0;i<180;i++){
			PORTD=0x00;
		}
	}
}

void SO(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<160;i++){
			PORTD=0x10;
		}
		for(i=0;i<160;i++){
			PORTD=0x00;
		}
	}
}
void RA(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<143;i++){
			PORTD=0x10;
		}
		for(i=0;i<143;i++){
			PORTD=0x00;
		}
	}
}
void SI1(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<134;i++){
			PORTD=0x10;
		}
		for(i=0;i<134;i++){
			PORTD=0x00;
		}
	}
}
void SI(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<130;i++){
			PORTD=0x10;
		}
		for(i=0;i<130;i++){
			PORTD=0x00;
		}
	}
}
void DO2(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<120;i++){
			PORTD=0x10;
		}
		for(i=0;i<120;i++){
			PORTD=0x00;
		}
	}
}
void RE2(int k){
	int i,m;
	for(m=0;m<k;m++){
		for(i=0;i<110;i++){
			PORTD=0x10;
		}
		for(i=0;i<110;i++){
			PORTD=0x00;
		}
	}
}

■現在のカテゴリ: 講義 > マイコン・サイエンス講座 > 講座内容

講義

コンピュータ・サイエンス講座

マイコン・サイエンス講座


n.s.研究所

About n.s.研究所

Activity 活動予定

Thema 研究テーマ

単細胞生物の行動に関する研究

多細胞生物の行動に関する研究

社会性昆虫の行動に関する研究

人の認識に関する研究

環境に関する研究

Thema 開発テーマ

ロボットの開発

作ってみた

Lecture 講義

コンピュータ・サイエンス講座

マイコン・サイエンス講座

Library ライブラリ

LabVIEWを使った物理数学集

VisualC++

Report 報告

学会等での発表

論文発表

その他の報告