// // MIDI Keyboard 20200613 // #include MIDI_CREATE_DEFAULT_INSTANCE(); #include PWMDAC_CREATE_WAVETABLE(sawtoothWavetable, PWMDAC_SAWTOOTH_WAVE); PROGMEM const Instrument instrument = {sawtoothWavetable, {8, 128, 11, 4}}; PWMDAC_CREATE_INSTANCE(&instrument); #define MIDI_ENABLE_PIN 2 #define OCTAVE_ANALOG_PIN 0 #define VELOCITY_ANALOG_PIN 1 #define JS_V_ANALOG_PIN 2 // Joystick Vertical #define JS_H_ANALOG_PIN 3 // Joystick Horizontal // MIDI channel to send from MIDI OUT byte current_midi_channel = 1; // 1==CH1, ... // MIDI IN receive callbacks to control PWM sound void HandleNoteOn(byte channel, byte pitch, byte velocity) { if( velocity == 0 ) PWMDACSynth::noteOff(channel, pitch, velocity); PWMDACSynth::noteOn(channel, pitch, velocity); } // A list of pressing buttons with notes typedef struct _Button { byte cathode8; byte sw_anode5; } Button; typedef struct _ButtonNote { struct _Button button; byte note; } ButtonNote; ButtonNote button_notes[10]; void freeButtonNote(struct _ButtonNote *bnp) { bnp->button.cathode8 = bnp->button.sw_anode5 = bnp->note = 0xFF; } void initButtonNotes() { ButtonNote *bnp = button_notes; char i=NumberOf(button_notes); for( ; i>0; i--, bnp++ ) { freeButtonNote(bnp); } } struct _ButtonNote *searchButtonNote(struct _Button *button_p) { ButtonNote *bnp = button_notes; char i=NumberOf(button_notes); for( ; i>0; i--, bnp++ ) if( bnp->button.cathode8 == button_p->cathode8 && bnp->button.sw_anode5 == button_p->sw_anode5 ) return bnp; return NULL; } struct _ButtonNote *getActiveNote() { ButtonNote *bnp = button_notes; char i=NumberOf(button_notes); for( ; i>0; i--, bnp++ ) if( bnp->note != 0xFF ) return bnp; return NULL; } void ButtonReleased(struct _Button *button_p) { ButtonNote *bnp = searchButtonNote(button_p); if( bnp == NULL ) return; char velocity = analogRead(VELOCITY_ANALOG_PIN) >> 3; PWMDACSynth::noteOff(current_midi_channel, bnp->note, velocity); MIDI.sendNoteOff(bnp->note, velocity, current_midi_channel); freeButtonNote(bnp); } void ButtonPressed(struct _Button *button_p) { int note = button_p->cathode8 + 8 * button_p->sw_anode5; // Search empty entry to regist button Button null_button; null_button.cathode8 = 0xFF; null_button.sw_anode5 = 0xFF; ButtonNote *bnp = searchButtonNote(&null_button); if( bnp == NULL ) return; // Button note table full - reject it ButtonNote bn; note += 12 * map( analogRead(OCTAVE_ANALOG_PIN), 0,1023, 0,8 ); if( note > 0x7F ) note -= 12; bn.note = note; bn.button = *button_p; *bnp = bn; char velocity = analogRead(VELOCITY_ANALOG_PIN) >> 3; HandleNoteOn(current_midi_channel, bnp->note, velocity); MIDI.sendNoteOn(bnp->note, velocity, current_midi_channel); } void setup() { PWMDACSynth::setup(); initButtonNotes(); DDRB &= 0b11000000; // port 8..13 for INPUT PORTB |= 0b00111111; // analog HIGH -> INPUT_PULLUP DDRD |= 0b11110000; // port 4..7 for OUTPUT (for 74HC138 + LED pin) PORTD |= 0b10000000; // port 7 to HIGH OUTPUT MIDI.begin(MIDI_CHANNEL_OMNI); // receives all MIDI channels MIDI.turnThruOff(); // Disable MIDI IN -> MIDI OUT mirroring MIDI.setHandleNoteOff(PWMDACSynth::noteOff); MIDI.setHandleNoteOn(HandleNoteOn); MIDI.setHandlePitchBend(PWMDACSynth::pitchBend); MIDI.setHandleControlChange(PWMDACSynth::controlChange); pinMode( MIDI_ENABLE_PIN, OUTPUT ); digitalWrite( MIDI_ENABLE_PIN, HIGH ); // enable MIDI port } byte in_values[8]= { 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, }; boolean was_js_button_on = false; boolean js_enabled = true; void loop() { Button button; byte portd_value = 0; for( button.cathode8 = 0 ; button.cathode8 < 8; button.cathode8++ ) { PORTD &= 0b10001111; // Port 4..6 bit off PORTD |= portd_value; // Put 3-bit value to port 4..6 (button cathode) byte *in_value_p = in_values + button.cathode8; delayMicroseconds(3); // To avoid getting electrically unstable PINB value byte in_value = PINB | 0b11100000; // Get value from port 8..12 (button anode) byte in_value_change = in_value ^ *in_value_p; if( in_value_change ) { *in_value_p = in_value; byte sw_anode5_mask = 1; for( button.sw_anode5 = 0; button.sw_anode5 < 5; button.sw_anode5++ ) { if( in_value_change & sw_anode5_mask ) { if( in_value & sw_anode5_mask ) ButtonReleased(&button); else ButtonPressed(&button); } sw_anode5_mask <<= 1; } } portd_value += 0b00010000; if( button.cathode8 % 4 == 0 ) PWMDACSynth::update(); } MIDI.read(); if( was_js_button_on != (digitalRead(13)==LOW) ) { was_js_button_on = !was_js_button_on; if( was_js_button_on ) { js_enabled = !js_enabled; digitalWrite( 7, (js_enabled?HIGH:LOW) ); MIDI.sendPitchBend( 0, current_midi_channel ); MIDI.sendControlChange( 1, 0, current_midi_channel ); } } if( js_enabled ) { MidiChannel *mcp = PWMDACSynth::getChannel(current_midi_channel); int new_pitch_bend = (511 - analogRead(JS_H_ANALOG_PIN)) * 16; if( (mcp->getPitchBend() & 0xFFF0) != (new_pitch_bend & 0xFFF0) ) { PWMDACSynth::pitchBend( current_midi_channel, new_pitch_bend ); MIDI.sendPitchBend( new_pitch_bend, current_midi_channel ); } int new_modulation = abs(511 - analogRead(JS_V_ANALOG_PIN)) >> 2; if( new_modulation > 127 ) new_modulation = 127; if( mcp->modulation != new_modulation ) { PWMDACSynth::controlChange( current_midi_channel, 1, new_modulation ); MIDI.sendControlChange( 1, new_modulation, current_midi_channel ); // Modulation MSB only } } }