aboutsummaryrefslogtreecommitdiffstats
path: root/classes/Bowelyzer.sc
blob: 2ff0e759015ac00665dd95da491714b066bd994e (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
Bowelyzer{

  var <gui, <server, <analyzer, <config, <hub;

  *new{
    arg configFile;
    ^super.new.init(configFile);
  }

  init{
    //initialize with configuration, if available (else use default)
    arg configFile;
    config = BowelyzerConfig.new(configFile);
    server = Server.new(
      \bowelyzer,
      BowelyzerOSCHub.getNetAddr(
        config.config.at(\synthServerAddress),
        config.config.at(\synthServerPort)
      )
    );
    Server.default = server;
    server.waitForBoot({
      hub = BowelyzerOSCHub.new(config.config);
      analyzer = BowelyzerAnalyzer.new(config.config);
      gui = BowelyzerGUI.new(config.config);
      this.addGUIListeners;
      this.addServerListeners;
   },
   5,
   {"scsynth failed to start!".postln});
  }

  addGUIListeners{
    // listen for control changes
    OSCdef.newMatching(
      key: \controls,
      func: {|msg, time, addr, recvPort|
        postln("Received: "++msg);
        // if the control exists, change it
        if(msg[1].notNil && msg[2].notNil && msg[3].notNil,{
          if(config.config.at(\controls).includesKey(msg[1]),{
            if(config.config.at(\controls).at(msg[1]).includesKey(msg[2]),{
              config.config.at(\controls).at(msg[1]).put(msg[2], config.getControlValue(msg[2], msg[3]));
            });
          });
        });
      },
      path: "/controls",
      srcID: NetAddr.new("127.0.0.1", NetAddr.langPort)
    );
    // listen for input changes (rename and input channel)
    OSCdef.newMatching(
      key: \inputs,
      func: {|msg, time, addr, recvPort|
        var name = msg[1],
        type = msg[2],
        update = msg[3];
        postln("Received: "++msg);
        if(name.notNil && type.notNil && update.notNil,{
          if(config.config.at(\inputs).includesKey(name) && config.config.at(\controls).includesKey(name),{
            switch(type,
              \name,{
                //if the name exists and the new name is not empty, change it
                //FIXME: check whether new name exists and only setup then, else reset the textfield
                if(update != "",{
                  //move the controls
                  config.config.at(\controls).put(update.asSymbol, config.config.at(\controls).at(name));
                  config.config.at(\controls).removeAt(name.asSymbol);
                  // rename the input
                  config.config.at(\inputs).put(update.asSymbol, config.config.at(\inputs).at(name));
                  config.config.at(\inputs).removeAt(name);
                  Routine{
                    // rename the channel View
                    gui.channels.do({|channel|
                      if(channel.name.asSymbol == name.asSymbol, {
                        channel.name = update.asSymbol;
                      });
                    });
                    // OSC indicator: stop old task, remove it and create a new one with updated name
                    gui.indicators.at(name).stop;
                    gui.indicators.removeAt(name);
                    gui.addOSCIndicatorFadeOutTask(update);
                  }.play(AppClock);
                  // stop the listener for LevelIndicator by name and start a new one based upon the updated name
                  OSCdef(("levels_"++name).asSymbol).free;
                  this.setupLevelListener(update, hub.synthServer);
                  // free the synth on the server and start a new one according to the config
                  analyzer.freeAnalysisSynth(name.asSymbol);
                  analyzer.synths.removeAt(name);
                  analyzer.addSynthWithName(update);
                  // start the Synth (possibly paused)
                  Routine{
                    1.wait;
                    analyzer.startSynthWithNameAndControls(update.asSymbol, config.config.at(\controls).at(update), config.config.at(\inputs).at(update));
                  }.play;
                  // rename the hub listener for the Synth
                  hub.freeSynthListener(name);
                  hub.addSynthListener(update);
                  hub.startSynthListener(update);
                });
              },
              \input,{
                // change the input in configuration
                config.config.at(\inputs).put(name, update.asInteger);
                // change the input in the Synth
                analyzer.setSynthControl(name.asSymbol, \inputChannel, update.asInteger);
              }
            );
          });
        });
      },
      path: "/inputs",
      srcID: NetAddr.new("127.0.0.1", NetAddr.langPort)
    );
    // listen for toggling messages to "mute" channel
    OSCdef.newMatching(
      key: \toggle,
      func: {|msg, time, addr, recvPort|
        var name = msg[1],
        toggle = msg[2];
        postln("Received: "++msg);
        if(name.notNil && toggle.notNil && toggle.isInteger,{
          if(config.config.at(\inputs).includesKey(name),{
            switch(toggle,
              0,{
                config.config.at(\controls).at(name).put(\active, true);
                analyzer.startAnalysisSynth(name);
              },
              1,{
                config.config.at(\controls).at(name).put(\active, false);
                analyzer.stopAnalysisSynth(name);
              }
            );
          });
        });
      },
      path: "/toggle",
      srcID: NetAddr.new("127.0.0.1", NetAddr.langPort)
    );

    // listen for address messages to change OSC addresses
    OSCdef.newMatching(
      key: \address,
      func: {|msg, time, addr, recvPort|
        var type = msg[1],
        address = msg[2];
        postln("Received: "++msg);
        if(type.notNil && config.config.includesKey(type),{
          config.config.put(type.asSymbol, address.asString);
          hub.setupNetAddressesFromConfig(config.config);
        });
      },
      path: "/address",
      srcID: hub.local
    );

    // listen for port messages to change OSC ports
    OSCdef.newMatching(
      key: \port,
      func: {|msg, time, addr, recvPort|
        var type = msg[1],
        port = msg[2];
        postln("Received: "++msg);
        if(type.notNil && config.config.includesKey(type),{
          config.config.put(type.asSymbol, port.asInteger);
          hub.setupNetAddressesFromConfig(config.config);
        });
      },
      path: "/port",
      srcID: hub.local
    );

    // listen for message to save configuration to current file
    OSCdef.newMatching(
      key: \save,
      func: {|msg, time, addr, recvPort|
        var path = msg[1];
        postln("Received: "++msg);
        config.writeConfigurationFile;
      },
      path: "/save",
      srcID: hub.local
    );

    // listen for message to save configuration to current file
    OSCdef.newMatching(
      key: \saveas,
      func: {|msg, time, addr, recvPort|
        var name = msg[0],
        fileName = msg[1];
        postln("Received: "++msg);
        config.writeConfigurationFile(fileName);
      },
      path: "/saveas",
      srcID: hub.local
    );

    // listen for messages to load configuration from file
    OSCdef.newMatching(
      key: \load,
      func: {|msg, time, addr, recvPort|
        var name = msg[0],
        fileName = msg[1];
        postln("Received: "++msg);
        //TODO: destroy current GUI elements
        //TODO: load channels, etc.
      },
      path: "/load",
      srcID: hub.local
    );

    // listen for messages to free (close) channels
    OSCdef.newMatching(
      key: \free,
      func: {|msg, time, addr, recvPort|
        var type = msg[0],
        name = msg[1];
        postln("Received: "++msg);
        // remove OSCdefs for the channel
        OSCdef.all.pairsDo({|key, value|
          if(key == name.asSymbol,{
            ("Freeing OSCdef: "++name).postln;
            value.free;
          });
          if(key == ("levels_"++name).asSymbol,{
            ("Freeing OSCdef: levels_"++name).postln;
            value.free;
          });
        });
        // free Synth
        analyzer.freeAnalysisSynth(name);
        // remove configurations for the channel
        config.config.at(\inputs).removeAt(name);
        config.config.at(\controls).removeAt(name);
        // remove fadeout task for OSC indicator
        gui.freeOSCIndicatorFadeOutTask(name);
        // remove GUI elements
        {
          gui.channels.do({|channel,i|
            if(channel.name == name.asString, {
              channel.remove;
              gui.channels.remove(channel);
            });
          });
          // resize the channelContainerView according to new amount of channels
          gui.channelContainerView.maxSize_((gui.channelViewWidth*config.config.at(\inputs).size)@gui.channelViewHeight);
        }.defer;
      },
      path: "/free",
      srcID: NetAddr.new("127.0.0.1", NetAddr.langPort)
    );

  }

  // add OSCdefs listening for messages coming from scsynth, to update the GUI
  addServerListeners{
    this.setupIndicatorListener;
    // add and start a listener for each channel's LevelIndicator
    config.config.at(\inputs).pairsDo({|key,value|
      this.setupLevelListener(key, hub.synthServer);
    });
  }

  // setup a listener for a level indicator by name and source (of synth server)
  setupIndicatorListener{
    // listen for indicator messages
    OSCdef.newMatching(
      key: \indicate,
      func: {|msg, time, addr, recvPort|
        var name = msg[1];
        //postln("Indicate for "++name);
        if(config.config.at(\inputs).includesKey(name),{
          if(gui.indicators.includesKey(name),{
            gui.indicators.at(name).stop;
            gui.indicators.at(name).reset;
            gui.indicators.at(name).start(AppClock);
          });
        });
      },
      path: "/indicate",
      srcID: NetAddr.new("127.0.0.1", NetAddr.langPort)
    );
  }

  // setup a listener for a level indicator by name and source (of synth server)
  setupLevelListener{
    arg name, server;
    postln("Setting up LevelListener for: "++name);
    OSCdef.newMatching(
      key: ("levels_"++name).asSymbol,
      func: {|msg, time, addr, recvPort|
        var name = msg[0].asString.replace("/levels/", "").asSymbol,
        value = msg[3].ampdb.linlin(-40, 0, 0, 1),
        peak = msg[4].ampdb.linlin(-40, 0, 0, 1);
        {
          gui.setLevel(name, value, peak);
        }.defer;
      },
      path: "/levels/"++name,
      srcID: server
    );
  }
}