diff --git a/examples/Tunes/Tunes.ino b/examples/Tunes/Tunes.ino index 0c572f8..c73c11c 100644 --- a/examples/Tunes/Tunes.ino +++ b/examples/Tunes/Tunes.ino @@ -1,4 +1,5 @@ #include "Arduboy.h" +#include const byte PROGMEM score [] = { // Sinfonia No.12 in A major BWV.798 J.S.Bach @@ -156,6 +157,7 @@ const byte PROGMEM score [] = { Arduboy arduboy; AbPrinter text(arduboy); +ArduboyPlaytune tunes; void setup() { @@ -163,8 +165,12 @@ void setup() text.setSize(4); text.setCursor(0,0); text.print("Music\nDemo"); - arduboy.display(); + // audio setup + tunes.initChannel(PIN_SPEAKER_1); + tunes.initChannel(PIN_SPEAKER_2); + + arduboy.display(); } @@ -198,6 +204,6 @@ void loop () arduboy.display(); // play the tune if we aren't already - if (!arduboy.tunes.playing()) - arduboy.tunes.playScore(score); + if (!tunes.playing()) + tunes.playScore(score); } diff --git a/src/Arduboy.h b/src/Arduboy.h index d2a8c50..f9f7c97 100644 --- a/src/Arduboy.h +++ b/src/Arduboy.h @@ -172,7 +172,6 @@ public: /// Swap the references of two pointers. void swap(int16_t& a, int16_t& b); - ArduboyTunes tunes; ArduboyAudio audio; void setFrameRate(uint8_t rate); diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 750761f..c495dd8 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -1,55 +1,10 @@ #include "Arduboy.h" #include "audio.h" -const byte PROGMEM tune_pin_to_timer_PGM[] = { 3, 1 }; -volatile byte *_tunes_timer1_pin_port; -volatile byte _tunes_timer1_pin_mask; -volatile int32_t timer1_toggle_count; -volatile byte *_tunes_timer3_pin_port; -volatile byte _tunes_timer3_pin_mask; -byte _tune_pins[AVAILABLE_TIMERS]; -byte _tune_num_chans = 0; -volatile boolean tune_playing = false; // is the score still playing? -volatile unsigned wait_timer_frequency2; /* its current frequency */ -volatile boolean wait_timer_playing = false; /* is it currently playing a note? */ -volatile boolean tonePlaying = false; -volatile unsigned long wait_toggle_count; /* countdown score waits */ - -// pointers to your musical score and your position in said score -volatile const byte *score_start = 0; -volatile const byte *score_cursor = 0; - -// Table of midi note frequencies * 2 -// They are times 2 for greater accuracy, yet still fits in a word. -// Generated from Excel by =ROUND(2*440/32*(2^((x-9)/12)),0) for 0= _tune_num_chans) - return; - - // we only have frequencies for 128 notes - if (note > 127) { - return; - } - - timer_num = pgm_read_byte(tune_pin_to_timer_PGM + chan); - if (note < 48) { - frequency2 = pgm_read_byte(_midi_byte_note_frequencies + note); - } else { - frequency2 = pgm_read_word(_midi_word_note_frequencies + note - 48); - } - - //****** 16-bit timer ********* - // two choices for the 16 bit timers: ck/1 or ck/64 - ocr = F_CPU / frequency2 - 1; - prescalar_bits = 0b001; - if (ocr > 0xffff) { - ocr = F_CPU / frequency2 / 64 - 1; - prescalar_bits = 0b011; - } - // Set the OCR for the given timer, then turn on the interrupts - switch (timer_num) { - case 1: - TCCR1B = (TCCR1B & 0b11111000) | prescalar_bits; - OCR1A = ocr; - bitWrite(TIMSK1, OCIE1A, 1); - break; - case 3: - TCCR3B = (TCCR3B & 0b11111000) | prescalar_bits; - OCR3A = ocr; - wait_timer_frequency2 = frequency2; // for "tune_delay" function - wait_timer_playing = true; - bitWrite(TIMSK3, OCIE3A, 1); - break; - } -} - -void ArduboyTunes::stopNote(byte chan) -{ - byte timer_num; - timer_num = pgm_read_byte(tune_pin_to_timer_PGM + chan); - switch (timer_num) { - case 1: - TIMSK1 &= ~(1 << OCIE1A); // disable the interrupt - *_tunes_timer1_pin_port &= ~(_tunes_timer1_pin_mask); // keep pin low after stop - break; - case 3: - wait_timer_playing = false; - *_tunes_timer3_pin_port &= ~(_tunes_timer3_pin_mask); // keep pin low after stop - break; - } -} - -void ArduboyTunes::playScore(const byte *score) -{ - score_start = score; - score_cursor = score_start; - step(); /* execute initial commands */ - tune_playing = true; /* release the interrupt routine */ -} - -void ArduboyTunes::stopScore (void) -{ - for (uint8_t i = 0; i < _tune_num_chans; i++) - stopNote(i); - tune_playing = false; -} - -bool ArduboyTunes::playing() -{ - return tune_playing; -} - -/* Do score commands until a "wait" is found, or the score is stopped. -This is called initially from tune_playcore, but then is called -from the interrupt routine when waits expire. -*/ -/* if CMD < 0x80, then the other 7 bits and the next byte are a 15-bit big-endian number of msec to wait */ -void ArduboyTunes::step() -{ - byte command, opcode, chan; - unsigned duration; - - while (1) { - command = pgm_read_byte(score_cursor++); - opcode = command & 0xf0; - chan = command & 0x0f; - if (opcode == TUNE_OP_STOPNOTE) { /* stop note */ - stopNote(chan); - } - else if (opcode == TUNE_OP_PLAYNOTE) { /* play note */ - playNote(chan, pgm_read_byte(score_cursor++)); - } - else if (opcode == TUNE_OP_RESTART) { /* restart score */ - score_cursor = score_start; - } - else if (opcode == TUNE_OP_STOP) { /* stop score */ - tune_playing = false; - break; - } - else if (opcode < 0x80) { /* wait count in msec. */ - duration = ((unsigned)command << 8) | (pgm_read_byte(score_cursor++)); - wait_toggle_count = ((unsigned long) wait_timer_frequency2 * duration + 500) / 1000; - if (wait_toggle_count == 0) wait_toggle_count = 1; - break; - } - } -} - -void ArduboyTunes::closeChannels(void) -{ - byte timer_num; - for (uint8_t chan=0; chan < _tune_num_chans; chan++) { - timer_num = pgm_read_byte(tune_pin_to_timer_PGM + chan); - switch (timer_num) { - case 1: - TIMSK1 &= ~(1 << OCIE1A); - break; - case 3: - TIMSK3 &= ~(1 << OCIE3A); - break; - } - digitalWrite(_tune_pins[chan], 0); - } - _tune_num_chans = 0; - tune_playing = false; -} - -void ArduboyTunes::soundOutput() -{ - if (wait_timer_playing) { // toggle the pin if we're sounding a note - *_tunes_timer3_pin_port ^= _tunes_timer3_pin_mask; - } - if (tune_playing && wait_toggle_count && --wait_toggle_count == 0) { - // end of a score wait, so execute more score commands - ArduboyTunes::step(); // execute commands - } -} - -void ArduboyTunes::tone(unsigned int frequency, unsigned long duration) -{ - tonePlaying = true; - uint8_t prescalarbits = 0b001; - int32_t toggle_count = 0; - uint32_t ocr = 0; - - // two choices for the 16 bit timers: ck/1 or ck/64 - ocr = F_CPU / frequency / 2 - 1; - prescalarbits = 0b001; - if (ocr > 0xffff) { - ocr = F_CPU / frequency / 2 / 64 - 1; - prescalarbits = 0b011; - } - TCCR1B = (TCCR1B & 0b11111000) | prescalarbits; - - // Calculate the toggle count - if (duration > 0) { - toggle_count = 2 * frequency * duration / 1000; - } - else { - toggle_count = -1; - } - // Set the OCR for the given timer, - // set the toggle count, - // then turn on the interrupts - OCR1A = ocr; - timer1_toggle_count = toggle_count; - bitWrite(TIMSK1, OCIE1A, 1); -} - -// TIMER 1 -ISR(TIMER1_COMPA_vect) -{ - if (tonePlaying) { - if (timer1_toggle_count != 0) { - // toggle the pin - *_tunes_timer1_pin_port ^= _tunes_timer1_pin_mask; - if (timer1_toggle_count > 0) timer1_toggle_count--; - } - else { - tonePlaying = false; - TIMSK1 &= ~(1 << OCIE1A); // disable the interrupt - *_tunes_timer1_pin_port &= ~(_tunes_timer1_pin_mask); // keep pin low after stop - } - } - else { - *_tunes_timer1_pin_port ^= _tunes_timer1_pin_mask; // toggle the pin - } -} - -// TIMER 3 -ISR(TIMER3_COMPA_vect) -{ - // Timer 3 is the one assigned first, so we keep it running always - // and use it to time score waits, whether or not it is playing a note. - ArduboyTunes::soundOutput(); -} - diff --git a/src/audio/audio.h b/src/audio/audio.h index 63cb70d..acff7b9 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -6,13 +6,6 @@ #include #include -#define AVAILABLE_TIMERS 2 -#define TUNE_OP_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte */ -#define TUNE_OP_STOPNOTE 0x80 /* stop a note: low nibble is generator # */ -#define TUNE_OP_RESTART 0xe0 /* restart the score from the beginning */ -#define TUNE_OP_STOP 0xf0 /* stop playing */ - - class ArduboyAudio { public: @@ -26,36 +19,4 @@ protected: bool static audio_enabled; }; - -class ArduboyTunes -{ -public: - // Playtune Functions - - /// Assign a timer to an output pin. - void static initChannel(byte pin); - - /// Start playing a polyphonic score. - void playScore(const byte *score); - - /// Stop playing the score. - void stopScore(); - - /// Delay in milliseconds. - void delay(unsigned msec); - - /// Stop all timers. - void closeChannels(); - - bool playing(); - void tone(unsigned int frequency, unsigned long duration); - - // called via interrupt - void static step(); - void static soundOutput(); - -private: - void static playNote (byte chan, byte note); - void static stopNote (byte chan); -}; #endif