Top Level Namespace

Defined Under Namespace

Classes: Patch, Resample, Sox

Constant Summary collapse

INIT_PATCH =
YAML.load "---\nversion: 1\npatch:\n  id: 2aedc73c-4095-4d1b-ab1b-2121ea9ac19d\n  pan:\n    x: 0.0\n    y: 0.0\n  zoom: 1.0\n  nodes: []\n  wires: []\n"
LIGHT_NODE =
YAML.load "---\ntype: Light\nid: b264602f-365b-48a2-9d25-9b95055a2c34\nposition:\n  x: 0.0\n  y: 0.0\n"
TRIGGER_NODE =
JSON.parse "{\n  \"type\": \"Trigger\",\n  \"id\": \"3e8e612d-3b7a-4c6d-a2ea-2e5f1a00161d\",\n  \"position\": {\n    \"x\": 0,\n    \"y\": 0\n  },\n  \"toggle\": false,\n  \"state\": false\n}\n"
INPUT_NODE =
JSON.parse "{\n  \"type\": \"Input\",\n  \"id\": \"3e8e612d-3b7a-4c6d-a2ea-2e5f1a00161d\",\n  \"position\": {\n    \"x\": 0,\n    \"y\": 0\n  },\n  \"exposedPosition\": {\n    \"x\": 0,\n    \"y\": 0\n  },\n  \"name\": \"input\"\n}\n"
SUBPATCH_NODE =
JSON.parse "{\n  \"type\": \"Patch\",\n  \"id\": \"0fe72e0e-2616-4366-8036-f852398d1c73\",\n  \"position\": {\n    \"x\": -33.04297,\n    \"y\": -44.77734\n  },\n  \"subPatch\": {\n    \"id\": \"0e096166-2c2d-4c0e-bce3-f9c5f42ce5c5\",\n    \"pan\": {\n      \"x\": 0,\n      \"y\": 0\n    },\n    \"zoom\": 1,\n    \"nodes\": [],\n    \"wires\": []\n  }\n}\n"
CLOCK_NODE =
JSON.parse(File.read(File.join(File.dirname(__FILE__), 'clock.json')))
VIA_NODE =

Instance Method Summary collapse

Instance Method Details

#add_node(patch, node) ⇒ Object



59
60
61
62
# File 'lib/audulus.rb', line 59

def add_node(patch, node)
  patch['nodes'] << node
  patch
end

#add_nodes(patch, nodes) ⇒ Object



64
65
66
67
68
# File 'lib/audulus.rb', line 64

def add_nodes(patch, nodes)
  nodes.each do |node|
    add_node(patch, node)
  end
end

#build_clock_nodeObject

gate output is output 0



123
124
125
# File 'lib/audulus.rb', line 123

def build_clock_node
  clone_node(CLOCK_NODE)
end

#build_demux_nodeObject



160
161
162
# File 'lib/audulus.rb', line 160

def build_demux_node
  build_simple_node("Demux8")
end

#build_expr_node(expr) ⇒ Object



164
165
166
167
168
# File 'lib/audulus.rb', line 164

def build_expr_node(expr)
  result = build_simple_node('Expr')
  result['expr'] = expr
  result
end

#build_init_docObject



95
96
97
98
# File 'lib/audulus.rb', line 95

def build_init_doc
  result = clone_node(INIT_PATCH)
  result
end

#build_input_nodeObject



131
132
133
# File 'lib/audulus.rb', line 131

def build_input_node
  clone_node(INPUT_NODE)
end

#build_knob_nodeObject



141
142
143
144
145
146
147
148
149
150
# File 'lib/audulus.rb', line 141

def build_knob_node
  result = build_simple_node("Knob")
  result['knob'] = {
    'value' => 0.5,
    'min' => 0.0,
    'max' => 1.0,
  }
  expose_node(result, 0, 0)
  result
end

#build_light_nodeObject



110
111
112
# File 'lib/audulus.rb', line 110

def build_light_node
  clone_node(LIGHT_NODE)
end

#build_mux_nodeObject



156
157
158
# File 'lib/audulus.rb', line 156

def build_mux_node
  build_simple_node("Mux8")
end

#build_output_nodeObject



135
136
137
138
139
# File 'lib/audulus.rb', line 135

def build_output_node
  result = build_simple_node("Output")
  result['name'] = "Output"
  result
end

#build_patch_from_samples(samples, title1, title2, output_path) ⇒ Object

Given a set of samples, build the Audulus wavetable node



259
260
261
262
# File 'lib/build_audulus_wavetable_node.rb', line 259

def build_patch_from_samples(samples, title1, title2, output_path)
  puts "building #{output_path}"
  File.write(output_path, JSON.generate(make_subpatch(Patch.build_patch(samples, title1, title2)['patch'])))
end

#build_patch_from_wav_file(path) ⇒ Object

Given a path to a single-cycle-waveform wav file, generate an Audulus wavetable node



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/build_audulus_wavetable_node.rb', line 216

def build_patch_from_wav_file(path)
  # break the path into directory and path so we can build the audulus file's name
  parent, file = path.split("/")[-2..-1]

  # load the samples from the WAV file
  samples = Sox.load_samples(path)

  # build the audulus patch name from the WAV file name
  basename = File.basename(file, ".wav")
  puts "building #{basename}.audulus"
  audulus_patch_name = "#{basename}.audulus"

  # build the patch as a full patch
  base_patch = Patch.build_patch(samples, parent, basename)['patch']

  # wrap it up as a subpatch
  final_patch = make_subpatch(base_patch)

  # write the patch to a file as JSON (the format Audulus uses)
  File.write(audulus_patch_name, JSON.generate(final_patch))
