Adafruit Macropad FULLY controlling all parameters on daisy seed 7 Voice VA Synthesizer

(Made a couple edits to fix a crash that was happening when the pitch bend went beyond 7. I’m not sure why it was happening but I limited to 7 and it seems stable now. And a few more to fix the color issues. Its getting there. :slight_smile: )

Hey guys! Just messing around with what I have, and I got an old Adafruit Macropad to fully control a daisy seed with Daisy-Seed-7-Voice-VA-Synthesizer loaded on it. I have not touched the daisy code, just altered the Macropad midi code. Its not perfect, but heres the circuitPython code for the Macropad and a video demo. Any help on cleaning up the code would be phenomenal. Let me know your thoughts!

# SPDX-FileCopyrightText: 2022 John Park for Adafruit Industries
# SPDX-License-Identifier: MIT
# Macropad MIDI Tester
# Play MIDI notes with keys
# Click encoder to switch modes
# Turn encoder to adjust CC, ProgramChange, or PitchBend
from adafruit_macropad import MacroPad
from adafruit_macropad import ProgramChange
from rainbowio import colorwheel

CC_NUM = 7  # select your CC number

macropad = MacroPad(rotation=180)  # create the macropad object, rotate orientation
macropad.display.auto_refresh = False  # avoid lag

# --- Pixel setup --- #
key_color = colorwheel(130)  # fill with cyan to start
macropad.pixels.brightness = 0.5
macropad.pixels.fill(key_color)

# --- MIDI variables ---
mode = 0
mode_text = ["Paramater", ("CC #%s" % (CC_NUM)), "Pitch Bend"]
midi_values = [0, 16, 8]  # bank, cc value, pitch
# Chromatic scale starting with C3 as bottom left keyswitch (or use any notes you like)
midi_notes = [57, 58, 59, 54, 55, 56, 51, 52, 53, 48, 49, 50]

# --- Display text setup ---
text_lines = macropad.display_text("Caidens Synth Beta")
text_lines[0].text = "Paramater: {}".format(
    midi_values[0] + 1
)  # Patch display offset by 1
if midi_values[0] + 1 == 1:
    text_lines[1].text = "Master Tuning"
elif midi_values[0] + 1 == 2:
    text_lines[1].text = "Osc #1 Waveform"
elif midi_values[0] + 1 == 3:
    text_lines[1].text = "Osc #2 Waveform"
elif midi_values[0] + 1 == 4:
    text_lines[1].text = "Oscillator Mix"
elif midi_values[0] + 1 == 5:
    text_lines[1].text = "Oscillator #2 De-Tune"
elif midi_values[0] + 1 == 6:
    text_lines[1].text = "Oscillator #2 Scale"
elif midi_values[0] + 1 == 7:
    text_lines[1].text = "Resonance"
elif midi_values[0] + 1 == 8:
    text_lines[1].text = "Osc #1 Pulse Width"
elif midi_values[0] + 1 == 9:
    text_lines[1].text = "Osc #2 Pulse Width"
elif midi_values[0] + 1 == 10:
    text_lines[1].text = "VCF Attack"
elif midi_values[0] + 1 == 11:
    text_lines[1].text = "VCF Decay"
elif midi_values[0] + 1 == 12:
    text_lines[1].text = "VCF Sustain"
elif midi_values[0] + 1 == 13:
    text_lines[1].text = "VCF Release"
elif midi_values[0] + 1 == 14:
    text_lines[1].text = "VCA Attack"
elif midi_values[0] + 1 == 15:
    text_lines[1].text = "VCA Decay"
elif midi_values[0] + 1 == 16:
    text_lines[1].text = "VCA Sustain"
elif midi_values[0] + 1 == 17:
    text_lines[1].text = "VCA Release"
elif midi_values[0] + 1 == 18:
    text_lines[1].text = "VCF Key Follow"
elif midi_values[0] + 1 == 19:
    text_lines[1].text = "ENV Key Follow"
