1 /*
2  * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.media.sound;
27 
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Comparator;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 
35 /**
36  * This class decodes information from ModelPeformer for use in SoftVoice.
37  * It also adds default connections if they where missing in ModelPerformer.
38  *
39  * @author Karl Helgason
40  */
41 public final class SoftPerformer {
42 
43     static ModelConnectionBlock[] defaultconnections
44             = new ModelConnectionBlock[42];
45 
46     static {
47         int o = 0;
48         defaultconnections[o++] = new ModelConnectionBlock(
49             new ModelSource(
50                 new ModelIdentifier("noteon", "on", 0),
51                 ModelStandardTransform.DIRECTION_MIN2MAX,
52                 ModelStandardTransform.POLARITY_UNIPOLAR,
53                 ModelStandardTransform.TRANSFORM_LINEAR),
54             1, new ModelDestination(new ModelIdentifier("eg", "on", 0)));
55 
56         defaultconnections[o++] = new ModelConnectionBlock(
57             new ModelSource(
58                 new ModelIdentifier("noteon", "on", 0),
59                 ModelStandardTransform.DIRECTION_MIN2MAX,
60                 ModelStandardTransform.POLARITY_UNIPOLAR,
61                 ModelStandardTransform.TRANSFORM_LINEAR),
62             1, new ModelDestination(new ModelIdentifier("eg", "on", 1)));
63 
64         defaultconnections[o++] = new ModelConnectionBlock(
65             new ModelSource(
66                 new ModelIdentifier("eg", "active", 0),
67                 ModelStandardTransform.DIRECTION_MIN2MAX,
68                 ModelStandardTransform.POLARITY_UNIPOLAR,
69                 ModelStandardTransform.TRANSFORM_LINEAR),
70             1, new ModelDestination(new ModelIdentifier("mixer", "active", 0)));
71 
72         defaultconnections[o++] = new ModelConnectionBlock(
73             new ModelSource(
74                 new ModelIdentifier("eg", 0),
75                 ModelStandardTransform.DIRECTION_MAX2MIN,
76                 ModelStandardTransform.POLARITY_UNIPOLAR,
77                 ModelStandardTransform.TRANSFORM_LINEAR),
78             -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
79 
80         defaultconnections[o++] = new ModelConnectionBlock(
81             new ModelSource(
82                 new ModelIdentifier("noteon", "velocity"),
83                 ModelStandardTransform.DIRECTION_MAX2MIN,
84                 ModelStandardTransform.POLARITY_UNIPOLAR,
85                 ModelStandardTransform.TRANSFORM_CONCAVE),
86             -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
87 
88         defaultconnections[o++] = new ModelConnectionBlock(
89             new ModelSource(
90                 new ModelIdentifier("midi", "pitch"),
91                 ModelStandardTransform.DIRECTION_MIN2MAX,
92                 ModelStandardTransform.POLARITY_BIPOLAR,
93                 ModelStandardTransform.TRANSFORM_LINEAR),
94             new ModelSource(new ModelIdentifier("midi_rpn", "0"),
95                 new ModelTransform() {
96                     @Override
97                     public double transform(double value) {
98                         int v = (int) (value * 16384.0);
99                         int msb = v >> 7;
100                         int lsb = v & 127;
101                         return msb * 100 + lsb;
102                     }
103                 }),
104             new ModelDestination(new ModelIdentifier("osc", "pitch")));
105 
106         defaultconnections[o++] = new ModelConnectionBlock(
107             new ModelSource(
108                 new ModelIdentifier("noteon", "keynumber"),
109                 ModelStandardTransform.DIRECTION_MIN2MAX,
110                 ModelStandardTransform.POLARITY_UNIPOLAR,
111                 ModelStandardTransform.TRANSFORM_LINEAR),
112             12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
113 
114         defaultconnections[o++] = new ModelConnectionBlock(
115             new ModelSource(
116                 new ModelIdentifier("midi_cc", "7"),
117                 ModelStandardTransform.DIRECTION_MAX2MIN,
118                 ModelStandardTransform.POLARITY_UNIPOLAR,
119                 ModelStandardTransform.TRANSFORM_CONCAVE),
120             -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
121 
122         defaultconnections[o++] = new ModelConnectionBlock(
123             new ModelSource(
124                 new ModelIdentifier("midi_cc", "8"),
125                 ModelStandardTransform.DIRECTION_MIN2MAX,
126                 ModelStandardTransform.POLARITY_UNIPOLAR,
127                 ModelStandardTransform.TRANSFORM_LINEAR),
128             1000, new ModelDestination(new ModelIdentifier("mixer", "balance")));
129 
130         defaultconnections[o++] = new ModelConnectionBlock(
131             new ModelSource(
132                 new ModelIdentifier("midi_cc", "10"),
133                 ModelStandardTransform.DIRECTION_MIN2MAX,
134                 ModelStandardTransform.POLARITY_UNIPOLAR,
135                 ModelStandardTransform.TRANSFORM_LINEAR),
136             1000, new ModelDestination(new ModelIdentifier("mixer", "pan")));
137 
138         defaultconnections[o++] = new ModelConnectionBlock(
139             new ModelSource(
140                 new ModelIdentifier("midi_cc", "11"),
141                 ModelStandardTransform.DIRECTION_MAX2MIN,
142                 ModelStandardTransform.POLARITY_UNIPOLAR,
143                 ModelStandardTransform.TRANSFORM_CONCAVE),
144             -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
145 
146         defaultconnections[o++] = new ModelConnectionBlock(
147             new ModelSource(
148                 new ModelIdentifier("midi_cc", "91"),
149                 ModelStandardTransform.DIRECTION_MIN2MAX,
150                 ModelStandardTransform.POLARITY_UNIPOLAR,
151                 ModelStandardTransform.TRANSFORM_LINEAR),
152             1000, new ModelDestination(new ModelIdentifier("mixer", "reverb")));
153 
154         defaultconnections[o++] = new ModelConnectionBlock(
155             new ModelSource(
156                 new ModelIdentifier("midi_cc", "93"),
157                 ModelStandardTransform.DIRECTION_MIN2MAX,
158                 ModelStandardTransform.POLARITY_UNIPOLAR,
159                 ModelStandardTransform.TRANSFORM_LINEAR),
160             1000, new ModelDestination(new ModelIdentifier("mixer", "chorus")));
161 
162         defaultconnections[o++] = new ModelConnectionBlock(
163             new ModelSource(
164                 new ModelIdentifier("midi_cc", "71"),
165                 ModelStandardTransform.DIRECTION_MIN2MAX,
166                 ModelStandardTransform.POLARITY_BIPOLAR,
167                 ModelStandardTransform.TRANSFORM_LINEAR),
168             200, new ModelDestination(new ModelIdentifier("filter", "q")));
169         defaultconnections[o++] = new ModelConnectionBlock(
170             new ModelSource(
171                 new ModelIdentifier("midi_cc", "74"),
172                 ModelStandardTransform.DIRECTION_MIN2MAX,
173                 ModelStandardTransform.POLARITY_BIPOLAR,
174                 ModelStandardTransform.TRANSFORM_LINEAR),
175             9600, new ModelDestination(new ModelIdentifier("filter", "freq")));
176 
177         defaultconnections[o++] = new ModelConnectionBlock(
178             new ModelSource(
179                 new ModelIdentifier("midi_cc", "72"),
180                 ModelStandardTransform.DIRECTION_MIN2MAX,
181                 ModelStandardTransform.POLARITY_BIPOLAR,
182                 ModelStandardTransform.TRANSFORM_LINEAR),
183             6000, new ModelDestination(new ModelIdentifier("eg", "release2")));
184 
185         defaultconnections[o++] = new ModelConnectionBlock(
186             new ModelSource(
187                 new ModelIdentifier("midi_cc", "73"),
188                 ModelStandardTransform.DIRECTION_MIN2MAX,
189                 ModelStandardTransform.POLARITY_BIPOLAR,
190                 ModelStandardTransform.TRANSFORM_LINEAR),
191             2000, new ModelDestination(new ModelIdentifier("eg", "attack2")));
192 
193         defaultconnections[o++] = new ModelConnectionBlock(
194             new ModelSource(
195                 new ModelIdentifier("midi_cc", "75"),
196                 ModelStandardTransform.DIRECTION_MIN2MAX,
197                 ModelStandardTransform.POLARITY_BIPOLAR,
198                 ModelStandardTransform.TRANSFORM_LINEAR),
199             6000, new ModelDestination(new ModelIdentifier("eg", "decay2")));
200 
201         defaultconnections[o++] = new ModelConnectionBlock(
202             new ModelSource(
203                 new ModelIdentifier("midi_cc", "67"),
204                 ModelStandardTransform.DIRECTION_MIN2MAX,
205                 ModelStandardTransform.POLARITY_UNIPOLAR,
206                 ModelStandardTransform.TRANSFORM_SWITCH),
207             -50, new ModelDestination(ModelDestination.DESTINATION_GAIN));
208 
209         defaultconnections[o++] = new ModelConnectionBlock(
210             new ModelSource(
211                 new ModelIdentifier("midi_cc", "67"),
212                 ModelStandardTransform.DIRECTION_MIN2MAX,
213                 ModelStandardTransform.POLARITY_UNIPOLAR,
214                 ModelStandardTransform.TRANSFORM_SWITCH),
215             -2400, new ModelDestination(ModelDestination.DESTINATION_FILTER_FREQ));
216 
217         defaultconnections[o++] = new ModelConnectionBlock(
218             new ModelSource(
219                 new ModelIdentifier("midi_rpn", "1"),
220                 ModelStandardTransform.DIRECTION_MIN2MAX,
221                 ModelStandardTransform.POLARITY_BIPOLAR,
222                 ModelStandardTransform.TRANSFORM_LINEAR),
223             100, new ModelDestination(new ModelIdentifier("osc", "pitch")));
224 
225         defaultconnections[o++] = new ModelConnectionBlock(
226             new ModelSource(
227                 new ModelIdentifier("midi_rpn", "2"),
228                 ModelStandardTransform.DIRECTION_MIN2MAX,
229                 ModelStandardTransform.POLARITY_BIPOLAR,
230                 ModelStandardTransform.TRANSFORM_LINEAR),
231             12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
232 
233         defaultconnections[o++] = new ModelConnectionBlock(
234             new ModelSource(
235                 new ModelIdentifier("master", "fine_tuning"),
236                 ModelStandardTransform.DIRECTION_MIN2MAX,
237                 ModelStandardTransform.POLARITY_BIPOLAR,
238                 ModelStandardTransform.TRANSFORM_LINEAR),
239             100, new ModelDestination(new ModelIdentifier("osc", "pitch")));
240 
241         defaultconnections[o++] = new ModelConnectionBlock(
242             new ModelSource(
243                 new ModelIdentifier("master", "coarse_tuning"),
244                 ModelStandardTransform.DIRECTION_MIN2MAX,
245                 ModelStandardTransform.POLARITY_BIPOLAR,
246                 ModelStandardTransform.TRANSFORM_LINEAR),
247             12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
248 
249         defaultconnections[o++] = new ModelConnectionBlock(13500,
250                 new ModelDestination(new ModelIdentifier("filter", "freq", 0)));
251 
252         defaultconnections[o++] = new ModelConnectionBlock(
253                 Float.NEGATIVE_INFINITY, new ModelDestination(
254                 new ModelIdentifier("eg", "delay", 0)));
255         defaultconnections[o++] = new ModelConnectionBlock(
256                 Float.NEGATIVE_INFINITY, new ModelDestination(
257                 new ModelIdentifier("eg", "attack", 0)));
258         defaultconnections[o++] = new ModelConnectionBlock(
259                 Float.NEGATIVE_INFINITY, new ModelDestination(
260                 new ModelIdentifier("eg", "hold", 0)));
261         defaultconnections[o++] = new ModelConnectionBlock(
262                 Float.NEGATIVE_INFINITY, new ModelDestination(
263                 new ModelIdentifier("eg", "decay", 0)));
264         defaultconnections[o++] = new ModelConnectionBlock(1000,
265                 new ModelDestination(new ModelIdentifier("eg", "sustain", 0)));
266         defaultconnections[o++] = new ModelConnectionBlock(
267                 Float.NEGATIVE_INFINITY, new ModelDestination(
268                 new ModelIdentifier("eg", "release", 0)));
269         defaultconnections[o++] = new ModelConnectionBlock(1200.0
270                 * Math.log(0.015) / Math.log(2), new ModelDestination(
271                 new ModelIdentifier("eg", "shutdown", 0))); // 15 msec default
272 
273         defaultconnections[o++] = new ModelConnectionBlock(
274                 Float.NEGATIVE_INFINITY, new ModelDestination(
275                 new ModelIdentifier("eg", "delay", 1)));
276         defaultconnections[o++] = new ModelConnectionBlock(
277                 Float.NEGATIVE_INFINITY, new ModelDestination(
278                 new ModelIdentifier("eg", "attack", 1)));
279         defaultconnections[o++] = new ModelConnectionBlock(
280                 Float.NEGATIVE_INFINITY, new ModelDestination(
281                 new ModelIdentifier("eg", "hold", 1)));
282         defaultconnections[o++] = new ModelConnectionBlock(
283                 Float.NEGATIVE_INFINITY, new ModelDestination(
284                 new ModelIdentifier("eg", "decay", 1)));
285         defaultconnections[o++] = new ModelConnectionBlock(1000,
286                 new ModelDestination(new ModelIdentifier("eg", "sustain", 1)));
287         defaultconnections[o++] = new ModelConnectionBlock(
288                 Float.NEGATIVE_INFINITY, new ModelDestination(
289                 new ModelIdentifier("eg", "release", 1)));
290 
291         defaultconnections[o++] = new ModelConnectionBlock(-8.51318,
292                 new ModelDestination(new ModelIdentifier("lfo", "freq", 0)));
293         defaultconnections[o++] = new ModelConnectionBlock(
294                 Float.NEGATIVE_INFINITY, new ModelDestination(
295                 new ModelIdentifier("lfo", "delay", 0)));
296         defaultconnections[o++] = new ModelConnectionBlock(-8.51318,
297                 new ModelDestination(new ModelIdentifier("lfo", "freq", 1)));
298         defaultconnections[o++] = new ModelConnectionBlock(
299                 Float.NEGATIVE_INFINITY, new ModelDestination(
300                 new ModelIdentifier("lfo", "delay", 1)));
301 
302     }
303     public int keyFrom = 0;
304     public int keyTo = 127;
305     public int velFrom = 0;
306     public int velTo = 127;
307     public int exclusiveClass = 0;
308     public boolean selfNonExclusive = false;
309     public boolean forcedVelocity = false;
310     public boolean forcedKeynumber = false;
311     public ModelPerformer performer;
312     public ModelConnectionBlock[] connections;
313     public ModelOscillator[] oscillators;
314     public Map<Integer, int[]> midi_rpn_connections = new HashMap<>();
315     public Map<Integer, int[]> midi_nrpn_connections = new HashMap<>();
316     public int[][] midi_ctrl_connections;
317     public int[][] midi_connections;
318     public int[] ctrl_connections;
319     private final List<Integer> ctrl_connections_list = new ArrayList<>();
320 
321     private static class KeySortComparator implements Comparator<ModelSource> {
322 
323         @Override
compare(ModelSource o1, ModelSource o2)324         public int compare(ModelSource o1, ModelSource o2) {
325             return o1.getIdentifier().toString().compareTo(
326                     o2.getIdentifier().toString());
327         }
328     }
329     private static final KeySortComparator keySortComparator = new KeySortComparator();
330 
extractKeys(ModelConnectionBlock conn)331     private String extractKeys(ModelConnectionBlock conn) {
332         StringBuilder sb = new StringBuilder();
333         if (conn.getSources() != null) {
334             sb.append("[");
335             ModelSource[] srcs = conn.getSources();
336             ModelSource[] srcs2 = new ModelSource[srcs.length];
337             for (int i = 0; i < srcs.length; i++)
338                 srcs2[i] = srcs[i];
339             Arrays.sort(srcs2, keySortComparator);
340             for (int i = 0; i < srcs.length; i++) {
341                 sb.append(srcs[i].getIdentifier());
342                 sb.append(";");
343             }
344             sb.append("]");
345         }
346         sb.append(";");
347         if (conn.getDestination() != null) {
348             sb.append(conn.getDestination().getIdentifier());
349         }
350         sb.append(";");
351         return sb.toString();
352     }
353 
processSource(ModelSource src, int ix)354     private void processSource(ModelSource src, int ix) {
355         ModelIdentifier id = src.getIdentifier();
356         String o = id.getObject();
357         if (o.equals("midi_cc"))
358             processMidiControlSource(src, ix);
359         else if (o.equals("midi_rpn"))
360             processMidiRpnSource(src, ix);
361         else if (o.equals("midi_nrpn"))
362             processMidiNrpnSource(src, ix);
363         else if (o.equals("midi"))
364             processMidiSource(src, ix);
365         else if (o.equals("noteon"))
366             processNoteOnSource(src, ix);
367         else if (o.equals("osc"))
368             return;
369         else if (o.equals("mixer"))
370             return;
371         else
372             ctrl_connections_list.add(ix);
373     }
374 
processMidiControlSource(ModelSource src, int ix)375     private void processMidiControlSource(ModelSource src, int ix) {
376         String v = src.getIdentifier().getVariable();
377         if (v == null)
378             return;
379         int c = Integer.parseInt(v);
380         if (midi_ctrl_connections[c] == null)
381             midi_ctrl_connections[c] = new int[]{ix};
382         else {
383             int[] olda = midi_ctrl_connections[c];
384             int[] newa = new int[olda.length + 1];
385             for (int i = 0; i < olda.length; i++)
386                 newa[i] = olda[i];
387             newa[newa.length - 1] = ix;
388             midi_ctrl_connections[c] = newa;
389         }
390     }
391 
processNoteOnSource(ModelSource src, int ix)392     private void processNoteOnSource(ModelSource src, int ix) {
393         String v = src.getIdentifier().getVariable();
394         int c = -1;
395         if (v.equals("on"))
396             c = 3;
397         if (v.equals("keynumber"))
398             c = 4;
399         if (c == -1)
400             return;
401         if (midi_connections[c] == null)
402             midi_connections[c] = new int[]{ix};
403         else {
404             int[] olda = midi_connections[c];
405             int[] newa = new int[olda.length + 1];
406             for (int i = 0; i < olda.length; i++)
407                 newa[i] = olda[i];
408             newa[newa.length - 1] = ix;
409             midi_connections[c] = newa;
410         }
411     }
412 
processMidiSource(ModelSource src, int ix)413     private void processMidiSource(ModelSource src, int ix) {
414         String v = src.getIdentifier().getVariable();
415         int c = -1;
416         if (v.equals("pitch"))
417             c = 0;
418         if (v.equals("channel_pressure"))
419             c = 1;
420         if (v.equals("poly_pressure"))
421             c = 2;
422         if (c == -1)
423             return;
424         if (midi_connections[c] == null)
425             midi_connections[c] = new int[]{ix};
426         else {
427             int[] olda = midi_connections[c];
428             int[] newa = new int[olda.length + 1];
429             for (int i = 0; i < olda.length; i++)
430                 newa[i] = olda[i];
431             newa[newa.length - 1] = ix;
432             midi_connections[c] = newa;
433         }
434     }
435 
processMidiRpnSource(ModelSource src, int ix)436     private void processMidiRpnSource(ModelSource src, int ix) {
437         String v = src.getIdentifier().getVariable();
438         if (v == null)
439             return;
440         int c = Integer.parseInt(v);
441         if (midi_rpn_connections.get(c) == null)
442             midi_rpn_connections.put(c, new int[]{ix});
443         else {
444             int[] olda = midi_rpn_connections.get(c);
445             int[] newa = new int[olda.length + 1];
446             for (int i = 0; i < olda.length; i++)
447                 newa[i] = olda[i];
448             newa[newa.length - 1] = ix;
449             midi_rpn_connections.put(c, newa);
450         }
451     }
452 
processMidiNrpnSource(ModelSource src, int ix)453     private void processMidiNrpnSource(ModelSource src, int ix) {
454         String v = src.getIdentifier().getVariable();
455         if (v == null)
456             return;
457         int c = Integer.parseInt(v);
458         if (midi_nrpn_connections.get(c) == null)
459             midi_nrpn_connections.put(c, new int[]{ix});
460         else {
461             int[] olda = midi_nrpn_connections.get(c);
462             int[] newa = new int[olda.length + 1];
463             for (int i = 0; i < olda.length; i++)
464                 newa[i] = olda[i];
465             newa[newa.length - 1] = ix;
466             midi_nrpn_connections.put(c, newa);
467         }
468     }
469 
SoftPerformer(ModelPerformer performer)470     public SoftPerformer(ModelPerformer performer) {
471         this.performer = performer;
472 
473         keyFrom = performer.getKeyFrom();
474         keyTo = performer.getKeyTo();
475         velFrom = performer.getVelFrom();
476         velTo = performer.getVelTo();
477         exclusiveClass = performer.getExclusiveClass();
478         selfNonExclusive = performer.isSelfNonExclusive();
479 
480         Map<String, ModelConnectionBlock> connmap = new HashMap<>();
481 
482         List<ModelConnectionBlock> performer_connections = new ArrayList<>();
483         performer_connections.addAll(performer.getConnectionBlocks());
484 
485         if (performer.isDefaultConnectionsEnabled()) {
486 
487             // Add modulation depth range (RPN 5) to the modulation wheel (cc#1)
488 
489             boolean isModulationWheelConectionFound = false;
490             for (int j = 0; j < performer_connections.size(); j++) {
491                 ModelConnectionBlock connection = performer_connections.get(j);
492                 ModelSource[] sources = connection.getSources();
493                 ModelDestination dest = connection.getDestination();
494                 boolean isModulationWheelConection = false;
495                 if (dest != null && sources != null && sources.length > 1) {
496                     for (int i = 0; i < sources.length; i++) {
497                         // check if connection block has the source "modulation
498                         // wheel cc#1"
499                         if (sources[i].getIdentifier().getObject().equals(
500                                 "midi_cc")) {
501                             if (sources[i].getIdentifier().getVariable()
502                                     .equals("1")) {
503                                 isModulationWheelConection = true;
504                                 isModulationWheelConectionFound = true;
505                                 break;
506                             }
507                         }
508                     }
509                 }
510                 if (isModulationWheelConection) {
511 
512                     ModelConnectionBlock newconnection = new ModelConnectionBlock();
513                     newconnection.setSources(connection.getSources());
514                     newconnection.setDestination(connection.getDestination());
515                     newconnection.addSource(new ModelSource(
516                             new ModelIdentifier("midi_rpn", "5")));
517                     newconnection.setScale(connection.getScale() * 256.0);
518                     performer_connections.set(j, newconnection);
519                 }
520             }
521 
522             if (!isModulationWheelConectionFound) {
523                 ModelConnectionBlock conn = new ModelConnectionBlock(
524                         new ModelSource(ModelSource.SOURCE_LFO1,
525                         ModelStandardTransform.DIRECTION_MIN2MAX,
526                         ModelStandardTransform.POLARITY_BIPOLAR,
527                         ModelStandardTransform.TRANSFORM_LINEAR),
528                         new ModelSource(new ModelIdentifier("midi_cc", "1", 0),
529                         ModelStandardTransform.DIRECTION_MIN2MAX,
530                         ModelStandardTransform.POLARITY_UNIPOLAR,
531                         ModelStandardTransform.TRANSFORM_LINEAR),
532                         50,
533                         new ModelDestination(ModelDestination.DESTINATION_PITCH));
534                 conn.addSource(new ModelSource(new ModelIdentifier("midi_rpn",
535                         "5")));
536                 conn.setScale(conn.getScale() * 256.0);
537                 performer_connections.add(conn);
538 
539             }
540 
541             // Let Aftertouch to behave just like modulation wheel (cc#1)
542             boolean channel_pressure_set = false;
543             boolean poly_pressure = false;
544             ModelConnectionBlock mod_cc_1_connection = null;
545             int mod_cc_1_connection_src_ix = 0;
546 
547             for (ModelConnectionBlock connection : performer_connections) {
548                 ModelSource[] sources = connection.getSources();
549                 ModelDestination dest = connection.getDestination();
550                 // if(dest != null && sources != null)
551                 if (dest != null && sources != null) {
552                     for (int i = 0; i < sources.length; i++) {
553                         ModelIdentifier srcid = sources[i].getIdentifier();
554                         // check if connection block has the source "modulation
555                         // wheel cc#1"
556                         if (srcid.getObject().equals("midi_cc")) {
557                             if (srcid.getVariable().equals("1")) {
558                                 mod_cc_1_connection = connection;
559                                 mod_cc_1_connection_src_ix = i;
560                             }
561                         }
562                         // check if channel or poly pressure are already
563                         // connected
564                         if (srcid.getObject().equals("midi")) {
565                             if (srcid.getVariable().equals("channel_pressure"))
566                                 channel_pressure_set = true;
567                             if (srcid.getVariable().equals("poly_pressure"))
568                                 poly_pressure = true;
569                         }
570                     }
571                 }
572 
573             }
574 
575             if (mod_cc_1_connection != null) {
576                 if (!channel_pressure_set) {
577                     ModelConnectionBlock mc = new ModelConnectionBlock();
578                     mc.setDestination(mod_cc_1_connection.getDestination());
579                     mc.setScale(mod_cc_1_connection.getScale());
580                     ModelSource[] src_list = mod_cc_1_connection.getSources();
581                     ModelSource[] src_list_new = new ModelSource[src_list.length];
582                     for (int i = 0; i < src_list_new.length; i++)
583                         src_list_new[i] = src_list[i];
584                     src_list_new[mod_cc_1_connection_src_ix] = new ModelSource(
585                             new ModelIdentifier("midi", "channel_pressure"));
586                     mc.setSources(src_list_new);
587                     connmap.put(extractKeys(mc), mc);
588                 }
589                 if (!poly_pressure) {
590                     ModelConnectionBlock mc = new ModelConnectionBlock();
591                     mc.setDestination(mod_cc_1_connection.getDestination());
592                     mc.setScale(mod_cc_1_connection.getScale());
593                     ModelSource[] src_list = mod_cc_1_connection.getSources();
594                     ModelSource[] src_list_new = new ModelSource[src_list.length];
595                     for (int i = 0; i < src_list_new.length; i++)
596                         src_list_new[i] = src_list[i];
597                     src_list_new[mod_cc_1_connection_src_ix] = new ModelSource(
598                             new ModelIdentifier("midi", "poly_pressure"));
599                     mc.setSources(src_list_new);
600                     connmap.put(extractKeys(mc), mc);
601                 }
602             }
603 
604             // Enable Vibration Sound Controllers : 76, 77, 78
605             ModelConnectionBlock found_vib_connection = null;
606             for (ModelConnectionBlock connection : performer_connections) {
607                 ModelSource[] sources = connection.getSources();
608                 if (sources.length != 0
609                         && sources[0].getIdentifier().getObject().equals("lfo")) {
610                     if (connection.getDestination().getIdentifier().equals(
611                             ModelDestination.DESTINATION_PITCH)) {
612                         if (found_vib_connection == null)
613                             found_vib_connection = connection;
614                         else {
615                             if (found_vib_connection.getSources().length > sources.length)
616                                 found_vib_connection = connection;
617                             else if (found_vib_connection.getSources()[0]
618                                     .getIdentifier().getInstance() < 1) {
619                                 if (found_vib_connection.getSources()[0]
620                                         .getIdentifier().getInstance() >
621                                         sources[0].getIdentifier().getInstance()) {
622                                     found_vib_connection = connection;
623                                 }
624                             }
625                         }
626 
627                     }
628                 }
629             }
630 
631             int instance = 1;
632 
633             if (found_vib_connection != null) {
634                 instance = found_vib_connection.getSources()[0].getIdentifier()
635                         .getInstance();
636             }
637             ModelConnectionBlock connection;
638 
639             connection = new ModelConnectionBlock(
640                 new ModelSource(new ModelIdentifier("midi_cc", "78"),
641                     ModelStandardTransform.DIRECTION_MIN2MAX,
642                     ModelStandardTransform.POLARITY_BIPOLAR,
643                     ModelStandardTransform.TRANSFORM_LINEAR),
644                 2000, new ModelDestination(
645                     new ModelIdentifier("lfo", "delay2", instance)));
646             connmap.put(extractKeys(connection), connection);
647 
648             final double scale = found_vib_connection == null ? 0
649                     : found_vib_connection.getScale();
650             connection = new ModelConnectionBlock(
651                 new ModelSource(new ModelIdentifier("lfo", instance)),
652                 new ModelSource(new ModelIdentifier("midi_cc", "77"),
653                     new ModelTransform() {
654                         double s = scale;
655                         @Override
656                         public double transform(double value) {
657                             value = value * 2 - 1;
658                             value *= 600;
659                             if (s == 0) {
660                                 return value;
661                             } else if (s > 0) {
662                                 if (value < -s)
663                                     value = -s;
664                                 return value;
665                             } else {
666                                 if (value < s)
667                                     value = -s;
668                                 return -value;
669                             }
670                         }
671                     }), new ModelDestination(ModelDestination.DESTINATION_PITCH));
672             connmap.put(extractKeys(connection), connection);
673 
674             connection = new ModelConnectionBlock(
675                 new ModelSource(new ModelIdentifier("midi_cc", "76"),
676                     ModelStandardTransform.DIRECTION_MIN2MAX,
677                     ModelStandardTransform.POLARITY_BIPOLAR,
678                     ModelStandardTransform.TRANSFORM_LINEAR),
679                 2400, new ModelDestination(
680                     new ModelIdentifier("lfo", "freq", instance)));
681             connmap.put(extractKeys(connection), connection);
682 
683         }
684 
685         // Add default connection blocks
686         if (performer.isDefaultConnectionsEnabled())
687             for (ModelConnectionBlock connection : defaultconnections)
688                 connmap.put(extractKeys(connection), connection);
689         // Add connection blocks from modelperformer
690         for (ModelConnectionBlock connection : performer_connections)
691             connmap.put(extractKeys(connection), connection);
692         // seperate connection blocks : Init time, Midi Time, Midi/Control Time,
693         // Control Time
694         List<ModelConnectionBlock> connections = new ArrayList<>();
695 
696         midi_ctrl_connections = new int[128][];
697         for (int i = 0; i < midi_ctrl_connections.length; i++) {
698             midi_ctrl_connections[i] = null;
699         }
700         midi_connections = new int[5][];
701         for (int i = 0; i < midi_connections.length; i++) {
702             midi_connections[i] = null;
703         }
704 
705         int ix = 0;
706         boolean mustBeOnTop = false;
707 
708         for (ModelConnectionBlock connection : connmap.values()) {
709             if (connection.getDestination() != null) {
710                 ModelDestination dest = connection.getDestination();
711                 ModelIdentifier id = dest.getIdentifier();
712                 if (id.getObject().equals("noteon")) {
713                     mustBeOnTop = true;
714                     if (id.getVariable().equals("keynumber"))
715                         forcedKeynumber = true;
716                     if (id.getVariable().equals("velocity"))
717                         forcedVelocity = true;
718                 }
719             }
720             if (mustBeOnTop) {
721                 connections.add(0, connection);
722                 mustBeOnTop = false;
723             } else
724                 connections.add(connection);
725         }
726 
727         for (ModelConnectionBlock connection : connections) {
728             if (connection.getSources() != null) {
729                 ModelSource[] srcs = connection.getSources();
730                 for (int i = 0; i < srcs.length; i++) {
731                     processSource(srcs[i], ix);
732                 }
733             }
734             ix++;
735         }
736 
737         this.connections = new ModelConnectionBlock[connections.size()];
738         connections.toArray(this.connections);
739 
740         this.ctrl_connections = new int[ctrl_connections_list.size()];
741 
742         for (int i = 0; i < this.ctrl_connections.length; i++)
743             this.ctrl_connections[i] = ctrl_connections_list.get(i);
744 
745         oscillators = new ModelOscillator[performer.getOscillators().size()];
746         performer.getOscillators().toArray(oscillators);
747 
748         for (ModelConnectionBlock conn : connections) {
749             if (conn.getDestination() != null) {
750                 if (isUnnecessaryTransform(conn.getDestination().getTransform())) {
751                     conn.getDestination().setTransform(null);
752                 }
753             }
754             if (conn.getSources() != null) {
755                 for (ModelSource src : conn.getSources()) {
756                     if (isUnnecessaryTransform(src.getTransform())) {
757                         src.setTransform(null);
758                     }
759                 }
760             }
761         }
762 
763     }
764 
isUnnecessaryTransform(ModelTransform transform)765     private static boolean isUnnecessaryTransform(ModelTransform transform) {
766         if (transform == null)
767             return false;
768         if (!(transform instanceof ModelStandardTransform))
769             return false;
770         ModelStandardTransform stransform = (ModelStandardTransform)transform;
771         if (stransform.getDirection() != ModelStandardTransform.DIRECTION_MIN2MAX)
772             return false;
773         if (stransform.getPolarity() != ModelStandardTransform.POLARITY_UNIPOLAR)
774             return false;
775         if (stransform.getTransform() != ModelStandardTransform.TRANSFORM_LINEAR)
776             return false;
777         return false;
778     }
779 }
780