end

#build_sample_and_hold_nodeObject



152
153
154
# File 'lib/audulus.rb', line 152

def build_sample_and_hold_node
  build_simple_node("Sample & Hold")
end

#build_simple_node(type) ⇒ Object



176
177
178
179
180
181
182
183
184
185
# File 'lib/audulus.rb', line 176

def build_simple_node(type)
  clone_node({
    "type" => type,
    "id" => "7e5486fc-994c-44f0-ae83-5ebba54d7e3b",
    "position" => {
      "x" => 0,
      "y" => 0
    }
  })
end

#build_subpatch_nodeObject



118
119
120
# File 'lib/audulus.rb', line 118

def build_subpatch_node
  clone_node(SUBPATCH_NODE)
end

#build_text_node(text) ⇒ Object



170
171
172
173
174
# File 'lib/audulus.rb', line 170

def build_text_node(text)
  result = build_simple_node("Text")
  result['text'] = text
  result
end

#build_trigger_nodeObject



114
115
116
# File 'lib/audulus.rb', line 114

def build_trigger_node
  clone_node(TRIGGER_NODE)
end

#build_uuid_map(node) ⇒ Object



27
28
29
30
31
32
33
# File 'lib/audulus.rb', line 27

def build_uuid_map(node)
  existing_uuids = scan_uuids(node)
  existing_uuids.reduce({}) {|h, uuid|
    h[uuid] = SecureRandom.uuid()
    h
  }
end

#build_via_nodeObject



127
128
129
# File 'lib/audulus.rb', line 127

def build_via_node
  clone_node(VIA_NODE)
end

#clone_node(node) ⇒ Object



35
36
37
38
# File 'lib/audulus.rb', line 35

def clone_node(node)
  uuid_map = build_uuid_map(node)
  clone_node_helper(node, uuid_map)
end

#clone_node_helper(node, uuid_map) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/audulus.rb', line 40

def clone_node_helper(node, uuid_map)
  case node
  when Hash
    Hash[node.map {|key, elem|
      if uuid?(elem)
        [key, uuid_map[elem]]
      else
        [key, clone_node_helper(elem, uuid_map)]
      end
    }]
  when Array
    node.map {|elem|
      clone_node_helper(elem, uuid_map)
    }
  else
    node
  end
end

#command(argv) ⇒ Object



272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/build_audulus_wavetable_node.rb', line 272

def command(argv)
  if argv.count != 1
    puts usage
  else
    path = argv[0]
    unless File.exist?(path)
      puts "Cannot find WAV file at #{path}"
      exit(1)
    end

    build_patch_from_wav_file(path)
  end
end

#expose_node(node, x, y) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/audulus.rb', line 78

def expose_node(node, x, y)
  node['exposedPosition'] = {
    'x' => x,
    'y' => y,
  }
  node
end

#make_parabolic_samples(count) ⇒ Object

Make a set of samples conforming to a parabola. This method would be used in place of loading the WAV file.



249
250
251
252
253
254
255
256
# File 'lib/build_audulus_wavetable_node.rb', line 249

def make_parabolic_samples(count)
  f = ->(x) { -4*x**2 + 4*x }
  count.times.map {|index|
    index.to_f / (count-1)
  }.map(&f).map {|sample|
    sample*2-1
  }
end

#make_random_samples(count) ⇒ Object

Make a set of random samples. Useful for generating a cyclic noise wavetable. This method would be used in place of loading the WAV file.



241
242
243
244
245
# File 'lib/build_audulus_wavetable_node.rb', line 241

def make_random_samples(count)
  count.times.map {
    rand*2-1
  }
end

#make_subpatch(subpatch) ⇒ Object



100
101
102
103
104
105
106
107
108
# File 'lib/audulus.rb', line 100

def make_subpatch(subpatch)
  doc = build_init_doc
  patch = doc['patch']

  subpatch_node = build_subpatch_node
  subpatch_node['subPatch'] = subpatch
  add_node(patch, subpatch_node)
  doc
end

#move_node(node, x, y) ⇒ Object



70
71
72
73
74
75
76
# File 'lib/audulus.rb', line 70

def move_node(node, x, y)
  node['position'] = {
    'x' => x,
    'y' => y,
  }
  node
end

#scan_uuids(node) ⇒ Object

Node -> [ UUID ]



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/audulus.rb', line 10

def scan_uuids(node)
  case node
  when Hash
    node.map {|key, elem|
      if uuid?(elem)
        elem
      else
        scan_uuids(elem)
      end
    }.flatten
  when Array
    node.map {|elem| scan_uuids(elem)}.flatten
  else
    []
  end
end

#usageObject



264
265
266
267
268
269
270
# File 'lib/build_audulus_wavetable_node.rb', line 264

def usage
  "Usage: build_audulus_wavetable_node <wav_file>\n\nOutputs an audulus patch built from the <wav_file>. Assumes the input is monophonic, containing a single-cycle waveform.\n  END\nend\n"

#uuid?(string) ⇒ Boolean



5
6
7
# File 'lib/audulus.rb', line 5

def uuid?(string)
  string.kind_of?(String) && /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ =~ string
end

#wire_output_to_input(patch, source_node, source_output, destination_node, destination_input) ⇒ Object



86
87
88
89
90
91
92
93
# File 'lib/audulus.rb', line 86

def wire_output_to_input(patch, source_node, source_output, destination_node, destination_input)
  patch['wires'] << {
    "from" => source_node['id'],
    "output" => source_output,
    "to" => destination_node['id'],
    "input": destination_input
  }
end