kopia lustrzana https://github.com/pimoroni/pimoroni-pico
				
				
				
			
		
			
				
	
	
		
			133 wiersze
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			133 wiersze
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
| #pragma once
 | |
| 
 | |
| #include <cstdint>
 | |
| 
 | |
| namespace synth {
 | |
| 
 | |
|   // The duration a note is played is determined by the amount of attack,
 | |
|   // decay, and release, combined with the length of the note as defined by
 | |
|   // the user.
 | |
|   //
 | |
|   // - Attack:  number of milliseconds it takes for a note to hit full volume
 | |
|   // - Decay:   number of milliseconds it takes for a note to settle to sustain volume
 | |
|   // - Sustain: percentage of full volume that the note sustains at (duration implied by other factors)
 | |
|   // - Release: number of milliseconds it takes for a note to reduce to zero volume after it has ended
 | |
|   //
 | |
|   // Attack (750ms) - Decay (500ms) -------- Sustain ----- Release (250ms)
 | |
|   //
 | |
|   //                +         +                                  +    +
 | |
|   //                |         |                                  |    |
 | |
|   //                |         |                                  |    |
 | |
|   //                |         |                                  |    |
 | |
|   //                v         v                                  v    v
 | |
|   // 0ms               1000ms              2000ms              3000ms              4000ms
 | |
|   //
 | |
|   // |              XXXX |                   |                   |                   |
 | |
|   // |             X    X|XX                 |                   |                   |
 | |
|   // |            X      |  XXX              |                   |                   |
 | |
|   // |           X       |     XXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|                   |
 | |
|   // |          X        |                   |                   |X                  |
 | |
|   // |         X         |                   |                   |X                  |
 | |
|   // |        X          |                   |                   | X                 |
 | |
|   // |       X           |                   |                   | X                 |
 | |
|   // |      X            |                   |                   |  X                |
 | |
|   // |     X             |                   |                   |  X                |
 | |
|   // |    X              |                   |                   |   X               |
 | |
|   // |   X               |                   |                   |   X               |
 | |
|   // |  X +    +    +    |    +    +    +    |    +    +    +    |    +    +    +    |    +
 | |
|   // | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
 | |
|   // |X   |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
 | |
|   // +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--->
 | |
| 
 | |
|   #define CHANNEL_COUNT 8
 | |
| 
 | |
|   constexpr float pi = 3.14159265358979323846f;
 | |
| 
 | |
|   const uint32_t sample_rate = 44100;
 | |
|   extern uint16_t volume;
 | |
| 
 | |
|   enum Waveform {
 | |
|     NOISE     = 128,
 | |
|     SQUARE    = 64,
 | |
|     SAW       = 32,
 | |
|     TRIANGLE  = 16,
 | |
|     SINE      = 8,
 | |
|     WAVE      = 1
 | |
|   };
 | |
| 
 | |
|   enum class ADSRPhase : uint8_t {
 | |
|     ATTACK,
 | |
|     DECAY,
 | |
|     SUSTAIN,
 | |
|     RELEASE,
 | |
|     OFF
 | |
|   };
 | |
| 
 | |
|   struct AudioChannel {
 | |
|     uint8_t   waveforms     = 0;      // bitmask for enabled waveforms (see AudioWaveform enum for values)
 | |
|     uint16_t  frequency     = 660;    // frequency of the voice (Hz)
 | |
|     uint16_t  volume        = 0xffff; // channel volume (default 50%)
 | |
| 
 | |
|     uint16_t  attack_ms     = 2;      // attack period
 | |
|     uint16_t  decay_ms      = 6;      // decay period
 | |
|     uint16_t  sustain       = 0xffff; // sustain volume
 | |
|     uint16_t  release_ms    = 1;      // release period
 | |
|     uint16_t  pulse_width   = 0x7fff; // duty cycle of square wave (default 50%)
 | |
|     int16_t   noise         = 0;      // current noise value
 | |
| 
 | |
|     uint32_t  waveform_offset  = 0;   // voice offset (Q8)
 | |
| 
 | |
|     int32_t   filter_last_sample = 0;
 | |
|     bool      filter_enable = false;
 | |
|     uint16_t  filter_cutoff_frequency = 0;
 | |
| 
 | |
|     uint32_t  adsr_frame    = 0;      // number of frames into the current ADSR phase
 | |
|     uint32_t  adsr_end_frame = 0;     // frame target at which the ADSR changes to the next phase
 | |
|     uint32_t  adsr          = 0;
 | |
|     int32_t   adsr_step     = 0;
 | |
|     ADSRPhase adsr_phase    = ADSRPhase::OFF;
 | |
| 
 | |
|     uint8_t   wave_buf_pos  = 0;      //
 | |
|     int16_t   wave_buffer[64];        // buffer for arbitrary waveforms. small as it's filled by user callback
 | |
| 
 | |
|     void *user_data = nullptr;
 | |
|     void (*wave_buffer_callback)(AudioChannel &channel);
 | |
| 
 | |
|     void trigger_attack()  {
 | |
|       adsr_frame = 0;
 | |
|       adsr_phase = ADSRPhase::ATTACK;
 | |
|       adsr_end_frame = (attack_ms * sample_rate) / 1000;
 | |
|       adsr_step = (int32_t(0xffffff) - int32_t(adsr)) / int32_t(adsr_end_frame);
 | |
|     }
 | |
|     void trigger_decay() {
 | |
|       adsr_frame = 0;
 | |
|       adsr_phase = ADSRPhase::DECAY;
 | |
|       adsr_end_frame = (decay_ms * sample_rate) / 1000;
 | |
|       adsr_step = (int32_t(sustain << 8) - int32_t(adsr)) / int32_t(adsr_end_frame);
 | |
|     }
 | |
|     void trigger_sustain() {
 | |
|       adsr_frame = 0;
 | |
|       adsr_phase = ADSRPhase::SUSTAIN;
 | |
|       adsr_end_frame = 0;
 | |
|       adsr_step = 0;
 | |
|     }
 | |
|     void trigger_release() {
 | |
|       adsr_frame = 0;
 | |
|       adsr_phase = ADSRPhase::RELEASE;
 | |
|       adsr_end_frame = (release_ms * sample_rate) / 1000;
 | |
|       adsr_step = (int32_t(0) - int32_t(adsr)) / int32_t(adsr_end_frame);
 | |
|     }
 | |
|     void off() {
 | |
|       adsr_frame = 0;
 | |
|       adsr_phase = ADSRPhase::OFF;
 | |
|       adsr_step = 0;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   extern AudioChannel channels[CHANNEL_COUNT];
 | |
| 
 | |
|   int16_t get_audio_frame();
 | |
|   bool is_audio_playing();
 | |
| 
 | |
| }
 |