blob: 3c3107bf23935822515dfbf7b35bd5c54613b697 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
# Sequencer/tracker based on a string pattern
⟨
Opts # Make options object
MakeTrack # Build entire track
Sequence # Build a single instrument
ReadPattern # Read pattern for a track from lf-separated string
GetLength # Find length of pattern
⟩⇐
"tracker.bqn takes a single option namespace, or no arguments" ! 1≥≠•args
op ← ≠◶⟨•Import∘"options.bqn", ⊑⟩ •args
⟨Add⟩‿⟨Lp2,Hp2⟩ ← op •Import¨ "mix.bqn"‿"filter.bqn"
# Sequencing options
Opts ← {
shifts ← {𝕩.shifts}⎊("><]["≍÷8‿¯8‿3‿¯3) 𝕩
gains ← {𝕩.gains} ⎊("-+"≍1.2⋆¯1‿1) 𝕩
noop ← {𝕩.noop} ⎊"." 𝕩
control ⇐ noop∾shifts∾○⊏gains # Non-sample characters
GetShift‿GetVol ⇐ 0‿1 {{𝕩⊏˜𝕨⊸⊐}⟜(∾⟜𝕨)˝𝕩}¨ shifts‿gains
beat ⇐ {𝕩.beat} ⎊(!˙) 𝕩 # Starting samples/beat (optional with :BEAT:)
empty ⇐ {𝕩.empty}⎊(2‿0⥊0) 𝕩
swing ⇐ {𝕩.swing}⎊⟨1⟩ 𝕩 # Modifier for length of each beat
pink ⇐ {𝕩.pink} ⎊2 𝕩 # Level of pink noise "humanization"
end ⇐ {𝕩.end} ⎊0 𝕩 # Number of additional beats at the end
useOverlap ⇐ {𝕩.UseOverlap}⎊(1˙) 𝕩 # Whether sample function 𝕩 should overlap
applyPost ⇐ {𝕩.ApplyPost }⎊({𝕎𝕩}˙) 𝕩 # Apply post-processing thing 𝕨
{𝕊:UseOverlap↩0˙⋄ApplyPost↩⊢}⍟{1≡𝕩.fast}⎊@ 𝕩 # fast←1 to take shortcuts
}
opt0 ← Opts{⇐}
MakeTrack ← { 𝕊𝕩:opt0𝕊𝕩 ; o 𝕊 pattern‿sample‿post:
_sum ← { o.empty 𝔽⊸Add´ 𝕩 } # Avoid extra memory use
sp ← pattern ⋈¨ sample
{ (post⊑˜⊑𝕩)o.ApplyPost (o Sequence ⊑⟜sp)_sum 𝕩 } _sum ⊔⊐post
}
# String handling
Ex ← +`⊸×⟜¬-⊢
Cut ← Ex˜∘=⊔⊢
ReadPattern ← { ∾˘⍉> 1⊸»⊸<⊸Ex∘(0=≠¨)⊸⊔ (@+10) Cut 𝕩 }
# :BEAT: indication handling
Getb ← •BQN
Avgb ← (+´÷≠)∘⥊
# Sound utilities
Con ← ∾≍
# Pink noise with no normalization
Rand ← -⟜¬ {op.RandFloats𝕩}
PinkDiff ← {𝕩 ↑ ⥊∘⍉∘≍´⌽ -⟜«∘Rand¨ (1⌈↕∘⌈)⌾(2⋆⁼⊢)2⌈𝕩}
# Output is ⟨list of lengths, list of characters, average lengths⟩
ParseBeats ← {
m ← "Initial beat length unknown"
s ← ':' Cut ' '⊸≠⊸/ 𝕩
g ← (⊑1↑s) (𝕨!∘m⊘⋈⊢)⊸∾⍟(0<≠∘⊣) Getb¨⌾(⊏⍉) ⌊‿2⥊1↓s
<∘∾˘ ⍉ (≠∘⊢ ⥊¨ ⋈∾Avgb∘⊣)´˘ g
}
GetLength ← { ⌊ +´ ⊑ 𝕨 ParseBeats ⊏𝕩 }
GetLastBeat ← {
¬⊑':'∊𝕩 ? 𝕨.beat ;
Avgb Getb 1↓ (1-˜⊢´)⊸=∘(+`':'⊸=)⊸/ 𝕩
}
Sequence ← { 𝕊𝕩:opt0𝕊𝕩 ; o 𝕊 pattern‿GetSamples:
# Beat length, character, average length
b‿c‿a ← o.beat ParseBeats pattern
# Compute lengths
b +↩ (⥊⟜(o.swing-1) + (o.pink÷100){(𝕗×PinkDiff)⍟(0≠𝕗)})∘≠⊸× a
ge ← (m∾0) (¬⊸×-⊣) g ← +`0∾˜ m ← ¬ c∊o.control
sh ← +´¨ ge ⊔ 0∾˜b × o.GetShift c
len ← ⌊0.5+ (-⟜»sh) + +´¨ g ⊔ b∾o.endׯ1⊏b
# Samples
vol ← ×´¨ ¯1↓ ge ⊔ 1∾˜o.GetVol c
d ← ⟨o.empty⟩ ∾ vol × GetSamples m/c
# Construct output from samples and lengths
{ o.UseOverlap getSamples ?
L‿H ← Lp2‿Hp2 {𝕩⊸𝕎⍟2}¨ 1000
Overlap ← {
tail ← o.empty
Next ← {l𝕊𝕩:
xt ← 𝕩‿tail
f ← l≥¯1⊑≢𝕩
tail ↩ f◶Add‿⊑ l ↓⎉1¨ f↓xt
+´ l ↑⎉1¨ xt
}
Con 𝕨 Next¨ 𝕩
}
len (Overlap⟜(H¨) Add ·Con(L↑⎉1)¨) d
;
Con len ↑⎉1¨ d
}
}
|