elif midi_values[0] + 1 == 20:
    text_lines[1].text = "Velocity Routing"
elif midi_values[0] + 1 == 21:
    text_lines[1].text = "VCF Envelope level"
elif midi_values[0] + 1 == 22:
    text_lines[1].text = "Mod Wheel LFO Rate"
elif midi_values[0] + 1 == 23:
    text_lines[1].text = "PWM LFO Rate"
elif midi_values[0] + 1 == 24:
    text_lines[1].text = "PWM LFO Mod"
elif midi_values[0] + 1 == 25:
    text_lines[1].text = "PWM2 LFO Rate"
elif midi_values[0] + 1 == 26:
    text_lines[1].text = "PWM2 LFO Mod"
elif midi_values[0] + 1 == 27:
    text_lines[1].text = "VCA/VCF LFO rate"
elif midi_values[0] + 1 == 28:
    text_lines[1].text = "VCA/VCF LFO Mod"
elif midi_values[0] + 1 == 29:
    text_lines[1].text = "VCA/VCF LFO Waveform"

text_lines.show()

last_knob_pos = macropad.encoder  # store knob position state

while True:
    while macropad.keys.events:  # check for key press or release
        key_event = macropad.keys.events.get()
        if key_event:
            if key_event.pressed:
                key = key_event.key_number
                macropad.midi.send(
                    macropad.NoteOn(midi_notes[key], 120)
                )  # send midi noteon
                macropad.pixels[key] = colorwheel(188)  # light up green
                if midi_values[0] + 1 == 1:
                    text_lines[1].text = "Master Tuning"
                elif midi_values[0] + 1 == 2:
                    text_lines[1].text = "Osc #1 Waveform"
                elif midi_values[0] + 1 == 3:
                    text_lines[1].text = "Osc #2 Waveform"
                elif midi_values[0] + 1 == 4:
                    text_lines[1].text = "Oscillator Mix"
                elif midi_values[0] + 1 == 5:
                    text_lines[1].text = "Oscillator #2 De-Tune"
                elif midi_values[0] + 1 == 6:
                    text_lines[1].text = "Oscillator #2 Scale"
                elif midi_values[0] + 1 == 7:
                    text_lines[1].text = "Resonance"
                elif midi_values[0] + 1 == 8:
                    text_lines[1].text = "Osc #1 Pulse Width"
                elif midi_values[0] + 1 == 9:
                    text_lines[1].text = "Osc #2 Pulse Width"
                elif midi_values[0] + 1 == 10:
                    text_lines[1].text = "VCF Attack"
                elif midi_values[0] + 1 == 11:
                    text_lines[1].text = "VCF Decay"
                elif midi_values[0] + 1 == 12:
                    text_lines[1].text = "VCF Sustain"
                elif midi_values[0] + 1 == 13:
                    text_lines[1].text = "VCF Release"
                elif midi_values[0] + 1 == 14:
                    text_lines[1].text = "VCA Attack"
                elif midi_values[0] + 1 == 15:
                    text_lines[1].text = "VCA Decay"
                elif midi_values[0] + 1 == 16:
                    text_lines[1].text = "VCA Sustain"
                elif midi_values[0] + 1 == 17:
                    text_lines[1].text = "VCA Release"
                elif midi_values[0] + 1 == 18:
                    text_lines[1].text = "VCF Key Follow"
                elif midi_values[0] + 1 == 19:
                    text_lines[1].text = "ENV Key Follow"
                elif midi_values[0] + 1 == 20:
                    text_lines[1].text = "Velocity Routing"
                elif midi_values[0] + 1 == 21:
                    text_lines[1].text = "VCF Envelope level"
                elif midi_values[0] + 1 == 22:
                    text_lines[1].text = "Mod Wheel LFO Rate"
                elif midi_values[0] + 1 == 23:
                    text_lines[1].text = "PWM LFO Rate"
                elif midi_values[0] + 1 == 24:
                    text_lines[1].text = "PWM LFO Mod"
                elif midi_values[0] + 1 == 25:
                    text_lines[1].text = "PWM2 LFO Rate"
                elif midi_values[0] + 1 == 26:
                    text_lines[1].text = "PWM2 LFO Mod"
                elif midi_values[0] + 1 == 27:
                    text_lines[1].text = "VCA/VCF LFO rate"
                elif midi_values[0] + 1 == 28:
                    text_lines[1].text = "VCA/VCF LFO Mod"
                elif midi_values[0] + 1 == 29:
                    text_lines[1].text = "VCA/VCF LFO Waveform"

            if key_event.released:
                key = key_event.key_number
                macropad.midi.send(macropad.NoteOff(midi_notes[key], 0))
                macropad.pixels[
                    key
                ] = key_color  # return to color set by encoder bank value
                if midi_values[0] + 1 == 1:
                    text_lines[1].text = "Master Tuning"
                elif midi_values[0] + 1 == 2:
                    text_lines[1].text = "Osc #1 Waveform"
                elif midi_values[0] + 1 == 3:
                    text_lines[1].text = "Osc #2 Waveform"
                elif midi_values[0] + 1 == 4:
                    text_lines[1].text = "Oscillator Mix"
                elif midi_values[0] + 1 == 5:
                    text_lines[1].text = "Oscillator #2 De-Tune"
                elif midi_values[0] + 1 == 6:
                    text_lines[1].text = "Oscillator #2 Scale"
                elif midi_values[0] + 1 == 7:
                    text_lines[1].text = "Resonance"
                elif midi_values[0] + 1 == 8:
                    text_lines[1].text = "Osc #1 Pulse Width"
                elif midi_values[0] + 1 == 9:
                    text_lines[1].text = "Osc #2 Pulse Width"
                elif midi_values[0] + 1 == 10:
                    text_lines[1].text = "VCF Attack"
                elif midi_values[0] + 1 == 11:
                    text_lines[1].text = "VCF Decay"
                elif midi_values[0] + 1 == 12:
                    text_lines[1].text = "VCF Sustain"
                elif midi_values[0] + 1 == 13:
                    text_lines[1].text = "VCF Release"
                elif midi_values[0] + 1 == 14:
                    text_lines[1].text = "VCA Attack"
                elif midi_values[0] + 1 == 15:
                    text_lines[1].text = "VCA Decay"
                elif midi_values[0] + 1 == 16:
                    text_lines[1].text = "VCA Sustain"
                elif midi_values[0] + 1 == 17:
                    text_lines[1].text = "VCA Release"
                elif midi_values[0] + 1 == 18:
                    text_lines[1].text = "VCF Key Follow"
                elif midi_values[0] + 1 == 19:
                    text_lines[1].text = "ENV Key Follow"
                elif midi_values[0] + 1 == 20:
                    text_lines[1].text = "Velocity Routing"
                elif midi_values[0] + 1 == 21:
                    text_lines[1].text = "VCF Envelope level"
                elif midi_values[0] + 1 == 22:
                    text_lines[1].text = "Mod Wheel LFO Rate"
                elif midi_values[0] + 1 == 23:
                    text_lines[1].text = "PWM LFO Rate"
                elif midi_values[0] + 1 == 24:
                    text_lines[1].text = "PWM LFO Mod"
                elif midi_values[0] + 1 == 25:
                    text_lines[1].text = "PWM2 LFO Rate"
                elif midi_values[0] + 1 == 26:
                    text_lines[1].text = "PWM2 LFO Mod"
                elif midi_values[0] + 1 == 27:
                    text_lines[1].text = "VCA/VCF LFO rate"
                elif midi_values[0] + 1 == 28:
                    text_lines[1].text = "VCA/VCF LFO Mod"
                elif midi_values[0] + 1 == 29:
                    text_lines[1].text = "VCA/VCF LFO Waveform"

    macropad.encoder_switch_debounced.update()  # check the knob switch for press or release
    if macropad.encoder_switch_debounced.pressed:
        mode = (mode + 1) % 3
        if mode == 0:
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] + 1,
            )
        elif mode == 1:
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                int(midi_values[mode] * 4.1),
            )
        else:
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] - 8,
            )
        macropad.red_led = macropad.encoder_switch
        text_lines[1].text = " "  # clear the note line

    if macropad.encoder_switch_debounced.released:
        if midi_values[0] + 1 == 1:
            text_lines[1].text = "Master Tuning"
        elif midi_values[0] + 1 == 2:
            text_lines[1].text = "Osc #1 Waveform"
        elif midi_values[0] + 1 == 3:
            text_lines[1].text = "Osc #2 Waveform"
        elif midi_values[0] + 1 == 4:
            text_lines[1].text = "Oscillator Mix"
        elif midi_values[0] + 1 == 5:
            text_lines[1].text = "Oscillator #2 De-Tune"
        elif midi_values[0] + 1 == 6:
            text_lines[1].text = "Oscillator #2 Scale"
        elif midi_values[0] + 1 == 7:
            text_lines[1].text = "Resonance"
        elif midi_values[0] + 1 == 8:
            text_lines[1].text = "Osc #1 Pulse Width"
        elif midi_values[0] + 1 == 9:
            text_lines[1].text = "Osc #2 Pulse Width"
        elif midi_values[0] + 1 == 10:
            text_lines[1].text = "VCF Attack"
        elif midi_values[0] + 1 == 11:
            text_lines[1].text = "VCF Decay"
        elif midi_values[0] + 1 == 12:
            text_lines[1].text = "VCF Sustain"
        elif midi_values[0] + 1 == 13:
            text_lines[1].text = "VCF Release"
        elif midi_values[0] + 1 == 14:
            text_lines[1].text = "VCA Attack"
        elif midi_values[0] + 1 == 15:
            text_lines[1].text = "VCA Decay"
        elif midi_values[0] + 1 == 16:
            text_lines[1].text = "VCA Sustain"
        elif midi_values[0] + 1 == 17:
            text_lines[1].text = "VCA Release"
        elif midi_values[0] + 1 == 18:
            text_lines[1].text = "VCF Key Follow"
        elif midi_values[0] + 1 == 19:
            text_lines[1].text = "ENV Key Follow"
        elif midi_values[0] + 1 == 20:
            text_lines[1].text = "Velocity Routing"
        elif midi_values[0] + 1 == 21:
            text_lines[1].text = "VCF Envelope level"
        elif midi_values[0] + 1 == 22:
            text_lines[1].text = "Mod Wheel LFO Rate"
        elif midi_values[0] + 1 == 23:
            text_lines[1].text = "PWM LFO Rate"
        elif midi_values[0] + 1 == 24:
            text_lines[1].text = "PWM LFO Mod"
        elif midi_values[0] + 1 == 25:
            text_lines[1].text = "PWM2 LFO Rate"
        elif midi_values[0] + 1 == 26:
            text_lines[1].text = "PWM2 LFO Mod"
        elif midi_values[0] + 1 == 27:
            text_lines[1].text = "VCA/VCF LFO rate"
        elif midi_values[0] + 1 == 28:
            text_lines[1].text = "VCA/VCF LFO Mod"
        elif midi_values[0] + 1 == 29:
            text_lines[1].text = "VCA/VCF LFO Waveform"

    macropad.red_led = macropad.encoder_switch

    if last_knob_pos is not macropad.encoder:  # knob has been turned
        knob_pos = macropad.encoder  # read encoder
        knob_delta = knob_pos - last_knob_pos  # compute knob_delta since last read
        last_knob_pos = knob_pos  # save new reading

        if mode == 0:  # ProgramChange
            midi_values[mode] = min(
                max(midi_values[mode] + knob_delta, 0), 28
            )  # delta + minmax
            macropad.midi.send(
                macropad.ProgramChange(midi_values[mode])
            )  # midi send ProgramChange
            macropad.midi.send(macropad.ControlChange(0, int(midi_values[mode]) + 98))
            key_color = colorwheel(
                int(midi_values[mode]) + 130
            )  # change key_color as patches change
            macropad.pixels.fill(key_color)
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] + 1,
            )

        if mode == 1:  # CC
            midi_values[mode] = min(
                max(midi_values[mode] + knob_delta, 0), 31
            )  # scale the value
            macropad.midi.send(
                macropad.ControlChange(CC_NUM, int(midi_values[mode] * 4.1))
            )
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                int(midi_values[mode] * 4.1),
            )
        if mode == 2:  # PitchBend
            midi_values[mode] = min(
                max(midi_values[mode] + knob_delta, 0), 15
            )  # smaller range
            macropad.midi.send(
                macropad.PitchBend((midi_values[mode] * 1024))
            )  # range * mult = 16384
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] - 8,
            )

    last_knob_pos = macropad.encoder

    macropad.display.refresh()

