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