3.4 とりあえず使ってみる(音を出してみる)

音の出力をします。ここでは音の出力は3種類考えます。
1.DigitalOutによるもの
2.PWMによるもの
3.DACによるもの

必要な知識
音の周波数は等比数列で表すことができます。1オクターブあげるのには倍の周波数が必要です。
1オクターブあげるには2倍、2オクターブあげるには4倍です

周波数
261.626
293.665
329.628
ファ349.228
391.995
440.000
493.883

このホームページを参考にしましたhttp://flawtips.ami.amigasa.jp/blog/060315.html

1.DigitalOutによる単純矩形波によるもの

これは圧電スピーカを使用するには最適です。
圧電スピーカに力が加わると高電圧が発生してマイコンを壊すので抵抗を入れます。
/media/uploads/yueee_yt/sound_1.jpg
完成したコード/users/yueee_yt/programs/sound_1/lth75y

#define mC 261.626
#define mD 293.665
#define mE 329.628
#define mF 349.228
#define mG 391.995
#define mA 440.000
#define mB 493.883

#include "mbed.h"

DigitalOut sp1(p5);
Ticker timer;

int oto=0;

void tick(void)
{
    sp1.write(oto);
    oto=!oto;
}

int main() {
    float mm[]={mC,mD,mE,mF,mG,mA,mB,mC*2};
    int i;
    for (i=0;i<sizeof(mm);i++) {
        timer.attach(&tick,1.0/mm[i]/2.0);
        wait(0.5f);
    }
    timer.detach();
    while(1);
}

コードに間違いがあり修正しました。
なんかこれでは上手く出ません。リセットするたびに音程が変わっているような。。もしかしたらTimerとTickerって一緒に使えないのかなあ。LED例ではつかっているけど。

Quote:

float mm[]={mC*2,mD*2,mE*2,mF*2,mG*2,mA*2,mB*2,mC*4};
にして2オクターブからならまだ聞けるかも

2.PWMを使用した単純矩形波による音だし

PWMを使用してみましょう。
PWMは周波数を指定すれば良いので、プログラムがとても簡単になります。が、使えるポートは限られます。
以下のように接続
/media/uploads/yueee_yt/sound_2.jpg

完成したコード/users/yueee_yt/programs/sound_2/ltbtei

#define mC 261.626
#define mD 293.665
#define mE 329.628
#define mF 349.228
#define mG 391.995
#define mA 440.000
#define mB 493.883

#include "mbed.h"

PwmOut sp1(p25);

int main() {
    float mm[]={mC,mD,mE,mF,mG,mA,mB,mC*2};
    int i;
    
    for (i=0;i<sizeof(mm);i++) {
        sp1.period(1.0/mm[i]);
        sp1.write(0.5f);
        wait(0.5f);
        sp1.write(0.0f);
    }
    while (1);
}

Quote:

PwmOutにバグ?

int main() {
    float mm[]={mC,mD,mE,mF,mG,mA,mB,mC*2};
    int i;
    sp1.write(0.5f);
    for (i=0;i<sizeof(mm);i++) {
        sp1.period(1.0/mm[i]);
        wait(0.5f);
        }
    sp1.write(0.0f);
    while (1);
}

とする音が止まらない!
要はPwmOutは、
1.periodの設定
2. write
の手順でないとwriteができなくなるのか!

3.PWMで疑似Sin波音源を作る

PWMなのでこれも一応矩形波です、 今後でDACからADCへ出力するために10kHz(100μs)で
ここからはアンプとスピーカもしくはヘッドフォンが必要です。音量は小さくなります。圧電スピーカは使用できません。圧電スピーカを使用する場合は、アンプが必要です。
スピーカを使用する回路は圧電スピーカの部分を置き換えるだけ、抵抗はなくてもよいです。
プログラムはここ/users/yueee_yt/programs/sound_3/ltbkx4 float ms[179];はfloat ms[180];の間違い

