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