presets.ttl 36.7 KB
Newer Older
1
# Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
Hanspeter Portner's avatar
Hanspeter Portner committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the Artistic License 2.0 as published by
# The Perl Foundation.
#
# This source is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Artistic License 2.0 for more details.
#
# You should have received a copy of the Artistic License 2.0
# along the source as a COPYING file. If not, obtain it from
# http://www.perlfoundation.org/artistic_license_2_0.

Hanspeter Portner's avatar
Hanspeter Portner committed
16 17 18 19 20 21 22 23 24
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
Hanspeter Portner's avatar
Hanspeter Portner committed
25

Hanspeter Portner's avatar
Hanspeter Portner committed
26 27
@prefix moony: <http://open-music-kontrollers.ch/lv2/moony#> .
@prefix lic: <http://opensource.org/licenses/> .
28
@prefix stateR: <urn:uuid:ecc34531-c5a5-4fd4-9edf-14f2d8cf7b57#> .
Hanspeter Portner's avatar
Hanspeter Portner committed
29 30 31

# to please sord_validate
moony:code
32 33
	a lv2:Parameter ;
	rdfs:range atom:String ;
34
	rdfs:label "Lua code chunk" .
35
moony:state
36 37
	a lv2:Parameter ;
	rdfs:range atom:Atom ;
38
	rdfs:label "Lua code state" .
39
moony:editorHidden
40 41
	a lv2:Parameter ;
	rdfs:range atom:Bool ;
42
	rdfs:label "Editor tab hidden state" .
43 44 45 46
moony:logHidden
	a lv2:Parameter ;
	rdfs:range atom:Bool ;
	rdfs:label "Log tab hidden state" .
47 48 49 50
moony:logFollow
	a lv2:Parameter ;
	rdfs:range atom:Bool ;
	rdfs:label "Log follow state" .
51
moony:paramHidden
52 53
	a lv2:Parameter ;
	rdfs:range atom:Bool ;
54 55
	rdfs:label "Parameter tab hidden state" .
moony:paramCols
56 57
	a lv2:Parameter ;
	rdfs:range atom:Int ;
58 59
	rdfs:label "Parameter tab columns" .
moony:paramRows
60 61
	a lv2:Parameter ;
	rdfs:range atom:Int ;
62
	rdfs:label "Parameter tab rows" .
63

64
stateR:pBool
65 66 67
	a lv2:Parameter ;
	rdfs:label "A Bool" ;
	rdfs:range atom:Bool .
68
stateR:pChunk
69 70 71
	a lv2:Parameter ;
	rdfs:label "A Chunk" ;
	rdfs:range atom:Chunk .
72
stateR:pEnum
73 74 75
	a lv2:Parameter ;
	rdfs:label "An Enum" ;
	rdfs:range atom:Int .
76
stateR:pFloat
77 78 79
	a lv2:Parameter ;
	rdfs:label "A Float" ;
	rdfs:range atom:Float .
80
stateR:pInt
81 82 83
	a lv2:Parameter ;
	rdfs:label "An Int" ;
	rdfs:range atom:Int .
84
stateR:pString
85 86 87
	a lv2:Parameter ;
	rdfs:label "A String" ;
	rdfs:range atom:String .
88
stateR:pURID
89 90 91
	a lv2:Parameter ;
	rdfs:label "A URID" ;
	rdfs:range atom:URI .
Hanspeter Portner's avatar
Hanspeter Portner committed
92

93
moony:bank-through_control-through
94 95
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
96
	state:state [
97
		moony:editorHidden false ;
98
		moony:logHidden true ;
99
		moony:logFollow true ;
100 101
		moony:paramHidden true ;
		moony:paramCols 3 ;
102
		moony:paramRows 4 ;
103
		moony:code """function run(n, control, notify, ...)
Hanspeter Portner's avatar
Hanspeter Portner committed
104 105 106 107
	return ...
end"""
	] .

108
moony:bank-through_atom-through
109 110
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
111
	state:state [
112
		moony:editorHidden false ;
113
		moony:logHidden true ;
114
		moony:logFollow true ;
115 116
		moony:paramHidden true ;
		moony:paramCols 3 ;
117
		moony:paramRows 4 ;
118
		moony:code """function run(n, control, notify, seq, forge, ...)
Hanspeter Portner's avatar
Hanspeter Portner committed
119
	for frames, atom in seq:foreach() do
Hanspeter Portner's avatar
Hanspeter Portner committed
120
		forge:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
121 122 123 124 125 126
	end

	return ...
end"""
	] .

127
moony:bank-through_atom-through2
128 129
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
130
	state:state [
131
		moony:editorHidden false ;
132
		moony:logHidden true ;
133
		moony:logFollow true ;
134 135
		moony:paramHidden true ;
		moony:paramCols 3 ;
136
		moony:paramRows 4 ;
137
		moony:code """function run(n, control, notify, seq1, forge1, seq2, forge2)
Hanspeter Portner's avatar
Hanspeter Portner committed
138
	for frames, atom in seq1:foreach() do
Hanspeter Portner's avatar
Hanspeter Portner committed
139
		forge1:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
140 141 142
	end

	for frames, atom in seq2:foreach() do
Hanspeter Portner's avatar
Hanspeter Portner committed
143
		forge2:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
144 145 146 147
	end
end"""
	] .

148
moony:bank-through_atom-through4
149 150
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
151
	state:state [
152
		moony:editorHidden false ;
153
		moony:logHidden true ;
154
		moony:logFollow true ;
155 156
		moony:paramHidden true ;
		moony:paramCols 3 ;
157
		moony:paramRows 4 ;
158
		moony:code """function run(n, control, notify, seq1, forge1, seq2, forge2, seq3, forge3, seq4, forge4)
Hanspeter Portner's avatar
Hanspeter Portner committed
159
	for frames, atom in seq1:foreach() do
Hanspeter Portner's avatar
Hanspeter Portner committed
160
		forge1:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
161 162 163
	end

	for frames, atom in seq2:foreach() do
Hanspeter Portner's avatar
Hanspeter Portner committed
164
		forge2:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
165 166 167
	end

	for frames, atom in seq3:foreach() do
Hanspeter Portner's avatar
Hanspeter Portner committed
168
		forge3:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
169 170 171
	end

	for frames, atom in seq4:foreach() do
Hanspeter Portner's avatar
Hanspeter Portner committed
172
		forge4:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
173 174 175 176
	end
end"""
	] .

