| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- -- a toy drum sequencer working in 2D desktop mode
- package.path = package.path .. ";../?.lua" -- needed for chui.lua to be located in parent directory
- local chui = require'chui'
- local step_count = 12
- local bar_length = 4 * 60 / 120
- local instruments = {
- { pitch = 1.0, volume = 1, name='snare', sample_path='dm-snare.ogg' },
- { pitch = 1.4, volume = 1, name='hh-open', sample_path='dm-hatopen.ogg' },
- { pitch = 1.2, volume = 1, name='hh-closed', sample_path='dm-hatclosed.ogg' },
- { pitch = 0.8, volume = 1, name='bass', sample_path='dm-bass.ogg' },
- -- add more as desired
- }
- -- load in the samples; we later clone them to be able to play multiple samples at once
- for _, instrument in ipairs(instruments) do
- instrument.sample = lovr.audio.newSource(instrument.sample_path, {pitchable=true, spatial=false})
- end
- local function defaultdict(default_value_factory)
- local t = {}
- local metatable = {}
- metatable.__index = function(tbl, key)
- if not rawget(tbl, key) then
- rawset(tbl, key, default_value_factory(key))
- end
- return rawget(tbl, key)
- end
- return setmetatable(t, metatable)
- end
- local seq_table = defaultdict(function() return {} end)
- -- start building the UI; first the instrument lanes and then general controls
- local sequencer_panel = chui.panel{ palette=chui.palettes[6] }
- local volume_sliders = {}
- local pitch_sliders = {}
- for r, instrument in ipairs(instruments) do
- -- a trigger pushbutton to play the sample
- sequencer_panel:button{ span=0.3, callback=function() instrument.sample:clone():play() end }
- -- sample name
- sequencer_panel:label{ text=instrument.name }
- -- volume and pitch parameters
- volume_sliders[r] = sequencer_panel:slider{ span=1.5, text='vol', min=0, max=1, value= instrument.volume }
- pitch_sliders[r] = sequencer_panel:slider{ span=2, text='pitch', min=0.25, max=4, value= instrument.pitch }
- -- a row of toggle buttons that activate the sequencer
- for c = 1, step_count do
- sequencer_panel:toggle{span=0.6, callback = function(_, state)
- seq_table[r][c] = state
- end}
- end
- -- finish the row after each instrument's widgets to prepare for next
- sequencer_panel:row()
- end
- -- tempo and bar progress spans are manually adjusted to be aligned with previous widgets
- local tempo_slider = sequencer_panel:slider{ span=5.5, text='tempo', min=64, max=216, value=116, step=0.5 }
- local progress_bar = sequencer_panel:progress{ span=9.5, text='bar' }
- -- after adding the widgets, layout them in horizontally centered rows
- sequencer_panel:layout()
- local last_step = 7
- local time = 0
- lovr.graphics.setBackgroundColor(1,1,1)
- function lovr.update(dt)
- sequencer_panel:update(dt) -- allow ui to process interactions
- bar_length = 4 * 60 / tempo_slider:get() -- use slide to scale the tempo_slider
- time = time + dt / bar_length * step_count
- local bar_time = time % step_count -- bar time, normalized to [0, step_count] range
- progress_bar:set(bar_time / step_count)
- -- play the step notes if the bar timer beyond the previous step
- if math.floor(bar_time) ~= last_step then
- -- play any active toggle bar on this step
- last_step = math.floor(bar_time)
- for row, instrument in ipairs(instruments) do
- instrument.volume = volume_sliders[row]:get()
- instrument.pitch = pitch_sliders[row]:get()
- local tgl = seq_table[row][last_step + 1]
- if tgl then
- local sample = instrument.sample:clone()
- sample:setVolume(instrument.volume)
- sample:setPitch(instrument.pitch)
- sample:play()
- end
- end
- end
- end
- function lovr.draw(pass)
- -- use orthographic projection; dynamically adapt the panel size/position to the window dimensions
- local screen_width, screen_height = pass:getDimensions()
- local scale = screen_width / sequencer_panel.span[1] * 0.95
- sequencer_panel.pose:set(screen_width / 2, screen_height / 2, 0):scale(scale)
- pass:setProjection(1, mat4():orthographic(
- 0, screen_width, screen_height, 0,
- scale, -scale))
- sequencer_panel:draw(pass, true) -- draw panel itself and the interacting pointer
- end
|