ChordJoy is a keyboard application that accepts chordal input from a set of digital ports, outputting letters and the chords that correspond to the piano keyboard keys the user has pressed.
Dependencies: PinDetect_KL25Z mbed-rtos mbed
main.cpp@4:400a042e762a, 2014-09-22 (annotated)
- Committer:
- andrewhead
- Date:
- Mon Sep 22 02:15:15 2014 +0000
- Revision:
- 4:400a042e762a
- Parent:
- 3:ed89297af2ce
- Child:
- 5:1186eb1f3c2b
Play chords after hearing rising edge!
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
andrewhead | 0:5ffec551c755 | 1 | #include "mbed.h" |
andrewhead | 0:5ffec551c755 | 2 | #include "rtos.h" |
andrewhead | 0:5ffec551c755 | 3 | |
andrewhead | 1:b6b866a58a87 | 4 | |
andrewhead | 1:b6b866a58a87 | 5 | const int BUTTONS = 7; |
andrewhead | 0:5ffec551c755 | 6 | const int BUZZERS = 3; |
andrewhead | 0:5ffec551c755 | 7 | const int LETTERS = 27; |
andrewhead | 0:5ffec551c755 | 8 | |
andrewhead | 3:ed89297af2ce | 9 | // Collection is the time that it takes after pressing one key and then reading |
andrewhead | 3:ed89297af2ce | 10 | // all the keys to determine what the chord is. |
andrewhead | 3:ed89297af2ce | 11 | const int COLLECTION_MS = 500; |
andrewhead | 3:ed89297af2ce | 12 | Timer collectionTimer; |
andrewhead | 1:b6b866a58a87 | 13 | |
andrewhead | 1:b6b866a58a87 | 14 | InterruptIn buttons [BUTTONS] = { |
andrewhead | 2:49e2b1273f16 | 15 | InterruptIn(D2), |
andrewhead | 1:b6b866a58a87 | 16 | InterruptIn(D4), |
andrewhead | 1:b6b866a58a87 | 17 | InterruptIn(D5), |
andrewhead | 1:b6b866a58a87 | 18 | InterruptIn(D8), |
andrewhead | 1:b6b866a58a87 | 19 | InterruptIn(D9), |
andrewhead | 1:b6b866a58a87 | 20 | InterruptIn(D10), |
andrewhead | 2:49e2b1273f16 | 21 | InterruptIn(D11) |
andrewhead | 1:b6b866a58a87 | 22 | }; |
andrewhead | 0:5ffec551c755 | 23 | Serial pc(USBTX, USBRX); |
andrewhead | 1:b6b866a58a87 | 24 | |
andrewhead | 1:b6b866a58a87 | 25 | // Note: there are only THREE PWMs on the board. So this is all we can use |
andrewhead | 1:b6b866a58a87 | 26 | // for this particular method of sound output. The pins chosen are important |
andrewhead | 1:b6b866a58a87 | 27 | // because the same PWM will map to multiple outputs. See the reference |
andrewhead | 1:b6b866a58a87 | 28 | // manual for Mbed for which pins map to distinct PWMs. |
andrewhead | 1:b6b866a58a87 | 29 | PwmOut buzzers [3] = { |
andrewhead | 1:b6b866a58a87 | 30 | PwmOut(D0), |
andrewhead | 1:b6b866a58a87 | 31 | PwmOut(D7), |
andrewhead | 1:b6b866a58a87 | 32 | PwmOut(D3) |
andrewhead | 0:5ffec551c755 | 33 | }; |
andrewhead | 0:5ffec551c755 | 34 | |
andrewhead | 0:5ffec551c755 | 35 | // Periods of notes in microseconds |
andrewhead | 0:5ffec551c755 | 36 | int notePeriods [8] = { |
andrewhead | 0:5ffec551c755 | 37 | 3818, // C4 |
andrewhead | 0:5ffec551c755 | 38 | 3412, // D4 |
andrewhead | 0:5ffec551c755 | 39 | 3030, // E4 |
andrewhead | 0:5ffec551c755 | 40 | 2895, // F4 |
andrewhead | 0:5ffec551c755 | 41 | 2551, // G4 |
andrewhead | 0:5ffec551c755 | 42 | 2273, // A4 |
andrewhead | 0:5ffec551c755 | 43 | 2024, // B4 |
andrewhead | 0:5ffec551c755 | 44 | 10000000 // inaudible pitch |
andrewhead | 0:5ffec551c755 | 45 | }; |
andrewhead | 3:ed89297af2ce | 46 | // Possible chords that can be played. |
andrewhead | 3:ed89297af2ce | 47 | // Note that 7 is no button pressed, or a 'silent' pitch. |
andrewhead | 0:5ffec551c755 | 48 | int chords [LETTERS][BUZZERS] = { |
andrewhead | 3:ed89297af2ce | 49 | {0, 7, 7}, // single notes |
andrewhead | 0:5ffec551c755 | 50 | {1, 7, 7}, |
andrewhead | 0:5ffec551c755 | 51 | {2, 7, 7}, |
andrewhead | 0:5ffec551c755 | 52 | {3, 7, 7}, |
andrewhead | 0:5ffec551c755 | 53 | {4, 7, 7}, |
andrewhead | 0:5ffec551c755 | 54 | {5, 7, 7}, |
andrewhead | 0:5ffec551c755 | 55 | {6, 7, 7}, |
andrewhead | 0:5ffec551c755 | 56 | {0, 2, 7}, // diads |
andrewhead | 0:5ffec551c755 | 57 | {0, 3, 7}, |
andrewhead | 0:5ffec551c755 | 58 | {0, 4, 7}, |
andrewhead | 0:5ffec551c755 | 59 | {0, 5, 7}, |
andrewhead | 0:5ffec551c755 | 60 | {1, 3, 7}, |
andrewhead | 0:5ffec551c755 | 61 | {1, 4, 7}, |
andrewhead | 0:5ffec551c755 | 62 | {1, 5, 7}, |
andrewhead | 0:5ffec551c755 | 63 | {1, 6, 7}, |
andrewhead | 0:5ffec551c755 | 64 | {2, 4, 7}, |
andrewhead | 0:5ffec551c755 | 65 | {2, 5, 7}, |
andrewhead | 0:5ffec551c755 | 66 | {2, 6, 7}, |
andrewhead | 0:5ffec551c755 | 67 | {3, 4, 7}, |
andrewhead | 0:5ffec551c755 | 68 | {3, 5, 7}, |
andrewhead | 0:5ffec551c755 | 69 | {4, 6, 7}, |
andrewhead | 0:5ffec551c755 | 70 | {0, 2, 4}, // triads |
andrewhead | 0:5ffec551c755 | 71 | {0, 2, 5}, |
andrewhead | 0:5ffec551c755 | 72 | {0, 3, 5}, |
andrewhead | 0:5ffec551c755 | 73 | {1, 4, 6}, |
andrewhead | 0:5ffec551c755 | 74 | {1, 3, 4}, |
andrewhead | 0:5ffec551c755 | 75 | {1, 3, 6} |
andrewhead | 0:5ffec551c755 | 76 | }; |
andrewhead | 0:5ffec551c755 | 77 | |
andrewhead | 4:400a042e762a | 78 | void playChord(int chordIndex) { |
andrewhead | 4:400a042e762a | 79 | int *chord = chords[chordIndex]; |
andrewhead | 4:400a042e762a | 80 | for (int i = 0; i < BUZZERS; i++) { |
andrewhead | 4:400a042e762a | 81 | int noteIndex = chord[i]; |
andrewhead | 4:400a042e762a | 82 | if (noteIndex == 7) { |
andrewhead | 4:400a042e762a | 83 | buzzers[i] = 0; |
andrewhead | 0:5ffec551c755 | 84 | } |
andrewhead | 4:400a042e762a | 85 | else { |
andrewhead | 4:400a042e762a | 86 | buzzers[i] = .5f; |
andrewhead | 4:400a042e762a | 87 | int period = notePeriods[noteIndex]; |
andrewhead | 4:400a042e762a | 88 | buzzers[i].period_us(period); |
andrewhead | 4:400a042e762a | 89 | } |
andrewhead | 0:5ffec551c755 | 90 | } |
andrewhead | 0:5ffec551c755 | 91 | } |
andrewhead | 0:5ffec551c755 | 92 | |
andrewhead | 1:b6b866a58a87 | 93 | void setFlag() { |
andrewhead | 3:ed89297af2ce | 94 | collectionTimer.start(); |
andrewhead | 1:b6b866a58a87 | 95 | } |
andrewhead | 1:b6b866a58a87 | 96 | |
andrewhead | 1:b6b866a58a87 | 97 | void silenceBuzzers(void) { |
andrewhead | 0:5ffec551c755 | 98 | // Initialize all duty cycles to off, so we hear nothing. |
andrewhead | 0:5ffec551c755 | 99 | for (int i = 0; i < BUZZERS; i++) { |
andrewhead | 0:5ffec551c755 | 100 | buzzers[i] = 0.0f; |
andrewhead | 0:5ffec551c755 | 101 | } |
andrewhead | 1:b6b866a58a87 | 102 | } |
andrewhead | 1:b6b866a58a87 | 103 | |
andrewhead | 1:b6b866a58a87 | 104 | void registerButtonInterrupts(void) { |
andrewhead | 1:b6b866a58a87 | 105 | for (int i = 0; i < BUTTONS; i++) { |
andrewhead | 1:b6b866a58a87 | 106 | buttons[i].rise(&setFlag); |
andrewhead | 1:b6b866a58a87 | 107 | } |
andrewhead | 1:b6b866a58a87 | 108 | } |
andrewhead | 1:b6b866a58a87 | 109 | |
andrewhead | 3:ed89297af2ce | 110 | int readChord(void) { |
andrewhead | 3:ed89297af2ce | 111 | /* Detect the current chord from the keys pressed. Return int index of chord. |
andrewhead | 3:ed89297af2ce | 112 | Return -1 if no chord can be found with the current keys. |
andrewhead | 3:ed89297af2ce | 113 | Note that we can only read as many keys as there are buzzers, so check |
andrewhead | 3:ed89297af2ce | 114 | on each key in an ascending order. */ |
andrewhead | 3:ed89297af2ce | 115 | int firstThreeButtons[BUZZERS] = {7, 7, 7}; |
andrewhead | 3:ed89297af2ce | 116 | int pressedCount = 0; |
andrewhead | 3:ed89297af2ce | 117 | for (int i = 0; i < BUTTONS; i++) { |
andrewhead | 3:ed89297af2ce | 118 | if (buttons[i].read() == 1) { |
andrewhead | 3:ed89297af2ce | 119 | firstThreeButtons[pressedCount] = i; |
andrewhead | 3:ed89297af2ce | 120 | pressedCount++; |
andrewhead | 3:ed89297af2ce | 121 | } |
andrewhead | 3:ed89297af2ce | 122 | if (pressedCount >= BUZZERS) { |
andrewhead | 3:ed89297af2ce | 123 | break; |
andrewhead | 3:ed89297af2ce | 124 | } |
andrewhead | 3:ed89297af2ce | 125 | } |
andrewhead | 3:ed89297af2ce | 126 | int matchIndex = -1; |
andrewhead | 3:ed89297af2ce | 127 | for (int i = 0; i < LETTERS; i++) { |
andrewhead | 3:ed89297af2ce | 128 | bool chordMatches = true; |
andrewhead | 3:ed89297af2ce | 129 | for (int j = 0; j < BUZZERS; j++) { |
andrewhead | 3:ed89297af2ce | 130 | if (firstThreeButtons[j] != chords[i][j]) { |
andrewhead | 3:ed89297af2ce | 131 | chordMatches = false; |
andrewhead | 3:ed89297af2ce | 132 | break; |
andrewhead | 3:ed89297af2ce | 133 | } |
andrewhead | 3:ed89297af2ce | 134 | } |
andrewhead | 3:ed89297af2ce | 135 | if (chordMatches == true) { |
andrewhead | 3:ed89297af2ce | 136 | matchIndex = i; |
andrewhead | 3:ed89297af2ce | 137 | break; |
andrewhead | 3:ed89297af2ce | 138 | } |
andrewhead | 3:ed89297af2ce | 139 | } |
andrewhead | 3:ed89297af2ce | 140 | pc.printf("Matched chord %d\n", matchIndex); |
andrewhead | 3:ed89297af2ce | 141 | return matchIndex; |
andrewhead | 3:ed89297af2ce | 142 | } |
andrewhead | 3:ed89297af2ce | 143 | |
andrewhead | 1:b6b866a58a87 | 144 | void waitForChord(void) { |
andrewhead | 3:ed89297af2ce | 145 | /* This method should be run in the main loop so that it can print */ |
andrewhead | 1:b6b866a58a87 | 146 | while (true) { |
andrewhead | 3:ed89297af2ce | 147 | if (collectionTimer.read_ms() > COLLECTION_MS) { |
andrewhead | 3:ed89297af2ce | 148 | collectionTimer.stop(); |
andrewhead | 3:ed89297af2ce | 149 | collectionTimer.reset(); |
andrewhead | 3:ed89297af2ce | 150 | int chordIndex = readChord(); |
andrewhead | 4:400a042e762a | 151 | if (chordIndex != -1) { |
andrewhead | 4:400a042e762a | 152 | playChord(chordIndex); |
andrewhead | 4:400a042e762a | 153 | } |
andrewhead | 1:b6b866a58a87 | 154 | } |
andrewhead | 3:ed89297af2ce | 155 | wait_ms(10); |
andrewhead | 1:b6b866a58a87 | 156 | } |
andrewhead | 1:b6b866a58a87 | 157 | } |
andrewhead | 1:b6b866a58a87 | 158 | |
andrewhead | 1:b6b866a58a87 | 159 | int main() { |
andrewhead | 1:b6b866a58a87 | 160 | silenceBuzzers(); |
andrewhead | 1:b6b866a58a87 | 161 | registerButtonInterrupts(); |
andrewhead | 1:b6b866a58a87 | 162 | waitForChord(); |
andrewhead | 0:5ffec551c755 | 163 | } |