Retro Invaders a space invaders clone by Chris Favreau. Written for the RetroMbuino development board from outrageouscircuits.com for the game programming contest.

Dependencies:   mbed

This is a space invaders clone written for the Retro Mbuino from outrageous circuits.

Development board: http://www.outrageouscircuits.com/shop/product/15 ).

The game itself is basic space invaders. Shoot them before they get to the bottom of the screen. It has a UFO saucer which you can shoot for extra points. You get 4 shields and each shield can be hit up to 4 times before it is gone. Hmm... as each level increases the speed of the invaders shots goes up. The invaders only speed up when there is less of them. You complete the level when you shoot all the invaders. The game ends when a) you run out of lives (you start with 3) or the invaders get to the bottom.

The LEDs turned out to be a pretty cool addition to the game. I wrote a class that blinks them and turns them on for a specified amount of time. They add a nice extra to the game. I use them on the intro screen and when the UFO is present.

The sound turned out to be really difficult for a few reasons. The biggest was that I had never written a sound engine before. The interrupt service routine working off the timer was the easier part. I also had a lot of trouble because there is no filter to filter out the PWM frequency to the speaker... so I had to run the PWM frequency way up there 30 kHz.

The graphics turned out to be a bit of a bear too. Thanks to Chris Taylor for his really great LCD API. I picked up a couple of frames per second from that. I had modified the DisplayN18 class for blitting a single line buffer to the LCD panel however his is a little faster for some reason? I used a different approach to doing the graphics (as I have very little experience with anything other than double buffered displays). I have a tile map and a list of sprites. Each tile/sprite is 1 bit 8x8. They could be bigger. I ran out of time. That much is not special. What is different from what I can tell is that I use a 1 line buffer that is 160 shorts long. The render function first adds the tile map data into the line buffer first. Then the sprites are added over the existing data. You can have a great deal of different sprites and maps going to the screen and just have to rewrite the LCD memory once per frame. After each line is composited, the line is then drawn to the LCD. Kind of like an Atari 2600. Each sprite/tile has a foreground and background color and can be different from the other tiles/sprites. There is one color reserved for Transparency.

There are 16 colors to choose from. I chose a palette based on the Macintosh OS 4.1 palette I found on WikiPedia. It is a very nice mix of colors.

