cembeddedstm32stm32f4

What's the reason for that noise and why do my exact values not produce any noise?


I've been working with an STM32F4, wanting to generate notes, passing values to a DAC like so:

float playNote(float noteFrequency, float t)
{
    float w = 2 * PI * noteFrequency;
    return sinf(w * t);
}

With this being how a note is generated in DAC (with DAC size being 500):

void getData4Dac(uint16_t* dac_buff, uint8_t note)
{
    uint16_t n;
    float t = 0;

    for(n=0; n<(DAC_BUFF_SIZE); n++)
    {
        switch (note)
        {
            case 0:
                y = 0;       // Pause
                break;
            case 1:
                y = playNote(NOTE_C, t);
                break;
            case 2:
                y = playNote(NOTE_D, t);
                break;
            case 3:
                y = playNote(NOTE_E, t);
                break;
            case 4:
                y = playNote(NOTE_F, t);
                break;
            case 5:
                y = playNote(NOTE_G, t);
                break;
            case 6:
                y = playNote(NOTE_A, t);
                break;
            case 7:
                y = playNote(NOTE_H, t);
                break;
            case 8:
                y = playNote(NOTE_C2, t);
                break;
            default:
                y = 0;
                break;
        }

        dac_buff[n] = (uint16_t)(y * 2047 + 2048);
        t = t + 0.5e-4;
    }
}

Now I've got two problems here.

The first one (which is really a question): The values of NOTE_x are:

#define NOTE_C   240
#define NOTE_D   280
#define NOTE_E   320
#define NOTE_F   360
#define NOTE_G   400
#define NOTE_A   440
#define NOTE_H   480
#define NOTE_C2  520

Real note frequencies for the 4th Octave range from 261.63 Hz to 493.88 Hz, but if I set my frequencies to those exact values, there is a lot of noise. What's the reason for that noise and why do my exact values produce no noise?

Also, I wanted to play multiple notes at the same time like so:

for(n=0; n<(DAC_BUFF_SIZE); n++)
{
    active_notes = 0;
    y = 0;
    for(i = 0; i < NUMBER_OF_NOTES; i++)
    {
        if(note_durations[i] > 0)
        {
            active_notes++;
            y += sinf(2 * PI * note_frequencies[i] * (1 << octave) * t);
            //y = sinf(2 * PI * note_frequencies[i] * (1 << octave) * t);
        }
    }
    y /= active_notes;
    t += 0.5e-4;
    dac_buff[n] = (uint16_t)(y*2047 + 2048);
}

But doing this y += [signal] totally messes up the sound and creates a lot of noise.

Why is that happening and what do I have to keep in mind to get the notes clean when doing y += [signal]?

Here's my entire main function:

#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "cs43l22.h"
#include "dac.h"
#include "math.h"

#define PI             3.1415
#define DAC_BUFF_SIZE  500

#define NOTE_C         230
#define NOTE_D         280
#define NOTE_E         320
#define NOTE_F         360
#define NOTE_G         400
#define NOTE_A         440
#define NOTE_H         480
#define NOTE_C2        520

#define NOTE_Cis       17.32
#define NOTE_Dis       19.45
#define NOTE_Fis       23.12
#define NOTE_Gis       25.96
#define NOTE_Ais       29.14

extern volatile int8_t octave;
extern volatile uint8_t note_durations[NUMBER_OF_NOTES];
static float note_frequencies[NUMBER_OF_NOTES];

uint16_t dac_buff[DAC_BUFF_SIZE];
uint32_t btime = 0;

void getData4Dac(uint16_t * dac_buff, int8_t octave);
void initNotes(void);
void updateNoteDurations(void);

int main(void)
{
    initUSART2(USART2_BAUDRATE_921600);
    enIrqUSART2();

    initDmaDAC1(dac_buff, DAC_BUFF_SIZE);
    getData4Dac(dac_buff, octave);

    initCS43L22(10, 48000);
    initSYSTIM();
    initNotes();
    printUSART2("PLAY NOW: ");

    while(1)
    {
        if(chk4TimeoutSYSTIM(btime, 1000) == SYSTIM_TIMEOUT)
        {
            updateNoteDurations();
            btime = getSYSTIM();
        }
        while((SPI3->SR & 0x00000002) == 0)
            ;

        SPI3->DR = 0x00;

        chkBuffUSART2();
        getData4Dac(dac_buff, octave);
    }
}

void getData4Dac(uint16_t* dac_buff, int8_t octave)
{
    uint8_t active_notes;
    uint16_t n, i;
    float y = 0, t = 0;

    for(n=0; n<(DAC_BUFF_SIZE); n++)
    {
        active_notes = 0;
        y = 0;
        for(i = 0; i < NUMBER_OF_NOTES; i++)
        {
            if(note_durations[i] > 0)
            {
                active_notes++;
                y += sinf(2 * PI * note_frequencies[i] * t); // sklonjeno octave
            }
        }
        y /= active_notes;
        t += 0.5e-4;
        dac_buff[n] = (uint16_t)(y*2048 + 2048);
    }
}

void initNotes()
{
    uint8_t i;
    for(i = 0; i < NUMBER_OF_NOTES; i++)
    {
        note_durations[i] = 0;
    }
    note_frequencies[0] = NOTA_C;
    note_frequencies[1] = NOTA_Cis;
    note_frequencies[2] = NOTA_D;
    note_frequencies[3] = NOTA_Dis;
    note_frequencies[4] = NOTA_E;
    note_frequencies[5] = NOTA_F;
    note_frequencies[6] = NOTA_Fis;
    note_frequencies[7] = NOTA_G;
    note_frequencies[8] = NOTA_Gis;
    note_frequencies[9] = NOTA_A;
    note_frequencies[10] = NOTA_Ais;
    note_frequencies[11] = NOTA_H;
    note_frequencies[12] = NOTA_C2;
}

void updateNoteDurations()
{
    uint8_t i;
    for(i = 0; i < NUMBER_OF_NOTES; i++)
    {
        if(note_durations[i] > 0)
        {
            note_durations[i]--;
        }
    }
}

Solution

  • What's the reason for that noise and why do my exact values not produce any noise?

    Short answer: Discontinuity

    If I understand your code correct, you fill the dac_buffer with samples and then replays the same dac_buffer a number of times.

    The time starts at 0 and you increase the time with 0.5e-4 for each sample and you have 500 samples, so the number of sine-periods in the buffer will be:

    periods = 0.5e-4 * 500 * NOTE_FREQUENCY
    

    So for these notes you have:

    #define NOTE_C        240  // exactly 6 periods
    #define NOTE_D        280  // exactly 7 periods
    

    but for note frequency 261.63 you'll get 6.54 periods.

    A single buffer will hold:

    enter image description here

    and when you repeat that e.g. 3 times you get:

    enter image description here

    enter image description here As the picture shows, you have discontinuity when you start the replay. Such discontinuity causes noise.

    As e.g. note frequency 240 is exactly 6 periods, it will not have that discontinuity and consequently less noise.

    If possible, depending on note frequency you need to adjust the number of samples used so that it always matches an integer number of periods (or as close as posible).

    BTW: Recreating a signal directly from samples will always contain noise. To get rid of that, you need a low-pass filter after the DAC.