Close
0%
0%

PIC32MX music box with FM synthesis and I2S DAC

Six-channel FM synthesis music box using PIC32MX250F128B microcontroller with envelope generator.

Similar projects worth following
This is an experimental, ongoing work on a simple PIC32MX music box:1.) The sine waves are generated using DDS (Digital Direct Synthesis) for individual carriers and modulators.2.) Each FM oscillator has two sine wave oscillators, so for six channels FM oscillators = 12 sine wave oscillators.3.) The sine wave and decay envelope is pre-calculated in a table which resides in the flash.4.) Audio frames are sent to the I2S DAC after calculating and mixing the oscillator into it. DMA is also used here for sake of convenience.5.) To play music, Len Shustek's miditones is used. No midi parsing ability is present in the system.6.) Added Bruce Land's envelope generators: https://people.ece.cornell.edu/land/courses/ece4760/PIC32/index_DSP.htmlFor further info, refer to the respective Github in the links

Software used:

  • MPLAB X IDE v.4.0.1
  • MPLAB Harmony 1.07.01
  • MPLAB XC32 1.44

Hardware used:

  • PIC32MX250F128B
  • Cytron SK1632
  • PICkit 3
  • DAC used:
    • WM8731
    • PCM5102
    • UDA1334ATS

Note on hardware: You can also use a Digilent chipKIT DP32 but you have to backup the bootloader since this one doesn't use any bootloaders.

  • This project going public! Clicking noise dampened, but now crackling is heard!

    NYH-workshop02/07/2018 at 13:43 0 comments

    Hello! Weekend is still far away, but there it is, I managed to put the high-pass filter into the music box, and added note-damping when the note is stopped. Unfortunately, the clicking noise is still heard, but now it has become a very short crackle instead of the click which still can be heard all over the song. 

    The crackle is mildy audible and it becomes very prominent when it has fast note passages, which could sound irritating. The good news is, half of the clicking has been gotten rid of because I damp the notes right after the note-stop event.

    There is still some more work to do on it, and I'll post up a Youtube of the thing playing music soon! Waiting for that weekend now... :)

  • 2nd attempt of a 2nd-order Butterworth High-Pass

    NYH-workshop01/27/2018 at 12:26 0 comments

    My goodness! That's a long week and I had almost forgotten about this thing!

    Anyway, back to some experimentation - since this microcontroller doesn't have an FPU, I might as well as use fixed-point implementation. Using Bruce Land's simple fix16 implementation, and the other mindless experimentation on Matlab/Octave and online C compiler, I got this filter finally working.

    The filter information is as follows:

    • 2nd order
    • Direct form I algorithm
    • Coefficients:
    //Fixed point coefficients for 20hz cutoff 2nd order high pass Butterworth filter:
    fix16 coeff_b_fp[N+1] = { float2fix16(0.9973), float2fix16(-1.9945), float2fix16(0.9972) };
    fix16 coeff_a_fp[N+1] = { float2fix16(1.0000), float2fix16(-1.9944), float2fix16(0.9945) };
    inline void butterworth(short* inputData, fix16* outputData_fix16) {
        unsigned short i = 0;
        unsigned short j = 0;
        unsigned short k = 0;
        
        for (i = N ; i < (BUFFER_LENGTH/2) + N; i++) {
    
            // First order:
            //outputData_fix16[i] = multfix16(-1 * coeff_a_fp[1], outputData_fix16[i - 1]) + multfix16(coeff_b_fp[0], short2fix16(inputData[i])) + multfix16(coeff_b_fp[0], short2fix16(inputData[i]));
            
            // Second order:
            // Direct Form I algorithm:
            outputData_fix16[i]  = -1*multfix16(coeff_a_fp[1], outputData_fix16[i-1]) - multfix16(coeff_a_fp[2], outputData_fix16[i-2]) + multfix16(coeff_b_fp[0], short2fix16(inputData[i])) + multfix16(coeff_b_fp[1], short2fix16(inputData[i-1])) + multfix16(coeff_b_fp[2], short2fix16(inputData[i-2])); 
                                      
        }
    
        for (i = BUFFER_LENGTH/2, j = 0; i < (BUFFER_LENGTH/2 + N); i++, j++) {
            outputData_fix16[j] = outputData_fix16[i];
            inputData[j] = inputData[i];        
        }
    
    };

    And one thing is, please pay attention to the numbers at the right of the decimal point! I got the filter to behave erratically because I made a mistake rounding the numbers in the coefficients!

    Here is a 20hz signal (captured using Analog Discovery 2) being passed into the filter with the mistake in the coefficients (the b part):

    fix16 coeff_b_fp[N+1] = { float2fix16(0.9973), float2fix16(-1.9945), float2fix16(0.9972) };

    The most obvious thing is, it never did the job, but made the signal more amplified.

    Then, checking back at Matlab, the coefficients are:

    0.9972   -1.9945    0.9972

     Trying 0.9972 inside, it gives me this instead:

    Putting 0.9973 inside, now the whole signal got attenuated. That is very strange.  Is it some precision or rounding error that I don't know here??

    I have to make do with that coefficient, and this begs an explaination why I can't directly use the coefficients from Matlab, but I have to fine tune the numbers inside??

    Anyway, I hope I can fix the remaining poor output volume and that nasty click noise between music notes.

  • Added a 1st order Butterworth High-pass IIR filter!

    NYH-workshop01/14/2018 at 06:15 0 comments

    Man, the first and the second week of this year is rough as the weather isn't looking good!

    However, after for so many experimentation, I have squeezed out an IIR filter. To try the code, I have used the book "Real time Digital Signal Processing from Matlab to C with the TMS320Cx DSPs", Matlab/Octave and an online C compiler before putting this into the microcontroller:

    /******************************************************************************
    
                                Online C Compiler.
                    Code, Compile, Run and Debug C program online.
    Write your code in this editor and press "Run" button to compile and execute it.
    
    *******************************************************************************/
    
    #include <stdio.h>
    
    float b[2] = {0.9980, -0.9980};
    float a[2] = {1.0000, -0.9961};
    float array1[8+1] = {0.000 ,0.000, 0.7071, 0.8660, 1.000, 0.8660, 0.7071, 0.000, -0.7071};
    float array2[8+1] = {0.000 ,-0.8660, -1, -0.8660, -0.7071, 0.0000, 0.7071, 0.8660, 1.000};
    float x[8];
    float y1[9] = { 0 };
    float y2[9] = { 0 };
    
    int main()
    {
        for(int i = 1; i <= 8; i++) {
            array1[i] = array1[i] + 2.0;
            //printf("%f\n", array1[i]);
        }
        
        for(int i = 1; i <= 8; i++) {
            array2[i] = array2[i] + 2.0;
            //printf("%f\n", array1[i]);
        }
    
    
        printf("IIR first order Butterworth, high-pass, cutoff 20Hz:\n");
        for(int n = 1; n <= 8; n++) {
            y1[n] = -1.000*a[1]*y1[n-1] + b[0]*array1[n] + b[1]*array1[n-1];
        }
        
        y2[0]     = y1[8];
        array2[0] = array1[8];
        
        for(int n = 1; n <= 8; n++) {
            y2[n] = -1.000*a[1]*y2[n-1] + b[0]*array2[n] + b[1]*array2[n-1];
        }
        
        printf("Array1 and Array2:\n");
        for(int i = 1; i <= 8; i++) {
            printf("%f, ", y1[i]);
        }
        
        for(int i = 1; i <= 8; i++) {
            printf("%f, ", y2[i]);
        }
        
    
        return 0;
    }

     I tested the code with a small float input and output array with 8 + 1 elements (the one (1) is for saving the previous output) and the coefficients are generated using Matlab/Octave:

    Fc = 20;        % cutoff.
    Fs = 32000;  % sampling rate.
    [b,a] = butter(1, Fc/(Fs/2), 'high');

    After confirming the output, I pasted and modified the code to fit into the microcontroller project. Using the mentioned book as a reference [Chapter 6: Frame Based DSP], the resulting code is this in the XC32 and Bruce Land's DSP routines for the 16.16 fixed point ones:

    outputData_fix16[0] = prev_output_value_fix16[0];
    inputData[0]        = prev_input_value[0];
    
    for (i = 1; i <= BUFFER_LENGTH/2; i++) {
    
        outputData_fix16[i] = multfix16(-1 * coeff_a_fp[1], outputData_fix16[i - 1]) + multfix16(coeff_b_fp[0], short2fix16(inputData[i])) + multfix16(coeff_b_fp[0], short2fix16(inputData[i]));
                
    }
    
    prev_output_value_fix16[0] = outputData_fix16[BUFFER_LENGTH/2];
    prev_input_value[0]        = inputData[BUFFER_LENGTH/2];
    

    Coefficients:

    // Fixed point coefficients for 20hz cutoff 1st order high pass Butterworth filter:
    // (in 16.16 format)
    fix16 coeff_b_fp[2] = { 65405, -65405 };
    fix16 coeff_a_fp[2] = { 65536, -65280 };
    
    // The same coefficients, but in floating point.    
    float coeff_b[2] = {0.9980, -0.9980};
    float coeff_a[2] = {1.0000, -0.9961};

    It works... but I gotta need to tune down the amplitude for whatever it is from the input or else the thing wraps around and make awful noises. If I have the saturate algorithm inside, it would sound less unpleasent, but I prefer to keep the audio amplitude in range.

    On top of that, I have added class functionality on that filter, but it refuses to work in MPLAB Harmony. Instead, I put aside that class thingy (might be useful for anything 32-bits Arduino-related) and work on the limitations on this platform first! 

  • Nasty clicking noise between change of notes, and poor volume output!

    NYH-workshop01/02/2018 at 03:07 0 comments

    I have been addressing this issue for a few months and noticed that something is really wrong with the output.

    First, what did work are the FM oscillators which are generating normally, as long as I don't put very high values on the FM modulator's amplitude.

    However, when the notes switches after playing, it clicked. Yeah, it sounded irritating. I have checked other references (especially that fancy Japanese musical clocks in Youtube and actual clock shops) and they don't click like mad! A search in Google revealed that I have to dampen the sound between note-switches to prevent very sharp change of values in the output. That - I'm inserting these after I port Bruce Land's envelope generator algorithm inside: Bruce Land's FM Sound Synthesis

    Another thing connecting the DAC to the little cheap mini compo set, the volume is decent. But to connect this to the LM386, or any of those PAM8403 and to the 4ohm speaker results in a tinny and unfulfilling volume! What just happened? I begin to suspect that there is a DC offset that prevents this from having a decent sound.

    Using the Analog Discovery 2 and getting the first 512 samples from the system, it is noticed that there is really a DC offset somewhere at the lowest frequency area: (FFT is calculated using Octave)

    Check the first value: 11.60945 + j0.0000. There could be a need to just get rid of it once and for all!

View all 4 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates