1 /*
2  * Copyright (c) 2007, 2014, 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.io.File;
29 import java.io.FileInputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.net.URL;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Stack;
40 
41 import javax.sound.midi.Instrument;
42 import javax.sound.midi.Patch;
43 import javax.sound.midi.Soundbank;
44 import javax.sound.midi.SoundbankResource;
45 import javax.sound.sampled.AudioFormat;
46 import javax.sound.sampled.AudioFormat.Encoding;
47 import javax.sound.sampled.AudioInputStream;
48 import javax.sound.sampled.AudioSystem;
49 
50 /**
51  * A DLS Level 1 and Level 2 soundbank reader (from files/url/streams).
52  *
53  * @author Karl Helgason
54  */
55 public final class DLSSoundbank implements Soundbank {
56 
57     private static class DLSID {
58         long i1;
59         int s1;
60         int s2;
61         int x1;
62         int x2;
63         int x3;
64         int x4;
65         int x5;
66         int x6;
67         int x7;
68         int x8;
69 
DLSID()70         private DLSID() {
71         }
72 
DLSID(long i1, int s1, int s2, int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8)73         DLSID(long i1, int s1, int s2, int x1, int x2, int x3, int x4,
74                 int x5, int x6, int x7, int x8) {
75             this.i1 = i1;
76             this.s1 = s1;
77             this.s2 = s2;
78             this.x1 = x1;
79             this.x2 = x2;
80             this.x3 = x3;
81             this.x4 = x4;
82             this.x5 = x5;
83             this.x6 = x6;
84             this.x7 = x7;
85             this.x8 = x8;
86         }
87 
read(RIFFReader riff)88         public static DLSID read(RIFFReader riff) throws IOException {
89             DLSID d = new DLSID();
90             d.i1 = riff.readUnsignedInt();
91             d.s1 = riff.readUnsignedShort();
92             d.s2 = riff.readUnsignedShort();
93             d.x1 = riff.readUnsignedByte();
94             d.x2 = riff.readUnsignedByte();
95             d.x3 = riff.readUnsignedByte();
96             d.x4 = riff.readUnsignedByte();
97             d.x5 = riff.readUnsignedByte();
98             d.x6 = riff.readUnsignedByte();
99             d.x7 = riff.readUnsignedByte();
100             d.x8 = riff.readUnsignedByte();
101             return d;
102         }
103 
104         @Override
hashCode()105         public int hashCode() {
106             return (int)i1;
107         }
108 
109         @Override
equals(Object obj)110         public boolean equals(Object obj) {
111             if (!(obj instanceof DLSID)) {
112                 return false;
113             }
114             DLSID t = (DLSID) obj;
115             return i1 == t.i1 && s1 == t.s1 && s2 == t.s2
116                 && x1 == t.x1 && x2 == t.x2 && x3 == t.x3 && x4 == t.x4
117                 && x5 == t.x5 && x6 == t.x6 && x7 == t.x7 && x8 == t.x8;
118         }
119     }
120 
121     /** X = X & Y */
122     private static final int DLS_CDL_AND = 0x0001;
123     /** X = X | Y */
124     private static final int DLS_CDL_OR = 0x0002;
125     /** X = X ^ Y */
126     private static final int DLS_CDL_XOR = 0x0003;
127     /** X = X + Y */
128     private static final int DLS_CDL_ADD = 0x0004;
129     /** X = X - Y */
130     private static final int DLS_CDL_SUBTRACT = 0x0005;
131     /** X = X * Y */
132     private static final int DLS_CDL_MULTIPLY = 0x0006;
133     /** X = X / Y */
134     private static final int DLS_CDL_DIVIDE = 0x0007;
135     /** X = X && Y */
136     private static final int DLS_CDL_LOGICAL_AND = 0x0008;
137     /** X = X || Y */
138     private static final int DLS_CDL_LOGICAL_OR = 0x0009;
139     /** X = (X < Y) */
140     private static final int DLS_CDL_LT = 0x000A;
141     /** X = (X <= Y) */
142     private static final int DLS_CDL_LE = 0x000B;
143     /** X = (X > Y) */
144     private static final int DLS_CDL_GT = 0x000C;
145     /** X = (X >= Y) */
146     private static final int DLS_CDL_GE = 0x000D;
147     /** X = (X == Y) */
148     private static final int DLS_CDL_EQ = 0x000E;
149     /** X = !X */
150     private static final int DLS_CDL_NOT = 0x000F;
151     /** 32-bit constant */
152     private static final int DLS_CDL_CONST = 0x0010;
153     /** 32-bit value returned from query */
154     private static final int DLS_CDL_QUERY = 0x0011;
155     /** 32-bit value returned from query */
156     private static final int DLS_CDL_QUERYSUPPORTED = 0x0012;
157 
158     private static final DLSID DLSID_GMInHardware = new DLSID(0x178f2f24,
159             0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
160     private static final DLSID DLSID_GSInHardware = new DLSID(0x178f2f25,
161             0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
162     private static final DLSID DLSID_XGInHardware = new DLSID(0x178f2f26,
163             0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
164     private static final DLSID DLSID_SupportsDLS1 = new DLSID(0x178f2f27,
165             0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
166     private static final DLSID DLSID_SupportsDLS2 = new DLSID(0xf14599e5,
167             0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);
168     private static final DLSID DLSID_SampleMemorySize = new DLSID(0x178f2f28,
169             0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
170     private static final DLSID DLSID_ManufacturersID = new DLSID(0xb03e1181,
171             0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
172     private static final DLSID DLSID_ProductID = new DLSID(0xb03e1182,
173             0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
174     private static final DLSID DLSID_SamplePlaybackRate = new DLSID(0x2a91f713,
175             0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
176 
177     private long major = -1;
178     private long minor = -1;
179 
180     private final DLSInfo info = new DLSInfo();
181 
182     private final List<DLSInstrument> instruments = new ArrayList<>();
183     private final List<DLSSample> samples = new ArrayList<>();
184 
185     private boolean largeFormat = false;
186     private File sampleFile;
187 
DLSSoundbank()188     public DLSSoundbank() {
189     }
190 
DLSSoundbank(URL url)191     public DLSSoundbank(URL url) throws IOException {
192         InputStream is = url.openStream();
193         try {
194             readSoundbank(is);
195         } finally {
196             is.close();
197         }
198     }
199 
DLSSoundbank(File file)200     public DLSSoundbank(File file) throws IOException {
201         largeFormat = true;
202         sampleFile = file;
203         InputStream is = new FileInputStream(file);
204         try {
205             readSoundbank(is);
206         } finally {
207             is.close();
208         }
209     }
210 
DLSSoundbank(InputStream inputstream)211     public DLSSoundbank(InputStream inputstream) throws IOException {
212         readSoundbank(inputstream);
213     }
214 
readSoundbank(InputStream inputstream)215     private void readSoundbank(InputStream inputstream) throws IOException {
216         RIFFReader riff = new RIFFReader(inputstream);
217         if (!riff.getFormat().equals("RIFF")) {
218             throw new RIFFInvalidFormatException(
219                     "Input stream is not a valid RIFF stream!");
220         }
221         if (!riff.getType().equals("DLS ")) {
222             throw new RIFFInvalidFormatException(
223                     "Input stream is not a valid DLS soundbank!");
224         }
225         while (riff.hasNextChunk()) {
226             RIFFReader chunk = riff.nextChunk();
227             if (chunk.getFormat().equals("LIST")) {
228                 if (chunk.getType().equals("INFO"))
229                     readInfoChunk(chunk);
230                 if (chunk.getType().equals("lins"))
231                     readLinsChunk(chunk);
232                 if (chunk.getType().equals("wvpl"))
233                     readWvplChunk(chunk);
234             } else {
235                 if (chunk.getFormat().equals("cdl ")) {
236                     if (!readCdlChunk(chunk)) {
237                         throw new RIFFInvalidFormatException(
238                                 "DLS file isn't supported!");
239                     }
240                 }
241                 if (chunk.getFormat().equals("colh")) {
242                     // skipped because we will load the entire bank into memory
243                     // long instrumentcount = chunk.readUnsignedInt();
244                     // System.out.println("instrumentcount = "+ instrumentcount);
245                 }
246                 if (chunk.getFormat().equals("ptbl")) {
247                     // Pool Table Chunk
248                     // skipped because we will load the entire bank into memory
249                 }
250                 if (chunk.getFormat().equals("vers")) {
251                     major = chunk.readUnsignedInt();
252                     minor = chunk.readUnsignedInt();
253                 }
254             }
255         }
256 
257         for (Map.Entry<DLSRegion, Long> entry : temp_rgnassign.entrySet()) {
258             entry.getKey().sample = samples.get((int)entry.getValue().longValue());
259         }
260 
261         temp_rgnassign = null;
262     }
263 
cdlIsQuerySupported(DLSID uuid)264     private boolean cdlIsQuerySupported(DLSID uuid) {
265         return uuid.equals(DLSID_GMInHardware)
266             || uuid.equals(DLSID_GSInHardware)
267             || uuid.equals(DLSID_XGInHardware)
268             || uuid.equals(DLSID_SupportsDLS1)
269             || uuid.equals(DLSID_SupportsDLS2)
270             || uuid.equals(DLSID_SampleMemorySize)
271             || uuid.equals(DLSID_ManufacturersID)
272             || uuid.equals(DLSID_ProductID)
273             || uuid.equals(DLSID_SamplePlaybackRate);
274     }
275 
cdlQuery(DLSID uuid)276     private long cdlQuery(DLSID uuid) {
277         if (uuid.equals(DLSID_GMInHardware))
278             return 1;
279         if (uuid.equals(DLSID_GSInHardware))
280             return 0;
281         if (uuid.equals(DLSID_XGInHardware))
282             return 0;
283         if (uuid.equals(DLSID_SupportsDLS1))
284             return 1;
285         if (uuid.equals(DLSID_SupportsDLS2))
286             return 1;
287         if (uuid.equals(DLSID_SampleMemorySize))
288             return Runtime.getRuntime().totalMemory();
289         if (uuid.equals(DLSID_ManufacturersID))
290             return 0;
291         if (uuid.equals(DLSID_ProductID))
292             return 0;
293         if (uuid.equals(DLSID_SamplePlaybackRate))
294             return 44100;
295         return 0;
296     }
297 
298 
299     // Reading cdl-ck Chunk
300     // "cdl " chunk can only appear inside : DLS,lart,lar2,rgn,rgn2
readCdlChunk(RIFFReader riff)301     private boolean readCdlChunk(RIFFReader riff) throws IOException {
302 
303         DLSID uuid;
304         long x;
305         long y;
306         Stack<Long> stack = new Stack<>();
307 
308         while (riff.available() != 0) {
309             int opcode = riff.readUnsignedShort();
310             switch (opcode) {
311             case DLS_CDL_AND:
312                 x = stack.pop();
313                 y = stack.pop();
314                 stack.push(Long.valueOf(((x != 0) && (y != 0)) ? 1 : 0));
315                 break;
316             case DLS_CDL_OR:
317                 x = stack.pop();
318                 y = stack.pop();
319                 stack.push(Long.valueOf(((x != 0) || (y != 0)) ? 1 : 0));
320                 break;
321             case DLS_CDL_XOR:
322                 x = stack.pop();
323                 y = stack.pop();
324                 stack.push(Long.valueOf(((x != 0) ^ (y != 0)) ? 1 : 0));
325                 break;
326             case DLS_CDL_ADD:
327                 x = stack.pop();
328                 y = stack.pop();
329                 stack.push(Long.valueOf(x + y));
330                 break;
331             case DLS_CDL_SUBTRACT:
332                 x = stack.pop();
333                 y = stack.pop();
334                 stack.push(Long.valueOf(x - y));
335                 break;
336             case DLS_CDL_MULTIPLY:
337                 x = stack.pop();
338                 y = stack.pop();
339                 stack.push(Long.valueOf(x * y));
340                 break;
341             case DLS_CDL_DIVIDE:
342                 x = stack.pop();
343                 y = stack.pop();
344                 stack.push(Long.valueOf(x / y));
345                 break;
346             case DLS_CDL_LOGICAL_AND:
347                 x = stack.pop();
348                 y = stack.pop();
349                 stack.push(Long.valueOf(((x != 0) && (y != 0)) ? 1 : 0));
350                 break;
351             case DLS_CDL_LOGICAL_OR:
352                 x = stack.pop();
353                 y = stack.pop();
354                 stack.push(Long.valueOf(((x != 0) || (y != 0)) ? 1 : 0));
355                 break;
356             case DLS_CDL_LT:
357                 x = stack.pop();
358                 y = stack.pop();
359                 stack.push(Long.valueOf((x < y) ? 1 : 0));
360                 break;
361             case DLS_CDL_LE:
362                 x = stack.pop();
363                 y = stack.pop();
364                 stack.push(Long.valueOf((x <= y) ? 1 : 0));
365                 break;
366             case DLS_CDL_GT:
367                 x = stack.pop();
368                 y = stack.pop();
369                 stack.push(Long.valueOf((x > y) ? 1 : 0));
370                 break;
371             case DLS_CDL_GE:
372                 x = stack.pop();
373                 y = stack.pop();
374                 stack.push(Long.valueOf((x >= y) ? 1 : 0));
375                 break;
376             case DLS_CDL_EQ:
377                 x = stack.pop();
378                 y = stack.pop();
379                 stack.push(Long.valueOf((x == y) ? 1 : 0));
380                 break;
381             case DLS_CDL_NOT:
382                 x = stack.pop();
383                 y = stack.pop();
384                 stack.push(Long.valueOf((x == 0) ? 1 : 0));
385                 break;
386             case DLS_CDL_CONST:
387                 stack.push(Long.valueOf(riff.readUnsignedInt()));
388                 break;
389             case DLS_CDL_QUERY:
390                 uuid = DLSID.read(riff);
391                 stack.push(cdlQuery(uuid));
392                 break;
393             case DLS_CDL_QUERYSUPPORTED:
394                 uuid = DLSID.read(riff);
395                 stack.push(Long.valueOf(cdlIsQuerySupported(uuid) ? 1 : 0));
396                 break;
397             default:
398                 break;
399             }
400         }
401         if (stack.isEmpty())
402             return false;
403 
404         return stack.pop() == 1;
405     }
406 
readInfoChunk(RIFFReader riff)407     private void readInfoChunk(RIFFReader riff) throws IOException {
408         info.name = null;
409         while (riff.hasNextChunk()) {
410             RIFFReader chunk = riff.nextChunk();
411             String format = chunk.getFormat();
412             if (format.equals("INAM"))
413                 info.name = chunk.readString(chunk.available());
414             else if (format.equals("ICRD"))
415                 info.creationDate = chunk.readString(chunk.available());
416             else if (format.equals("IENG"))
417                 info.engineers = chunk.readString(chunk.available());
418             else if (format.equals("IPRD"))
419                 info.product = chunk.readString(chunk.available());
420             else if (format.equals("ICOP"))
421                 info.copyright = chunk.readString(chunk.available());
422             else if (format.equals("ICMT"))
423                 info.comments = chunk.readString(chunk.available());
424             else if (format.equals("ISFT"))
425                 info.tools = chunk.readString(chunk.available());
426             else if (format.equals("IARL"))
427                 info.archival_location = chunk.readString(chunk.available());
428             else if (format.equals("IART"))
429                 info.artist = chunk.readString(chunk.available());
430             else if (format.equals("ICMS"))
431                 info.commissioned = chunk.readString(chunk.available());
432             else if (format.equals("IGNR"))
433                 info.genre = chunk.readString(chunk.available());
434             else if (format.equals("IKEY"))
435                 info.keywords = chunk.readString(chunk.available());
436             else if (format.equals("IMED"))
437                 info.medium = chunk.readString(chunk.available());
438             else if (format.equals("ISBJ"))
439                 info.subject = chunk.readString(chunk.available());
440             else if (format.equals("ISRC"))
441                 info.source = chunk.readString(chunk.available());
442             else if (format.equals("ISRF"))
443                 info.source_form = chunk.readString(chunk.available());
444             else if (format.equals("ITCH"))
445                 info.technician = chunk.readString(chunk.available());
446         }
447     }
448 
readLinsChunk(RIFFReader riff)449     private void readLinsChunk(RIFFReader riff) throws IOException {
450         while (riff.hasNextChunk()) {
451             RIFFReader chunk = riff.nextChunk();
452             if (chunk.getFormat().equals("LIST")) {
453                 if (chunk.getType().equals("ins "))
454                     readInsChunk(chunk);
455             }
456         }
457     }
458 
readInsChunk(RIFFReader riff)459     private void readInsChunk(RIFFReader riff) throws IOException {
460         DLSInstrument instrument = new DLSInstrument(this);
461 
462         while (riff.hasNextChunk()) {
463             RIFFReader chunk = riff.nextChunk();
464             String format = chunk.getFormat();
465             if (format.equals("LIST")) {
466                 if (chunk.getType().equals("INFO")) {
467                     readInsInfoChunk(instrument, chunk);
468                 }
469                 if (chunk.getType().equals("lrgn")) {
470                     while (chunk.hasNextChunk()) {
471                         RIFFReader subchunk = chunk.nextChunk();
472                         if (subchunk.getFormat().equals("LIST")) {
473                             if (subchunk.getType().equals("rgn ")) {
474                                 DLSRegion split = new DLSRegion();
475                                 if (readRgnChunk(split, subchunk))
476                                     instrument.getRegions().add(split);
477                             }
478                             if (subchunk.getType().equals("rgn2")) {
479                                 // support for DLS level 2 regions
480                                 DLSRegion split = new DLSRegion();
481                                 if (readRgnChunk(split, subchunk))
482                                     instrument.getRegions().add(split);
483                             }
484                         }
485                     }
486                 }
487                 if (chunk.getType().equals("lart")) {
488                     List<DLSModulator> modlist = new ArrayList<>();
489                     while (chunk.hasNextChunk()) {
490                         RIFFReader subchunk = chunk.nextChunk();
491                         if (chunk.getFormat().equals("cdl ")) {
492                             if (!readCdlChunk(chunk)) {
493                                 modlist.clear();
494                                 break;
495                             }
496                         }
497                         if (subchunk.getFormat().equals("art1"))
498                             readArt1Chunk(modlist, subchunk);
499                     }
500                     instrument.getModulators().addAll(modlist);
501                 }
502                 if (chunk.getType().equals("lar2")) {
503                     // support for DLS level 2 ART
504                     List<DLSModulator> modlist = new ArrayList<>();
505                     while (chunk.hasNextChunk()) {
506                         RIFFReader subchunk = chunk.nextChunk();
507                         if (chunk.getFormat().equals("cdl ")) {
508                             if (!readCdlChunk(chunk)) {
509                                 modlist.clear();
510                                 break;
511                             }
512                         }
513                         if (subchunk.getFormat().equals("art2"))
514                             readArt2Chunk(modlist, subchunk);
515                     }
516                     instrument.getModulators().addAll(modlist);
517                 }
518             } else {
519                 if (format.equals("dlid")) {
520                     instrument.guid = new byte[16];
521                     chunk.readFully(instrument.guid);
522                 }
523                 if (format.equals("insh")) {
524                     chunk.readUnsignedInt(); // Read Region Count - ignored
525 
526                     int bank = chunk.read();             // LSB
527                     bank += (chunk.read() & 127) << 7;   // MSB
528                     chunk.read(); // Read Reserved byte
529                     int drumins = chunk.read();          // Drum Instrument
530 
531                     int id = chunk.read() & 127; // Read only first 7 bits
532                     chunk.read(); // Read Reserved byte
533                     chunk.read(); // Read Reserved byte
534                     chunk.read(); // Read Reserved byte
535 
536                     instrument.bank = bank;
537                     instrument.preset = id;
538                     instrument.druminstrument = (drumins & 128) > 0;
539                     //System.out.println("bank="+bank+" drumkit="+drumkit
540                     //        +" id="+id);
541                 }
542 
543             }
544         }
545         instruments.add(instrument);
546     }
547 
readArt1Chunk(List<DLSModulator> modulators, RIFFReader riff)548     private void readArt1Chunk(List<DLSModulator> modulators, RIFFReader riff)
549             throws IOException {
550         long size = riff.readUnsignedInt();
551         long count = riff.readUnsignedInt();
552 
553         if (size - 8 != 0)
554             riff.skip(size - 8);
555 
556         for (int i = 0; i < count; i++) {
557             DLSModulator modulator = new DLSModulator();
558             modulator.version = 1;
559             modulator.source = riff.readUnsignedShort();
560             modulator.control = riff.readUnsignedShort();
561             modulator.destination = riff.readUnsignedShort();
562             modulator.transform = riff.readUnsignedShort();
563             modulator.scale = riff.readInt();
564             modulators.add(modulator);
565         }
566     }
567 
readArt2Chunk(List<DLSModulator> modulators, RIFFReader riff)568     private void readArt2Chunk(List<DLSModulator> modulators, RIFFReader riff)
569             throws IOException {
570         long size = riff.readUnsignedInt();
571         long count = riff.readUnsignedInt();
572 
573         if (size - 8 != 0)
574             riff.skip(size - 8);
575 
576         for (int i = 0; i < count; i++) {
577             DLSModulator modulator = new DLSModulator();
578             modulator.version = 2;
579             modulator.source = riff.readUnsignedShort();
580             modulator.control = riff.readUnsignedShort();
581             modulator.destination = riff.readUnsignedShort();
582             modulator.transform = riff.readUnsignedShort();
583             modulator.scale = riff.readInt();
584             modulators.add(modulator);
585         }
586     }
587 
588     private Map<DLSRegion, Long> temp_rgnassign = new HashMap<>();
589 
readRgnChunk(DLSRegion split, RIFFReader riff)590     private boolean readRgnChunk(DLSRegion split, RIFFReader riff)
591             throws IOException {
592         while (riff.hasNextChunk()) {
593             RIFFReader chunk = riff.nextChunk();
594             String format = chunk.getFormat();
595             if (format.equals("LIST")) {
596                 if (chunk.getType().equals("lart")) {
597                     List<DLSModulator> modlist = new ArrayList<>();
598                     while (chunk.hasNextChunk()) {
599                         RIFFReader subchunk = chunk.nextChunk();
600                         if (chunk.getFormat().equals("cdl ")) {
601                             if (!readCdlChunk(chunk)) {
602                                 modlist.clear();
603                                 break;
604                             }
605                         }
606                         if (subchunk.getFormat().equals("art1"))
607                             readArt1Chunk(modlist, subchunk);
608                     }
609                     split.getModulators().addAll(modlist);
610                 }
611                 if (chunk.getType().equals("lar2")) {
612                     // support for DLS level 2 ART
613                     List<DLSModulator> modlist = new ArrayList<>();
614                     while (chunk.hasNextChunk()) {
615                         RIFFReader subchunk = chunk.nextChunk();
616                         if (chunk.getFormat().equals("cdl ")) {
617                             if (!readCdlChunk(chunk)) {
618                                 modlist.clear();
619                                 break;
620                             }
621                         }
622                         if (subchunk.getFormat().equals("art2"))
623                             readArt2Chunk(modlist, subchunk);
624                     }
625                     split.getModulators().addAll(modlist);
626                 }
627             } else {
628 
629                 if (format.equals("cdl ")) {
630                     if (!readCdlChunk(chunk))
631                         return false;
632                 }
633                 if (format.equals("rgnh")) {
634                     split.keyfrom = chunk.readUnsignedShort();
635                     split.keyto = chunk.readUnsignedShort();
636                     split.velfrom = chunk.readUnsignedShort();
637                     split.velto = chunk.readUnsignedShort();
638                     split.options = chunk.readUnsignedShort();
639                     split.exclusiveClass = chunk.readUnsignedShort();
640                 }
641                 if (format.equals("wlnk")) {
642                     split.fusoptions = chunk.readUnsignedShort();
643                     split.phasegroup = chunk.readUnsignedShort();
644                     split.channel = chunk.readUnsignedInt();
645                     long sampleid = chunk.readUnsignedInt();
646                     temp_rgnassign.put(split, sampleid);
647                 }
648                 if (format.equals("wsmp")) {
649                     split.sampleoptions = new DLSSampleOptions();
650                     readWsmpChunk(split.sampleoptions, chunk);
651                 }
652             }
653         }
654         return true;
655     }
656 
readWsmpChunk(DLSSampleOptions sampleOptions, RIFFReader riff)657     private void readWsmpChunk(DLSSampleOptions sampleOptions, RIFFReader riff)
658             throws IOException {
659         long size = riff.readUnsignedInt();
660         sampleOptions.unitynote = riff.readUnsignedShort();
661         sampleOptions.finetune = riff.readShort();
662         sampleOptions.attenuation = riff.readInt();
663         sampleOptions.options = riff.readUnsignedInt();
664         long loops = riff.readInt();
665 
666         if (size > 20)
667             riff.skip(size - 20);
668 
669         for (int i = 0; i < loops; i++) {
670             DLSSampleLoop loop = new DLSSampleLoop();
671             long size2 = riff.readUnsignedInt();
672             loop.type = riff.readUnsignedInt();
673             loop.start = riff.readUnsignedInt();
674             loop.length = riff.readUnsignedInt();
675             sampleOptions.loops.add(loop);
676             if (size2 > 16)
677                 riff.skip(size2 - 16);
678         }
679     }
680 
readInsInfoChunk(DLSInstrument dlsinstrument, RIFFReader riff)681     private void readInsInfoChunk(DLSInstrument dlsinstrument, RIFFReader riff)
682             throws IOException {
683         dlsinstrument.info.name = null;
684         while (riff.hasNextChunk()) {
685             RIFFReader chunk = riff.nextChunk();
686             String format = chunk.getFormat();
687             if (format.equals("INAM")) {
688                 dlsinstrument.info.name = chunk.readString(chunk.available());
689             } else if (format.equals("ICRD")) {
690                 dlsinstrument.info.creationDate =
691                         chunk.readString(chunk.available());
692             } else if (format.equals("IENG")) {
693                 dlsinstrument.info.engineers =
694                         chunk.readString(chunk.available());
695             } else if (format.equals("IPRD")) {
696                 dlsinstrument.info.product = chunk.readString(chunk.available());
697             } else if (format.equals("ICOP")) {
698                 dlsinstrument.info.copyright =
699                         chunk.readString(chunk.available());
700             } else if (format.equals("ICMT")) {
701                 dlsinstrument.info.comments =
702                         chunk.readString(chunk.available());
703             } else if (format.equals("ISFT")) {
704                 dlsinstrument.info.tools = chunk.readString(chunk.available());
705             } else if (format.equals("IARL")) {
706                 dlsinstrument.info.archival_location =
707                         chunk.readString(chunk.available());
708             } else if (format.equals("IART")) {
709                 dlsinstrument.info.artist = chunk.readString(chunk.available());
710             } else if (format.equals("ICMS")) {
711                 dlsinstrument.info.commissioned =
712                         chunk.readString(chunk.available());
713             } else if (format.equals("IGNR")) {
714                 dlsinstrument.info.genre = chunk.readString(chunk.available());
715             } else if (format.equals("IKEY")) {
716                 dlsinstrument.info.keywords =
717                         chunk.readString(chunk.available());
718             } else if (format.equals("IMED")) {
719                 dlsinstrument.info.medium = chunk.readString(chunk.available());
720             } else if (format.equals("ISBJ")) {
721                 dlsinstrument.info.subject = chunk.readString(chunk.available());
722             } else if (format.equals("ISRC")) {
723                 dlsinstrument.info.source = chunk.readString(chunk.available());
724             } else if (format.equals("ISRF")) {
725                 dlsinstrument.info.source_form =
726                         chunk.readString(chunk.available());
727             } else if (format.equals("ITCH")) {
728                 dlsinstrument.info.technician =
729                         chunk.readString(chunk.available());
730             }
731         }
732     }
733 
readWvplChunk(RIFFReader riff)734     private void readWvplChunk(RIFFReader riff) throws IOException {
735         while (riff.hasNextChunk()) {
736             RIFFReader chunk = riff.nextChunk();
737             if (chunk.getFormat().equals("LIST")) {
738                 if (chunk.getType().equals("wave"))
739                     readWaveChunk(chunk);
740             }
741         }
742     }
743 
readWaveChunk(RIFFReader riff)744     private void readWaveChunk(RIFFReader riff) throws IOException {
745         DLSSample sample = new DLSSample(this);
746 
747         while (riff.hasNextChunk()) {
748             RIFFReader chunk = riff.nextChunk();
749             String format = chunk.getFormat();
750             if (format.equals("LIST")) {
751                 if (chunk.getType().equals("INFO")) {
752                     readWaveInfoChunk(sample, chunk);
753                 }
754             } else {
755                 if (format.equals("dlid")) {
756                     sample.guid = new byte[16];
757                     chunk.readFully(sample.guid);
758                 }
759 
760                 if (format.equals("fmt ")) {
761                     int sampleformat = chunk.readUnsignedShort();
762                     if (sampleformat != 1 && sampleformat != 3) {
763                         throw new RIFFInvalidDataException(
764                                 "Only PCM samples are supported!");
765                     }
766                     int channels = chunk.readUnsignedShort();
767                     long samplerate = chunk.readUnsignedInt();
768                     // bytes per sec
769                     /* long framerate = */ chunk.readUnsignedInt();
770                     // block align, framesize
771                     int framesize = chunk.readUnsignedShort();
772                     int bits = chunk.readUnsignedShort();
773                     AudioFormat audioformat = null;
774                     if (sampleformat == 1) {
775                         if (bits == 8) {
776                             audioformat = new AudioFormat(
777                                     Encoding.PCM_UNSIGNED, samplerate, bits,
778                                     channels, framesize, samplerate, false);
779                         } else {
780                             audioformat = new AudioFormat(
781                                     Encoding.PCM_SIGNED, samplerate, bits,
782                                     channels, framesize, samplerate, false);
783                         }
784                     }
785                     if (sampleformat == 3) {
786                         audioformat = new AudioFormat(
787                                 Encoding.PCM_FLOAT, samplerate, bits,
788                                 channels, framesize, samplerate, false);
789                     }
790 
791                     sample.format = audioformat;
792                 }
793 
794                 if (format.equals("data")) {
795                     if (largeFormat) {
796                         sample.setData(new ModelByteBuffer(sampleFile,
797                                 chunk.getFilePointer(), chunk.available()));
798                     } else {
799                         byte[] buffer = new byte[chunk.available()];
800                         //  chunk.read(buffer);
801                         sample.setData(buffer);
802 
803                         int read = 0;
804                         int avail = chunk.available();
805                         while (read != avail) {
806                             if (avail - read > 65536) {
807                                 chunk.readFully(buffer, read, 65536);
808                                 read += 65536;
809                             } else {
810                                 chunk.readFully(buffer, read, avail - read);
811                                 read = avail;
812                             }
813                         }
814                     }
815                 }
816 
817                 if (format.equals("wsmp")) {
818                     sample.sampleoptions = new DLSSampleOptions();
819                     readWsmpChunk(sample.sampleoptions, chunk);
820                 }
821             }
822         }
823 
824         samples.add(sample);
825 
826     }
827 
readWaveInfoChunk(DLSSample dlssample, RIFFReader riff)828     private void readWaveInfoChunk(DLSSample dlssample, RIFFReader riff)
829             throws IOException {
830         dlssample.info.name = null;
831         while (riff.hasNextChunk()) {
832             RIFFReader chunk = riff.nextChunk();
833             String format = chunk.getFormat();
834             if (format.equals("INAM")) {
835                 dlssample.info.name = chunk.readString(chunk.available());
836             } else if (format.equals("ICRD")) {
837                 dlssample.info.creationDate =
838                         chunk.readString(chunk.available());
839             } else if (format.equals("IENG")) {
840                 dlssample.info.engineers = chunk.readString(chunk.available());
841             } else if (format.equals("IPRD")) {
842                 dlssample.info.product = chunk.readString(chunk.available());
843             } else if (format.equals("ICOP")) {
844                 dlssample.info.copyright = chunk.readString(chunk.available());
845             } else if (format.equals("ICMT")) {
846                 dlssample.info.comments = chunk.readString(chunk.available());
847             } else if (format.equals("ISFT")) {
848                 dlssample.info.tools = chunk.readString(chunk.available());
849             } else if (format.equals("IARL")) {
850                 dlssample.info.archival_location =
851                         chunk.readString(chunk.available());
852             } else if (format.equals("IART")) {
853                 dlssample.info.artist = chunk.readString(chunk.available());
854             } else if (format.equals("ICMS")) {
855                 dlssample.info.commissioned =
856                         chunk.readString(chunk.available());
857             } else if (format.equals("IGNR")) {
858                 dlssample.info.genre = chunk.readString(chunk.available());
859             } else if (format.equals("IKEY")) {
860                 dlssample.info.keywords = chunk.readString(chunk.available());
861             } else if (format.equals("IMED")) {
862                 dlssample.info.medium = chunk.readString(chunk.available());
863             } else if (format.equals("ISBJ")) {
864                 dlssample.info.subject = chunk.readString(chunk.available());
865             } else if (format.equals("ISRC")) {
866                 dlssample.info.source = chunk.readString(chunk.available());
867             } else if (format.equals("ISRF")) {
868                 dlssample.info.source_form = chunk.readString(chunk.available());
869             } else if (format.equals("ITCH")) {
870                 dlssample.info.technician = chunk.readString(chunk.available());
871             }
872         }
873     }
874 
save(String name)875     public void save(String name) throws IOException {
876         writeSoundbank(new RIFFWriter(name, "DLS "));
877     }
878 
save(File file)879     public void save(File file) throws IOException {
880         writeSoundbank(new RIFFWriter(file, "DLS "));
881     }
882 
save(OutputStream out)883     public void save(OutputStream out) throws IOException {
884         writeSoundbank(new RIFFWriter(out, "DLS "));
885     }
886 
writeSoundbank(RIFFWriter writer)887     private void writeSoundbank(RIFFWriter writer) throws IOException {
888         RIFFWriter colh_chunk = writer.writeChunk("colh");
889         colh_chunk.writeUnsignedInt(instruments.size());
890 
891         if (major != -1 && minor != -1) {
892             RIFFWriter vers_chunk = writer.writeChunk("vers");
893             vers_chunk.writeUnsignedInt(major);
894             vers_chunk.writeUnsignedInt(minor);
895         }
896 
897         writeInstruments(writer.writeList("lins"));
898 
899         RIFFWriter ptbl = writer.writeChunk("ptbl");
900         ptbl.writeUnsignedInt(8);
901         ptbl.writeUnsignedInt(samples.size());
902         long ptbl_offset = writer.getFilePointer();
903         for (int i = 0; i < samples.size(); i++)
904             ptbl.writeUnsignedInt(0);
905 
906         RIFFWriter wvpl = writer.writeList("wvpl");
907         long off = wvpl.getFilePointer();
908         List<Long> offsettable = new ArrayList<>();
909         for (DLSSample sample : samples) {
910             offsettable.add(Long.valueOf(wvpl.getFilePointer() - off));
911             writeSample(wvpl.writeList("wave"), sample);
912         }
913 
914         // small cheat, we are going to rewrite data back in wvpl
915         long bak = writer.getFilePointer();
916         writer.seek(ptbl_offset);
917         writer.setWriteOverride(true);
918         for (Long offset : offsettable)
919             writer.writeUnsignedInt(offset.longValue());
920         writer.setWriteOverride(false);
921         writer.seek(bak);
922 
923         writeInfo(writer.writeList("INFO"), info);
924 
925         writer.close();
926     }
927 
writeSample(RIFFWriter writer, DLSSample sample)928     private void writeSample(RIFFWriter writer, DLSSample sample)
929             throws IOException {
930 
931         AudioFormat audioformat = sample.getFormat();
932 
933         Encoding encoding = audioformat.getEncoding();
934         float sampleRate = audioformat.getSampleRate();
935         int sampleSizeInBits = audioformat.getSampleSizeInBits();
936         int channels = audioformat.getChannels();
937         int frameSize = audioformat.getFrameSize();
938         float frameRate = audioformat.getFrameRate();
939         boolean bigEndian = audioformat.isBigEndian();
940 
941         boolean convert_needed = false;
942 
943         if (audioformat.getSampleSizeInBits() == 8) {
944             if (!encoding.equals(Encoding.PCM_UNSIGNED)) {
945                 encoding = Encoding.PCM_UNSIGNED;
946                 convert_needed = true;
947             }
948         } else {
949             if (!encoding.equals(Encoding.PCM_SIGNED)) {
950                 encoding = Encoding.PCM_SIGNED;
951                 convert_needed = true;
952             }
953             if (bigEndian) {
954                 bigEndian = false;
955                 convert_needed = true;
956             }
957         }
958 
959         if (convert_needed) {
960             audioformat = new AudioFormat(encoding, sampleRate,
961                     sampleSizeInBits, channels, frameSize, frameRate, bigEndian);
962         }
963 
964         // fmt
965         RIFFWriter fmt_chunk = writer.writeChunk("fmt ");
966         int sampleformat = 0;
967         if (audioformat.getEncoding().equals(Encoding.PCM_UNSIGNED))
968             sampleformat = 1;
969         else if (audioformat.getEncoding().equals(Encoding.PCM_SIGNED))
970             sampleformat = 1;
971         else if (audioformat.getEncoding().equals(Encoding.PCM_FLOAT))
972             sampleformat = 3;
973 
974         fmt_chunk.writeUnsignedShort(sampleformat);
975         fmt_chunk.writeUnsignedShort(audioformat.getChannels());
976         fmt_chunk.writeUnsignedInt((long) audioformat.getSampleRate());
977         long srate = ((long)audioformat.getFrameRate())*audioformat.getFrameSize();
978         fmt_chunk.writeUnsignedInt(srate);
979         fmt_chunk.writeUnsignedShort(audioformat.getFrameSize());
980         fmt_chunk.writeUnsignedShort(audioformat.getSampleSizeInBits());
981         fmt_chunk.write(0);
982         fmt_chunk.write(0);
983 
984         writeSampleOptions(writer.writeChunk("wsmp"), sample.sampleoptions);
985 
986         if (convert_needed) {
987             RIFFWriter data_chunk = writer.writeChunk("data");
988             AudioInputStream stream = AudioSystem.getAudioInputStream(
989                     audioformat, (AudioInputStream)sample.getData());
990             byte[] buff = new byte[1024];
991             int ret;
992             while ((ret = stream.read(buff)) != -1) {
993                 data_chunk.write(buff, 0, ret);
994             }
995         } else {
996             RIFFWriter data_chunk = writer.writeChunk("data");
997             ModelByteBuffer databuff = sample.getDataBuffer();
998             databuff.writeTo(data_chunk);
999             /*
1000             data_chunk.write(databuff.array(),
1001             databuff.arrayOffset(),
1002             databuff.capacity());
1003              */
1004         }
1005 
1006         writeInfo(writer.writeList("INFO"), sample.info);
1007     }
1008 
writeInstruments(RIFFWriter writer)1009     private void writeInstruments(RIFFWriter writer) throws IOException {
1010         for (DLSInstrument instrument : instruments) {
1011             writeInstrument(writer.writeList("ins "), instrument);
1012         }
1013     }
1014 
writeInstrument(RIFFWriter writer, DLSInstrument instrument)1015     private void writeInstrument(RIFFWriter writer, DLSInstrument instrument)
1016             throws IOException {
1017 
1018         int art1_count = 0;
1019         int art2_count = 0;
1020         for (DLSModulator modulator : instrument.getModulators()) {
1021             if (modulator.version == 1)
1022                 art1_count++;
1023             if (modulator.version == 2)
1024                 art2_count++;
1025         }
1026         for (DLSRegion region : instrument.regions) {
1027             for (DLSModulator modulator : region.getModulators()) {
1028                 if (modulator.version == 1)
1029                     art1_count++;
1030                 if (modulator.version == 2)
1031                     art2_count++;
1032             }
1033         }
1034 
1035         int version = 1;
1036         if (art2_count > 0)
1037             version = 2;
1038 
1039         RIFFWriter insh_chunk = writer.writeChunk("insh");
1040         insh_chunk.writeUnsignedInt(instrument.getRegions().size());
1041         insh_chunk.writeUnsignedInt(instrument.bank +
1042                 (instrument.druminstrument ? 2147483648L : 0));
1043         insh_chunk.writeUnsignedInt(instrument.preset);
1044 
1045         RIFFWriter lrgn = writer.writeList("lrgn");
1046         for (DLSRegion region: instrument.regions)
1047             writeRegion(lrgn, region, version);
1048 
1049         writeArticulators(writer, instrument.getModulators());
1050 
1051         writeInfo(writer.writeList("INFO"), instrument.info);
1052 
1053     }
1054 
writeArticulators(RIFFWriter writer, List<DLSModulator> modulators)1055     private void writeArticulators(RIFFWriter writer,
1056             List<DLSModulator> modulators) throws IOException {
1057         int art1_count = 0;
1058         int art2_count = 0;
1059         for (DLSModulator modulator : modulators) {
1060             if (modulator.version == 1)
1061                 art1_count++;
1062             if (modulator.version == 2)
1063                 art2_count++;
1064         }
1065         if (art1_count > 0) {
1066             RIFFWriter lar1 = writer.writeList("lart");
1067             RIFFWriter art1 = lar1.writeChunk("art1");
1068             art1.writeUnsignedInt(8);
1069             art1.writeUnsignedInt(art1_count);
1070             for (DLSModulator modulator : modulators) {
1071                 if (modulator.version == 1) {
1072                     art1.writeUnsignedShort(modulator.source);
1073                     art1.writeUnsignedShort(modulator.control);
1074                     art1.writeUnsignedShort(modulator.destination);
1075                     art1.writeUnsignedShort(modulator.transform);
1076                     art1.writeInt(modulator.scale);
1077                 }
1078             }
1079         }
1080         if (art2_count > 0) {
1081             RIFFWriter lar2 = writer.writeList("lar2");
1082             RIFFWriter art2 = lar2.writeChunk("art2");
1083             art2.writeUnsignedInt(8);
1084             art2.writeUnsignedInt(art2_count);
1085             for (DLSModulator modulator : modulators) {
1086                 if (modulator.version == 2) {
1087                     art2.writeUnsignedShort(modulator.source);
1088                     art2.writeUnsignedShort(modulator.control);
1089                     art2.writeUnsignedShort(modulator.destination);
1090                     art2.writeUnsignedShort(modulator.transform);
1091                     art2.writeInt(modulator.scale);
1092                 }
1093             }
1094         }
1095     }
1096 
writeRegion(RIFFWriter writer, DLSRegion region, int version)1097     private void writeRegion(RIFFWriter writer, DLSRegion region, int version)
1098             throws IOException {
1099         RIFFWriter rgns = null;
1100         if (version == 1)
1101             rgns = writer.writeList("rgn ");
1102         if (version == 2)
1103             rgns = writer.writeList("rgn2");
1104         if (rgns == null)
1105             return;
1106 
1107         RIFFWriter rgnh = rgns.writeChunk("rgnh");
1108         rgnh.writeUnsignedShort(region.keyfrom);
1109         rgnh.writeUnsignedShort(region.keyto);
1110         rgnh.writeUnsignedShort(region.velfrom);
1111         rgnh.writeUnsignedShort(region.velto);
1112         rgnh.writeUnsignedShort(region.options);
1113         rgnh.writeUnsignedShort(region.exclusiveClass);
1114 
1115         if (region.sampleoptions != null)
1116             writeSampleOptions(rgns.writeChunk("wsmp"), region.sampleoptions);
1117 
1118         if (region.sample != null) {
1119             if (samples.indexOf(region.sample) != -1) {
1120                 RIFFWriter wlnk = rgns.writeChunk("wlnk");
1121                 wlnk.writeUnsignedShort(region.fusoptions);
1122                 wlnk.writeUnsignedShort(region.phasegroup);
1123                 wlnk.writeUnsignedInt(region.channel);
1124                 wlnk.writeUnsignedInt(samples.indexOf(region.sample));
1125             }
1126         }
1127         writeArticulators(rgns, region.getModulators());
1128         rgns.close();
1129     }
1130 
writeSampleOptions(RIFFWriter wsmp, DLSSampleOptions sampleoptions)1131     private void writeSampleOptions(RIFFWriter wsmp,
1132             DLSSampleOptions sampleoptions) throws IOException {
1133         wsmp.writeUnsignedInt(20);
1134         wsmp.writeUnsignedShort(sampleoptions.unitynote);
1135         wsmp.writeShort(sampleoptions.finetune);
1136         wsmp.writeInt(sampleoptions.attenuation);
1137         wsmp.writeUnsignedInt(sampleoptions.options);
1138         wsmp.writeInt(sampleoptions.loops.size());
1139 
1140         for (DLSSampleLoop loop : sampleoptions.loops) {
1141             wsmp.writeUnsignedInt(16);
1142             wsmp.writeUnsignedInt(loop.type);
1143             wsmp.writeUnsignedInt(loop.start);
1144             wsmp.writeUnsignedInt(loop.length);
1145         }
1146     }
1147 
writeInfoStringChunk(RIFFWriter writer, String name, String value)1148     private void writeInfoStringChunk(RIFFWriter writer,
1149             String name, String value) throws IOException {
1150         if (value == null)
1151             return;
1152         RIFFWriter chunk = writer.writeChunk(name);
1153         chunk.writeString(value);
1154         int len = value.getBytes("ascii").length;
1155         chunk.write(0);
1156         len++;
1157         if (len % 2 != 0)
1158             chunk.write(0);
1159     }
1160 
writeInfo(RIFFWriter writer, DLSInfo info)1161     private void writeInfo(RIFFWriter writer, DLSInfo info) throws IOException {
1162         writeInfoStringChunk(writer, "INAM", info.name);
1163         writeInfoStringChunk(writer, "ICRD", info.creationDate);
1164         writeInfoStringChunk(writer, "IENG", info.engineers);
1165         writeInfoStringChunk(writer, "IPRD", info.product);
1166         writeInfoStringChunk(writer, "ICOP", info.copyright);
1167         writeInfoStringChunk(writer, "ICMT", info.comments);
1168         writeInfoStringChunk(writer, "ISFT", info.tools);
1169         writeInfoStringChunk(writer, "IARL", info.archival_location);
1170         writeInfoStringChunk(writer, "IART", info.artist);
1171         writeInfoStringChunk(writer, "ICMS", info.commissioned);
1172         writeInfoStringChunk(writer, "IGNR", info.genre);
1173         writeInfoStringChunk(writer, "IKEY", info.keywords);
1174         writeInfoStringChunk(writer, "IMED", info.medium);
1175         writeInfoStringChunk(writer, "ISBJ", info.subject);
1176         writeInfoStringChunk(writer, "ISRC", info.source);
1177         writeInfoStringChunk(writer, "ISRF", info.source_form);
1178         writeInfoStringChunk(writer, "ITCH", info.technician);
1179     }
1180 
getInfo()1181     public DLSInfo getInfo() {
1182         return info;
1183     }
1184 
1185     @Override
getName()1186     public String getName() {
1187         return info.name;
1188     }
1189 
1190     @Override
getVersion()1191     public String getVersion() {
1192         return major + "." + minor;
1193     }
1194 
1195     @Override
getVendor()1196     public String getVendor() {
1197         return info.engineers;
1198     }
1199 
1200     @Override
getDescription()1201     public String getDescription() {
1202         return info.comments;
1203     }
1204 
setName(String s)1205     public void setName(String s) {
1206         info.name = s;
1207     }
1208 
setVendor(String s)1209     public void setVendor(String s) {
1210         info.engineers = s;
1211     }
1212 
setDescription(String s)1213     public void setDescription(String s) {
1214         info.comments = s;
1215     }
1216 
1217     @Override
getResources()1218     public SoundbankResource[] getResources() {
1219         SoundbankResource[] resources = new SoundbankResource[samples.size()];
1220         int j = 0;
1221         for (int i = 0; i < samples.size(); i++)
1222             resources[j++] = samples.get(i);
1223         return resources;
1224     }
1225 
1226     @Override
getInstruments()1227     public DLSInstrument[] getInstruments() {
1228         DLSInstrument[] inslist_array =
1229                 instruments.toArray(new DLSInstrument[instruments.size()]);
1230         Arrays.sort(inslist_array, new ModelInstrumentComparator());
1231         return inslist_array;
1232     }
1233 
getSamples()1234     public DLSSample[] getSamples() {
1235         return samples.toArray(new DLSSample[samples.size()]);
1236     }
1237 
1238     @Override
getInstrument(Patch patch)1239     public Instrument getInstrument(Patch patch) {
1240         int program = patch.getProgram();
1241         int bank = patch.getBank();
1242         boolean percussion = false;
1243         if (patch instanceof ModelPatch)
1244             percussion = ((ModelPatch) patch).isPercussion();
1245         for (Instrument instrument : instruments) {
1246             Patch patch2 = instrument.getPatch();
1247             int program2 = patch2.getProgram();
1248             int bank2 = patch2.getBank();
1249             if (program == program2 && bank == bank2) {
1250                 boolean percussion2 = false;
1251                 if (patch2 instanceof ModelPatch)
1252                     percussion2 = ((ModelPatch) patch2).isPercussion();
1253                 if (percussion == percussion2)
1254                     return instrument;
1255             }
1256         }
1257         return null;
1258     }
1259 
addResource(SoundbankResource resource)1260     public void addResource(SoundbankResource resource) {
1261         if (resource instanceof DLSInstrument)
1262             instruments.add((DLSInstrument) resource);
1263         if (resource instanceof DLSSample)
1264             samples.add((DLSSample) resource);
1265     }
1266 
removeResource(SoundbankResource resource)1267     public void removeResource(SoundbankResource resource) {
1268         if (resource instanceof DLSInstrument)
1269             instruments.remove(resource);
1270         if (resource instanceof DLSSample)
1271             samples.remove(resource);
1272     }
1273 
addInstrument(DLSInstrument resource)1274     public void addInstrument(DLSInstrument resource) {
1275         instruments.add(resource);
1276     }
1277 
removeInstrument(DLSInstrument resource)1278     public void removeInstrument(DLSInstrument resource) {
1279         instruments.remove(resource);
1280     }
1281 
getMajor()1282     public long getMajor() {
1283         return major;
1284     }
1285 
setMajor(long major)1286     public void setMajor(long major) {
1287         this.major = major;
1288     }
1289 
getMinor()1290     public long getMinor() {
1291         return minor;
1292     }
1293 
setMinor(long minor)1294     public void setMinor(long minor) {
1295         this.minor = minor;
1296     }
1297 }
1298