マイクロコントローラーからのポリフォニックサウンド?


14

ピエゾブザーに接続された単一のピンを(可変レートで)切り替えることにより、モノフォニックサウンドを作成できます。

ソフトウェアで2つの混合オーディオ信号を生成してポリフォニーを作成するにはどうすればよいですか?

簡単な曲を演奏するために使用しているコードは次のとおりです。

#define F_CPU 8000000UL // 8MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>

// number of timer0 overflows/sec
#define INT_PER_SEC 31250

// Frequencies (in Hz) of notes
#define F_FSH_4 370
#define F_A_4 440
#define F_B_4 494
#define F_E_4 330
#define F_CSH_5 554
#define F_D_5 587
#define F_FSH_5 740
#define F_CSH_4 277
#define F_GSH_4 415

// number of timer0 overflows for notes
#define REST -1 // special case
#define FSH_4 INT_PER_SEC/F_FSH_4
#define A_4 INT_PER_SEC/F_A_4
#define B_4 INT_PER_SEC/F_B_4
#define E_4 INT_PER_SEC/F_E_4
#define CSH_5 INT_PER_SEC/F_CSH_5
#define D_5 INT_PER_SEC/F_D_5
#define FSH_5 INT_PER_SEC/F_FSH_5
#define CSH_4 INT_PER_SEC/F_CSH_4
#define GSH_4 INT_PER_SEC/F_GSH_4

#define SEMIQUAVER_TIME 60  // ms
#define BREATH_TIME 20      // ms

volatile uint32_t intrs = 0;
volatile int32_t curNote = REST;

// TIMER0 overflow
ISR(TIMER0_OVF_vect)
{
    if (curNote == REST)
        intrs = 0;
    else
    {
        intrs++;
        if (intrs >= curNote)
        {
            PORTD ^= _BV(PD4);
            intrs = 0;
        }
    }
}


void play(int32_t note, uint32_t len)
{
    int i;
    curNote = note;
    for (i = 0; i< len; i++)
        _delay_ms(SEMIQUAVER_TIME);
    curNote = REST;
    _delay_ms(BREATH_TIME);
}

int main(void)
{
    /* setup clock divider. Timer0 overflows on counting to 256.
     * 8Mhz / 1 (CS0=1) = 8000000 increments/sec. Overflows every 256, so 31250
     * overflow interrupts/sec */
    TCCR0B |= _BV(CS00);

    // enable overflow interrupts
    TIMSK0 |= _BV(TOIE0);

    // PD4 as output
    DDRD = _BV(PD4);

    TCNT0 = 0;
    intrs = 0;

    curNote = REST;

    // enable interrupts
    sei();

    while (1)
    {
        // Axel F
        play(FSH_4, 2);
        play(REST, 2);
        play(A_4, 3);
        play(FSH_4, 2);
        play(FSH_4, 1);
        play(B_4, 2);
        play(FSH_4, 2);
        play(E_4, 2);
        play(FSH_4, 2);
        play(REST, 2);
        play(CSH_5, 3);
        play(FSH_4, 2);
        play(FSH_4, 1);
        play(D_5, 2);
        play(CSH_5, 2);
        play(A_4, 2);
        play(FSH_4, 2);
        play(CSH_5, 2);
        play(FSH_5, 2);
        play(FSH_4, 1);
        play(E_4, 2);
        play(E_4, 1);
        play(CSH_4, 2);
        play(GSH_4, 2);
        play(FSH_4, 6);
        play(REST, 12);
    }
}

ちょっとこのことは人間の言語を発することができますか?言葉のように?
Rick_2047


@Jobyから提供されたリソースは素晴らしかったですが、デモを見ましたが、実際に聞こえるものは何もありません。
Rick_2047

DACがないわけではありません。
トビージャフィー

@Joby DACには何がありますか?
Rick_2047

回答:


8

簡単なトリックの1つは、PWMで2つのピンを使用し、それらをスピーカーの反対側に接続することです。次に、各ピンを異なる速度で変調すると、2つのノートを一度に再生できます。基本的に、スピーカーはそれらをミキシングします。3つ以上のメモがあり、前述のようにソフトウェアで行う必要があります。


1
PWM(目的の信号よりもはるかに高い周波数でスイッチング)を使用している場合は、1つの出力ピンのみを使用して複数の信号を既に混合することができます。
エンドリス

5

ポリフォニーを取得する標準的な方法は、一定の割り込みレート(ほとんどの場合8000 Hzまたは44100 Hz)で割り込み、各音源から「高」(+1)または「低」(-1)(または中間)を取得することです。 、すべての数値を合計して合計を取得し、その合計数をDACから送信します。

