マイコンシーケンサーの製作

ネットワークシステム研究室
指導教員:坂本 直志 准教授
05kc066:高島 亮作

1.はじめに

聞くという観点から音楽を考えた場合、利用できる媒体はここ十数年の間をとってもカセットテープからCD、MiniDiscから今日ではMP3などの音声圧縮方式を利用したものへと変化し、より圧縮率が高く、小型化、可搬化に優れたものへと変化してきている。しかし楽曲として見た音楽は変化するものではない。楽曲データを演奏したものを聴くという音楽鑑賞という形態においてリスナーの前で楽曲の演奏を行うという手法はある意味究極であるといえる。本研究では音の基礎を学び、音の入出力を行う際の足がかりとするためにPICマイコンを利用し音楽の自動演奏を行う装置を作成した。

2.準備

2.1 PICについて

PICとはperipheral Interface Controllerの略称で、マイクロチップテクノロジー社のワンチップ・マイクロプロセッサーのことである。

PICはクロック発振に使用するクリスタルかセラミックの発振子と使用するPICに応じた安定化した直流電源があれば動作する。

2.2 開発環境

PICマイコン用の統合開発環境ソフトウエアとしてMPLAB IDEが無償で提供されており、マイクロチップテクノロジー本社サイトからダウンロードすることができる。テキストエディタ、アセンブラ、デバッガシミュレータが入っている。また、Cコンパイラ、インサーキットエミュレータ、インサーキットデバッガ、プログラムライタを組み込むことができる。 本研究ではdsPIC30f2012を使用し、MPLAB IDEにCコンパイラを組み込み、開発言語としてC言語を使用する。 インサーキットデバッガとしてMPLAB ICD 2 、実験用ボードとして16bit 28-pin DEMO BOARDを使用する。

図1 MPLAB ICD 2と16bit 28-pin DEMO BOARD

3.製作物の仕様

dsPIC30f2012を使用したマイコンシーケンサーであり、PORTB0〜9までの10のI/OPORTを使用し方形波、三角波をそれぞれ出力することができる。440HzのA音(ラ)から1760HzのA音(ラ)まで3オクターブ25音の音を発生させる。音価の基準となる音符の種類は全音符、二分音符、四分音符、八分音符、十六分音符、三十二分音符の六種類である。また、テンポのコントロールが可能である。

ハードウェア

図2  製作物のブロック図

ソフトウェア

MMLのような楽譜を簡単にプログラムに変換できるAPIを作成する。MMLとはMusic Macro Languageの略でコンピュータ上の楽譜となるような簡易言語である。各音階、音長を簡単な記号を用いて表現できる。APIには予め作成した25音の音階と音価についてマクロ化したものを用意する。楽曲の演奏を行う場合、楽譜はプログラム中に挿入する。

4.回路

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)

図3 ラダー抵抗回路の回路図
図4 ラダー抵抗回路

ノイズを抑制するためにローパスフィルタを用いた。ローパスフィルタとは特定の周波数以外の信号を遮断するフィルタのうち、電気信号の低い周波数成分を取り出すフィルタ回路である。高い周波数成分は抑制される。また、直流分をカットする目的で電解コンデンサを使用している。

図5 ローパスフィルタの回路図
図6 ローパスフィルタと電解コンデンサ

電解コンデンサーの先にミニジャックを接続し、スピーカのミニプラグを挿し込みスピーカを接続し音を発生させる。今回使用したスピーカの仕様は以下の通りである。

ロジクール LS11 ステレオ2.0チャンネル PCスピーカ

5.プログラム

5.1 音階の作成

特定の音階を出すためには各々の音に応じた特定の周波数を持った波を出力する必要がある。本研究では十二平均律に基づく音階周波数を採用し調整を行った。

十二平均律とは一オクターブの周波数の幅を十二の平等な音程に分け、それを半音として定めた音律である。隣り合う二つの音の周波数比が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

5.1.1 方形波の音階の作成

方形波はI/OPORTでON、OFFを繰り返すことで発生させることができる。各々の音階に対する周波数の調整はI/OPORTのON、OFFの間隔をC言語のライブラリに付属してくるdelay_us関数を用いることで行った。

delay_us関数は指定した時間(μs)処理を遅らせる働きがある。

図7 方形波440Hzのときの波と時間の関係図

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

5.1.2 三角波の音階の作成

方形波を用いて音を出した場合、正弦波を用いた場合に比べ硬めの音色となる。

ここでは正弦波に近い三角波で音を出すことを考える。

図8 作成した三角波の構成図

三角波は図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

5.2 音長とテンポの調整

楽曲を演奏するためには相対的な音の長さ(音価)を設定する必要がある。

全音符を基準としたとき、各音符の音価は表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では音長と音の周波数の比例を考え等式を作成し、方形波、三角波それぞれについて音長の調整を行った。

5.2.1 方形波の音長の調整

音長の調整は音長と音の周波数の比例を考えることで行えるが、ここでは音の持つ山の数を考えることで式を立てることとする。

440HzのA音を例に挙げると図7のように一秒間には440個の山を持つ音の波が生じる。440Hzの方形波半周期にかかる時間をXz(μs)とすると、音の波の山の数は1000×1000(μs)×1(s)/2×Xz(μs)=440と求めることができる。

図9 周波数Z(Hz)のときの音長yと時間xの関係図

音長が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)となる。

5.2.2 三角波の音長の調整

三角波でも方形波同様に音の周波数をZ(Hz)としたとき生じる音の波の山の数を考え式を立てる。

図10 周波数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)となる。

5.3 作成した関数について

方形波

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);

6.まとめ

本研究ではdsPIC30f2012を用いてPORTB0〜9までの10のI/OPORTを使用しラダー抵抗回路を用いたD/A変換を行い、方形波、三角波をそれぞれ出力し、440HzのA音から1760HzのA音まで3オクターブ25音の音階を実際にオシロスコープを用いて測定を行い、周波数を調整し音階を作成し、MMLに準ずるプログラムを作成し楽曲の演奏を行う装置を製作した。実際に製作を行うことによっていくつかの問題点と課題が浮き彫りとなった。

問題点と今後の課題

・ 三角波を作成する際、段数のみを変更することで波形を出したため、段数の少なくなる高音になればなるほど山の高さが低くなり音量が小さくなる。
段数を固定し一段あたりの時間を調整し周波数の設定を行う。
・ 楽曲データを挿入しテンポを指定することで音長が決まる。このとき商算が含まれており、方形波ではあまり影響はないが、三角波では誤差が大きくなる。
音価を予め比で表し、ビットシフトを行うことにより商算を減らし、誤差を少なくする。
・ 音階作成の際、オシロスコープを用いて実測であることによる人の手による誤差
本研究では一つの音につき三回の測定を行った。精度を上げるためには試行回数を増やすことが必要である。
・ 音色の追加
楽曲を表現するにあたり音色は重要な要素である。元となる波の形については正弦波、ノコギリ波の追加を行うプログラムの作成、PICのタイマを用いた和音の演奏を実現したい。
・ MMLの実装
本研究で作成したプログラムでは楽曲データ部分にplay関数が残ってしまい楽曲データの入力にその分手間がかかる。play関数をなくし記号のみで入力を行えるよう検討したい。

7.参考文献

8.付録

付録1 方形波で音階を出力するプログラム

#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;
}

付録2 三角波で音階を出力するプログラム

#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;
}

付録3 楽曲データ(さくらさくら 日本古謡

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);

付録4 回路図

付録5 全体図

付録6 今回作成した方形波と三角波(440Hz)