// // Danger Shield MIDI drawbars for Yamaha PSR/Tyros // Sketch source file: DrawbarsTest.ino // // // Author: P.J. Drongowski // Date: 27 March 2014 // Version: 1.0 // // Copyright (c) 2014 Paul J. Drongowski // Permission to use, copy, modify and distribute as long as this // attribution appears in the code. // // // Shift register bit values to display a hexadecimal digit 0-F on // the seven segment display. // Bit order: HGFEDCBA where H is the decimal point // --A-- // | | // F B // | | // --G-- // | | // E C // | | // --D-- H // const byte sevenSegmentDigits[16] = { B00111111, // 0 B00000110, // 1 B01011011, // 2 B01001111, // 3 B01100110, // 4 B01101101, // 5 B01111101, // 6 B00000111, // 7 B01111111, // 8 B01101111, // 9 B01110111, // A B01111100, // b B00111001, // C B01011110, // d B01111001, // E B01110001 // F }; #define sevenSegment_A B01110111 #define sevenSegment_b B01111100 #define sevenSegment_c B01011000 #define sevenSegment_C B00111001 #define sevenSegment_d B01011110 #define sevenSegment_E B01111001 #define sevenSegment_F B01110001 #define sevenSegment_g B01101111 #define sevenSegment_h B01110100 #define sevenSegment_H B01110110 #define sevenSegment_i B00010000 #define sevenSegment_I B00110000 #define sevenSegment_J B00011110 #define sevenSegment_l B00011000 #define sevenSegment_L B00111000 #define sevenSegment_n B01010100 #define sevenSegment_o B01011100 #define sevenSegment_O B00111111 #define sevenSegment_P B01110011 #define sevenSegment_q B01100111 #define sevenSegment_r B01010000 #define sevenSegment_S B01101101 #define sevenSegment_t B01111000 #define sevenSegment_u B00011100 #define sevenSegment_y B01101110 #define sevenSegment_Z B01011011 #define sevenSegment_DP B10000000 #define sevenSegment_BLANK B00000000 // // Pin definitions (See Sparkfun DangerShield documentation) // #define SLIDER0 0 #define SLIDER1 1 #define SLIDER2 2 #define KNOCK 5 #define BUTTON1 10 #define BUTTON2 11 #define BUTTON3 12 #define LED1 5 #define LED2 6 #define BUZZER 3 #define TEMP 4 #define LIGHT 3 #define LATCH 7 #define CLOCK 8 #define DATA 4 // // The variable "mode" keeps track of the operating mode, i.e., // one of {drawbar, effect, preset} modes. Start in drawbar // mode. // #define DRAWBAR_MODE 1 #define EFFECT_MODE 2 #define PRESET_MODE 3 int mode = DRAWBAR_MODE ; // // Current slider (drawbar) group for each mode. The slider // group determines the slider's meaning (functionality) within // the current operating mode. It's a way to control up to nine // different drawbar settings (effects, presets) with just // three physical sliders. // #define DRAWBAR_A 1 #define DRAWBAR_B 2 #define DRAWBAR_C 3 int drawbarGroup = DRAWBAR_A ; #define EFFECT_A 1 #define EFFECT_B 2 #define EFFECT_C 3 int effectGroup = EFFECT_A ; #define PRESET_A 1 #define PRESET_B 2 #define PRESET_C 3 int presetGroup = PRESET_A ; // // Slider values. These variables track the raw slider values. // int sliderLeft = 0 ; int sliderMiddle = 0 ; int sliderRight = 0 ; int newValue = 0 ; // // Current PSR/Tyros organ flute values. We need to maintain // the entire state of the organ flute settings such that we // can send out a complete SysEx message with all of the current // setting values. // int drawbar_1 = 0 ; // Drawbar group 3, right int drawbar_113 = 0 ; // Drawbar group 3, middle int drawbar_135 = 0 ; // Drawbar group 3, left int drawbar_2 = 0 ; // Drawbar group 2, right int drawbar_223 = 0 ; // Drawbar group 2, middle int drawbar_4 = 0 ; // Drawbar group 2, left int drawbar_8 = 8 ; // Drawbar group 1, right int drawbar_513 = 8 ; // Drawbar group 1, middle int drawbar_16 = 8 ; // Drawbar group 1, left int attack_2 = 0 ; // Perc group int attack_223 = 0 ; // No user control (always off) int attack_4 = 0 ; // Perc group int attack_length = 0 ; // Perc group int response = 0 ; // Perc group int wave_variation = 1 ; // No user control (always vintage) int volume = 8 ; // Perc group // // Current effect values: Percussion (ON/OFF), rotary speaker // speed (SLOW/FAST), Percussion volume (NORMAL/SOFT), // percussion decay (SLOW/FAST), percussion harmonic (2nd/3rd), // vibrato (ON/OFF), vibrato depth (1/2/3), organ volume. // #define PERC_OFF 0 #define PERC_ON 1 int percussionStatus = PERC_OFF ; #define ROTARY_SLOW 0 #define ROTARY_FAST 1 int rotarySpeed = ROTARY_SLOW ; #define PERC_NORMAL 0 #define PERC_SOFT 1 #define ATTACK_NORMAL 8 #define ATTACK_SOFT 4 #define ATTACK_OFF 0 int percussionVolume = PERC_NORMAL ; #define PERC_SLOW 0 #define PERC_FAST 1 #define ATTACK_SLOW 8 #define ATTACK_FAST 1 int percussionDecay = PERC_SLOW ; #define PERC_SECOND 0 #define PERC_THIRD 1 int percussionHarmonic = PERC_SECOND ; #define VIBRATO_OFF 0 #define VIBRATO_ON 1 int vibratoStatus = VIBRATO_OFF ; // // MIDI CC values for vibrato depth. The selected depth (1/2/3) // must be mapped to an appropriate MIDI CC value. // #define VIBRATO_DEPTH_1 0x45 #define VIBRATO_DEPTH_2 0x48 #define VIBRATO_DEPTH_3 0x4B #define VIBRATO_DEPTH_OFF 0x40 int vibratoDepth = 0 ; #define VIBRATO_OFFSET 20 #define VIBRATO_SCALE 10 int vibratoSpeed = VIBRATO_DEPTH_1 ; int organVolume = 8 ; // // SysEx message buffers. // unsigned char OrganSysEx[34] ; // Organ flutes message unsigned char dspSysEx[8] ; // Change DSP message unsigned char midiShort[8] ; // Any short MID message // // Convert current drawbar and attack values to a valid // Yamaha organ flutes SysEx message. This function maps the // Arduino-side settings into SysEx values understood by the // Yamaha keyboard. // void makeOrganSysEx() { int i ; int sum ; int attack ; OrganSysEx[0] = 0xF0 ; OrganSysEx[1] = 0x43 ; // Yamaha ID OrganSysEx[2] = 0x73 ; // Clavinova ID OrganSysEx[3] = 0x01 ; // Model ID OrganSysEx[4] = 0x06 ; // Bulk ID OrganSysEx[5] = 0x0B ; // Bulk number (organ flutes data) OrganSysEx[6] = 0x00 ; // Data length (4 bytes) OrganSysEx[7] = 0x00 ; OrganSysEx[8] = 0x01 ; OrganSysEx[9] = 0x06 ; OrganSysEx[10] = 0x00 ; // Channel number OrganSysEx[11] = drawbar_1 ; OrganSysEx[12] = drawbar_113 ; OrganSysEx[13] = drawbar_135 ; OrganSysEx[14] = drawbar_2 ; OrganSysEx[15] = drawbar_223 ; OrganSysEx[16] = drawbar_4 ; OrganSysEx[17] = drawbar_513 ; OrganSysEx[18] = drawbar_8 ; OrganSysEx[19] = drawbar_16 ; if (percussionStatus == PERC_OFF) { OrganSysEx[21] = 0x00 ; // Attack 2 2/3' OrganSysEx[22] = 0x00 ; // Attack 4' OrganSysEx[23] = 0x00 ; // Attack length } else { if (percussionVolume == PERC_NORMAL) { attack = ATTACK_NORMAL ; } else { attack = ATTACK_SOFT ; } if (percussionHarmonic == PERC_SECOND) { OrganSysEx[22] = attack ; // Attack 4' } else { OrganSysEx[21] = attack ; // Attack 2 2/3' } if (percussionDecay == PERC_SLOW) { OrganSysEx[23] = ATTACK_SLOW ; // Attack length } else { OrganSysEx[23] = ATTACK_FAST ; // Attack length } } OrganSysEx[20] = 0x00 ; // Attack 2' (Not authentic B-3) OrganSysEx[24] = 0x00 ; // Response OrganSysEx[25] = 0x01 ; // Attack mode (always first) OrganSysEx[26] = 0x01 ; // Wave (always vintage) OrganSysEx[27] = 0x08 ; // Volume (always maximum) OrganSysEx[28] = 0x00 ; OrganSysEx[29] = 0x00 ; OrganSysEx[30] = 0x00 ; OrganSysEx[31] = 0x00 ; OrganSysEx[32] = 0x00 ; // Checksum OrganSysEx[33] = 0xF7 ; // Compute checksum sum = 0 ; for (i = 10 ; i < 32 ; i++) sum = sum + OrganSysEx[i] ; OrganSysEx[32] = 128 - (sum & 0x7F) ; } // // Send organ flutes SysEx message to PSR/Tyros keyboard // void sendOrganSysEx() { int i ; makeOrganSysEx() ; #ifdef TRACE Serial.println("Send drawbar SysEx") ; #else for (i = 0 ; i < 34 ; i++) { // Send MIDI byte Serial.write(OrganSysEx[i]) ; } #endif } // // Send a short 3-byte MIDI message. This could be any MIDI // channel message like note ON, note OFF, continuous // controller (CC), etc. // void sendMidiShort(int status, int data1, int data2) { #ifdef TRACE Serial.println("Send MIDI") ; #else midiShort[0] = status ; midiShort[1] = data1 ; midiShort[2] = data2 ; Serial.write(status) ; Serial.write(data1) ; Serial.write(data2) ; #endif } // // Send MIDI messages to change vibrato depth // void sendVibratoDepth(int depth) { sendMidiShort(0xB0, 0x63, 0x01) ; // NRPN MSB sendMidiShort(0xB0, 0x62, 0x09) ; // NRPN LSB // Data Entry MSB switch (depth) { case 0: { // Same as vibrato OFF sendMidiShort(0xB0, 0x06, VIBRATO_DEPTH_OFF) ; break ; } case 1: { sendMidiShort(0xB0, 0x06, VIBRATO_DEPTH_1) ; break ; } case 2: { sendMidiShort(0xB0, 0x06, VIBRATO_DEPTH_2) ; break ; } case 3: { sendMidiShort(0xB0, 0x06, VIBRATO_DEPTH_3) ; break ; } } sendMidiShort(0xB0, 0x26, 0x00) ; // Data Entry LSB } // // Send MIDI messages to change vibrato speed // // The argument speed has already been converted to the // PSR/Tyros data range 0:127. // void sendVibratoSpeed(int speed) { sendMidiShort(0xB0, 0x63, 0x01) ; // NRPN MSB sendMidiShort(0xB0, 0x62, 0x08) ; // NRPN LSB sendMidiShort(0xB0, 0x06, (speed&0x7F)) ; // Data Entry MSB sendMidiShort(0xB0, 0x26, 0x00) ; // Data Entry LSB } // // Send DSP variation ON/OFF SysEx message // // Currently assumes that the organ flutes voice is MIDI // channel 0 (also known as "Part 1"). // void sendDSPVarControl(int status) { dspSysEx[0] = 0xF0 ; dspSysEx[1] = 0x43 ; dspSysEx[2] = 0x10 ; dspSysEx[3] = 0x4C ; dspSysEx[4] = 0x03 ; dspSysEx[5] = 0x01 ; dspSysEx[6] = 0x25 ; if (status) { dspSysEx[7] = 0x01 ; } else { dspSysEx[7] = 0x00 ; } dspSysEx[8] = 0xF7 ; } // // Display a character in the 7 segment display // void displayChar(int segments, int dp) { if (dp) segments = (segments | sevenSegment_DP) ; digitalWrite(LATCH, LOW) ; shiftOut(DATA, CLOCK, MSBFIRST, ~segments) ; digitalWrite(LATCH, HIGH) ; } // // Display a (hexa)decimal digit in the 7 segment display // void displayDigit(int value, int dp) { int i = (value & 0x000F) ; int segments = sevenSegmentDigits[i] ; if (dp) segments = (segments | sevenSegment_DP) ; digitalWrite(LATCH, LOW) ; shiftOut(DATA, CLOCK, MSBFIRST, ~segments) ; digitalWrite(LATCH, HIGH) ; } // // Turn on the yellow LEDs according to group // 0: All off // 1: Top LED (LED1) on // 2: Bottom LED (LED2) on // 3: Top and bottom LEDs on // void displayGroup(int group) { switch(group & 0x3) { case 0: { digitalWrite(LED1, LOW) ; digitalWrite(LED2, LOW) ; break ; } case 1: { digitalWrite(LED1, HIGH) ; digitalWrite(LED2, LOW) ; break ; } case 2: { digitalWrite(LED1, LOW) ; digitalWrite(LED2, HIGH) ; break ; } case 3: { digitalWrite(LED1, HIGH) ; digitalWrite(LED2, HIGH) ; break ; } } } void setup() { int drawbarSet = 0 ; // TRACE mode writes messages back to the PC host and is used // for debugging _only_. If TRACE is not defined, then the // program writes binary MIDI messages to the serial port. // Don't cross the streams! #ifdef TRACE Serial.begin(9600) ; #else Serial.begin(31250) ; #endif // Standard DangerShield pin initialization pinMode(BUTTON1, INPUT) ; pinMode(BUTTON2, INPUT) ; pinMode(BUTTON3, INPUT) ; digitalWrite(BUTTON1, HIGH) ; digitalWrite(BUTTON2, HIGH) ; digitalWrite(BUTTON3, HIGH) ; pinMode(BUZZER, OUTPUT) ; pinMode(LED1, OUTPUT) ; pinMode(LED2, OUTPUT) ; digitalWrite(LED1, HIGH) ; digitalWrite(LED2, LOW) ; pinMode(LATCH, OUTPUT) ; pinMode(CLOCK, OUTPUT) ; pinMode(DATA, OUTPUT) ; // Read and map slider values to the range [0:8] sliderLeft = (analogRead(SLIDER2) / 114) ; sliderMiddle = (analogRead(SLIDER1) / 114) ; sliderRight = (analogRead(SLIDER0) / 114) ; #ifdef TRACE Serial.println("Drawbars test") ; #endif // Send initial organ set-up to the PSR/Tyros sendOrganSysEx() ; displayChar(sevenSegment_P, 0) ; delay(300) ; displayChar(sevenSegment_J, 0) ; delay(300) ; displayChar(sevenSegment_d, 0) ; delay(30) ; } void loop() { if(!(digitalRead(BUTTON1))) // Select drawbar group { delay(200) ; // Debounce if (drawbarGroup >= DRAWBAR_C) drawbarGroup = DRAWBAR_A ; else drawbarGroup++ ; displayGroup(drawbarGroup) ; displayChar(sevenSegment_d, 0) ; mode = DRAWBAR_MODE ; } if(!(digitalRead(BUTTON2))) // Select effectGroup { delay(200) ; // Debounce if (effectGroup >= EFFECT_C) effectGroup = EFFECT_A ; else effectGroup++ ; displayGroup(effectGroup) ; displayChar(sevenSegment_E, 1) ; mode = EFFECT_MODE ; } if(!(digitalRead(BUTTON3))) // Select preset group { delay(200) ; // Debounce if (presetGroup >= PRESET_A) presetGroup = PRESET_A ; else presetGroup++ ; displayGroup(presetGroup) ; displayChar(sevenSegment_P, 0) ; mode = PRESET_MODE ; } ////////////////////////////////// // Drawbar mode ////////////////////////////////// if (mode == DRAWBAR_MODE) { newValue = (analogRead(SLIDER2) / 114) ; if (newValue != sliderLeft) { sliderLeft = newValue ; displayDigit(sliderLeft, 0) ; #ifdef TRACE Serial.println("Drawbar left") ; #endif switch (drawbarGroup) { case DRAWBAR_A: { drawbar_16 = sliderLeft ; break ; } case DRAWBAR_B: { drawbar_4 = sliderLeft ; break ; } case DRAWBAR_C: { drawbar_135 = sliderLeft ; break ; } } sendOrganSysEx() ; } newValue = (analogRead(SLIDER1) / 114) ; if (newValue != sliderMiddle) { sliderMiddle = newValue ; displayDigit(sliderMiddle, 0) ; #ifdef TRACE Serial.println("Drawbar middle") ; #endif switch (drawbarGroup) { case DRAWBAR_A: { drawbar_513 = sliderMiddle ; break ; } case DRAWBAR_B: { drawbar_223 = sliderMiddle ; break ; } case DRAWBAR_C: { drawbar_113 = sliderMiddle ; break ; } } sendOrganSysEx() ; } newValue = (analogRead(SLIDER0) / 114) ; if (newValue != sliderRight) { sliderRight = newValue ; displayDigit(sliderRight, 0) ; #ifdef TRACE Serial.println("Drawbar right") ; #endif switch (drawbarGroup) { case DRAWBAR_A: { drawbar_8 = sliderRight ; break ; } case DRAWBAR_B: { drawbar_2 = sliderRight ; break ; } case DRAWBAR_C: { drawbar_1 = sliderRight ; break ; } } sendOrganSysEx() ; } } ////////////////////////////////// // Effect mode ////////////////////////////////// else if (mode == EFFECT_MODE) { newValue = (analogRead(SLIDER2) / 114) ; if (newValue != sliderLeft) { sliderLeft = newValue ; displayDigit(sliderLeft, 1) ; #ifdef TRACE Serial.println("Effect left") ; #endif switch (effectGroup) { case EFFECT_A: { if (sliderLeft > 4) { rotarySpeed = ROTARY_SLOW ; } else { rotarySpeed = ROTARY_FAST ; } sendDSPVarControl(rotarySpeed) ; break ; } case EFFECT_B: { if (sliderLeft > 4) { percussionVolume = PERC_NORMAL ; } else { percussionVolume = PERC_SOFT ; } sendOrganSysEx() ; break ; } case EFFECT_C: { // Organ volume (STUB: subject to change) organVolume = sliderLeft ; break ; } } } newValue = (analogRead(SLIDER1) / 114) ; if (newValue != sliderMiddle) { sliderMiddle = newValue ; displayDigit(sliderMiddle, 1) ; #ifdef TRACE Serial.println("Effect middle") ; #endif switch (effectGroup) { case EFFECT_A: { if (sliderMiddle > 4) { percussionStatus = PERC_OFF ; } else { percussionStatus = PERC_ON ; } sendOrganSysEx() ; break ; } case EFFECT_B: { if (sliderMiddle > 4) { percussionDecay = PERC_SLOW ; } else { percussionDecay = PERC_FAST ; } sendOrganSysEx() ; break ; } case EFFECT_C: { if (sliderMiddle < 3) vibratoDepth = 1 ; else if (sliderMiddle > 6) vibratoDepth = 3; else vibratoDepth = 2; if (vibratoStatus == VIBRATO_OFF) { sendVibratoDepth(VIBRATO_DEPTH_OFF) ; } else { sendVibratoDepth(vibratoDepth) ; } break ; } } } newValue = (analogRead(SLIDER0) / 114) ; if (newValue != sliderRight) { sliderRight = newValue ; displayDigit(sliderRight, 1) ; #ifdef TRACE Serial.println("Effect right") ; #endif switch (effectGroup) { case EFFECT_A: { if (sliderRight > 4) { vibratoStatus = VIBRATO_OFF ; sendVibratoDepth(VIBRATO_DEPTH_OFF) ; } else { vibratoStatus = VIBRATO_ON ; sendVibratoDepth(vibratoDepth) ; } break ; } case EFFECT_B: { if (sliderRight > 4) { percussionHarmonic = PERC_THIRD ; } else { percussionHarmonic = PERC_SECOND ; } sendOrganSysEx() ; break ; } case EFFECT_C: { vibratoSpeed = sliderRight ; sendVibratoSpeed( (vibratoSpeed*VIBRATO_SCALE)+VIBRATO_OFFSET) ; break ; } } } } ////////////////////////////////// // Preset mode ////////////////////////////////// else if (mode == PRESET_MODE) { // Allow nine presets newValue = (analogRead(SLIDER2) / 114) ; if (newValue != sliderLeft) { sliderLeft = newValue ; displayDigit(sliderLeft, 0) ; #ifdef TRACE Serial.println("Preset left") ; #endif } } }