I found a sprite editor called SpriteX ( https://code.google.com/p/spritesx-ed/ )... it works nicely except that the 16x16 sprites are in a weird format. Time limited me to 8x8 sprites. Oh well.

I used nokring to make the music. It makes RTTTL formatted ring tones which my sound api can play. Here is a useful site that has lots of arcade/video game ring tones with a link to nokring in the utilities page. http://arcadetones.emuunlim.com/files.htm

Other than all that stuff I used state machines to do most of the game logic. Please excuse the horrible coding as I tried to comment a lot of it however it is not very nice to look at. Lots of long functions...

Committer:
cfavreau
Date:
Tue Mar 03 04:26:01 2015 +0000
Revision:
0:c79e1f29f029
Retro Invaders by Chris Favreau for the RetroMbuino Platform - outrageouscircuits.com game programming contest.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
cfavreau 0:c79e1f29f029 1 #include <string.h>
cfavreau 0:c79e1f29f029 2 #include "sound.h"
cfavreau 0:c79e1f29f029 3 #include "lpc111x.h"
cfavreau 0:c79e1f29f029 4
cfavreau 0:c79e1f29f029 5 // Useful Macros
cfavreau 0:c79e1f29f029 6 #define ISNUMBER(a) ((a >= '0') && (a <= '9'))
cfavreau 0:c79e1f29f029 7 #define ISALPHA(a) (((a >= 'a') && (a <= 'z')) || ((a >= 'A') && (a <= 'Z')))
cfavreau 0:c79e1f29f029 8 #define TOLOWER(a) if ((a >= 'A') && (a <= 'Z')) a = (a - 'A') + 'a';
cfavreau 0:c79e1f29f029 9
cfavreau 0:c79e1f29f029 10 // ======== Waveform Definitions ========
cfavreau 0:c79e1f29f029 11 // 64 elements
cfavreau 0:c79e1f29f029 12 const char sine[] = {
cfavreau 0:c79e1f29f029 13 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
cfavreau 0:c79e1f29f029 14 0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
cfavreau 0:c79e1f29f029 15 0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
cfavreau 0:c79e1f29f029 16 0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 17 0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
cfavreau 0:c79e1f29f029 18 0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
cfavreau 0:c79e1f29f029 19 0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
cfavreau 0:c79e1f29f029 20 0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
cfavreau 0:c79e1f29f029 21 0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
cfavreau 0:c79e1f29f029 22 0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
cfavreau 0:c79e1f29f029 23 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
cfavreau 0:c79e1f29f029 24 0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 25 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
cfavreau 0:c79e1f29f029 26 0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
cfavreau 0:c79e1f29f029 27 0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
cfavreau 0:c79e1f29f029 28 0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c};
cfavreau 0:c79e1f29f029 29
cfavreau 0:c79e1f29f029 30 const char square[] = {
cfavreau 0:c79e1f29f029 31 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 32 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 33 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 34 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 35 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 36 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 37 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 38 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
cfavreau 0:c79e1f29f029 39 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 40 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 41 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 42 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 43 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 44 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 45 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
cfavreau 0:c79e1f29f029 46 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
cfavreau 0:c79e1f29f029 47
cfavreau 0:c79e1f29f029 48 const char sawtooth[] = {
cfavreau 0:c79e1f29f029 49 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
cfavreau 0:c79e1f29f029 50 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
cfavreau 0:c79e1f29f029 51 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
cfavreau 0:c79e1f29f029 52 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
cfavreau 0:c79e1f29f029 53 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
cfavreau 0:c79e1f29f029 54 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
cfavreau 0:c79e1f29f029 55 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
cfavreau 0:c79e1f29f029 56 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
cfavreau 0:c79e1f29f029 57 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
cfavreau 0:c79e1f29f029 58 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
cfavreau 0:c79e1f29f029 59 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
cfavreau 0:c79e1f29f029 60 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
cfavreau 0:c79e1f29f029 61 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
cfavreau 0:c79e1f29f029 62 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
cfavreau 0:c79e1f29f029 63 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
cfavreau 0:c79e1f29f029 64 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff};
cfavreau 0:c79e1f29f029 65
cfavreau 0:c79e1f29f029 66 const char triangle[] = {
cfavreau 0:c79e1f29f029 67 0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
cfavreau 0:c79e1f29f029 68 0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
cfavreau 0:c79e1f29f029 69 0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
cfavreau 0:c79e1f29f029 70 0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
cfavreau 0:c79e1f29f029 71 0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
cfavreau 0:c79e1f29f029 72 0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
cfavreau 0:c79e1f29f029 73 0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
cfavreau 0:c79e1f29f029 74 0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
cfavreau 0:c79e1f29f029 75 0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
cfavreau 0:c79e1f29f029 76 0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
cfavreau 0:c79e1f29f029 77 0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
cfavreau 0:c79e1f29f029 78 0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
cfavreau 0:c79e1f29f029 79 0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
cfavreau 0:c79e1f29f029 80 0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
cfavreau 0:c79e1f29f029 81 0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
cfavreau 0:c79e1f29f029 82 0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01};
cfavreau 0:c79e1f29f029 83
cfavreau 0:c79e1f29f029 84
cfavreau 0:c79e1f29f029 85 // For vibrato - 64 elements in length - thanks lft
cfavreau 0:c79e1f29f029 86 const int8_t sinetable[] = {
cfavreau 0:c79e1f29f029 87 0, 12, 25, 37, 49, 60, 71, 81, 90, 98, 106, 112, 117, 122, 125, 126,
cfavreau 0:c79e1f29f029 88 127, 126, 125, 122, 117, 112, 106, 98, 90, 81, 71, 60, 49, 37, 25, 12,
cfavreau 0:c79e1f29f029 89 0, -12, -25, -37, -49, -60, -71, -81, -90, -98, -106, -112, -117, -122,
cfavreau 0:c79e1f29f029 90 -125, -126, -127, -126, -125, -122, -117, -112, -106, -98, -90, -81,
cfavreau 0:c79e1f29f029 91 -71, -60, -49, -37, -25, -12
cfavreau 0:c79e1f29f029 92 };
cfavreau 0:c79e1f29f029 93
cfavreau 0:c79e1f29f029 94 // -=-=-=-=-=-=-= End Waveforms -=-=-=-=-=-=-=-=-=
cfavreau 0:c79e1f29f029 95
cfavreau 0:c79e1f29f029 96 #define PWM_FREQUENCY 23448 // 23.448 kHz => Match value of ~2047 (Convenient for binary math)
cfavreau 0:c79e1f29f029 97 #define PWM_MATCH_VAL (48000000 / PWM_FREQUENCY)
cfavreau 0:c79e1f29f029 98 #define EFFECT_CALL_FREQ 50 // Hz
cfavreau 0:c79e1f29f029 99 #define EFFECT_CALL_WAIT PWM_FREQUENCY / EFFECT_CALL_FREQ
cfavreau 0:c79e1f29f029 100 #define MAX_FREQ 8000 // 8 kHz (something pratical.. and not ear splitting)
cfavreau 0:c79e1f29f029 101 #define MAX_FREQ_INC ((65535 * MAX_FREQ) / PWM_FREQUENCY)
cfavreau 0:c79e1f29f029 102 #define SLIDE_SCALER ((65535 * 10) / PWM_FREQUENCY) // 10 Hz per tick...
cfavreau 0:c79e1f29f029 103
cfavreau 0:c79e1f29f029 104 // Volatile Interrupt Variables
cfavreau 0:c79e1f29f029 105 volatile int timer32_0_counter;
cfavreau 0:c79e1f29f029 106 Sound *pSound = NULL;
cfavreau 0:c79e1f29f029 107 volatile unsigned short int effect_wait = EFFECT_CALL_WAIT;
cfavreau 0:c79e1f29f029 108
cfavreau 0:c79e1f29f029 109 // Interrupt ---------------------------------------
cfavreau 0:c79e1f29f029 110
cfavreau 0:c79e1f29f029 111 void TIMER32_0_IRQHandler(void)
cfavreau 0:c79e1f29f029 112 {
cfavreau 0:c79e1f29f029 113 // Check to see if the PWM interrupt is triggered (MR3)
cfavreau 0:c79e1f29f029 114 if (TMR32B0IR & BIT3)
cfavreau 0:c79e1f29f029 115 {
cfavreau 0:c79e1f29f029 116 if (!pSound) return;
cfavreau 0:c79e1f29f029 117
cfavreau 0:c79e1f29f029 118 effect_wait--;
cfavreau 0:c79e1f29f029 119 if (effect_wait == 0)
cfavreau 0:c79e1f29f029 120 {
cfavreau 0:c79e1f29f029 121 // Do the effects at 50 Hz ... why because everyone does it that way
cfavreau 0:c79e1f29f029 122 pSound->DoEffects();
cfavreau 0:c79e1f29f029 123 effect_wait = EFFECT_CALL_WAIT;
cfavreau 0:c79e1f29f029 124 // Do the song at 50 Hz
cfavreau 0:c79e1f29f029 125 pSound->DoSong();
cfavreau 0:c79e1f29f029 126 }
cfavreau 0:c79e1f29f029 127
cfavreau 0:c79e1f29f029 128 static unsigned int noiseseed = 1;
cfavreau 0:c79e1f29f029 129 unsigned char newbit;
cfavreau 0:c79e1f29f029 130
cfavreau 0:c79e1f29f029 131 newbit = 0;
cfavreau 0:c79e1f29f029 132 if(noiseseed & 0x80000000L) newbit ^= 1;
cfavreau 0:c79e1f29f029 133 if(noiseseed & 0x01000000L) newbit ^= 1;
cfavreau 0:c79e1f29f029 134 if(noiseseed & 0x00000040L) newbit ^= 1;
cfavreau 0:c79e1f29f029 135 if(noiseseed & 0x00000200L) newbit ^= 1;
cfavreau 0:c79e1f29f029 136 noiseseed = (noiseseed << 1) | newbit;
cfavreau 0:c79e1f29f029 137
cfavreau 0:c79e1f29f029 138 // Reset the interrupt flag
cfavreau 0:c79e1f29f029 139 TMR32B0IR = BIT3;
cfavreau 0:c79e1f29f029 140
cfavreau 0:c79e1f29f029 141 // Increment the counter
cfavreau 0:c79e1f29f029 142 timer32_0_counter++;
cfavreau 0:c79e1f29f029 143
cfavreau 0:c79e1f29f029 144 register int master_acc = 0;
cfavreau 0:c79e1f29f029 145 register int acc = 0;
cfavreau 0:c79e1f29f029 146 register int channels = 0;
cfavreau 0:c79e1f29f029 147 bool bMuted = pSound->bMuted;
cfavreau 0:c79e1f29f029 148
cfavreau 0:c79e1f29f029 149 for (int i = 0; i < SOUND_MAX_CHANNELS; i++)
cfavreau 0:c79e1f29f029 150 {
cfavreau 0:c79e1f29f029 151 SOUND_CHANNEL *pChannel = &pSound->channel[i];
cfavreau 0:c79e1f29f029 152 if ((pChannel->active) && (pChannel->volume > 0))
cfavreau 0:c79e1f29f029 153 {
cfavreau 0:c79e1f29f029 154 pChannel->phase_acc += pChannel->phase_inc;
cfavreau 0:c79e1f29f029 155 acc = pChannel->pWaveform[pChannel->phase_acc >> 8]; // -128 to 127
cfavreau 0:c79e1f29f029 156 if (pChannel->use_noise) acc = (noiseseed & 0xFF) - 127;// -128 to 127
cfavreau 0:c79e1f29f029 157 acc *= pChannel->volume; // 0 to 65535
cfavreau 0:c79e1f29f029 158 acc = acc >> 8; // Rescale to -128 to 127
cfavreau 0:c79e1f29f029 159 acc += 128; // 0 to 255
cfavreau 0:c79e1f29f029 160 master_acc += acc;
cfavreau 0:c79e1f29f029 161 channels++;
cfavreau 0:c79e1f29f029 162 }
cfavreau 0:c79e1f29f029 163 }
cfavreau 0:c79e1f29f029 164
cfavreau 0:c79e1f29f029 165 if (channels > 1) master_acc /= channels;
cfavreau 0:c79e1f29f029 166 master_acc = master_acc << 3; // 0 to 2047 (PWM_MATCH_VAL)
cfavreau 0:c79e1f29f029 167 //master_acc = acc << 3;
cfavreau 0:c79e1f29f029 168 if (master_acc > PWM_MATCH_VAL) master_acc = PWM_MATCH_VAL;
cfavreau 0:c79e1f29f029 169
cfavreau 0:c79e1f29f029 170 // Check to see if we are muted
cfavreau 0:c79e1f29f029 171 if (bMuted) master_acc = 0;
cfavreau 0:c79e1f29f029 172
cfavreau 0:c79e1f29f029 173 // Bounds check the master accumulator
cfavreau 0:c79e1f29f029 174 TMR32B0MR0 = master_acc; // ( xx >> 8 equivalent to x / 256)
cfavreau 0:c79e1f29f029 175 }
cfavreau 0:c79e1f29f029 176
cfavreau 0:c79e1f29f029 177 return;
cfavreau 0:c79e1f29f029 178 }
cfavreau 0:c79e1f29f029 179
cfavreau 0:c79e1f29f029 180 // Implementation ----------------------------------
cfavreau 0:c79e1f29f029 181
cfavreau 0:c79e1f29f029 182 Sound::Sound()
cfavreau 0:c79e1f29f029 183 {
cfavreau 0:c79e1f29f029 184 timer32_0_counter = 0;
cfavreau 0:c79e1f29f029 185 pSound = this;
cfavreau 0:c79e1f29f029 186 for (int i = 0; i < SOUND_MAX_CHANNELS; i++)
cfavreau 0:c79e1f29f029 187 channel[i].active = false;
cfavreau 0:c79e1f29f029 188 // Initialize the song stuff
cfavreau 0:c79e1f29f029 189 bPlaySong = false;
cfavreau 0:c79e1f29f029 190 bLoopSong = false;
cfavreau 0:c79e1f29f029 191 m_pSong = NULL;
cfavreau 0:c79e1f29f029 192 m_pSongBuffer = NULL;
cfavreau 0:c79e1f29f029 193 iSongChannel = 0;
cfavreau 0:c79e1f29f029 194 iSongNoteOffset = 0;
cfavreau 0:c79e1f29f029 195 iNoteCounter = 0;
cfavreau 0:c79e1f29f029 196 iTicksPerBeat = 0;
cfavreau 0:c79e1f29f029 197 bMuted = false;
cfavreau 0:c79e1f29f029 198 }
cfavreau 0:c79e1f29f029 199
cfavreau 0:c79e1f29f029 200 Sound::~Sound()
cfavreau 0:c79e1f29f029 201 {
cfavreau 0:c79e1f29f029 202 }
cfavreau 0:c79e1f29f029 203
cfavreau 0:c79e1f29f029 204 void Sound::Init()
cfavreau 0:c79e1f29f029 205 {
cfavreau 0:c79e1f29f029 206 SetupDefaults();
cfavreau 0:c79e1f29f029 207
cfavreau 0:c79e1f29f029 208 // DISABLE the TImer
cfavreau 0:c79e1f29f029 209 TMR32B0TCR = 0;
cfavreau 0:c79e1f29f029 210
cfavreau 0:c79e1f29f029 211 // DEBUG - do not enable the output pin so we do not make so much noise!!!
cfavreau 0:c79e1f29f029 212 // Initialize the PWM Match Output
cfavreau 0:c79e1f29f029 213 // Configure IO control register IOCON_PIO0_18 offset 0x40044048 with CT32B0_MAT0 bit set (0x02)
cfavreau 0:c79e1f29f029 214 REGISTER_32(0x40044048) = BIT1 | BIT4;
cfavreau 0:c79e1f29f029 215
cfavreau 0:c79e1f29f029 216 // Configure the interrupt
cfavreau 0:c79e1f29f029 217 NVIC_SetVector(TIMER_32_0_IRQn, (uint32_t)TIMER32_0_IRQHandler);
cfavreau 0:c79e1f29f029 218 NVIC_SetPriority(TIMER_32_1_IRQn,0);
cfavreau 0:c79e1f29f029 219 NVIC_EnableIRQ(TIMER_32_0_IRQn);
cfavreau 0:c79e1f29f029 220
cfavreau 0:c79e1f29f029 221 // CT32B0 base address 0x40014000
cfavreau 0:c79e1f29f029 222 // Turn ON CT32B0 => System Clock Control Register => SYSAHBCLKCTRL Bit 9
cfavreau 0:c79e1f29f029 223 SYSAHBCLKCTRL |= BIT9;
cfavreau 0:c79e1f29f029 224 // Set our frequency (counter value) in Match Register 0 (as it does not map directly to a pin) => Match Register 0 MR0
cfavreau 0:c79e1f29f029 225 TMR32B0MR3 = PWM_MATCH_VAL; // PWM Frequency
cfavreau 0:c79e1f29f029 226 TMR32B0MR0 = 0; // 0% Duty for now (OFF)
cfavreau 0:c79e1f29f029 227 // Configure Reset TC on Match with MR0 MR0R (Bit 1) MR0I (Bit 0) => Match Control Register MCR
cfavreau 0:c79e1f29f029 228 // Reset on Match Register 3 MR3 (as this is the register we use for the base frequency ... MR0 is used to set the duty cycle)
cfavreau 0:c79e1f29f029 229 TMR32B0MCR = BIT10 | BIT9;
cfavreau 0:c79e1f29f029 230 // PWM Control Register
cfavreau 0:c79e1f29f029 231 TMR32B0PWMC = BIT0;
cfavreau 0:c79e1f29f029 232 // Zero the counter 0 => Timer Counter Register TC
cfavreau 0:c79e1f29f029 233 TMR32B0TC = 0;
cfavreau 0:c79e1f29f029 234 // Set the PreScale Register
cfavreau 0:c79e1f29f029 235 TMR32B0PR = 0;
cfavreau 0:c79e1f29f029 236 // Clear the Prescale Counter
cfavreau 0:c79e1f29f029 237 TMR32B0PC = 0;
cfavreau 0:c79e1f29f029 238 // Enable the timer => Timer Control Register CR
cfavreau 0:c79e1f29f029 239 TMR32B0TCR = BIT0;
cfavreau 0:c79e1f29f029 240
cfavreau 0:c79e1f29f029 241 __enable_irq(); // Enable Interrupts
cfavreau 0:c79e1f29f029 242 }
cfavreau 0:c79e1f29f029 243
cfavreau 0:c79e1f29f029 244 void Sound::PlaySound(int iChannel, int iFreqHz, int iLength)
cfavreau 0:c79e1f29f029 245 {
cfavreau 0:c79e1f29f029 246 // Check the channel
cfavreau 0:c79e1f29f029 247 if (iChannel < 0) return;
cfavreau 0:c79e1f29f029 248 if (iChannel >= SOUND_MAX_CHANNELS) return;
cfavreau 0:c79e1f29f029 249
cfavreau 0:c79e1f29f029 250 if (iFreqHz == 0)
cfavreau 0:c79e1f29f029 251 {
cfavreau 0:c79e1f29f029 252 StopSound(iChannel);
cfavreau 0:c79e1f29f029 253 return;
cfavreau 0:c79e1f29f029 254 }
cfavreau 0:c79e1f29f029 255
cfavreau 0:c79e1f29f029 256 // Get a pointer to the sound channel structure
cfavreau 0:c79e1f29f029 257 SOUND_CHANNEL *pChannel = &channel[iChannel];
cfavreau 0:c79e1f29f029 258
cfavreau 0:c79e1f29f029 259 // Calculate a phase increment value
cfavreau 0:c79e1f29f029 260 int inc = (65535 * iFreqHz) / PWM_FREQUENCY;
cfavreau 0:c79e1f29f029 261 pChannel->phase_inc = (unsigned short)inc;
cfavreau 0:c79e1f29f029 262 pChannel->orig_phase_inc = (unsigned short)inc;
cfavreau 0:c79e1f29f029 263 // Clear the phase accumulator
cfavreau 0:c79e1f29f029 264 pChannel->phase_acc = 0;
cfavreau 0:c79e1f29f029 265
cfavreau 0:c79e1f29f029 266 // Set the play length (0 to 255, 0 = INFINITE, 1 to 255 delay in 50 Hz ticks)
cfavreau 0:c79e1f29f029 267 pChannel->play_length = iLength;
cfavreau 0:c79e1f29f029 268
cfavreau 0:c79e1f29f029 269 // Reset the volume level to its initial setting
cfavreau 0:c79e1f29f029 270 pChannel->volume = pChannel->vol_level;
cfavreau 0:c79e1f29f029 271
cfavreau 0:c79e1f29f029 272 // Reset the change wait to the change speed
cfavreau 0:c79e1f29f029 273 pChannel->change_wait = pChannel->change_speed;
cfavreau 0:c79e1f29f029 274
cfavreau 0:c79e1f29f029 275 // Set the active flag last
cfavreau 0:c79e1f29f029 276 pChannel->active = true;
cfavreau 0:c79e1f29f029 277 }
cfavreau 0:c79e1f29f029 278
cfavreau 0:c79e1f29f029 279 void Sound::StopSound(int iChannel)
cfavreau 0:c79e1f29f029 280 {
cfavreau 0:c79e1f29f029 281 // Check the channel
cfavreau 0:c79e1f29f029 282 if (iChannel < 0) return;
cfavreau 0:c79e1f29f029 283 if (iChannel >= SOUND_MAX_CHANNELS) return;
cfavreau 0:c79e1f29f029 284
cfavreau 0:c79e1f29f029 285 // Clear the active flag
cfavreau 0:c79e1f29f029 286 channel[iChannel].active = false;
cfavreau 0:c79e1f29f029 287 }
cfavreau 0:c79e1f29f029 288
cfavreau 0:c79e1f29f029 289 bool Sound::IsBusy(int iChannel)
cfavreau 0:c79e1f29f029 290 {
cfavreau 0:c79e1f29f029 291 // Check the channel
cfavreau 0:c79e1f29f029 292 if (iChannel < 0) return false;
cfavreau 0:c79e1f29f029 293 if (iChannel >= SOUND_MAX_CHANNELS) return false;
cfavreau 0:c79e1f29f029 294
cfavreau 0:c79e1f29f029 295 return (channel[iChannel].active && (channel[iChannel].volume > 0));
cfavreau 0:c79e1f29f029 296 }
cfavreau 0:c79e1f29f029 297
cfavreau 0:c79e1f29f029 298 int Sound::GetCounter(void)
cfavreau 0:c79e1f29f029 299 {
cfavreau 0:c79e1f29f029 300 return timer32_0_counter;
cfavreau 0:c79e1f29f029 301 }
cfavreau 0:c79e1f29f029 302
cfavreau 0:c79e1f29f029 303 void Sound::SetInstrument(int iChannel, int iWaveform, int iVolume, int iDecayTime, int iSlide, int iChangeAmount, int iChangeSpeed, int iRepeatSpeed, int iVibratoDepth, int iVibratoSpeed)
cfavreau 0:c79e1f29f029 304 {
cfavreau 0:c79e1f29f029 305 // Bounds check the channel
cfavreau 0:c79e1f29f029 306 if (iChannel < 0) return;
cfavreau 0:c79e1f29f029 307 if (iChannel >= SOUND_MAX_CHANNELS) return;
cfavreau 0:c79e1f29f029 308
cfavreau 0:c79e1f29f029 309 // Set the waveform
cfavreau 0:c79e1f29f029 310 channel[iChannel].use_noise = false;
cfavreau 0:c79e1f29f029 311 switch (iWaveform)
cfavreau 0:c79e1f29f029 312 {
cfavreau 0:c79e1f29f029 313 default:
cfavreau 0:c79e1f29f029 314 case (SOUND_SQUARE):
cfavreau 0:c79e1f29f029 315 channel[iChannel].pWaveform = (unsigned char *)square;
cfavreau 0:c79e1f29f029 316 break;
cfavreau 0:c79e1f29f029 317 case (SOUND_SAW):
cfavreau 0:c79e1f29f029 318 channel[iChannel].pWaveform = (unsigned char *)sawtooth;
cfavreau 0:c79e1f29f029 319 break;
cfavreau 0:c79e1f29f029 320 case (SOUND_TRIANGLE):
cfavreau 0:c79e1f29f029 321 channel[iChannel].pWaveform = (unsigned char *)triangle;
cfavreau 0:c79e1f29f029 322 break;
cfavreau 0:c79e1f29f029 323 case (SOUND_SINE):
cfavreau 0:c79e1f29f029 324 channel[iChannel].pWaveform = (unsigned char *)sine;
cfavreau 0:c79e1f29f029 325 break;
cfavreau 0:c79e1f29f029 326 case (SOUND_NOISE):
cfavreau 0:c79e1f29f029 327 channel[iChannel].pWaveform = (unsigned char *)square;
cfavreau 0:c79e1f29f029 328 channel[iChannel].use_noise = true;
cfavreau 0:c79e1f29f029 329 break;
cfavreau 0:c79e1f29f029 330 }
cfavreau 0:c79e1f29f029 331
cfavreau 0:c79e1f29f029 332 // Set the volume level
cfavreau 0:c79e1f29f029 333 channel[iChannel].vol_level = iVolume;
cfavreau 0:c79e1f29f029 334 channel[iChannel].volume = iVolume;
cfavreau 0:c79e1f29f029 335
cfavreau 0:c79e1f29f029 336 // Set the Decay time
cfavreau 0:c79e1f29f029 337 channel[iChannel].decay_amount = iDecayTime;
cfavreau 0:c79e1f29f029 338
cfavreau 0:c79e1f29f029 339 // Set the Slide value
cfavreau 0:c79e1f29f029 340 channel[iChannel].slide = iSlide;
cfavreau 0:c79e1f29f029 341
cfavreau 0:c79e1f29f029 342 // Set the Change Amount and Change Speed
cfavreau 0:c79e1f29f029 343 int inc = (65535 * iChangeAmount / PWM_FREQUENCY);
cfavreau 0:c79e1f29f029 344 channel[iChannel].change_inc = (signed short)inc; // frequency amount in phase increments
cfavreau 0:c79e1f29f029 345 channel[iChannel].change_speed = iChangeSpeed; // change delay in 50 hz ticks
cfavreau 0:c79e1f29f029 346 channel[iChannel].change_wait = iChangeSpeed;
cfavreau 0:c79e1f29f029 347
cfavreau 0:c79e1f29f029 348 // Set the repeat speed
cfavreau 0:c79e1f29f029 349 channel[iChannel].repeat_speed = iRepeatSpeed; // effect repeat speed in 50 hz ticks (really a delay)
cfavreau 0:c79e1f29f029 350 channel[iChannel].repeat_wait = iRepeatSpeed;
cfavreau 0:c79e1f29f029 351
cfavreau 0:c79e1f29f029 352 // Set the vibrato parameters
cfavreau 0:c79e1f29f029 353 channel[iChannel].vibrato_depth = iVibratoDepth; // Vibrato amplitude
cfavreau 0:c79e1f29f029 354 channel[iChannel].vibrato_speed = iVibratoSpeed; // Vibrato speed in 50 Hz ticks (delay)
cfavreau 0:c79e1f29f029 355 channel[iChannel].vibrato_phase = 0;
cfavreau 0:c79e1f29f029 356 channel[iChannel].vibrato_inc = 0;
cfavreau 0:c79e1f29f029 357 }
cfavreau 0:c79e1f29f029 358
cfavreau 0:c79e1f29f029 359 // Setup the default instruments and stuff
cfavreau 0:c79e1f29f029 360 void Sound::SetupDefaults(void)
cfavreau 0:c79e1f29f029 361 {
cfavreau 0:c79e1f29f029 362 for (int i = 0; i < SOUND_MAX_CHANNELS; i++)
cfavreau 0:c79e1f29f029 363 {
cfavreau 0:c79e1f29f029 364 // Set the default instrument parameters
cfavreau 0:c79e1f29f029 365 SetInstrument(i, SOUND_SQUARE, 255, 0, 0, 0, 0, 0, 0, 0);
cfavreau 0:c79e1f29f029 366 }
cfavreau 0:c79e1f29f029 367 }
cfavreau 0:c79e1f29f029 368
cfavreau 0:c79e1f29f029 369 void Sound::DoEffects(void)
cfavreau 0:c79e1f29f029 370 {
cfavreau 0:c79e1f29f029 371 for (int i = 0; i < SOUND_MAX_CHANNELS; i++)
cfavreau 0:c79e1f29f029 372 {
cfavreau 0:c79e1f29f029 373
cfavreau 0:c79e1f29f029 374 if (!channel[i].active) continue;
cfavreau 0:c79e1f29f029 375
cfavreau 0:c79e1f29f029 376 // Set the play length (0 to 255, 0 = INFINITE, 1 to 255 delay in 50 Hz ticks)
cfavreau 0:c79e1f29f029 377 if (channel[i].play_length > 0)
cfavreau 0:c79e1f29f029 378 {
cfavreau 0:c79e1f29f029 379 // Subtract 1 tick
cfavreau 0:c79e1f29f029 380 channel[i].play_length--;
cfavreau 0:c79e1f29f029 381 // If we get to 0 set the channel to inactive
cfavreau 0:c79e1f29f029 382 if (channel[i].play_length == 0) channel[i].active = false;
cfavreau 0:c79e1f29f029 383 }
cfavreau 0:c79e1f29f029 384
cfavreau 0:c79e1f29f029 385 // Volume Related Effects
cfavreau 0:c79e1f29f029 386 if (channel[i].volume > 0)
cfavreau 0:c79e1f29f029 387 {
cfavreau 0:c79e1f29f029 388 short int vol = channel[i].volume;
cfavreau 0:c79e1f29f029 389 // Decay
cfavreau 0:c79e1f29f029 390 if (channel[i].decay_amount)
cfavreau 0:c79e1f29f029 391 {
cfavreau 0:c79e1f29f029 392 if (channel[i].decay_amount < vol)
cfavreau 0:c79e1f29f029 393 vol -= channel[i].decay_amount;
cfavreau 0:c79e1f29f029 394 else
cfavreau 0:c79e1f29f029 395 vol = 0;
cfavreau 0:c79e1f29f029 396 }
cfavreau 0:c79e1f29f029 397 // Set the adjusted volume level
cfavreau 0:c79e1f29f029 398 channel[i].volume = vol;
cfavreau 0:c79e1f29f029 399 }
cfavreau 0:c79e1f29f029 400 // If the volume is 0 then who cares?
cfavreau 0:c79e1f29f029 401 // Do not turn the channel off as Repeat will not work?
cfavreau 0:c79e1f29f029 402 /*
cfavreau 0:c79e1f29f029 403 if (channel[i].volume == 0)
cfavreau 0:c79e1f29f029 404 {
cfavreau 0:c79e1f29f029 405 // Turn this channel off for now
cfavreau 0:c79e1f29f029 406 channel[i].active = false;
cfavreau 0:c79e1f29f029 407 }
cfavreau 0:c79e1f29f029 408 */
cfavreau 0:c79e1f29f029 409
cfavreau 0:c79e1f29f029 410 // Slide (Frequency Shifting) Effects
cfavreau 0:c79e1f29f029 411 if (channel[i].slide != 0)
cfavreau 0:c79e1f29f029 412 {
cfavreau 0:c79e1f29f029 413 int inc = (int)channel[i].phase_inc + ((int)channel[i].slide * SLIDE_SCALER);
cfavreau 0:c79e1f29f029 414 if ((inc > 0) && (inc < MAX_FREQ_INC))
cfavreau 0:c79e1f29f029 415 channel[i].phase_inc = inc;
cfavreau 0:c79e1f29f029 416 else
cfavreau 0:c79e1f29f029 417 //channel[i].active = false;
cfavreau 0:c79e1f29f029 418 channel[i].volume = 0;
cfavreau 0:c79e1f29f029 419 }
cfavreau 0:c79e1f29f029 420
cfavreau 0:c79e1f29f029 421 // Change (Frequency Change after Delay)
cfavreau 0:c79e1f29f029 422 if (channel[i].change_wait > 0)
cfavreau 0:c79e1f29f029 423 {
cfavreau 0:c79e1f29f029 424 channel[i].change_wait--;
cfavreau 0:c79e1f29f029 425 if (channel[i].change_wait == 0)
cfavreau 0:c79e1f29f029 426 channel[i].phase_inc += channel[i].change_inc;
cfavreau 0:c79e1f29f029 427 }
cfavreau 0:c79e1f29f029 428
cfavreau 0:c79e1f29f029 429 // Vibrato
cfavreau 0:c79e1f29f029 430 if (channel[i].vibrato_speed > 0)
cfavreau 0:c79e1f29f029 431 {
cfavreau 0:c79e1f29f029 432 // Calculate the vibrato waveform's phase
cfavreau 0:c79e1f29f029 433 channel[i].vibrato_phase += (channel[i].vibrato_speed * SLIDE_SCALER);
cfavreau 0:c79e1f29f029 434 // Calculate the sinewave values
cfavreau 0:c79e1f29f029 435 int inc = sinetable[channel[i].vibrato_phase & 63] * channel[i].vibrato_depth;
cfavreau 0:c79e1f29f029 436 // Rescale (from 65535 to 255)
cfavreau 0:c79e1f29f029 437 inc = inc >> 8;
cfavreau 0:c79e1f29f029 438
cfavreau 0:c79e1f29f029 439 inc = inc + (int)channel[i].phase_inc;
cfavreau 0:c79e1f29f029 440 channel[i].phase_inc = inc;
cfavreau 0:c79e1f29f029 441 }
cfavreau 0:c79e1f29f029 442
cfavreau 0:c79e1f29f029 443 // Repeat (resets parameters after a certain mount of time ... pew pew pew)
cfavreau 0:c79e1f29f029 444 if (channel[i].repeat_speed > 0)
cfavreau 0:c79e1f29f029 445 {
cfavreau 0:c79e1f29f029 446 channel[i].repeat_wait--;
cfavreau 0:c79e1f29f029 447 if (channel[i].repeat_wait == 0)
cfavreau 0:c79e1f29f029 448 {
cfavreau 0:c79e1f29f029 449 // Reset the repeat wait (so we can do this again!
cfavreau 0:c79e1f29f029 450 channel[i].repeat_wait = channel[i].repeat_speed;
cfavreau 0:c79e1f29f029 451 // Reset the frequency and amplitude to the original
cfavreau 0:c79e1f29f029 452 channel[i].phase_inc = channel[i].orig_phase_inc;
cfavreau 0:c79e1f29f029 453 // Reset the volume (amplitude) to the original
cfavreau 0:c79e1f29f029 454 channel[i].volume = channel[i].vol_level;
cfavreau 0:c79e1f29f029 455 }
cfavreau 0:c79e1f29f029 456 }
cfavreau 0:c79e1f29f029 457 }
cfavreau 0:c79e1f29f029 458 }
cfavreau 0:c79e1f29f029 459
cfavreau 0:c79e1f29f029 460 void Sound::PlaySong(char *pRTTTL, int iChannel, int iBPM, bool bLoop)
cfavreau 0:c79e1f29f029 461 {
cfavreau 0:c79e1f29f029 462 // Check the RTTTL pointer
cfavreau 0:c79e1f29f029 463 if (!pRTTTL) return;
cfavreau 0:c79e1f29f029 464
cfavreau 0:c79e1f29f029 465 // Stop the song temporarily
cfavreau 0:c79e1f29f029 466 bPlaySong = false;
cfavreau 0:c79e1f29f029 467
cfavreau 0:c79e1f29f029 468 // Setup the song variables
cfavreau 0:c79e1f29f029 469 iSongChannel = iChannel;
cfavreau 0:c79e1f29f029 470 SetSongTempo(iBPM);
cfavreau 0:c79e1f29f029 471 bLoopSong = bLoop;
cfavreau 0:c79e1f29f029 472 iNoteCounter = 0;
cfavreau 0:c79e1f29f029 473
cfavreau 0:c79e1f29f029 474 // Get the length of this song (in characters)
cfavreau 0:c79e1f29f029 475 int iNewLen = strlen(pRTTTL);
cfavreau 0:c79e1f29f029 476
cfavreau 0:c79e1f29f029 477 // Copy and allocate the song buffer
cfavreau 0:c79e1f29f029 478
cfavreau 0:c79e1f29f029 479 // Deal with the song buffer if it has already allocated memory
cfavreau 0:c79e1f29f029 480 if (m_pSongBuffer)
cfavreau 0:c79e1f29f029 481 {
cfavreau 0:c79e1f29f029 482 // Check to see if we need to allocate more memory
cfavreau 0:c79e1f29f029 483 if (iSongBufferLen < iNewLen)
cfavreau 0:c79e1f29f029 484 {
cfavreau 0:c79e1f29f029 485 // Cleanup the old and allocate a new buffer
cfavreau 0:c79e1f29f029 486 delete [] m_pSongBuffer;
cfavreau 0:c79e1f29f029 487 m_pSongBuffer = NULL;
cfavreau 0:c79e1f29f029 488 }
cfavreau 0:c79e1f29f029 489 }
cfavreau 0:c79e1f29f029 490 // Allocate a new song buffer if we need to
cfavreau 0:c79e1f29f029 491 if (!m_pSongBuffer)
cfavreau 0:c79e1f29f029 492 {
cfavreau 0:c79e1f29f029 493 m_pSongBuffer = new char[iNewLen + 1];
cfavreau 0:c79e1f29f029 494 }
cfavreau 0:c79e1f29f029 495 // Copy the song into the song buffer
cfavreau 0:c79e1f29f029 496 memcpy(m_pSongBuffer, pRTTTL, iNewLen);
cfavreau 0:c79e1f29f029 497 // Add an extra NULL terminator just incase
cfavreau 0:c79e1f29f029 498 m_pSongBuffer[iNewLen] = 0;
cfavreau 0:c79e1f29f029 499
cfavreau 0:c79e1f29f029 500 // Set the song pointer to the beginning
cfavreau 0:c79e1f29f029 501 m_pSong = m_pSongBuffer;
cfavreau 0:c79e1f29f029 502
cfavreau 0:c79e1f29f029 503 // Start the song
cfavreau 0:c79e1f29f029 504 bPlaySong = true;
cfavreau 0:c79e1f29f029 505 }
cfavreau 0:c79e1f29f029 506
cfavreau 0:c79e1f29f029 507 #define TICKSPERMINUTE (60 * 50)
cfavreau 0:c79e1f29f029 508
cfavreau 0:c79e1f29f029 509 void Sound::SetSongTempo(int iBPM)
cfavreau 0:c79e1f29f029 510 {
cfavreau 0:c79e1f29f029 511 // Setup the note wait time in 50Hz ticks
cfavreau 0:c79e1f29f029 512 iTicksPerBeat = TICKSPERMINUTE / iBPM;
cfavreau 0:c79e1f29f029 513 if (iTicksPerBeat < 1) iTicksPerBeat = 1;
cfavreau 0:c79e1f29f029 514 if (iTicksPerBeat > TICKSPERMINUTE) iTicksPerBeat = TICKSPERMINUTE;
cfavreau 0:c79e1f29f029 515 }
cfavreau 0:c79e1f29f029 516
cfavreau 0:c79e1f29f029 517 void Sound::TransposeSong(int iOffset)
cfavreau 0:c79e1f29f029 518 {
cfavreau 0:c79e1f29f029 519 iSongNoteOffset = iOffset;
cfavreau 0:c79e1f29f029 520 }
cfavreau 0:c79e1f29f029 521
cfavreau 0:c79e1f29f029 522 void Sound::StopSong(void)
cfavreau 0:c79e1f29f029 523 {
cfavreau 0:c79e1f29f029 524 // clear the play sound
cfavreau 0:c79e1f29f029 525 bPlaySong = false;
cfavreau 0:c79e1f29f029 526 // Silence
cfavreau 0:c79e1f29f029 527 StopSound(iSongChannel);
cfavreau 0:c79e1f29f029 528 // All done with this function
cfavreau 0:c79e1f29f029 529 return;
cfavreau 0:c79e1f29f029 530 }
cfavreau 0:c79e1f29f029 531
cfavreau 0:c79e1f29f029 532 void Sound::DoSong(void)
cfavreau 0:c79e1f29f029 533 {
cfavreau 0:c79e1f29f029 534 // Check to see if we are playing a song
cfavreau 0:c79e1f29f029 535 if (!bPlaySong) return;
cfavreau 0:c79e1f29f029 536
cfavreau 0:c79e1f29f029 537 // Check to see if there is a song to play
cfavreau 0:c79e1f29f029 538 if (!m_pSong) return;
cfavreau 0:c79e1f29f029 539
cfavreau 0:c79e1f29f029 540 // Check to see if we need to play the next note
cfavreau 0:c79e1f29f029 541 iNoteCounter--;
cfavreau 0:c79e1f29f029 542 if (iNoteCounter > 0) return;
cfavreau 0:c79e1f29f029 543
cfavreau 0:c79e1f29f029 544 int duration = 4;
cfavreau 0:c79e1f29f029 545 int octave = 5;
cfavreau 0:c79e1f29f029 546 int freq = NOTE_C4;
cfavreau 0:c79e1f29f029 547 char note = 'c';
cfavreau 0:c79e1f29f029 548 bool bSharp = false;
cfavreau 0:c79e1f29f029 549 bool bDotted = false;
cfavreau 0:c79e1f29f029 550 int volume = channel[iSongChannel].volume;
cfavreau 0:c79e1f29f029 551 while (*m_pSong != ',')
cfavreau 0:c79e1f29f029 552 {
cfavreau 0:c79e1f29f029 553 // Parse and play a note
cfavreau 0:c79e1f29f029 554 // Check for duration
cfavreau 0:c79e1f29f029 555 if (ISNUMBER(*m_pSong))
cfavreau 0:c79e1f29f029 556 {
cfavreau 0:c79e1f29f029 557 duration = *m_pSong - '0';
cfavreau 0:c79e1f29f029 558 m_pSong++;
cfavreau 0:c79e1f29f029 559 if (ISNUMBER(*m_pSong))
cfavreau 0:c79e1f29f029 560 {
cfavreau 0:c79e1f29f029 561 duration = (duration * 10) + (*m_pSong - '0');
cfavreau 0:c79e1f29f029 562 m_pSong++;
cfavreau 0:c79e1f29f029 563 }
cfavreau 0:c79e1f29f029 564 }
cfavreau 0:c79e1f29f029 565 // Check for the note
cfavreau 0:c79e1f29f029 566 if (ISALPHA(*m_pSong))
cfavreau 0:c79e1f29f029 567 {
cfavreau 0:c79e1f29f029 568 TOLOWER(*m_pSong);
cfavreau 0:c79e1f29f029 569 note = *m_pSong;
cfavreau 0:c79e1f29f029 570 m_pSong++;
cfavreau 0:c79e1f29f029 571 }
cfavreau 0:c79e1f29f029 572 // Check for a sharp
cfavreau 0:c79e1f29f029 573 if (*m_pSong == '#') { bSharp = true; m_pSong++; }
cfavreau 0:c79e1f29f029 574 // Check for dotted
cfavreau 0:c79e1f29f029 575 if (*m_pSong == '.') { bDotted = true; m_pSong++; }
cfavreau 0:c79e1f29f029 576 // Check for the octave
cfavreau 0:c79e1f29f029 577 if (ISNUMBER(*m_pSong))
cfavreau 0:c79e1f29f029 578 {
cfavreau 0:c79e1f29f029 579 octave = *m_pSong - '0';
cfavreau 0:c79e1f29f029 580 m_pSong++;
cfavreau 0:c79e1f29f029 581 }
cfavreau 0:c79e1f29f029 582 // Check to see if we need to start over
cfavreau 0:c79e1f29f029 583 if (*m_pSong == 0)
cfavreau 0:c79e1f29f029 584 {
cfavreau 0:c79e1f29f029 585 if (bLoopSong)
cfavreau 0:c79e1f29f029 586 {
cfavreau 0:c79e1f29f029 587 // Set the song buffer to the beginning
cfavreau 0:c79e1f29f029 588 m_pSong = m_pSongBuffer;
cfavreau 0:c79e1f29f029 589 // and process the first note all over again
cfavreau 0:c79e1f29f029 590 continue;
cfavreau 0:c79e1f29f029 591 }
cfavreau 0:c79e1f29f029 592 else
cfavreau 0:c79e1f29f029 593 {
cfavreau 0:c79e1f29f029 594 StopSong();
cfavreau 0:c79e1f29f029 595 // All done with this function
cfavreau 0:c79e1f29f029 596 return;
cfavreau 0:c79e1f29f029 597 }
cfavreau 0:c79e1f29f029 598 }
cfavreau 0:c79e1f29f029 599 // Next character so we do not get stuck
cfavreau 0:c79e1f29f029 600 if (*m_pSong != ',') m_pSong++;
cfavreau 0:c79e1f29f029 601 // Check for silence
cfavreau 0:c79e1f29f029 602 if (note != 'p')
cfavreau 0:c79e1f29f029 603 {
cfavreau 0:c79e1f29f029 604 // Convert the note to a frequency
cfavreau 0:c79e1f29f029 605 freq = SongGetFreq(note, octave, bSharp);
cfavreau 0:c79e1f29f029 606 // Play the note
cfavreau 0:c79e1f29f029 607 PlaySound(iSongChannel, freq, volume + 1);
cfavreau 0:c79e1f29f029 608 }
cfavreau 0:c79e1f29f029 609 else
cfavreau 0:c79e1f29f029 610 {
cfavreau 0:c79e1f29f029 611 // Silence (Pause)
cfavreau 0:c79e1f29f029 612 StopSound(iSongChannel);
cfavreau 0:c79e1f29f029 613 }
cfavreau 0:c79e1f29f029 614
cfavreau 0:c79e1f29f029 615 // Set the note counter so we play the note for so many frames
cfavreau 0:c79e1f29f029 616 switch(duration)
cfavreau 0:c79e1f29f029 617 {
cfavreau 0:c79e1f29f029 618 case (1): iNoteCounter = 4 * iTicksPerBeat; break;
cfavreau 0:c79e1f29f029 619 case (2): iNoteCounter = 2 * iTicksPerBeat; break;
cfavreau 0:c79e1f29f029 620 default:
cfavreau 0:c79e1f29f029 621 case (4): iNoteCounter = iTicksPerBeat; break;
cfavreau 0:c79e1f29f029 622 case (8): iNoteCounter = iTicksPerBeat >> 1; break;
cfavreau 0:c79e1f29f029 623 case (16): iNoteCounter = iTicksPerBeat >> 2; break;
cfavreau 0:c79e1f29f029 624 case (32): iNoteCounter = iTicksPerBeat >> 3; break;
cfavreau 0:c79e1f29f029 625 }
cfavreau 0:c79e1f29f029 626 if (bDotted) iNoteCounter += iNoteCounter >> 1;
cfavreau 0:c79e1f29f029 627
cfavreau 0:c79e1f29f029 628 //char cTemp[32];
cfavreau 0:c79e1f29f029 629 //snprintf(cTemp, 32, "N(%c%c)D(%02d)O(%d)", note, bSharp ? '#' : ' ', duration, octave);
cfavreau 0:c79e1f29f029 630 //disp.drawString(0, 16, cTemp, DisplayN18::RED, DisplayN18::BLACK);
cfavreau 0:c79e1f29f029 631
cfavreau 0:c79e1f29f029 632 }
cfavreau 0:c79e1f29f029 633 m_pSong++;
cfavreau 0:c79e1f29f029 634 }
cfavreau 0:c79e1f29f029 635
cfavreau 0:c79e1f29f029 636 int Sound::SongGetFreq(char note, int octave, bool bSharp)
cfavreau 0:c79e1f29f029 637 {
cfavreau 0:c79e1f29f029 638 // Bounds check the octave
cfavreau 0:c79e1f29f029 639 if (octave < 4) octave = 4;
cfavreau 0:c79e1f29f029 640 if (octave > 6) octave = 6;
cfavreau 0:c79e1f29f029 641
cfavreau 0:c79e1f29f029 642 switch(octave)
cfavreau 0:c79e1f29f029 643 {
cfavreau 0:c79e1f29f029 644 case (4):
cfavreau 0:c79e1f29f029 645 switch(note)
cfavreau 0:c79e1f29f029 646 {
cfavreau 0:c79e1f29f029 647 case ('a'):
cfavreau 0:c79e1f29f029 648 if (bSharp) return NOTE_As4;
cfavreau 0:c79e1f29f029 649 return NOTE_A4;
cfavreau 0:c79e1f29f029 650 case ('b'):
cfavreau 0:c79e1f29f029 651 if (bSharp) return NOTE_C5;
cfavreau 0:c79e1f29f029 652 return NOTE_B4;
cfavreau 0:c79e1f29f029 653 case ('c'):
cfavreau 0:c79e1f29f029 654 if (bSharp) return NOTE_Cs4;
cfavreau 0:c79e1f29f029 655 return NOTE_C4;
cfavreau 0:c79e1f29f029 656 case ('d'):
cfavreau 0:c79e1f29f029 657 if (bSharp) return NOTE_Ds4;
cfavreau 0:c79e1f29f029 658 return NOTE_D4;
cfavreau 0:c79e1f29f029 659 case ('e'):
cfavreau 0:c79e1f29f029 660 if (bSharp) return NOTE_F4;
cfavreau 0:c79e1f29f029 661 return NOTE_E4;
cfavreau 0:c79e1f29f029 662 case ('f'):
cfavreau 0:c79e1f29f029 663 if (bSharp) return NOTE_Fs4;
cfavreau 0:c79e1f29f029 664 return NOTE_F4;
cfavreau 0:c79e1f29f029 665 case ('g'):
cfavreau 0:c79e1f29f029 666 if (bSharp) return NOTE_Gs4;
cfavreau 0:c79e1f29f029 667 return NOTE_G4;
cfavreau 0:c79e1f29f029 668 }
cfavreau 0:c79e1f29f029 669 case (5):
cfavreau 0:c79e1f29f029 670 switch(note)
cfavreau 0:c79e1f29f029 671 {
cfavreau 0:c79e1f29f029 672 case ('a'):
cfavreau 0:c79e1f29f029 673 if (bSharp) return NOTE_As5;
cfavreau 0:c79e1f29f029 674 return NOTE_A5;
cfavreau 0:c79e1f29f029 675 case ('b'):
cfavreau 0:c79e1f29f029 676 if (bSharp) return NOTE_C6;
cfavreau 0:c79e1f29f029 677 return NOTE_B5;
cfavreau 0:c79e1f29f029 678 case ('c'):
cfavreau 0:c79e1f29f029 679 if (bSharp) return NOTE_Cs5;
cfavreau 0:c79e1f29f029 680 return NOTE_C5;
cfavreau 0:c79e1f29f029 681 case ('d'):
cfavreau 0:c79e1f29f029 682 if (bSharp) return NOTE_Ds5;
cfavreau 0:c79e1f29f029 683 return NOTE_D5;
cfavreau 0:c79e1f29f029 684 case ('e'):
cfavreau 0:c79e1f29f029 685 if (bSharp) return NOTE_F5;
cfavreau 0:c79e1f29f029 686 return NOTE_E5;
cfavreau 0:c79e1f29f029 687 case ('f'):
cfavreau 0:c79e1f29f029 688 if (bSharp) return NOTE_Fs5;
cfavreau 0:c79e1f29f029 689 return NOTE_F5;
cfavreau 0:c79e1f29f029 690 case ('g'):
cfavreau 0:c79e1f29f029 691 if (bSharp) return NOTE_Gs5;
cfavreau 0:c79e1f29f029 692 return NOTE_G5;
cfavreau 0:c79e1f29f029 693 }
cfavreau 0:c79e1f29f029 694 case (6):
cfavreau 0:c79e1f29f029 695 switch(note)
cfavreau 0:c79e1f29f029 696 {
cfavreau 0:c79e1f29f029 697 case ('a'):
cfavreau 0:c79e1f29f029 698 if (bSharp) return NOTE_As6;
cfavreau 0:c79e1f29f029 699 return NOTE_A6;
cfavreau 0:c79e1f29f029 700 case ('b'):
cfavreau 0:c79e1f29f029 701 if (bSharp) return NOTE_C7;
cfavreau 0:c79e1f29f029 702 return NOTE_B6;
cfavreau 0:c79e1f29f029 703 case ('c'):
cfavreau 0:c79e1f29f029 704 if (bSharp) return NOTE_Cs6;
cfavreau 0:c79e1f29f029 705 return NOTE_C6;
cfavreau 0:c79e1f29f029 706 case ('d'):
cfavreau 0:c79e1f29f029 707 if (bSharp) return NOTE_Ds6;
cfavreau 0:c79e1f29f029 708 return NOTE_D6;
cfavreau 0:c79e1f29f029 709 case ('e'):
cfavreau 0:c79e1f29f029 710 if (bSharp) return NOTE_F6;
cfavreau 0:c79e1f29f029 711 return NOTE_E6;
cfavreau 0:c79e1f29f029 712 case ('f'):
cfavreau 0:c79e1f29f029 713 if (bSharp) return NOTE_Fs6;
cfavreau 0:c79e1f29f029 714 return NOTE_F6;
cfavreau 0:c79e1f29f029 715 case ('g'):
cfavreau 0:c79e1f29f029 716 if (bSharp) return NOTE_Gs6;
cfavreau 0:c79e1f29f029 717 return NOTE_G6;
cfavreau 0:c79e1f29f029 718 }
cfavreau 0:c79e1f29f029 719 }
cfavreau 0:c79e1f29f029 720
cfavreau 0:c79e1f29f029 721 return NOTE_C7;
cfavreau 0:c79e1f29f029 722 }
cfavreau 0:c79e1f29f029 723
cfavreau 0:c79e1f29f029 724 void Sound:: Mute(bool mute)
cfavreau 0:c79e1f29f029 725 {
cfavreau 0:c79e1f29f029 726 bMuted = mute;
cfavreau 0:c79e1f29f029 727 }