aboutsummaryrefslogtreecommitdiff
path: root/tracker.bqn
blob: 5841781a4f205cc0ec569c798cc019cccde97f88 (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
101
# 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¯83¯3) 𝕩
  gains    {𝕩.gains} ("-+"1.2¯11) 𝕩
  noop     {𝕩.noop}  "." 𝕩
  control  noopshiftsgains # Non-sample characters
  GetShiftGetVol  01 {{𝕩˜𝕨}(𝕨)˝𝕩}¨ shiftsgains

  beat   {𝕩.beat} (!˙) 𝕩 # Starting samples/beat (optional with :BEAT:)
  empty  {𝕩.empty}(200) 𝕩
  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
}
opt0  Opts{}

# Build a multi-track pattern
MakeTrack  { 𝕊𝕩:opt0𝕊𝕩 ; o 𝕊 𝕩:
  patternsample  𝕩  pattern<˘(1<=)   # Template, and sample function
  overlap    {voverlap:v;0¨ pattern}𝕩 # Whether adjacent samples overlap
  post       {vpost   :v;˙¨pattern}𝕩 # Post-processing for each channel
  postgroup  {vpostgroup:v; ↕≠post}𝕩  # Group equal values: add before post-processing
  pso  [pattern,sample,overlap]
  _sum  { 𝔽_𝕣x: 𝔽x ; o.empty 𝔽Add´ 𝕩 }
  { (post˜𝕩){𝕎𝕩} (o Sequence pso)_sum 𝕩 } _sum postgroup
}

# 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  (1s) (𝕨!m⋈⊢)(0<≠) Getb¨(⊏⍉) 21s
  <˘  ( ¨ ⋈∾Avgb)´˘ g
}
GetLength  {  +´  𝕨 ParseBeats 𝕩 }
GetLastBeat  {
  ¬⊑':'𝕩 ? 𝕨.beat ;
  Avgb Getb 1 (1-˜´)=(+`':'=)/ 𝕩
}

Sequence  { 𝕊𝕩:opt0𝕊𝕩 ; o𝕊ps:o𝕊𝕩0 ; o 𝕊 patternGetSamplesoverlap:
  # Beat length, character, average length
  bca  o.beat ParseBeats pattern

  # Compute lengths
  b + ((o.swing-1) + (o.pink÷100){(0𝕗)0,𝕗×PinkDiff})× a
  ge  (m0) (¬×-⊣) g  +`0˜ m  ¬ co.control
  sh  +´¨ ge  0˜b × o.GetShift c
  len  0.5+ (-»sh) + +´¨ g  bo.endׯ1b

  # Samples
  vol  ×´¨ ¯1 ge  1˜o.GetVol c
  d  o.empty  vol × GetSamples m/c

  # Construct output from samples and lengths
  { overlap ?
    LH  Lp2Hp2 {𝕩𝕎2}¨ 1000
    Overlap  {
      tail  o.empty
      Next  {l𝕊𝕩:
        xt  𝕩tail
        f  l¯1⊑≢𝕩
        tail  fAdd l 1¨ fxt
        +´ l 1¨ xt
      }
      Con 𝕨 Next¨ 𝕩
    }
    len (Overlap(H¨) Add ·Con(L↑1)¨) d
  ;
    Con len 1¨ d
  }
}