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 package com.sun.media.sound;
  26 
  27 import java.io.File;
  28 import java.io.FileInputStream;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.OutputStream;
  32 import java.net.URL;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Iterator;
  36 import java.util.List;
  37 import java.util.Map;
  38 
  39 import javax.sound.midi.Instrument;
  40 import javax.sound.midi.Patch;
  41 import javax.sound.midi.Soundbank;
  42 import javax.sound.midi.SoundbankResource;
  43 
  44 /**
  45  * A SoundFont 2.04 soundbank reader.
  46  *
  47  * Based on SoundFont 2.04 specification from:
  48  * <p>  http://developer.creative.com <br>
  49  *      http://www.soundfont.com/ ;
  50  *
  51  * @author Karl Helgason
  52  */
  53 public final class SF2Soundbank implements Soundbank {
  54 
  55     // version of the Sound Font RIFF file
  56     int major = 2;
  57     int minor = 1;
  58     // target Sound Engine
  59     String targetEngine = "EMU8000";
  60     // Sound Font Bank Name
  61     String name = "untitled";
  62     // Sound ROM Name
  63     String romName = null;
  64     // Sound ROM Version
  65     int romVersionMajor = -1;
  66     int romVersionMinor = -1;
  67     // Date of Creation of the Bank
  68     String creationDate = null;
  69     // Sound Designers and Engineers for the Bank
  70     String engineers = null;
  71     // Product for which the Bank was intended
  72     String product = null;
  73     // Copyright message
  74     String copyright = null;
  75     // Comments
  76     String comments = null;
  77     // The SoundFont tools used to create and alter the bank
  78     String tools = null;
  79     // The Sample Data loaded from the SoundFont
  80     private ModelByteBuffer sampleData = null;
  81     private ModelByteBuffer sampleData24 = null;
  82     private File sampleFile = null;
  83     private boolean largeFormat = false;
  84     private final List<SF2Instrument> instruments = new ArrayList<SF2Instrument>();
  85     private final List<SF2Layer> layers = new ArrayList<SF2Layer>();
  86     private final List<SF2Sample> samples = new ArrayList<SF2Sample>();
  87 
  88     public SF2Soundbank() {
  89     }
  90 
  91     public SF2Soundbank(URL url) throws IOException {
  92 
  93         InputStream is = url.openStream();
  94         try {
  95             readSoundbank(is);
  96         } finally {
  97             is.close();
  98         }
  99     }
 100 
 101     public SF2Soundbank(File file) throws IOException {
 102         largeFormat = true;
 103         sampleFile = file;
 104         InputStream is = new FileInputStream(file);
 105         try {
 106             readSoundbank(is);
 107         } finally {
 108             is.close();
 109         }
 110     }
 111 
 112     public SF2Soundbank(InputStream inputstream) throws IOException {
 113         readSoundbank(inputstream);
 114     }
 115 
 116     private void readSoundbank(InputStream inputstream) throws IOException {
 117         RIFFReader riff = new RIFFReader(inputstream);
 118         if (!riff.getFormat().equals("RIFF")) {
 119             throw new RIFFInvalidFormatException(
 120                     "Input stream is not a valid RIFF stream!");
 121         }
 122         if (!riff.getType().equals("sfbk")) {
 123             throw new RIFFInvalidFormatException(
 124                     "Input stream is not a valid SoundFont!");
 125         }
 126         while (riff.hasNextChunk()) {
 127             RIFFReader chunk = riff.nextChunk();
 128             if (chunk.getFormat().equals("LIST")) {
 129                 if (chunk.getType().equals("INFO"))
 130                     readInfoChunk(chunk);
 131                 if (chunk.getType().equals("sdta"))
 132                     readSdtaChunk(chunk);
 133                 if (chunk.getType().equals("pdta"))
 134                     readPdtaChunk(chunk);
 135             }
 136         }
 137     }
 138 
 139     private void readInfoChunk(RIFFReader riff) throws IOException {
 140         while (riff.hasNextChunk()) {
 141             RIFFReader chunk = riff.nextChunk();
 142             String format = chunk.getFormat();
 143             if (format.equals("ifil")) {
 144                 major = chunk.readUnsignedShort();
 145                 minor = chunk.readUnsignedShort();
 146             } else if (format.equals("isng")) {
 147                 this.targetEngine = chunk.readString(chunk.available());
 148             } else if (format.equals("INAM")) {
 149                 this.name = chunk.readString(chunk.available());
 150             } else if (format.equals("irom")) {
 151                 this.romName = chunk.readString(chunk.available());
 152             } else if (format.equals("iver")) {
 153                 romVersionMajor = chunk.readUnsignedShort();
 154                 romVersionMinor = chunk.readUnsignedShort();
 155             } else if (format.equals("ICRD")) {
 156                 this.creationDate = chunk.readString(chunk.available());
 157             } else if (format.equals("IENG")) {
 158                 this.engineers = chunk.readString(chunk.available());
 159             } else if (format.equals("IPRD")) {
 160                 this.product = chunk.readString(chunk.available());
 161             } else if (format.equals("ICOP")) {
 162                 this.copyright = chunk.readString(chunk.available());
 163             } else if (format.equals("ICMT")) {
 164                 this.comments = chunk.readString(chunk.available());
 165             } else if (format.equals("ISFT")) {
 166                 this.tools = chunk.readString(chunk.available());
 167             }
 168 
 169         }
 170     }
 171 
 172     private void readSdtaChunk(RIFFReader riff) throws IOException {
 173         while (riff.hasNextChunk()) {
 174             RIFFReader chunk = riff.nextChunk();
 175             if (chunk.getFormat().equals("smpl")) {
 176                 if (!largeFormat) {
 177                     byte[] sampleData = new byte[chunk.available()];
 178 
 179                     int read = 0;
 180                     int avail = chunk.available();
 181                     while (read != avail) {
 182                         if (avail - read > 65536) {
 183                             chunk.readFully(sampleData, read, 65536);
 184                             read += 65536;
 185                         } else {
 186                             chunk.readFully(sampleData, read, avail - read);
 187                             read = avail;
 188                         }
 189 
 190                     }
 191                     this.sampleData = new ModelByteBuffer(sampleData);
 192                     //chunk.read(sampleData);
 193                 } else {
 194                     this.sampleData = new ModelByteBuffer(sampleFile,
 195                             chunk.getFilePointer(), chunk.available());
 196                 }
 197             }
 198             if (chunk.getFormat().equals("sm24")) {
 199                 if (!largeFormat) {
 200                     byte[] sampleData24 = new byte[chunk.available()];
 201                     //chunk.read(sampleData24);
 202 
 203                     int read = 0;
 204                     int avail = chunk.available();
 205                     while (read != avail) {
 206                         if (avail - read > 65536) {
 207                             chunk.readFully(sampleData24, read, 65536);
 208                             read += 65536;
 209                         } else {
 210                             chunk.readFully(sampleData24, read, avail - read);
 211                             read = avail;
 212                         }
 213 
 214                     }
 215                     this.sampleData24 = new ModelByteBuffer(sampleData24);
 216                 } else {
 217                     this.sampleData24 = new ModelByteBuffer(sampleFile,
 218                             chunk.getFilePointer(), chunk.available());
 219                 }
 220 
 221             }
 222         }
 223     }
 224 
 225     private void readPdtaChunk(RIFFReader riff) throws IOException {
 226 
 227         List<SF2Instrument> presets = new ArrayList<SF2Instrument>();
 228         List<Integer> presets_bagNdx = new ArrayList<Integer>();
 229         List<SF2InstrumentRegion> presets_splits_gen
 230                 = new ArrayList<SF2InstrumentRegion>();
 231         List<SF2InstrumentRegion> presets_splits_mod
 232                 = new ArrayList<SF2InstrumentRegion>();
 233 
 234         List<SF2Layer> instruments = new ArrayList<SF2Layer>();
 235         List<Integer> instruments_bagNdx = new ArrayList<Integer>();
 236         List<SF2LayerRegion> instruments_splits_gen
 237                 = new ArrayList<SF2LayerRegion>();
 238         List<SF2LayerRegion> instruments_splits_mod
 239                 = new ArrayList<SF2LayerRegion>();
 240 
 241         while (riff.hasNextChunk()) {
 242             RIFFReader chunk = riff.nextChunk();
 243             String format = chunk.getFormat();
 244             if (format.equals("phdr")) {
 245                 // Preset Header / Instrument
 246                 if (chunk.available() % 38 != 0)
 247                     throw new RIFFInvalidDataException();
 248                 int count = chunk.available() / 38;
 249                 for (int i = 0; i < count; i++) {
 250                     SF2Instrument preset = new SF2Instrument(this);
 251                     preset.name = chunk.readString(20);
 252                     preset.preset = chunk.readUnsignedShort();
 253                     preset.bank = chunk.readUnsignedShort();
 254                     presets_bagNdx.add(chunk.readUnsignedShort());
 255                     preset.library = chunk.readUnsignedInt();
 256                     preset.genre = chunk.readUnsignedInt();
 257                     preset.morphology = chunk.readUnsignedInt();
 258                     presets.add(preset);
 259                     if (i != count - 1)
 260                         this.instruments.add(preset);
 261                 }
 262             } else if (format.equals("pbag")) {
 263                 // Preset Zones / Instruments splits
 264                 if (chunk.available() % 4 != 0)
 265                     throw new RIFFInvalidDataException();
 266                 int count = chunk.available() / 4;
 267 
 268                 // Skip first record
 269                 {
 270                     int gencount = chunk.readUnsignedShort();
 271                     int modcount = chunk.readUnsignedShort();
 272                     while (presets_splits_gen.size() < gencount)
 273                         presets_splits_gen.add(null);
 274                     while (presets_splits_mod.size() < modcount)
 275                         presets_splits_mod.add(null);
 276                     count--;
 277                 }
 278 
 279                 if (presets_bagNdx.isEmpty()) {
 280                     throw new RIFFInvalidDataException();
 281                 }
 282                 int offset = presets_bagNdx.get(0);
 283                 // Offset should be 0 (but just case)
 284                 for (int i = 0; i < offset; i++) {
 285                     if (count == 0)
 286                         throw new RIFFInvalidDataException();
 287                     int gencount = chunk.readUnsignedShort();
 288                     int modcount = chunk.readUnsignedShort();
 289                     while (presets_splits_gen.size() < gencount)
 290                         presets_splits_gen.add(null);
 291                     while (presets_splits_mod.size() < modcount)
 292                         presets_splits_mod.add(null);
 293                     count--;
 294                 }
 295 
 296                 for (int i = 0; i < presets_bagNdx.size() - 1; i++) {
 297                     int zone_count = presets_bagNdx.get(i + 1)
 298                                      - presets_bagNdx.get(i);
 299                     SF2Instrument preset = presets.get(i);
 300                     for (int ii = 0; ii < zone_count; ii++) {
 301                         if (count == 0)
 302                             throw new RIFFInvalidDataException();
 303                         int gencount = chunk.readUnsignedShort();
 304                         int modcount = chunk.readUnsignedShort();
 305                         SF2InstrumentRegion split = new SF2InstrumentRegion();
 306                         preset.regions.add(split);
 307                         while (presets_splits_gen.size() < gencount)
 308                             presets_splits_gen.add(split);
 309                         while (presets_splits_mod.size() < modcount)
 310                             presets_splits_mod.add(split);
 311                         count--;
 312                     }
 313                 }
 314             } else if (format.equals("pmod")) {
 315                 // Preset Modulators / Split Modulators
 316                 for (int i = 0; i < presets_splits_mod.size(); i++) {
 317                     SF2Modulator modulator = new SF2Modulator();
 318                     modulator.sourceOperator = chunk.readUnsignedShort();
 319                     modulator.destinationOperator = chunk.readUnsignedShort();
 320                     modulator.amount = chunk.readShort();
 321                     modulator.amountSourceOperator = chunk.readUnsignedShort();
 322                     modulator.transportOperator = chunk.readUnsignedShort();
 323                     SF2InstrumentRegion split = presets_splits_mod.get(i);
 324                     if (split != null)
 325                         split.modulators.add(modulator);
 326                 }
 327             } else if (format.equals("pgen")) {
 328                 // Preset Generators / Split Generators
 329                 for (int i = 0; i < presets_splits_gen.size(); i++) {
 330                     int operator = chunk.readUnsignedShort();
 331                     short amount = chunk.readShort();
 332                     SF2InstrumentRegion split = presets_splits_gen.get(i);
 333                     if (split != null)
 334                         split.generators.put(operator, amount);
 335                 }
 336             } else if (format.equals("inst")) {
 337                 // Instrument Header / Layers
 338                 if (chunk.available() % 22 != 0)
 339                     throw new RIFFInvalidDataException();
 340                 int count = chunk.available() / 22;
 341                 for (int i = 0; i < count; i++) {
 342                     SF2Layer layer = new SF2Layer(this);
 343                     layer.name = chunk.readString(20);
 344                     instruments_bagNdx.add(chunk.readUnsignedShort());
 345                     instruments.add(layer);
 346                     if (i != count - 1)
 347                         this.layers.add(layer);
 348                 }
 349             } else if (format.equals("ibag")) {
 350                 // Instrument Zones / Layer splits
 351                 if (chunk.available() % 4 != 0)
 352                     throw new RIFFInvalidDataException();
 353                 int count = chunk.available() / 4;
 354 
 355                 // Skip first record
 356                 {
 357                     int gencount = chunk.readUnsignedShort();
 358                     int modcount = chunk.readUnsignedShort();
 359                     while (instruments_splits_gen.size() < gencount)
 360                         instruments_splits_gen.add(null);
 361                     while (instruments_splits_mod.size() < modcount)
 362                         instruments_splits_mod.add(null);
 363                     count--;
 364                 }
 365 
 366                 if (instruments_bagNdx.isEmpty()) {
 367                     throw new RIFFInvalidDataException();
 368                 }
 369                 int offset = instruments_bagNdx.get(0);
 370                 // Offset should be 0 (but just case)
 371                 for (int i = 0; i < offset; i++) {
 372                     if (count == 0)
 373                         throw new RIFFInvalidDataException();
 374                     int gencount = chunk.readUnsignedShort();
 375                     int modcount = chunk.readUnsignedShort();
 376                     while (instruments_splits_gen.size() < gencount)
 377                         instruments_splits_gen.add(null);
 378                     while (instruments_splits_mod.size() < modcount)
 379                         instruments_splits_mod.add(null);
 380                     count--;
 381                 }
 382 
 383                 for (int i = 0; i < instruments_bagNdx.size() - 1; i++) {
 384                     int zone_count = instruments_bagNdx.get(i + 1) - instruments_bagNdx.get(i);
 385                     SF2Layer layer = layers.get(i);
 386                     for (int ii = 0; ii < zone_count; ii++) {
 387                         if (count == 0)
 388                             throw new RIFFInvalidDataException();
 389                         int gencount = chunk.readUnsignedShort();
 390                         int modcount = chunk.readUnsignedShort();
 391                         SF2LayerRegion split = new SF2LayerRegion();
 392                         layer.regions.add(split);
 393                         while (instruments_splits_gen.size() < gencount)
 394                             instruments_splits_gen.add(split);
 395                         while (instruments_splits_mod.size() < modcount)
 396                             instruments_splits_mod.add(split);
 397                         count--;
 398                     }
 399                 }
 400 
 401             } else if (format.equals("imod")) {
 402                 // Instrument Modulators / Split Modulators
 403                 for (int i = 0; i < instruments_splits_mod.size(); i++) {
 404                     SF2Modulator modulator = new SF2Modulator();
 405                     modulator.sourceOperator = chunk.readUnsignedShort();
 406                     modulator.destinationOperator = chunk.readUnsignedShort();
 407                     modulator.amount = chunk.readShort();
 408                     modulator.amountSourceOperator = chunk.readUnsignedShort();
 409                     modulator.transportOperator = chunk.readUnsignedShort();
 410                     if (i < 0 || i >= instruments_splits_gen.size()) {
 411                         throw new RIFFInvalidDataException();
 412                     }
 413                     SF2LayerRegion split = instruments_splits_gen.get(i);
 414                     if (split != null)
 415                         split.modulators.add(modulator);
 416                 }
 417             } else if (format.equals("igen")) {
 418                 // Instrument Generators / Split Generators
 419                 for (int i = 0; i < instruments_splits_gen.size(); i++) {
 420                     int operator = chunk.readUnsignedShort();
 421                     short amount = chunk.readShort();
 422                     SF2LayerRegion split = instruments_splits_gen.get(i);
 423                     if (split != null)
 424                         split.generators.put(operator, amount);
 425                 }
 426             } else if (format.equals("shdr")) {
 427                 // Sample Headers
 428                 if (chunk.available() % 46 != 0)
 429                     throw new RIFFInvalidDataException();
 430                 int count = chunk.available() / 46;
 431                 for (int i = 0; i < count; i++) {
 432                     SF2Sample sample = new SF2Sample(this);
 433                     sample.name = chunk.readString(20);
 434                     long start = chunk.readUnsignedInt();
 435                     long end = chunk.readUnsignedInt();
 436                     if (sampleData != null)
 437                         sample.data = sampleData.subbuffer(start * 2, end * 2, true);
 438                     if (sampleData24 != null)
 439                         sample.data24 = sampleData24.subbuffer(start, end, true);
 440                     /*
 441                     sample.data = new ModelByteBuffer(sampleData, (int)(start*2),
 442                             (int)((end - start)*2));
 443                     if (sampleData24 != null)
 444                         sample.data24 = new ModelByteBuffer(sampleData24,
 445                                 (int)start, (int)(end - start));
 446                      */
 447                     sample.startLoop = chunk.readUnsignedInt() - start;
 448                     sample.endLoop = chunk.readUnsignedInt() - start;
 449                     if (sample.startLoop < 0)
 450                         sample.startLoop = -1;
 451                     if (sample.endLoop < 0)
 452                         sample.endLoop = -1;
 453                     sample.sampleRate = chunk.readUnsignedInt();
 454                     sample.originalPitch = chunk.readUnsignedByte();
 455                     sample.pitchCorrection = chunk.readByte();
 456                     sample.sampleLink = chunk.readUnsignedShort();
 457                     sample.sampleType = chunk.readUnsignedShort();
 458                     if (i != count - 1)
 459                         this.samples.add(sample);
 460                 }
 461             }
 462         }
 463 
 464         Iterator<SF2Layer> liter = this.layers.iterator();
 465         while (liter.hasNext()) {
 466             SF2Layer layer = liter.next();
 467             Iterator<SF2LayerRegion> siter = layer.regions.iterator();
 468             SF2Region globalsplit = null;
 469             while (siter.hasNext()) {
 470                 SF2LayerRegion split = siter.next();
 471                 if (split.generators.get(SF2LayerRegion.GENERATOR_SAMPLEID) != null) {
 472                     int sampleid = split.generators.get(
 473                             SF2LayerRegion.GENERATOR_SAMPLEID);
 474                     split.generators.remove(SF2LayerRegion.GENERATOR_SAMPLEID);
 475                     if (sampleid < 0 || sampleid >= samples.size()) {
 476                         throw new RIFFInvalidDataException();
 477                     }
 478                     split.sample = samples.get(sampleid);
 479                 } else {
 480                     globalsplit = split;
 481                 }
 482             }
 483             if (globalsplit != null) {
 484                 layer.getRegions().remove(globalsplit);
 485                 SF2GlobalRegion gsplit = new SF2GlobalRegion();
 486                 gsplit.generators = globalsplit.generators;
 487                 gsplit.modulators = globalsplit.modulators;
 488                 layer.setGlobalZone(gsplit);
 489             }
 490         }
 491 
 492 
 493         Iterator<SF2Instrument> iiter = this.instruments.iterator();
 494         while (iiter.hasNext()) {
 495             SF2Instrument instrument = iiter.next();
 496             Iterator<SF2InstrumentRegion> siter = instrument.regions.iterator();
 497             SF2Region globalsplit = null;
 498             while (siter.hasNext()) {
 499                 SF2InstrumentRegion split = siter.next();
 500                 if (split.generators.get(SF2LayerRegion.GENERATOR_INSTRUMENT) != null) {
 501                     int instrumentid = split.generators.get(
 502                             SF2InstrumentRegion.GENERATOR_INSTRUMENT);
 503                     split.generators.remove(SF2LayerRegion.GENERATOR_INSTRUMENT);
 504                     if (instrumentid < 0 || instrumentid >= layers.size()) {
 505                         throw new RIFFInvalidDataException();
 506                     }
 507                     split.layer = layers.get(instrumentid);
 508                 } else {
 509                     globalsplit = split;
 510                 }
 511             }
 512 
 513             if (globalsplit != null) {
 514                 instrument.getRegions().remove(globalsplit);
 515                 SF2GlobalRegion gsplit = new SF2GlobalRegion();
 516                 gsplit.generators = globalsplit.generators;
 517                 gsplit.modulators = globalsplit.modulators;
 518                 instrument.setGlobalZone(gsplit);
 519             }
 520         }
 521 
 522     }
 523 
 524     public void save(String name) throws IOException {
 525         writeSoundbank(new RIFFWriter(name, "sfbk"));
 526     }
 527 
 528     public void save(File file) throws IOException {
 529         writeSoundbank(new RIFFWriter(file, "sfbk"));
 530     }
 531 
 532     public void save(OutputStream out) throws IOException {
 533         writeSoundbank(new RIFFWriter(out, "sfbk"));
 534     }
 535 
 536     private void writeSoundbank(RIFFWriter writer) throws IOException {
 537         writeInfo(writer.writeList("INFO"));
 538         writeSdtaChunk(writer.writeList("sdta"));
 539         writePdtaChunk(writer.writeList("pdta"));
 540         writer.close();
 541     }
 542 
 543     private void writeInfoStringChunk(RIFFWriter writer, String name,
 544             String value) throws IOException {
 545         if (value == null)
 546             return;
 547         RIFFWriter chunk = writer.writeChunk(name);
 548         chunk.writeString(value);
 549         int len = value.getBytes("ascii").length;
 550         chunk.write(0);
 551         len++;
 552         if (len % 2 != 0)
 553             chunk.write(0);
 554     }
 555 
 556     private void writeInfo(RIFFWriter writer) throws IOException {
 557         if (this.targetEngine == null)
 558             this.targetEngine = "EMU8000";
 559         if (this.name == null)
 560             this.name = "";
 561 
 562         RIFFWriter ifil_chunk = writer.writeChunk("ifil");
 563         ifil_chunk.writeUnsignedShort(this.major);
 564         ifil_chunk.writeUnsignedShort(this.minor);
 565         writeInfoStringChunk(writer, "isng", this.targetEngine);
 566         writeInfoStringChunk(writer, "INAM", this.name);
 567         writeInfoStringChunk(writer, "irom", this.romName);
 568         if (romVersionMajor != -1) {
 569             RIFFWriter iver_chunk = writer.writeChunk("iver");
 570             iver_chunk.writeUnsignedShort(this.romVersionMajor);
 571             iver_chunk.writeUnsignedShort(this.romVersionMinor);
 572         }
 573         writeInfoStringChunk(writer, "ICRD", this.creationDate);
 574         writeInfoStringChunk(writer, "IENG", this.engineers);
 575         writeInfoStringChunk(writer, "IPRD", this.product);
 576         writeInfoStringChunk(writer, "ICOP", this.copyright);
 577         writeInfoStringChunk(writer, "ICMT", this.comments);
 578         writeInfoStringChunk(writer, "ISFT", this.tools);
 579 
 580         writer.close();
 581     }
 582 
 583     private void writeSdtaChunk(RIFFWriter writer) throws IOException {
 584 
 585         byte[] pad = new byte[32];
 586 
 587         RIFFWriter smpl_chunk = writer.writeChunk("smpl");
 588         for (SF2Sample sample : samples) {
 589             ModelByteBuffer data = sample.getDataBuffer();
 590             data.writeTo(smpl_chunk);
 591             /*
 592             smpl_chunk.write(data.array(),
 593             data.arrayOffset(),
 594             data.capacity());
 595              */
 596             smpl_chunk.write(pad);
 597             smpl_chunk.write(pad);
 598         }
 599         if (major < 2)
 600             return;
 601         if (major == 2 && minor < 4)
 602             return;
 603 
 604 
 605         for (SF2Sample sample : samples) {
 606             ModelByteBuffer data24 = sample.getData24Buffer();
 607             if (data24 == null)
 608                 return;
 609         }
 610 
 611         RIFFWriter sm24_chunk = writer.writeChunk("sm24");
 612         for (SF2Sample sample : samples) {
 613             ModelByteBuffer data = sample.getData24Buffer();
 614             data.writeTo(sm24_chunk);
 615             /*
 616             sm24_chunk.write(data.array(),
 617             data.arrayOffset(),
 618             data.capacity());*/
 619             smpl_chunk.write(pad);
 620         }
 621     }
 622 
 623     private void writeModulators(RIFFWriter writer, List<SF2Modulator> modulators)
 624             throws IOException {
 625         for (SF2Modulator modulator : modulators) {
 626             writer.writeUnsignedShort(modulator.sourceOperator);
 627             writer.writeUnsignedShort(modulator.destinationOperator);
 628             writer.writeShort(modulator.amount);
 629             writer.writeUnsignedShort(modulator.amountSourceOperator);
 630             writer.writeUnsignedShort(modulator.transportOperator);
 631         }
 632     }
 633 
 634     private void writeGenerators(RIFFWriter writer, Map<Integer, Short> generators)
 635             throws IOException {
 636         Short keyrange = generators.get(SF2Region.GENERATOR_KEYRANGE);
 637         Short velrange = generators.get(SF2Region.GENERATOR_VELRANGE);
 638         if (keyrange != null) {
 639             writer.writeUnsignedShort(SF2Region.GENERATOR_KEYRANGE);
 640             writer.writeShort(keyrange);
 641         }
 642         if (velrange != null) {
 643             writer.writeUnsignedShort(SF2Region.GENERATOR_VELRANGE);
 644             writer.writeShort(velrange);
 645         }
 646         for (Map.Entry<Integer, Short> generator : generators.entrySet()) {
 647             if (generator.getKey() == SF2Region.GENERATOR_KEYRANGE)
 648                 continue;
 649             if (generator.getKey() == SF2Region.GENERATOR_VELRANGE)
 650                 continue;
 651             writer.writeUnsignedShort(generator.getKey());
 652             writer.writeShort(generator.getValue());
 653         }
 654     }
 655 
 656     private void writePdtaChunk(RIFFWriter writer) throws IOException {
 657 
 658         RIFFWriter phdr_chunk = writer.writeChunk("phdr");
 659         int phdr_zone_count = 0;
 660         for (SF2Instrument preset : this.instruments) {
 661             phdr_chunk.writeString(preset.name, 20);
 662             phdr_chunk.writeUnsignedShort(preset.preset);
 663             phdr_chunk.writeUnsignedShort(preset.bank);
 664             phdr_chunk.writeUnsignedShort(phdr_zone_count);
 665             if (preset.getGlobalRegion() != null)
 666                 phdr_zone_count += 1;
 667             phdr_zone_count += preset.getRegions().size();
 668             phdr_chunk.writeUnsignedInt(preset.library);
 669             phdr_chunk.writeUnsignedInt(preset.genre);
 670             phdr_chunk.writeUnsignedInt(preset.morphology);
 671         }
 672         phdr_chunk.writeString("EOP", 20);
 673         phdr_chunk.writeUnsignedShort(0);
 674         phdr_chunk.writeUnsignedShort(0);
 675         phdr_chunk.writeUnsignedShort(phdr_zone_count);
 676         phdr_chunk.writeUnsignedInt(0);
 677         phdr_chunk.writeUnsignedInt(0);
 678         phdr_chunk.writeUnsignedInt(0);
 679 
 680 
 681         RIFFWriter pbag_chunk = writer.writeChunk("pbag");
 682         int pbag_gencount = 0;
 683         int pbag_modcount = 0;
 684         for (SF2Instrument preset : this.instruments) {
 685             if (preset.getGlobalRegion() != null) {
 686                 pbag_chunk.writeUnsignedShort(pbag_gencount);
 687                 pbag_chunk.writeUnsignedShort(pbag_modcount);
 688                 pbag_gencount += preset.getGlobalRegion().getGenerators().size();
 689                 pbag_modcount += preset.getGlobalRegion().getModulators().size();
 690             }
 691             for (SF2InstrumentRegion region : preset.getRegions()) {
 692                 pbag_chunk.writeUnsignedShort(pbag_gencount);
 693                 pbag_chunk.writeUnsignedShort(pbag_modcount);
 694                 if (layers.indexOf(region.layer) != -1) {
 695                     // One generator is used to reference to instrument record
 696                     pbag_gencount += 1;
 697                 }
 698                 pbag_gencount += region.getGenerators().size();
 699                 pbag_modcount += region.getModulators().size();
 700 
 701             }
 702         }
 703         pbag_chunk.writeUnsignedShort(pbag_gencount);
 704         pbag_chunk.writeUnsignedShort(pbag_modcount);
 705 
 706         RIFFWriter pmod_chunk = writer.writeChunk("pmod");
 707         for (SF2Instrument preset : this.instruments) {
 708             if (preset.getGlobalRegion() != null) {
 709                 writeModulators(pmod_chunk,
 710                         preset.getGlobalRegion().getModulators());
 711             }
 712             for (SF2InstrumentRegion region : preset.getRegions())
 713                 writeModulators(pmod_chunk, region.getModulators());
 714         }
 715         pmod_chunk.write(new byte[10]);
 716 
 717         RIFFWriter pgen_chunk = writer.writeChunk("pgen");
 718         for (SF2Instrument preset : this.instruments) {
 719             if (preset.getGlobalRegion() != null) {
 720                 writeGenerators(pgen_chunk,
 721                         preset.getGlobalRegion().getGenerators());
 722             }
 723             for (SF2InstrumentRegion region : preset.getRegions()) {
 724                 writeGenerators(pgen_chunk, region.getGenerators());
 725                 int ix = layers.indexOf(region.layer);
 726                 if (ix != -1) {
 727                     pgen_chunk.writeUnsignedShort(SF2Region.GENERATOR_INSTRUMENT);
 728                     pgen_chunk.writeShort((short) ix);
 729                 }
 730             }
 731         }
 732         pgen_chunk.write(new byte[4]);
 733 
 734         RIFFWriter inst_chunk = writer.writeChunk("inst");
 735         int inst_zone_count = 0;
 736         for (SF2Layer instrument : this.layers) {
 737             inst_chunk.writeString(instrument.name, 20);
 738             inst_chunk.writeUnsignedShort(inst_zone_count);
 739             if (instrument.getGlobalRegion() != null)
 740                 inst_zone_count += 1;
 741             inst_zone_count += instrument.getRegions().size();
 742         }
 743         inst_chunk.writeString("EOI", 20);
 744         inst_chunk.writeUnsignedShort(inst_zone_count);
 745 
 746 
 747         RIFFWriter ibag_chunk = writer.writeChunk("ibag");
 748         int ibag_gencount = 0;
 749         int ibag_modcount = 0;
 750         for (SF2Layer instrument : this.layers) {
 751             if (instrument.getGlobalRegion() != null) {
 752                 ibag_chunk.writeUnsignedShort(ibag_gencount);
 753                 ibag_chunk.writeUnsignedShort(ibag_modcount);
 754                 ibag_gencount
 755                         += instrument.getGlobalRegion().getGenerators().size();
 756                 ibag_modcount
 757                         += instrument.getGlobalRegion().getModulators().size();
 758             }
 759             for (SF2LayerRegion region : instrument.getRegions()) {
 760                 ibag_chunk.writeUnsignedShort(ibag_gencount);
 761                 ibag_chunk.writeUnsignedShort(ibag_modcount);
 762                 if (samples.indexOf(region.sample) != -1) {
 763                     // One generator is used to reference to instrument record
 764                     ibag_gencount += 1;
 765                 }
 766                 ibag_gencount += region.getGenerators().size();
 767                 ibag_modcount += region.getModulators().size();
 768 
 769             }
 770         }
 771         ibag_chunk.writeUnsignedShort(ibag_gencount);
 772         ibag_chunk.writeUnsignedShort(ibag_modcount);
 773 
 774 
 775         RIFFWriter imod_chunk = writer.writeChunk("imod");
 776         for (SF2Layer instrument : this.layers) {
 777             if (instrument.getGlobalRegion() != null) {
 778                 writeModulators(imod_chunk,
 779                         instrument.getGlobalRegion().getModulators());
 780             }
 781             for (SF2LayerRegion region : instrument.getRegions())
 782                 writeModulators(imod_chunk, region.getModulators());
 783         }
 784         imod_chunk.write(new byte[10]);
 785 
 786         RIFFWriter igen_chunk = writer.writeChunk("igen");
 787         for (SF2Layer instrument : this.layers) {
 788             if (instrument.getGlobalRegion() != null) {
 789                 writeGenerators(igen_chunk,
 790                         instrument.getGlobalRegion().getGenerators());
 791             }
 792             for (SF2LayerRegion region : instrument.getRegions()) {
 793                 writeGenerators(igen_chunk, region.getGenerators());
 794                 int ix = samples.indexOf(region.sample);
 795                 if (ix != -1) {
 796                     igen_chunk.writeUnsignedShort(SF2Region.GENERATOR_SAMPLEID);
 797                     igen_chunk.writeShort((short) ix);
 798                 }
 799             }
 800         }
 801         igen_chunk.write(new byte[4]);
 802 
 803 
 804         RIFFWriter shdr_chunk = writer.writeChunk("shdr");
 805         long sample_pos = 0;
 806         for (SF2Sample sample : samples) {
 807             shdr_chunk.writeString(sample.name, 20);
 808             long start = sample_pos;
 809             sample_pos += sample.data.capacity() / 2;
 810             long end = sample_pos;
 811             long startLoop = sample.startLoop + start;
 812             long endLoop = sample.endLoop + start;
 813             if (startLoop < start)
 814                 startLoop = start;
 815             if (endLoop > end)
 816                 endLoop = end;
 817             shdr_chunk.writeUnsignedInt(start);
 818             shdr_chunk.writeUnsignedInt(end);
 819             shdr_chunk.writeUnsignedInt(startLoop);
 820             shdr_chunk.writeUnsignedInt(endLoop);
 821             shdr_chunk.writeUnsignedInt(sample.sampleRate);
 822             shdr_chunk.writeUnsignedByte(sample.originalPitch);
 823             shdr_chunk.writeByte(sample.pitchCorrection);
 824             shdr_chunk.writeUnsignedShort(sample.sampleLink);
 825             shdr_chunk.writeUnsignedShort(sample.sampleType);
 826             sample_pos += 32;
 827         }
 828         shdr_chunk.writeString("EOS", 20);
 829         shdr_chunk.write(new byte[26]);
 830 
 831     }
 832 
 833     public String getName() {
 834         return name;
 835     }
 836 
 837     public String getVersion() {
 838         return major + "." + minor;
 839     }
 840 
 841     public String getVendor() {
 842         return engineers;
 843     }
 844 
 845     public String getDescription() {
 846         return comments;
 847     }
 848 
 849     public void setName(String s) {
 850         name = s;
 851     }
 852 
 853     public void setVendor(String s) {
 854         engineers = s;
 855     }
 856 
 857     public void setDescription(String s) {
 858         comments = s;
 859     }
 860 
 861     public SoundbankResource[] getResources() {
 862         SoundbankResource[] resources
 863                 = new SoundbankResource[layers.size() + samples.size()];
 864         int j = 0;
 865         for (int i = 0; i < layers.size(); i++)
 866             resources[j++] = layers.get(i);
 867         for (int i = 0; i < samples.size(); i++)
 868             resources[j++] = samples.get(i);
 869         return resources;
 870     }
 871 
 872     public SF2Instrument[] getInstruments() {
 873         SF2Instrument[] inslist_array
 874                 = instruments.toArray(new SF2Instrument[instruments.size()]);
 875         Arrays.sort(inslist_array, new ModelInstrumentComparator());
 876         return inslist_array;
 877     }
 878 
 879     public SF2Layer[] getLayers() {
 880         return layers.toArray(new SF2Layer[layers.size()]);
 881     }
 882 
 883     public SF2Sample[] getSamples() {
 884         return samples.toArray(new SF2Sample[samples.size()]);
 885     }
 886 
 887     public Instrument getInstrument(Patch patch) {
 888         int program = patch.getProgram();
 889         int bank = patch.getBank();
 890         boolean percussion = false;
 891         if (patch instanceof ModelPatch)
 892             percussion = ((ModelPatch)patch).isPercussion();
 893         for (Instrument instrument : instruments) {
 894             Patch patch2 = instrument.getPatch();
 895             int program2 = patch2.getProgram();
 896             int bank2 = patch2.getBank();
 897             if (program == program2 && bank == bank2) {
 898                 boolean percussion2 = false;
 899                 if (patch2 instanceof ModelPatch)
 900                     percussion2 = ((ModelPatch) patch2).isPercussion();
 901                 if (percussion == percussion2)
 902                     return instrument;
 903             }
 904         }
 905         return null;
 906     }
 907 
 908     public String getCreationDate() {
 909         return creationDate;
 910     }
 911 
 912     public void setCreationDate(String creationDate) {
 913         this.creationDate = creationDate;
 914     }
 915 
 916     public String getProduct() {
 917         return product;
 918     }
 919 
 920     public void setProduct(String product) {
 921         this.product = product;
 922     }
 923 
 924     public String getRomName() {
 925         return romName;
 926     }
 927 
 928     public void setRomName(String romName) {
 929         this.romName = romName;
 930     }
 931 
 932     public int getRomVersionMajor() {
 933         return romVersionMajor;
 934     }
 935 
 936     public void setRomVersionMajor(int romVersionMajor) {
 937         this.romVersionMajor = romVersionMajor;
 938     }
 939 
 940     public int getRomVersionMinor() {
 941         return romVersionMinor;
 942     }
 943 
 944     public void setRomVersionMinor(int romVersionMinor) {
 945         this.romVersionMinor = romVersionMinor;
 946     }
 947 
 948     public String getTargetEngine() {
 949         return targetEngine;
 950     }
 951 
 952     public void setTargetEngine(String targetEngine) {
 953         this.targetEngine = targetEngine;
 954     }
 955 
 956     public String getTools() {
 957         return tools;
 958     }
 959 
 960     public void setTools(String tools) {
 961         this.tools = tools;
 962     }
 963 
 964     public void addResource(SoundbankResource resource) {
 965         if (resource instanceof SF2Instrument)
 966             instruments.add((SF2Instrument)resource);
 967         if (resource instanceof SF2Layer)
 968             layers.add((SF2Layer)resource);
 969         if (resource instanceof SF2Sample)
 970             samples.add((SF2Sample)resource);
 971     }
 972 
 973     public void removeResource(SoundbankResource resource) {
 974         if (resource instanceof SF2Instrument)
 975             instruments.remove((SF2Instrument)resource);
 976         if (resource instanceof SF2Layer)
 977             layers.remove((SF2Layer)resource);
 978         if (resource instanceof SF2Sample)
 979             samples.remove((SF2Sample)resource);
 980     }
 981 
 982     public void addInstrument(SF2Instrument resource) {
 983         instruments.add(resource);
 984     }
 985 
 986     public void removeInstrument(SF2Instrument resource) {
 987         instruments.remove(resource);
 988     }
 989 }