#define mC 261.626
#define mD 293.665
#define mE 329.628
#define mF 349.228
#define mG 391.995
#define mA 440.000
#define mB 493.883

#include "mbed.h"

Ticker timer;
PwmOut sp1(p25);
float ms[180];
float m1;

void sound_out(void) {
    static float j=0; //jはStaticで保存できるようにします
    j=j+m1;           //jに100μSで動く角度/2を足します。
    if(j>180)j=j-180; //1周期を超えたら360/2を引く
    sp1.write(ms[(int)j]);
}

int main() {
    float mm[]={mC,mD,mE,mF,mG,mA,mB,mC*2};
    int i;
    //Sin波のテーブルを作成します(2度づつ)
    for (i=0;i<180;i++) {
        ms[i]=sin(2*3.1415*(float)i/180.0)/2.0+0.5;
    }

    sp1.period_us(10); //100kHz PWMは100kHzとしました。これは10kHzより早ければ問題なし

    for (i=0;i<sizeof(mm);i++) {
        m1=mm[i]*180/10000; //100μs(10kHz)で動く角度/2の時間を計算
        timer.attach_us(&sound_out,100); //10kHz
        wait(0.5f);
    }
    sp1.write(0.0f);
    while (1);
}

LPFを通せばいい音になるかも。

4.DACを使用して音を出す

DACで音を作ってみましょう
回路図
/media/uploads/yueee_yt/sound_4.jpg
プログラムはここ/users/yueee_yt/programs/sound_4/ltbx0k float ms[179];はfloat ms[180];の間違い
PWM版とあまり変更はありません。

#define mC 261.626
#define mD 293.665
#define mE 329.628
#define mF 349.228
#define mG 391.995
#define mA 440.000
#define mB 493.883

#include "mbed.h"

Ticker timer;
AnalogOut sp1(p18);
float ms[180];
float m1;

void sound_out(void) {
    static float j=0;
    j=j+m1;
    if(j>180)j=j-180;
    sp1.write(ms[(int)j]);
}

int main() {
    float mm[]={mC,mD,mE,mF,mG,mA,mB,mC*2};
    int i;
    //setting sincurv
    for (i=0;i<180;i++) {
        ms[i]=sin(2*3.1415*(float)i/180.0)/2.0+0.5;
    }

    for (i=0;i<sizeof(mm);i++) {
        m1=mm[i]*180/10000;
        timer.attach_us(&sound_out,100); //10kHz
        wait(0.5f);
    }
    sp1.write(0.0f);
    while (1);
}

たぶん10kHzのノイズがのっていると思われる。LPFで取り除けばそこそこきれいな音になるでしょう。

応用 和音

プログラムはここhttp://mbed.org/users/yueee_yt/programs/sound_5/ltgk7z float ms[179];はfloat ms[180];の間違い

#define mC 261.626
#define mD 293.665
#define mE 329.628
#define mF 349.228
#define mG 391.995
#define mA 440.000
#define mB 493.883

#include "mbed.h"

Ticker timer;
AnalogOut sp1(p18);
float ms[180];
float m1,m2,m3;

void sound_out(void) {
    static float j1=0;
    static float j2=0;
    static float j3=0;
    j1=j1+m1;
    j2=j2+m2;
    j3=j3+m3;
    if (j1>180)j1=j1-180;
    if (j2>180)j2=j2-180;
    if (j3>180)j3=j3-180;
    sp1.write((ms[(int)j1]+ms[(int)j2]+ms[(int)j3])/3.0);
}

