Search code examples
audioarduinofrequency

Ardunino - Buzzers with PWM


I am trying to make a polyphonic music player with buzzers & Arduino. I was following this tutorial as a guide but I can't figure out something. Code is like:

void playTone() {
    long elapsed_time = 0;
    if (tone_ > 0) { // if this isn't a Rest beat, while the tone has 
                     //  played less long than 'duration', pulse speaker HIGH and LOW
        while (elapsed_time < duration) {

            digitalWrite(speakerOut, HIGH);
            delayMicroseconds(tone_ / 2);

            // DOWN
            digitalWrite(speakerOut, LOW);
            delayMicroseconds(tone_ / 2);

            // Keep track of how long we pulsed
            elapsed_time += (tone_);
        }
    }
    else { // Rest beat; loop times delay
        for (int j = 0; j < rest_count; j++) { // See NOTE on rest_count
            delayMicroseconds(duration);
        }
    }
}

void loop() {
    // Set up a counter to pull from melody[] and beats[]
    for (int i = 0; i<MAX_COUNT; i++) {
        tone_ = melody[i];
        beat = beats[i];

        duration = beat * tempo; // Set up timing

        playTone();
        // A pause between notes...
        delayMicroseconds(pause);

    }
}

I am stucked at "digitalWrite(speakerOut, HIGH);" line. Ok before I call playTone function I set "tone_ = melody[i];" but in the function we only use this in delay function.

My question is how it follows the melody array? Based on that I will add one more buzzer and control it by another array but now I can't do anything...


