oPossum 1,083 Posted April 13, 2012 Share Posted April 13, 2012 Plays ringtones in the RTTTL format. That is a text string for simple monophonic music. The parser is very simple and does not attempt strict compliance with the specification. It should properly play strings that are written to spec. The output is a sine wave constructed using 48 kHz PWM on P1.6 that should be low pass filtered by a simple R/C filter. Code was tested on G2553 - not sure if this will fit in the 2K chips - it should work if it fits. #include #include // // RTTTL spec: http://www.mobilefish.com/tutorials/rtttl/rtttl_quickguide_specification.html // static const long int phase_limit = 48048 * 1000; // Phase accumulator limit, sample rate * 1/resolution static volatile long int pi = 0; // Phase increment (frequency in millihertz) static volatile unsigned note_timer = 0; // Note timer, decrements 48048 times per second static int isnote(char c) // Get note index, or 128 for pause { // // A B C D E F G H( static const int note_map[8] = { 9, 11, 0, 2, 4, 5, 7, 11 }; // MIDI note value if(c >= 'a' && c <= 'h') return note_map[c - 'a']; // Lower case note if(c >= 'A' && c <= 'H') return note_map[c - 'A']; // Upper case note if(c == 'p' || c == 'P') return 128; // Pause return -1; // Invalid, return -1 } static void play_note(unsigned n, unsigned d) // Play note { // n: MIDI note value // d: duration in units of 1/48048 second // static const unsigned note_spacing = 480; // 10 ms between notes static const unsigned long midi_notes[12] = { // MIDI upper 12 notes 6644875L, // 116 G# 9 7040000L, // 117 A 9 7458620L, // 118 A# 9 7902133L, // 119 B 9 8372018L, // 120 C 10 8869844L, // 121 C# 10 9397273L, // 122 D 10 9956063L, // 123 D# 10 10548082L, // 124 E 10 11175303L, // 125 F 10 11839822L, // 126 F# 10 12543854L // 127 G 10 }; // long f; // if(n > 127) { // Invalid note value is pause f = 0; // Frequency = 0 } else { // unsigned o = 10; n += 4; // Init octave, adjust note while(n >= 12) n -= 12, --o; // Normalize note, adjust octave f = midi_notes[n]; // Get frequency while(o) f >>= 1, --o; // Adjust frequency for octave } // while(note_timer); // Wait for current note to finish // pi = 0; // Silence between notes note_timer = note_spacing; // while(note_timer); // // if(d > note_spacing) d -= note_spacing; // Adjust note duration for note spacing note_timer = d; // Setup note time pi = f - phase_limit; // Play note } void play(const char *s) // Play RTTTL string { // unsigned state = 0; // Initial state char c, d; // Char from string, default specifier unsigned bpm = 63; // Beats (quarter notes) per minute unsigned default_duration = 4; // Default note duration (fraction of a whole note) unsigned default_octave = 5; // Default octave long unsigned dot; // Numerator for note clock count calculation, adjusted for dot notation unsigned duration = 0; // Note duration int note; // Note index 0 to 11 int octave; // Octave 0 to 9 // do { // c = *s++; // Get a char from RTTTL string if(c > 0 && c <= 32) continue; // Ingore whitespace and control chars switch(state) { // case 0: // - Skip name if(c == ':') ++state; // Toss chars until ':' break; // case 1: // - Parse defaults state1: // if(c == ':') { // End of default section, begin parsing notes state = 4; // } else if(isalpha(c)) { // Default designator - allow any alpha d = c; // Save it state = 2; // Verify assignment character } else { // state = 1; // Invalid char, stay in this state } // break; // case 2: // - Default assignment char, must be '=' if(c == '=') { // note = 0; // Init default ++state; // Parse default value } else goto state1; // Invalid char, start over break; // case 3: // - Parse and assign default value if(isdigit(c)) { // Update default note = note * 10 + (c - '0'); // } else if(c == ',' || c == ':') { // End of default, assign to variable switch(d) { // case 'b': // Default BPM case 'B': // bpm = note; // break; // case 'o': // Default octave case 'O': // default_octave = note; // break; // case 'd': // Default note duration case 'D': // default_duration = note;// break; // } // state = (c == ':') ? 4 : 1; // Get next default or begin parsing notes } else goto state1; // Invalid char, start over break; // case 4: // - Parse note dot = 48048L * 60 * 4; // Setup defaults for this note duration = default_duration; // octave = default_octave; // note = 0; // if(isdigit(c)) { // May begin with duration duration = c - '0'; // Init duration ++state; // May be more than one digit, continue in next state } else if((note = isnote(c)) >= 0) {// May begin with note state = 6; // Got note, parse modifiers and octave } // break; // case 5: // - Parse duration if(isdigit(c)) { // Numeric digit, update duration duration = duration * 10 + (c -'0'); } else if((note = isnote(c)) >= 0) {// Got note, parse modifiers and octave ++state; // } else { // Invlid char, start over state = 4; // } // break; // case 6: // - Parse modifiers and octave if(c == ',' || c == 0) { // End of note, play it play_note((octave + 1) * 12 + note, dot / (duration * bpm)); state = 4; // Next note } else if(c == '#') { // Sharp, increment note value ++note; // } else if(c == 'b') { // Flat, decrement note value --note; // } else if(c == '.') { // Dotted note, extend length by 50% dot += (dot >> 1); // } else if(isdigit(c)) { // Octave, 0 to 9 allowed, should be 4 to 7 octave = c - '0'; // } // break; // // } // } while(c); // End of string while(note_timer); // Wait for last note to finish pi = 0; // Silence } static const signed char sine[734] = { // 8 bit signed sine wave samples 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 73, 74, 75, 76, 77, 78, 79, 80, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 89, 89, 90, 91, 92, 92, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 101, 101, 102, 103, 103, 104, 105, 105, 106, 106, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 113, 113, 114, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 118, 119, 119, 120, 120, 120, 121, 121, 121, 122, 122, 122, 122, 123, 123, 123, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 124, 124, 124, 124, 123, 123, 123, 123, 122, 122, 122, 121, 121, 121, 120, 120, 120, 119, 119, 119, 118, 118, 117, 117, 117, 116, 116, 115, 115, 114, 114, 113, 113, 112, 112, 111, 111, 110, 110, 109, 109, 108, 107, 107, 106, 106, 105, 104, 104, 103, 102, 102, 101, 101, 100, 99, 99, 98, 97, 96, 96, 95, 94, 94, 93, 92, 91, 91, 90, 89, 88, 87, 87, 86, 85, 84, 83, 83, 82, 81, 80, 79, 78, 77, 77, 76, 75, 74, 73, 72, 71, 70, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -68, -69, -70, -71, -72, -73, -74, -75, -76, -76, -77, -78, -79, -80, -81, -82, -82, -83, -84, -85, -86, -86, -87, -88, -89, -90, -90, -91, -92, -93, -93, -94, -95, -96, -96, -97, -98, -98, -99, -100, -100, -101, -102, -102, -103, -104, -104, -105, -106, -106, -107, -107, -108, -108, -109, -110, -110, -111, -111, -112, -112, -113, -113, -114, -114, -115, -115, -116, -116, -116, -117, -117, -118, -118, -119, -119, -119, -120, -120, -120, -121, -121, -121, -122, -122, -122, -123, -123, -123, -123, -124, -124, -124, -124, -125, -125, -125, -125, -125, -126, -126, -126, -126, -126, -126, -126, -126, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -126, -126, -126, -126, -126, -126, -126, -125, -125, -125, -125, -125, -125, -124, -124, -124, -124, -123, -123, -123, -123, -122, -122, -122, -121, -121, -121, -120, -120, -120, -119, -119, -118, -118, -118, -117, -117, -116, -116, -115, -115, -115, -114, -114, -113, -113, -112, -112, -111, -111, -110, -109, -109, -108, -108, -107, -107, -106, -105, -105, -104, -103, -103, -102, -102, -101, -100, -100, -99, -98, -98, -97, -96, -95, -95, -94, -93, -92, -92, -91, -90, -89, -89, -88, -87, -86, -85, -85, -84, -83, -82, -81, -81, -80, -79, -78, -77, -76, -75, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -3, -2, -1, 0 }; #pragma vector = TIMER0_A1_VECTOR // Timer A0 Overflow interrupt __interrupt void timer0_a1_isr(void) // { // static long int pa = 0; // Phase accumulator static unsigned sample = 200; // PWM sample // TACCR1 = sample; // Output previous PWM sample // volatile unsigned z = TAIV; // Clear interrupt flag // sample = 200 - sine[pa >> 16]; // Get next PWM value // if((pa += pi) < 0) pa += phase_limit; // Update phase accumulator // if(note_timer) --note_timer; // Decrement note timer } // static const char * const tunes[] = { "StWars:d=4,o=5,b=180:8f,8f,8f,2a#.,2f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8d#6,2c6,p,8f,8f,8f,2a#.,2f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8d#6,2c6", "axelf:d=4,o=5,b=160:f#,8a.,8f#,16f#,8a#,8f#,8e,f#,8c.6,8f#,16f#,8d6,8c#6,8a,8f#,8c#6,8f#6,16f#,8e,16e,8c#,8g#,f#.", "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c", "Mission:d=4,o=6,b=100:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,16g,8p,16g,8p,16a#,16p,16c,16p,16g,8p,16g,8p,16f,16p,16f#,16p,16g,8p,16g,8p,16a#,16p,16c,16p,16g,8p,16g,8p,16f,16p,16f#,16p,16a#,16g,2d,32p,16a#,16g,2c#,32p,16a#,16g,2c,16p,16a#5,16c", "TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5", "Greensleaves:d=4,o=5,b=140:g,2a#,c6,d.6,8d#6,d6,2c6,a,f.,8g,a,2a#,g,g.,8f,g,2a,f,2d,g,2a#,c6,d.6,8e6,d6,2c6,a,f.,8g,a,a#.,8a,g,f#.,8e,f#,2g", 0 }; void main(void) { WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // // P1DIR = BIT6; // Enable PWM output on P1.6 P1SEL = BIT6; // // TACTL = TASSEL_2 | MC_1 | TAIE; // Timer A config: SMCLK, count up, ovrflow int enabled TACCR0 = 332; // Setup Timer A period for 48.048 kHz TACCR1 = 200; // Setup Timer A compare to "DAC" zero value TACCTL1 = OUTMOD_7; // Setup Timer A reset/set output mode // _EINT(); // Enable interrupts // const char * const *t; // for(; for(t = tunes; *t; ++t) { // Iterate all ring tones play(*t); // Play it note_timer = 48048; // Wait a second before next while(note_timer); // } // } dacoffey, gordon, bluehash and 3 others 6 Quote Link to post Share on other sites
dacoffey 5 Posted April 13, 2012 Share Posted April 13, 2012 This, like all of your projects, is very cool. You are one prolific possum. Quote Link to post Share on other sites
Rickta59 589 Posted April 13, 2012 Share Posted April 13, 2012 That is a great implementation. It makes nice smooth sine waves. I have a square wave RTTL player that works, but your version sounds much better. What values for resistor and capacitor would you recommend for a low pass filter if the intent was to make it input into an opamp? -rick Quote Link to post Share on other sites
oPossum 1,083 Posted April 13, 2012 Author Share Posted April 13, 2012 The PWM frequency is 48 kHz, that is essentially the sample rate. So the cutoff frequency should be less than half that. The highest frequency the code can generate is aprox 12.5 kHz, so the cutoff frequency should be above that. The output current of the MSP430 IO pin should be less than 5 mA, so the resistor should be no less than 820 ohms. The output impedance should not be too high, so the resistor should be 10k or less. So cutoff frequency of 15 to 20 kHz and resistance of 820 ohm to 10 k. Fc = 1 / (2 * Pi * R * C) Some common values that will work well... 820 12n ~16.2 kHz 1k0 10n ~15.9 kHz 2k2 4n7 ~15.4 kHz 3k3 3n3 ~14.6 kHz 4k7 2n2 ~15.4 kHz 6k8 1n5 ~15.6 kHz 8k2 1n2 ~16.2 kHz 10k 1n0 ~15.9 kHz Quote Link to post Share on other sites
oPossum 1,083 Posted April 14, 2012 Author Share Posted April 14, 2012 Updated with 256 step envelope #include #include // // RTTTL spec: http://www.mobilefish.com/tutorials/rtttl/rtttl_quickguide_specification.html // static const long int phase_limit = 48048 * 1000; // Phase accumulator limit, sample rate * 1/resolution static volatile long int pi = 0; // Phase increment (frequency in millihertz) static volatile unsigned note_env = 0; // Note envelope, increments 48048 times per second static volatile unsigned long note_timer = 0; // Note timer, decrements 48048 times per second static int isnote(char c) // Get note index, or 128 for pause { // // A B C D E F G H( static const int note_map[8] = { 9, 11, 0, 2, 4, 5, 7, 11 }; // MIDI note value if(c >= 'a' && c <= 'h') return note_map[c - 'a']; // Lower case note if(c >= 'A' && c <= 'H') return note_map[c - 'A']; // Upper case note if(c == 'p' || c == 'P') return 128; // Pause return -1; // Invalid, return -1 } static void play_note(unsigned n, unsigned long d) // Play note { // n: MIDI note value // d: duration in units of 1/48048 second // //static const unsigned note_spacing = 480; // 10 ms between notes static const unsigned note_spacing = 0; static const unsigned long midi_notes[12] = { // MIDI upper 12 notes 6644875L, // 116 G# 9 7040000L, // 117 A 9 7458620L, // 118 A# 9 7902133L, // 119 B 9 8372018L, // 120 C 10 8869844L, // 121 C# 10 9397273L, // 122 D 10 9956063L, // 123 D# 10 10548082L, // 124 E 10 11175303L, // 125 F 10 11839822L, // 126 F# 10 12543854L // 127 G 10 }; // long f; // if(n > 127) { // Invalid note value is pause f = 0; // Frequency = 0 } else { // unsigned o = 10; n += 4; // Init octave, adjust note while(n >= 12) n -= 12, --o; // Normalize note, adjust octave f = midi_notes[n]; // Get frequency while(o) f >>= 1, --o; // Adjust frequency for octave } // while(note_timer); // Wait for current note to finish // if(note_spacing) { // Silence between notes pi = 0; // note_timer = note_spacing; // while(note_timer); // } // // if(d > note_spacing) d -= note_spacing; // Adjust note duration for note spacing note_timer = d; // Setup note time if(f) note_env = 0; // Setup envelope pi = f - phase_limit; // Play note } void play(const char *s) // Play RTTTL string { // unsigned state = 0; // Initial state char c, d; // Char from string, default specifier unsigned bpm = 63; // Beats (quarter notes) per minute unsigned default_duration = 4; // Default note duration (fraction of a whole note) unsigned default_octave = 5; // Default octave long unsigned dot; // Numerator for note clock count calculation, adjusted for dot notation unsigned duration = 0; // Note duration int note; // Note index 0 to 11 int octave; // Octave 0 to 9 // do { // c = *s++; // Get a char from RTTTL string if(c > 0 && c <= 32) continue; // Ingore whitespace and control chars switch(state) { // case 0: // - Skip name if(c == ':') ++state; // Toss chars until ':' break; // case 1: // - Parse defaults state1: // if(c == ':') { // End of default section, begin parsing notes state = 4; // } else if(isalpha(c)) { // Default designator - allow any alpha d = c; // Save it state = 2; // Verify assignment character } else { // state = 1; // Invalid char, stay in this state } // break; // case 2: // - Default assignment char, must be '=' if(c == '=') { // note = 0; // Init default ++state; // Parse default value } else goto state1; // Invalid char, start over break; // case 3: // - Parse and assign default value if(isdigit(c)) { // Update default note = note * 10 + (c - '0'); // } else if(c == ',' || c == ':') { // End of default, assign to variable switch(d) { // case 'b': // Default BPM case 'B': // bpm = note; // break; // case 'o': // Default octave case 'O': // default_octave = note; // break; // case 'd': // Default note duration case 'D': // default_duration = note;// break; // } // state = (c == ':') ? 4 : 1; // Get next default or begin parsing notes } else goto state1; // Invalid char, start over break; // case 4: // - Parse note dot = 48048L * 60 * 4; // Setup defaults for this note duration = default_duration; // octave = default_octave; // note = 0; // if(isdigit(c)) { // May begin with duration duration = c - '0'; // Init duration ++state; // May be more than one digit, continue in next state } else if((note = isnote(c)) >= 0) {// May begin with note state = 6; // Got note, parse modifiers and octave } // break; // case 5: // - Parse duration if(isdigit(c)) { // Numeric digit, update duration duration = duration * 10 + (c -'0'); } else if((note = isnote(c)) >= 0) {// Got note, parse modifiers and octave ++state; // } else { // Invlid char, start over state = 4; // } // break; // case 6: // - Parse modifiers and octave if(c == ',' || c == 0) { // End of note, play it play_note((octave + 1) * 12 + note, dot / (duration * bpm)); state = 4; // Next note } else if(c == '#') { // Sharp, increment note value ++note; // } else if(c == 'b') { // Flat, decrement note value --note; // } else if(c == '.') { // Dotted note, extend length by 50% dot += (dot >> 1); // } else if(isdigit(c)) { // Octave, 0 to 9 allowed, should be 4 to 7 octave = c - '0'; // } // break; // // } // } while(c); // End of string while(note_timer); // Wait for last note to finish pi = 0; // Silence } static const signed char sine[734] = { // 8 bit signed sine wave samples 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 73, 74, 75, 76, 77, 78, 79, 80, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 89, 89, 90, 91, 92, 92, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 101, 101, 102, 103, 103, 104, 105, 105, 106, 106, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 113, 113, 114, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 118, 119, 119, 120, 120, 120, 121, 121, 121, 122, 122, 122, 122, 123, 123, 123, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 124, 124, 124, 124, 123, 123, 123, 123, 122, 122, 122, 121, 121, 121, 120, 120, 120, 119, 119, 119, 118, 118, 117, 117, 117, 116, 116, 115, 115, 114, 114, 113, 113, 112, 112, 111, 111, 110, 110, 109, 109, 108, 107, 107, 106, 106, 105, 104, 104, 103, 102, 102, 101, 101, 100, 99, 99, 98, 97, 96, 96, 95, 94, 94, 93, 92, 91, 91, 90, 89, 88, 87, 87, 86, 85, 84, 83, 83, 82, 81, 80, 79, 78, 77, 77, 76, 75, 74, 73, 72, 71, 70, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -68, -69, -70, -71, -72, -73, -74, -75, -76, -76, -77, -78, -79, -80, -81, -82, -82, -83, -84, -85, -86, -86, -87, -88, -89, -90, -90, -91, -92, -93, -93, -94, -95, -96, -96, -97, -98, -98, -99, -100, -100, -101, -102, -102, -103, -104, -104, -105, -106, -106, -107, -107, -108, -108, -109, -110, -110, -111, -111, -112, -112, -113, -113, -114, -114, -115, -115, -116, -116, -116, -117, -117, -118, -118, -119, -119, -119, -120, -120, -120, -121, -121, -121, -122, -122, -122, -123, -123, -123, -123, -124, -124, -124, -124, -125, -125, -125, -125, -125, -126, -126, -126, -126, -126, -126, -126, -126, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -126, -126, -126, -126, -126, -126, -126, -125, -125, -125, -125, -125, -125, -124, -124, -124, -124, -123, -123, -123, -123, -122, -122, -122, -121, -121, -121, -120, -120, -120, -119, -119, -118, -118, -118, -117, -117, -116, -116, -115, -115, -115, -114, -114, -113, -113, -112, -112, -111, -111, -110, -109, -109, -108, -108, -107, -107, -106, -105, -105, -104, -103, -103, -102, -102, -101, -100, -100, -99, -98, -98, -97, -96, -95, -95, -94, -93, -92, -92, -91, -90, -89, -89, -88, -87, -86, -85, -85, -84, -83, -82, -81, -81, -80, -79, -78, -77, -76, -75, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -3, -2, -1, 0 }; const unsigned char envelope[256] = { // 5.33 ms per entry #if 0 // Linear 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #endif #if 0 // Log 0.99 255, 252, 249, 247, 244, 242, 240, 237, 235, 232, 230, 228, 226, 223, 221, 219, 217, 214, 212, 210, 208, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 183, 181, 179, 177, 175, 174, 172, 170, 168, 167, 165, 163, 162, 160, 158, 157, 155, 154, 152, 151, 149, 148, 146, 145, 143, 142, 140, 139, 138, 136, 135, 134, 132, 131, 130, 128, 127, 126, 124, 123, 122, 121, 119, 118, 117, 116, 115, 114, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 86, 85, 84, 83, 82, 81, 81, 80, 79, 78, 77, 77, 76, 75, 74, 74, 73, 72, 71, 71, 70, 69, 69, 68, 67, 66, 66, 65, 65, 64, 63, 63, 62, 61, 61, 60, 59, 59, 58, 58, 57, 57, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 43, 42, 42, 41, 41, 40, 40, 40, 39, 39, 38, 38, 38, 37, 37, 37, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 32, 32, 32, 31, 31, 31, 30, 30, 30, 29, 29, 29, 29, 28, 28, 28, 27, 27, 27, 27, 26, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23, 22, 22, 22, 22, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 10, 0 #endif #if 1 // Log 0.98 255, 249, 244, 240, 235, 230, 225, 221, 216, 212, 208, 204, 200, 196, 192, 188, 184, 180, 177, 173, 170, 166, 163, 160, 157, 153, 150, 147, 144, 141, 139, 136, 133, 130, 128, 125, 123, 120, 118, 115, 113, 111, 109, 106, 104, 102, 100, 98, 96, 94, 92, 91, 89, 87, 85, 83, 82, 80, 79, 77, 75, 74, 72, 71, 69, 68, 67, 65, 64, 63, 61, 60, 59, 58, 57, 56, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 43, 42, 41, 40, 39, 38, 38, 37, 36, 35, 35, 34, 33, 33, 32, 31, 31, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 24, 24, 23, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, #endif }; #pragma vector = TIMER0_A1_VECTOR // Timer A0 Overflow interrupt __interrupt void timer0_a1_isr(void) // { // static long int pa = 0; // Phase accumulator static unsigned sample = 200; // PWM sample // TACCR1 = sample; // Output previous PWM sample // volatile unsigned z = TAIV; // Clear interrupt flag // // Get next PWM value sample = 200 - ((envelope[note_env >> 8] * sine[pa >> 16]) >> 8); // if((pa += pi) < 0) pa += phase_limit; // Update phase accumulator // if(note_env != 0xFFFF) ++note_env; // Increment envelope if not at maximum // if(note_timer) --note_timer; // Decrement note timer } // static const char * const tunes[] = { "Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6", "Popcorn:d=4,o=5,b=160:8c6,8a#,8c6,8g,8d#,8g,c,8c6,8a#,8c6,8g,8d#,8g,c,8c6,8d6,8d#6,16c6,8d#6,16c6,8d#6,8d6,16a#,8d6,16a#,8d6,8c6,8a#,8g,8a#,c6", "peanuts:d=4,o=5,b=160:f,8g,a,8a,8g,f,2g,f,p,f,8g,a,1a,2p,f,8g,a,8a,8g,f,2g,2f,2f,8g,1g", "PinkPanther:d=4,o=5,b=160:8d#,8e,2p,8f#,8g,2p,8d#,8e,16p,8f#,8g,16p,8c6,8b,16p,8d#,8e,16p,8b,2a#,2p,16a,16g,16e,16d,2e", "aadams:d=4,o=5,b=160:8c,f,8a,f,8c,b4,2g,8f,e,8g,e,8e4,a4,2f,8c,f,8a,f,8c,b4,2g,8f,e,8c,d,8e,1f,8c,8d,8e,8f,1p,8d,8e,8f#,8g,1p,8d,8e,8f#,8g,p,8d,8e,8f#,8g,p,8c,8d,8e,8f", "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c", //"StWars:d=4,o=5,b=180:8f,8f,8f,2a#.,2f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8d#6,2c6,p,8f,8f,8f,2a#.,2f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8d#6,2c6", //"Mission:d=4,o=6,b=100:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,16g,8p,16g,8p,16a#,16p,16c,16p,16g,8p,16g,8p,16f,16p,16f#,16p,16g,8p,16g,8p,16a#,16p,16c,16p,16g,8p,16g,8p,16f,16p,16f#,16p,16a#,16g,2d,32p,16a#,16g,2c#,32p,16a#,16g,2c,16p,16a#5,16c", 0 }; void main(void) { WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // // P1DIR = BIT6; // Enable PWM output on P1.6 P1SEL = BIT6; // // TACTL = TASSEL_2 | MC_1 | TAIE; // Timer A config: SMCLK, count up, ovrflow int enabled TACCR0 = 332; // Setup Timer A period for 48.048 kHz TACCR1 = 200; // Setup Timer A compare to "DAC" zero value TACCTL1 = OUTMOD_7; // Setup Timer A reset/set output mode // _EINT(); // Enable interrupts // const char * const *t; // for(; for(t = tunes; *t; ++t) { // Iterate all ring tones play(*t); // Play it note_timer = 48048; // Wait a second before next while(note_timer); // } // } Quote Link to post Share on other sites
oPossum 1,083 Posted April 14, 2012 Author Share Posted April 14, 2012 Square wave version. Changes TimerA period, does not use DDS. Can not go below octave 4 (withing spec, but parser allows lower octaves). #include #include // // RTTTL spec: http://www.mobilefish.com/tutorials/rtttl/rtttl_quickguide_specification.html // static volatile unsigned long note_timer = 0; // Note timer, decrements at note frequency static int isnote(char c) // Get note index, or 128 for pause { // // A B C D E F G H( static const int note_map[8] = { 9, 11, 0, 2, 4, 5, 7, 11 }; // MIDI note value if(c >= 'a' && c <= 'h') return note_map[c - 'a']; // Lower case note if(c >= 'A' && c <= 'H') return note_map[c - 'A']; // Upper case note if(c == 'p' || c == 'P') return 128; // Pause return -1; // Invalid, return -1 } static void play_note(unsigned n, unsigned long d) // Play note { // n: MIDI note value // d: duration in units of 1/16000000 second // static const unsigned note_spacing = 10; // 10 ms between notes static const unsigned long midi_notes[12] = { // MIDI upper 12 notes 664488L, // 116 G# 9 704000L, // 117 A 9 745862L, // 118 A# 9 790213L, // 119 B 9 837202L, // 120 C 10 886984L, // 121 C# 10 939727L, // 122 D 10 995606L, // 123 D# 10 1054808L, // 124 E 10 1117530L, // 125 F 10 1183982L, // 126 F# 10 1254385L // 127 G 10 }; // unsigned long f; // if(n > 127) { // Invalid note value is pause f = 0; // Frequency = 0 } else { // unsigned o = 10; n += 4; // Init octave, adjust note while(n >= 12) n -= 12, --o; // Normalize note, adjust octave f = midi_notes[n]; // Get frequency while(o) f >>= 1, --o; // Adjust frequency for octave } // const unsigned c = f ? (16000000UL * 100) / f : 16000; // TimerA period const unsigned t = d / c; // Note cycles // while(note_timer); // Wait for current note to finish // if(note_spacing) { // Silence between notes TACCTL1 = OUTMOD_0; // PWM off TACCR0 = 16000 - 1 ; // 1 ms period note_timer = note_spacing; // while(note_timer); // } // // note_timer = t; // Setup note time TACCR0 = c - 1; // TACCR1 = TACCR0 >> 1; // TACCTL1 = f ? OUTMOD_7 : OUTMOD_0; // PWM on for tone, off for pause } void play(const char *s) // Play RTTTL string { // unsigned state = 0; // Initial state char c, d; // Char from string, default specifier unsigned bpm = 63; // Beats (quarter notes) per minute unsigned default_duration = 4; // Default note duration (fraction of a whole note) unsigned default_octave = 5; // Default octave long unsigned dot; // Numerator for note clock count calculation, adjusted for dot notation unsigned duration = 0; // Note duration int note; // Note index 0 to 11 int octave; // Octave 0 to 9 // do { // c = *s++; // Get a char from RTTTL string if(c > 0 && c <= 32) continue; // Ingore whitespace and control chars switch(state) { // case 0: // - Skip name if(c == ':') ++state; // Toss chars until ':' break; // case 1: // - Parse defaults state1: // if(c == ':') { // End of default section, begin parsing notes state = 4; // } else if(isalpha(c)) { // Default designator - allow any alpha d = c; // Save it state = 2; // Verify assignment character } else { // state = 1; // Invalid char, stay in this state } // break; // case 2: // - Default assignment char, must be '=' if(c == '=') { // note = 0; // Init default ++state; // Parse default value } else goto state1; // Invalid char, start over break; // case 3: // - Parse and assign default value if(isdigit(c)) { // Update default note = note * 10 + (c - '0'); // } else if(c == ',' || c == ':') { // End of default, assign to variable switch(d) { // case 'b': // Default BPM case 'B': // bpm = note; // break; // case 'o': // Default octave case 'O': // default_octave = note; // break; // case 'd': // Default note duration case 'D': // default_duration = note;// break; // } // state = (c == ':') ? 4 : 1; // Get next default or begin parsing notes } else goto state1; // Invalid char, start over break; // case 4: // - Parse note dot = 16000000UL * 60 * 4; // Setup defaults for this note duration = default_duration; // octave = default_octave; // note = 0; // if(isdigit(c)) { // May begin with duration duration = c - '0'; // Init duration ++state; // May be more than one digit, continue in next state } else if((note = isnote(c)) >= 0) {// May begin with note state = 6; // Got note, parse modifiers and octave } // break; // case 5: // - Parse duration if(isdigit(c)) { // Numeric digit, update duration duration = duration * 10 + (c -'0'); } else if((note = isnote(c)) >= 0) {// Got note, parse modifiers and octave ++state; // } else { // Invlid char, start over state = 4; // } // break; // case 6: // - Parse modifiers and octave if(c == ',' || c == 0) { // End of note, play it play_note((octave + 1) * 12 + note, dot / (duration * bpm)); state = 4; // Next note } else if(c == '#') { // Sharp, increment note value ++note; // } else if(c == 'b') { // Flat, decrement note value --note; // } else if(c == '.') { // Dotted note, extend length by 50% dot += (dot >> 1); // } else if(isdigit(c)) { // Octave, 0 to 9 allowed, should be 4 to 7 octave = c - '0'; // } // break; // // } // } while(c); // End of string while(note_timer); // Wait for last note to finish TACCTL1 = OUTMOD_0; // PWM off TACCR0 = 16000 - 1 ; // 1 ms period } #pragma vector = TIMER0_A1_VECTOR // Timer A0 Overflow interrupt __interrupt void timer0_a1_isr(void) // { // volatile unsigned z = TAIV; // Clear interrupt flag if(note_timer) --note_timer; // Decrement note timer } // static const char * const tunes[] = { "Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6", "Popcorn:d=4,o=5,b=160:8c6,8a#,8c6,8g,8d#,8g,c,8c6,8a#,8c6,8g,8d#,8g,c,8c6,8d6,8d#6,16c6,8d#6,16c6,8d#6,8d6,16a#,8d6,16a#,8d6,8c6,8a#,8g,8a#,c6", "peanuts:d=4,o=5,b=160:f,8g,a,8a,8g,f,2g,f,p,f,8g,a,1a,2p,f,8g,a,8a,8g,f,2g,2f,2f,8g,1g", "PinkPanther:d=4,o=5,b=160:8d#,8e,2p,8f#,8g,2p,8d#,8e,16p,8f#,8g,16p,8c6,8b,16p,8d#,8e,16p,8b,2a#,2p,16a,16g,16e,16d,2e", "aadams:d=4,o=5,b=160:8c,f,8a,f,8c,b4,2g,8f,e,8g,e,8e4,a4,2f,8c,f,8a,f,8c,b4,2g,8f,e,8c,d,8e,1f,8c,8d,8e,8f,1p,8d,8e,8f#,8g,1p,8d,8e,8f#,8g,p,8d,8e,8f#,8g,p,8c,8d,8e,8f", "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c", "StWars:d=4,o=5,b=180:8f,8f,8f,2a#.,2f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8d#6,2c6,p,8f,8f,8f,2a#.,2f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8c6,2a#.6,f.6,8d#6,8d6,8d#6,2c6", "Mission:d=4,o=6,b=100:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,16g,8p,16g,8p,16a#,16p,16c,16p,16g,8p,16g,8p,16f,16p,16f#,16p,16g,8p,16g,8p,16a#,16p,16c,16p,16g,8p,16g,8p,16f,16p,16f#,16p,16a#,16g,2d,32p,16a#,16g,2c#,32p,16a#,16g,2c,16p,16a#5,16c", 0 }; void main(void) { WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // // P1DIR = BIT6; // Enable PWM output on P1.6 P1SEL = BIT6; // // TACTL = TASSEL_2 | MC_1 | TAIE; // Timer A config: SMCLK, count up, ovrflow int enabled TACCR0 = 16000 - 1 ; // 1 ms period // _EINT(); // Enable interrupts // const char * const *t; // for(; for(t = tunes; *t; ++t) { // Iterate all ring tones play(*t); // Play it note_timer = 1000; // Wait a second before next while(note_timer); // } // } Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.