int main() {
    int i;
    //setting sincurv
    for (i=0;i<180;i++) {
        ms[i]=sin(2*3.1415*(float)i/180.0)/2.0+0.5;
    }
    timer.attach_us(&sound_out,100); //10kHz
    //ceg
    m1=mC*2*180/10000;
    m2=mE*2*180/10000;
    m3=mG*2*180/10000;
    wait(1.0f);
    //cfa
    m1=mC*2*180/10000;
    m2=mF*2*180/10000;
    m3=mA*2*180/10000;
    wait(1.0f);
    //ceg
    m1=mC*2*180/10000;
    m2=mE*2*180/10000;
    m3=mG*2*180/10000;
    wait(1.0f);
    //bdg
    m1=mB*180/10000;
    m2=mD*2*180/10000;
    m3=mG*2*180/10000;
    wait(1.0f);
    //ceg
    m1=mC*2*180/10000;
    m2=mE*2*180/10000;
    m3=mG*2*180/10000;
    wait(1.0f);
    timer.detach();
    sp1.write(0.0f);
    while (1);
}

プログラムを少し修正しました。
PWM版はここ/users/yueee_yt/programs/sound_6/ltb35h float ms[179];はfloat ms[180];の間違い


6 comments on 3.4 とりあえず使ってみる(音を出してみる):

26 Jun 2011

いろんな方法で音が出せるのって面白いですね.

ちなみに各音の周波数をあらかじめmbedに計算させておくことも可能です.
"mbed.h"をインクルードしてあれば標準C数学関数が使えるので,プログラムの最初のほうで

#define     tone_A  440.0
    float       tone_freq[ 11 ];

    for ( int i = 0; i < 12; i++ )
        tone_freq[ i ]  = pow( 2, (float)i / 12.0 ) * tone_A;
}

のようにしてしまうのも手です.
(tone_freq配列には添字0〜11の範囲でA(ラ)〜G#(ソ#)までの周波数が入ります [12平均律で])
こうしておけばピッチをA=440Hzからシフトさせるときにも便利です(笑)

28 Jun 2011

コメントありがとうございます。
周波数を特に表記しなくてもよいので楽になると思います。
個人的にはドからが判りやすいですけど。ラからはちょっと。。慣れの問題かなあ

28 Jun 2011

確かに「ド」から始めるほうが馴染み深いですよね.
ド(261.6Hz)から始めるなら..

#define     tone_A  440.0
    float       tone_freq[ 11 ];

    for ( int i = 0; i < 12; i++ )
        tone_freq[ i ]  = pow( 2, ((float)(i + 3) / 12.0) - 1.0 ) * tone_A;

これで0〜11にド〜シが入ります.
ドレミファソラシの7音(半音なし)なら

#define     tone_A  440.0
    float       doremi_freq[ 7 ]    = { 3, 5, 7, 8, 10, 12, 14 };

    for ( int i = 0; i < 7; i++ )
        doremi_freq[ i ]  = pow( 2, (doremi_freq[ i ] / 12.0) - 1.0 ) * tone_A;

のような感じでしょうか (^^)

04 Jul 2011

上のコードの方ですが 配列宣言は個数分なので0-11の12個必要なら float tone_freq[12]; ですね。自分もコードのあちらこちらで間違っている。

08 Jul 2011

その通りです.12個必要ならfloat tone_freq[12];となります.[この例では「ド〜シ」までの1オクターブ分の周波数があれば,あとはオクターブによって「2^n」倍すればよいので,半音で11個分(0〜10)を用意してました.音域が1オクターブ分とするのであれば12個用意しておく方が便利ですね]

19 Dec 2016

見やすい記事をありがとうございます。 周辺のマイコンを使ったことがない人への説明に大変役立っています。

本題ですが、 「2.PWMを使用した単純矩形波による音だし」 の下で、PWMにバグ?と書かれていましたが、おそらくfor文の最後でこけていることが原因で for文の下のPWM.write(0.0f);が実行されていないと思われます。

sizeof()はそのように使用するならおそらく sizeof(mm)/sizeof(mm[0]) ではないかと…

ex. for (i=0;i<sizeof(mm)/sizeof(mm[0]);i++) { ... }

Please log in to post comments.