177
moony:bank-multiplex_atom-multiplex2
178 179
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
180
	state:state [
181
		moony:editorHidden false ;
182
		moony:logHidden true ;
183
		moony:logFollow true ;
184 185
		moony:paramHidden true ;
		moony:paramCols 3 ;
186
		moony:paramRows 4 ;
187
		moony:code """function run(n, control, notify, seq1, forge1, seq2, forge2)
188
	for frames, atom in seq1:foreach(seq2) do
Hanspeter Portner's avatar
Hanspeter Portner committed
189 190
		forge1:time(frames):atom(atom)
		forge2:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
191 192 193 194
	end
end"""
	] .

195
moony:bank-multiplex_atom-multiplex4
196 197
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
198
	state:state [
199
		moony:editorHidden false ;
200
		moony:logHidden true ;
201
		moony:logFollow true ;
202 203
		moony:paramHidden true ;
		moony:paramCols 3 ;
204
		moony:paramRows 4 ;
205
		moony:code """function run(n, control, notify, seq1, forge1, seq2, forge2, seq3, forge3, seq4, forge4)
206
	for frames, atom in seq1:foreach(seq2, seq3, seq4) do
Hanspeter Portner's avatar
Hanspeter Portner committed
207 208 209 210
		forge1:time(frames):atom(atom)
		forge2:time(frames):atom(atom)
		forge3:time(frames):atom(atom)
		forge4:time(frames):atom(atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
211 212 213 214
	end
end"""
	] .

215
moony:bank-midi_midi-responder
216 217
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
218
	state:state [
219
		moony:editorHidden false ;
220
		moony:logHidden true ;
221
		moony:logFollow true ;
222 223
		moony:paramHidden true ;
		moony:paramCols 3 ;
224
		moony:paramRows 4 ;
225
		moony:code """local midiR = MIDIResponder({
226
	[MIDI.NoteOn] = function(self, frames, forge, chan, note, vel)
227
		forge:time(frames):midi(MIDI.NoteOn | chan, note, vel)
Hanspeter Portner's avatar
Hanspeter Portner committed
228
	end,
229
	[MIDI.NoteOff] = function(self, frames, forge, chan, note, vel)
230
		forge:time(frames):midi(MIDI.NoteOff | chan, note, vel)
Hanspeter Portner's avatar
Hanspeter Portner committed
231
	end
232
})
Hanspeter Portner's avatar
Hanspeter Portner committed
233

234
function run(n, control, notify, seq, forge)
Hanspeter Portner's avatar
Hanspeter Portner committed
235
	for frames, atom in seq:foreach() do
236
		local handled = midiR(frames, forge, atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
237 238 239 240
	end
end"""
	] .

241
moony:bank-time_midi-sequencer
242 243
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
244
	state:state [
245
		moony:editorHidden false ;
246
		moony:logHidden true ;
247
		moony:logFollow true ;
248 249
		moony:paramHidden true ;
		moony:paramCols 3 ;
250
		moony:paramRows 4 ;
251
		moony:code """local timeR = TimeResponder({
252 253 254
  [Time.speed] = function(self, frames, forge, speed)
    self.rolling = speed > 0.0
    if not self.rolling and forge then
255 256
      forge:time(frames):midi(MIDI.Controller, MIDI.AllNotesOff)
    end
257
  end,
258 259
  [Time.barBeat] = function(self, frames, forge, barBeat)
    if self.rolling and forge and math.tointeger(barBeat) then
260 261 262 263 264
      forge:time(frames):midi(MIDI.NoteOff, 24, 0x7f)
      forge:time(frames):midi(MIDI.NoteOn, 24, 0x7f)
    end
  end,
  [Time.bar] = function(self, frames, forge, bar)
265
    if self.rolling and forge and math.tointeger(bar) then
266 267 268 269 270
      forge:time(frames):midi(MIDI.NoteOff, 48, 0x7f)
      forge:time(frames):midi(MIDI.NoteOn, 48, 0x7f)
    end
  end,
  rolling = false
271
})
272

273
function stash(forge)
274
  timeR:stash(forge)
275 276 277
end

function apply(atom)
278
  timeR:apply(atom)
279 280
end

281
function run(n, control, notify, seq, forge)
282
  local from = 0
283
  for frames, atom in seq:foreach() do
284
    timeR(from, frames, forge, atom)
285
    from = frames
286
  end
287
  timeR(from, n, forge)
288 289 290
end"""
	] .

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
moony:bank-time_lindenmayer-system
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
		moony:editorHidden false ;
		moony:logHidden false ;
		moony:logFollow true ;
		moony:paramHidden true ;
		moony:paramCols 3 ;
		moony:paramRows 4 ;
		moony:code """local mtointeger = math.tointeger -- reference locally

local rolling = false -- transport state
local oldNote = nil -- currently palying note
local velocity = 0x7f -- default note-on velocity value

-- Lindenmayer pattern-rule iterator
local function iterate(pattern, rules, n)
  for i = 1, n do -- iterate n-times
    pattern = string.gsub(pattern, '.', function(c) -- replace every single character with ...
      return rules[c] or c -- ... matching rule or itself
    end)
  end
  return pattern -- resulting iterated over pattern
end

local rules = {
  ['n'] = 'n.[.n..]', -- replacement string for character 'n'
  ['.'] = '[[..n]]' -- replacement string for character '.'
}

local start = 'n' -- initial pattern
local pattern = iterate(start, rules, 4) -- pattern after 4 iterations

local refNote = Note['C#+4'] -- initial reference note
local incNote = 3 -- reference note increment in MIDI semitones

local actions = {
  ['n'] = function() -- action for character 'n'
    coroutine.yield(refNote) -- return current reference note
  end,
  ['.'] = function() -- action for character '.'
    coroutine.yield(nil) -- return nil, e.g. this note is a pause
  end,
  ['['] = function() -- action for character '['
    refNote = (refNote + incNote) & 0x7f -- increase reference note by increment
  end,
  [']'] = function() -- action for character ']'
    refNote = (refNote - incNote) & 0x7f -- decrease reference note by increment
  end
}

local nextNote = coroutine.wrap(function() -- create iterator to get action from pattern
	while true do -- enter infinite loop
    for c in string.gmatch(pattern, '.') do -- iteratore over each single character in pattern
      local action = actions[c] -- look up action for character
      if action then
        action() -- execute action if defined
      end
    end
  end
end)

local timeR = TimeResponder({
  [Time.speed] = function(self, frames, forge, speed)
    rolling = speed ~= 0.0 -- update transport state

    if not rolling and oldNote then -- if at transport stop and currently playing a note
      forge:time(frames):midi(MIDI.NoteOff, oldNote, 0x0) -- send note-off
      oldNote = nil -- invalidate oldNote
    end
  end,
  [Time.barBeat] = function(self, frames, forge, barBeat)
    if rolling and mtointeger(barBeat) then -- if transport is rolling and barBeat being a whole integer
      if oldNote then -- if currently playing a note
        forge:time(frames):midi(MIDI.NoteOff, oldNote, 0x0) -- send note-off
        oldNote = nil -- invalidate oldNote
      end

      local newNote = nextNote() -- get new note or nil from pattern iterator
      if newNote then -- if not a pause
        forge:time(frames):midi(MIDI.NoteOn, newNote, velocity) -- send note-on
        oldNote = newNote -- update currently playing note
      end
    end
  end
}, 16) -- run 16x faster than host time

function run(n, control, notify, seq, forge)
  local from = 0 -- initialize reference frame time

  for frames, atom in seq:foreach() do -- iterate over input event sequence
    timeR(from, frames, forge, atom) -- let time responder handle the event
    from = frames -- advance reference frame time
  end

  timeR(from, n, forge) -- tell time responder about remaining frames of cycle
end"""
	] .

