Skip to content

Commit

Permalink
musicjam: add activity with edits for current hardware
Browse files Browse the repository at this point in the history
Signed-off-by: deadprogram <[email protected]>
  • Loading branch information
deadprogram committed Jan 21, 2025
1 parent 1f0fc20 commit 07570f7
Show file tree
Hide file tree
Showing 24 changed files with 1,479 additions and 0 deletions.
81 changes: 81 additions & 0 deletions musicjam/README.md
Original file line number Diff line number Diff line change
@@ -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)
103 changes: 103 additions & 0 deletions musicjam/arduino/README.md
Original file line number Diff line number Diff line change
@@ -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

<img src="https://docs.arduino.cc/static/8b9e4e17c1e1afa836057c5ba87c27c9/2f891/pinout.png" alt="Arduino Nano RP2040 Connect" width="600"/>

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/

56 changes: 56 additions & 0 deletions musicjam/arduino/arpeggio-display/display.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
142 changes: 142 additions & 0 deletions musicjam/arduino/arpeggio-display/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading

0 comments on commit 07570f7

Please sign in to comment.