Arduino powered 13note bass pedal with MIDI and synth (build)

Started by anotherjim, November 30, 2018, 11:37:20 AM

Previous topic - Next topic

anotherjim

It's as good as finished, only waiting for a few cosmetic touches. Schematics are ready.
The MIDI/Arduino bit...

I built the Arduino inside the case, so it's power plug has an extender to a 2.1 socket mounted to the outside. I decided to reverse the polarity to suit standard stompbox 9v power with a protection diode fitted for good measure.

The midi channel is read off a 4 way DIP switch. During code setup, Arduino pin 13 is set high and the 4bit binary word from the switch is read back using pins A0 to A3. This is the only time this switch is read, so if midi channel needs changing, you must power/down & up. I think the channel must be settable, but you don't have to do it often. There is a panel "activity" LED run off pin 13 which light briefly at start up (something the board does anyway whatever your code is) then for a longer time after midi channel is set up as a "confidence" indication. Thereafter, the LED lights for the duration of each note played. It's actually flashing at audio frequency, because pin13 also provides the pitch tone for the onboard synth according to the note played on the pedal board.

While no note is playing, pin13 is low and the midi note octave can be read in from pins A0-A2 as a 3 bit word encoded by the rotary switch and diodes D9-13. Also, pinA3 is used to read the velocity pot and this is used in the transmission of midi note on messages. I chose a 5 octave range for midi since I might expect to play percussion or samples over midi using the pedalboard, not just bass sounds.

The pedalboard used has 13 note C-C monophonic low-note priority switching as shown on the diagram. However, the code used is monophonic anyway, as indeed is the onboard synth. It should work just as well with plain normally open contacts that can connect the relevant Arduino pin to 0v when the pedal key is down. The code waits for each note to go off before scanning for any new ones.

The Arduino serial transmit TX pin is buffered by IC3 to drive the midi output, although the board pin is capable of driving R2 directly, IC3 protects from external damage. IC3 doesn't have be a CD4011, it could be any 5v capable inverting logic device like 74HC00 etc.

anotherjim

For completeness, the Arduino sketch needs to be seen. Same as I posted elsewhere but with more comments.
//Arduino midi 13note bass pedals cobbled together by Jim Yale and inspired by...
//https://www.instructables.com/id/Build-MIDI-Bass-Pedals-for-About-150/
//Changed pin usuage to suit smaller Diecimila to Uno types. This is working on a Freeduino clone with ATmega168.
//Code rewrite to suit monophonic operation and additional features.
//MIDI shield not used only TX pin used for hardware MIDI output.
//Added midi channel select via dip switches only during startup. Selected by diode gating when pin13 is high.
//Added octave select rotary switch using 3bit diode coding to pins A0 to A2. Only values 0 to 4 used. Only available when pin13 is low.
//Added velocity pot on pin A3.
//Add tone generator for onboard synth using pin13 and Tone command. Settings cannot be checked while tone sounds.

struct key
{
  int pin;
  int midiKey;
  int toneFreq;
};
// pin usage changed from original Mega board.
//Array used for key scan.
struct key keys[] =
{
  { 2, 24, 2093 },  // C Low. Values here used when key scan i = 0
  { 3, 25, 2217 },  // C#
  { 4, 26, 2349 },  // D
  { 5, 27, 2489 },  // D#
  { 6, 28, 2637 },  // E
  { 7, 29, 2794 },  // F
  { 8, 30, 2960 },  // F#
  { 9, 31, 3136 },  // G
  { 10, 32, 3322 },  // G#
  { 11, 33, 3520 },  // A
  { 12, 34, 3729 },  // A#
  { A5, 35, 3951 },  // B
  { A4, 36, 4186 },  // C High. Values here used when key scan i = 12
  { 0, 0, 0,}        // End of array. Key scan ends when pin value of 0 found.
};