391
moony:bank-osc_osc-responder
392 393
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
Hanspeter Portner's avatar
Hanspeter Portner committed
394
	state:state [
395
		moony:editorHidden false ;
396
		moony:logHidden true ;
397
		moony:logFollow true ;
398 399
		moony:paramHidden true ;
		moony:paramCols 3 ;
400
		moony:paramRows 4 ;
401
		moony:code """local oscR = OSCResponder({
402 403
	['/ping'] = function(self, frames, forge, fmt, ...)
		forge:time(frames):message('/pong', fmt, ...)
404
	end,
405 406
	['/pong'] = function(self, frames, forge, fmt, ...)
		forge:time(frames):message('/ping', fmt, ...)
407
	end
408
})
Hanspeter Portner's avatar
Hanspeter Portner committed
409

410
function run(n, control, notify, seq, forge)
Hanspeter Portner's avatar
Hanspeter Portner committed
411
  for frames, atom in seq:foreach() do
412
    local handled = oscR(frames, forge, atom)
Hanspeter Portner's avatar
Hanspeter Portner committed
413 414 415
  end
end"""
	] .
416

417
moony:bank-state_state-responder
418 419
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
420
	state:state [
421
		moony:editorHidden false ;
422
		moony:logHidden true ;
423
		moony:logFollow true ;
424
		moony:paramHidden false ;
425
		moony:paramCols 3 ;
426
		moony:paramRows 4 ;
427
		moony:code """local urn = Mapper('urn:uuid:ecc34531-c5a5-4fd4-9edf-14f2d8cf7b57#')
428
local sync = false
429

430
local pLong
431
local pInt = Parameter{
432 433
  [RDFS.label] = 'Integer',
  [RDFS.comment] = 'This is an Integer',
434
  [RDFS.range] = Atom.Int,
435
  [Units.unit] = Units.hz,
436 437
  [LV2.minimum] = 0,
  [LV2.maximum] = 10,
438 439 440 441 442 443 444 445
	_value = 5,
	[Patch.Get] = function(self)
		return self._value
	end,
	[Patch.Set] = function(self, value)
		self._value = value
		pLong(value * 2)
		sync = true
446
  end
447 448
}

449
pLong = Parameter{
450 451 452 453
  [RDFS.label] = 'Long',
  [RDFS.comment] = 'This is a Long',
  [RDFS.range] = Atom.Long,
  [Units.unit] = Units.hz,
454 455
  [LV2.minimum] = pInt[LV2.minimum] * 2,
  [LV2.maximum] = pInt[LV2.maximum] * 2,
456
  [RDF.value] = pInt[RDF.value] * 2
457 458
}

459
local pDouble
460
local pFloat = Parameter{
461 462 463 464
  [RDFS.label] = 'Float',
  [RDFS.comment] = 'This is a Float',
  [RDFS.range] = Atom.Float,
  [Units.unit] = Units.m,
465 466
  [LV2.minimum] = -1.0,
  [LV2.maximum] = 1.0,
467 468 469 470 471 472 473 474 475
	_value = 0.0,
	[Patch.Get] = function(self)
		return self._value
	end,
	[Patch.Set] = function(self, value)
		self._value = value
		pDouble(value * 2)
		sync = true
	end
476 477
}

478
pDouble = Parameter{
479 480
  [RDFS.label] = 'Double',
  [RDFS.comment] = 'This is a Double',
481
  [RDFS.range] = Atom.Double,
482
  [Units.unit] = Units.m,
483 484
  [LV2.minimum] = pFloat[LV2.minimum] * 2,
  [LV2.maximum] = pFloat[LV2.maximum] * 2,
485
  [RDF.value] = pFloat[RDF.value] * 2
486 487
}

488
local pBool = Parameter{
489 490
  [RDFS.label] = 'Bool',
  [RDFS.comment] = 'This is a Boolean',
491
  [RDFS.range] = Atom.Bool,
492 493 494
  [RDF.value] = true
}

495
local pURID = Parameter{
496 497 498
  [RDFS.label] = 'URID',
  [RDFS.comment] = 'This is an URID',
  [RDFS.range] = Atom.URID,
499
	[RDF.value] = MIDI.MidiEvent
500 501
}

502
local pString = Parameter{
503 504 505 506 507 508
  [RDFS.label] = 'String',
  [RDFS.comment] = 'This is a String',
  [RDFS.range] = Atom.String,
  [RDF.value] = 'hello world'
}

509
local pChunk = Parameter{
510 511 512
  [RDFS.label] = 'Chunk',
  [RDFS.comment] = 'This is a Chunk',
  [RDFS.range] = Atom.Chunk,
513 514 515 516 517 518
  _value = string.char(0x1, 0x2, 0x3),
  [Patch.Get] = function(self)
		return self._value
	end,
	[Patch.Set] = function(self, value)
		self._value = value
519 520 521 522 523 524
    for i = 1, #value do
      print(i, string.byte(value, i))
    end
  end
}

525
local pEnum = Parameter{
526 527 528 529
  [RDFS.label] = 'Enum',
  [RDFS.comment] = 'This is an Enum',
  [RDFS.range] = Atom.Int,
  [RDF.value] = 0,
530 531 532
  [LV2.minimum] = 0,
  [LV2.maximum] = 2,
  [LV2.scalePoint] = {
533 534 535 536
    zero = 0,
    one = 1,
    two = 2
  }
537 538
}

539
local stateR = StateResponder{
540
  [Patch.writable] = {
541 542 543 544 545 546 547
    [urn.pInt] = pInt,
    [urn.pFloat] = pFloat,
    [urn.pBool] = pBool,
    [urn.pURID] = pURID,
    [urn.pString] = pString,
    [urn.pChunk] = pChunk,
    [urn.pEnum] = pEnum
548 549
  },
  [Patch.readable] = {
550 551
    [urn.pLong] = pLong,
    [urn.pDouble] = pDouble
552
  }
553
}
554

555
function save(forge)
556
  stateR:stash(forge)
557 558
end

559
function restore(atom)
560
  stateR:apply(atom)
561 562
end

563
function once(n, control, notify)
564
  stateR:register(0, notify)
565
end
566

567
function run(n, control, notify)
568
  for frames, atom in control:foreach() do
569 570 571 572
    if stateR(frames, notify, atom) and sync then
			stateR:sync(frames, notify)
			sync = false
		end
573
  end
574
end""" ;
575 576 577 578 579 580 581 582
		moony:state [
			stateR:pBool true ;
			stateR:pChunk "AQIDBAUGBw=="^^xsd:base64Binary ;
			stateR:pEnum 0 ;
			stateR:pFloat "8e-1"^^xsd:float ;
			stateR:pInt 9 ;
			stateR:pString "world hello" ;
			stateR:pURID <http://lv2plug.in/ns/ext/midi#MidiEvent>
583
		]
584
	] .