Here’s a quick demo

PS: This is my first time attempting CircuitPython, thats why that big if/else list of parameters is all over the place. I need some help making that into a class so I can just call it when I need it and slim down the controller code. Or perhaps someone has a better idea. I’m extremely open to suggestions.

Finally I’d LOVE to get my Mac out of the mix and just use the daisy as a USB host like I can on a teensy. Any ideas about that would be amazing. The Macropad has Stemma QT out but I’m not sure what to do with that. Thanks again!

The Macropad code above actually works perfectly with:
Daisy-Seed-7-Voice-VA-Synthesizer
Daisy-Seed-Synthesizer-with-KarlsenLPF

And works with this as well you just have to change the names of the parameters but it’s all the same concept.
STM32-Daisy-Seed-NucleoSynth Public

2 Likes

Nice!!! This is a really cool way to sound design with a Daisy synth that has bunch of parameters!
The video demonstrates it well :smiley:

Thank you for sharing @subminimal!!

1 Like

HOLY CRAP THANKS SO MUCH TAKUMI! You’re like a celebrity to me. I actually just linked them through a Rasbberry pi and now its like a self contained system. I’m super stoked on it. Any thoughts on how to deal with that super long list of if elif parameters in the controller code? I feel like just pasting it 3 times was super inelegant.

