From 07570f7871c35a956a4ba08f4c7e71ce70e876a8 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Tue, 21 Jan 2025 13:53:08 +0100 Subject: [PATCH] musicjam: add activity with edits for current hardware Signed-off-by: deadprogram --- musicjam/README.md | 81 ++++++++++ musicjam/arduino/README.md | 103 +++++++++++++ musicjam/arduino/arpeggio-display/display.go | 56 +++++++ musicjam/arduino/arpeggio-display/main.go | 142 ++++++++++++++++++ musicjam/arduino/arpeggio/main.go | 141 +++++++++++++++++ musicjam/arduino/chorder-display/display.go | 53 +++++++ musicjam/arduino/chorder-display/main.go | 72 +++++++++ musicjam/arduino/chorder/main.go | 70 +++++++++ musicjam/arduino/fader-display/display.go | 54 +++++++ musicjam/arduino/fader-display/main.go | 72 +++++++++ musicjam/arduino/fader/main.go | 69 +++++++++ musicjam/arduino/fourkey-display/display.go | 57 +++++++ musicjam/arduino/fourkey-display/main.go | 81 ++++++++++ musicjam/arduino/fourkey/main.go | 77 ++++++++++ musicjam/arduino/go.mod | 11 ++ musicjam/arduino/go.sum | 8 + musicjam/arduino/hello/main.go | 18 +++ musicjam/arduino/onenote/main.go | 56 +++++++ .../arduino/therestick-display/display.go | 57 +++++++ musicjam/arduino/therestick-display/main.go | 82 ++++++++++ musicjam/arduino/therestick/main.go | 75 +++++++++ musicjam/controller.md | 10 ++ musicjam/go.mod | 5 + musicjam/go.sum | 29 ++++ 24 files changed, 1479 insertions(+) create mode 100644 musicjam/README.md create mode 100644 musicjam/arduino/README.md create mode 100644 musicjam/arduino/arpeggio-display/display.go create mode 100644 musicjam/arduino/arpeggio-display/main.go create mode 100644 musicjam/arduino/arpeggio/main.go create mode 100644 musicjam/arduino/chorder-display/display.go create mode 100644 musicjam/arduino/chorder-display/main.go create mode 100644 musicjam/arduino/chorder/main.go create mode 100644 musicjam/arduino/fader-display/display.go create mode 100644 musicjam/arduino/fader-display/main.go create mode 100644 musicjam/arduino/fader/main.go create mode 100644 musicjam/arduino/fourkey-display/display.go create mode 100644 musicjam/arduino/fourkey-display/main.go create mode 100644 musicjam/arduino/fourkey/main.go create mode 100644 musicjam/arduino/go.mod create mode 100644 musicjam/arduino/go.sum create mode 100644 musicjam/arduino/hello/main.go create mode 100644 musicjam/arduino/onenote/main.go create mode 100644 musicjam/arduino/therestick-display/display.go create mode 100644 musicjam/arduino/therestick-display/main.go create mode 100644 musicjam/arduino/therestick/main.go create mode 100644 musicjam/controller.md create mode 100644 musicjam/go.mod create mode 100644 musicjam/go.sum diff --git a/musicjam/README.md b/musicjam/README.md new file mode 100644 index 0000000..c0d38e1 --- /dev/null +++ b/musicjam/README.md @@ -0,0 +1,81 @@ +# TinyGo Music Jam + +Make music using your own TinyGo-based customized MIDI controller using audio software running on your notebook computer. + +``` +┌────────────────────────────┐ ┌────────────────────────────────────────────────┐ +│ │ │ │ +│ ┌────────────────────────┐ │ │ ┌──────────────────────┐ │ +│ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ MIDI Controller │ │ │ │ USB-MIDI │ │ +│ │ ├─┼──────┼─► │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ └────────────────────────┘ │ │ └──────────┬───────────┘ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ ┌──────────▼───────────┐ │ +│ │ │ │ ├─────────────────────┐ │ +│ │ │ │ │ │ │ +│ │ │ │ Web MIDI API │ Web Software Synth │ │ +│ │ │ │ │ │ │ +│ │ │ │ ├─────────────────────┘ │ +│ │ │ │ │ │ +│ │ │ │ │ │ +│ │ │ │ │ │ +│ │ │ └──────────────────────┘ │ +│ │ │ │ +└────────────────────────────┘ └────────────────────────────────────────────────┘ + + Arduino/Badger2040 Computer + +``` + +Thanks to USB-MIDI standard, the Arduino will appear as a standard MIDI controller. You can use it to connect to online instruments that use the Web MIDI API. + + +## Online Synths and Instruments + +This is just a list of a few of the available online synths and other virtual instruments. + +https://midi.city/ + +https://www.websynths.com/microtonal/ + +https://www.gsn-lib.org/apps/cardboardsynth/index.html + +https://g200kg.github.io/webaudio-tinysynth/soundedit.html + +https://signal.vercel.app/ + +https://juno-106.js.org/ + +https://virtualpiano.eu/ + +https://experiments.withgoogle.com/ai/sound-maker/view/ + +## Hardware Controllers + +A hardware MIDI controller lets you control the virtual instruments running on your computer. + +Each of the TinyGo programs for the MIDI Controller is intended to run directly on the external hardware to send MIDI commands via the USB interface to your computer. + +The musical activities can be done with either the Arduino RP2040 Nano or Badger2040. + +### Arduino RP2040 Nano + +You can use a Arduino RP2040 board along with one of the button kits that we have brought with us. + +To follow the MIDI activities using the Arduino RP2040 Nano, [click here](./arduino.md) + +### Badger2040 + +If you have a Badger2040 you can use it as your MIDI controller. Check with us for some accessories you will need. + +To follow the MIDI activities using the Badger2040, [click here](https://github.com/conejoninja/badger2040/tree/main/tutorial/musicjam) diff --git a/musicjam/arduino/README.md b/musicjam/arduino/README.md new file mode 100644 index 0000000..893b441 --- /dev/null +++ b/musicjam/arduino/README.md @@ -0,0 +1,103 @@ +# MIDI Music Jam using the Arduino RP2040 Nano + +## What you need + + - Arduino Nano RP2040 Connect IoT board + - MIDI Starter Kit parts + - Personal computer with Go 1.19+ and TinyGo installed, and a USB port. + +### TinyGo drivers + +All of the code dependencies you will need are already in the Go modules file in this directory, so they will be downloaded and installed automatically. You don't need to do anything, when you follow the subsequent instructions they will be downloaded by TinyGo. + +Just for your information, the TinyGo drivers that let you connect to sensors, displays, and other external peripheral devices are located in the separate repository at https://github.com/tinygo-org/drivers + + +## Connecting the Arduino Nano RP2040 Connect to your computer + +Arduino Nano RP2040 Connect + +Plug the Arduino Nano RP2040 Connect into your computer using a USB cable. There may be one provided in your starter kit. + +## Running the code + +The TinyGo programs will run directly on the Arduino microcontoller. The procedure is basically: + +- Edit your TinyGo program. +- Compile and flash it to your Arduino. +- The program executes from the Arduino. + +Let's get started! + +## Code + +### hello - Built-in LED + +![Arduino Nano RP2040 Connect](./assets/step0.jpg) + +This tests that you can compile and flash your Arduino with TinyGo code, by blinking the built-in LED. + +Run the following command to compile your code, and flash it onto the Arduino: + +``` +tinygo flash -target nano-rp2040 ./hello/ +``` + +Once the Arduino is flashed correctly, the built-in amber LED to the right of the USB jack should start to turn on and off once per second. Now everything is setup correctly and you are ready to continue. + +### onenote + +This introductory MIDI controller sends only a single note. + +- Connect one of the "Ground" pins on the Arduino to the breadboard's ground rail (-). + +- Connect a black cable from one pin of the blue button on the breadboard to the breadboard's ground rail (-). + +- Connect a colored cable from the other pin of the blue button on the breadboard to pin D12 on the Arduino. + +To build/flash the `onenote` example on Arduino Nano RP2040: + + tinygo flash -target nano-rp2040 ./onenote/ + +Press the button. + +This should send MIDI messages that can trigger sounds on your computer by using your Arduino MIDI controller. + +Open a web page with one of the online synths listed above. You should be able to use your new custom MIDI controller to make music. + +Have fun! + +### chorder + +This MIDI controller sends entire chords with a single touch. It uses the exact same wiring setup as the `onenote` program. + +Each time you press the controller, it will play the next chord in the programmed chord progression. + +To build/flash the `chorder` program on Arduino Nano RP2040: + + tinygo flash -target nano-rp2040 ./chorder/ + +Launch one of the online synths. You should be able to use your new custom MIDI controller to make music. + +Have fun! + +### fourkeys + +This MIDI controller sends four different notes. + +- Connect a black cable from one pin of the green button on the breadboard to the breadboard's ground rail (-). + +- Connect a colored cable from the other pin of the green button on the breadboard to pin D11 on the Arduino. + +- Connect a black cable from one pin of the red button on the breadboard to the breadboard's ground rail (-). + +- Connect a colored cable from the other pin of the red button on the breadboard to pin D10 on the Arduino. + +- Connect a black cable from one pin of the yellow button on the breadboard to the breadboard's ground rail (-). + +- Connect a colored cable from the other pin of the yellow button on the breadboard to pin D9 on the Arduino. + +To build/flash the `fourkey` program on Arduino Nano RP2040: + + tinygo flash -target nano-rp2040 ./fourkey/ + diff --git a/musicjam/arduino/arpeggio-display/display.go b/musicjam/arduino/arpeggio-display/display.go new file mode 100644 index 0000000..7ef30d7 --- /dev/null +++ b/musicjam/arduino/arpeggio-display/display.go @@ -0,0 +1,56 @@ +package main + +import ( + "machine" + + "image/color" + "time" + + "tinygo.org/x/drivers/ssd1306" + "tinygo.org/x/tinydraw" + "tinygo.org/x/tinyfont" + "tinygo.org/x/tinyfont/freemono" +) + +const ( + startx = 10 + starty = 20 + radius int16 = 6 +) + +func handleDisplay() { + machine.I2C0.Configure(machine.I2CConfig{ + Frequency: machine.TWI_FREQ_400KHZ, + }) + + display := ssd1306.NewI2C(machine.I2C0) + display.Configure(ssd1306.Config{ + Address: ssd1306.Address_128_32, + Width: 128, + Height: 64, + }) + + display.ClearDisplay() + + black := color.RGBA{1, 1, 1, 255} + + for { + display.ClearBuffer() + msg := "" + + for i, key := range keys { + if key.pressed { + tinydraw.FilledCircle(&display, startx+radius+32*int16(i), 48-radius-1, radius, black) + msg = key.chord.name + } else { + tinydraw.Circle(&display, startx+radius+32*int16(i), 48-radius-1, radius, black) + } + } + + tinyfont.WriteLine(&display, &freemono.Bold9pt7b, startx, starty, msg, black) + + display.Display() + + time.Sleep(200 * time.Millisecond) + } +} diff --git a/musicjam/arduino/arpeggio-display/main.go b/musicjam/arduino/arpeggio-display/main.go new file mode 100644 index 0000000..c89bce3 --- /dev/null +++ b/musicjam/arduino/arpeggio-display/main.go @@ -0,0 +1,142 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +type chord struct { + name string + notes []midi.Note +} + +var ( + led = machine.LED + buttonC machine.Pin = machine.D12 + buttonE machine.Pin = machine.D11 + buttonG machine.Pin = machine.D10 + buttonB machine.Pin = machine.D9 + + chords = []chord{ + {name: "C ", notes: []midi.Note{midi.C3, midi.E3, midi.G3}}, + {name: "G ", notes: []midi.Note{midi.G3, midi.B3, midi.D4}}, + {name: "Am", notes: []midi.Note{midi.A3, midi.C4, midi.E4}}, + {name: "F ", notes: []midi.Note{midi.F3, midi.A3, midi.C4}}, + } + + keys = []key{ + {name: "C", pin: buttonC, chord: chords[0]}, + {name: "G", pin: buttonE, chord: chords[1]}, + {name: "A", pin: buttonG, chord: chords[2]}, + {name: "F", pin: buttonB, chord: chords[3]}, + } + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + // change this to speed up or slow down the music + bpm int = 80 + + currentNote int = 0 + currentNotes []midi.Note +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + buttonC.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonE.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonG.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonB.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + go handleKeys() + go handleDisplay() + + for { + playArpeggio() + time.Sleep(time.Duration(60000/bpm/4) * time.Millisecond) + } +} + +func handleKeys() { + for { + for i := range keys { + switch { + case keys[i].press(): + led.High() + + startArpeggio(keys[i].chord.notes) + + keys[i].pressed = true + + case keys[i].release(): + led.Low() + + stopArpeggio() + + keys[i].pressed = false + } + } + + time.Sleep(100 * time.Millisecond) + } +} + +type key struct { + name string + pin machine.Pin + chord chord + pressed bool +} + +func (k key) press() bool { + if !k.pin.Get() && !k.pressed { + return true + } + return false +} + +func (k key) release() bool { + if k.pin.Get() && k.pressed { + return true + } + return false +} + +func startArpeggio(notes []midi.Note) { + if currentNotes != nil { + stopArpeggio() + } + + currentNotes = notes + + midi.Port().NoteOn(midicable, midichannel, currentNotes[currentNote], velocity) +} + +func stopArpeggio() { + if currentNotes == nil { + return + } + + midi.Port().NoteOff(midicable, midichannel, currentNotes[currentNote], velocity) + + currentNotes = nil +} + +func playArpeggio() { + if currentNotes == nil { + return + } + + midi.Port().NoteOff(midicable, midichannel, currentNotes[currentNote], velocity) + + currentNote++ + if currentNote >= len(currentNotes) { + currentNote = 0 + } + + midi.Port().NoteOn(midicable, midichannel, currentNotes[currentNote], velocity) +} diff --git a/musicjam/arduino/arpeggio/main.go b/musicjam/arduino/arpeggio/main.go new file mode 100644 index 0000000..3eb6246 --- /dev/null +++ b/musicjam/arduino/arpeggio/main.go @@ -0,0 +1,141 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +type chord struct { + name string + notes []midi.Note +} + +var ( + led = machine.LED + buttonC machine.Pin = machine.D12 + buttonE machine.Pin = machine.D11 + buttonG machine.Pin = machine.D10 + buttonB machine.Pin = machine.D9 + + chords = []chord{ + {name: "C ", notes: []midi.Note{midi.C3, midi.E3, midi.G3}}, + {name: "G ", notes: []midi.Note{midi.G3, midi.B3, midi.D4}}, + {name: "Am", notes: []midi.Note{midi.A3, midi.C4, midi.E4}}, + {name: "F ", notes: []midi.Note{midi.F3, midi.A3, midi.C4}}, + } + + keys = []key{ + {name: "C", pin: buttonC, chord: chords[0]}, + {name: "G", pin: buttonE, chord: chords[1]}, + {name: "A", pin: buttonG, chord: chords[2]}, + {name: "F", pin: buttonB, chord: chords[3]}, + } + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + // change this to speed up or slow down the music + bpm int = 80 + + currentNote int = 0 + currentNotes []midi.Note +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + buttonC.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonE.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonG.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonB.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + go handleKeys() + + for { + playArpeggio() + time.Sleep(time.Duration(60000/bpm/4) * time.Millisecond) + } +} + +func handleKeys() { + for { + for i := range keys { + switch { + case keys[i].press(): + led.High() + + startArpeggio(keys[i].chord.notes) + + keys[i].pressed = true + + case keys[i].release(): + led.Low() + + stopArpeggio() + + keys[i].pressed = false + } + } + + time.Sleep(100 * time.Millisecond) + } +} + +type key struct { + name string + pin machine.Pin + chord chord + pressed bool +} + +func (k key) press() bool { + if !k.pin.Get() && !k.pressed { + return true + } + return false +} + +func (k key) release() bool { + if k.pin.Get() && k.pressed { + return true + } + return false +} + +func startArpeggio(notes []midi.Note) { + if currentNotes != nil { + stopArpeggio() + } + + currentNotes = notes + + midi.Port().NoteOn(midicable, midichannel, currentNotes[currentNote], velocity) +} + +func stopArpeggio() { + if currentNotes == nil { + return + } + + midi.Port().NoteOff(midicable, midichannel, currentNotes[currentNote], velocity) + + currentNotes = nil +} + +func playArpeggio() { + if currentNotes == nil { + return + } + + midi.Port().NoteOff(midicable, midichannel, currentNotes[currentNote], velocity) + + currentNote++ + if currentNote >= len(currentNotes) { + currentNote = 0 + } + + midi.Port().NoteOn(midicable, midichannel, currentNotes[currentNote], velocity) +} diff --git a/musicjam/arduino/chorder-display/display.go b/musicjam/arduino/chorder-display/display.go new file mode 100644 index 0000000..fec1e66 --- /dev/null +++ b/musicjam/arduino/chorder-display/display.go @@ -0,0 +1,53 @@ +package main + +import ( + "machine" + + "image/color" + "time" + + "tinygo.org/x/drivers/ssd1306" + "tinygo.org/x/tinydraw" + "tinygo.org/x/tinyfont" + "tinygo.org/x/tinyfont/freemono" +) + +const ( + startx = 10 + starty = 20 + radius int16 = 6 +) + +func handleDisplay() { + machine.I2C0.Configure(machine.I2CConfig{ + Frequency: machine.TWI_FREQ_400KHZ, + }) + + display := ssd1306.NewI2C(machine.I2C0) + display.Configure(ssd1306.Config{ + Address: ssd1306.Address_128_32, + Width: 128, + Height: 64, + }) + + display.ClearDisplay() + + black := color.RGBA{1, 1, 1, 255} + + for { + display.ClearBuffer() + + msg := chords[index].name + tinyfont.WriteLine(&display, &freemono.Bold12pt7b, startx, starty, msg, black) + + if pressed { + tinydraw.FilledCircle(&display, startx+radius+48*0, 48-radius-1, radius, black) + } else { + tinydraw.Circle(&display, startx+radius+48*0, 48-radius-1, radius, black) + } + + display.Display() + + time.Sleep(200 * time.Millisecond) + } +} diff --git a/musicjam/arduino/chorder-display/main.go b/musicjam/arduino/chorder-display/main.go new file mode 100644 index 0000000..c453c13 --- /dev/null +++ b/musicjam/arduino/chorder-display/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + button = machine.D12 + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + chords = []struct { + name string + notes []midi.Note + }{ + {name: "C ", notes: []midi.Note{midi.C3, midi.E3, midi.G3}}, + {name: "G ", notes: []midi.Note{midi.G3, midi.B3, midi.D4}}, + {name: "Am", notes: []midi.Note{midi.A3, midi.C4, midi.E4}}, + {name: "F ", notes: []midi.Note{midi.F3, midi.A3, midi.C4}}, + } + + index int + pressed bool +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + go handleDisplay() + + for { + switch { + case press(): + led.High() + for _, note := range chords[index].notes { + midi.Port().NoteOn(midicable, midichannel, note, velocity) + } + case release(): + led.Low() + for _, note := range chords[index].notes { + midi.Port().NoteOff(midicable, midichannel, note, velocity) + } + index = (index + 1) % len(chords) + } + + time.Sleep(time.Millisecond * 100) + } +} + +func press() bool { + if !button.Get() && !pressed { + pressed = true + return true + } + return false +} + +func release() bool { + if button.Get() && pressed { + pressed = false + return true + } + return false +} + diff --git a/musicjam/arduino/chorder/main.go b/musicjam/arduino/chorder/main.go new file mode 100644 index 0000000..e163842 --- /dev/null +++ b/musicjam/arduino/chorder/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + button = machine.D12 + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + chords = []struct { + name string + notes []midi.Note + }{ + {name: "C ", notes: []midi.Note{midi.C3, midi.E3, midi.G3}}, + {name: "G ", notes: []midi.Note{midi.G3, midi.B3, midi.D4}}, + {name: "Am", notes: []midi.Note{midi.A3, midi.C4, midi.E4}}, + {name: "F ", notes: []midi.Note{midi.F3, midi.A3, midi.C4}}, + } + + pressed bool +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + index := 0 + + for { + switch { + case press(): + led.High() + for _, note := range chords[index].notes { + midi.Port().NoteOn(midicable, midichannel, note, velocity) + } + case release(): + led.Low() + for _, note := range chords[index].notes { + midi.Port().NoteOff(midicable, midichannel, note, velocity) + } + index = (index + 1) % len(chords) + } + + time.Sleep(time.Millisecond * 100) + } +} + +func press() bool { + if !button.Get() && !pressed { + pressed = true + return true + } + return false +} + +func release() bool { + if button.Get() && pressed { + pressed = false + return true + } + return false +} diff --git a/musicjam/arduino/fader-display/display.go b/musicjam/arduino/fader-display/display.go new file mode 100644 index 0000000..cf005c3 --- /dev/null +++ b/musicjam/arduino/fader-display/display.go @@ -0,0 +1,54 @@ +package main + +import ( + "machine" + "strconv" + + "image/color" + "time" + + "tinygo.org/x/drivers/ssd1306" + "tinygo.org/x/tinydraw" + "tinygo.org/x/tinyfont" + "tinygo.org/x/tinyfont/freemono" +) + +const ( + startx = 10 + starty = 20 + radius int16 = 6 +) + +func handleDisplay() { + machine.I2C0.Configure(machine.I2CConfig{ + Frequency: machine.TWI_FREQ_400KHZ, + }) + + display := ssd1306.NewI2C(machine.I2C0) + display.Configure(ssd1306.Config{ + Address: ssd1306.Address_128_32, + Width: 128, + Height: 64, + }) + + display.ClearDisplay() + + black := color.RGBA{1, 1, 1, 255} + + for { + display.ClearBuffer() + + msg := strconv.Itoa(int(svol)) + tinyfont.WriteLine(&display, &freemono.Bold12pt7b, startx, starty, msg, black) + + if pressed { + tinydraw.FilledCircle(&display, startx+radius+48*0, 48-radius-1, radius, black) + } else { + tinydraw.Circle(&display, startx+radius+48*0, 48-radius-1, radius, black) + } + + display.Display() + + time.Sleep(200 * time.Millisecond) + } +} diff --git a/musicjam/arduino/fader-display/main.go b/musicjam/arduino/fader-display/main.go new file mode 100644 index 0000000..569e8fb --- /dev/null +++ b/musicjam/arduino/fader-display/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + button = machine.D12 + fader = machine.ADC{machine.A1} + + note = midi.C3 + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + pressed = false + svol uint32 +) + +func main() { + machine.InitADC() + + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + fader.Configure(machine.ADCConfig{}) + + go handleDisplay() + + for { + switch { + case press(): + led.High() + midi.Port().NoteOn(midicable, midichannel, note, velocity) + + case release(): + led.Low() + midi.Port().NoteOff(midicable, midichannel, note, velocity) + } + + // fader controls volume + vol := fader.Get() + + if pressed { + // scale to range 0x0 thru 0x7f + svol = 0x7F * uint32(vol) / 0xFFFF + midi.Port().ControlChange(midicable, midichannel, midi.CCVolume, uint8(svol)) + } + + time.Sleep(time.Millisecond * 100) + } +} + +func press() bool { + if !button.Get() && !pressed { + pressed = true + return true + } + return false +} + +func release() bool { + if button.Get() && pressed { + pressed = false + return true + } + return false +} diff --git a/musicjam/arduino/fader/main.go b/musicjam/arduino/fader/main.go new file mode 100644 index 0000000..a825ace --- /dev/null +++ b/musicjam/arduino/fader/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + button = machine.D12 + fader = machine.ADC{machine.A1} + + note = midi.C3 + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + pressed = false +) + +func main() { + machine.InitADC() + + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + fader.Configure(machine.ADCConfig{}) + + for { + switch { + case press(): + led.High() + midi.Port().NoteOn(midicable, midichannel, note, velocity) + + case release(): + led.Low() + midi.Port().NoteOff(midicable, midichannel, note, velocity) + } + + // fader controls volume + vol := fader.Get() + + if pressed { + // scale to range 0x0 thru 0x7f + svol := 0x7F * uint32(vol) / 0xFFFF + midi.Port().ControlChange(midicable, midichannel, midi.CCVolume, uint8(svol)) + } + + time.Sleep(time.Millisecond * 100) + } +} + +func press() bool { + if !button.Get() && !pressed { + pressed = true + return true + } + return false +} + +func release() bool { + if button.Get() && pressed { + pressed = false + return true + } + return false +} diff --git a/musicjam/arduino/fourkey-display/display.go b/musicjam/arduino/fourkey-display/display.go new file mode 100644 index 0000000..9bcd96b --- /dev/null +++ b/musicjam/arduino/fourkey-display/display.go @@ -0,0 +1,57 @@ +package main + +import ( + "machine" + + "image/color" + "time" + + "tinygo.org/x/drivers/ssd1306" + "tinygo.org/x/tinydraw" + "tinygo.org/x/tinyfont" + "tinygo.org/x/tinyfont/freemono" +) + +const ( + startx = 10 + starty = 20 + radius int16 = 6 +) + +func handleDisplay() { + machine.I2C0.Configure(machine.I2CConfig{ + Frequency: machine.TWI_FREQ_400KHZ, + }) + + display := ssd1306.NewI2C(machine.I2C0) + display.Configure(ssd1306.Config{ + Address: ssd1306.Address_128_32, + Width: 128, + Height: 64, + }) + + display.ClearDisplay() + + black := color.RGBA{1, 1, 1, 255} + + for { + display.ClearBuffer() + msg := "" + + for i, key := range keys { + if key.pressed { + tinydraw.FilledCircle(&display, startx+radius+32*int16(i), 48-radius-1, radius, black) + msg = msg + key.name + } else { + tinydraw.Circle(&display, startx+radius+32*int16(i), 48-radius-1, radius, black) + } + } + + tinyfont.WriteLine(&display, &freemono.Bold9pt7b, startx, starty, msg, black) + + display.Display() + + time.Sleep(200 * time.Millisecond) + } +} + diff --git a/musicjam/arduino/fourkey-display/main.go b/musicjam/arduino/fourkey-display/main.go new file mode 100644 index 0000000..7e58dab --- /dev/null +++ b/musicjam/arduino/fourkey-display/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + buttonC machine.Pin = machine.D12 + buttonE machine.Pin = machine.D11 + buttonG machine.Pin = machine.D10 + buttonB machine.Pin = machine.D9 + + keys = []key{ + {name: "C", pin: buttonC, note: midi.C3}, + {name: "E", pin: buttonE, note: midi.E3}, + {name: "G", pin: buttonG, note: midi.G3}, + {name: "B", pin: buttonB, note: midi.B3}, + } + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + buttonC.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonE.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonG.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonB.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + go handleDisplay() + + for { + handleKeys() + time.Sleep(100 * time.Millisecond) + } +} + +func handleKeys() { + for i := range keys { + switch { + case keys[i].press(): + led.High() + midi.Port().NoteOn(midicable, midichannel, keys[i].note, velocity) + keys[i].pressed = true + + case keys[i].release(): + led.Low() + midi.Port().NoteOff(midicable, midichannel, keys[i].note, velocity) + keys[i].pressed = false + } + } +} + +type key struct { + name string + pin machine.Pin + note midi.Note + pressed bool +} + +func (k key) press() bool { + if !k.pin.Get() && !k.pressed { + return true + } + return false +} + +func (k key) release() bool { + if k.pin.Get() && k.pressed { + return true + } + return false +} + diff --git a/musicjam/arduino/fourkey/main.go b/musicjam/arduino/fourkey/main.go new file mode 100644 index 0000000..26a06ce --- /dev/null +++ b/musicjam/arduino/fourkey/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + buttonC machine.Pin = machine.D12 + buttonE machine.Pin = machine.D11 + buttonG machine.Pin = machine.D10 + buttonB machine.Pin = machine.D9 + + keys = []key{ + {pin: buttonC, note: midi.C3}, + {pin: buttonE, note: midi.E3}, + {pin: buttonG, note: midi.G3}, + {pin: buttonB, note: midi.B3}, + } + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + buttonC.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonE.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonG.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + buttonB.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + for { + handleKeys() + time.Sleep(100 * time.Millisecond) + } +} + +func handleKeys() { + for i := range keys { + switch { + case keys[i].press(): + led.High() + midi.Port().NoteOn(midicable, midichannel, keys[i].note, velocity) + keys[i].pressed = true + + case keys[i].release(): + led.Low() + midi.Port().NoteOff(midicable, midichannel, keys[i].note, velocity) + keys[i].pressed = false + } + } +} + +type key struct { + pin machine.Pin + note midi.Note + pressed bool +} + +func (k key) press() bool { + if !k.pin.Get() && !k.pressed { + return true + } + return false +} + +func (k key) release() bool { + if k.pin.Get() && k.pressed { + return true + } + return false +} diff --git a/musicjam/arduino/go.mod b/musicjam/arduino/go.mod new file mode 100644 index 0000000..d75e020 --- /dev/null +++ b/musicjam/arduino/go.mod @@ -0,0 +1,11 @@ +module github.com/hybridgroup/goperhcon-2023/musicjam-arduino + +go 1.19 + +require ( + tinygo.org/x/drivers v0.29.0 + tinygo.org/x/tinydraw v0.4.0 + tinygo.org/x/tinyfont v0.4.0 +) + +require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect diff --git a/musicjam/arduino/go.sum b/musicjam/arduino/go.sum new file mode 100644 index 0000000..ff5596c --- /dev/null +++ b/musicjam/arduino/go.sum @@ -0,0 +1,8 @@ +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +tinygo.org/x/drivers v0.29.0 h1:xHuq8Fr1D/D2+1V/3d+aXufqP81/CLi1itdVbrYgrE0= +tinygo.org/x/drivers v0.29.0/go.mod h1:q/mU8G/wz821p8xXqbkBACOlmZFDHXd//DnYnCW+dDQ= +tinygo.org/x/tinydraw v0.4.0 h1:U9V0mHz8/jPShKjlh199vCfq1ARFyUOD1b+FfqIwV8c= +tinygo.org/x/tinydraw v0.4.0/go.mod h1:WCV/EMljTv8w04iAxjv+fRD6/4ffx0afATYeJlN90Yo= +tinygo.org/x/tinyfont v0.4.0 h1:XexPKEKiHInf6p4CMCJwsIheVPY0T46HUs6ictYyZfE= +tinygo.org/x/tinyfont v0.4.0/go.mod h1:7nVj3j3geqBoPDzpFukAhF1C8AP9YocMsZy0HSAcGCA= diff --git a/musicjam/arduino/hello/main.go b/musicjam/arduino/hello/main.go new file mode 100644 index 0000000..1dbce38 --- /dev/null +++ b/musicjam/arduino/hello/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "machine" + "time" +) + +func main() { + led := machine.LED + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + for { + led.Low() + time.Sleep(time.Millisecond * 500) + + led.High() + time.Sleep(time.Millisecond * 500) + } +} diff --git a/musicjam/arduino/onenote/main.go b/musicjam/arduino/onenote/main.go new file mode 100644 index 0000000..75c502f --- /dev/null +++ b/musicjam/arduino/onenote/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + button = machine.D12 + + note = midi.C3 + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + pressed = false +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + for { + switch { + case press(): + led.High() + midi.Port().NoteOn(midicable, midichannel, note, velocity) + + case release(): + led.Low() + midi.Port().NoteOff(midicable, midichannel, note, velocity) + } + + time.Sleep(time.Millisecond * 100) + } +} + +func press() bool { + if !button.Get() && !pressed { + pressed = true + return true + } + return false +} + +func release() bool { + if button.Get() && pressed { + pressed = false + return true + } + return false +} diff --git a/musicjam/arduino/therestick-display/display.go b/musicjam/arduino/therestick-display/display.go new file mode 100644 index 0000000..29ecf83 --- /dev/null +++ b/musicjam/arduino/therestick-display/display.go @@ -0,0 +1,57 @@ +package main + +import ( + "machine" + + "image/color" + "strconv" + "time" + + "tinygo.org/x/drivers/ssd1306" + "tinygo.org/x/tinydraw" + "tinygo.org/x/tinyfont" + "tinygo.org/x/tinyfont/freemono" +) + +const ( + startx = 10 + starty = 20 + radius int16 = 6 +) + +func handleDisplay() { + machine.I2C0.Configure(machine.I2CConfig{ + Frequency: machine.TWI_FREQ_400KHZ, + }) + + display := ssd1306.NewI2C(machine.I2C0) + display.Configure(ssd1306.Config{ + Address: ssd1306.Address_128_32, + Width: 128, + Height: 64, + }) + + display.ClearDisplay() + + black := color.RGBA{1, 1, 1, 255} + + for { + display.ClearBuffer() + + mod := "M: " + strconv.Itoa(int(sx)) + pb := "P: " + strconv.Itoa(int(sy)) + + tinyfont.WriteLine(&display, &freemono.Bold12pt7b, startx, starty, mod, black) + tinyfont.WriteLine(&display, &freemono.Bold12pt7b, startx, starty*2, pb, black) + + if pressed { + tinydraw.FilledCircle(&display, startx+radius+48*0, starty*3-radius-1, radius, black) + } else { + tinydraw.Circle(&display, startx+radius+48*0, starty*3-radius-1, radius, black) + } + + display.Display() + + time.Sleep(200 * time.Millisecond) + } +} diff --git a/musicjam/arduino/therestick-display/main.go b/musicjam/arduino/therestick-display/main.go new file mode 100644 index 0000000..12c68f2 --- /dev/null +++ b/musicjam/arduino/therestick-display/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + button = machine.D12 + stickX = machine.ADC{machine.A2} + stickY = machine.ADC{machine.A3} + + note = midi.C3 + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + sx, sy uint32 + pressed = false +) + +func main() { + machine.InitADC() + + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + stickX.Configure(machine.ADCConfig{}) + stickY.Configure(machine.ADCConfig{}) + + go handleDisplay() + + for { + switch { + case press(): + led.High() + midi.Port().NoteOn(midicable, midichannel, note, velocity) + + case release(): + led.Low() + midi.Port().NoteOff(midicable, midichannel, note, velocity) + + sx, sy = 0, 0 + midi.Port().ControlChange(midicable, midichannel, midi.CCModulationWheel, 0) + midi.Port().PitchBend(midicable, midichannel, 0) + } + + if pressed { + // x axis for modulation, y axis for pitch bend + x, y := stickX.Get(), stickY.Get() + + // scale x to range 0-127 + sx = 0x7F * uint32(x) / 0xFFFF + midi.Port().ControlChange(midicable, midichannel, midi.CCModulationWheel, uint8(sx)) + + // scale y to range 0x0 thru 0x3FFF + sy = 0x3FFF * uint32(y) / 0xFFFF + midi.Port().PitchBend(midicable, midichannel, uint16(sy)) + } + + time.Sleep(time.Millisecond * 100) + } +} + +func press() bool { + if !button.Get() && !pressed { + pressed = true + return true + } + return false +} + +func release() bool { + if button.Get() && pressed { + pressed = false + return true + } + return false +} diff --git a/musicjam/arduino/therestick/main.go b/musicjam/arduino/therestick/main.go new file mode 100644 index 0000000..cd388da --- /dev/null +++ b/musicjam/arduino/therestick/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "machine" + "machine/usb/adc/midi" + + "time" +) + +var ( + led = machine.LED + button = machine.D12 + stickX = machine.ADC{machine.A2} + stickY = machine.ADC{machine.A3} + + note = midi.C3 + + midicable uint8 = 0 + midichannel uint8 = 1 + velocity uint8 = 0x40 + + pressed = false +) + +func main() { + machine.InitADC() + + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + stickX.Configure(machine.ADCConfig{}) + stickY.Configure(machine.ADCConfig{}) + + for { + switch { + case press(): + led.High() + midi.Port().NoteOn(midicable, midichannel, note, velocity) + + case release(): + led.Low() + midi.Port().NoteOff(midicable, midichannel, note, velocity) + } + + // x axis for modulation, y axis for pitch bend + x, y := stickX.Get(), stickY.Get() + + if pressed { + // scale x to range 0-127 + sx := 0x7F * uint32(x) / 0xFFFF + midi.Port().ControlChange(midicable, midichannel, midi.CCModulationWheel, uint8(sx)) + + // scale y to range 0x0 thru 0x3FFF + sy := 0x3FFF * uint32(y) / 0xFFFF + midi.Port().PitchBend(midicable, midichannel, uint16(sy)) + } + + time.Sleep(time.Millisecond * 100) + } +} + +func press() bool { + if !button.Get() && !pressed { + pressed = true + return true + } + return false +} + +func release() bool { + if button.Get() && pressed { + pressed = false + return true + } + return false +} diff --git a/musicjam/controller.md b/musicjam/controller.md new file mode 100644 index 0000000..aa823bc --- /dev/null +++ b/musicjam/controller.md @@ -0,0 +1,10 @@ +# Controller mk2 + +## Faders + +1 to GND +2 to ADC +3 to 3V3 + +The side with pins 1-2 is the low side + diff --git a/musicjam/go.mod b/musicjam/go.mod new file mode 100644 index 0000000..f53967a --- /dev/null +++ b/musicjam/go.mod @@ -0,0 +1,5 @@ +module github.com/hybridgroup/goperhcon-2023/musicjam + +go 1.18 + +require tinygo.org/x/drivers v0.25.0 diff --git a/musicjam/go.sum b/musicjam/go.sum new file mode 100644 index 0000000..2b9bc89 --- /dev/null +++ b/musicjam/go.sum @@ -0,0 +1,29 @@ +github.com/bgould/http v0.0.0-20190627042742-d268792bdee7/go.mod h1:BTqvVegvwifopl4KTEDth6Zezs9eR+lCWhvGKvkxJHE= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/hajimehoshi/go-jisx0208 v1.0.0/go.mod h1:yYxEStHL7lt9uL+AbdWgW9gBumwieDoZCiB1f/0X0as= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/sago35/go-bdf v0.0.0-20200313142241-6c17821c91c4/go.mod h1:rOebXGuMLsXhZAC6mF/TjxONsm45498ZyzVhel++6KM= +github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= +tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= +tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= +tinygo.org/x/drivers v0.19.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI= +tinygo.org/x/drivers v0.25.0 h1:MFnec5lY8Sxk1bIfqQWsflIbxcpAFbohWhg/qZ7psdM= +tinygo.org/x/drivers v0.25.0/go.mod h1:v+mXaA4cgpz/YZJ3ZPm/86bYQJAXTaYtMkHlVwbodbw= +tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM= +tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk= +tinygo.org/x/tinyfs v0.1.0/go.mod h1:ysc8Y92iHfhTXeyEM9+c7zviUQ4fN9UCFgSOFfMWv20= +tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4=