int keyOffset = 12; //octave setting
int keyVelocity = 127;  //max velocity
int MidiChannel = 0; // just a default value
int NoteOnCd = 0x90; // the midi note on command code is the "9", the "0" will be replaced by the midi channel number.
int NoteOffCd = 0x80; // as above, the "8" is the note off command & the "0" is the channel.
int PotVal = 100; // just a default velocity value to start with
int PotPin = 3; // the analog read pin for velocity.
void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);  //Set pin13 to read midi channel data
  // Enable pedal key inputs
  for (int i = 0; keys[i].pin != 0; ++i)
  {
    pinMode(keys[i].pin, INPUT_PULLUP);
  }
  //Use A0 to A4 to read 4 bit midi channel switches
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);
  if (digitalRead(A0) == HIGH) {
    MidiChannel += 1;
  }
  if (digitalRead(A1) == HIGH) {
    MidiChannel += 2;
  }
  if (digitalRead(A2) == HIGH) {
    MidiChannel += 4;
  }
  if (digitalRead(A3) == HIGH) {
    MidiChannel += 8;
  }
  //Construct midi note on & off messages for selected channel by "or"ing with the midi command.
  NoteOnCd |= MidiChannel;
  NoteOffCd |= MidiChannel;
  delay(500); // will light the Activity LED briefly for confidence.
  digitalWrite(13, LOW);    //Deselect channel switches.
  //start serial with midi baudrate 31250
  Serial.begin(31250);
  octave();   //initialise selected octave
  Velocity();   //Initialise velocity pot value
  //End of setup, functions follow.
  //
}
void Midi_Send(byte cmd, byte data1, byte data2)
{
  Serial.write(cmd);
  Serial.write(data1);
  Serial.write(data2);
}

void noteOn(int midiKey)
{
  Midi_Send(NoteOnCd, midiKey, keyVelocity);
}

void noteOff(int midiKey)
{
  Midi_Send(NoteOffCd, midiKey, keyVelocity);
}
void octave()
{
  keyOffset = 0;
  digitalWrite(13, LOW);
  if (digitalRead(A0) == HIGH) {
    keyOffset += 1;
  }
  if (digitalRead(A1) == HIGH) {
    keyOffset += 2;
  }
  if (digitalRead(A2) == HIGH) {
    keyOffset += 4;
  }
  keyOffset *= 12;
}
void Velocity() {
  digitalWrite(13, LOW);
  PotVal = analogRead(PotPin) / 8; //convert 10bit ADC result to 7bit range for midi.
  keyVelocity = (keyVelocity + PotVal) / 2; //average with the previous reading to smooth result.
}
//End of functions, main loop follows
//
void loop() {
  // Main Loop
  byte byte1;
  byte byte2;
  byte byte3;
  int value;
  //
  // Scan pedal keys
  for (int i = 0; keys[i].pin != 0; ++i)
  {
    value = digitalRead(keys[i].pin);
    if (value == LOW)       // Key pointed to by i is on
    {
      noteOn(keys[i].midiKey + keyOffset);      // Send the MIDI note on message
      tone(13, keys[i].toneFreq);               //Play tone for synth
      delay(20);                                //Wait out any contact bouncing before looking for note off
      do                                        //Look for note off
      {
        value = digitalRead(keys[i].pin);
      }
      while (value == LOW);                     //Note off contact is defined by pull-up - it cannot bounce down hence no debounce.
      noteOff(keys[i].midiKey + keyOffset);     //Send MIDI note off
      noTone(13);                               //End synth tone
    }

  }
  octave();   //Update octave setting
  Velocity();   //Update velocity value
}


anotherjim

Now for the onboard synth. The fun bit...

The reason the tone pitch is so high in the Arduino sketch apart from it helps pitch accuracy, is to make the synth hardware a little easier.

The tone input is either present or not and there is no spare Arduino pin to provide a gate/trigger signal to the synth. So an envelope detector is used providing a "note on" voltage on C6. The D type flip-flop IC2a is not needed as a flip-flop, so is wired up to behave just as a non-inverting buffer between the S & Q pins.
When IC2a pin 1 goes high (note on) it clocks IC2b putting the Q output pin13 high. This generates the attack curve of the modulation envelope on C8.
When the voltage on C8 is high enough, it will reset the IC2b flip-flop and the decay phase of the envelope begins. If at any time during attack-decay, the note is ended, IC2a pin goes low and quickly discharges C8 via D16.
R14 is a single "shape" or "contour" control that can adjust the relative timings of the attack and decay cycle. Switch S5 can add extra capacitance to C8 for increased timing of the envelope.
Op-amp IC4 buffers and amplifies the envelope voltage to suit the VCF 9v operating range. A high input impedance CMOS type is used for its close to rail-rail performance.
The mod envelope only affects the VCF. There is no amplitude envelope.

The signal path first goes to an octave divider using the 4bit binary counter IC1a. 3 octaves can be selected with rotary switch S2. Next is another 4bit counter IC1b. This is the waveshaper. It always divides by 16 so now you know why the tone pitches are set so high. A 4  throw rotary switch chooses one of 4 "waveforms".
1: Composite. Actually I called it Oct for octave on the panel because it diode ors the lowest pitch from Q3 with a higher pitch from Q1. This sounds like a low note with a higher one riding on it.
2: 25% pulse. Diode Or's Q3 with Q2 to make a 25% (or 75%) duty cycle pulse. Sounds a lot like a Saw wave.
3: Square. The direct frequency from Q3. a 50% duty cycle square wave.
4. Saw. A staircase sawtooth wave using all count outputs driving a resistor network. Makes a pretty good saw for few parts.
Because the VCF is only a single stage, and extra "pre-filter" low pass is used next. To suit each of the 3 octave ranges, a spare pole of the octave switch is used to change the capacitor size accordingly C11-C13.