585

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
moony:bank-canvas_lv2-logo
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
		moony:editorHidden false ;
		moony:logHidden true ;
		moony:logFollow true ;
		moony:paramHidden true ;
		moony:paramCols 3 ;
		moony:paramRows 4 ;
		moony:code """local bg = 0x00000000 -- transparent black
local fg = 0xff0066ff -- purple

local function render(frames, forge)
  local ctx = forge:time(frames):graph()
  
  -- background
  ctx:rectangle(0.0, 0.0, 1.0, 1.0):style(bg):fill()

  -- 'L'
  ctx:beginPath()
  ctx:moveTo(0.05, 0.275)
  ctx:lineTo(0.05, 0.73463521816969)
  ctx:lineTo(0.39996786383766, 0.73463521816969)
  ctx:lineTo(0.35805418792799, 0.61981755929103)
  ctx:lineTo(0.16950515672412, 0.61981755929103)
  ctx:lineTo(0.16950515672412, 0.275)
  ctx:lineTo(0.05, 0.275)
  ctx:closePath():style(fg):stroke()
  
  -- 'V'
  ctx:beginPath()
  ctx:moveTo(0.44035674587458, 0.73463521816969)
  ctx:lineTo(0.27321237521861, 0.275)
  ctx:lineTo(0.39612954205777, 0.275)
  ctx:lineTo(0.5215250619933, 0.61980400005209)
  ctx:lineTo(0.64678627651808, 0.275)
  ctx:lineTo(0.76999411666921, 0.275)
  ctx:lineTo(0.60269884777111, 0.73463521816969)
  ctx:lineTo(0.44035674587458, 0.73463521816969)
  ctx:closePath():style(fg):stroke()

  -- '2'
  ctx:beginPath()
  ctx:moveTo(0.92679577564592, 0.33745757758451)
  ctx:curveTo(0.95, 0.37544661222032,
    0.9486097413556, 0.42890103900541, 0.91866073788306, 0.46581025262318)
  ctx:curveTo(0.87662774067075, 0.51761178520021,
    0.84865149155459, 0.52351773004551, 0.8188709443895, 0.55088574387747)
  ctx:lineTo(0.93798338878322, 0.55088574387747)
  ctx:lineTo(0.93798338878322, 0.61972641362727)
  ctx:lineTo(0.68857649440815, 0.61972641362727)
  ctx:curveTo(0.70410821191941, 0.57897193773781,
    0.71568706655441, 0.55649255812279, 0.73505227967577, 0.53436493734023)
  ctx:curveTo(0.78431409785481, 0.47807598612821,
    0.88073913173375, 0.44149338929647, 0.87483180798279, 0.39074363998918)
  ctx:curveTo(0.8731729385169, 0.37649219041461,
    0.86900905711197, 0.34385128732334, 0.80655313421425, 0.34385128732334)
  ctx:lineTo(0.7834998081023, 0.34385128732334)
  ctx:lineTo(0.80849192152801, 0.275)
  ctx:curveTo(0.88098903540187, 0.275,
    0.90879494370618, 0.30798728419169, 0.92679577564592, 0.33745757758451)
  ctx:closePath():style(fg):stroke()
  
  ctx:pop()
end

function once(n, control, notify, seq, forge)
	render(0, forge)	
end"""
	] .

