singe/docs/learn/27-midi.singe
2026-09-22 21:57:42 -05:00

115 lines
2.7 KiB
Text

-- Lesson 27: Music And MIDI. The finished script, exactly as the lesson ends.
local NOTE_NAMES = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }
local MIDI_NOTE_ON = 0x90
local LOWEST = 60
local HIGHEST = 71
local HOLD_MS = 400
local VOLUME_STEP = 8
local theme = musicLoad("Singe/menuIntro.flac")
local volume = 64
local target = LOWEST
local heard = "nothing yet"
local score = 0
local inPort = "none"
local outPort = "none"
function noteName(key)
return NOTE_NAMES[(key % 12) + 1] .. (math.floor(key / 12) - 1)
end
function pickTarget()
target = math.random(LOWEST, HIGHEST)
end
function playTarget()
if not midiIsOutputOpen() then
return
end
local key = target
midiNoteOn(1, key, 100)
timerAfter(HOLD_MS, function()
midiNoteOff(1, key)
end)
end
function setVolume(level)
volume = math.min(128, math.max(0, level))
musicSetVolume(volume)
end
function onInputPressed(what)
if what == SWITCH_BUTTON1 then
playTarget()
elseif what == SWITCH_BUTTON2 then
if musicIsPlaying(theme) then
musicPause(theme)
else
musicResume(theme)
end
elseif what == SWITCH_BUTTON3 then
if musicIsPlaying(theme) then
musicStop(theme, 2000)
else
musicPlay(theme, -1)
end
elseif what == SWITCH_UP then
setVolume(volume + VOLUME_STEP)
elseif what == SWITCH_DOWN then
setVolume(volume - VOLUME_STEP)
end
end
function onMidiMessage(status, data1, data2, bytes)
if (status & 0xF0) ~= MIDI_NOTE_ON or data2 == 0 then
return
end
heard = noteName(data1)
if data1 == target then
score = score + 1
pickTarget()
playTarget()
end
end
function onOverlayUpdate()
overlayClear()
overlayPrint(2, 2, "Music: " .. (musicIsPlaying(theme) and "playing" or "stopped"))
overlayPrint(2, 3, "Volume: " .. volume .. " of 128")
overlayPrint(2, 5, "MIDI in: " .. inPort)
overlayPrint(2, 6, "MIDI out: " .. outPort)
overlayPrint(2, 8, "Play this note: " .. noteName(target))
overlayPrint(2, 9, "Last note heard: " .. heard)
overlayPrint(2, 10, "Score: " .. score)
overlayPrint(2, 12, "Up and down change the volume.")
overlayPrint(2, 13, "Space sends the note out, Alt pauses, Shift fades out.")
return OVERLAY_UPDATED
end
function onShutdown()
midiCloseInput()
midiCloseOutput()
musicUnload(theme)
end
setVolume(volume)
musicPlay(theme, -1)
if midiInputCount() > 0 and midiOpenInput(0) then
inPort = midiInputName(0)
end
if midiOutputCount() > 0 and midiOpenOutput(0) then
outPort = midiOutputName(0)
end
pickTarget()