Last active
October 15, 2024 01:49
-
-
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!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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