1 Like

I’d use midi_values[0]+1 as an index into a list of all those duplicate strings.

1 Like

Thank’s so much for the reply tele_player. I’m a little code rusty, could you provide a little more detail? Like put that whole list into an array with midi_values[0]+1 as the index? Not 100% sure the best way to do that.

Switching to computer, typing on iPad is a drag.

1 Like

I moved the whole shebang to a Raspberry Pi 5 and it works! I had an old touchscreen setup and gave it a shot. Loaded up reaper and bam. Shrunk! Next a Pi Zero and were in real business!

Here’s a short demo:
Adafruit Macropad Midi / Daisy Seed Synth moved to Raspberry Pi

The only thing left I noticed is some of the parameters top off way higher than 127 which is my max atm so I’m going to have to figure out how to change the parameter range for each program change. You can see in this section of the seed code here:

ControlChangeEvent p = m.AsControlChange();
            switch(p.control_number)
            {
				case 1: // Modulation Wheel
					vasynth.lfo_amp_ = ((float)p.value / 127.0f);
                    vasynth.SetLFO();
                    break;
				case 7: // Data Slider Default (Volume)
					switch(param_)
					{
						case 0: // This is set as the default parameter
						{
							master_tune = 1.0f - ((float)p.value / 64.0f);
							break;
						}
						case 1:
						{
							switch(p.value >> 5)
							{
								case 0:
									vasynth.waveform_ = WAVE_TRI;
									break;
								case 1:
									vasynth.waveform_ = WAVE_SAW;
									break;
								case 2:
									vasynth.waveform_ = WAVE_SQUARE;
									break;
								case 3:
									vasynth.waveform_ = WAVE_POLYBLEP_SAW;
									break;
							}
							vasynth.SetWaveform();
							break;
						}
						case 2:
						{
							switch(p.value >> 5)
							{
								case 0:
									vasynth.osc2_waveform_ = WAVE_TRI;
									break;
								case 1:
									vasynth.osc2_waveform_ = WAVE_SAW;
									break;
								case 2:
									vasynth.osc2_waveform_ = WAVE_SQUARE;
									break;
								case 3:
									vasynth.osc2_waveform_ = WAVE_POLYBLEP_SAW;
									break;
							}
							vasynth.SetWaveform();
							break;
						}
						case 3:
						{
							vasynth.osc_mix_ = ((float)p.value / 127.0f);
							break;
						}
						case 4:
						{
							vasynth.osc2_detune_ = ((float)p.value / 255.0f);
							break;
						}
						case 5:
						{
							vasynth.osc2_transpose_ = (1.0f + ((float)p.value / 127.0f));
							break;
						}
						case 6:
						{
							vasynth.filter_res_ = ((float)p.value / 127.0f);
                    		vasynth.SetFilter();
							break;
						}
						case 7:
						{
							vasynth.osc_pw_ = ((float)p.value / 255.0f);
							break;
						}
						case 8:
						{
							vasynth.osc2_pw_ = ((float)p.value / 255.0f);
							break;
						}
						case 9:
						{
							vasynth.eg_f_attack_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 10:
						{
							vasynth.eg_f_decay_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 11:
						{
							vasynth.eg_f_sustain_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 12:
						{
							vasynth.eg_f_release_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 13:
						{
							vasynth.eg_a_attack_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 14:
						{
							vasynth.eg_a_decay_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 15:
						{
							vasynth.eg_a_sustain_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 16:
						{
							vasynth.eg_a_release_ = ((float)p.value / 127.0f);
							vasynth.SetEG();
							break;
						}
						case 17:
						{
							vasynth.vcf_kbd_follow_= ((float)p.value / 127.0f);
							break;
						}
						case 18:
						{
							vasynth.env_kbd_follow_ = ((float)p.value / 127.0f);
							break;
						}
						case 19:
						{
							vasynth.vel_select_ = p.value >> 5;
							break;
						}
						case 20:
						{
							vasynth.eg_f_amount_ = ((float)p.value / 127.0f);
							break;
						}
						case 21:
						{
							vasynth.lfo_freq_ = ((float)p.value / 127.0f);
							vasynth.SetLFO();
							break;
						}
						case 22:
						{
							vasynth.pwmlfo_freq_ = ((float)p.value / 127.0f);
							vasynth.SetPWMLFO();
							break;
						}
						case 23:
						{
							vasynth.pwmlfo_amp_ = ((float)p.value / 511.0f);
							vasynth.SetPWMLFO();
							break;
						}
						case 24:
						{
							vasynth.pwm2lfo_freq_ = ((float)p.value / 127.0f);
							vasynth.SetPWM2LFO();
							break;
						}
						case 25:
						{
							vasynth.pwm2lfo_amp_ = ((float)p.value / 511.0f);
							vasynth.SetPWM2LFO();
							break;
						}
						case 26:
						{
							vasynth.vcavcflfo_freq_ = ((float)p.value / 127.0f);
							vasynth.SetVCAVCFLFO();
							break;
						}
						case 27:
						{
							vasynth.vcavcflfo_amp_ = ((float)p.value / 127.0f);
							vasynth.SetVCAVCFLFO();
							break;
						}
						case 28:
						{
							switch(p.value >> 5)
							{
								case 0:
									vasynth.vcavcflfo_waveform_ = WAVE_TRI;
									break;
								case 1:
									vasynth.vcavcflfo_waveform_ = WAVE_SAW;
									break;
								case 2:
									vasynth.vcavcflfo_waveform_ = WAVE_SQUARE;
									break;
								case 3:
									vasynth.vcavcflfo_waveform_ = WAVE_POLYBLEP_SAW;
									break;
							}
							vasynth.SetVCAVCFLFO();
							break;
						}
					}
					break;

OSC Detune and pulsewidth are 255, pwm2lfo is 511!

Once I figure that out I think I’m golden.

I agree. Thank you for taking the time my friend I truly appreciate you.

Something like this:

text_strings = [ "Master Tuning", "Osc #1 Waveform", "Osc #2 Waveform", "Oscillator Mix",
"Oscillator #2 De-Tune", "Oscillator #2 Scale", "Resonance", "Osc #1 Pulse Width",
"Osc #2 Pulse Width", "VCF Attack", "VCF Decay", "VCF Sustain", "VCF Release",
"VCA Attack", "VCA Decay", "VCA Sustain", "VCA Release", "VCF Key Follow", 
"ENV Key Follow", "Velocity Routing", "VCF Envelope level", "Mod Wheel LFO Rate", 
"PWM LFO Rate", "PWM LFO Mod", "PWM2 LFO Rate", "PWM2 LFO Mod", "VCA/VCF LFO rate",
"VCA/VCF LFO Mod", "VCA/VCF LFO Waveform" ]

So, instead of:

if midi_values[0] + 1 == 1:
            text_lines[1].text = "Master Tuning"

you could do:

            text_lines[1].text = text_strings[midi_values[0]]

(I’m so glad I didn’t try to type that on iPad!)

2 Likes

OH wow thank you so much for takin g the time to help me! I cant tell you how much I apprecieate it. OK awesome I completely get that. One question though. Right now I have each parameter going from 0-127. It was a hack job. As I mentioned above some need to go to 255, even 511, in one case 64. How would you handle that?

Holy crap tele_player… I just implented your solution like so:

# SPDX-FileCopyrightText: 2022 John Park for Adafruit Industries
# SPDX-License-Identifier: MIT
# Macropad MIDI Tester
# Play MIDI notes with keys
# Click encoder to switch modes
# Turn encoder to adjust CC, ProgramChange, or PitchBend
from adafruit_macropad import MacroPad
from adafruit_macropad import ProgramChange
from rainbowio import colorwheel

CC_NUM = 7  # select your CC number

macropad = MacroPad(rotation=180)  # create the macropad object, rotate orientation
macropad.display.auto_refresh = False  # avoid lag

# --- Pixel setup --- #
key_color = colorwheel(130)  # fill with cyan to start
macropad.pixels.brightness = 0.1
macropad.pixels.fill(key_color)

# --- MIDI variables ---
mode = 0
mode_text = ["Paramater", ("CC #%s" % (CC_NUM)), "Pitch Bend"]
midi_values = [0, 16, 8]  # bank, cc value, pitch
# Chromatic scale starting with C3 as bottom left keyswitch (or use any notes you like)
midi_notes = [57, 58, 59, 54, 55, 56, 51, 52, 53, 48, 49, 50]

text_strings = [ "Master Tuning", "Osc #1 Waveform", "Osc #2 Waveform", "Oscillator Mix",
"Oscillator #2 De-Tune", "Oscillator #2 Scale", "Resonance", "Osc #1 Pulse Width",
"Osc #2 Pulse Width", "VCF Attack", "VCF Decay", "VCF Sustain", "VCF Release",
"VCA Attack", "VCA Decay", "VCA Sustain", "VCA Release", "VCF Key Follow",
"ENV Key Follow", "Velocity Routing", "VCF Envelope level", "Mod Wheel LFO Rate",
"PWM LFO Rate", "PWM LFO Mod", "PWM2 LFO Rate", "PWM2 LFO Mod", "VCA/VCF LFO rate",
"VCA/VCF LFO Mod", "VCA/VCF LFO Waveform" ]

# --- Display text setup ---
text_lines = macropad.display_text("Caidens Synth Beta")
text_lines[0].text = "Paramater: {}".format(
    midi_values[0] + 1)

text_lines.show()

last_knob_pos = macropad.encoder  # store knob position state

while True:
    while macropad.keys.events:  # check for key press or release
        key_event = macropad.keys.events.get()
        if key_event:
            if key_event.pressed:
                key = key_event.key_number
                macropad.midi.send(
                    macropad.NoteOn(midi_notes[key], 120)
                )  # send midi noteon
                macropad.pixels[key] = colorwheel(188)  # light up green

            if key_event.released:
                key = key_event.key_number
                macropad.midi.send(macropad.NoteOff(midi_notes[key], 0))
                macropad.pixels[
                    key
                ] = key_color  # return to color set by encoder bank value


    macropad.encoder_switch_debounced.update()  # check the knob switch for press or release
    if macropad.encoder_switch_debounced.pressed:
        mode = (mode + 1) % 3
        if mode == 0:
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] + 1,
            )
        elif mode == 1:
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                int(midi_values[mode]),
            )
        else:
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] - 8,
            )
        macropad.red_led = macropad.encoder_switch
        text_lines[1].text = " "  # clear the note line

    if macropad.encoder_switch_debounced.released:

        macropad.red_led = macropad.encoder_switch

    if last_knob_pos is not macropad.encoder:  # knob has been turned
        knob_pos = macropad.encoder  # read encoder
        knob_delta = knob_pos - last_knob_pos  # compute knob_delta since last read
        last_knob_pos = knob_pos  # save new reading

        if mode == 0:  # ProgramChange
            midi_values[mode] = min(
                max(midi_values[mode] + knob_delta, 0), 28
            )  # delta + minmax
            macropad.midi.send(
                macropad.ProgramChange(midi_values[mode])
            )  # midi send ProgramChange
            macropad.midi.send(macropad.ControlChange(0, int(midi_values[mode]) + 98))
            key_color = colorwheel(
                int(midi_values[mode]) + 130
            )  # change key_color as patches change
            macropad.pixels.fill(key_color)
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] + 1,
            )

        if mode == 1:  # CC
            midi_values[mode] = min(
                max(midi_values[mode] + knob_delta, 0), 127
            )  # scale the value
            macropad.midi.send(
                macropad.ControlChange(CC_NUM, int(midi_values[mode]))
            )
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                int(midi_values[mode]),
            )
        if mode == 2:  # PitchBend
            midi_values[mode] = min(
                max(midi_values[mode] + knob_delta, 0), 15
            )  # smaller range
            macropad.midi.send(
                macropad.PitchBend((midi_values[mode] * 1024))
            )  # range * mult = 16384
            text_lines[0].text = "Mode: %s %d" % (
                mode_text[mode],
                midi_values[mode] - 8,
            )

    last_knob_pos = macropad.encoder
    text_lines[1].text = text_strings[midi_values[0]]
    macropad.display.refresh()

