聞くという観点から音楽を考えた場合、利用できる媒体はここ十数年の間をとってもカセットテープからCD、MiniDiscから今日ではMP3などの音声圧縮方式を利用したものへと変化し、より圧縮率が高く、小型化、可搬化に優れたものへと変化してきている。しかし楽曲として見た音楽は変化するものではない。楽曲データを演奏したものを聴くという音楽鑑賞という形態においてリスナーの前で楽曲の演奏を行うという手法はある意味究極であるといえる。本研究では音の基礎を学び、音の入出力を行う際の足がかりとするためにPICマイコンを利用し音楽の自動演奏を行う装置を作成した。
PICとはperipheral Interface Controllerの略称で、マイクロチップテクノロジー社のワンチップ・マイクロプロセッサーのことである。
PICはクロック発振に使用するクリスタルかセラミックの発振子と使用するPICに応じた安定化した直流電源があれば動作する。
PICマイコン用の統合開発環境ソフトウエアとしてMPLAB IDEが無償で提供されており、マイクロチップテクノロジー本社サイトからダウンロードすることができる。テキストエディタ、アセンブラ、デバッガシミュレータが入っている。また、Cコンパイラ、インサーキットエミュレータ、インサーキットデバッガ、プログラムライタを組み込むことができる。 本研究ではdsPIC30f2012を使用し、MPLAB IDEにCコンパイラを組み込み、開発言語としてC言語を使用する。 インサーキットデバッガとしてMPLAB ICD 2 、実験用ボードとして16bit 28-pin DEMO BOARDを使用する。
dsPIC30f2012を使用したマイコンシーケンサーであり、PORTB0〜9までの10のI/OPORTを使用し方形波、三角波をそれぞれ出力することができる。440HzのA音(ラ)から1760HzのA音(ラ)まで3オクターブ25音の音を発生させる。音価の基準となる音符の種類は全音符、二分音符、四分音符、八分音符、十六分音符、三十二分音符の六種類である。また、テンポのコントロールが可能である。
MMLのような楽譜を簡単にプログラムに変換できるAPIを作成する。MMLとはMusic Macro Languageの略でコンピュータ上の楽譜となるような簡易言語である。各音階、音長を簡単な記号を用いて表現できる。APIには予め作成した25音の音階と音価についてマクロ化したものを用意する。楽曲の演奏を行う場合、楽譜はプログラム中に挿入する。
PICは基本的にはデジタルデータしか扱えないため、スピーカから音声を出力するためにはアナログ信号に変換する必要がある。(D/A変換)本研究では使用したdsPIC30f2012のPORTBにPORTB0〜9までの10bitのI/OPORTがあるため、これを使用する。入力が10bitのD/A変換を行うラダー抵抗回路を製作した。ラダー抵抗回路は2種類の抵抗のみで製作することができるD/A変換回路である。デジタルデータが10ビットでPORTBの出力電圧が5Vのとき、デジタルデータ値とD/A変換結果の電圧は以下の式となる。
電圧=(5V/1024)×デジタルデータ値
(デジタルデータ値:0〜1023)
ノイズを抑制するためにローパスフィルタを用いた。ローパスフィルタとは特定の周波数以外の信号を遮断するフィルタのうち、電気信号の低い周波数成分を取り出すフィルタ回路である。高い周波数成分は抑制される。また、直流分をカットする目的で電解コンデンサを使用している。
電解コンデンサーの先にミニジャックを接続し、スピーカのミニプラグを挿し込みスピーカを接続し音を発生させる。今回使用したスピーカの仕様は以下の通りである。
ロジクール LS11 ステレオ2.0チャンネル PCスピーカ
特定の音階を出すためには各々の音に応じた特定の周波数を持った波を出力する必要がある。本研究では十二平均律に基づく音階周波数を採用し調整を行った。
十二平均律とは一オクターブの周波数の幅を十二の平等な音程に分け、それを半音として定めた音律である。隣り合う二つの音の周波数比が2の12乗根(1.0594631)であらわされる。
本研究で作成した音名と各音階の周波数の対応は表1のようになっている
表1 音名と周波数の対応表(十二平均律)
音名 | 周波数 |
A | 440 |
A#/Bb | 466.16376 |
B | 493.8833 |
C | 523.25113 |
C#/Ab | 554.36526 |
D | 587.32953 |
D#/Eb | 622.25396 |
E | 659.25511 |
F | 698.45646 |
F#/Gb | 739.98884 |
G | 783.99087 |
G#/Ab | 830.60939 |
A | 880 |
A#/Bb | 932.32752 |
B | 987.7666 |
C | 1046.5022 |
C#/Ab | 1108.7305 |
D | 1174.659 |
D#/Eb | 1244.5079 |
E | 1318.5102 |
F | 1396.9129 |
F#/Gb | 1479.9776 |
G | 1567.9817 |
G#/Ab | 1661.2187 |
A | 1760 |
方形波はI/OPORTでON、OFFを繰り返すことで発生させることができる。各々の音階に対する周波数の調整はI/OPORTのON、OFFの間隔をC言語のライブラリに付属してくるdelay_us関数を用いることで行った。
delay_us関数は指定した時間(μs)処理を遅らせる働きがある。
A音の周波数は440Hzである(一秒間に440個の波の山が出る)
440hzのA音の場合、図7のように一秒間に440個の波の山が出ることより、A音の周波数とxの関係式は以下のようになる。
1000×1000(μs)/2×x=440
この式よりx=1136.3636…と求まり、この値を目安に実際の回路でオシロスコープを用いて実測を行い440HZに最も近い値を採用した。
同様にして466.16376HzのA#音から1760HzのA音まで3オクターブ25音を作成した。
表2 各音名とdelay_usに与える値xとの対応表
音名 | 周波数(HZ) | X |
A | 440 | 1117 |
A#/Bb | 466.16376 | 1054 |
B | 493.8833 | 996 |
C | 523.25113 | 939 |
C#/Ab | 554.36526 | 885 |
D | 587.32953 | 835 |
D#/Eb | 622.25396 | 787 |
E | 659.25511 | 742 |
F | 698.45646 | 701 |
F#/Gb | 739.98884 | 660 |
G | 783.99087 | 622 |
G#/Ab | 830.60939 | 587 |
A | 880 | 552 |
A#/Bb | 932.32752 | 521 |
B | 987.7666 | 491 |
C | 1046.5022 | 463 |
C#/Ab | 1108.7305 | 437 |
D | 1174.659 | 411 |
D#/Eb | 1244.5079 | 388 |
E | 1318.5102 | 365 |
F | 1396.9129 | 344 |
F#/Gb | 1479.9776 | 324 |
G | 1567.9817 | 305 |
G#/Ab | 1661.2187 | 287 |
A | 1760 | 270 |
方形波を用いて音を出した場合、正弦波を用いた場合に比べ硬めの音色となる。
ここでは正弦波に近い三角波で音を出すことを考える。
三角波は図8のように1ビットずつある値xまで増加減少を繰り返すことで波を作成した。
xの値を変更することで、周波数を調整し、方形波のときと同様に440HzのA音から1960HzのA音までを作成した。
表3 各音と設定したcounterの値との対応表
音名 | 周波数(Hz) | Counter(x=段数) |
A | 440 | 199 |
A#/Bb | 466.16376 | 188 |
B | 493.8833 | 178 |
C | 523.25113 | 168 |
C#/Ab | 554.36526 | 159 |
D | 587.32953 | 150 |
D#/Eb | 622.25396 | 141 |
E | 659.25511 | 133 |
F | 698.45646 | 126 |
F#/Gb | 739.98884 | 119 |
G | 783.99087 | 112 |
G#/Ab | 830.60939 | 106 |
A | 880 | 100 |
A#/Bb | 932.32752 | 94 |
B | 987.7666 | 89 |
C | 1046.5022 | 84 |
C#/Ab | 1108.7305 | 79 |
D | 1174.659 | 75 |
D#/Eb | 1244.5079 | 71 |
E | 1318.5102 | 67 |
F | 1396.9129 | 63 |
F#/Gb | 1479.9776 | 59 |
G | 1567.9817 | 56 |
G#/Ab | 1661.2187 | 53 |
A | 1760 | 50 |
楽曲を演奏するためには相対的な音の長さ(音価)を設定する必要がある。
全音符を基準としたとき、各音符の音価は表4のようになる。
表4 音符の種類と音価
音符の種類 | 音価 |
全音符 | 1 |
2分音符 | 0.5 |
4分音符 | 0.25 |
8分音符 | 0.125 |
16分音符 | 0.0625 |
32分音符 | 0.03125 |
テンポは毎分の4分音符の拍数として与える。
音長を変更したときでも音の周波数は常に一定でなければならない。
5.2.1、5.2.2では音長と音の周波数の比例を考え等式を作成し、方形波、三角波それぞれについて音長の調整を行った。
音長の調整は音長と音の周波数の比例を考えることで行えるが、ここでは音の持つ山の数を考えることで式を立てることとする。
440HzのA音を例に挙げると図7のように一秒間には440個の山を持つ音の波が生じる。440Hzの方形波半周期にかかる時間をXz(μs)とすると、音の波の山の数は1000×1000(μs)×1(s)/2×Xz(μs)=440と求めることができる。
音長がy(秒)、音の周波数をZ、音の周波数がZのときの方形波半周期にかかる時間をx(μs)とすると図9のように音の波の山の数は 1000×1000(μs)×y(s)/2×x(μs)=Z×yと求めることができ、このとき音の周波数はZ(Hz)となる。よって、 1000×1000(μs)×y(s)/2×x(μs)で求められる波はZ(Hz)であり、そのときの音長はy(s)となる。
三角波でも方形波同様に音の周波数をZ(Hz)としたとき生じる音の波の山の数を考え式を立てる。
図10のように波の周波数をZ、音長をyとしたときの音長と生じる音の波の山の数の関係はy×1000×1000(μs)/2×x×一段あたりの時間(μs)=Z×yが成り立つことがわかる。
この式より一段あたりの時間が求まれば、音長yを調整することが可能であることがわかった。 ここでxの値は各音階ごとに2でcounterとして実測した値である。 そこで一段あたりの時間をx,y,Zの値から逆算することで求めた。
440HZのA音のときを例として挙げると、440Hzの波は一秒間で440周期(440個の音の波の山が生じる)であることよりx=199,y=1,Z=440となる。これを上記の式に代入することで一段あたりの時間は5.7(μs)と求まった。よって、y×1000×1000(μs)/2×x×5.7(μs)=Z×yが成り立つ。
5.7の逆数を掛け、式を整理し、y×5/ x×175438=Z×yこのとき音の周波数はZ(Hz)となる。よって、 y×5/ x×175438で求められる三角波はZ(Hz)でありそのときの音長はy(s)となる。
void play (int x,float y){ int i; for (i=0 ; i<500000*y/x ; i++){ square(x);
void play (int x,double y){ int i; for (i=0 ; i<5*y/x*175438 ; i++){ triangul(x);
出力された波に対し音程と音長をコントロールするために 上記のようなplay関数を作成した。
xは5.1で作成した25音に対応した値、yは音長である。
for文の中の式は5.2で作成した音の周波数と音長の関係式である。 x、yにそれぞれの値を代入するとfor文の中の式により対応する周波数が決まる。 つまり、作成したplay関数は値xのときの周波数の音が音長y出力される関数である。
xとyはそれぞれ#defineを用いてマクロ化した。 マクロ化によりx、yに値を直接代入せずとも関数が利用できる。
音名は880HzのA音を中央とし同じ記号を持つ音に関してはLOW、HIGHの頭文字を用いて区別することとした。
表5 プログラム中の音名のマクロ記述と周波数の対応表
マクロ記述 | 周波数(Hz) |
AL | 440 |
A$L | 466.16376 |
BL | 493.8833 |
C | 523.25113 |
C$ | 554.36526 |
D | 587.32953 |
D$ | 622.25396 |
E | 659.25511 |
F | 698.45646 |
F$ | 739.98884 |
G | 783.99087 |
G$ | 830.60939 |
A | 880 |
A$ | 932.32752 |
B | 987.7666 |
CH | 1046.5022 |
C$H | 1108.7305 |
DH | 1174.659 |
D$H | 1244.5079 |
EH | 1318.5102 |
FH | 1396.9129 |
F$H | 1479.9776 |
GH | 1567.9817 |
G$H | 1661.2187 |
AH | 1760 |
表6 プログラム中の音長のマクロ記述と音長の対応表
音長 | マクロ記述 |
全音符 | L1 |
2分音符 | L2 |
4分音符 | L4 |
8分音符 | L8 |
16分音符 | L16 |
32分音符 | L32 |
はじめにテンポを設定する。tempo=120の場合、int tempo=120と楽曲データのはじめに記述することで音長が決まる。
各音符の作成は上記で作成したplay関数を用いる。play(音名,音長);となっており、例としてplay(AL,L4);と記述すれば440HzのA(ラ)の四分音符を表す。
以下のように記述すればtempo=120,四分音符でドレミファソラシドと出力できる。
int tempo=120 play(C,L4); play(D,L4); play(E,L4); play(F,L4); play(G,L4); play(A,L4); play(B,L4); plau(CH,L4);
本研究ではdsPIC30f2012を用いてPORTB0〜9までの10のI/OPORTを使用しラダー抵抗回路を用いたD/A変換を行い、方形波、三角波をそれぞれ出力し、440HzのA音から1760HzのA音まで3オクターブ25音の音階を実際にオシロスコープを用いて測定を行い、周波数を調整し音階を作成し、MMLに準ずるプログラムを作成し楽曲の演奏を行う装置を製作した。実際に製作を行うことによっていくつかの問題点と課題が浮き彫りとなった。
#include <p30f2012.h> #define Clock 7370000*16 _FWDT(WDT_OFF); /* disablle Watchdog Timer */ _FGS(CODE_PROT_OFF); /* General Code Segment --> not guard */ _FOSC(CSW_FSCM_OFF&FRC_PLL16); /*FRC(7.37MHz)x16/4=29.48MHz,Tcy=33.92ns */ _FBORPOR(PBOR_OFF & PWRT_64 & MCLR_EN); #define AL 1117 //440hzのラ音 #define A$L 1054 #define BL 996 #define C 939 #define C$ 885 #define D 835 #define D$ 787 #define E 742 #define F 701 #define F$ 660 #define G 622 #define G$ 587 #define A 552 #define A$ 521 #define B 491 #define CH 463 #define C$H 437 #define DH 411 #define D$H 388 #define EH 365 #define FH 344 #define F$H 324 #define GH 305 #define G$H 287 #define AH 270 #define R 0 // 休符 //Length #define L1 tempo/60.0 // 全音符/休符 #define L2 tempo/120.0 // 2分音符/休符 #define L4 tempo/240.0 // 4分音符/休符 #define L8 tempo/480.0 // 8分音符/休符 #define L16 tempo/960.0 // 16分音符/休符 #define L32 tempo/1920.0 // 32分音符/休符 void __delay32(unsigned long cycles); void delay_us(unsigned int N) { __delay32((unsigned int)((double)Clock/4000/1000*N)); //システムクロック周波数4000000HzのPICの1サイクルディレー時間=1μsec } void square (int x){ PORTB = 0x0000; delay_us(x); PORTB = 0x03ff; delay_us(x); } int main() { ADPCFG = 0x03ff; //picのAD変換機能OFF TRISB = 0x0000; void play (int x,float y){ int i; for (i=0 ; i<500000*y/x ; i++){ square(x); } } while(1){ //ここに楽曲データを挿入 } return 0; }
#include <p30f2012.h> #define Clock 7370000*16 _FWDT(WDT_OFF); /* disablle Watchdog Timer */ _FGS(CODE_PROT_OFF); /* General Code Segment --`> not guard */ _FOSC(CSW_FSCM_OFF&FRC_PLL16); /*FRC(7.37MHz)x16/4=29.48MHz,Tcy=33.92ns */ _FBORPOR(PBOR_OFF & PWRT_64 & MCLR_EN); #define AL 199 //440hzのラ音 #define A$L 188 #define BL 178 #define C 168 #define C$ 159 #define D 150 #define D$ 141 #define E 133 #define F 126 #define F$ 119 #define G 112 #define G$ 106 #define A 100 #define A$ 94 #define B 89 #define CH 84 #define C$H 79 #define DH 75 #define D$H 71 #define EH 67 #define FH 63 #define F$H 59 #define GH 56 #define G$H 53 #define AH 50 #define R 0 // 休符 //Length #define L1 tempo/60.0 // 全音符/休符 #define L2 tempo/120.0 // 2分音符/休符 #define L4 tempo/240.0 // 4分音符/休符 #define L8 tempo/480.0 // 8分音符/休符 #define L16 tempo/960.0 // 16分音符/休符 #define L32 tempo/1920.0 // 32分音符/休符 #define UpMode 0 #define DownMode 1 void __delay32(unsigned long cycles); void delay_us(unsigned int N) { __delay32((unsigned int)((double)Clock/4000/1000*N)); //システムクロック周波数4000000HzのPICの1サイクルディレー時間=1μsec } short int Mode = UpMode; int counter=0; void triangul (int x){ if(Mode == UpMode) // 増加モード { PORTB=++counter; delay_us(0); if(counter == x)Mode = DownMode; } else //減少モード { PORTB=--counter; delay_us(0); if(counter == 0)Mode = UpMode; } } int main() { ADPCFG = 0x03ff; //picのAD変換機能OFF TRISB = 0x0000; void play (int x,double y){ int i; for (i=0 ; i<5*y/x*175438 ; i++){ triangul(x); } } while(1){ //ここに楽曲データを挿入 } return 0; }
int tempo = 112; // テンポ(毎分の四分音符の拍数) play (A,L4); // play (A,L4); play (B,L2); play (A,L4); play (A,L4); play (B,L2); play (A,L4); play (B,L4); play (CH,L4); play (B,L4); play (A,L4); play (B,L8); play (A,L8); play (F,L2); // play (E,L4); play (C,L4); play (E,L4); play (F,L4); play (E,L4); play (E,L8); play (C,L8); play (BL,L2); play (A,L4); play (B,L4); play (CH,L4); play (B,L4); play (A,L4); play (B,L8); play (A,L8); play (F,L2);// play (E,L4); play (C,L4); play (E,L4); play (F,L4); play (E,L4); play (E,L8); play (C,L8); play (BL,L2); play (A,L4); play (A,L4); play (B,L2);// play (A,L4); play (A,L4); play (B,L2); play (E,L4); play (F,L4); play (B,L8); play (A,L8); play (F,L4); play (E,L2); play (R,L4);