Jay_

recTone - Arduino

May 2nd, 2013
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.22 KB | None | 0 0
  1. /**
  2. * Arduino Coursework 2 for Physical Computing: Input & Output - James Wilkinson
  3. * recTone - v0.1
  4. *
  5. * INSTRUCTIONS:
  6. * Open up the Serial Port and Enter any of the notes which are shown in the below table.
  7. * You can enter a comma (,) to add an extra long delay when playing tunes.
  8. * When a sequence of notes have been entered representing your favourite tune, simply
  9. * enter "p" to start playing the notes in sequence.
  10. *
  11. * You can also use the Potentiometer to control the delay which occurs between each note to get the tune sounding accurate.
  12. *
  13. * When the tune has finished, you can start again without having to restart the program!
  14. *
  15. * INSTRUCTIONS FOR SETTING THE BOARD UP:-
  16. * Black wire from neg -> GND
  17. * Red wire from pos -> 5v
  18. * White wire from J16 to pin 9
  19. * Resistor from J17 to negative
  20. * Red wire from e8 to positive
  21. * White wire from e7 to analogue 0
  22. * Black wire from e6 to negative
  23. * Potentiometer in a6, a7 and a8
  24. *
  25. *
  26. *  James Wilkinson - 21604819 - Edge Hill University
  27. *  May 2013.
  28. *  REF: Uses an extracted piece of code to determine the frequency of each note.
  29. *  Developed by  (cleft) 2005 D. Cuartielles for K3 - see below calculation:
  30. *
  31. * The calculation of the tones is made following the mathematical
  32. * operation:
  33. *
  34. *       timeHigh = period / 2 = 1 / (2 * toneFrequency)
  35. *
  36. * where the different tones are described as in the table:
  37. *
  38. * note          frequency   period  timeHigh
  39. * c             261 Hz          3830    1915    
  40. * d             294 Hz          3400    1700    
  41. * e             329 Hz          3038    1519    
  42. * f             349 Hz          2864    1432    
  43. * g             392 Hz          2550    1275    
  44. * a             440 Hz          2272    1136    
  45. * b             493 Hz          2028    1014   
  46. * C         523 Hz          1912    956
  47. *
  48. * see:
  49. * http://www.arduino.cc/en/Tutorial/Melody
  50. *
  51. *
  52. */
  53.  
  54.  
  55. // Define the pin in which the speaker is plugged into it
  56. // const(ant) variables tell the compiler it can't be modified and allows for more efficient compiling.
  57. // Since there is never any need to edit this on runtime (and technicaly it cant be anyway unless it reads from something) declare this const
  58. int const SPEAKER_PIN = 9;
  59.  
  60. // Potentiometer pin ("dial") - used to read analog inputs from it when deciding the duration for each note
  61. int const DIAL_PIN = 0;
  62.  
  63. // Represents how many microseconds the recording should delay for when a command
  64. // is entered into the input. By default it's 200 ms.
  65. int const COMMA_DELAY_MS = 400;
  66.  
  67. // Since C is compiled code and memory is allocated statically it does not
  68. // support dynamic arrays. Therefore a limit must be forced upon the maximum notes that can be recorded.
  69. // Should more notes be required, feel free to increase.
  70. int const MAX_RECORDED_NOTES = 64;
  71.  
  72. // definitions for the notes together with the frequency as according to the above table
  73. char const names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  74. int const tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
  75.  
  76. // These two store the data for the recording of the notes
  77. int recordedNotes[MAX_RECORDED_NOTES];
  78. int recorded_notes_idx = 0;
  79.  
  80.  
  81.  
  82. /**
  83. * playTone
  84. * Sends the instruction to the piezo element to play the tone at the stated frequency
  85. * and duration passed to the function
  86. * @param int tone - reprensents the frequency value for the note to be played - see playNote
  87. * @param int duration - integer value representing the duration for the note to be played in microseconds
  88. **/
  89. void playTone(int tone, int duration)
  90. {
  91.   for (long i = 0; i < duration * 1000L; i += tone * 2)
  92.   {
  93.     digitalWrite(SPEAKER_PIN, HIGH);
  94.     delayMicroseconds(tone);
  95.     digitalWrite(SPEAKER_PIN, LOW);
  96.     delayMicroseconds(tone);
  97.   }
  98. }
  99.  
  100. /**
  101. * playNote
  102. * Play's the given note represented by a letter for the purposes of readability
  103. * for the duration stated (in microseconds)
  104. * @param char note the note to play
  105. * @param int duration the time, in microseconds, that the note should play for.
  106. **/
  107. void playNote(char note, int duration)
  108. {
  109.    // play the tone corresponding to the note name
  110.   // For the sake of adding more notes in the future I've removed the
  111.   // hardcoded value of 8 to the correct value of the size of the array
  112.   for (int i = 0; i < sizeof(tones); i++)
  113.   {
  114.     if (names[i] != note)
  115.       continue;
  116.      
  117.     // No need to continue looping once our note has been found
  118.     playTone(tones[i], duration);
  119.     break;  
  120.   }
  121. }
  122.  
  123. /**
  124. * getToneDuration
  125. * Returns an integer representing the duration  
  126. * that each tone should be as according to the potentiometer (aka dial)
  127. * @return int duration
  128. **/
  129. int getToneDuration()
  130. {
  131.   return (0 + analogRead(DIAL_PIN));
  132. }
  133.  
  134.  
  135. /**
  136. * addNoteToIndex
  137. * Appends a note to the array index accordingly providing that the note is valid.
  138. * @param char note represents the note to record - also supports commas (,) for pausing in between
  139. **/
  140. void addNoteToIndex(char note)
  141. {
  142.   // Only add valid notes to the index and don't forget to support commas
  143.   if(!isValidNote(note) && note != ',')
  144.   {
  145.     return;
  146.   }
  147.  
  148.   // Check for buffer overflows within the array and prevent them.
  149.   if(recorded_notes_idx >= MAX_RECORDED_NOTES)
  150.   {
  151.     // if the max is reached just play the recording.
  152.     playRecording();
  153.     return;
  154.   }
  155.  
  156.   // Simply add it to the array and increase the integer
  157.   // representing the index so we can monitor it later
  158.   recordedNotes[recorded_notes_idx] = note;
  159.   recorded_notes_idx++;
  160.   Serial.print(note);
  161.   Serial.print(" ");
  162. }
  163.  
  164.  
  165. /**
  166. * playRecording
  167. * This simply plays back the recording by looping through the stored notes
  168. * in the recordedNotes array. It also searches for any commas to check for extra long
  169. * delays. Once a tune has finished playing back, the counter is reset to make room for a new
  170. * tune to be entered.
  171. */
  172. void playRecording()
  173. {
  174.   // First of all make sure at least one note has been recorded
  175.   if(recorded_notes_idx == 0)
  176.     return;
  177.  
  178.   // Loop through the array and playback any notes accordingly.
  179.   // Watch out for commas - if a comma is included, it means there should be a
  180.   // slightly longer delay regrdless of the potentiometer when moving on to the next note
  181.   for(int i = 0; i < recorded_notes_idx; i++)
  182.   {
  183.     if(recordedNotes[i] == ',')
  184.     {
  185.       delay(COMMA_DELAY_MS);
  186.       continue;
  187.     }
  188.    
  189.     // Okay now just play the note accordingly
  190.     playNote(recordedNotes[i], getToneDuration());
  191.  
  192.     // Add just a short hardcoded delay
  193.     // I would've liked to have based this delay on another potentiometer switch but unfortunately
  194.     // do not have the resources now
  195.     delay(200);
  196.   }
  197.  
  198.   // Reset the counter back to 0 so that another can be played
  199.   recorded_notes_idx = 0;
  200.   Serial.println("Tune played!");
  201. }
  202.  
  203.  
  204.  
  205.  
  206. /**
  207. * This function returns an integer representing a true/false indication
  208. * as to whether an input is a valid note or not. Necessary for error checking
  209. * prior to processing
  210. * @param int input - represents the input value which is returned from Serial.read()
  211. *
  212.  UPDATE: Whilst using a switch/case statement is one practical way of doing this,
  213.  it just hit me - why not just use a loop?!
  214.   switch(input)
  215.   {
  216.     case 'c': return 1;
  217.     case 'd': return 1;
  218.     case
  219.     default: return 0;
  220.   }
  221.  */
  222. int isValidNote(char note)
  223. {
  224.   for(int i = 0; i < sizeof(names); i++)
  225.   {
  226.     if(note == names[i])
  227.       return 1;
  228.   }
  229.   return 0;
  230. }  
  231.  
  232.  
  233.  
  234.  
  235. /**
  236. * Called once (i.e. equivilant to main) upon initialisation
  237. **/
  238. void setup()
  239. {
  240.   Serial.begin(9600);  
  241.   pinMode(SPEAKER_PIN, OUTPUT);  
  242.   Serial.print("recTone v0.1\nINSTRUCTIONS:\nEnter any sequence of the following notes:\nc, d, e, f, g, a, b, C\nEnter a comma (,) for an additional pause\nEnter p to start playback\nNotes Entered:\n");
  243. }
  244.  
  245. /**
  246. * This is a repeating function that is consistently, repeatively called throughout
  247. * the programs life. It handles the reading of input from the console to detect for the
  248. * input of notes and if a note is valid it will play it accordingly
  249. **/
  250. void loop()
  251. {
  252.   // Detect if any input has been given in the serial panel
  253.   if(Serial.available() > 0)
  254.   {
  255.     int serialInput = Serial.read();
  256.    
  257.     if(serialInput == 'p')
  258.     {
  259.       playRecording();
  260.     }
  261.     else
  262.     {
  263.       addNoteToIndex(serialInput);
  264.     }
  265.   }
  266. }
Advertisement
Add Comment
Please, Sign In to add comment