Skip to content

Commit 6e7eec0

Browse files
committed
update pattern.lua
- add examples to all public functions - allow function generators in `new` for initial values - fixed `empty_value` handling - remove `add` alias to `push_back`
1 parent 345690e commit 6e7eec0

File tree

1 file changed

+135
-28
lines changed

1 file changed

+135
-28
lines changed

types/nerdo/library/pattern.lua

+135-28
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ local empty_pulse_values = {
3434
---pattern.from{ 1, 0.5, 1, 1 }:euclidean(12)
3535
----- generate/init from functions
3636
---pattern.new(12):init(function() return math.random(0.5, 1.0) end )
37+
---pattern.new(16):init(scale("c", "minor").notes_iter())
3738
----- generate note patterns
3839
---pattern.from{ "c4", "g4", "a4" } * 7 + { "a4", "g4", "c4" }
39-
---pattern.from{ 1, 5, 6, 4 }:map(function(index, degree)
40-
--- return scale("c", "minor"):chord(degree)
40+
----- generate chord patterns
41+
---pattern.from{ 1, 5, 6, 4 }:map(function(index, degree)
42+
--- return scale("c", "minor"):chord(degree)
4143
---end)
4244
---```
4345
---@class Pattern : table
@@ -50,17 +52,17 @@ pattern = {}
5052
----------------------------------------------------------------------------------------------------
5153

5254
---Create a new empty pattern or pattern with the given length.
53-
---@param length integer?
54-
---@param value PulseValue? Value used as initial value when length > 0. by default 0.
55+
---@param length integer? Initial length of the pattern. When undefined, an empty pattern is created.
56+
---@param value (PulseValue|(fun(index: integer):PulseValue))? Value or generator function, which sets the initial values in the pattern.
5557
---@return Pattern
5658
---@nodiscard
5759
function pattern.new(length, value)
5860
local t = setmetatable({}, {
59-
---all table functions can be accessed as member functions
61+
---all pattern functions can be accessed as member functions
6062
__index = pattern,
6163
---operator + adds two patterns
6264
__add = function(a, b)
63-
return a:copy():add(b)
65+
return a:copy():push_back(b)
6466
end,
6567
---operator * creates a repeated pattern
6668
__mul = function(a, b)
@@ -70,15 +72,19 @@ function pattern.new(length, value)
7072
-- initialize
7173
if length ~= nil then
7274
value = value or 0
73-
for _ = 1, length do
74-
table.insert(t, value)
75-
end
75+
t:init(value, length)
7676
end
7777
return t
7878
end
7979

8080
---Create a new pattern from a set of values or tables.
8181
---When passing tables, those will be flattened.
82+
---
83+
---### examples:
84+
---```lua
85+
---local p = pattern.from(1,0,1,0) -- {1,0,1,0}
86+
---p = pattern.from({1,0},{1,0}) -- {1,0,1,0}
87+
---```
8288
---@param ... PulseValue|(PulseValue[])
8389
---@return Pattern
8490
---@nodiscard
@@ -87,19 +93,27 @@ function pattern.from(...)
8793
end
8894

8995
-- create a shallow-copy of the given pattern (or self)
96+
---
97+
---### examples:
98+
---```lua
99+
---local p = pattern.from(1, 0)
100+
---local p2 = p:copy() --- {1,0}
101+
---```
90102
---@return Pattern
91103
---@nodiscard
92104
function pattern.copy(self)
93105
return pattern.from(self:unpack())
94106
end
95107

96-
---Create an new pattern or spread and existing pattern evenly within the given length,
97-
---using Bresenham’s line algorithm. Similar, but not exactly like "euclidean".
108+
---Create an new pattern or spread and existing pattern evenly within the given length.
109+
---Similar, but not exactly like `euclidean`.
110+
---
111+
---Shortcut for `pattern.from{1,1,1}:spread(length / #self):rotate(offset)`
98112
---
99-
---Shortcut for:
113+
---### examples:
100114
---```lua
101-
---pattern.new(1, steps):spread(length / steps):rotate(offset) -- or
102-
---pattern.from{1,1,1}:spread(length / #self):rotate(offset)
115+
---local p = pattern.distributed(3, 8) --- {1,0,0,1,0,1,0}
116+
---p = pattern.from{1,1}:distributed(4, 1) --- {0,1,0,1}
103117
---```
104118
---@param steps table|integer Existing pattern or number of on steps in the pattern.
105119
---@param length integer Number of total steps in the pattern.
@@ -164,6 +178,12 @@ end
164178
---Create a new euclidean rhythm pattern with the given pulses or number of new pulses
165179
---in the given length and optionally rotate the contents.
166180
---[Euclidean Rhythm](https://en.wikipedia.org/wiki/Euclidean_rhythm)
181+
---
182+
---### examples:
183+
---```lua
184+
---local p = pattern.euclidean(3, 8) -- {1,0,0,1,0,0,1,0}
185+
---p = pattern.from{"x", "x", "x"}:euclidean(8, 0, "-") -- {"x","-","-","x","-","-","x","-"}
186+
---```
167187
---@param steps table|integer Existing pattern or number of on steps in the pattern.
168188
---@param length integer Number of total steps in the pattern.
169189
---@param offset integer? Optional rotation offset.
@@ -186,7 +206,7 @@ function pattern.euclidean(steps, length, offset, empty_value)
186206
"invalid length argument (expecting an integer > 0)")
187207
assert(type(offset) == "number" or offset == nil,
188208
"invalid offset argument (must be an integer or nil)")
189-
empty_value = empty_value == nil and empty_pulse_value(steps) or 0
209+
empty_value = empty_value or empty_pulse_value(steps)
190210
if #front == 0 then
191211
local result = pattern.new();
192212
for _ = 1, length do
@@ -224,6 +244,12 @@ end
224244
----------------------------------------------------------------------------------------------------
225245

226246
---Shortcut for table.unpack(pattern): returns elements from this pattern as var args.
247+
---
248+
---### examples:
249+
---```lua
250+
---local p = pattern.from{1,2,3,4}
251+
---local v1, v2, v3, v4 = p:unpack()
252+
---```
227253
---@return (PulseValue)[]
228254
---@nodiscard
229255
function pattern.unpack(self)
@@ -232,6 +258,13 @@ end
232258

233259
---Get sub range from the pattern as new pattern.
234260
---When the given length is past end of this pattern its filled up with empty values.
261+
---
262+
---### examples:
263+
---```lua
264+
---local p = pattern.from{1,2,3,4}
265+
---p = p:subrange(2,3) -- {2,3}
266+
---p = p:subrange(1,4,"X") -- {2,3,"X","X"}
267+
---```
235268
---@param i integer Subrange start
236269
---@param j integer? Subrange end (defaults to pattern length)
237270
---@param empty_value PulseValue? Value used as empty value (by default 0 or guessed from existing content).
@@ -242,26 +275,41 @@ function pattern.subrange(self, i, j, empty_value)
242275
"invalid subrange end argument (must be an integer > 0)")
243276
local len = j or #self
244277
local a = pattern.new()
245-
empty_value = empty_value == nil and empty_pulse_value(self) or 0
278+
empty_value = empty_value or empty_pulse_value(self)
246279
for ii = i, len do
247280
a:push_back(self[ii] or empty_value)
248281
end
249282
return a
250283
end
251284

252285
---Get first n items from the pattern as new pattern.
286+
---When the given length is past end of this pattern its filled up with empty values.
287+
---
288+
---### examples:
289+
---```lua
290+
---local p = pattern.from{1,2,3,4}
291+
---p = p:take(2) -- {1,2}
292+
---p = p:take(4, "") -- {1,2,"",""}
293+
---```
253294
---@param length integer
254-
function pattern.take(self, length)
295+
---@param empty_value PulseValue? Value used as empty value (by default 0 or guessed from existing content).
296+
function pattern.take(self, length, empty_value)
255297
assert(type(length) == "number" and length > 0,
256298
"invalid length argument (must be an integer > 0)")
257-
return self:subrange(1, length)
299+
return self:subrange(1, length, empty_value)
258300
end
259301

260302
----------------------------------------------------------------------------------------------------
261303
--- Modify contents
262304
----------------------------------------------------------------------------------------------------
263305

264306
---Clear a pattern, remove all its contents.
307+
---
308+
---### examples:
309+
---```lua
310+
---local p = pattern.from{0,0}
311+
---p:clear() -- {}
312+
---```
265313
function pattern.clear(self)
266314
while #self > 0 do
267315
table.remove(self)
@@ -270,6 +318,13 @@ function pattern.clear(self)
270318
end
271319

272320
---Fill pattern with the given value or generator function in length.
321+
---
322+
---### examples:
323+
---```lua
324+
---local p = pattern.from{0,0}
325+
---p:init(1) -- {1,1}
326+
---p:init("X", 3) -- {"X","X", "X"}
327+
---```
273328
---@param value PulseValue|fun(index: integer):PulseValue
274329
---@param length integer?
275330
function pattern.init(self, value, length)
@@ -292,6 +347,14 @@ function pattern.init(self, value, length)
292347
end
293348

294349
---Apply the given function to every item in the pattern.
350+
---
351+
---### examples:
352+
---```lua
353+
---local p = pattern.from{1,3,5}
354+
---p:map(function(k, v)
355+
--- return scale("c", "minor"):degree(v)
356+
---end) -- {48, 51, 55}
357+
---```
295358
---@param fun fun(index: integer, value: PulseValue): PulseValue
296359
function pattern.map(self, fun)
297360
local num = #self
@@ -302,6 +365,12 @@ function pattern.map(self, fun)
302365
end
303366

304367
---Invert the order of items.
368+
---
369+
---### examples:
370+
---```lua
371+
---local p = pattern.from{1,2,3}
372+
---p:reverse() -- {3,2,1}
373+
---```
305374
function pattern.reverse(self)
306375
local num = #self
307376
for i = 1, math.floor(num / 2) do
@@ -312,6 +381,13 @@ function pattern.reverse(self)
312381
end
313382

314383
---Shift contents by the given amount to the left (negative amount) or right.
384+
---
385+
---### examples:
386+
---```lua
387+
---local p = pattern.from{1,0,0}
388+
---p:rotate(1) -- {0,1,0}
389+
---p:rotate(-2) -- {0,0,1}
390+
---```
315391
---@param amount integer
316392
function pattern.rotate(self, amount)
317393
assert(type(amount) == "number",
@@ -332,8 +408,17 @@ end
332408
--- Add/remove contents
333409
----------------------------------------------------------------------------------------------------
334410

335-
---Push any number of items or other pattern contents to the end of the pattern.
336-
---When passing array alike tables or patterns, they will be unpacked.
411+
---Push a single or multiple number of items or other pattern contents to the end of the pattern.
412+
---Note: When passing array alike tables or patterns, they will be *unpacked*.
413+
---
414+
---### examples:
415+
---```lua
416+
---local p = pattern.new()
417+
---p:push_back(1) -- {1}
418+
---p:push_back(2,3) -- {1,2,3}
419+
---p:push_back{4} -- {1,2,3,4}
420+
---p:push_back({5,{6,7}) -- {1,2,3,4,5,6,7}
421+
---```
337422
---@param ... PulseValue|(PulseValue)[]
338423
function pattern.push_back(self, ...)
339424
local function add_unpacked(v)
@@ -348,23 +433,34 @@ function pattern.push_back(self, ...)
348433
end
349434
end
350435
local len = select("#", ...)
351-
for i = 1,len do
436+
for i = 1, len do
352437
local v = select(i, ...)
353438
add_unpacked(v)
354439
end
355440
return self
356441
end
357442

358-
---Alias for pattern.push_back.
359-
pattern.add = pattern.push_back
360-
361-
---Remove an entry from the back of the pattern and returns the popped item.
443+
---Remove an entry from the back of the pattern. returns the popped item.
444+
---
445+
---### examples:
446+
---```lua
447+
---local p = pattern.from({1,2})
448+
---p:pop_back() -- {1}
449+
---p:pop_back() -- {}
450+
---p:pop_back() -- {}
451+
---```
362452
---@return PulseValue
363453
function pattern.pop_back(self)
364454
return table.remove(self)
365455
end
366456

367457
---Duplicate the pattern n times.
458+
---
459+
---### examples:
460+
---```lua
461+
---local p = pattern.from{1,2,3}
462+
---patterns:repeat_n(2) -- {1,2,3,1,2,3}
463+
---```
368464
---@param count integer
369465
function pattern.repeat_n(self, count)
370466
assert(type(count) == "number" and count > 0,
@@ -381,17 +477,28 @@ end
381477
---Expand (with amount > 1) or shrink (amount < 1) the length of the pattern by the
382478
---given factor, spreading allowed content evenly and filling gaps with 0 or the
383479
---given empty value.
480+
---
481+
---### examples:
482+
---```lua
483+
---local p = pattern.from{1,1}
484+
---p:spread(2) -- {1,0,1,0}
485+
---p:spread(0.5) -- {1,1}
486+
---```
384487
---@param amount number Spread factor (2 = double, 0.5 = half the size).
385488
---@param empty_value PulseValue? Value used as empty value (by default 0 or guessed from existing content).
386489
function pattern.spread(self, amount, empty_value)
387490
assert(type(amount) == "number" and amount > 0,
388491
"invalid amount argument (must be an integer > 0)")
389-
empty_value = empty_value == nil and empty_pulse_value(self) or 0
492+
empty_value = empty_value or empty_pulse_value(self)
390493
local old_num = #self
494+
local new_num = math.floor(old_num * amount + 0.5)
391495
local old = self:copy()
392-
self:init(empty_value, old_num * amount)
496+
self:init(empty_value, new_num)
393497
for i = 1, old_num do
394-
self[math.floor((i - 1) * amount + 0.5) + 1] = old[i]
498+
local j = math.floor((i - 1) * amount + 0.5) + 1
499+
if j <= new_num then
500+
self[j] = old[i]
501+
end
395502
end
396503
return self
397504
end

0 commit comments

Comments
 (0)