658 659 660 661
moony:bank-tutorial_part-1
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
662
		moony:editorHidden false ;
663
		moony:logHidden true ;
664
		moony:logFollow true ;
665 666
		moony:paramHidden true ;
		moony:paramCols 3 ;
667
		moony:paramRows 4 ;
668 669 670 671 672 673 674 675 676 677 678
		moony:code """-- Tutorial 1: MIDI Channel Blocker

-- define table which holds active MIDI channels
local channels = {0, 2, 4, 6}

-- derive channel mask based on active channels
local mask = 0 -- block everything
for i, v in ipairs(channels) do
	mask = mask | (1 << v) -- toggle bit of active channel
end

679
function run(n, control, notify, seq, forge)
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
	for frames, atom in seq:foreach() do -- iterate over incoming events
		local block = false -- assume a not-to-be-blocked event by default

		if atom.type == MIDI.MidiEvent then -- check for MIDI message
			local msg = atom[1] -- get first MIDI byte
			local cmd = msg & 0xf0 -- get MIDI command nibble
			local chn = msg & 0x0f -- get MIDI channel nibble

			if (cmd ~= 0xf0) and ((1 << chn) & mask == 0) then
				block = true -- if not a system message and channel not part of mask
			end
		end

		if not block then -- let atom through
			forge:time(frames):atom(atom) -- serialize atom as-is
		end
	end
end"""
	] .

moony:bank-tutorial_part-2
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
704
		moony:editorHidden false ;
705
		moony:logHidden true ;
706
		moony:logFollow true ;
707 708
		moony:paramHidden true ;
		moony:paramCols 3 ;
709
		moony:paramRows 4 ;
710
		moony:code """-- Tutorial 2: MIDI Chorder
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733

-- define table that holds number of chord notes and their offsets
local chord = {0, 12, 24, 36} -- octaves, obviously

-- define a general note responder factory
local function noteResponder(cmd)
	return function (self, frames, forge, chan, note, vel)
		for i, v in ipairs(chord) do -- iterate over chord offsets
			local chanNew = i - 1 -- set MIDI channel to chord index - 1
			local noteNew = note + v -- set MIDI note to chord offset
			if noteNew >= 0 and noteNew <= 0x7f then -- handle note under/overflows
				forge:time(frames):midi(cmd | chanNew, noteNew, vel) -- serialize event
			end
		end
	end
end

-- define a MIDIResponder object configured to pass-through unmatched messages
local midiR = MIDIResponder({
	[MIDI.NoteOn] = noteResponder(MIDI.NoteOn), -- create responder for NoteOn
	[MIDI.NoteOff] = noteResponder(MIDI.NoteOff), -- and NoteOff
	[MIDI.NotePressure] = noteResponder(MIDI.NotePressure) -- and NotePressure
}, true)
734

735
function run(n, control, notify, seq, forge)
736 737 738 739 740 741 742 743 744 745
	for frames, atom in seq:foreach() do -- iterate over incoming events
		local handled = midiR(frames, forge, atom) -- call responder for event
	end
end"""
	] .

moony:bank-tutorial_part-3
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
746
		moony:editorHidden false ;
747
		moony:logHidden true ;
748
		moony:logFollow true ;
749 750
		moony:paramHidden true ;
		moony:paramCols 3 ;
751
		moony:paramRows 4 ;
752 753
		moony:code """-- Tutorial 3: MIDI Sample & Hold

754
local urn = Mapper('urn:uuid:03340863-7f87-4f67-9fc9-9cac49c2dfba3#') -- prefix of this presets URIs
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
local sample = false -- flag to tell whether we are sampling or not
local noteOffs = {} -- table to store NoteOff events while sampling

-- MIDI responder for NoteOff events
local function noteOff(self, frames, forge, chan, note, vel)
	local msg = string.char(MIDI.NoteOff | chan, note, vel) -- encode to byte string

	if sample then -- are we sampling?
		table.insert(noteOffs, msg) -- add event to table, e.g. put it on-hold
	else
		forge:time(frames):midi(msg) -- let event through as-is
	end
end

-- MIDI responder for Controller events
local function controller(self, frames, forge, chan, control, value)
	if control == MIDI.SustainPedal then -- check for SustainPedal
		local newSample = value > 0 -- derive new sampling state from Controller value

		if newSample ~= sample then -- is new sampling state different from current one?
			if not newSample then -- sustain pedal has been released
				for i, msg in ipairs(noteOffs) do -- release NoteOff events on-hold
					forge:time(frames):midi(msg)
				end
				noteOffs = {} -- clear table as events have been released
			end

			sample = newSample -- update sampling state
		end
	else -- let through non-SustainPedal Controller events
		forge:time(frames):midi(MIDI.Controller | chan, control, value)
	end
end

-- define a MIDIResponder object configured to pass-through unmatched messages
local midiR = MIDIResponder({
	[MIDI.NoteOff] = noteOff, -- register NoteOff responder
	[MIDI.Controller] = controller -- register Controller responder
}, true)

-- push sampling state and events on-hold on stash
function stash(forge)
	local obj = forge:object() -- create object

	-- add boolean property for sampling state to object
	obj:key(urn.sample):bool(sample)

	-- add tuple property for events on-hold to object
	local tup = forge:key(urn.noteOffs):tuple()
	for i, msg in ipairs(noteOffs) do -- add events on-hold to tuple
		tup:midi(msg)
	end

	tup:pop() -- finalize tuple
	obj:pop() -- finalize object
end

-- pop sampling state and events on-hold from stash
function apply(atom)
	-- get sampling state from object
	if atom[urn.sample] then
		sample = atom[urn.sample].body
	end

	-- get events on-hold from object
	if atom[urn.noteOffs] then
		noteOffs = {} -- clear table
		for i, msg in atom[urn.noteOffs]:foreach() do -- iterate tuple
			noteOffs[i] = msg -- add events on-hold to table
		end
	end
end

828
function run(n, control, notify, seq, forge)
829 830 831 832 833
	for frames, atom in seq:foreach() do -- iterate over incoming events
		local handled = midiR(frames, forge, atom) -- call responder for event
	end
end"""
	] .
