829N/A/*
6321N/A * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
829N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
829N/A *
829N/A * This code is free software; you can redistribute it and/or modify it
829N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
829N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
829N/A *
829N/A * This code is distributed in the hope that it will be useful, but WITHOUT
829N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
829N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
829N/A * version 2 for more details (a copy is included in the LICENSE file that
829N/A * accompanied this code).
829N/A *
829N/A * You should have received a copy of the GNU General Public License version
829N/A * 2 along with this work; if not, write to the Free Software Foundation,
829N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
829N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
829N/A */
829N/Apackage com.sun.media.sound;
829N/A
829N/Aimport java.io.File;
829N/Aimport java.io.FileInputStream;
829N/Aimport java.io.IOException;
829N/Aimport java.io.InputStream;
829N/Aimport java.io.OutputStream;
829N/Aimport java.net.URL;
829N/Aimport java.util.ArrayList;
829N/Aimport java.util.Arrays;
829N/Aimport java.util.Iterator;
829N/Aimport java.util.List;
829N/Aimport java.util.Map;
829N/A
829N/Aimport javax.sound.midi.Instrument;
829N/Aimport javax.sound.midi.Patch;
829N/Aimport javax.sound.midi.Soundbank;
829N/Aimport javax.sound.midi.SoundbankResource;
829N/A
829N/A/**
829N/A * A SoundFont 2.04 soundbank reader.
829N/A *
829N/A * Based on SoundFont 2.04 specification from:
829N/A * <p> http://developer.creative.com <br>
829N/A * http://www.soundfont.com/ ;
829N/A *
829N/A * @author Karl Helgason
829N/A */
6321N/Apublic final class SF2Soundbank implements Soundbank {
829N/A
829N/A // version of the Sound Font RIFF file
6321N/A int major = 2;
6321N/A int minor = 1;
829N/A // target Sound Engine
6321N/A String targetEngine = "EMU8000";
829N/A // Sound Font Bank Name
6321N/A String name = "untitled";
829N/A // Sound ROM Name
6321N/A String romName = null;
829N/A // Sound ROM Version
6321N/A int romVersionMajor = -1;
6321N/A int romVersionMinor = -1;
829N/A // Date of Creation of the Bank
6321N/A String creationDate = null;
829N/A // Sound Designers and Engineers for the Bank
6321N/A String engineers = null;
829N/A // Product for which the Bank was intended
6321N/A String product = null;
829N/A // Copyright message
6321N/A String copyright = null;
829N/A // Comments
6321N/A String comments = null;
829N/A // The SoundFont tools used to create and alter the bank
6321N/A String tools = null;
829N/A // The Sample Data loaded from the SoundFont
829N/A private ModelByteBuffer sampleData = null;
829N/A private ModelByteBuffer sampleData24 = null;
829N/A private File sampleFile = null;
829N/A private boolean largeFormat = false;
6321N/A private final List<SF2Instrument> instruments = new ArrayList<SF2Instrument>();
6321N/A private final List<SF2Layer> layers = new ArrayList<SF2Layer>();
6321N/A private final List<SF2Sample> samples = new ArrayList<SF2Sample>();
829N/A
829N/A public SF2Soundbank() {
829N/A }
829N/A
829N/A public SF2Soundbank(URL url) throws IOException {
829N/A
829N/A InputStream is = url.openStream();
829N/A try {
829N/A readSoundbank(is);
829N/A } finally {
829N/A is.close();
829N/A }
829N/A }
829N/A
829N/A public SF2Soundbank(File file) throws IOException {
829N/A largeFormat = true;
829N/A sampleFile = file;
829N/A InputStream is = new FileInputStream(file);
829N/A try {
829N/A readSoundbank(is);
829N/A } finally {
829N/A is.close();
829N/A }
829N/A }
829N/A
829N/A public SF2Soundbank(InputStream inputstream) throws IOException {
829N/A readSoundbank(inputstream);
829N/A }
829N/A
829N/A private void readSoundbank(InputStream inputstream) throws IOException {
829N/A RIFFReader riff = new RIFFReader(inputstream);
829N/A if (!riff.getFormat().equals("RIFF")) {
829N/A throw new RIFFInvalidFormatException(
829N/A "Input stream is not a valid RIFF stream!");
829N/A }
829N/A if (!riff.getType().equals("sfbk")) {
829N/A throw new RIFFInvalidFormatException(
829N/A "Input stream is not a valid SoundFont!");
829N/A }
829N/A while (riff.hasNextChunk()) {
829N/A RIFFReader chunk = riff.nextChunk();
829N/A if (chunk.getFormat().equals("LIST")) {
829N/A if (chunk.getType().equals("INFO"))
829N/A readInfoChunk(chunk);
829N/A if (chunk.getType().equals("sdta"))
829N/A readSdtaChunk(chunk);
829N/A if (chunk.getType().equals("pdta"))
829N/A readPdtaChunk(chunk);
829N/A }
829N/A }
829N/A }
829N/A
829N/A private void readInfoChunk(RIFFReader riff) throws IOException {
829N/A while (riff.hasNextChunk()) {
829N/A RIFFReader chunk = riff.nextChunk();
829N/A String format = chunk.getFormat();
829N/A if (format.equals("ifil")) {
829N/A major = chunk.readUnsignedShort();
829N/A minor = chunk.readUnsignedShort();
829N/A } else if (format.equals("isng")) {
829N/A this.targetEngine = chunk.readString(chunk.available());
829N/A } else if (format.equals("INAM")) {
829N/A this.name = chunk.readString(chunk.available());
829N/A } else if (format.equals("irom")) {
829N/A this.romName = chunk.readString(chunk.available());
829N/A } else if (format.equals("iver")) {
829N/A romVersionMajor = chunk.readUnsignedShort();
829N/A romVersionMinor = chunk.readUnsignedShort();
829N/A } else if (format.equals("ICRD")) {
829N/A this.creationDate = chunk.readString(chunk.available());
829N/A } else if (format.equals("IENG")) {
829N/A this.engineers = chunk.readString(chunk.available());
829N/A } else if (format.equals("IPRD")) {
829N/A this.product = chunk.readString(chunk.available());
829N/A } else if (format.equals("ICOP")) {
829N/A this.copyright = chunk.readString(chunk.available());
829N/A } else if (format.equals("ICMT")) {
829N/A this.comments = chunk.readString(chunk.available());
829N/A } else if (format.equals("ISFT")) {
829N/A this.tools = chunk.readString(chunk.available());
829N/A }
829N/A
829N/A }
829N/A }
829N/A
829N/A private void readSdtaChunk(RIFFReader riff) throws IOException {
829N/A while (riff.hasNextChunk()) {
829N/A RIFFReader chunk = riff.nextChunk();
829N/A if (chunk.getFormat().equals("smpl")) {
829N/A if (!largeFormat) {
829N/A byte[] sampleData = new byte[chunk.available()];
829N/A
829N/A int read = 0;
829N/A int avail = chunk.available();
829N/A while (read != avail) {
829N/A if (avail - read > 65536) {
829N/A chunk.readFully(sampleData, read, 65536);
829N/A read += 65536;
829N/A } else {
829N/A chunk.readFully(sampleData, read, avail - read);
829N/A read = avail;
829N/A }
829N/A
829N/A }
829N/A this.sampleData = new ModelByteBuffer(sampleData);
829N/A //chunk.read(sampleData);
829N/A } else {
829N/A this.sampleData = new ModelByteBuffer(sampleFile,
829N/A chunk.getFilePointer(), chunk.available());
829N/A }
829N/A }
829N/A if (chunk.getFormat().equals("sm24")) {
829N/A if (!largeFormat) {
829N/A byte[] sampleData24 = new byte[chunk.available()];
829N/A //chunk.read(sampleData24);
829N/A
829N/A int read = 0;
829N/A int avail = chunk.available();
829N/A while (read != avail) {
829N/A if (avail - read > 65536) {
829N/A chunk.readFully(sampleData24, read, 65536);
829N/A read += 65536;
829N/A } else {
829N/A chunk.readFully(sampleData24, read, avail - read);
829N/A read = avail;
829N/A }
829N/A
829N/A }
829N/A this.sampleData24 = new ModelByteBuffer(sampleData24);
829N/A } else {
829N/A this.sampleData24 = new ModelByteBuffer(sampleFile,
829N/A chunk.getFilePointer(), chunk.available());
829N/A }
829N/A
829N/A }
829N/A }
829N/A }
829N/A
829N/A private void readPdtaChunk(RIFFReader riff) throws IOException {
829N/A
829N/A List<SF2Instrument> presets = new ArrayList<SF2Instrument>();
829N/A List<Integer> presets_bagNdx = new ArrayList<Integer>();
829N/A List<SF2InstrumentRegion> presets_splits_gen
829N/A = new ArrayList<SF2InstrumentRegion>();
829N/A List<SF2InstrumentRegion> presets_splits_mod
829N/A = new ArrayList<SF2InstrumentRegion>();
829N/A
829N/A List<SF2Layer> instruments = new ArrayList<SF2Layer>();
829N/A List<Integer> instruments_bagNdx = new ArrayList<Integer>();
829N/A List<SF2LayerRegion> instruments_splits_gen
829N/A = new ArrayList<SF2LayerRegion>();
829N/A List<SF2LayerRegion> instruments_splits_mod
829N/A = new ArrayList<SF2LayerRegion>();
829N/A
829N/A while (riff.hasNextChunk()) {
829N/A RIFFReader chunk = riff.nextChunk();
829N/A String format = chunk.getFormat();
829N/A if (format.equals("phdr")) {
829N/A // Preset Header / Instrument
829N/A if (chunk.available() % 38 != 0)
829N/A throw new RIFFInvalidDataException();
829N/A int count = chunk.available() / 38;
829N/A for (int i = 0; i < count; i++) {
829N/A SF2Instrument preset = new SF2Instrument(this);
829N/A preset.name = chunk.readString(20);
829N/A preset.preset = chunk.readUnsignedShort();
829N/A preset.bank = chunk.readUnsignedShort();
829N/A presets_bagNdx.add(chunk.readUnsignedShort());
829N/A preset.library = chunk.readUnsignedInt();
829N/A preset.genre = chunk.readUnsignedInt();
829N/A preset.morphology = chunk.readUnsignedInt();
829N/A presets.add(preset);
829N/A if (i != count - 1)
829N/A this.instruments.add(preset);
829N/A }
829N/A } else if (format.equals("pbag")) {
829N/A // Preset Zones / Instruments splits
829N/A if (chunk.available() % 4 != 0)
829N/A throw new RIFFInvalidDataException();
829N/A int count = chunk.available() / 4;
829N/A
829N/A // Skip first record
829N/A {
829N/A int gencount = chunk.readUnsignedShort();
829N/A int modcount = chunk.readUnsignedShort();
829N/A while (presets_splits_gen.size() < gencount)
829N/A presets_splits_gen.add(null);
829N/A while (presets_splits_mod.size() < modcount)
829N/A presets_splits_mod.add(null);
829N/A count--;
829N/A }
829N/A
829N/A int offset = presets_bagNdx.get(0);
829N/A // Offset should be 0 (but just case)
829N/A for (int i = 0; i < offset; i++) {
829N/A if (count == 0)
829N/A throw new RIFFInvalidDataException();
829N/A int gencount = chunk.readUnsignedShort();
829N/A int modcount = chunk.readUnsignedShort();
829N/A while (presets_splits_gen.size() < gencount)
829N/A presets_splits_gen.add(null);
829N/A while (presets_splits_mod.size() < modcount)
829N/A presets_splits_mod.add(null);
829N/A count--;
829N/A }
829N/A
829N/A for (int i = 0; i < presets_bagNdx.size() - 1; i++) {
829N/A int zone_count = presets_bagNdx.get(i + 1)
829N/A - presets_bagNdx.get(i);
829N/A SF2Instrument preset = presets.get(i);
829N/A for (int ii = 0; ii < zone_count; ii++) {
829N/A if (count == 0)
829N/A throw new RIFFInvalidDataException();
829N/A int gencount = chunk.readUnsignedShort();
829N/A int modcount = chunk.readUnsignedShort();
829N/A SF2InstrumentRegion split = new SF2InstrumentRegion();
829N/A preset.regions.add(split);
829N/A while (presets_splits_gen.size() < gencount)
829N/A presets_splits_gen.add(split);
829N/A while (presets_splits_mod.size() < modcount)
829N/A presets_splits_mod.add(split);
829N/A count--;
829N/A }
829N/A }
829N/A } else if (format.equals("pmod")) {
829N/A // Preset Modulators / Split Modulators
829N/A for (int i = 0; i < presets_splits_mod.size(); i++) {
829N/A SF2Modulator modulator = new SF2Modulator();
829N/A modulator.sourceOperator = chunk.readUnsignedShort();
829N/A modulator.destinationOperator = chunk.readUnsignedShort();
829N/A modulator.amount = chunk.readShort();
829N/A modulator.amountSourceOperator = chunk.readUnsignedShort();
829N/A modulator.transportOperator = chunk.readUnsignedShort();
829N/A SF2InstrumentRegion split = presets_splits_mod.get(i);
829N/A if (split != null)
829N/A split.modulators.add(modulator);
829N/A }
829N/A } else if (format.equals("pgen")) {
829N/A // Preset Generators / Split Generators
829N/A for (int i = 0; i < presets_splits_gen.size(); i++) {
829N/A int operator = chunk.readUnsignedShort();
829N/A short amount = chunk.readShort();
829N/A SF2InstrumentRegion split = presets_splits_gen.get(i);
829N/A if (split != null)
829N/A split.generators.put(operator, amount);
829N/A }
829N/A } else if (format.equals("inst")) {
829N/A // Instrument Header / Layers
829N/A if (chunk.available() % 22 != 0)
829N/A throw new RIFFInvalidDataException();
829N/A int count = chunk.available() / 22;
829N/A for (int i = 0; i < count; i++) {
829N/A SF2Layer layer = new SF2Layer(this);
829N/A layer.name = chunk.readString(20);
829N/A instruments_bagNdx.add(chunk.readUnsignedShort());
829N/A instruments.add(layer);
829N/A if (i != count - 1)
829N/A this.layers.add(layer);
829N/A }
829N/A } else if (format.equals("ibag")) {
829N/A // Instrument Zones / Layer splits
829N/A if (chunk.available() % 4 != 0)
829N/A throw new RIFFInvalidDataException();
829N/A int count = chunk.available() / 4;
829N/A
829N/A // Skip first record
829N/A {
829N/A int gencount = chunk.readUnsignedShort();
829N/A int modcount = chunk.readUnsignedShort();
829N/A while (instruments_splits_gen.size() < gencount)
829N/A instruments_splits_gen.add(null);
829N/A while (instruments_splits_mod.size() < modcount)
829N/A instruments_splits_mod.add(null);
829N/A count--;
829N/A }
829N/A
829N/A int offset = instruments_bagNdx.get(0);
829N/A // Offset should be 0 (but just case)
829N/A for (int i = 0; i < offset; i++) {
829N/A if (count == 0)
829N/A throw new RIFFInvalidDataException();
829N/A int gencount = chunk.readUnsignedShort();
829N/A int modcount = chunk.readUnsignedShort();
829N/A while (instruments_splits_gen.size() < gencount)
829N/A instruments_splits_gen.add(null);
829N/A while (instruments_splits_mod.size() < modcount)
829N/A instruments_splits_mod.add(null);
829N/A count--;
829N/A }
829N/A
829N/A for (int i = 0; i < instruments_bagNdx.size() - 1; i++) {
829N/A int zone_count = instruments_bagNdx.get(i + 1) - instruments_bagNdx.get(i);
829N/A SF2Layer layer = layers.get(i);
829N/A for (int ii = 0; ii < zone_count; ii++) {
829N/A if (count == 0)
829N/A throw new RIFFInvalidDataException();
829N/A int gencount = chunk.readUnsignedShort();
829N/A int modcount = chunk.readUnsignedShort();
829N/A SF2LayerRegion split = new SF2LayerRegion();
829N/A layer.regions.add(split);
829N/A while (instruments_splits_gen.size() < gencount)
829N/A instruments_splits_gen.add(split);
829N/A while (instruments_splits_mod.size() < modcount)
829N/A instruments_splits_mod.add(split);
829N/A count--;
829N/A }
829N/A }
829N/A
829N/A } else if (format.equals("imod")) {
829N/A // Instrument Modulators / Split Modulators
829N/A for (int i = 0; i < instruments_splits_mod.size(); i++) {
829N/A SF2Modulator modulator = new SF2Modulator();
829N/A modulator.sourceOperator = chunk.readUnsignedShort();
829N/A modulator.destinationOperator = chunk.readUnsignedShort();
829N/A modulator.amount = chunk.readShort();
829N/A modulator.amountSourceOperator = chunk.readUnsignedShort();
829N/A modulator.transportOperator = chunk.readUnsignedShort();
829N/A SF2LayerRegion split = instruments_splits_gen.get(i);
829N/A if (split != null)
829N/A split.modulators.add(modulator);
829N/A }
829N/A } else if (format.equals("igen")) {
829N/A // Instrument Generators / Split Generators
829N/A for (int i = 0; i < instruments_splits_gen.size(); i++) {
829N/A int operator = chunk.readUnsignedShort();
829N/A short amount = chunk.readShort();
829N/A SF2LayerRegion split = instruments_splits_gen.get(i);
829N/A if (split != null)
829N/A split.generators.put(operator, amount);
829N/A }
829N/A } else if (format.equals("shdr")) {
829N/A // Sample Headers
829N/A if (chunk.available() % 46 != 0)
829N/A throw new RIFFInvalidDataException();
829N/A int count = chunk.available() / 46;
829N/A for (int i = 0; i < count; i++) {
829N/A SF2Sample sample = new SF2Sample(this);
829N/A sample.name = chunk.readString(20);
829N/A long start = chunk.readUnsignedInt();
829N/A long end = chunk.readUnsignedInt();
829N/A sample.data = sampleData.subbuffer(start * 2, end * 2, true);
829N/A if (sampleData24 != null)
829N/A sample.data24 = sampleData24.subbuffer(start, end, true);
829N/A /*
829N/A sample.data = new ModelByteBuffer(sampleData, (int)(start*2),
829N/A (int)((end - start)*2));
829N/A if (sampleData24 != null)
829N/A sample.data24 = new ModelByteBuffer(sampleData24,
829N/A (int)start, (int)(end - start));
829N/A */
829N/A sample.startLoop = chunk.readUnsignedInt() - start;
829N/A sample.endLoop = chunk.readUnsignedInt() - start;
829N/A if (sample.startLoop < 0)
829N/A sample.startLoop = -1;
829N/A if (sample.endLoop < 0)
829N/A sample.endLoop = -1;
829N/A sample.sampleRate = chunk.readUnsignedInt();
829N/A sample.originalPitch = chunk.readUnsignedByte();
829N/A sample.pitchCorrection = chunk.readByte();
829N/A sample.sampleLink = chunk.readUnsignedShort();
829N/A sample.sampleType = chunk.readUnsignedShort();
829N/A if (i != count - 1)
829N/A this.samples.add(sample);
829N/A }
829N/A }
829N/A }
829N/A
829N/A Iterator<SF2Layer> liter = this.layers.iterator();
829N/A while (liter.hasNext()) {
829N/A SF2Layer layer = liter.next();
829N/A Iterator<SF2LayerRegion> siter = layer.regions.iterator();
829N/A SF2Region globalsplit = null;
829N/A while (siter.hasNext()) {
829N/A SF2LayerRegion split = siter.next();
829N/A if (split.generators.get(SF2LayerRegion.GENERATOR_SAMPLEID) != null) {
829N/A int sampleid = split.generators.get(
829N/A SF2LayerRegion.GENERATOR_SAMPLEID);
829N/A split.generators.remove(SF2LayerRegion.GENERATOR_SAMPLEID);
829N/A split.sample = samples.get(sampleid);
829N/A } else {
829N/A globalsplit = split;
829N/A }
829N/A }
829N/A if (globalsplit != null) {
829N/A layer.getRegions().remove(globalsplit);
829N/A SF2GlobalRegion gsplit = new SF2GlobalRegion();
829N/A gsplit.generators = globalsplit.generators;
829N/A gsplit.modulators = globalsplit.modulators;
829N/A layer.setGlobalZone(gsplit);
829N/A }
829N/A }
829N/A
829N/A
829N/A Iterator<SF2Instrument> iiter = this.instruments.iterator();
829N/A while (iiter.hasNext()) {
829N/A SF2Instrument instrument = iiter.next();
829N/A Iterator<SF2InstrumentRegion> siter = instrument.regions.iterator();
829N/A SF2Region globalsplit = null;
829N/A while (siter.hasNext()) {
829N/A SF2InstrumentRegion split = siter.next();
829N/A if (split.generators.get(SF2LayerRegion.GENERATOR_INSTRUMENT) != null) {
829N/A int instrumentid = split.generators.get(
829N/A SF2InstrumentRegion.GENERATOR_INSTRUMENT);
829N/A split.generators.remove(SF2LayerRegion.GENERATOR_INSTRUMENT);
829N/A split.layer = layers.get(instrumentid);
829N/A } else {
829N/A globalsplit = split;
829N/A }
829N/A }
829N/A
829N/A if (globalsplit != null) {
829N/A instrument.getRegions().remove(globalsplit);
829N/A SF2GlobalRegion gsplit = new SF2GlobalRegion();
829N/A gsplit.generators = globalsplit.generators;
829N/A gsplit.modulators = globalsplit.modulators;
829N/A instrument.setGlobalZone(gsplit);
829N/A }
829N/A }
829N/A
829N/A }
829N/A
829N/A public void save(String name) throws IOException {
829N/A writeSoundbank(new RIFFWriter(name, "sfbk"));
829N/A }
829N/A
829N/A public void save(File file) throws IOException {
829N/A writeSoundbank(new RIFFWriter(file, "sfbk"));
829N/A }
829N/A
829N/A public void save(OutputStream out) throws IOException {
829N/A writeSoundbank(new RIFFWriter(out, "sfbk"));
829N/A }
829N/A
829N/A private void writeSoundbank(RIFFWriter writer) throws IOException {
829N/A writeInfo(writer.writeList("INFO"));
829N/A writeSdtaChunk(writer.writeList("sdta"));
829N/A writePdtaChunk(writer.writeList("pdta"));
829N/A writer.close();
829N/A }
829N/A
829N/A private void writeInfoStringChunk(RIFFWriter writer, String name,
829N/A String value) throws IOException {
829N/A if (value == null)
829N/A return;
829N/A RIFFWriter chunk = writer.writeChunk(name);
829N/A chunk.writeString(value);
829N/A int len = value.getBytes("ascii").length;
829N/A chunk.write(0);
829N/A len++;
829N/A if (len % 2 != 0)
829N/A chunk.write(0);
829N/A }
829N/A
829N/A private void writeInfo(RIFFWriter writer) throws IOException {
829N/A if (this.targetEngine == null)
829N/A this.targetEngine = "EMU8000";
829N/A if (this.name == null)
829N/A this.name = "";
829N/A
829N/A RIFFWriter ifil_chunk = writer.writeChunk("ifil");
829N/A ifil_chunk.writeUnsignedShort(this.major);
829N/A ifil_chunk.writeUnsignedShort(this.minor);
829N/A writeInfoStringChunk(writer, "isng", this.targetEngine);
829N/A writeInfoStringChunk(writer, "INAM", this.name);
829N/A writeInfoStringChunk(writer, "irom", this.romName);
829N/A if (romVersionMajor != -1) {
829N/A RIFFWriter iver_chunk = writer.writeChunk("iver");
829N/A iver_chunk.writeUnsignedShort(this.romVersionMajor);
829N/A iver_chunk.writeUnsignedShort(this.romVersionMinor);
829N/A }
829N/A writeInfoStringChunk(writer, "ICRD", this.creationDate);
829N/A writeInfoStringChunk(writer, "IENG", this.engineers);
829N/A writeInfoStringChunk(writer, "IPRD", this.product);
829N/A writeInfoStringChunk(writer, "ICOP", this.copyright);
829N/A writeInfoStringChunk(writer, "ICMT", this.comments);
829N/A writeInfoStringChunk(writer, "ISFT", this.tools);
829N/A
829N/A writer.close();
829N/A }
829N/A
829N/A private void writeSdtaChunk(RIFFWriter writer) throws IOException {
829N/A
829N/A byte[] pad = new byte[32];
829N/A
829N/A RIFFWriter smpl_chunk = writer.writeChunk("smpl");
829N/A for (SF2Sample sample : samples) {
829N/A ModelByteBuffer data = sample.getDataBuffer();
829N/A data.writeTo(smpl_chunk);
829N/A /*
829N/A smpl_chunk.write(data.array(),
829N/A data.arrayOffset(),
829N/A data.capacity());
829N/A */
829N/A smpl_chunk.write(pad);
829N/A smpl_chunk.write(pad);
829N/A }
829N/A if (major < 2)
829N/A return;
829N/A if (major == 2 && minor < 4)
829N/A return;
829N/A
829N/A
829N/A for (SF2Sample sample : samples) {
829N/A ModelByteBuffer data24 = sample.getData24Buffer();
829N/A if (data24 == null)
829N/A return;
829N/A }
829N/A
829N/A RIFFWriter sm24_chunk = writer.writeChunk("sm24");
829N/A for (SF2Sample sample : samples) {
829N/A ModelByteBuffer data = sample.getData24Buffer();
829N/A data.writeTo(sm24_chunk);
829N/A /*
829N/A sm24_chunk.write(data.array(),
829N/A data.arrayOffset(),
829N/A data.capacity());*/
829N/A smpl_chunk.write(pad);
829N/A }
829N/A }
829N/A
829N/A private void writeModulators(RIFFWriter writer, List<SF2Modulator> modulators)
829N/A throws IOException {
829N/A for (SF2Modulator modulator : modulators) {
829N/A writer.writeUnsignedShort(modulator.sourceOperator);
829N/A writer.writeUnsignedShort(modulator.destinationOperator);
829N/A writer.writeShort(modulator.amount);
829N/A writer.writeUnsignedShort(modulator.amountSourceOperator);
829N/A writer.writeUnsignedShort(modulator.transportOperator);
829N/A }
829N/A }
829N/A
829N/A private void writeGenerators(RIFFWriter writer, Map<Integer, Short> generators)
829N/A throws IOException {
829N/A Short keyrange = (Short) generators.get(SF2Region.GENERATOR_KEYRANGE);
829N/A Short velrange = (Short) generators.get(SF2Region.GENERATOR_VELRANGE);
829N/A if (keyrange != null) {
829N/A writer.writeUnsignedShort(SF2Region.GENERATOR_KEYRANGE);
829N/A writer.writeShort(keyrange);
829N/A }
829N/A if (velrange != null) {
829N/A writer.writeUnsignedShort(SF2Region.GENERATOR_VELRANGE);
829N/A writer.writeShort(velrange);
829N/A }
829N/A for (Map.Entry<Integer, Short> generator : generators.entrySet()) {
829N/A if (generator.getKey() == SF2Region.GENERATOR_KEYRANGE)
829N/A continue;
829N/A if (generator.getKey() == SF2Region.GENERATOR_VELRANGE)
829N/A continue;
829N/A writer.writeUnsignedShort(generator.getKey());
829N/A writer.writeShort(generator.getValue());
829N/A }
829N/A }
829N/A
829N/A private void writePdtaChunk(RIFFWriter writer) throws IOException {
829N/A
829N/A RIFFWriter phdr_chunk = writer.writeChunk("phdr");
829N/A int phdr_zone_count = 0;
829N/A for (SF2Instrument preset : this.instruments) {
829N/A phdr_chunk.writeString(preset.name, 20);
829N/A phdr_chunk.writeUnsignedShort(preset.preset);
829N/A phdr_chunk.writeUnsignedShort(preset.bank);
829N/A phdr_chunk.writeUnsignedShort(phdr_zone_count);
829N/A if (preset.getGlobalRegion() != null)
829N/A phdr_zone_count += 1;
829N/A phdr_zone_count += preset.getRegions().size();
829N/A phdr_chunk.writeUnsignedInt(preset.library);
829N/A phdr_chunk.writeUnsignedInt(preset.genre);
829N/A phdr_chunk.writeUnsignedInt(preset.morphology);
829N/A }
829N/A phdr_chunk.writeString("EOP", 20);
829N/A phdr_chunk.writeUnsignedShort(0);
829N/A phdr_chunk.writeUnsignedShort(0);
829N/A phdr_chunk.writeUnsignedShort(phdr_zone_count);
829N/A phdr_chunk.writeUnsignedInt(0);
829N/A phdr_chunk.writeUnsignedInt(0);
829N/A phdr_chunk.writeUnsignedInt(0);
829N/A
829N/A
829N/A RIFFWriter pbag_chunk = writer.writeChunk("pbag");
829N/A int pbag_gencount = 0;
829N/A int pbag_modcount = 0;
829N/A for (SF2Instrument preset : this.instruments) {
829N/A if (preset.getGlobalRegion() != null) {
829N/A pbag_chunk.writeUnsignedShort(pbag_gencount);
829N/A pbag_chunk.writeUnsignedShort(pbag_modcount);
829N/A pbag_gencount += preset.getGlobalRegion().getGenerators().size();
829N/A pbag_modcount += preset.getGlobalRegion().getModulators().size();
829N/A }
829N/A for (SF2InstrumentRegion region : preset.getRegions()) {
829N/A pbag_chunk.writeUnsignedShort(pbag_gencount);
829N/A pbag_chunk.writeUnsignedShort(pbag_modcount);
829N/A if (layers.indexOf(region.layer) != -1) {
829N/A // One generator is used to reference to instrument record
829N/A pbag_gencount += 1;
829N/A }
829N/A pbag_gencount += region.getGenerators().size();
829N/A pbag_modcount += region.getModulators().size();
829N/A
829N/A }
829N/A }
829N/A pbag_chunk.writeUnsignedShort(pbag_gencount);
829N/A pbag_chunk.writeUnsignedShort(pbag_modcount);
829N/A
829N/A RIFFWriter pmod_chunk = writer.writeChunk("pmod");
829N/A for (SF2Instrument preset : this.instruments) {
829N/A if (preset.getGlobalRegion() != null) {
829N/A writeModulators(pmod_chunk,
829N/A preset.getGlobalRegion().getModulators());
829N/A }
829N/A for (SF2InstrumentRegion region : preset.getRegions())
829N/A writeModulators(pmod_chunk, region.getModulators());
829N/A }
829N/A pmod_chunk.write(new byte[10]);
829N/A
829N/A RIFFWriter pgen_chunk = writer.writeChunk("pgen");
829N/A for (SF2Instrument preset : this.instruments) {
829N/A if (preset.getGlobalRegion() != null) {
829N/A writeGenerators(pgen_chunk,
829N/A preset.getGlobalRegion().getGenerators());
829N/A }
829N/A for (SF2InstrumentRegion region : preset.getRegions()) {
829N/A writeGenerators(pgen_chunk, region.getGenerators());
829N/A int ix = (int) layers.indexOf(region.layer);
829N/A if (ix != -1) {
829N/A pgen_chunk.writeUnsignedShort(SF2Region.GENERATOR_INSTRUMENT);
829N/A pgen_chunk.writeShort((short) ix);
829N/A }
829N/A }
829N/A }
829N/A pgen_chunk.write(new byte[4]);
829N/A
829N/A RIFFWriter inst_chunk = writer.writeChunk("inst");
829N/A int inst_zone_count = 0;
829N/A for (SF2Layer instrument : this.layers) {
829N/A inst_chunk.writeString(instrument.name, 20);
829N/A inst_chunk.writeUnsignedShort(inst_zone_count);
829N/A if (instrument.getGlobalRegion() != null)
829N/A inst_zone_count += 1;
829N/A inst_zone_count += instrument.getRegions().size();
829N/A }
829N/A inst_chunk.writeString("EOI", 20);
829N/A inst_chunk.writeUnsignedShort(inst_zone_count);
829N/A
829N/A
829N/A RIFFWriter ibag_chunk = writer.writeChunk("ibag");
829N/A int ibag_gencount = 0;
829N/A int ibag_modcount = 0;
829N/A for (SF2Layer instrument : this.layers) {
829N/A if (instrument.getGlobalRegion() != null) {
829N/A ibag_chunk.writeUnsignedShort(ibag_gencount);
829N/A ibag_chunk.writeUnsignedShort(ibag_modcount);
829N/A ibag_gencount
829N/A += instrument.getGlobalRegion().getGenerators().size();
829N/A ibag_modcount
829N/A += instrument.getGlobalRegion().getModulators().size();
829N/A }
829N/A for (SF2LayerRegion region : instrument.getRegions()) {
829N/A ibag_chunk.writeUnsignedShort(ibag_gencount);
829N/A ibag_chunk.writeUnsignedShort(ibag_modcount);
829N/A if (samples.indexOf(region.sample) != -1) {
829N/A // One generator is used to reference to instrument record
829N/A ibag_gencount += 1;
829N/A }
829N/A ibag_gencount += region.getGenerators().size();
829N/A ibag_modcount += region.getModulators().size();
829N/A
829N/A }
829N/A }
829N/A ibag_chunk.writeUnsignedShort(ibag_gencount);
829N/A ibag_chunk.writeUnsignedShort(ibag_modcount);
829N/A
829N/A
829N/A RIFFWriter imod_chunk = writer.writeChunk("imod");
829N/A for (SF2Layer instrument : this.layers) {
829N/A if (instrument.getGlobalRegion() != null) {
829N/A writeModulators(imod_chunk,
829N/A instrument.getGlobalRegion().getModulators());
829N/A }
829N/A for (SF2LayerRegion region : instrument.getRegions())
829N/A writeModulators(imod_chunk, region.getModulators());
829N/A }
829N/A imod_chunk.write(new byte[10]);
829N/A
829N/A RIFFWriter igen_chunk = writer.writeChunk("igen");
829N/A for (SF2Layer instrument : this.layers) {
829N/A if (instrument.getGlobalRegion() != null) {
829N/A writeGenerators(igen_chunk,
829N/A instrument.getGlobalRegion().getGenerators());
829N/A }
829N/A for (SF2LayerRegion region : instrument.getRegions()) {
829N/A writeGenerators(igen_chunk, region.getGenerators());
829N/A int ix = samples.indexOf(region.sample);
829N/A if (ix != -1) {
829N/A igen_chunk.writeUnsignedShort(SF2Region.GENERATOR_SAMPLEID);
829N/A igen_chunk.writeShort((short) ix);
829N/A }
829N/A }
829N/A }
829N/A igen_chunk.write(new byte[4]);
829N/A
829N/A
829N/A RIFFWriter shdr_chunk = writer.writeChunk("shdr");
829N/A long sample_pos = 0;
829N/A for (SF2Sample sample : samples) {
829N/A shdr_chunk.writeString(sample.name, 20);
829N/A long start = sample_pos;
829N/A sample_pos += sample.data.capacity() / 2;
829N/A long end = sample_pos;
829N/A long startLoop = sample.startLoop + start;
829N/A long endLoop = sample.endLoop + start;
829N/A if (startLoop < start)
829N/A startLoop = start;
829N/A if (endLoop > end)
829N/A endLoop = end;
829N/A shdr_chunk.writeUnsignedInt(start);
829N/A shdr_chunk.writeUnsignedInt(end);
829N/A shdr_chunk.writeUnsignedInt(startLoop);
829N/A shdr_chunk.writeUnsignedInt(endLoop);
829N/A shdr_chunk.writeUnsignedInt(sample.sampleRate);
829N/A shdr_chunk.writeUnsignedByte(sample.originalPitch);
829N/A shdr_chunk.writeByte(sample.pitchCorrection);
829N/A shdr_chunk.writeUnsignedShort(sample.sampleLink);
829N/A shdr_chunk.writeUnsignedShort(sample.sampleType);
829N/A sample_pos += 32;
829N/A }
829N/A shdr_chunk.writeString("EOS", 20);
829N/A shdr_chunk.write(new byte[26]);
829N/A
829N/A }
829N/A
829N/A public String getName() {
829N/A return name;
829N/A }
829N/A
829N/A public String getVersion() {
829N/A return major + "." + minor;
829N/A }
829N/A
829N/A public String getVendor() {
829N/A return engineers;
829N/A }
829N/A
829N/A public String getDescription() {
829N/A return comments;
829N/A }
829N/A
829N/A public void setName(String s) {
829N/A name = s;
829N/A }
829N/A
829N/A public void setVendor(String s) {
829N/A engineers = s;
829N/A }
829N/A
829N/A public void setDescription(String s) {
829N/A comments = s;
829N/A }
829N/A
829N/A public SoundbankResource[] getResources() {
829N/A SoundbankResource[] resources
829N/A = new SoundbankResource[layers.size() + samples.size()];
829N/A int j = 0;
829N/A for (int i = 0; i < layers.size(); i++)
829N/A resources[j++] = layers.get(i);
829N/A for (int i = 0; i < samples.size(); i++)
829N/A resources[j++] = samples.get(i);
829N/A return resources;
829N/A }
829N/A
829N/A public SF2Instrument[] getInstruments() {
829N/A SF2Instrument[] inslist_array
829N/A = instruments.toArray(new SF2Instrument[instruments.size()]);
829N/A Arrays.sort(inslist_array, new ModelInstrumentComparator());
829N/A return inslist_array;
829N/A }
829N/A
829N/A public SF2Layer[] getLayers() {
829N/A return layers.toArray(new SF2Layer[layers.size()]);
829N/A }
829N/A
829N/A public SF2Sample[] getSamples() {
829N/A return samples.toArray(new SF2Sample[samples.size()]);
829N/A }
829N/A
829N/A public Instrument getInstrument(Patch patch) {
829N/A int program = patch.getProgram();
829N/A int bank = patch.getBank();
829N/A boolean percussion = false;
829N/A if (patch instanceof ModelPatch)
829N/A percussion = ((ModelPatch)patch).isPercussion();
829N/A for (Instrument instrument : instruments) {
829N/A Patch patch2 = instrument.getPatch();
829N/A int program2 = patch2.getProgram();
829N/A int bank2 = patch2.getBank();
829N/A if (program == program2 && bank == bank2) {
829N/A boolean percussion2 = false;
829N/A if (patch2 instanceof ModelPatch)
829N/A percussion2 = ((ModelPatch) patch2).isPercussion();
829N/A if (percussion == percussion2)
829N/A return instrument;
829N/A }
829N/A }
829N/A return null;
829N/A }
829N/A
829N/A public String getCreationDate() {
829N/A return creationDate;
829N/A }
829N/A
829N/A public void setCreationDate(String creationDate) {
829N/A this.creationDate = creationDate;
829N/A }
829N/A
829N/A public String getProduct() {
829N/A return product;
829N/A }
829N/A
829N/A public void setProduct(String product) {
829N/A this.product = product;
829N/A }
829N/A
829N/A public String getRomName() {
829N/A return romName;
829N/A }
829N/A
829N/A public void setRomName(String romName) {
829N/A this.romName = romName;
829N/A }
829N/A
829N/A public int getRomVersionMajor() {
829N/A return romVersionMajor;
829N/A }
829N/A
829N/A public void setRomVersionMajor(int romVersionMajor) {
829N/A this.romVersionMajor = romVersionMajor;
829N/A }
829N/A
829N/A public int getRomVersionMinor() {
829N/A return romVersionMinor;
829N/A }
829N/A
829N/A public void setRomVersionMinor(int romVersionMinor) {
829N/A this.romVersionMinor = romVersionMinor;
829N/A }
829N/A
829N/A public String getTargetEngine() {
829N/A return targetEngine;
829N/A }
829N/A
829N/A public void setTargetEngine(String targetEngine) {
829N/A this.targetEngine = targetEngine;
829N/A }
829N/A
829N/A public String getTools() {
829N/A return tools;
829N/A }
829N/A
829N/A public void setTools(String tools) {
829N/A this.tools = tools;
829N/A }
829N/A
829N/A public void addResource(SoundbankResource resource) {
829N/A if (resource instanceof SF2Instrument)
829N/A instruments.add((SF2Instrument)resource);
829N/A if (resource instanceof SF2Layer)
829N/A layers.add((SF2Layer)resource);
829N/A if (resource instanceof SF2Sample)
829N/A samples.add((SF2Sample)resource);
829N/A }
829N/A
829N/A public void removeResource(SoundbankResource resource) {
829N/A if (resource instanceof SF2Instrument)
829N/A instruments.remove((SF2Instrument)resource);
829N/A if (resource instanceof SF2Layer)
829N/A layers.remove((SF2Layer)resource);
829N/A if (resource instanceof SF2Sample)
829N/A samples.remove((SF2Sample)resource);
829N/A }
829N/A
829N/A public void addInstrument(SF2Instrument resource) {
829N/A instruments.add(resource);
829N/A }
829N/A
829N/A public void removeInstrument(SF2Instrument resource) {
829N/A instruments.remove(resource);
829N/A }
829N/A}