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.

Committer:
kkado
Date:
Mon Jul 03 08:35:31 2017 +0000
Revision:
1:db0c24aebb8a
Parent:
0:c5ca205c0a80
Corrected documentation placement.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kkado 0:c5ca205c0a80 1 #ifndef STMstation_synth_h
kkado 0:c5ca205c0a80 2 #define STMstation_synth_h
kkado 0:c5ca205c0a80 3
kkado 0:c5ca205c0a80 4 #include "mbed.h"
kkado 0:c5ca205c0a80 5 #include "arm_math.h"
kkado 0:c5ca205c0a80 6 #include "FastPWM.h"
kkado 0:c5ca205c0a80 7
kkado 1:db0c24aebb8a 8 #define AUDIO_PIN PB_0
kkado 1:db0c24aebb8a 9 #define CHANNELS 5
kkado 1:db0c24aebb8a 10
kkado 1:db0c24aebb8a 11 /** Stores data for a song or sound effect
kkado 1:db0c24aebb8a 12 * @param notes Note frequency and timbre
kkado 1:db0c24aebb8a 13 * @param durations Duration (from 1/64th to 4 counts)
kkado 1:db0c24aebb8a 14 * @param AR Attack/Release (bits 7:4 - attack, 3:0 - release)
kkado 1:db0c24aebb8a 15 * @param vol Volume (0 - muted, 127 - 1x volume, 255 - 2x volume
kkado 1:db0c24aebb8a 16 * @param max Max index of each channel
kkado 1:db0c24aebb8a 17 * @param ended Has the channel completed playing?
kkado 1:db0c24aebb8a 18 * @param repeat Should the channel repeat or stop?
kkado 1:db0c24aebb8a 19 * @param bpm Tempo, in 1/4th counts per minute
kkado 1:db0c24aebb8a 20 */
kkado 1:db0c24aebb8a 21 struct melody{
kkado 1:db0c24aebb8a 22 const uint8_t * notes[CHANNELS];
kkado 1:db0c24aebb8a 23 const uint8_t * durations[CHANNELS];
kkado 1:db0c24aebb8a 24 const uint8_t * AR[CHANNELS];
kkado 1:db0c24aebb8a 25 const uint8_t * vol[CHANNELS];
kkado 1:db0c24aebb8a 26 uint16_t max[CHANNELS];
kkado 1:db0c24aebb8a 27 bool ended[CHANNELS], repeat[CHANNELS];
kkado 1:db0c24aebb8a 28 uint8_t bpm;
kkado 1:db0c24aebb8a 29 };
kkado 1:db0c24aebb8a 30
kkado 1:db0c24aebb8a 31 /** Stores data that is currently being played. So many members! A lot of these are just coefficients for waveform synthesis.
kkado 1:db0c24aebb8a 32 * @param notes (LOADED FROM MELODY) Note frequency and timbre
kkado 1:db0c24aebb8a 33 * @param durations (LOADED FROM MELODY) Duration (from 1/64th to 4 counts)
kkado 1:db0c24aebb8a 34 * @param AR (LOADED FROM MELODY) Attack/Release (bits 7:4 - attack, 3:0 - release)
kkado 1:db0c24aebb8a 35 * @param vol (LOADED FROM MELODY) Volume (0 - muted, 127 - 1x volume, 255 - 2x volume
kkado 1:db0c24aebb8a 36 * @param index Note currently being played
kkado 1:db0c24aebb8a 37 * @param max (LOADED FROM MELODY) Max index of each channel
kkado 1:db0c24aebb8a 38 * @param env (FOR SYNTHEESIS) Envelope value (between 0~1)
kkado 1:db0c24aebb8a 39 * @param sineCoef (FOR SYNTHEESIS) Coefficient for sine wave synthesis
kkado 1:db0c24aebb8a 40 * @param vSum (FOR SYNTHEESIS) Divider to prevent clipping
kkado 1:db0c24aebb8a 41 * @param atkSlope (FOR SYNTHEESIS) Attack slope for envelope
kkado 1:db0c24aebb8a 42 * @param relSlope (FOR SYNTHEESIS) Release slope for envelope
kkado 1:db0c24aebb8a 43 * @param relOffset (FOR SYNTHEESIS) Release offset for envelope
kkado 1:db0c24aebb8a 44 * @param volCoef (FOR SYNTHEESIS) Volume coefficient to control volume, prevent clipping
kkado 1:db0c24aebb8a 45 * @param halfPeriod (FOR SYNTHEESIS) Half period for square, triangle functions
kkado 1:db0c24aebb8a 46 * @param triSlope (FOR SYNTHEESIS) Triangle wave slope
kkado 1:db0c24aebb8a 47 * @param noiseVal (FOR SYNTHEESIS) Value returned from random noise generation
kkado 1:db0c24aebb8a 48 * @param triVal (FOR SYNTHEESIS) Value returned from triangle wave calculation
kkado 1:db0c24aebb8a 49 * @param counter Current sample value
kkado 1:db0c24aebb8a 50 * @param envAtkEnd (FOR SYNTHEESIS) Counter value at which attack ramp ends
kkado 1:db0c24aebb8a 51 * @param envRelStart (FOR SYNTHEESIS) Counter value at which release ramp starts
kkado 1:db0c24aebb8a 52 * @param val 8-bit sample value
kkado 1:db0c24aebb8a 53 * @timbre (FOR SYNTHEESIS) What kind of waveform is playing? 0 - sine, 1 - sq, 2 - tri, 3 - noise
kkado 1:db0c24aebb8a 54 * @freqIndex (FOR SYNTHEESIS) What frequency is playing? 0 - rest, 1~freqLength frequencies in ascending order
kkado 1:db0c24aebb8a 55 * @param bpm (LOADED FROM MELODY) Tempo, in 1/4th counts per minute
kkado 1:db0c24aebb8a 56 * @param repeat (LOADED FROM MELODY) Channel repeat
kkado 1:db0c24aebb8a 57 * @param endptr (LOADED FROM MELODY) Points to melody end value
kkado 1:db0c24aebb8a 58 */
kkado 1:db0c24aebb8a 59 struct master{
kkado 1:db0c24aebb8a 60 const uint8_t * notes[CHANNELS];
kkado 1:db0c24aebb8a 61 const uint8_t * durations[CHANNELS];
kkado 1:db0c24aebb8a 62 const uint8_t * AR[CHANNELS];
kkado 1:db0c24aebb8a 63 const uint8_t * vol[CHANNELS];
kkado 1:db0c24aebb8a 64 uint16_t index[CHANNELS], max[CHANNELS];
kkado 1:db0c24aebb8a 65 float env[CHANNELS], sineCoef[CHANNELS], vSum, atkSlope[CHANNELS], relSlope[CHANNELS], relOffset[CHANNELS], volCoef[CHANNELS], halfPeriod[CHANNELS], triSlope[CHANNELS], noiseVal[CHANNELS], triVal[CHANNELS];
kkado 1:db0c24aebb8a 66 uint32_t counter[CHANNELS], envAtkEnd[CHANNELS], envRelStart[CHANNELS];
kkado 1:db0c24aebb8a 67 uint8_t val, timbre[CHANNELS], freqIndex[CHANNELS], bpm[CHANNELS];
kkado 1:db0c24aebb8a 68 bool repeat[CHANNELS];
kkado 1:db0c24aebb8a 69 bool* endptr[CHANNELS];
kkado 1:db0c24aebb8a 70 };
kkado 1:db0c24aebb8a 71
kkado 0:c5ca205c0a80 72 /** Basic synthesizer library for STM32F401RE Nucleo or STMstation P.1 development boards - may work with
kkado 0:c5ca205c0a80 73 * other targets, but not tested yet.
kkado 0:c5ca205c0a80 74 *
kkado 0:c5ca205c0a80 75 * This is a multi-channel synthesizer, with sine, square, triangle, and noise waveform generation. Music is
kkado 0:c5ca205c0a80 76 * stored as arrays in flash, but it is possible to use editable arrays in RAM. Standard number of channels is
kkado 0:c5ca205c0a80 77 * 5, but additional channels can be added by changing the CHANNELS define.
kkado 0:c5ca205c0a80 78 *
kkado 0:c5ca205c0a80 79 * Audio signal is output as a PWM signal on PB_0, which MUST be fed into a 1-bit DAC to generate sound!
kkado 0:c5ca205c0a80 80 *
kkado 0:c5ca205c0a80 81 * Suggested setup:
kkado 0:c5ca205c0a80 82 * @code
kkado 0:c5ca205c0a80 83 * (PB_0)-----(R1)-----(C2)
kkado 0:c5ca205c0a80 84 * | |
kkado 0:c5ca205c0a80 85 * (C1) (SPEAKER)
kkado 0:c5ca205c0a80 86 * | |
kkado 0:c5ca205c0a80 87 * (GND) (GND)
kkado 0:c5ca205c0a80 88 *
kkado 0:c5ca205c0a80 89 * R1 = 150 Ohms, C1 = 47nF, C2 = 1uF, Speaker = 0.25W, 8 Ohms
kkado 0:c5ca205c0a80 90 * @endcode
kkado 0:c5ca205c0a80 91 */
kkado 0:c5ca205c0a80 92 class STMstation_synth{
kkado 0:c5ca205c0a80 93 public:
kkado 1:db0c24aebb8a 94 /** Create an instance of STMstation_synth
kkado 1:db0c24aebb8a 95 * @param audio_pin PWM output
kkado 1:db0c24aebb8a 96 */
kkado 1:db0c24aebb8a 97 STMstation_synth(PinName audio_pin);
kkado 1:db0c24aebb8a 98 /** Create an instance of STMstation_synth
kkado 1:db0c24aebb8a 99 * Use default output for STMstation P.1, PB_0
kkado 1:db0c24aebb8a 100 */
kkado 0:c5ca205c0a80 101 STMstation_synth();
kkado 1:db0c24aebb8a 102 /** Play a track
kkado 1:db0c24aebb8a 103 * @param newMelody Melody to play
kkado 1:db0c24aebb8a 104 * @param refChannel If starting from nonzero index, specify reference channel
kkado 1:db0c24aebb8a 105 * @param newIndex Start index
kkado 1:db0c24aebb8a 106 */
kkado 1:db0c24aebb8a 107 void play(melody &newMelody, uint8_t refChannel = 0, uint16_t newIndex = 0);
kkado 1:db0c24aebb8a 108 /** Clear all data from a selected channel
kkado 1:db0c24aebb8a 109 * @param channel Channel to clear
kkado 1:db0c24aebb8a 110 */
kkado 1:db0c24aebb8a 111 void clear_channel(uint8_t _channel);
kkado 1:db0c24aebb8a 112 /** Stop playing a track
kkado 1:db0c24aebb8a 113 * @param newMelody Melody to stop
kkado 1:db0c24aebb8a 114 */
kkado 1:db0c24aebb8a 115 void stop_track(melody &newMelody);
kkado 1:db0c24aebb8a 116 /** Check if a track is still playing
kkado 1:db0c24aebb8a 117 * @param newMelody Melody to check
kkado 1:db0c24aebb8a 118 */
kkado 1:db0c24aebb8a 119 bool check_track(melody &newMelody);
kkado 1:db0c24aebb8a 120
kkado 1:db0c24aebb8a 121 struct master Master;
kkado 1:db0c24aebb8a 122 private:
kkado 1:db0c24aebb8a 123 void begin();
kkado 0:c5ca205c0a80 124 void calc_coefs(int i);
kkado 1:db0c24aebb8a 125 void calc_vSum();
kkado 0:c5ca205c0a80 126 void calc_env();
kkado 0:c5ca205c0a80 127 int8_t square(float _halfperiod, uint16_t _counter);
kkado 0:c5ca205c0a80 128 void calc_triangle(uint8_t _channel, float _halfperiod, float _trislope, uint32_t _counter);
kkado 0:c5ca205c0a80 129 void calc_noise(uint8_t _channel, uint8_t _freq, uint32_t _counter);
kkado 1:db0c24aebb8a 130 void calc_val();
kkado 0:c5ca205c0a80 131 void check_end();
kkado 0:c5ca205c0a80 132 void check_start();
kkado 1:db0c24aebb8a 133 void note();
kkado 0:c5ca205c0a80 134
kkado 0:c5ca205c0a80 135 Ticker sample;
kkado 0:c5ca205c0a80 136 FastPWM tone;
kkado 0:c5ca205c0a80 137 };
kkado 0:c5ca205c0a80 138
kkado 0:c5ca205c0a80 139 #endif