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                 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                 int offset = instruments_bagNdx.get(0);
 364                 // Offset should be 0 (but just case)
 365                 for (int i = 0; i < offset; i++) {
 366                     if (count == 0)
 367                         throw new RIFFInvalidDataException();
 368                     int gencount = chunk.readUnsignedShort();
 369                     int modcount = chunk.readUnsignedShort();
 370                     while (instruments_splits_gen.size() < gencount)
 371                         instruments_splits_gen.add(null);
 372                     while (instruments_splits_mod.size() < modcount)
 373                         instruments_splits_mod.add(null);
 374                     count--;
 375                 }
 376 
 377                 for (int i = 0; i < instruments_bagNdx.size() - 1; i++) {
 378                     int zone_count = instruments_bagNdx.get(i + 1) - instruments_bagNdx.get(i);
 379                     SF2Layer layer = layers.get(i);
 380                     for (int ii = 0; ii < zone_count; ii++) {
 381                         if (count == 0)
 382                             throw new RIFFInvalidDataException();
 383                         int gencount = chunk.readUnsignedShort();
 384                         int modcount = chunk.readUnsignedShort();
 385                         SF2LayerRegion split = new SF2LayerRegion();
 386                         layer.regions.add(split);
 387                         while (instruments_splits_gen.size() < gencount)
 388                             instruments_splits_gen.add(split);
 389                         while (instruments_splits_mod.size() < modcount)
 390                             instruments_splits_mod.add(split);
 391                         count--;
 392                     }
 393                 }
 394 
 395             } else if (format.equals("imod")) {
 396                 // Instrument Modulators / Split Modulators
 397                 for (int i = 0; i < instruments_splits_mod.size(); i++) {
 398                     SF2Modulator modulator = new SF2Modulator();
 399                     modulator.sourceOperator = chunk.readUnsignedShort();
 400                     modulator.destinationOperator = chunk.readUnsignedShort();
 401                     modulator.amount = chunk.readShort();
 402                     modulator.amountSourceOperator = chunk.readUnsignedShort();
 403                     modulator.transportOperator = chunk.readUnsignedShort();
 404                     SF2LayerRegion split = instruments_splits_gen.get(i);
 405                     if (split != null)
 406                         split.modulators.add(modulator);
 407                 }
 408             } else if (format.equals("igen")) {
 409                 // Instrument Generators / Split Generators
 410                 for (int i = 0; i < instruments_splits_gen.size(); i++) {
 411                     int operator = chunk.readUnsignedShort();
 412                     short amount = chunk.readShort();
 413                     SF2LayerRegion split = instruments_splits_gen.get(i);
 414                     if (split != null)
 415                         split.generators.put(operator, amount);
 416                 }
 417             } else if (format.equals("shdr")) {
 418                 // Sample Headers
 419                 if (chunk.available() % 46 != 0)
 420                     throw new RIFFInvalidDataException();
 421                 int count = chunk.available() / 46;
 422                 for (int i = 0; i < count; i++) {
 423                     SF2Sample sample = new SF2Sample(this);
 424                     sample.name = chunk.readString(20);
 425                     long start = chunk.readUnsignedInt();
 426                     long end = chunk.readUnsignedInt();
 427                     sample.data = sampleData.subbuffer(start * 2, end * 2, true);
 428                     if (sampleData24 != null)
 429                         sample.data24 = sampleData24.subbuffer(start, end, true);
 430                     /*
 431                     sample.data = new ModelByteBuffer(sampleData, (int)(start*2),
 432                             (int)((end - start)*2));
 433                     if (sampleData24 != null)
 434                         sample.data24 = new ModelByteBuffer(sampleData24,
 435                                 (int)start, (int)(end - start));
 436                      */
 437                     sample.startLoop = chunk.readUnsignedInt() - start;
 438                     sample.endLoop = chunk.readUnsignedInt() - start;
 439                     if (sample.startLoop < 0)
 440                         sample.startLoop = -1;
 441                     if (sample.endLoop < 0)
 442                         sample.endLoop = -1;
 443                     sample.sampleRate = chunk.readUnsignedInt();
 444                     sample.originalPitch = chunk.readUnsignedByte();
 445                     sample.pitchCorrection = chunk.readByte();
 446                     sample.sampleLink = chunk.readUnsignedShort();
 447                     sample.sampleType = chunk.readUnsignedShort();
 448                     if (i != count - 1)
 449                         this.samples.add(sample);
 450                 }
 451             }
 452         }
 453 
 454         Iterator<SF2Layer> liter = this.layers.iterator();
 455         while (liter.hasNext()) {
 456             SF2Layer layer = liter.next();
 457             Iterator<SF2LayerRegion> siter = layer.regions.iterator();
 458             SF2Region globalsplit = null;
 459             while (siter.hasNext()) {
 460                 SF2LayerRegion split = siter.next();
 461                 if (split.generators.get(SF2LayerRegion.GENERATOR_SAMPLEID) != null) {
 462                     int sampleid = split.generators.get(
 463                             SF2LayerRegion.GENERATOR_SAMPLEID);
 464                     split.generators.remove(SF2LayerRegion.GENERATOR_SAMPLEID);
 465                     split.sample = samples.get(sampleid);
 466                 } else {
 467                     globalsplit = split;
 468                 }
 469             }
 470             if (globalsplit != null) {
 471                 layer.getRegions().remove(globalsplit);
 472                 SF2GlobalRegion gsplit = new SF2GlobalRegion();
 473                 gsplit.generators = globalsplit.generators;
 474                 gsplit.modulators = globalsplit.modulators;
 475                 layer.setGlobalZone(gsplit);
 476             }
 477         }
 478 
 479 
 480         Iterator<SF2Instrument> iiter = this.instruments.iterator();
 481         while (iiter.hasNext()) {
 482             SF2Instrument instrument = iiter.next();
 483             Iterator<SF2InstrumentRegion> siter = instrument.regions.iterator();
 484             SF2Region globalsplit = null;
 485             while (siter.hasNext()) {
 486                 SF2InstrumentRegion split = siter.next();
 487                 if (split.generators.get(SF2LayerRegion.GENERATOR_INSTRUMENT) != null) {
 488                     int instrumentid = split.generators.get(
 489                             SF2InstrumentRegion.GENERATOR_INSTRUMENT);
 490                     split.generators.remove(SF2LayerRegion.GENERATOR_INSTRUMENT);
 491                     split.layer = layers.get(instrumentid);
 492                 } else {
 493                     globalsplit = split;
 494                 }
 495             }
 496 
 497             if (globalsplit != null) {
 498                 instrument.getRegions().remove(globalsplit);
 499                 SF2GlobalRegion gsplit = new SF2GlobalRegion();
 500                 gsplit.generators = globalsplit.generators;
 501                 gsplit.modulators = globalsplit.modulators;
 502                 instrument.setGlobalZone(gsplit);
 503             }
 504         }
 505 
 506     }
 507 
 508     public void save(String name) throws IOException {
 509         writeSoundbank(new RIFFWriter(name, "sfbk"));
 510     }
 511 
 512     public void save(File file) throws IOException {
 513         writeSoundbank(new RIFFWriter(file, "sfbk"));
 514     }
 515 
 516     public void save(OutputStream out) throws IOException {
 517         writeSoundbank(new RIFFWriter(out, "sfbk"));
 518     }
 519 
 520     private void writeSoundbank(RIFFWriter writer) throws IOException {
 521         writeInfo(writer.writeList("INFO"));
 522         writeSdtaChunk(writer.writeList("sdta"));
 523         writePdtaChunk(writer.writeList("pdta"));
 524         writer.close();
 525     }
 526 
 527     private void writeInfoStringChunk(RIFFWriter writer, String name,
 528             String value) throws IOException {
 529         if (value == null)
 530             return;
 531         RIFFWriter chunk = writer.writeChunk(name);
 532         chunk.writeString(value);
 533         int len = value.getBytes("ascii").length;
 534         chunk.write(0);
 535         len++;
 536         if (len % 2 != 0)
 537             chunk.write(0);
 538     }
 539 
 540     private void writeInfo(RIFFWriter writer) throws IOException {
 541         if (this.targetEngine == null)
 542             this.targetEngine = "EMU8000";
 543         if (this.name == null)
 544             this.name = "";
 545 
 546         RIFFWriter ifil_chunk = writer.writeChunk("ifil");
 547         ifil_chunk.writeUnsignedShort(this.major);
 548         ifil_chunk.writeUnsignedShort(this.minor);
 549         writeInfoStringChunk(writer, "isng", this.targetEngine);
 550         writeInfoStringChunk(writer, "INAM", this.name);
 551         writeInfoStringChunk(writer, "irom", this.romName);
 552         if (romVersionMajor != -1) {
 553             RIFFWriter iver_chunk = writer.writeChunk("iver");
 554             iver_chunk.writeUnsignedShort(this.romVersionMajor);
 555             iver_chunk.writeUnsignedShort(this.romVersionMinor);
 556         }
 557         writeInfoStringChunk(writer, "ICRD", this.creationDate);
 558         writeInfoStringChunk(writer, "IENG", this.engineers);
 559         writeInfoStringChunk(writer, "IPRD", this.product);
 560         writeInfoStringChunk(writer, "ICOP", this.copyright);
 561         writeInfoStringChunk(writer, "ICMT", this.comments);
 562         writeInfoStringChunk(writer, "ISFT", this.tools);
 563 
 564         writer.close();
 565     }
 566 
 567     private void writeSdtaChunk(RIFFWriter writer) throws IOException {
 568 
 569         byte[] pad = new byte[32];
 570 
 571         RIFFWriter smpl_chunk = writer.writeChunk("smpl");
 572         for (SF2Sample sample : samples) {
 573             ModelByteBuffer data = sample.getDataBuffer();
 574             data.writeTo(smpl_chunk);
 575             /*
 576             smpl_chunk.write(data.array(),
 577             data.arrayOffset(),
 578             data.capacity());
 579              */
 580             smpl_chunk.write(pad);
 581             smpl_chunk.write(pad);
 582         }
 583         if (major < 2)
 584             return;
 585         if (major == 2 && minor < 4)
 586             return;
 587 
 588 
 589         for (SF2Sample sample : samples) {
 590             ModelByteBuffer data24 = sample.getData24Buffer();
 591             if (data24 == null)
 592                 return;
 593         }
 594 
 595         RIFFWriter sm24_chunk = writer.writeChunk("sm24");
 596         for (SF2Sample sample : samples) {
 597             ModelByteBuffer data = sample.getData24Buffer();
 598             data.writeTo(sm24_chunk);
 599             /*
 600             sm24_chunk.write(data.array(),
 601             data.arrayOffset(),
 602             data.capacity());*/
 603             smpl_chunk.write(pad);
 604         }
 605     }
 606 
 607     private void writeModulators(RIFFWriter writer, List<SF2Modulator> modulators)
 608             throws IOException {
 609         for (SF2Modulator modulator : modulators) {
 610             writer.writeUnsignedShort(modulator.sourceOperator);
 611             writer.writeUnsignedShort(modulator.destinationOperator);
 612             writer.writeShort(modulator.amount);
 613             writer.writeUnsignedShort(modulator.amountSourceOperator);
 614             writer.writeUnsignedShort(modulator.transportOperator);
 615         }
 616     }
 617 
 618     private void writeGenerators(RIFFWriter writer, Map<Integer, Short> generators)
 619             throws IOException {
 620         Short keyrange = generators.get(SF2Region.GENERATOR_KEYRANGE);
 621         Short velrange = generators.get(SF2Region.GENERATOR_VELRANGE);
 622         if (keyrange != null) {
 623             writer.writeUnsignedShort(SF2Region.GENERATOR_KEYRANGE);
 624             writer.writeShort(keyrange);
 625         }
 626         if (velrange != null) {
 627             writer.writeUnsignedShort(SF2Region.GENERATOR_VELRANGE);
 628             writer.writeShort(velrange);
 629         }
 630         for (Map.Entry<Integer, Short> generator : generators.entrySet()) {
 631             if (generator.getKey() == SF2Region.GENERATOR_KEYRANGE)
 632                 continue;
 633             if (generator.getKey() == SF2Region.GENERATOR_VELRANGE)
 634                 continue;
 635             writer.writeUnsignedShort(generator.getKey());
 636             writer.writeShort(generator.getValue());
 637         }
 638     }
 639 
 640     private void writePdtaChunk(RIFFWriter writer) throws IOException {
 641 
 642         RIFFWriter phdr_chunk = writer.writeChunk("phdr");
 643         int phdr_zone_count = 0;
 644         for (SF2Instrument preset : this.instruments) {
 645             phdr_chunk.writeString(preset.name, 20);
 646             phdr_chunk.writeUnsignedShort(preset.preset);
 647             phdr_chunk.writeUnsignedShort(preset.bank);
 648             phdr_chunk.writeUnsignedShort(phdr_zone_count);
 649             if (preset.getGlobalRegion() != null)
 650                 phdr_zone_count += 1;
 651             phdr_zone_count += preset.getRegions().size();
 652             phdr_chunk.writeUnsignedInt(preset.library);
 653             phdr_chunk.writeUnsignedInt(preset.genre);
 654             phdr_chunk.writeUnsignedInt(preset.morphology);
 655         }
 656         phdr_chunk.writeString("EOP", 20);
 657         phdr_chunk.writeUnsignedShort(0);
 658         phdr_chunk.writeUnsignedShort(0);
 659         phdr_chunk.writeUnsignedShort(phdr_zone_count);
 660         phdr_chunk.writeUnsignedInt(0);
 661         phdr_chunk.writeUnsignedInt(0);
 662         phdr_chunk.writeUnsignedInt(0);
 663 
 664 
 665         RIFFWriter pbag_chunk = writer.writeChunk("pbag");
 666         int pbag_gencount = 0;
 667         int pbag_modcount = 0;
 668         for (SF2Instrument preset : this.instruments) {
 669             if (preset.getGlobalRegion() != null) {
 670                 pbag_chunk.writeUnsignedShort(pbag_gencount);
 671                 pbag_chunk.writeUnsignedShort(pbag_modcount);
 672                 pbag_gencount += preset.getGlobalRegion().getGenerators().size();
 673                 pbag_modcount += preset.getGlobalRegion().getModulators().size();
 674             }
 675             for (SF2InstrumentRegion region : preset.getRegions()) {
 676                 pbag_chunk.writeUnsignedShort(pbag_gencount);
 677                 pbag_chunk.writeUnsignedShort(pbag_modcount);
 678                 if (layers.indexOf(region.layer) != -1) {
 679                     // One generator is used to reference to instrument record
 680                     pbag_gencount += 1;
 681                 }
 682                 pbag_gencount += region.getGenerators().size();
 683                 pbag_modcount += region.getModulators().size();
 684 
 685             }
 686         }
 687         pbag_chunk.writeUnsignedShort(pbag_gencount);
 688         pbag_chunk.writeUnsignedShort(pbag_modcount);
 689 
 690         RIFFWriter pmod_chunk = writer.writeChunk("pmod");
 691         for (SF2Instrument preset : this.instruments) {
 692             if (preset.getGlobalRegion() != null) {
 693                 writeModulators(pmod_chunk,
 694                         preset.getGlobalRegion().getModulators());
 695             }
 696             for (SF2InstrumentRegion region : preset.getRegions())
 697                 writeModulators(pmod_chunk, region.getModulators());
 698         }
 699         pmod_chunk.write(new byte[10]);
 700 
 701         RIFFWriter pgen_chunk = writer.writeChunk("pgen");
 702         for (SF2Instrument preset : this.instruments) {
 703             if (preset.getGlobalRegion() != null) {
 704                 writeGenerators(pgen_chunk,
 705                         preset.getGlobalRegion().getGenerators());
 706             }
 707             for (SF2InstrumentRegion region : preset.getRegions()) {
 708                 writeGenerators(pgen_chunk, region.getGenerators());
 709                 int ix = layers.indexOf(region.layer);
 710                 if (ix != -1) {
 711                     pgen_chunk.writeUnsignedShort(SF2Region.GENERATOR_INSTRUMENT);
 712                     pgen_chunk.writeShort((short) ix);
 713                 }
 714             }
 715         }
 716         pgen_chunk.write(new byte[4]);
 717 
 718         RIFFWriter inst_chunk = writer.writeChunk("inst");
 719         int inst_zone_count = 0;
 720         for (SF2Layer instrument : this.layers) {
 721             inst_chunk.writeString(instrument.name, 20);
 722             inst_chunk.writeUnsignedShort(inst_zone_count);
 723             if (instrument.getGlobalRegion() != null)
 724                 inst_zone_count += 1;
 725             inst_zone_count += instrument.getRegions().size();
 726         }
 727         inst_chunk.writeString("EOI", 20);
 728         inst_chunk.writeUnsignedShort(inst_zone_count);
 729 
 730 
 731         RIFFWriter ibag_chunk = writer.writeChunk("ibag");
 732         int ibag_gencount = 0;
 733         int ibag_modcount = 0;
 734         for (SF2Layer instrument : this.layers) {
 735             if (instrument.getGlobalRegion() != null) {
 736                 ibag_chunk.writeUnsignedShort(ibag_gencount);
 737                 ibag_chunk.writeUnsignedShort(ibag_modcount);
 738                 ibag_gencount
 739                         += instrument.getGlobalRegion().getGenerators().size();
 740                 ibag_modcount
 741                         += instrument.getGlobalRegion().getModulators().size();
 742             }
 743             for (SF2LayerRegion region : instrument.getRegions()) {
 744                 ibag_chunk.writeUnsignedShort(ibag_gencount);
 745                 ibag_chunk.writeUnsignedShort(ibag_modcount);
 746                 if (samples.indexOf(region.sample) != -1) {
 747                     // One generator is used to reference to instrument record
 748                     ibag_gencount += 1;
 749                 }
 750                 ibag_gencount += region.getGenerators().size();
 751                 ibag_modcount += region.getModulators().size();
 752 
 753             }
 754         }
 755         ibag_chunk.writeUnsignedShort(ibag_gencount);
 756         ibag_chunk.writeUnsignedShort(ibag_modcount);
 757 
 758 
 759         RIFFWriter imod_chunk = writer.writeChunk("imod");
 760         for (SF2Layer instrument : this.layers) {
 761             if (instrument.getGlobalRegion() != null) {
 762                 writeModulators(imod_chunk,
 763                         instrument.getGlobalRegion().getModulators());
 764             }
 765             for (SF2LayerRegion region : instrument.getRegions())
 766                 writeModulators(imod_chunk, region.getModulators());
 767         }
 768         imod_chunk.write(new byte[10]);
 769 
 770         RIFFWriter igen_chunk = writer.writeChunk("igen");
 771         for (SF2Layer instrument : this.layers) {
 772             if (instrument.getGlobalRegion() != null) {
 773                 writeGenerators(igen_chunk,
 774                         instrument.getGlobalRegion().getGenerators());
 775             }
 776             for (SF2LayerRegion region : instrument.getRegions()) {
 777                 writeGenerators(igen_chunk, region.getGenerators());
 778                 int ix = samples.indexOf(region.sample);
 779                 if (ix != -1) {
 780                     igen_chunk.writeUnsignedShort(SF2Region.GENERATOR_SAMPLEID);
 781                     igen_chunk.writeShort((short) ix);
 782                 }
 783             }
 784         }
 785         igen_chunk.write(new byte[4]);
 786 
 787 
 788         RIFFWriter shdr_chunk = writer.writeChunk("shdr");
 789         long sample_pos = 0;
 790         for (SF2Sample sample : samples) {
 791             shdr_chunk.writeString(sample.name, 20);
 792             long start = sample_pos;
 793             sample_pos += sample.data.capacity() / 2;
 794             long end = sample_pos;
 795             long startLoop = sample.startLoop + start;
 796             long endLoop = sample.endLoop + start;
 797             if (startLoop < start)
 798                 startLoop = start;
 799             if (endLoop > end)
 800                 endLoop = end;
 801             shdr_chunk.writeUnsignedInt(start);
 802             shdr_chunk.writeUnsignedInt(end);
 803             shdr_chunk.writeUnsignedInt(startLoop);
 804             shdr_chunk.writeUnsignedInt(endLoop);
 805             shdr_chunk.writeUnsignedInt(sample.sampleRate);
 806             shdr_chunk.writeUnsignedByte(sample.originalPitch);
 807             shdr_chunk.writeByte(sample.pitchCorrection);
 808             shdr_chunk.writeUnsignedShort(sample.sampleLink);
 809             shdr_chunk.writeUnsignedShort(sample.sampleType);
 810             sample_pos += 32;
 811         }
 812         shdr_chunk.writeString("EOS", 20);
 813         shdr_chunk.write(new byte[26]);
 814 
 815     }
 816 
 817     public String getName() {
 818         return name;
 819     }
 820 
 821     public String getVersion() {
 822         return major + "." + minor;
 823     }
 824 
 825     public String getVendor() {
 826         return engineers;
 827     }
 828 
 829     public String getDescription() {
 830         return comments;
 831     }
 832 
 833     public void setName(String s) {
 834         name = s;
 835     }
 836 
 837     public void setVendor(String s) {
 838         engineers = s;
 839     }
 840 
 841     public void setDescription(String s) {
 842         comments = s;
 843     }
 844 
 845     public SoundbankResource[] getResources() {
 846         SoundbankResource[] resources
 847                 = new SoundbankResource[layers.size() + samples.size()];
 848         int j = 0;
 849         for (int i = 0; i < layers.size(); i++)
 850             resources[j++] = layers.get(i);
 851         for (int i = 0; i < samples.size(); i++)
 852             resources[j++] = samples.get(i);
 853         return resources;
 854     }
 855 
 856     public SF2Instrument[] getInstruments() {
 857         SF2Instrument[] inslist_array
 858                 = instruments.toArray(new SF2Instrument[instruments.size()]);
 859         Arrays.sort(inslist_array, new ModelInstrumentComparator());
 860         return inslist_array;
 861     }
 862 
 863     public SF2Layer[] getLayers() {
 864         return layers.toArray(new SF2Layer[layers.size()]);
 865     }
 866 
 867     public SF2Sample[] getSamples() {
 868         return samples.toArray(new SF2Sample[samples.size()]);
 869     }
 870 
 871     public Instrument getInstrument(Patch patch) {
 872         int program = patch.getProgram();
 873         int bank = patch.getBank();
 874         boolean percussion = false;
 875         if (patch instanceof ModelPatch)
 876             percussion = ((ModelPatch)patch).isPercussion();
 877         for (Instrument instrument : instruments) {
 878             Patch patch2 = instrument.getPatch();
 879             int program2 = patch2.getProgram();
 880             int bank2 = patch2.getBank();
 881             if (program == program2 && bank == bank2) {
 882                 boolean percussion2 = false;
 883                 if (patch2 instanceof ModelPatch)
 884                     percussion2 = ((ModelPatch) patch2).isPercussion();
 885                 if (percussion == percussion2)
 886                     return instrument;
 887             }
 888         }
 889         return null;
 890     }
 891 
 892     public String getCreationDate() {
 893         return creationDate;
 894     }
 895 
 896     public void setCreationDate(String creationDate) {
 897         this.creationDate = creationDate;
 898     }
 899 
 900     public String getProduct() {
 901         return product;
 902     }
 903 
 904     public void setProduct(String product) {
 905         this.product = product;
 906     }
 907 
 908     public String getRomName() {
 909         return romName;
 910     }
 911 
 912     public void setRomName(String romName) {
 913         this.romName = romName;
 914     }
 915 
 916     public int getRomVersionMajor() {
 917         return romVersionMajor;
 918     }
 919 
 920     public void setRomVersionMajor(int romVersionMajor) {
 921         this.romVersionMajor = romVersionMajor;
 922     }
 923 
 924     public int getRomVersionMinor() {
 925         return romVersionMinor;
 926     }
 927 
 928     public void setRomVersionMinor(int romVersionMinor) {
 929         this.romVersionMinor = romVersionMinor;
 930     }
 931 
 932     public String getTargetEngine() {
 933         return targetEngine;
 934     }
 935 
 936     public void setTargetEngine(String targetEngine) {
 937         this.targetEngine = targetEngine;
 938     }
 939 
 940     public String getTools() {
 941         return tools;
 942     }
 943 
 944     public void setTools(String tools) {
 945         this.tools = tools;
 946     }
 947 
 948     public void addResource(SoundbankResource resource) {
 949         if (resource instanceof SF2Instrument)
 950             instruments.add((SF2Instrument)resource);
 951         if (resource instanceof SF2Layer)
 952             layers.add((SF2Layer)resource);
 953         if (resource instanceof SF2Sample)
 954             samples.add((SF2Sample)resource);
 955     }
 956 
 957     public void removeResource(SoundbankResource resource) {
 958         if (resource instanceof SF2Instrument)
 959             instruments.remove((SF2Instrument)resource);
 960         if (resource instanceof SF2Layer)
 961             layers.remove((SF2Layer)resource);
 962         if (resource instanceof SF2Sample)
 963             samples.remove((SF2Sample)resource);
 964     }
 965 
 966     public void addInstrument(SF2Instrument resource) {
 967         instruments.add(resource);
 968     }
 969 
 970     public void removeInstrument(SF2Instrument resource) {
 971         instruments.remove(resource);
 972     }
 973 }