Solution

  • I see your confusion as your program is blocking (just delay in loop) you can not add more melodies in parallel. The solution is to rewrite your code to non blocking interface using timer/counter and iterative process. For simplicity lets assume simple digital 1 bit sound. To ensure polyphony latter we need to have PWM so we can add more channels together.

    1. Lets start with monophonic tone

      for each sound channel you need to have actual state stored like:

      • period of tone T [T] where T = ftimer/ftone
      • actual time in the tone PWM cycle t [T] where 0 <= t <= T
      • tone amplitude A [-] or volume should be A <= T/2
      • speaker state on (ON/OFF)


      where [T] represents the period of some timer/counter this will be updated with (sampling rate) converting all the values into integers. Now you just create some update function that will be called from some timer periodically with high enough frequency and which will decide the state of speaker (on/off).

      mono tone

    2. Melody

      simply add some counter n [T] that represents how many cycles current tone should be playing. And in the update decrement it and if it hits zero fetch new tone from your melody table. So you need to add also some index which tone from your melody is currently playing.

    3. polyphony

      well you can achieve it by HW by wire-oring more digital pins (one per each channel). But this is doable also by SW means. Simply add each channel logicaly together by oring the on value of each should channel together and set the speaker by its result (A|B). That work but the output quality is not very good. Much better is use arithmetic addition (A+B) and use the result as a PWM for output that has some multiple of the sample frequency used for tone synthesis.

      polyphony

      You can also use DAC instead of PWM too ...

    I do not code for ARDUINO so I can not share full code. However on mine AVR32 the C/C++ code looks like this:

    //---------------------------------------------------------------------------
    #ifndef _music_h
    #define _music_h
    //---------------------------------------------------------------------------
    #ifdef VCL_H
    #define U8 BYTE
    #define U16 WORD
    #define U32 DWORD
    #endif
    //---------------------------------------------------------------------------
    //#define _music_shifted_notes
    //---------------------------------------------------------------------------
    // volume
    #define music_A  5
    // max channels
    #define music_sys_T 3
    // _music update freq
    #define music_f  ftim0/music_sys_T
    //---------------------------------------------------------------------------
    // music frequency 100*f [Hz], C_0 means C#0, P means pause
    //---------------------------------------------------------------------------
    #define P   0
    
    #ifdef _music_shifted_notes
    // musics data shifted frequencies C4 -> C1
    #define C1  26163
    #define C_1 27718
    #define D1  29366
    #define D_1 31113
    #define E1  32963
    #define F1  34923
    #define F_1 36999
    #define G1  39200
    #define G_1 41530
    #define A1  44000
    #define A_1 46616
    #define B1  49388
    
    #define C2  52325
    #define C_2 55437
    #define D2  58733
    #define D_2 62225
    #define E2  65925
    #define F2  69846
    #define F_2 73999
    #define G2  78399
    #define G_2 83061
    #define A2  88000
    #define A_2 93233
    #define B2  98777
    
    #define C3  104650
    #define C_3 110873
    #define D3  117466
    #define D_3 124451
    #define E3  131851
    #define F3  139691
    #define F_3 147998
    #define G3  156798
    #define G_3 166122
    #define A3  176000
    #define A_3 186466
    #define B3  197553
    
    #define C4  209300
    #define C_4 221746
    #define D4  234932
    #define D_4 248902
    #define E4  263702
    #define F4  279383
    #define F_4 295996
    #define G4  313596
    #define G_4 332244
    #define A4  352000
    #define A_4 372931
    #define B4  395107
    #else
    // real note frequuencies
    #define C0  1635
    #define C_0 1732
    #define D0  1835
    #define D_0 1945
    #define E0  2060
    #define F0  2183
    #define F_0 2312
    #define G0  2450
    #define G_0 2596
    #define A0  2750
    #define A_0 2914
    #define B0  3087
    
    #define C1  3270
    #define C_1 3465
    #define D1  3671
    #define D_1 3889
    #define E1  4120
    #define F1  4365
    #define F_1 4625
    #define G1  4900
    #define G_1 5191
    #define A1  5500
    #define A_1 5827
    #define B1  6174
    
    #define C2  6541
    #define C_2 6930
    #define D2  7342
    #define D_2 7778
    #define E2  8241
    #define F2  8731
    #define F_2 9250
    #define G2  9800
    #define G_2 10383
    #define A2  11000
    #define A_2 11654
    #define B2  12347
    
    #define C3  13081
    #define C_3 13859
    #define D3  14683
    #define D_3 15556
    #define E3  16481
    #define F3  17461
    #define F_3 18500
    #define G3  19600
    #define G_3 20765
    #define A3  22000
    #define A_3 23308
    #define B3  24694
    
    #define C4  26163
    #define C_4 27718
    #define D4  29366
    #define D_4 31113
    #define E4  32963
    #define F4  34923
    #define F_4 36999
    #define G4  39200
    #define G_4 41530
    #define A4  44000
    #define A_4 46616
    #define B4  49388
    
    #define C5  52325
    #define C_5 55437
    #define D5  58733
    #define D_5 62225
    #define E5  65925
    #define F5  69846
    #define F_5 73999
    #define G5  78399
    #define G_5 83061
    #define A5  88000
    #define A_5 93233
    #define B5  98777
    
    #define C6  104650
    #define C_6 110873
    #define D6  117466
    #define D_6 124451
    #define E6  131851
    #define F6  139691
    #define F_6 147998
    #define G6  156798
    #define G_6 166122
    #define A6  176000
    #define A_6 186466
    #define B6  197553
    
    #define C7  209300
    #define C_7 221746
    #define D7  234932
    #define D_7 248902
    #define E7  263702
    #define F7  279383
    #define F_7 295996
    #define G7  313596
    #define G_7 332244
    #define A7  352000
    #define A_7 372931
    #define B7  395107
    
    #define C8  418601
    #define C_8 443492
    #define D8  469863
    #define D_8 497803
    #define E8  527404
    #define F8  558765
    #define F_8 591991
    #define G8  627193
    #define G_8 664488
    #define A8  704000
    #define A_8 745862
    #define B8  790213
    #endif
    
    #define H0  B0
    #define H1  B1
    #define H2  B2
    #define H3  B3
    #define H4  B4
    #define H5  B5
    #define H6  B6
    #define H7  B7
    #define H8  B8
    #define H_0 B_0
    #define H_1 B_1
    #define H_2 B_2
    #define H_3 B_3
    #define H_4 B_4
    #define H_5 B_5
    #define H_6 B_6
    #define H_7 B_7
    #define H_8 B_8
    //---------------------------------------------------------------------------
    // music tempo delays [ms]
    //---------------------------------------------------------------------------
    #define T32   62
    #define T20  100
    #define T16  125
    #define T10  200
    #define T9   222
    #define T8   250
    #define T7   285
    #define T6   333
    #define T5   400
    #define T4   500
    #define T3   666
    #define T2  1000
    #define T1  2000
    //---------------------------------------------------------------------------
    typedef struct 
        {
        U32 T,t,n,on;
        U32 *melody;
        } _music;
    //---------------------------------------------------------------------------
    void music_play(_music *m,U32 *melody)
        {
        m->melody=melody;
        m->t=0;
        m->T=0;
        m->on=0;
        m->n=1;
        }
    //---------------------------------------------------------------------------
    void music_stop(_music *m)
        {
        m->melody=NULL;
        m->t=0;
        m->T=0;
        m->on=0;
        m->n=1;
        }
    //---------------------------------------------------------------------------
    void music_update(_music *m)
        {
        if (m->melody==NULL) { m->on=0; return; } 
        m->n--; if (!m->n)
            {
            U32 x;                      
            m->T=m->melody[0]; m->melody++; 
            if (m->T) m->T=(100*music_f)/m->T;
            if (m->melody==NULL) { m->on=0; return; }
            m->n=(m->melody[0]*music_f)/1000; m->melody++;
            m->on=1; m->t=0;
            if (m->n==0) m->melody=NULL;
            }   
        m->t++; if (m->t==m->T){ m->on=1; m->t=0; }
                if (m->t==music_A) m->on=0;
        }
    //---------------------------------------------------------------------------
    void music_sys_init(_music *m,int n)
        {
        for (int i=0;i<n;i++) music_stop(m+i);
        }
    //---------------------------------------------------------------------------
    void music_sys_update(_music *m,int n,U32 pin)
        {
        static U32 t=0,A=0;
        int i;
        t++;
        if (t==music_sys_T)
            { 
            t=0; A=0;
            for (i=0;i<n;i++) 
                {
                music_update(m+i);
                if (m[i].on) A++;
                }
            if (A) gpio_set_gpio_pin(pin); 
            }
        if (t==A) gpio_clr_gpio_pin(pin);
        }
    //---------------------------------------------------------------------------
    #endif
    //--------------------------------------------------------------------------- 
    

    So I just have some array of _music representing all my channels and call music_sys_update for them in some timer with frequency ftim0 I am using ftim0=200000 [Hz].

    #include "music.h"
    #define music_channels 2    
    volatile _music music[music_channels];
    
    const U32 music_data[]= // melody (frequency [0.01*Hz],duration [ms])
        {
        C5 ,400,D5 ,400,E5 ,400,C5 ,400,
        C5 ,400,D5 ,400,E5 ,400,C5 ,400,
        E5 ,400,F5 ,400,G5 ,800,E5 ,400,F5 ,400,G5 ,800,
        G5 ,200,A5 ,200,G5 ,200,F5 ,200,E5 ,400,C5 ,400,
        G5 ,200,A5 ,200,G5 ,200,F5 ,200,E5 ,400,C5 ,400,
        C5 ,400,G4 ,400,C5 ,800,
        C5 ,400,G4 ,400,C5 ,800,
        0,0, // end of melody
        };
    
    // this do once for init 
    music_sys_init(music,music_channels);
    // this starts playback on channel 0
    music_play(&music[0],music_data);
    
    // this should be inside timer but also blocking loop like this would do:
    for (;;)
        {
        music_sys_update(music,music_channels,sound_pin);
        wait 1000000/ftim0 [us]
        }
    

    so just port it to your environment and have fun :) ...