Simple synthesizer for STM32F401RE/STMstation.

Dependencies:   FastPWM mbed-dsp

This is a basic synthesizer to play music on the STM32F401RE Nucleo/STMstation development board:

/media/uploads/kkado/imgp1229.jpg

Please see the API documentation for further details.

Here's a demo of the synthesizer at work in a music composing program on the STMstation. This one is "Miku" by Anamanaguchi.

STMstation_synth.h

Committer:
kkado
Date:
2017-07-03
Revision:
1:db0c24aebb8a
Parent:
0:c5ca205c0a80

File content as of revision 1:db0c24aebb8a:

#ifndef STMstation_synth_h
#define STMstation_synth_h

#include "mbed.h"
#include "arm_math.h"
#include "FastPWM.h"

#define AUDIO_PIN   PB_0
#define CHANNELS    5

/** Stores data for a song or sound effect
 *  @param notes     Note frequency and timbre
 *  @param durations Duration (from 1/64th to 4 counts)
 *  @param AR        Attack/Release (bits 7:4 - attack, 3:0 - release)
 *  @param vol       Volume (0 - muted, 127 - 1x volume, 255 - 2x volume
 *  @param max       Max index of each channel
 *  @param ended     Has the channel completed playing?
 *  @param repeat    Should the channel repeat or stop?
 *  @param bpm       Tempo, in 1/4th counts per minute
 */
struct melody{
    const uint8_t * notes[CHANNELS];
    const uint8_t * durations[CHANNELS]; 
    const uint8_t * AR[CHANNELS]; 
    const uint8_t * vol[CHANNELS];
    uint16_t max[CHANNELS];
    bool ended[CHANNELS], repeat[CHANNELS];
    uint8_t bpm;
};

/** Stores data that is currently being played. So many members! A lot of these are just coefficients for waveform synthesis.
 *  @param notes        (LOADED FROM MELODY) Note frequency and timbre
 *  @param durations    (LOADED FROM MELODY) Duration (from 1/64th to 4 counts)
 *  @param AR           (LOADED FROM MELODY) Attack/Release (bits 7:4 - attack, 3:0 - release)
 *  @param vol          (LOADED FROM MELODY) Volume (0 - muted, 127 - 1x volume, 255 - 2x volume
 *  @param index        Note currently being played
 *  @param max          (LOADED FROM MELODY) Max index of each channel
 *  @param env          (FOR SYNTHEESIS) Envelope value (between 0~1)
 *  @param sineCoef     (FOR SYNTHEESIS) Coefficient for sine wave synthesis
 *  @param vSum         (FOR SYNTHEESIS) Divider to prevent clipping
 *  @param atkSlope     (FOR SYNTHEESIS) Attack slope for envelope
 *  @param relSlope     (FOR SYNTHEESIS) Release slope for envelope
 *  @param relOffset    (FOR SYNTHEESIS) Release offset for envelope
 *  @param volCoef      (FOR SYNTHEESIS) Volume coefficient to control volume, prevent clipping
 *  @param halfPeriod   (FOR SYNTHEESIS) Half period for square, triangle functions
 *  @param triSlope     (FOR SYNTHEESIS) Triangle wave slope
 *  @param noiseVal     (FOR SYNTHEESIS) Value returned from random noise generation
 *  @param triVal       (FOR SYNTHEESIS) Value returned from triangle wave calculation
 *  @param counter      Current sample value
 *  @param envAtkEnd    (FOR SYNTHEESIS) Counter value at which attack ramp ends
 *  @param envRelStart  (FOR SYNTHEESIS) Counter value at which release ramp starts
 *  @param val          8-bit sample value
 *  @timbre             (FOR SYNTHEESIS) What kind of waveform is playing? 0 - sine, 1 - sq, 2 - tri, 3 - noise
 *  @freqIndex          (FOR SYNTHEESIS) What frequency is playing? 0 - rest, 1~freqLength frequencies in ascending order
 *  @param bpm          (LOADED FROM MELODY) Tempo, in 1/4th counts per minute
 *  @param repeat       (LOADED FROM MELODY) Channel repeat
 *  @param endptr       (LOADED FROM MELODY) Points to melody end value
 */
struct master{
    const uint8_t * notes[CHANNELS];
    const uint8_t * durations[CHANNELS]; 
    const uint8_t * AR[CHANNELS]; 
    const uint8_t * vol[CHANNELS];
    uint16_t index[CHANNELS], max[CHANNELS];
    float env[CHANNELS], sineCoef[CHANNELS], vSum, atkSlope[CHANNELS], relSlope[CHANNELS], relOffset[CHANNELS], volCoef[CHANNELS], halfPeriod[CHANNELS], triSlope[CHANNELS], noiseVal[CHANNELS], triVal[CHANNELS];
    uint32_t counter[CHANNELS], envAtkEnd[CHANNELS], envRelStart[CHANNELS];
    uint8_t val, timbre[CHANNELS], freqIndex[CHANNELS], bpm[CHANNELS];
    bool repeat[CHANNELS];
    bool* endptr[CHANNELS];
};

/** Basic synthesizer library for STM32F401RE Nucleo or STMstation P.1 development boards - may work with
 *  other targets, but not tested yet.
 *
 *  This is a multi-channel synthesizer, with sine, square, triangle, and noise waveform generation. Music is 
 *  stored as arrays in flash, but it is possible to use editable arrays in RAM. Standard number of channels is
 *  5, but additional channels can be added by changing the CHANNELS define.
 *  
 *  Audio signal is output as a PWM signal on PB_0, which MUST be fed into a 1-bit DAC to generate sound!
 *
 *  Suggested setup:
 *  @code
 *      (PB_0)-----(R1)-----(C2) 
 *                  |        |
 *                 (C1)  (SPEAKER)
 *                  |        |
 *                 (GND)    (GND)
 *      
 *      R1 = 150 Ohms, C1 = 47nF, C2 = 1uF, Speaker = 0.25W, 8 Ohms
 *  @endcode 
 */
class STMstation_synth{
    public:
        /** Create an instance of STMstation_synth 
         *  @param audio_pin PWM output
         */
        STMstation_synth(PinName audio_pin);
        /** Create an instance of STMstation_synth
         *  Use default output for STMstation P.1, PB_0 
         */
        STMstation_synth();
        /** Play a track
         *  @param newMelody    Melody to play
         *  @param refChannel   If starting from nonzero index, specify reference channel
         *  @param newIndex     Start index
         */
        void play(melody &newMelody, uint8_t refChannel = 0, uint16_t newIndex = 0);
        /** Clear all data from a selected channel
         *  @param channel      Channel to clear
         */
        void clear_channel(uint8_t _channel);
        /** Stop playing a track
         *  @param newMelody    Melody to stop
         */
        void stop_track(melody &newMelody);
        /** Check if a track is still playing
         *  @param newMelody    Melody to check
         */
        bool check_track(melody &newMelody);
                
        struct master Master;
    private:
        void begin();
        void calc_coefs(int i);
        void calc_vSum();
        void calc_env();
        int8_t square(float _halfperiod, uint16_t _counter);
        void calc_triangle(uint8_t _channel, float _halfperiod, float _trislope, uint32_t _counter);
        void calc_noise(uint8_t _channel, uint8_t _freq, uint32_t _counter);
        void calc_val();
        void check_end();
        void check_start();
        void note();
        
        Ticker sample;
        FastPWM tone;
};

#endif