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 }