-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
musicjam: add activity with edits for current hardware
Signed-off-by: deadprogram <[email protected]>
- Loading branch information
1 parent
1f0fc20
commit 07570f7
Showing
24 changed files
with
1,479 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
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) |
This file contains 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
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 | ||
|
||
 | ||
|
||
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/ | ||
|
This file contains 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
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) | ||
} | ||
} |
This file contains 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
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) | ||
} |
Oops, something went wrong.