Sequencer DSL with interacting voices
A complete example showcasing the sequencer DSL with multiple melodic lines that interact with each other:
require 'musa-dsl'
require 'midi-communications'
# Include all Musa DSL components
include Musa::All
# Setup MIDI output
output = MIDICommunications::Output.gets # Interactively select output
# Create clock and transport with sequencer
# 4 beats per bar, 24 ticks per beat
clock = TimerClock.new(bpm: 120, ticks_per_beat: 24)
transport = Transport.new(clock, 4, 24)
# Create scale for our composition
scale = Scales.et12[440.0].major[60] # C major starting at middle C
# Setup three MIDI voices on different channels
voices = MIDIVoices.new(
sequencer: transport.sequencer,
output: output,
channels: [0, 1, 2] # Channels for beat, melody, and harmony
)
beat_voice = voices.voices[0]
melody_voice = voices.voices[1]
harmony_voice = voices.voices[2]
# Shared state: current melody note (so harmony can follow)
current_melody_note = nil
# Program the sequencer using DSL
transport.sequencer.with do
# Line 1: Beat every 4 bars (for 32 bars total)
at 1 do
every 4, duration: 32 do
beat_voice.note pitch: 36, velocity: 100, duration: 1/8r # Kick drum
end
end
# Line 2: Ascending melody (bars 1-16) then descending (bars 17-32)
at 1 do
# Ascending: move from grade 0 to grade 14 over 16 bars
move(from: 0, to: 14, duration: 16, every: 1/4r) do |grade|
pitch = scale[grade.round].pitch
current_melody_note = grade.round # Share with harmony line
melody_voice.note pitch: pitch, velocity: 80, duration: 1/8r
end
end
at 17 do
# Descending: move from grade 14 to grade 0 over 16 bars
move(from: 14, to: 0, duration: 16, every: 1/4r) do |grade|
pitch = scale[grade.round].pitch
current_melody_note = grade.round # Share with harmony line
melody_voice.note pitch: pitch, velocity: 80, duration: 1/8r
end
end
# Line 3: Harmony playing 3rd or 5th every 2 bars (for 32 bars total)
at 1 do
use_third = true # Alternate between 3rd and 5th
every 2, duration: 32 do
if current_melody_note
# Calculate harmony: 3rd is +2 grades, 5th is +4 grades
harmony_grade = current_melody_note + (use_third ? 2 : 4)
harmony_pitch = scale[harmony_grade].pitch
# Play long note (whole note duration)
harmony_voice.note pitch: harmony_pitch, velocity: 70, duration: 1
# Alternate for next time
use_third = !use_third
end
end
end
end
# Start playback
transport.start
This example demonstrates:
- Multiple independent voice lines scheduled in the sequencer
movefunction to smoothly animate pitch values over timeeveryfor repeating events at regular intervals- Shared state between voices (harmony follows melody)
- Precise timing control with bars and beats