SUCCESS!!! THANK YOU SOOOOOOO SO MUCH!

2 Likes

If the parameter range thing hasn’t been solved yet, a similar list for the scaling coefficients might be the way to go.

I’m much more used to C/C++, recently started tinkering in python. In C++, I’d be using arrays of structs.

1 Like

So in 2000 I taught myself Macromedia Flash Actionscript. Got a sweet gig with it too. We all know how that went… Since then I’ve been a bit of a script kiddie, lots of Arduino (I even started my daisy journey with DaisyDuino and quickly realized it was super limited compared to the C++ stuff) and the original Processing sketches. I got super into building midi controllers with teensy (its SO easy) but I wanted to make full blown instruments. Then I found Daisy. I absolutely love it. I gotta admit, the community can be a bit… stagnant. But I REALLY truly thank you guys especially tele_player for ALL this its a huge help. CircuitPython seems cool but it gets finicky with all the indentations etc. I’m gonna keep hacking away with the values but if you think of anything, do drop me a line. Thanks so much again. This means so much to me! I feel like this setup ( a macropad midi controller, daisy seed with a Raspi interface) is a cool, quick and easy way to get a real live synth up and running super quick. If I can get daisy to be a true usb host like teensy does I could feasibly even cut out the Raspi which would be amazing. Anyway thats my history and my plans. Thanks again everyone! You have an AMAZING product here.

1 Like