電子楽器CAmiDionのArduinoスケッチ(C++)です。回路図も置いてあります。
リビジョン | 6fbad073fea6a57891f9e1ed3e4c93b89b56c4f0 (tree) |
---|---|
日時 | 2019-08-16 12:14:22 |
作者 | Akiyoshi Kamide <kamide@yk.r...> |
コミッター | Akiyoshi Kamide |
Program Change sending feature added, refactored, and webpage URL updated
@@ -1,9 +1,9 @@ | ||
1 | 1 | // |
2 | 2 | // CAmiDion - Musical Chord Instrument |
3 | -// ver.20160429 | |
3 | +// ver.20190816 | |
4 | 4 | // by Akiyoshi Kamide (Twitter: @akiyoshi_kamide) |
5 | -// http://kamide.b.osdn.me/camidion/ | |
6 | -// http://osdn.jp/users/kamide/pf/CAmiDion/ | |
5 | +// http://camidion.wordpress.com/camidion/ | |
6 | +// http://osdn.net/users/kamide/pf/CAmiDion/ | |
7 | 7 | // http://www.yk.rim.or.jp/~kamide/music/chordhelper/hardware/ |
8 | 8 | // |
9 | 9 |
@@ -71,7 +71,8 @@ PROGMEM const Instrument DRUM_INSTRUMENT = {randomWavetable, {5, 0, 5, 0}}; | ||
71 | 71 | |
72 | 72 | class WaveSelecter { |
73 | 73 | protected: |
74 | - byte current_midi_channel; // 1==CH1, ... | |
74 | + byte current_midi_channel; // 1==CH1 | |
75 | + byte programs[0x10]; // 0[0]==Prog1[CH1] | |
75 | 76 | public: |
76 | 77 | WaveSelecter() { |
77 | 78 | PWMDACSynth::getChannel(DRUM_MIDI_CHANNEL)->programChange(&DRUM_INSTRUMENT); |
@@ -79,9 +80,10 @@ class WaveSelecter { | ||
79 | 80 | #ifdef USE_LED |
80 | 81 | led_ctrl.setMidiChannel(0); |
81 | 82 | #endif |
83 | + memset(programs, 0, sizeof(programs)); | |
82 | 84 | } |
83 | 85 | #ifdef USE_LCD |
84 | - void showWaveform(char delimiter) { | |
86 | + void showWaveform(const char delimiter) { | |
85 | 87 | lcd.printWaveform( |
86 | 88 | current_midi_channel, |
87 | 89 | PWMDACSynth::getChannel(current_midi_channel)->wavetable, |
@@ -93,7 +95,7 @@ class WaveSelecter { | ||
93 | 95 | } |
94 | 96 | #endif |
95 | 97 | byte getCurrentMidiChannel() { return current_midi_channel; } |
96 | - void changeCurrentMidiChannel(char offset) { | |
98 | + void changeCurrentMidiChannel(const char offset) { | |
97 | 99 | char ch0 = current_midi_channel + offset - 1; |
98 | 100 | current_midi_channel = (ch0 &= 0xF) + 1; |
99 | 101 | #ifdef USE_LED |
@@ -104,7 +106,7 @@ class WaveSelecter { | ||
104 | 106 | showEnvelope(); |
105 | 107 | #endif |
106 | 108 | } |
107 | - void changeWaveform(char offset) { | |
109 | + void changeWaveform(const char offset) { | |
108 | 110 | if( ! offset ) return; |
109 | 111 | MidiChannel *cp = PWMDACSynth::getChannel(current_midi_channel); |
110 | 112 | for( char i = 0; i < NumberOf(WAVETABLE_LIST); i++ ) { |
@@ -118,7 +120,7 @@ class WaveSelecter { | ||
118 | 120 | return; |
119 | 121 | } |
120 | 122 | } |
121 | - void changeEnvelope(AdsrStatus adsr, char offset) { | |
123 | + void changeEnvelope(const AdsrStatus adsr, const char offset) { | |
122 | 124 | byte *p = PWMDACSynth::getChannel(current_midi_channel)->envelope.getParam(adsr); |
123 | 125 | if( adsr == ADSR_SUSTAIN ) *p += offset * 0x10; // 0..F -> 0..FF |
124 | 126 | else { *p += offset; *p &= 0xF; } |
@@ -126,6 +128,25 @@ class WaveSelecter { | ||
126 | 128 | showEnvelope(); |
127 | 129 | #endif |
128 | 130 | } |
131 | + byte getProgram(const byte channel) { return programs[channel - 1]; } | |
132 | + byte getProgram() { return getProgram(current_midi_channel); } | |
133 | + void programChange(const byte channel, const byte program) { | |
134 | + if( channel == DRUM_MIDI_CHANNEL ) return; | |
135 | + byte *pp = programs + channel - 1; | |
136 | + *pp = program & 0x7F; | |
137 | +#ifdef USE_LCD | |
138 | + if( channel == current_midi_channel ) lcd.printProgram(*pp); | |
139 | +#endif | |
140 | +#if defined(OCTAVE_ANALOG_PIN) | |
141 | + PWMDACSynth::getChannel(channel)->programChange(INSTRUMENTS + *pp); | |
142 | +#endif | |
143 | +#ifdef USE_MIDI_OUT | |
144 | + MIDI.sendProgramChange(*pp, channel); | |
145 | +#endif | |
146 | + } | |
147 | + void programChange(const byte program) { | |
148 | + programChange(current_midi_channel, program); | |
149 | + } | |
129 | 150 | }; |
130 | 151 | |
131 | 152 | WaveSelecter wave_selecter; |
@@ -151,8 +172,7 @@ void HandleNoteOn(byte channel, byte pitch, byte velocity) { | ||
151 | 172 | |
152 | 173 | #if defined(OCTAVE_ANALOG_PIN) |
153 | 174 | void HandleProgramChange(byte channel, byte number) { |
154 | - if( channel == DRUM_MIDI_CHANNEL ) return; | |
155 | - PWMDACSynth::getChannel(channel)->programChange(INSTRUMENTS + number); | |
175 | + wave_selecter.programChange(channel, number); | |
156 | 176 | } |
157 | 177 | #endif |
158 | 178 |
@@ -636,6 +656,9 @@ class MyButtonHandler : public ButtonHandler { | ||
636 | 656 | if( button_input.isOn(MIDI_CH_BUTTON) ) { |
637 | 657 | if(y == 0) { |
638 | 658 | if( x >= -1 && x <= 1 ) wave_selecter.changeCurrentMidiChannel(x); |
659 | +#ifdef USE_LCD | |
660 | + else if( x == -2 ) lcd.printProgram(wave_selecter.getProgram()); | |
661 | +#endif | |
639 | 662 | return; |
640 | 663 | } |
641 | 664 | switch(x) { |
@@ -644,6 +667,8 @@ class MyButtonHandler : public ButtonHandler { | ||
644 | 667 | case 2: wave_selecter.changeEnvelope(ADSR_DECAY, y); break; |
645 | 668 | case 3: wave_selecter.changeEnvelope(ADSR_SUSTAIN, y); break; |
646 | 669 | case 4: wave_selecter.changeEnvelope(ADSR_RELEASE, y); break; |
670 | + case -1: wave_selecter.programChange(wave_selecter.getProgram() + y); break; | |
671 | + case -2: wave_selecter.programChange(wave_selecter.getProgram() + y*8); break; | |
647 | 672 | } |
648 | 673 | return; |
649 | 674 | } |
@@ -52,9 +52,22 @@ class CAmiDionLCD : public LCD_PARENT_CLASS { | ||
52 | 52 | memcpy( bufp, str, len ); bufp += len; |
53 | 53 | } |
54 | 54 | void setString(char *str) { setString(str,strlen(str)); } |
55 | - void setHex(byte value) { | |
55 | + void setSingleHex(byte value) { | |
56 | 56 | *bufp++ = value + (value < 10 ?'0':'A'-10); |
57 | 57 | } |
58 | + void setDecimal(byte value) { | |
59 | + byte d100 = value / 100; | |
60 | + if( d100 ) { | |
61 | + *bufp++ = d100 + '0'; | |
62 | + value -= d100 * 100; | |
63 | + } | |
64 | + byte d10 = value / 10; | |
65 | + if( d100 || d10 ) { | |
66 | + *bufp++ = d10 + '0'; | |
67 | + value -= d10 * 10; | |
68 | + } | |
69 | + *bufp++ = value + '0'; | |
70 | + } | |
58 | 71 | public: |
59 | 72 | CAmiDionLCD() : LCD_PARENT_CLASS( LCD_CONSTRUCTOR_ARGS ) { |
60 | 73 | current_chord = MusicalChord(); |
@@ -115,7 +128,7 @@ class CAmiDionLCD : public LCD_PARENT_CLASS { | ||
115 | 128 | setCursor(0,0); |
116 | 129 | printLineBuffer(); |
117 | 130 | } |
118 | - void printTempo(unsigned int bpm, char delimiter) { | |
131 | + void printTempo(const unsigned int bpm, const char delimiter) { | |
119 | 132 | #if LCD_COLS >= 15 |
120 | 133 | setString("Tempo",5); |
121 | 134 | #else |
@@ -126,15 +139,21 @@ class CAmiDionLCD : public LCD_PARENT_CLASS { | ||
126 | 139 | printLineBuffer(); |
127 | 140 | clearChord(); |
128 | 141 | } |
129 | - void printEnvelope(EnvelopeParam *ep) { | |
130 | - *bufp++ = 'a'; setHex(*(ep->getParam(ADSR_ATTACK))); | |
131 | - *bufp++ = 'd'; setHex(*(ep->getParam(ADSR_DECAY))); | |
132 | - *bufp++ = 's'; setHex(*(ep->getParam(ADSR_SUSTAIN)) >> 4); | |
133 | - *bufp++ = 'r'; setHex(*(ep->getParam(ADSR_RELEASE))); | |
142 | + void printProgram(byte program) { // program = 0..127 | |
143 | + setString("Prog:"); | |
144 | + setDecimal(++program); | |
134 | 145 | setCursor(0,1); |
135 | 146 | printLineBuffer(); |
136 | 147 | } |
137 | - void printWaveform(byte midi_channel, PROGMEM const byte wavetable[], char delimiter = ':') { | |
148 | + void printEnvelope(EnvelopeParam* const ep) { | |
149 | + *bufp++ = 'a'; setSingleHex(*(ep->getParam(ADSR_ATTACK))); | |
150 | + *bufp++ = 'd'; setSingleHex(*(ep->getParam(ADSR_DECAY))); | |
151 | + *bufp++ = 's'; setSingleHex(*(ep->getParam(ADSR_SUSTAIN)) >> 4); | |
152 | + *bufp++ = 'r'; setSingleHex(*(ep->getParam(ADSR_RELEASE))); | |
153 | + setCursor(0,1); | |
154 | + printLineBuffer(); | |
155 | + } | |
156 | + void printWaveform(const byte midi_channel, PROGMEM const byte wavetable[], const char delimiter = ':') { | |
138 | 157 | PROGMEM static const uint8_t random_pattern[] = { |
139 | 158 | B10101, |
140 | 159 | B01010, |
@@ -224,10 +243,7 @@ class CAmiDionLCD : public LCD_PARENT_CLASS { | ||
224 | 243 | static const char wavename_guitar[] PROGMEM = "Guitar"; |
225 | 244 | #endif |
226 | 245 | setString("Ch",2); |
227 | - if( midi_channel >= 10 ) { | |
228 | - *bufp++ = '1'; midi_channel -= 10; | |
229 | - } | |
230 | - *bufp++ = midi_channel + '0'; | |
246 | + setDecimal(midi_channel); | |
231 | 247 | *bufp++ = delimiter; |
232 | 248 | const char PROGMEM *wavename; |
233 | 249 | if( wavetable == shepardToneSineWavetable ) { |