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