The VCF is IC4B and is Escobedo's Quick & Dirty using an LED D2 as a voltage dependent resistor. It has a lot of gain so trimmer R1 is used to pad the input down to avoid clipping. My build needed R1 set about halfway. The Q or resonance pot is replaced by fixed resistor R29 with a bypass switch Q as I find the original idea of this being a pot was pretty much all or nothing anyway.

After the VCF there is an overall low pass tone control. As the VCF is using a non-inverting op-amp, it still passes x1 the input unaffected. Pot R27 can smooth any unwanted high end when a smooth bass tone is needed. the BJT buffer drives the volume control and line output. It is capable of feeding a 10k mixer input without bass loss.

The above synth was designed to fill a need for basic synth and organ bass tone and makes no attempt at plucked/bowed AUB or electric bass guitar sound. It works surprisingly well. Demos/pics will follow....






ElectricDruid

Very nice. I look forward to the demos!

One comment for the next one: If you'd arranged the various with inputs into a matrix you could have saved some pins. For example, if you read a full byte each time (so 8 pins) and have three groups of switches (so 3 more pins) you'd have 24 inputs (8x3), and you'd only use 11 pins. You've got 13 just for the keyboard. Adding another group only costs you a single pin, so for 12 pins, you get 8x4=32 inputs. There's a great page on all the many various ways to handle inputs/outputs on micro controllers over here:

http://www.openmusiclabs.com/learning/digital/input-matrix-scanning/index.html


potul

Using a matrix, does it handle multiple button presses at once?



anotherjim

Funnily enough, the Arduino language doesn't support byte or nibble operations, which is really odd I think. There's probably a library out there for parallel I/O out there, but I wanted to avoid dependency on external code. You can hard code the ports with C language expressions, but I feel that is breaking the simple language of the Arduino and harder for newbies to read. So this single pin scan method is kind of a natural way on this platform.

Actually, if I wanted more than one octave for a mono synth using the tone function, I'd have every note key (all the C's etc) of each octave play the same tone pitch, and with external logic externally select an octave division according to which octave the key pressed is in. You could go on like that forever depending on how high the Tone pitches are. doing that means you only really need 12 note tones, so the pin for low C can be used for something else. Or, maybe shuffle them round to release an "A" pin so another analogue read is possible to get a pitch control for the Tone?
I haven't tried this, but reading up on the Tone function, it seems you can pitch a lot higher than I thought. In theory, it can go ultrasonic high, so an octave higher than I've used should work, although the frequency must be an integer and is only monophonic.

If you used a "matrix", and given the key contacts are independent, it should be able to be polyphonic. But Arduino  Tone is monophonic, at least to a single output pin, so it's academic. Depends how you do it, there is a way that causes contacts to be masked by others and a way that puts all contacts in a selected row to separate port pins so they can each be seen, depending on the column selection. It really requires external logic so for 16 max keys, 2 output bits select 1 of 4 consecutive rows of contacts to 4 input pins. Without external logic, you need 4 out pins using a complete 8bit port.
OTOH, a pair of 74HC148 encoder chips + quad nand can generate a 4 bit code and then you need only 5 pins, all inputs. The extra pin to catch the encoder strobe.




deadastronaut

cool, nice work man....cant wait to see/hear it in action.... 8) 8) 8)
https://www.youtube.com/user/100roberthenry
https://deadastronaut.wixsite.com/effects

chasm reverb/tremshifter/faze filter/abductor II delay/timestream reverb/dreamtime delay/skinwalker hi gain dist/black triangle OD/ nano drums/space patrol fuzz//

ElectricDruid

Quote from: potul on December 01, 2018, 03:14:29 AM
Using a matrix, does it handle multiple button presses at once?

Yes, it can do. That's why the diodes are there - they make sure each switch is isolated from the others, so you only read the row you're looking at. Without the diodes, switches on other rows will interfere.
There are a *lot* of possible approaches though, with different amounts of decoding the rows and/or columns and different amounts of external hardware. That article I posted covers some mixed-up-seeming approaches I'd never have thought of, but they're all valid.

