cembeddedstm32stm32f4

what's the reason for that noise and why do my exact values produce no 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.63Hz to 493.88Hz but if I set my frequencies to those exact values there is a lot of noise. My question is where is what's the reason for that noise and why do my exact values produce no noise?

The second question is:

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. Can someone help me figure out why that is happening and what do i have to keep in mind to get the notes clean when doing y += [signal].

Thanks!

Edit: 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

  • My question is where is what's the reason for that noise and why do my exact values produce no 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.