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
|
---|