AnotherJim: thanks, I didn't know that about Arduino - I assumed you'd be able to read a whole port but if you can't, like you say, the single pin read is the obvious way. It's simple and readable, after all.

anotherjim

Wot it looks like...

I'm still pondering over the demo arrangement/format.


deadastronaut

https://www.youtube.com/user/100roberthenry
https://deadastronaut.wixsite.com/effects

chasm reverb/tremshifter/faze filter/abductor II delay/timestream reverb/dreamtime delay/skinwalker hi gain dist/black triangle OD/ nano drums/space patrol fuzz//

anotherjim





Some gut shots for yer.

A lot of the build is from recycled stuff. The base is an offcut of particle flooring board. The sides all round are faced from offcuts of 3mm ply sheet. The corners are reinforced with short lengths of aluminium angle. The pedal mechanism straight out of old Farfisa and is enclosed in a diecast box that was simply bolted to the bottom board of the cabinet and depended on the floor to limit pedal travel. Here it is mounted on top of the baseboard. All of that brush painted with matt black "blackboard" paint. Love that stuff - don't need primer & thick enough to be self-filling of the wood.

The pedal switch connections are a multicore cable out of the top of the mechanism and I didn't need to alter any of the wiring. The green wire is the "chassis" ground of the pedal mechanism.
The metal cover/panel isn't my genius metal bending, it was the shield/dust cover of the upper keyboard contact board in the Farfisa. it just happened to be almost perfect to go over the top of the pedal mechanism and put the controls some distance out of the way of stomping feet. The Aerosol gods were with me when I sprayed the primer and satin black on - no runs or orange peel finish!

The little perf board has the 4011 chip. To get started with the project, I needed a midi output so that got built first.
I wasn't thinking of having a synth on board, so I picked a piece of stripboard more than big enough for the dip switch & diodes then the synth got squeezed in afterwards. Some components are on the switches. If you look closely, you may see thin blue Kynar wirewrapping wire used for links. I don't bother using link strips on stripboard but use Kynar front or back for direct connections. That way, stripboard layouts can be just as compact as perfboard. It's something of a "make it up as you go along" artform, so don't ask for layouts!
You may recognise the use of grey ribbon cable from obsolete PC's.


anotherjim

The onboard synth sounds. All bass so headphones/sub recommended.
Demo1 is plain boring scales/octaves/voices with no filter action.
Demo2 is various with filter effects.

https://soundcloud.com/ashdalestudio/sets/basspedalsynth

deadastronaut

awesome....love it,  geddy would be proud...nice work man.  8) 8) 8)
https://www.youtube.com/user/100roberthenry
https://deadastronaut.wixsite.com/effects

chasm reverb/tremshifter/faze filter/abductor II delay/timestream reverb/dreamtime delay/skinwalker hi gain dist/black triangle OD/ nano drums/space patrol fuzz//

Hecrowell

I want to make a set of bass pedals which will produce only bass notes and will plug into an audio amp.
Will this Arduino circuit even come close to what I want?

anotherjim

It has it's own synth with audio output. So yes, the Midi ports, channel switches and velocity pot do not need to be fitted. The Tone input to the synth circuit would come directly from Arduino Dig13 pin.


Ben N

Bringing it back from the dead: Any suggestions as to how to create a sequencer for this?
  • SUPPORTER

garcho

I would send the Arduino commands via UART/MIDI and use a sequencer i already have.

EDIT: any reason you wouldn't just use MIDI? You can make basic CMOS sequencers, but way easier to code something.
  • SUPPORTER
"...and weird on top!"

Ben N

Quote from: garcho on June 03, 2023, 10:50:03 AM
I would send the Arduino commands via UART/MIDI and use a sequencer i already have.

EDIT: any reason you wouldn't just use MIDI? You can make basic CMOS sequencers, but way easier to code something.

Through Pin D0(RX), correct?
  • SUPPORTER

ElectricDruid

Quote from: Ben N on June 04, 2023, 05:14:02 AM
Through Pin D0(RX), correct?
If that's the Arduino's UART Rx pin, then yes. There's no MIDI opto-isolator circuit included in the schematics above either, so you'd need to include that. MIDI *Inputs* need the opto-isolator, MIDI *outputs* don't. I found myself adding this circuit to different things several times, so I did a little PCB for it:

https://electricdruid.net/wp-content/uploads/2016/04/MIDISchematic-3.jpg
https://electricdruid.net/product/midi-inoutthru-pcb/

garcho

I realized only now that there's no MIDI input on the schemo, sorry! If you need help handling MIDI on the Arduino side, I spend a lot of time in that realm, just ask.
  • SUPPORTER
"...and weird on top!"