Skip to content

Instantly share code, notes, and snippets.

@spotlesscoder
Forked from benongithub/picoSequencer.py
Created March 3, 2024 21:08

Revisions

  1. @benongithub benongithub revised this gist May 15, 2021. No changes.
  2. @benongithub benongithub created this gist May 15, 2021.
    238 changes: 238 additions & 0 deletions picoSequencer.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,238 @@
    import time
    import board
    import neopixel
    import math
    import rotaryio
    import board
    import digitalio
    import adafruit_rgbled
    import busio
    import adafruit_midi
    from adafruit_midi.control_change import ControlChange
    from adafruit_midi.note_off import NoteOff
    from adafruit_midi.note_on import NoteOn
    from adafruit_midi.pitch_bend import PitchBend
    from adafruit_midi.timing_Clock import TimingClock
    from adafruit_midi.start import Start
    from adafruit_midi.stop import Stop


    ###################### Midi Setup
    midiuart = busio.UART(board.GP16, board.GP17, baudrate=31250, timeout=0.00001)
    midi = adafruit_midi.MIDI(
    midi_in=midiuart, in_channel=0, midi_out=midiuart, out_channel=0, debug=True
    )


    ###################### Neopixel Setup
    num_pixels = 24

    pixels = neopixel.NeoPixel(board.GP9, num_pixels, pixel_order=neopixel.GRB, auto_write=False)
    pixels.brightness = 0.1
    pixels.fill((0,0,0))
    pixels.show()

    render_last = time.monotonic_ns()
    render_delay = 100000000

    num_small_pixels = 12

    small_pixels = neopixel.NeoPixel(board.GP8, num_small_pixels, pixel_order=neopixel.GRB, auto_write=False)
    small_pixels.brightness = 0.05
    small_pixels.fill((0,0,0))
    small_pixels.show()

    def translate(value, leftMin, leftMax, rightMin, rightMax):
    leftSpan = leftMax - leftMin
    rightSpan = rightMax - rightMin
    valueScaled = float(value - leftMin) / float(leftSpan)
    return rightMin + (valueScaled * rightSpan)

    def amount_small_circle(amount = 0, color=(0, 150, 156)):
    for i in range(num_small_pixels):
    if i < amount:
    small_pixels[i] = color
    else:
    small_pixels[i] = (0,0,0)
    small_pixels.show()

    ###################### Rotary Encoder Setup
    button = digitalio.DigitalInOut(board.GP10)
    button.direction = digitalio.Direction.INPUT
    button.pull = digitalio.Pull.DOWN

    button_state = None

    encoder = rotaryio.IncrementalEncoder(board.GP18, board.GP19)
    last_position = None

    ###################### Button Row Setup
    black_button = digitalio.DigitalInOut(board.GP4)
    black_button.direction = digitalio.Direction.INPUT
    black_button.pull = digitalio.Pull.DOWN
    black_button_event = None

    yellow_button = digitalio.DigitalInOut(board.GP3)
    yellow_button.direction = digitalio.Direction.INPUT
    yellow_button.pull = digitalio.Pull.DOWN
    yellow_button_event = None

    white_button = digitalio.DigitalInOut(board.GP5)
    white_button.direction = digitalio.Direction.INPUT
    white_button.pull = digitalio.Pull.DOWN
    white_button_event = None

    red_button = digitalio.DigitalInOut(board.GP2)
    red_button.direction = digitalio.Direction.INPUT
    red_button.pull = digitalio.Pull.DOWN
    red_button_event = None


    ###################### Sequencer Setup Setup
    start_sequencer = False

    active_sequence = 0
    num_sequence = 5

    amo = [0, 0, 0, 0, 0]
    off = [12, 12, 12, 12, 12]

    sequence = []
    for i in range(num_sequence):
    sequence.append([(0,0,0) for _ in range(num_pixels)])


    beats_per_minute = 120
    steps_per_beat = 6
    tick_time = ((60 / beats_per_minute) / steps_per_beat) * 1000000000

    amount_small_circle(math.floor(translate(beats_per_minute, 40, 240, 0, 12)))
    # print("Tick time in nano seconds: ", tick_time)
    tick_last = time.monotonic_ns()
    # print("Tick last: ", tick_last)
    tick_counter = 12
    tick_color = [(196, 0, 163), (101, 0, 200), (2, 179, 75), (212, 141, 0), (0, 212, 14)]

    beat_start_color = [(0, 71, 212), (227, 204, 0), (204, 0, 112), (0, 204, 143), (183, 0, 255) ]
    beat_step_color = [(0, 23, 69),(92, 82, 0), (60, 0, 30), (0, 74, 52), (30, 0, 30), ]

    #beat_led = digitalio.DigitalInOut(board.GP11)
    #beat_led.direction = digitalio.Direction.OUTPUT
    #beat_led.value = True
    RED_LED = board.GP11
    GREEN_LED = board.GP12
    BLUE_LED = board.GP13

    # Create a RGB LED object
    beat_led = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED, invert_pwm=True)
    beat_led.color = (0,0,0)

    ###################### Helper Functions
    def placePixels(amount = 0, offset = 0, start_color = (255, 0, 0), step_color = (255, 255, 255)):
    # pixels.fill((0,0,0))
    # pixels.show()
    # print("Placing Pixels: ", amount, offset)
    seq = [(0,0,0) for _ in range(num_pixels)]
    if amount == 0:
    return seq
    else:
    step_distance = num_pixels / amount
    for i in range(amount):
    if i == 0:
    seq[(math.floor(i * step_distance) + offset) % num_pixels] = start_color
    else:
    seq[(math.floor(i * step_distance) + offset) % num_pixels] = step_color
    return seq
    ###################### Main Loop
    while True:

    position = encoder.position
    if last_position is None or position != last_position:
    if last_position is not None:
    if not button.value and not red_button.value:
    amo[active_sequence] = amo[active_sequence] - (last_position - position)
    if amo[active_sequence] > num_pixels:
    amo[active_sequence] = num_pixels
    elif amo[active_sequence] < 0:
    amo[active_sequence] = 0
    elif button.value and not red_button.value:
    off[active_sequence] = off[active_sequence] - (last_position - position)
    elif not button.value and red_button.value:
    beats_per_minute -= (last_position - position)
    if beats_per_minute < 40:
    beats_per_minute = 40
    elif beats_per_minute >= 240:
    beats_per_minute = 240
    tick_time = ((60 / beats_per_minute) / steps_per_beat) * 1000000000
    amount_small_circle(math.floor(translate(beats_per_minute, 40, 240, 0, 12)))
    last_position = position

    if not button.value and button_state is None:
    button_state = "pressed"
    if button.value and button_state == "pressed":
    print("Button pressed.")
    button_state = None

    if black_button.value and black_button_event is None:
    black_button_event = True
    if not black_button.value and not black_button_event:
    black_button_event = None

    if yellow_button.value and yellow_button_event is None:
    yellow_button_event = True
    if not yellow_button.value and not yellow_button_event:
    yellow_button_event = None

    if yellow_button_event:
    start_sequencer = not start_sequencer
    #time.sleep(0.001)
    yellow_button_event = False

    if (time.monotonic_ns() - tick_last) >= tick_time:
    # print("Beat")
    note_list = ["C2", "D2", "E2", "G2", "A2"]
    send_list = []
    for s in range(len(sequence)):
    sequence[s] = placePixels(amo[s], off[s], beat_start_color[s], beat_step_color[s])
    if (sequence[s][tick_counter] != (0,0,0)) and start_sequencer:
    beat_led.color = tick_color[s]
    send_list.append(NoteOn(note_list[s], 120))
    # else:
    # beat_led.color = (0,0,0)
    if len(send_list) > 0:
    midi.send(send_list[0])

    time.sleep(0.01)

    send_list = []
    for s in range(len(sequence)):
    if (sequence[s][tick_counter] != (0,0,0)) and start_sequencer:
    send_list.append(NoteOff(note_list[s], 120))
    #beat_led.color = (0, 0, 0)
    if len(send_list) > 0:
    midi.send(send_list[0])

    if black_button_event and active_sequence < num_sequence-1:
    active_sequence += 1
    black_button_event = False
    elif black_button_event and active_sequence == num_sequence-1:
    active_sequence = 0
    black_button_event = False

    sequence[active_sequence][tick_counter] = tick_color[active_sequence]


    if tick_counter < num_pixels - 1 and start_sequencer:
    tick_counter += 1
    elif start_sequencer:
    tick_counter = 0
    tick_last = time.monotonic_ns()
    for i in range(num_pixels):
    pixels[i] = sequence[active_sequence][i]
    pixels.show()
    sequence = []
    for i in range(num_sequence):
    sequence.append([(0,0,0) for _ in range(num_pixels)])
    render_last = time.monotonic_ns()