834 835 836 837 838

moony:bank-tutorial_part-4
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
839
		moony:editorHidden false ;
840
		moony:logHidden true ;
841
		moony:logFollow true ;
842 843
		moony:paramHidden true ;
		moony:paramCols 3 ;
844
		moony:paramRows 4 ;
845 846 847 848 849 850 851 852
		moony:code """-- Tutorial 4: MIDI Arpeggiator

local schar = string.char -- local variable is more efficient to look up

local chord = {0, 3, 7, 11} -- table with chord note offsets
local offset = 0.1 -- time offset between notes in seconds

local schedule = {} -- table to store Note events
853
local dur = math.floor(offset * Options[Param.sampleRate].body) -- time offset in frames
854 855 856 857 858 859 860 861 862 863 864 865 866

-- compare function to sort scheduled messages according to frame time
local function cmp(a, b)
	return a[1] < b[1]
end

local function note_responder(cmd)
	return function(self, frames, forge, chan, note, vel)
		for i, v in ipairs(chord) do
			local chanNew = i - 1 -- set new channel to chord note index - 1
			local noteNew = note + v -- set new note to chord note offset
			local msg = schar(cmd | chanNew, noteNew, vel) -- serialize message
			local off = frames + (i-1)*dur
867
			table.insert(schedule, {off, msg}) -- schedule message with offset
868
		end
869
		table.sort(schedule, cmp) -- sort table
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
	end
end

-- define a MIDIResponder object configured to pass-through unmatched messages
local midiR = MIDIResponder({
	[MIDI.NoteOn] = note_responder(MIDI.NoteOn), -- register responder for NoteOn
	[MIDI.NoteOff] = note_responder(MIDI.NoteOff), -- and NoteOff
	[MIDI.NotePressure] = note_responder(MIDI.NotePressure) -- and NotePressure
}, true)

-- push scheduled events stash
function stash(forge)
	local seq= forge:sequence() -- create atom sequence
	for i, v in ipairs(schedule) do -- iterate over scheduled events
		seq:time(v[1]):midi(v[2]) -- add events to atom sequence
	end
	seq:pop() -- finalize atom sequence
end

-- pop scheduled events from stash
function apply(atom)
	if atom.type == Atom.Sequence then -- check for correct atom type
		schedule = {} -- clear table with scheduled events
		for off, itm in atom:foreach() do -- iteratore over sequence events
			table.insert(schedule, {off, itm.body}) -- insert event into table
		end
		table.sort(schedule, cmp) -- sort events
	end
end

-- are there any scheduled events to dispatch?
local function dispatch(n, forge)
::loop::
	for i, v in ipairs(schedule) do
		if v[1] < n then
			forge:time(v[1]):midi(v[2]) -- send message
			table.remove(schedule, i) -- remove message from scheduled events
			goto loop -- restart loop as we have removed an item
		else
			v[1] = v[1] - n -- decrease timestamp by period size
		end
	end
end

914
function run(n, control, notify, seq, forge)
915 916 917 918 919 920 921
	for frames, atom in seq:foreach() do -- iterate over incoming events
		local handled = midiR(frames, forge, atom) -- call responder for event
	end

	dispatch(n, forge) -- dispatch scheduled events
end"""
	] .
922

923 924 925 926
moony:bank-tutorial_part-5
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
927
		moony:editorHidden false ;
928
		moony:logHidden true ;
929
		moony:logFollow true ;
930 931
		moony:paramHidden true ;
		moony:paramCols 3 ;
932
		moony:paramRows 4 ;
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
		moony:code """-- Tutorial 5: MIDI Velocity Curve Interpolator

local X = {0, 15, 112, 127} -- X-coordinates of curve
local Y = {0,  7, 120, 127} -- Y-coordinates of curve
local N = #X -- number of points to interpolate over
local Ymin = 0 -- clip minimum
local Ymax = 127 -- clip maximum

-- Lagrange Polynomial Interpolation
local function P(x)
	local sum = 0
	for j = 1, N do
		local prod = Y[j]
		for k = 1, N do
			prod = prod * (k == j and 1.0 or (x - X[k]) / (X[j] - X[k]))
		end
		sum = sum + prod
	end
	sum = math.floor(sum) -- round to lower integer
	return sum < Ymin and Ymin or (sum > Ymax and Ymax or sum) -- clip to [Ymin, Ymax]
end

-- fill velocity curve lookup table
local curve = {}
for i = 0, 127 do
	curve[i+1] = P(i)
end

-- note responder function factory
local function note_responder(cmd)
	return function(self, frames, forge, chan, note, vel)
		local velNew = curve[vel+1] -- index velocity curve lookup table
		forge:time(frames):midi(cmd | chan, note, velNew) -- send event
	end
end

-- define a MIDIResponder object configured to pass-through unmatched messages
local midiR = MIDIResponder({
	[MIDI.NoteOn] = note_responder(MIDI.NoteOn), -- register responder for NoteOn
	[MIDI.NoteOff] = note_responder(MIDI.NoteOff) -- and NoteOff
}, true)

975
function run(n, control, notify, seq, forge)
976 977 978 979 980 981
	for frames, atom in seq:foreach() do -- iterate over incoming events
		local handled = midiR(frames, forge, atom) -- call responder for event
	end
end"""
	] .

