Skip to content

Instantly share code, notes, and snippets.

@quonic
Last active October 15, 2024 01:49
Show Gist options
  • Save quonic/474fef4002ba4ac20b349e7afa65b608 to your computer and use it in GitHub Desktop.
Save quonic/474fef4002ba4ac20b349e7afa65b608 to your computer and use it in GitHub Desktop.
Talk to a Launchpad Mini via portmidi in Odin and press all the notes and draw a check box!
package main
import "core:fmt"
import "core:time"
import "vendor:portmidi"
// Launchpad Mini
// https://fael-downloads-prod.focusrite.com/customer/prod/s3fs-public/downloads/Launchpad%20Mini%20-%20Programmers%20Reference%20Manual.pdf
note_matrix: [8][8]i32 = {
{64, 65, 66, 67, 96, 97, 98, 99},
{60, 61, 62, 63, 92, 93, 94, 95},
{56, 57, 58, 59, 88, 89, 90, 91},
{52, 53, 54, 55, 84, 85, 86, 87},
{48, 49, 50, 51, 80, 81, 82, 83},
{44, 45, 46, 47, 76, 77, 78, 79},
{40, 41, 42, 43, 72, 73, 74, 75},
{36, 37, 38, 39, 68, 69, 70, 71},
}
daw_mode: []u8 = {0xF0, 0x00, 0x20, 0x29, 0x02, 0x0D, 0x10, 0x01, 0xF7}
standalone_mode: []u8 = {0xF0, 0x00, 0x20, 0x29, 0x02, 0x0D, 0x10, 0x00, 0xF7}
red_escapecode := "\e[31m"
green_escapecode := "\e[32m"
yellow_escapecode := "\e[33m"
reset_escapecode := "\e[0m"
main :: proc() {
if err := portmidi.Initialize(); err != nil {
fmt.printfln("Failed to initialize portmidi: %v", err)
return
}
defer portmidi.Terminate()
devices: [dynamic]^portmidi.DeviceInfo
// Get devices
info := portmidi.GetDefaultOutputDeviceID()
for i: i32 = 0; i < portmidi.CountDevices() - 1; i = i + 1 {
append(&devices, portmidi.GetDeviceInfo((portmidi.DeviceID)(i)))
}
// Print devices
for device, i in devices {
if device.output {
if (portmidi.DeviceID)(i) == info {
fmt.printfln("Device %d: %v (default)", i, device.name)
} else {
fmt.printfln("Device %d: %v", i, device.name)
}
}
}
stream := portmidi.Stream{}
if err := portmidi.OpenOutput(&stream, info, nil, 1024, nil, nil, 0); err != nil {
fmt.printfln("Failed to open output: %v", err)
return
}
fmt.printfln("")
// DAW mode - not working
// portmidi.WriteSysEx(stream, 0, "F0 00 20 29 02 0D 10 01 F7") // DAW mode
// defer portmidi.WriteSysEx(stream, 0, "F0 00 20 29 02 0D 10 00 F7") // Standalone mode
// time.sleep(5 * time.Second) // Test delay
// Fill the matrix
{
clear_matrix(stream)
fmt.printfln("%v", yellow_escapecode)
for i, y in note_matrix {
for j, x in i {
time.sleep(time.Duration(20) * time.Millisecond)
draw_pixel(stream, x, y, 127)
fmt.printf("X")
}
fmt.printfln("")
}
fmt.printf("%v", reset_escapecode)
}
// Draw a square
{
clear_matrix(stream)
time.sleep(time.Duration(20) * time.Millisecond)
draw_line(stream, 0, 0, 7, 0, 120)
draw_line(stream, 7, 0, 7, 7, 120)
draw_line(stream, 0, 7, 7, 7, 120)
draw_line(stream, 0, 0, 0, 7, 120)
fmt.printf("\e[8A;\e[8D") // Move cursor up 8 lines and left 8 columns
fmt.printfln("%vXXXXXXXX", red_escapecode)
fmt.printfln("X X")
fmt.printfln("X X")
fmt.printfln("X X")
fmt.printfln("X X")
fmt.printfln("X X")
fmt.printfln("X X")
fmt.printfln("%vXXXXXXXX%v", red_escapecode, reset_escapecode)
time.sleep(time.Duration(500) * time.Millisecond)
}
// Draw a checkmark
{
time.sleep(time.Duration(20) * time.Millisecond)
draw_line(stream, 2, 4, 3, 5, 123)
draw_line(stream, 3, 5, 6, 2, 123)
fmt.printf("\e[8A;\e[8D") // Move cursor up 8 lines and left 8 columns
fmt.printfln("%vXXXXXXXX", red_escapecode)
fmt.printfln("%vX X", red_escapecode)
fmt.printfln("%vX %vX%vX", red_escapecode, green_escapecode, red_escapecode)
fmt.printfln("%vX %vX %vX", red_escapecode, green_escapecode, red_escapecode)
fmt.printfln("%vX %vX X %vX", red_escapecode, green_escapecode, red_escapecode)
fmt.printfln("%vX %vX %vX", red_escapecode, green_escapecode, red_escapecode)
fmt.printfln("%vX X", red_escapecode)
fmt.printfln("%vXXXXXXXX%v", red_escapecode, reset_escapecode)
time.sleep(time.Duration(2) * time.Second)
}
clear_matrix(stream)
fmt.printfln("")
}
play_note :: proc(stream: portmidi.Stream, note: i32, velocity: i32, duration: int) {
if err := portmidi.WriteShort(stream, 0, portmidi.MessageCompose(0x90, note, velocity));
err != nil {
fmt.printfln("Failed to write note: %v", err)
}
time.sleep(time.Duration(duration) * time.Millisecond)
if err := portmidi.WriteShort(stream, 0, portmidi.MessageCompose(0x80, note, velocity));
err != nil {
fmt.printfln("Failed to write note: %v", err)
}
}
draw_pixel :: proc(stream: portmidi.Stream, x: int, y: int, color: i32) {
if err := portmidi.WriteShort(
stream,
0,
portmidi.MessageCompose(0x90, note_matrix[y][x], color),
); err != nil {
fmt.printfln("Failed to write note: %v", err)
}
}
clear_matrix :: proc(stream: portmidi.Stream) {
for i, y in note_matrix {
for j, x in i {
draw_pixel(stream, x, y, 0)
}
}
}
draw_line :: proc(stream: portmidi.Stream, x1: int, y1: int, x2: int, y2: int, color: i32) {
x1, x2 := x1, x2
y1, y2 := y1, y2
dx := x2 - x1
dy := y2 - y1
if dx == 0 {
for y := y1; y <= y2; y = y + 1 {
draw_pixel(stream, x1, y, color)
}
} else if dy == 0 {
for x := x1; x <= x2; x = x + 1 {
draw_pixel(stream, x, y1, color)
}
} else {
if abs(dy) > abs(dx) {
if dy < 0 {
x1, x2 = x2, x1
y1, y2 = y2, y1
}
dx = x2 - x1
dy = y2 - y1
for y := y1; y <= y2; y = y + 1 {
x := x1 + dx * (y - y1) / dy
draw_pixel(stream, x, y, color)
}
} else {
if dx < 0 {
x1, x2 = x2, x1
y1, y2 = y2, y1
}
dx = x2 - x1
dy = y2 - y1
for x := x1; x <= x2; x = x + 1 {
y := y1 + dy * (x - x1) / dx
draw_pixel(stream, x, y, color)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment