Receive MIDI alongside internal Sequencer for Teensy drone synthesizer

Status
Not open for further replies.

mbryne

New member
Greetings everyone,

I am looking at putting together a drone synth using a Teensy 3.5. I am using 6 pots, 2 buttons, 1 rotary encoder and an 16x2 LCD to control the parameters of the various audio objects. I will also eventually be adding preset storage and recall. I have put together the hardware for this and it is working well. I am also comfortable putting the software together for this understandably gargantuan task and will be throwing the code and schematics up on github at https://github.com/mbryne/msynth and sharing progress as I go.

The synth will find itself home amongst a few other keyboard synths and Ableton Live and the NDLR super arpeggiator, the latter of which has a drone note playing on every down beat which has prompted me to put together a drone synth for low, ambient audio textures.

Alongside the above features and basic MIDI input I am also hoping to put together a rudimentary pattern editor based on something like the Adafruit FifteenStep Sequencer. It seems though that this and most sequencers are focused on triggering external instruments.

I was actually hoping for an initial bit of guidance about reconciling two seemingly at-odds use cases:

----

Use Case 1: Receive MIDI On/Off Notes from external USB MIDI

This seems relatively straightforward, lots and lots of examples. This will have the drone synth sit alongside Ableton Live and the NDLR with grace and ease.

Use Case 2: Receive MIDI Clock from external USB MIDI and internal Sequencer

This seems more involved, I feel like receiving the external MIDI clock and hooking that into the internal sequencer should be fine, the sequencer could then trigger notes using the code one from Use Case 1 etc.

----

Assuming I have Use Case 1 up and running, I am curious about how Use Case 2 would best be handled:

  1. Would the sequencer send MIDI notes from Case 2, to itself, using existing MIDI triggers from Case 1, on it's own channel?
  2. Would I disable the sequencer when not in use?
  3. Would I even need to take in a midi clock on Use Case 1?
  4. Would I always have the sequencer running, receive MIDI notes and trigger Use Case 1? (this seems inefficient)

I'm sure these questions are all variations on the same chicken and egg situation going on in my head around this, can anyone shed some light on the best direction to head on this before I start putting together the code for either solution?

Any input is greatly appreciated and if I can provide further clarification on anything please let me know.

Many Thanks,
Michael
 

Attachments

  • msynth.jpg
    msynth.jpg
    69.9 KB · Views: 97
Would the sequencer send MIDI notes from Case 2, to itself, using existing MIDI triggers from Case 1, on it's own channel?
Timer code would call note events that would both fire internal NoteOn events for envelop objects in the audio library and send MIDI. That is to say you fire the same code but the midi is not calling the internal code rather they are called together.
 
I have produced similar projects, where I want an internal sequencer running alongside midi input. Its not that difficult really.
What you need to do is set up a global variable called something like TriggerNote, then have an if statement that says

if (TriggerNote == 1)
{
TriggerNote = 0;
#set note frequency#;
}

This creates an independent snippet of code for triggering and setting the frequency of your notes (you can also have any envelope on commands in here too).
You would need a variable in your function called something like NoteNumber, that contains the note number to be converted to a frequency.
The easiest way is to have a lookup table of note frequencies that can be used to directly convert midi numbers to note frequencies, Like this:

const float freqs[180] =
{
8.1758, 8.6620, 9.1770, 9.7227, 10.3009, 10.9134, 11.5623, 12.2499, 12.9783, 13.7500, 14.5676, 15.4339,
16.3516, 17.3239, 18.3540, 19.4454, 20.6017, 21.8268, 23.1247, 24.4997, 25.9565, 27.5000, 29.1352, 30.8677,
32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994, 51.9131, 55.0000, 58.2705, 61.7354,
65.4064, 69.2957, 73.4162, 77.7817, 82.4069, 87.3071, 92.4986, 97.9989, 103.8262, 110.0000, 116.5409, 123.4708,
130.8128, 138.5913, 146.8324, 155.5635, 164.8138, 174.6141, 184.9972, 195.9977, 207.6523, 220.0000, 233.0819, 246.9417,
261.6256, 277.1826, 293.6648, 311.1270, 329.6276, 349.2282, 369.9944, 391.9954, 415.3047, 440.0000, 466.1638, 493.8833,
523.2511, 554.3653, 587.3295, 622.2540, 659.2551, 698.4565, 739.9888, 783.9909, 830.6094, 880.0000, 932.3275, 987.7666,
1046.5023, 1108.7305, 1174.6591, 1244.5079, 1318.5102, 1396.9129, 1479.9777, 1567.9817, 1661.2188, 1760.0000, 1864.6550, 1975.5332,
2093.0045, 2217.4610, 2349.3181, 2489.0159, 2637.0205, 2793.8259, 2959.9554, 3135.9635, 3322.4376, 3520.0000, 3729.3101, 3951.0664,
4186.0090, 4434.9221, 4698.6363, 4978.0317, 5274.0409, 5587.6517, 5919.9108, 6271.9270, 6644.8752, 7040.0000, 7458.6202, 7902.1328,
8372.0181, 8869.8442, 9397.2726, 9956.0635, 10548.0818, 11175.3034, 11839.8215, 12543.8540, 13289.7504, 14080.0000, 14917.2404, 15804.2656,
16744.0362, 17739.6884, 18794.5452, 19912.127, 21096.1636, 22350.6068, 23679.643, 25087.7080, 26579.5008, 28160.0000, 29834.4808, 31608.5312,
33488.0724, 35479.3768, 37589.0904, 39824.254, 42192.3272, 44701.2136, 47359.286, 50175.416, 53159.0016, 56320.0000, 59668.9616, 63217.0624,
66976.1448, 70958.7536, 75178.1808, 79648.508, 84384.6544, 89402.4272, 94718.572, 100350.832, 106318.003, 112640.0000, 119337.923, 126434.125,
133952.29, 141917.507, 150356.362, 159297.016, 168769.309, 178804.854, 189437.144, 200701.664, 212636.006, 225280.0000, 238675.846, 252868.25
};