982 983 984 985
moony:bank-tutorial_part-6
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
986
		moony:editorHidden false ;
987
		moony:logHidden true ;
988
		moony:logFollow true ;
989 990
		moony:paramHidden true ;
		moony:paramCols 3 ;
991
		moony:paramRows 4 ;
992 993
		moony:code """-- Tutorial 6: MIDI Sequencer

994 995

local urn = Mapper('urn:uuid:4cc65393-869d-4ca1-8ac4-fcbe902b36d6#')
996 997 998

-- table with MIDI notes for 8 beats
local notes = {
999 1000
	Note['C+4'],  -- beat 1
	Note['D#+4'], -- beat 2
1001
	false,        -- beat 3
1002 1003
	Note['C+4'],  -- beat 4
	Note['G+4'],  -- beat 5 
1004
	false,        -- beat 6
1005
	Note['G+3'],  -- beat 7
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
	false         -- beat 8
}

local vel = 0x7f -- MIDI note velocity

local active = nil -- active MIDI note

-- register Time responder object
local timeR = TimeResponder({
	rolling = false, -- flag to signal whether we are rolling or not
	[Time.speed] = function(self, frames, forge, speed) -- transport start/stop
		self.rolling = speed > 0 and true or false

		-- send note off event at transport stop with active note
		if not self.rolling and forge and active then
			forge:time(frames):midi(MIDI.NoteOff, active, vel)
			active = nil
		end
	end,
	[Time.barBeat] = function(self, frames, forge, barBeat)
		if self.rolling and forge and math.tointeger(barBeat) then
			-- send note off event for active note
			if active then
				forge:time(frames):midi(MIDI.NoteOff, active, vel)
				active = nil
			end

			-- get new note
			local note = notes[barBeat % #notes + 1]
			-- send note on event for new note
			if note then
				forge:time(frames):midi(MIDI.NoteOn, note, vel)
				active = note
			end
		end
	end
})

-- push time responder state and active note to stash
function stash(forge)
	local obj = forge:object()

	obj:key(Time.Position)
	timeR:stash(obj)

	if active then
		obj:key(urn.active):int(active)
	end

	obj:pop()
end

-- pop time responder state and active note from stash
function apply(atom)
	if atom[Time.Position] then
		timeR:apply(atom[Time.Position])
	end

	if atom[urn.active] then
		active = atom[urn.active].body
	end
end

function run(n, control, notify, seq, forge)
	local from = 0

	for to, atom in seq:foreach() do -- iterate over incoming events
		timeR(from, to, forge, atom) -- call responder for event

		from = to
	end

	timeR(from, n, forge)
end"""
	] .

1082 1083 1084 1085
moony:bank-template_part-1
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
1086
		moony:editorHidden false ;
1087
		moony:logHidden true ;
1088
		moony:logFollow true ;
1089 1090
		moony:paramHidden true ;
		moony:paramCols 3 ;
1091
		moony:paramRows 4 ;
1092 1093 1094 1095
		moony:code """-- Template 1: MIDI Responder

local block = false -- route unmatched messages as-is

1096
-- define a MIDIResponder object
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156
local midiR = MIDIResponder({
	-- callbacks featuring a channel parameter
	[MIDI.NoteOn] = function(self, frames, forge, chan, note, vel)
		--TODO
	end,
	[MIDI.NoteOff] = function(self, frames, forge, chan, note, vel)
		--TODO
	end,
	[MIDI.NotePressure] = function(self, frames, forge, chan, note, vel)
		--TODO
	end,
	[MIDI.Bender] = function(self, frames, forge, chan, lsb, msb)
		--TODO
	end,
	[MIDI.Controller] = function(self, frames, forge, chan, cntrl, val)
		--TODO
	end,
	[MIDI.ProgramChange] = function(self, frames, forge, chan, lsb, msb)
		--TODO
	end,
	[MIDI.ChannelPressure] = function(self, frames, forge, chan, val)
		--TODO
	end,

	-- callbacks featuring no channel parameter
	[MIDI.SystemExclusive] = function(self, frames, forge, _, ...)
		--TODO
	end,
	[MIDI.QuarterFrame] = function(self, frames, forge, _, type, val)
		--TODO
	end,
	[MIDI.SongPosition] = function(self, frames, forge, _, lsb, msb) 
		--TODO
	end,
	[MIDI.SongSelect] = function(self, frames, forge, _, val)
		--TODO
	end,
	[MIDI.TuneRequest] = function(self, frames, forge)
		--TODO
	end,
	[MIDI.Clock] = function(self, frames, forge)
		--TODO
	end,
	[MIDI.Start] = function(self, frames, forge)
		--TODO
	end,
	[MIDI.Continue] = function(self, frames, forge)
		--TODO
	end,
	[MIDI.Stop] = function(self, frames, forge)
		--TODO
	end,
	[MIDI.ActiveSense] = function(self, frames, forge)
		--TODO
	end,
	[MIDI.Reset] = function(self, frames, forge)
		--TODO
	end
}, not block)

1157
function run(n, control, notify, seq, forge)
1158 1159 1160 1161 1162
	for frames, atom in seq:foreach() do -- iterate over incoming events
		local handled = midiR(frames, forge, atom) -- call responder for event
	end
end"""
	] .
1163 1164 1165 1166 1167

moony:bank-template_part-2
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
1168
		moony:editorHidden false ;
1169
		moony:logHidden true ;
1170
		moony:logFollow true ;
1171 1172
		moony:paramHidden true ;
		moony:paramCols 3 ;
