Drum Rack example
This commit is contained in:
parent
53579742c7
commit
9c57c04a00
4 changed files with 235 additions and 0 deletions
|
|
@ -16,6 +16,7 @@ Custom micropython build for the Olimex RP2350B-XL. Comes with some features ena
|
|||
9. Run `make BOARD=OLIMEX_PICO2_XL submodules` to install dependencies
|
||||
10. Run `make BOARD=OLIMEX_PICO2_XL clean` to remove previous build artefacts (if any)
|
||||
11. Run `make BOARD=OLIMEX_PICO2_XL` to build the firmware
|
||||
12. To rebuild: `make BOARD=OLIMEX_PICO2_XL clean && make BOARD=OLIMEX_PICO2_XL`
|
||||
|
||||
## flashing
|
||||
|
||||
|
|
|
|||
20
firmware/examples/nanogrid-drum-rack/README.md
Normal file
20
firmware/examples/nanogrid-drum-rack/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# MIDI Drum Rack example
|
||||
|
||||
Press to send a MIDI note in the range from C1 to D#2. Buttons are
|
||||
ordered in the same way the pads are in Ableton Drum Rack: with the USB
|
||||
port poiting from the player left-to-right, bottom-to-top.
|
||||
|
||||
|
||||
## build
|
||||
|
||||
1. Make a micropython build with the libraries added:
|
||||
|
||||
```bash
|
||||
cd $MICROPYTHON_PATH/ports/rp2
|
||||
make BOARD=OLIMEX_PICO2_XL clean && make V=1 BOARD=OLIMEX_PICO2_XL FROZEN_MANIFEST={path-to-neogrid-project}/firmware/examples/nanogrid-drum-rack/manifest.py
|
||||
```
|
||||
|
||||
2. Flush the board with the resulting uf2 (see
|
||||
[firmware/README.md](/firmware/README.md))
|
||||
|
||||
3. Copy the `main.py` to the board
|
||||
210
firmware/examples/nanogrid-drum-rack/main.py
Normal file
210
firmware/examples/nanogrid-drum-rack/main.py
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
# MicroPython USB MIDI example
|
||||
#
|
||||
# This example demonstrates creating a custom MIDI device.
|
||||
#
|
||||
# To run this example:
|
||||
#
|
||||
# 1. Make sure `usb-device-midi` is installed via: mpremote mip install usb-device-midi
|
||||
#
|
||||
# 2. Run the example via: mpremote run midi_example.py
|
||||
#
|
||||
# 3. mpremote will exit with an error after the previous step, because when the
|
||||
# example runs the existing USB device disconnects and then re-enumerates with
|
||||
# the MIDI interface present. At this point, the example is running.
|
||||
#
|
||||
# 4. To see output from the example, re-connect: mpremote connect PORTNAME
|
||||
#
|
||||
#
|
||||
# MIT license; Copyright (c) 2023-2024 Angus Gratton
|
||||
import usb.device
|
||||
from usb.device.midi import MIDIInterface
|
||||
import time
|
||||
|
||||
from machine import Pin
|
||||
|
||||
import array, time
|
||||
import rp2
|
||||
|
||||
NUM_COL = 4
|
||||
NUM_ROW = 4
|
||||
NUM_LEDS = NUM_COL * NUM_ROW
|
||||
PIN_NUM = 16
|
||||
brightness = 0.2
|
||||
|
||||
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
|
||||
def ws2812():
|
||||
T1 = 2
|
||||
T2 = 5
|
||||
T3 = 3
|
||||
wrap_target()
|
||||
label("bitloop")
|
||||
out(x, 1) .side(1) [T3 - 1]
|
||||
jmp(not_x, "do_zero") .side(0) [T1 - 1]
|
||||
jmp("bitloop") .side(0) [T2 - 1]
|
||||
label("do_zero")
|
||||
nop() .side(1) [T2 - 1]
|
||||
wrap()
|
||||
|
||||
def init_leds():
|
||||
# Create the StateMachine with the ws2812 program, outputting on pin
|
||||
sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(PIN_NUM))
|
||||
|
||||
# Start the StateMachine, it will wait for data on its FIFO.
|
||||
sm.active(1)
|
||||
|
||||
# Display a pattern on the LEDs via an array of LED RGB values.
|
||||
ar = array.array("I", [0 for _ in range(NUM_LEDS)])
|
||||
|
||||
##########################################################################
|
||||
def show_all():
|
||||
dimmer_ar = array.array("I", [0 for _ in range(NUM_LEDS)])
|
||||
for i, c in enumerate(ar):
|
||||
r = int(((c >> 8) & 0xFF) * brightness)
|
||||
g = int(((c >> 16) & 0xFF) * brightness)
|
||||
b = int((c & 0xFF) * brightness)
|
||||
dimmer_ar[i] = (g<<16) + (r<<8) + b
|
||||
sm.put(dimmer_ar, 8)
|
||||
|
||||
def set_led(i, color):
|
||||
row = i // NUM_COL
|
||||
col = i - row * NUM_COL
|
||||
new_col = NUM_COL - col - 1
|
||||
new_row = NUM_ROW - row - 1 if new_col % 2 == 0 else row
|
||||
ar[new_col * NUM_ROW + new_row] = (color[1]<<16) + (color[0]<<8) + color[2]
|
||||
|
||||
return (set_led, show_all)
|
||||
|
||||
class MIDIExample(MIDIInterface):
|
||||
# Very simple example event handler functions, showing how to receive note
|
||||
# and control change messages sent from the host to the device.
|
||||
#
|
||||
# If you need to send MIDI data to the host, then it's fine to instantiate
|
||||
# MIDIInterface class directly.
|
||||
|
||||
def on_open(self):
|
||||
super().on_open()
|
||||
print("Device opened by host")
|
||||
|
||||
def on_note_on(self, channel, pitch, vel):
|
||||
print(f"RX Note On channel {channel} pitch {pitch} velocity {vel}")
|
||||
|
||||
def on_note_off(self, channel, pitch, vel):
|
||||
print(f"RX Note Off channel {channel} pitch {pitch} velocity {vel}")
|
||||
|
||||
def on_control_change(self, channel, controller, value):
|
||||
print(f"RX Control channel {channel} controller {controller} value {value}")
|
||||
|
||||
|
||||
m = MIDIExample()
|
||||
# Remove builtin_driver=True if you don't want the MicroPython serial REPL available.
|
||||
usb.device.get().init(m, builtin_driver=True)
|
||||
|
||||
print("Waiting for USB host to configure the interface...")
|
||||
|
||||
while not m.is_open():
|
||||
time.sleep_ms(100)
|
||||
|
||||
# TX constants
|
||||
CHANNEL = 0
|
||||
PITCH_C1 = 37
|
||||
CONTROLLER = 64
|
||||
|
||||
control_val = 0
|
||||
|
||||
def init_btns():
|
||||
row_pins = [
|
||||
Pin(0, Pin.OUT),
|
||||
Pin(1, Pin.OUT),
|
||||
Pin(2, Pin.OUT),
|
||||
Pin(3, Pin.OUT),
|
||||
]
|
||||
|
||||
col_pins = [
|
||||
Pin(32, Pin.IN, Pin.PULL_UP),
|
||||
Pin(33, Pin.IN, Pin.PULL_UP),
|
||||
Pin(34, Pin.IN, Pin.PULL_UP),
|
||||
Pin(35, Pin.IN, Pin.PULL_UP),
|
||||
]
|
||||
|
||||
pressed_keys = set()
|
||||
|
||||
# reset cols
|
||||
for pin in row_pins:
|
||||
pin.value(1)
|
||||
|
||||
def loop():
|
||||
pressed_keys.clear()
|
||||
for row, row_pin in enumerate(row_pins):
|
||||
# set only active pin low, keep rest high
|
||||
row_pin.value(0)
|
||||
for col, col_pin in enumerate(col_pins):
|
||||
key = (
|
||||
(NUM_ROW - row - 1) +
|
||||
(NUM_COL - col - 1) * NUM_ROW
|
||||
)
|
||||
if not col_pin.value():
|
||||
pressed_keys.add(key)
|
||||
row_pin.value(1)
|
||||
return set(pressed_keys)
|
||||
|
||||
return loop
|
||||
|
||||
btn_loop = init_btns()
|
||||
turned_on = set()
|
||||
|
||||
(set_led, show_all) = init_leds()
|
||||
|
||||
# for i in range(NUM_LEDS):
|
||||
# set_led(i, CYAN)
|
||||
# for j in range(NUM_LEDS):
|
||||
# if i != j:
|
||||
# set_led(j, BLACK)
|
||||
# show_all()
|
||||
# time.sleep(2)
|
||||
#
|
||||
|
||||
CYAN = (0, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
|
||||
print("Starting loop...")
|
||||
while m.is_open():
|
||||
time.sleep_ms(5)
|
||||
# print(f"TX Note On channel {CHANNEL} pitch {PITCH_C1}")
|
||||
# m.note_on(CHANNEL, PITCH_C1) # Velocity is an optional third argument
|
||||
# time.sleep(0.5)
|
||||
# print(f"TX Note Off channel {CHANNEL} pitch {PITCH_C1}")
|
||||
# m.note_off(CHANNEL, PITCH_C1)
|
||||
# time.sleep(1)
|
||||
# print(f"TX Control channel {CHANNEL} controller {CONTROLLER} value {control_val}")
|
||||
# m.control_change(CHANNEL, CONTROLLER, control_val)
|
||||
# control_val += 1
|
||||
# if control_val == 0x7F:
|
||||
# control_val = 0
|
||||
# time.sleep(1)
|
||||
|
||||
pressed = btn_loop()
|
||||
|
||||
to_turn_on = pressed.difference(turned_on)
|
||||
to_turn_off = turned_on.difference(pressed)
|
||||
|
||||
if len(to_turn_on):
|
||||
print("to_turn_on", to_turn_on)
|
||||
|
||||
if len(to_turn_off):
|
||||
print("to_turn_off", to_turn_off)
|
||||
|
||||
|
||||
for key in to_turn_on:
|
||||
m.note_on(CHANNEL, PITCH_C1 + key - 1)
|
||||
set_led(key, CYAN)
|
||||
turned_on.add(key)
|
||||
|
||||
for key in to_turn_off:
|
||||
m.note_off(CHANNEL, PITCH_C1 + key - 1)
|
||||
turned_on.remove(key)
|
||||
set_led(key, BLACK)
|
||||
|
||||
show_all()
|
||||
|
||||
print("USB host has reset device, example done.")
|
||||
|
||||
4
firmware/examples/nanogrid-drum-rack/manifest.py
Normal file
4
firmware/examples/nanogrid-drum-rack/manifest.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
metadata(version="0.1.0")
|
||||
include("$(PORT_DIR)/boards/manifest.py")
|
||||
require("usb-device")
|
||||
require("usb-device-midi")
|
||||
Loading…
Add table
Add a link
Reference in a new issue