The next step is to set up your midi code so that whenever it detects a midi note, it stores the note number in a variable called say MidiNoteNumber, then sets TriggerNote to 1, if a note is released you set Triggernote = 2..

You then write a piece of code into your sequencer that stores any sequence notes generated in a variable called say SequenceNoteNumber, again setting TriggerNote = 1 or 2 depending on sequence triggers.

Assuming that you are wanting the keyboard to shift the sequence up and down the scale, you then need to add the sequence and midi notes together to obtain your final frequency:

if (TriggerNote == 1)
{
TriggerNote = 0;
NoteNumber = MidiNoteNumber + SequenceNoteNumber;
Frequency = freqs[NoteNumber]
envelope1.noteOn();
}
else if (TriggerNote == 2)
{
TriggerNote = 0;
envelope1.noteOff();
}

You would not need to disable the sequencer when not in use, just make SequenceNoteNumber = 0, that way the sequencer is not adding its values to the midi input.

if you want to synchronise to a midi clock, then in the code that sorts out which midi clock signals are going to be used you output a TriggerNote = 1 command every time you want the time code to trigger a note.

Because the 'if (TriggerNote == 1)' is independent of the other code blocks, it does not matter where the trigger command comes from (sequencer, midi keyboard or time code), it will trigger a note regardless of source.

I hope that makes sense and helps you with your project :)
 
Great, thank you both very much, that does clarify things and is in line with what I have been finding in similar projects online.

My next main cause of lost sleep is how to maintain a decent separation of concerns from my main synth controller code and each of the individual modes. No specific questions there as I am going to just hook everything up and seek direct feedback on the code after its written.

Thanks again.
 
Hope you'll share some info about whatever you learn along the way. Quite a few people are interested in building snyth projects, but these finer details aren't always obvious. I'm sure anything you take a little time to explain could really help others! If you can share photos or a video, we can also show it on the main website blog. :)
 
Paul, definitely will do, I am about to take 4 days off work to spend full time on this project and will post another update after that.

Based on the hardware controls in the original photos I have settled on the following interface paradigm:

  • Buttons control left and right and switch between "Modes"
  • Each mode controls a different aspect of the synth, with the 7 knobs controlling a different bank of parameters
  • The modes and corresponding synth parameters are defined in separate files for organisational purposes
  • I have split out my main sketch into seperate files that handle the Hardware interface and Synth audio generation
  • The main sketch serves as the interface / controller between the different components
  • The 16x2 LCD display will show the current mode by default and will switch to the currently edited parameter and display a bar graph for its value
As the 6 knobs were potentiometers and their function would change depending on the mode, I was going to program in 'catch' functionality found on other synths. This would not start to update the synth parameter until the potentiometer position matched its underlying value. I could quickly tell this was going to be less than ideal as I started to think about each individual mode so I have recently ordered 6 more rotary encoders and will drop these in and solder up the board into its final configuration.

My example modes at this stage are things like MIXER, OSCILLATOR 1, OSCILLATOR 2, SAMPLE, NOISE and some of the example parameters are things like WAVE TYPE, ATTACK, DECAY etc.

I am a software developer as a profession but this is my first real foray into C/C++ and I have been leaning on a few external resources to bring me up to speed. This has resulted in the aforementioned separation of concerns not being as clean or well structured as I would like but I am definitely open to suggestions if anyone has any. I also hope that my approach may be slightly more approachable for another beginner or intermediate developer to pick up rather than going in heavily around accepted design patterns and abstractions.

A few resources I have found really valuable:

https://www.pjrc.com/teensy/td_midi.html - Obviously
https://www.codeproject.com/Articles/721796/Design-patterns-in-action-with-Arduino - This is quite a heavy level of abstraction but I do feel this had me considering the best and most appropriate places I wanted to divide my functionality into
https://github.com/jmechnich/TeensySynth - I have found this to be the best "synth" implementation of teensy after pulling down every "teensy synth" repo on Github. It is just implemented well when it comes to midi CC and note inputs and just has made the most sense to me. Definitely worth checking out.

Thanks Paul for all your efforts on the Teensy platform. The work you and others do in this space lifts us all up and I can't wait to keep heading down this road and seeing what I uncover.

I will keep posting updates as the project progresses.
 
Status
Not open for further replies.
Back
Top