1173
		moony:paramRows 4 ;
1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
		moony:code """-- Template 2: Time Responder

-- define a TimeResponder object
local timeR = TimeResponder({
	[Time.speed] = function(self, frames, forge, speed)
		--TODO
	end,
	[Time.bar] = function(self, frames, forge, bar)
		--TODO
	end,
	[Time.barBeat] = function(self, frames, forge, barBeat)
		--TODO
	end,
	[Time.beatUnit] = function(self, frames, forge, beatUnit)
		--TODO
	end,
	[Time.beatsPerBar] = function(self, frames, forge, beatsPerBar)
		--TODO
	end,
	[Time.beatsPerMinute] = function(self, frames, forge, beatsPerMinute)
		--TODO
	end,
	[Time.framesPerSecond] = function(self, frames, forge, framesPerSecond)
		--TODO
	end,
	[Time.frame] = function(self, frames, forge, frame)
		--TODO
	end
})

1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
-- push time responder state to stash
function stash(forge)
	timeR:stash(forge)
end

-- pop time responder state from stash
function apply(atom)
	timeR:apply(atom)
end

1214
function run(n, control, notify, seq, forge)
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
	local from = 0 -- initial frame offset

	for to, atom in seq:foreach() do -- iterate over incoming events
		timeR(from, to, forge, atom) -- call responder for event

		from = to -- update to new frame offset
	end

	timeR(from, n, forge) -- call responder for remaining frames
end"""
	] .
1226 1227 1228 1229 1230

moony:bank-template_part-3
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
1231
		moony:editorHidden false ;
1232
		moony:logHidden true ;
1233
		moony:logFollow true ;
1234 1235
		moony:paramHidden true ;
		moony:paramCols 3 ;
1236
		moony:paramRows 4 ;
1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
		moony:code """-- Template 3: OSC Responder

-- define an OSCResponder object
local oscR = OSCResponder({
	['/ping'] = function(self, frames, forge, fmt, ...)
		--TODO
	end,
	['/pong'] = function(self, frames, forge, fmt, ...)
		--TODO
	end
})

1249
function run(n, control, notify, seq, forge)
1250 1251 1252 1253 1254
	for frames, atom in seq:foreach() do -- iterate over incoming events
		local handled = oscR(frames, forge, atom) -- call responder for event
	end
end"""
	] .
1255 1256 1257 1258 1259

moony:bank-template_part-4
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
1260
		moony:editorHidden false ;
1261
		moony:logHidden true ;
1262
		moony:logFollow true ;
1263
		moony:paramHidden false ;
1264
		moony:paramCols 3 ;
1265
		moony:paramRows 4 ;
1266 1267 1268
		moony:code """-- Template 4: State Responder

-- define URI prefix for state parameters
1269
local urn = Mapper('urn:uuid:3473b33a-e6d2-471c-89d3-4dea1a0d8feb#')
1270 1271

-- define single parameter
1272
local param = Parameter({
1273 1274 1275
	[RDFS.label] = 'Awesome Parameter',
	[RDFS.comment] = 'does this and that...',
	[RDFS.range] = Atom.Int,
1276 1277
	[LV2.minimum] = 0,
	[LV2.maximum] = 10,
1278 1279
	[Units.unit] = Units.hz,
	[RDF.value] = 5
1280
})
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302

-- define a StateResponder object
local stateR = StateResponder({
	[Patch.readable] = {
		--TODO
	},
	[Patch.writable] = {
		[urn.param] = param -- register parameter to writable group
	}
})

-- push parameter values to disk
function save(forge)
	stateR:stash(forge)
end

-- pop parameter values from disk
function restore(atom)
	stateR:apply(atom)
end

-- register parameters to UI
1303
function once(n, control, notify)
1304 1305 1306
	stateR:register(0, notify)
end

1307 1308
function run(n, control, notify)
	for frames, atom in control:foreach() do -- iterate over incoming events
1309 1310 1311 1312
		local handled = stateR(frames, notify, atom)
	end
end"""
	] .
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326

moony:bank-template_part-5
	a pset:Preset ;
	doap:license lic:Artistic-2.0 ;
	state:state [
		moony:editorHidden false ;
		moony:logHidden true ;
		moony:logFollow true ;
		moony:paramHidden false ;
		moony:paramCols 1 ;
		moony:paramRows 1 ;
		moony:code """-- Template 5: Code Injection

-- define URI prefix for state parameters
1327
local urn = Mapper('urn:uuid:1ad928a1-e050-4380-be39-8dca9bc18f44#')
1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386

-- pretty print error message from injection
local function perr(err)
  return string.gsub(err, '%b[]:', '')
end

-- define injection parameter
local injection = Parameter({
  [RDFS.label] = 'Injection',
  [RDFS.comment] = 'enter your code for injection here',
  [RDFS.range] = Atom.String,
  [Moony.syntax] = Lua.lang,
  _value = '',
  [Patch.Get] = function(self)
    return self._value
  end,
  [Patch.Set] = function(self, value)
    self._value = value

    local fn, err = load(value) -- compile injection code
    if fn then -- compilation succeded
      local stat, err = pcall(fn) -- run injection code
      if not stat then -- running code failed
        print(perr(err)) -- report error
      end
    else -- compilation failed
      print(perr(err)) -- report error
    end
  end
})

-- define a StateResponder object
local stateR = StateResponder({
  [Patch.writable] = {
    [urn.injection] = injection -- register parameter to writable group
  }
})

-- push parameter values to disk
function save(forge)
  stateR:stash(forge)
end

-- pop parameter values from disk
function restore(atom)
  stateR:apply(atom)
end

-- register parameters to UI
function once(n, control, notify)
  stateR:register(0, notify)
end

function run(n, control, notify)
  for frames, atom in control:foreach() do -- iterate over incoming events
    local handled = stateR(frames, notify, atom)
  end
end"""
	] .