ここで他の人が言ったように、ちょっとした賢明さで、高速PWMはDACを置き換えることができます。

「マイクロポリフォニー」ページには、いくつかの詳細とヒントを提供します。


3

この素敵で古いPC DOSゲームの逸品は、PCスピーカーから実際のポリフォニックサウンドを使用していると思います。 ディガー

どうやって作ったのかわかりませんが、Cソースコードはサイトからダウンロードできます。


私はまだ私の頭の中で曲を聞くことができます
トビージャフィー



2

ソフトウェアを使用してスピーカーイベントのタイミングを計る場合、おそらく最も簡単な方法は、2つの独立したデータストリームを生成し、それらを交互に切り替えることです。このアプローチは、スピーカー出力がI / OピンまたはDACのどちらで制御されていても、かなりうまく機能します。例えば:

intセレクター。
uint16_t phase [8]、freq [8];

void interrupt(void) { selector ++; selector&= 7; phase [selector] + freq [selector]; DAC_OUT = sinewave [phase [selector] >> 8]; }

上記は、1996年にPICベースのオルゴールで使用した基本的なアプローチです(Cではなくアセンブリコードを使用)。割り込みレートは、有効なサンプルレートの8倍である必要がありますが、各割り込みは、単一の音声の処理のみを行う必要があることに注意してください。出力フィルタリングが良好な場合、このアプローチでは、サンプルを数値的に加算して出力するよりも3ビット多くの有効なDAC解像度が得られますが、サンプルレートとその倍数で多くのノイズが生成されることに注意してください。したがって、フィルタリングは、そうでない場合よりもさらに重要です。


1

彼らは昔のゲームシステムや「PCスピーカー」の時代にこれを行っていましたが、どのように私は知りません。

最初の推測:理想的に作成する波を考え、それをひどくクリップされた正方形に変形し、適切なタイミングで出力を切り替えてその正方形を作成することを想像してください。しかし、多くの相互変調があります。

再考:発振の周波数を大幅に上げて、PWMスタイルのアナログ信号出力できますか?


2
私はずっと前にNESエミュレーターを見ていたことを覚えており、それぞれがプログラム可能な周波数の3つの波形を使用したと思います。2つの方形波と1つの三角波。
mjh2007

...そして明らかに1つのノイズ源。 en.wikipedia.org/wiki/NES_Sound_Format
エンドリス

1

既に述べたように、これはPCスピーカー(オプションでPWMコントローラーに接続されたオン/オフのみをサポートする)で行われいたのと同じ方法で行うことができます。完全にオンまたはオフにならないほど十分に高速です(スイッチモード電源の動作に少し似ています)。これにより、スピーカーは常にオンとオフの間を移動し、アナログ信号を生成します。

唯一の落とし穴は、実際のスピーカーが必要なことです(ピエゾは非常に速く移動し、完全にオンになり、完全にオフになりすぎると思います)。私はいくつかの実験を行い、最大速度5MHzを思い付きました。これは、11,025 Hzのオーディオ信号に十分なはずです(おそらく、あなたが望む最高の品質です)。

もちろん、8ビットの11025Hz @ 11キロバイト/秒は、シリアルポートの速度よりもはるかに高速です。フラッシュに保存できるのは2〜3分のオーディオのみであるため、オンザフライで生成されたオーディオの再生にほぼ制限され、スピーカーをいじるのに十分なCPU時間を確保できます。

これを実現する方法は他にもいくつかあり、上記の方法のArduino実装がすでにあるようです。


2
スピーカー自体の移動速度に関係なく、スピーカーの前にフィルターを使用してPWMを滑らかにすることができます。
エンドリス


1

サウンドAを50ミリ秒程度の間再生してから、サウンドBを切り替えて、前後に切り替えます。アイデアは、耳が伝えるよりも速く切り替えることで、両方が同時に演奏しているように聞こえます。


1

Arduinoには2つのトーンを行うトーンライブラリがあると思います。使用しているAVRチップにコードを適合させることができるはずです。arduino.ccには、いくつかの優れた波形生成スレッドもあります。

DACを追加することにした場合は、http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html 4つの独立した出力チャネルに数値制御発振器の例を示します。クワッドDACとリファレンスは約2ドル程度です。


0

これは、2曲を同時に再生するための私のコードです。申し訳ありませんが、アクセスするにはAVRフリークに登録する必要があります。


4
私は...あなたがここにコードを掲示している場合、または私はアカウントを必要としない、どこかにあなたにupvoteを与えるだろう
トビーJaffey
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.