[136] | 1 | // Squawk Soft-Synthesizer Library for Arduino
|
---|
| 2 | //
|
---|
| 3 | // Davey Taylor 2013
|
---|
| 4 | // d.taylor@arduino.cc
|
---|
| 5 |
|
---|
| 6 | #ifndef _SQUAWK_H_
|
---|
| 7 | #define _SQUAWK_H_
|
---|
| 8 | #include <stddef.h>
|
---|
| 9 | #include <inttypes.h>
|
---|
| 10 | #include "Arduino.h"
|
---|
| 11 |
|
---|
| 12 | #define Melody const uint8_t PROGMEM
|
---|
| 13 |
|
---|
| 14 | class SquawkStream {
|
---|
| 15 | public:
|
---|
| 16 | virtual ~SquawkStream() = 0;
|
---|
| 17 | virtual uint8_t read() = 0;
|
---|
| 18 | virtual void seek(size_t offset) = 0;
|
---|
| 19 | };
|
---|
| 20 | inline SquawkStream::~SquawkStream() { }
|
---|
| 21 |
|
---|
| 22 | class SquawkSynth {
|
---|
| 23 |
|
---|
| 24 | protected:
|
---|
| 25 | // Load and play specified melody
|
---|
| 26 | void play(SquawkStream *melody);
|
---|
| 27 |
|
---|
| 28 | public:
|
---|
| 29 | SquawkSynth() {};
|
---|
| 30 |
|
---|
| 31 | // Initialize Squawk to generate samples at sample_rate Hz
|
---|
| 32 | void begin(uint16_t sample_rate);
|
---|
| 33 |
|
---|
| 34 | // Load and play specified melody
|
---|
| 35 | // melody needs to point to PROGMEM data
|
---|
| 36 | void play(const uint8_t *melody);
|
---|
| 37 |
|
---|
| 38 | // Resume currently loaded melody (or enable direct osc manipulation by sketch)
|
---|
| 39 | void play();
|
---|
| 40 |
|
---|
| 41 | // Pause playback
|
---|
| 42 | void pause();
|
---|
| 43 |
|
---|
| 44 | // Stop playback (unloads song)
|
---|
| 45 | void stop();
|
---|
| 46 |
|
---|
| 47 | // Tune Squawk to a different frequency - default is 1.0
|
---|
| 48 | void tune(float tuning);
|
---|
| 49 |
|
---|
| 50 | // Change the tempo - default is 50
|
---|
| 51 | void tempo(uint16_t tempo);
|
---|
| 52 | };
|
---|
| 53 |
|
---|
| 54 | extern SquawkSynth Squawk;
|
---|
| 55 |
|
---|
| 56 | // oscillator structure
|
---|
| 57 | typedef struct {
|
---|
| 58 | uint8_t vol;
|
---|
| 59 | uint16_t freq;
|
---|
| 60 | uint16_t phase;
|
---|
| 61 | } osc_t;
|
---|
| 62 |
|
---|
| 63 | typedef osc_t Oscillator;
|
---|
| 64 |
|
---|
| 65 | // oscillator memory
|
---|
| 66 | extern osc_t osc[4];
|
---|
| 67 | extern uint8_t pcm;
|
---|
| 68 | // channel 0 is pulse wave @ 25% duty
|
---|
| 69 | // channel 1 is square wave
|
---|
| 70 | // channel 2 is triangle wave
|
---|
| 71 | // channel 3 is noise
|
---|
| 72 |
|
---|
| 73 | // For channel 3, freq is used as part of its LFSR and should not be changed.
|
---|
| 74 | // LFSR: Linear feedback shift register, a method of producing a
|
---|
| 75 | // pseudo-random bit sequence, used to generate nasty noise.
|
---|
| 76 |
|
---|
| 77 | #ifdef __AVR_ATmega32U4__
|
---|
| 78 | // Supported configurations for ATmega32U4
|
---|
| 79 | #define SQUAWK_PWM_PIN5 OCR3AL
|
---|
| 80 | #define SQUAWK_PWM_PIN11 OCR0A
|
---|
| 81 | #define SQUAWK_PWM_PIN3 OCR0B
|
---|
| 82 | /*
|
---|
| 83 | // NOT SUPPORTED YET
|
---|
| 84 | #define SQUAWK_PWM_PIN6 OCR4D
|
---|
| 85 | #define SQUAWK_PWM_PIN9 OCR4B
|
---|
| 86 | #define SQUAWK_PWM_PIN10 OCR4B
|
---|
| 87 | */
|
---|
| 88 | #endif
|
---|
| 89 |
|
---|
| 90 | #ifdef __AVR_ATmega168__
|
---|
| 91 | // Supported configurations for ATmega168
|
---|
| 92 | #define SQUAWK_PWM_PIN6 OCR0A
|
---|
| 93 | #define SQUAWK_PWM_PIN5 OCR0B
|
---|
| 94 | #define SQUAWK_PWM_PIN11 OCR2A
|
---|
| 95 | #define SQUAWK_PWM_PIN3 OCR2B
|
---|
| 96 | #endif
|
---|
| 97 |
|
---|
| 98 | #ifdef __AVR_ATmega328P__
|
---|
| 99 | // Supported configurations for ATmega328P
|
---|
| 100 | #define SQUAWK_PWM_PIN6 OCR0A
|
---|
| 101 | #define SQUAWK_PWM_PIN5 OCR0B
|
---|
| 102 | #define SQUAWK_PWM_PIN11 OCR2A
|
---|
| 103 | #define SQUAWK_PWM_PIN3 OCR2B
|
---|
| 104 | #endif
|
---|
| 105 |
|
---|
| 106 | /*
|
---|
| 107 | // NOT SUPPORTED YET
|
---|
| 108 | #define SQUAWK_SPI SPDR
|
---|
| 109 | #define SQUAWK_RLD_PORTB PORTB
|
---|
| 110 | #define SQUAWK_RLD_PORTC PORTC
|
---|
| 111 | */
|
---|
| 112 |
|
---|
| 113 | extern void squawk_playroutine() asm("squawk_playroutine");
|
---|
| 114 |
|
---|
| 115 | // SAMPLE GRINDER
|
---|
| 116 | // generates samples and updates oscillators
|
---|
| 117 | // uses 132 cycles (not counting playroutine)
|
---|
| 118 | // ~1/3 CPU @ 44kHz on 16MHz
|
---|
| 119 | #define SQUAWK_CONSTRUCT_ISR(TARGET_REGISTER) \
|
---|
| 120 | uint16_t cia, cia_count; \
|
---|
| 121 | intptr_t squawk_register = (intptr_t)&TARGET_REGISTER; \
|
---|
| 122 | ISR(TIMER1_COMPA_vect, ISR_NAKED) { \
|
---|
| 123 | asm volatile( \
|
---|
| 124 | "push r2 " "\n\t" \
|
---|
| 125 | "in r2, __SREG__ " "\n\t" \
|
---|
| 126 | "push r18 " "\n\t" \
|
---|
| 127 | "push r27 " "\n\t" \
|
---|
| 128 | "push r26 " "\n\t" \
|
---|
| 129 | "push r0 " "\n\t" \
|
---|
| 130 | "push r1 " "\n\t" \
|
---|
| 131 | \
|
---|
| 132 | "lds r18, osc+2*%[mul]+%[fre] " "\n\t" \
|
---|
| 133 | "lds r0, osc+2*%[mul]+%[pha] " "\n\t" \
|
---|
| 134 | "add r0, r18 " "\n\t" \
|
---|
| 135 | "sts osc+2*%[mul]+%[pha], r0 " "\n\t" \
|
---|
| 136 | "lds r18, osc+2*%[mul]+%[fre]+1" "\n\t" \
|
---|
| 137 | "lds r1, osc+2*%[mul]+%[pha]+1" "\n\t" \
|
---|
| 138 | "adc r1, r18 " "\n\t" \
|
---|
| 139 | "sts osc+2*%[mul]+%[pha]+1, r1 " "\n\t" \
|
---|
| 140 | \
|
---|
| 141 | "mov r27, r1 " "\n\t" \
|
---|
| 142 | "sbrc r27, 7 " "\n\t" \
|
---|
| 143 | "com r27 " "\n\t" \
|
---|
| 144 | "lsl r27 " "\n\t" \
|
---|
| 145 | "lds r26, osc+2*%[mul]+%[vol] " "\n\t" \
|
---|
| 146 | "subi r27, 128 " "\n\t" \
|
---|
| 147 | "muls r27, r26 " "\n\t" \
|
---|
| 148 | "lsl r1 " "\n\t" \
|
---|
| 149 | "mov r26, r1 " "\n\t" \
|
---|
| 150 | \
|
---|
| 151 | "lds r18, osc+0*%[mul]+%[fre] " "\n\t" \
|
---|
| 152 | "lds r0, osc+0*%[mul]+%[pha] " "\n\t" \
|
---|
| 153 | "add r0, r18 " "\n\t" \
|
---|
| 154 | "sts osc+0*%[mul]+%[pha], r0 " "\n\t" \
|
---|
| 155 | "lds r18, osc+0*%[mul]+%[fre]+1" "\n\t" \
|
---|
| 156 | "lds r1, osc+0*%[mul]+%[pha]+1" "\n\t" \
|
---|
| 157 | "adc r1, r18 " "\n\t" \
|
---|
| 158 | "sts osc+0*%[mul]+%[pha]+1, r1 " "\n\t" \
|
---|
| 159 | \
|
---|
| 160 | "mov r18, r1 " "\n\t" \
|
---|
| 161 | "lsl r18 " "\n\t" \
|
---|
| 162 | "and r18, r1 " "\n\t" \
|
---|
| 163 | "lds r27, osc+0*%[mul]+%[vol] " "\n\t" \
|
---|
| 164 | "sbrc r18, 7 " "\n\t" \
|
---|
| 165 | "neg r27 " "\n\t" \
|
---|
| 166 | "add r26, r27 " "\n\t" \
|
---|
| 167 | \
|
---|
| 168 | "lds r18, osc+1*%[mul]+%[fre] " "\n\t" \
|
---|
| 169 | "lds r0, osc+1*%[mul]+%[pha] " "\n\t" \
|
---|
| 170 | "add r0, r18 " "\n\t" \
|
---|
| 171 | "sts osc+1*%[mul]+%[pha], r0 " "\n\t" \
|
---|
| 172 | "lds r18, osc+1*%[mul]+%[fre]+1" "\n\t" \
|
---|
| 173 | "lds r1, osc+1*%[mul]+%[pha]+1" "\n\t" \
|
---|
| 174 | "adc r1, r18 " "\n\t" \
|
---|
| 175 | "sts osc+1*%[mul]+%[pha]+1, r1 " "\n\t" \
|
---|
| 176 | \
|
---|
| 177 | "lds r27, osc+1*%[mul]+%[vol] " "\n\t" \
|
---|
| 178 | "sbrc r1, 7 " "\n\t" \
|
---|
| 179 | "neg r27 " "\n\t" \
|
---|
| 180 | "add r26, r27 " "\n\t" \
|
---|
| 181 | \
|
---|
| 182 | "ldi r27, 1 " "\n\t" \
|
---|
| 183 | "lds r0, osc+3*%[mul]+%[fre] " "\n\t" \
|
---|
| 184 | "lds r1, osc+3*%[mul]+%[fre]+1" "\n\t" \
|
---|
| 185 | "add r0, r0 " "\n\t" \
|
---|
| 186 | "adc r1, r1 " "\n\t" \
|
---|
| 187 | "sbrc r1, 7 " "\n\t" \
|
---|
| 188 | "eor r0, r27 " "\n\t" \
|
---|
| 189 | "sbrc r1, 6 " "\n\t" \
|
---|
| 190 | "eor r0, r27 " "\n\t" \
|
---|
| 191 | "sts osc+3*%[mul]+%[fre], r0 " "\n\t" \
|
---|
| 192 | "sts osc+3*%[mul]+%[fre]+1, r1 " "\n\t" \
|
---|
| 193 | \
|
---|
| 194 | "lds r27, osc+3*%[mul]+%[vol] " "\n\t" \
|
---|
| 195 | "sbrc r1, 7 " "\n\t" \
|
---|
| 196 | "neg r27 " "\n\t" \
|
---|
| 197 | "add r26, r27 " "\n\t" \
|
---|
| 198 | \
|
---|
| 199 | "lds r27, pcm " "\n\t" \
|
---|
| 200 | "add r26, r27 " "\n\t" \
|
---|
| 201 | "sts %[reg], r26 " "\n\t" \
|
---|
| 202 | \
|
---|
| 203 | "lds r27, cia_count+1 " "\n\t" \
|
---|
| 204 | "lds r26, cia_count " "\n\t" \
|
---|
| 205 | "sbiw r26, 1 " "\n\t" \
|
---|
| 206 | "breq call_playroutine " "\n\t" \
|
---|
| 207 | "sts cia_count+1, r27 " "\n\t" \
|
---|
| 208 | "sts cia_count, r26 " "\n\t" \
|
---|
| 209 | "pop r1 " "\n\t" \
|
---|
| 210 | "pop r0 " "\n\t" \
|
---|
| 211 | "pop r26 " "\n\t" \
|
---|
| 212 | "pop r27 " "\n\t" \
|
---|
| 213 | "pop r18 " "\n\t" \
|
---|
| 214 | "out __SREG__, r2 " "\n\t" \
|
---|
| 215 | "pop r2 " "\n\t" \
|
---|
| 216 | "reti " "\n\t" \
|
---|
| 217 | "call_playroutine: " "\n\t" \
|
---|
| 218 | \
|
---|
| 219 | "lds r27, cia+1 " "\n\t" \
|
---|
| 220 | "lds r26, cia " "\n\t" \
|
---|
| 221 | "sts cia_count+1, r27 " "\n\t" \
|
---|
| 222 | "sts cia_count, r26 " "\n\t" \
|
---|
| 223 | \
|
---|
| 224 | "sei " "\n\t" \
|
---|
| 225 | "push r19 " "\n\t" \
|
---|
| 226 | "push r20 " "\n\t" \
|
---|
| 227 | "push r21 " "\n\t" \
|
---|
| 228 | "push r22 " "\n\t" \
|
---|
| 229 | "push r23 " "\n\t" \
|
---|
| 230 | "push r24 " "\n\t" \
|
---|
| 231 | "push r25 " "\n\t" \
|
---|
| 232 | "push r30 " "\n\t" \
|
---|
| 233 | "push r31 " "\n\t" \
|
---|
| 234 | \
|
---|
| 235 | "clr r1 " "\n\t" \
|
---|
| 236 | "call squawk_playroutine " "\n\t" \
|
---|
| 237 | \
|
---|
| 238 | "pop r31 " "\n\t" \
|
---|
| 239 | "pop r30 " "\n\t" \
|
---|
| 240 | "pop r25 " "\n\t" \
|
---|
| 241 | "pop r24 " "\n\t" \
|
---|
| 242 | "pop r23 " "\n\t" \
|
---|
| 243 | "pop r22 " "\n\t" \
|
---|
| 244 | "pop r21 " "\n\t" \
|
---|
| 245 | "pop r20 " "\n\t" \
|
---|
| 246 | "pop r19 " "\n\t" \
|
---|
| 247 | \
|
---|
| 248 | "pop r1 " "\n\t" \
|
---|
| 249 | "pop r0 " "\n\t" \
|
---|
| 250 | "pop r26 " "\n\t" \
|
---|
| 251 | "pop r27 " "\n\t" \
|
---|
| 252 | "pop r18 " "\n\t" \
|
---|
| 253 | "out __SREG__, r2 " "\n\t" \
|
---|
| 254 | "pop r2 " "\n\t" \
|
---|
| 255 | "reti " "\n\t" \
|
---|
| 256 | : \
|
---|
| 257 | : [reg] "M" _SFR_MEM_ADDR(TARGET_REGISTER), \
|
---|
| 258 | [mul] "M" (sizeof(Oscillator)), \
|
---|
| 259 | [pha] "M" (offsetof(Oscillator, phase)), \
|
---|
| 260 | [fre] "M" (offsetof(Oscillator, freq)), \
|
---|
| 261 | [vol] "M" (offsetof(Oscillator, vol)) \
|
---|
| 262 | ); \
|
---|
| 263 | }
|
---|
| 264 |
|
---|
| 265 | #endif
|
---|