aboutsummaryrefslogtreecommitdiff
path: root/libraries/Robot_Control/Squawk.h
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/Robot_Control/Squawk.h')
-rw-r--r--libraries/Robot_Control/Squawk.h265
1 files changed, 265 insertions, 0 deletions
diff --git a/libraries/Robot_Control/Squawk.h b/libraries/Robot_Control/Squawk.h
new file mode 100644
index 0000000..3481acf
--- /dev/null
+++ b/libraries/Robot_Control/Squawk.h
@@ -0,0 +1,265 @@
+// Squawk Soft-Synthesizer Library for Arduino
+//
+// Davey Taylor 2013
+// d.taylor@arduino.cc
+
+#ifndef _SQUAWK_H_
+#define _SQUAWK_H_
+#include <stddef.h>
+#include <inttypes.h>
+#include "Arduino.h"
+
+#define Melody const uint8_t PROGMEM
+
+class SquawkStream {
+ public:
+ virtual ~SquawkStream() = 0;
+ virtual uint8_t read() = 0;
+ virtual void seek(size_t offset) = 0;
+};
+inline SquawkStream::~SquawkStream() { }
+
+class SquawkSynth {
+
+protected:
+ // Load and play specified melody
+ void play(SquawkStream *melody);
+
+public:
+ SquawkSynth() {};
+
+ // Initialize Squawk to generate samples at sample_rate Hz
+ void begin(uint16_t sample_rate);
+
+ // Load and play specified melody
+ // melody needs to point to PROGMEM data
+ void play(const uint8_t *melody);
+
+ // Resume currently loaded melody (or enable direct osc manipulation by sketch)
+ void play();
+
+ // Pause playback
+ void pause();
+
+ // Stop playback (unloads song)
+ void stop();
+
+ // Tune Squawk to a different frequency - default is 1.0
+ void tune(float tuning);
+
+ // Change the tempo - default is 50
+ void tempo(uint16_t tempo);
+};
+
+extern SquawkSynth Squawk;
+
+// oscillator structure
+typedef struct {
+ uint8_t vol;
+ uint16_t freq;
+ uint16_t phase;
+} osc_t;
+
+typedef osc_t Oscillator;
+
+// oscillator memory
+extern osc_t osc[4];
+extern uint8_t pcm;
+// channel 0 is pulse wave @ 25% duty
+// channel 1 is square wave
+// channel 2 is triangle wave
+// channel 3 is noise
+
+// For channel 3, freq is used as part of its LFSR and should not be changed.
+// LFSR: Linear feedback shift register, a method of producing a
+// pseudo-random bit sequence, used to generate nasty noise.
+
+#ifdef __AVR_ATmega32U4__
+// Supported configurations for ATmega32U4
+#define SQUAWK_PWM_PIN5 OCR3AL
+#define SQUAWK_PWM_PIN11 OCR0A
+#define SQUAWK_PWM_PIN3 OCR0B
+/*
+// NOT SUPPORTED YET
+#define SQUAWK_PWM_PIN6 OCR4D
+#define SQUAWK_PWM_PIN9 OCR4B
+#define SQUAWK_PWM_PIN10 OCR4B
+*/
+#endif
+
+#ifdef __AVR_ATmega168__
+// Supported configurations for ATmega168
+#define SQUAWK_PWM_PIN6 OCR0A
+#define SQUAWK_PWM_PIN5 OCR0B
+#define SQUAWK_PWM_PIN11 OCR2A
+#define SQUAWK_PWM_PIN3 OCR2B
+#endif
+
+#ifdef __AVR_ATmega328P__
+// Supported configurations for ATmega328P
+#define SQUAWK_PWM_PIN6 OCR0A
+#define SQUAWK_PWM_PIN5 OCR0B
+#define SQUAWK_PWM_PIN11 OCR2A
+#define SQUAWK_PWM_PIN3 OCR2B
+#endif
+
+/*
+// NOT SUPPORTED YET
+#define SQUAWK_SPI SPDR
+#define SQUAWK_RLD_PORTB PORTB
+#define SQUAWK_RLD_PORTC PORTC
+*/
+
+extern void squawk_playroutine() asm("squawk_playroutine");
+
+// SAMPLE GRINDER
+// generates samples and updates oscillators
+// uses 132 cycles (not counting playroutine)
+// ~1/3 CPU @ 44kHz on 16MHz
+#define SQUAWK_CONSTRUCT_ISR(TARGET_REGISTER) \
+uint16_t cia, cia_count; \
+intptr_t squawk_register = (intptr_t)&TARGET_REGISTER; \
+ISR(TIMER1_COMPA_vect, ISR_NAKED) { \
+ asm volatile( \
+ "push r2 " "\n\t" \
+ "in r2, __SREG__ " "\n\t" \
+ "push r18 " "\n\t" \
+ "push r27 " "\n\t" \
+ "push r26 " "\n\t" \
+ "push r0 " "\n\t" \
+ "push r1 " "\n\t" \
+\
+ "lds r18, osc+2*%[mul]+%[fre] " "\n\t" \
+ "lds r0, osc+2*%[mul]+%[pha] " "\n\t" \
+ "add r0, r18 " "\n\t" \
+ "sts osc+2*%[mul]+%[pha], r0 " "\n\t" \
+ "lds r18, osc+2*%[mul]+%[fre]+1" "\n\t" \
+ "lds r1, osc+2*%[mul]+%[pha]+1" "\n\t" \
+ "adc r1, r18 " "\n\t" \
+ "sts osc+2*%[mul]+%[pha]+1, r1 " "\n\t" \
+\
+ "mov r27, r1 " "\n\t" \
+ "sbrc r27, 7 " "\n\t" \
+ "com r27 " "\n\t" \
+ "lsl r27 " "\n\t" \
+ "lds r26, osc+2*%[mul]+%[vol] " "\n\t" \
+ "subi r27, 128 " "\n\t" \
+ "muls r27, r26 " "\n\t" \
+ "lsl r1 " "\n\t" \
+ "mov r26, r1 " "\n\t" \
+\
+ "lds r18, osc+0*%[mul]+%[fre] " "\n\t" \
+ "lds r0, osc+0*%[mul]+%[pha] " "\n\t" \
+ "add r0, r18 " "\n\t" \
+ "sts osc+0*%[mul]+%[pha], r0 " "\n\t" \
+ "lds r18, osc+0*%[mul]+%[fre]+1" "\n\t" \
+ "lds r1, osc+0*%[mul]+%[pha]+1" "\n\t" \
+ "adc r1, r18 " "\n\t" \
+ "sts osc+0*%[mul]+%[pha]+1, r1 " "\n\t" \
+\
+ "mov r18, r1 " "\n\t" \
+ "lsl r18 " "\n\t" \
+ "and r18, r1 " "\n\t" \
+ "lds r27, osc+0*%[mul]+%[vol] " "\n\t" \
+ "sbrc r18, 7 " "\n\t" \
+ "neg r27 " "\n\t" \
+ "add r26, r27 " "\n\t" \
+\
+ "lds r18, osc+1*%[mul]+%[fre] " "\n\t" \
+ "lds r0, osc+1*%[mul]+%[pha] " "\n\t" \
+ "add r0, r18 " "\n\t" \
+ "sts osc+1*%[mul]+%[pha], r0 " "\n\t" \
+ "lds r18, osc+1*%[mul]+%[fre]+1" "\n\t" \
+ "lds r1, osc+1*%[mul]+%[pha]+1" "\n\t" \
+ "adc r1, r18 " "\n\t" \
+ "sts osc+1*%[mul]+%[pha]+1, r1 " "\n\t" \
+\
+ "lds r27, osc+1*%[mul]+%[vol] " "\n\t" \
+ "sbrc r1, 7 " "\n\t" \
+ "neg r27 " "\n\t" \
+ "add r26, r27 " "\n\t" \
+\
+ "ldi r27, 1 " "\n\t" \
+ "lds r0, osc+3*%[mul]+%[fre] " "\n\t" \
+ "lds r1, osc+3*%[mul]+%[fre]+1" "\n\t" \
+ "add r0, r0 " "\n\t" \
+ "adc r1, r1 " "\n\t" \
+ "sbrc r1, 7 " "\n\t" \
+ "eor r0, r27 " "\n\t" \
+ "sbrc r1, 6 " "\n\t" \
+ "eor r0, r27 " "\n\t" \
+ "sts osc+3*%[mul]+%[fre], r0 " "\n\t" \
+ "sts osc+3*%[mul]+%[fre]+1, r1 " "\n\t" \
+\
+ "lds r27, osc+3*%[mul]+%[vol] " "\n\t" \
+ "sbrc r1, 7 " "\n\t" \
+ "neg r27 " "\n\t" \
+ "add r26, r27 " "\n\t" \
+\
+ "lds r27, pcm " "\n\t" \
+ "add r26, r27 " "\n\t" \
+ "sts %[reg], r26 " "\n\t" \
+\
+ "lds r27, cia_count+1 " "\n\t" \
+ "lds r26, cia_count " "\n\t" \
+ "sbiw r26, 1 " "\n\t" \
+ "breq call_playroutine " "\n\t" \
+ "sts cia_count+1, r27 " "\n\t" \
+ "sts cia_count, r26 " "\n\t" \
+ "pop r1 " "\n\t" \
+ "pop r0 " "\n\t" \
+ "pop r26 " "\n\t" \
+ "pop r27 " "\n\t" \
+ "pop r18 " "\n\t" \
+ "out __SREG__, r2 " "\n\t" \
+ "pop r2 " "\n\t" \
+ "reti " "\n\t" \
+ "call_playroutine: " "\n\t" \
+\
+ "lds r27, cia+1 " "\n\t" \
+ "lds r26, cia " "\n\t" \
+ "sts cia_count+1, r27 " "\n\t" \
+ "sts cia_count, r26 " "\n\t" \
+\
+ "sei " "\n\t" \
+ "push r19 " "\n\t" \
+ "push r20 " "\n\t" \
+ "push r21 " "\n\t" \
+ "push r22 " "\n\t" \
+ "push r23 " "\n\t" \
+ "push r24 " "\n\t" \
+ "push r25 " "\n\t" \
+ "push r30 " "\n\t" \
+ "push r31 " "\n\t" \
+\
+ "clr r1 " "\n\t" \
+ "call squawk_playroutine " "\n\t" \
+\
+ "pop r31 " "\n\t" \
+ "pop r30 " "\n\t" \
+ "pop r25 " "\n\t" \
+ "pop r24 " "\n\t" \
+ "pop r23 " "\n\t" \
+ "pop r22 " "\n\t" \
+ "pop r21 " "\n\t" \
+ "pop r20 " "\n\t" \
+ "pop r19 " "\n\t" \
+\
+ "pop r1 " "\n\t" \
+ "pop r0 " "\n\t" \
+ "pop r26 " "\n\t" \
+ "pop r27 " "\n\t" \
+ "pop r18 " "\n\t" \
+ "out __SREG__, r2 " "\n\t" \
+ "pop r2 " "\n\t" \
+ "reti " "\n\t" \
+ : \
+ : [reg] "M" _SFR_MEM_ADDR(TARGET_REGISTER), \
+ [mul] "M" (sizeof(Oscillator)), \
+ [pha] "M" (offsetof(Oscillator, phase)), \
+ [fre] "M" (offsetof(Oscillator, freq)), \
+ [vol] "M" (offsetof(Oscillator, vol)) \
+ ); \
+}
+
+#endif \ No newline at end of file