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