diff --git a/firmware/Makefile b/firmware/Makefile index 13dcc1e..fcb16f4 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -19,6 +19,7 @@ COUNTER_APP = 1 DMESG_APP = 1 PAGER_APP = 0 JUKEBOX_APP = 1 +MUSIC_APP = 0 #Don't forget to set your timezone for Beats in config.h. BEATS_APP = 0 @@ -114,7 +115,10 @@ ifeq ($(BEATS_APP),1) APPS_OBJ += apps/beats.o APPS_DEFINES += BEATS_APP endif - +ifeq ($(MUSIC_APP),1) +APPS_OBJ += apps/music.o +APPS_DEFINES += MUSIC_APP +endif #GCC8 from Texas Instruments, not the GCC4 that ships with Debian. diff --git a/firmware/applist.c b/firmware/applist.c index aea3b3c..7f936e2 100644 --- a/firmware/applist.c +++ b/firmware/applist.c @@ -176,6 +176,12 @@ const struct app subapps[]={ }, #endif +#ifdef MUSIC_APP + //MUSIC + {.name="music", .init=music_init, .draw=music_draw, .exit=music_exit, + .keypress=music_keypress, .fallthrough=music_fallthrough, + }, +#endif #ifdef CALIBRATE_APP //CALIBRATE diff --git a/firmware/applist.h b/firmware/applist.h index e4b576a..b9b017c 100644 --- a/firmware/applist.h +++ b/firmware/applist.h @@ -33,7 +33,7 @@ extern const struct app clock_applet; #include "apps/counter.h" #include "apps/pager.h" #include "apps/jukebox.h" - +#include "apps/music.h" /* For each application, the init() function is called at entry. The draw() function is called four times per second. The exit() diff --git a/firmware/apps/music.c b/firmware/apps/music.c new file mode 100644 index 0000000..f389c74 --- /dev/null +++ b/firmware/apps/music.c @@ -0,0 +1,257 @@ +/*! \file music.c + \brief Music Application + + Plays music with the buzzer + +*/ + +#include +#include +#include + +#include "api.h" + +/*! this defines a song + * key defines the key press on the keypad + * */ +typedef struct { + const char key; + const uint16_t *notes; +} music_t; + +#define DURATION_100 0x2000 +#define DURATION_200 0x4000 +#define DURATION_400 0x6000 +#define DURATION_800 0x8000 +#define DURATION_1600 0xa000 +#define DURATION_3200 0xc000 +#define DURATION_6400 0xe000 + +#define TONE_MASK 0x1fff +#define DURATION_SHIFT 13 +#define DURATION_MASK 0xe000 + +#define PAUSE 0 + +#define END 0 + +#define NUM_MUSIC (sizeof(music) / sizeof(music_t)) + +static const uint16_t zelda_lullaby[] = { + NOTE_E5 | DURATION_400, NOTE_G5 | DURATION_200, + NOTE_D5 | DURATION_400, NOTE_C5 | DURATION_100, NOTE_D5 | DURATION_100, + NOTE_E5 | DURATION_400, NOTE_G5 | DURATION_200, + NOTE_D5 | DURATION_100, + NOTE_E5 | DURATION_400, NOTE_G5 | DURATION_200, + NOTE_D6 | DURATION_400, NOTE_C6 | DURATION_200, + NOTE_G5 | DURATION_400, NOTE_F5 | DURATION_100, NOTE_E5 | DURATION_100, + NOTE_D5 | DURATION_100, + NOTE_E5 | DURATION_400, NOTE_G5 | DURATION_200, + NOTE_D5 | DURATION_400, NOTE_C5 | DURATION_100, NOTE_D5 | DURATION_100, + NOTE_E5 | DURATION_400, NOTE_G5 | DURATION_200, + NOTE_D5 | DURATION_100, + NOTE_E5 | DURATION_400, NOTE_G5 | DURATION_200, + + NOTE_D6 | DURATION_400, NOTE_C6 | DURATION_200, + NOTE_G5 | DURATION_400, NOTE_F5 | DURATION_100, NOTE_E5 | DURATION_100, + NOTE_F5 | DURATION_100, NOTE_E5 | DURATION_100, NOTE_C5 | DURATION_400, + NOTE_F5 | DURATION_400, NOTE_E5 | DURATION_100, NOTE_D5 | DURATION_100, + NOTE_E5 | DURATION_100, NOTE_D5 | DURATION_100, NOTE_A4 | DURATION_400, + NOTE_G5 | DURATION_400, NOTE_F5 | DURATION_100, NOTE_E5 | DURATION_100, + NOTE_F5 | DURATION_100, NOTE_E5 | DURATION_100, NOTE_C5 | DURATION_200, NOTE_F5 | DURATION_200, + NOTE_C6 | DURATION_100, + END, +}; + +static const uint16_t zelda_song_of_storms[] = { + NOTE_D5 | DURATION_100, + NOTE_F5 | DURATION_100, + NOTE_D6 | DURATION_200, + + NOTE_D5 | DURATION_100, + NOTE_F5 | DURATION_100, + NOTE_D6 | DURATION_200, + + NOTE_E6 | DURATION_200, + NOTE_F6 | DURATION_100, + NOTE_E6 | DURATION_100, + NOTE_F6 | DURATION_100, + NOTE_E6 | DURATION_100, + NOTE_C6 | DURATION_100, + NOTE_A5 | DURATION_100, + + NOTE_A5 | DURATION_200, + NOTE_D5 | DURATION_200, + NOTE_F5 | DURATION_100, + NOTE_G5 | DURATION_100, + NOTE_A5 | DURATION_400, + + NOTE_A5 | DURATION_200, + NOTE_D5 | DURATION_200, + NOTE_F5 | DURATION_100, + NOTE_G5 | DURATION_100, + NOTE_E5 | DURATION_400, + + NOTE_D5 | DURATION_100, + NOTE_F5 | DURATION_100, + NOTE_D6 | DURATION_200, + + NOTE_D5 | DURATION_100, + NOTE_F5 | DURATION_100, + NOTE_D6 | DURATION_200, + + NOTE_E6 | DURATION_200, + NOTE_F6 | DURATION_100, + NOTE_E6 | DURATION_100, + NOTE_F6 | DURATION_100, + NOTE_E6 | DURATION_100, + NOTE_C6 | DURATION_100, + NOTE_A5 | DURATION_100, + + NOTE_A5 | DURATION_200, + NOTE_D5 | DURATION_200, + NOTE_F5 | DURATION_100, + NOTE_G5 | DURATION_100, + NOTE_A5 | DURATION_400, + NOTE_A5 | DURATION_200, + NOTE_D5 | DURATION_400, + END, +}; + +static const uint16_t zelda_item[] = { + NOTE_F5 | DURATION_200, + NOTE_FS5 | DURATION_200, + NOTE_G5 | DURATION_200, + NOTE_GS5 | DURATION_800, + END, +}; + +static const uint16_t zelda_secret[] = { + NOTE_G5 | DURATION_100, + NOTE_FS5 | DURATION_100, + NOTE_DS5 | DURATION_100, + NOTE_A4 | DURATION_100, + NOTE_GS4 | DURATION_100, + NOTE_E5 | DURATION_100, + NOTE_GS5 | DURATION_100, + NOTE_C6 | DURATION_100, + END, +}; + +static const uint16_t happy_birthday[] = { + NOTE_C5 | DURATION_100, + NOTE_C5 | DURATION_100, + NOTE_D5 | DURATION_200, + NOTE_C5 | DURATION_200, + NOTE_F5 | DURATION_200, + NOTE_E5 | DURATION_400, + + NOTE_C5 | DURATION_100, + NOTE_C5 | DURATION_100, + NOTE_D5 | DURATION_200, + NOTE_C5 | DURATION_200, + NOTE_G5 | DURATION_200, + NOTE_F5 | DURATION_100, + + NOTE_C5 | DURATION_100, + NOTE_C5 | DURATION_100, + NOTE_C6 | DURATION_200, + NOTE_A5 | DURATION_200, + NOTE_F5 | DURATION_200, + NOTE_E5 | DURATION_200, + NOTE_D5 | DURATION_200, + + NOTE_AS5 | DURATION_100, + NOTE_AS5 | DURATION_100, + NOTE_A5 | DURATION_200, + NOTE_F5 | DURATION_200, + NOTE_G5 | DURATION_200, + NOTE_F5 | DURATION_200, + END, +}; + +static const uint16_t game_of_thrones[] = { + NOTE_G5 | DURATION_400, NOTE_C5 | DURATION_400, + + NOTE_DS5 | DURATION_100, NOTE_F5 | DURATION_100, NOTE_G5 | DURATION_400, NOTE_C5 | DURATION_400, NOTE_DS5 | DURATION_100, NOTE_F5 | DURATION_100, + NOTE_D5 | DURATION_800, + NOTE_F5 | DURATION_400, NOTE_AS4 | DURATION_400, + NOTE_DS5 | DURATION_100, NOTE_D5 | DURATION_100, NOTE_F5 | DURATION_400, NOTE_AS4 | DURATION_400, + NOTE_DS5 | DURATION_100, NOTE_D5 | DURATION_100, NOTE_C5 | DURATION_800, + + END, +}; + + +/*! this is the music library */ + +static const music_t music[] = { + { .key = '1', .notes = zelda_song_of_storms}, + { .key = '2', .notes = zelda_lullaby}, + { .key = '3', .notes = zelda_item}, + { .key = '4', .notes = zelda_secret}, + { .key = '5', .notes = happy_birthday}, + { .key = '6', .notes = game_of_thrones}, +}; + + +static void play_song(const music_t *music){ + const uint16_t *ptr = music->notes; + while (*ptr != END){ + tone(*ptr & TONE_MASK, (1 << (((*ptr & DURATION_MASK) >> DURATION_SHIFT) - 1)) * 100); + + ptr++; + } +} + + +//! Enter the Music application. +void music_init(){ +} + +//! Exit the Music application. +int music_exit(){ + return 0; +} + +enum STATE { IDLE, ABOUT_TO_PLAY, PLAYING }; + +static enum STATE state = IDLE; +static uint8_t selected = 0; + +//! Draw the Music screen. +void music_draw(){ + switch (state){ + case IDLE: + lcd_string("press bt"); + break; + + case ABOUT_TO_PLAY: + lcd_string("playing "); + state = PLAYING; + break; + + case PLAYING: + play_song(&music[selected]); + state = IDLE; + break; + }; +} + +//! Keypress handler for the music applet. +int music_keypress(char ch){ + for (int i = 0; i < NUM_MUSIC; i++){ + if (ch == music[i].key){ + selected = i; + state = ABOUT_TO_PLAY; + return 1; + } + } + return 0; +} + + +//! Fallthrough handler for the music applet. +int music_fallthrough(char ch){ + return music_keypress(ch); +} diff --git a/firmware/apps/music.h b/firmware/apps/music.h new file mode 100644 index 0000000..82f6953 --- /dev/null +++ b/firmware/apps/music.h @@ -0,0 +1,22 @@ +/*! \file music.h + \brief music Application + +*/ + +//! Enter the music application. +void music_init(); + +//! Exit the music application. +int music_exit(); + +//! Draw the music screen. +void music_draw(); + +//! Handle an incoming packet. +void music_packetrx(uint8_t *packet, int len); + +//! Keypress handler for the music applet. +int music_keypress(char ch); + +//! Fallthrough handler for the music applet. +int music_fallthrough(char ch);