Path: blob/master/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java
41161 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.media.sound;2627import java.io.File;28import java.io.FileInputStream;29import java.io.IOException;30import java.io.InputStream;31import java.io.OutputStream;32import java.net.URL;33import java.util.ArrayList;34import java.util.Arrays;35import java.util.HashMap;36import java.util.List;37import java.util.Map;38import java.util.Stack;3940import javax.sound.midi.Instrument;41import javax.sound.midi.Patch;42import javax.sound.midi.Soundbank;43import javax.sound.midi.SoundbankResource;44import javax.sound.sampled.AudioFormat;45import javax.sound.sampled.AudioFormat.Encoding;46import javax.sound.sampled.AudioInputStream;47import javax.sound.sampled.AudioSystem;4849/**50* A DLS Level 1 and Level 2 soundbank reader (from files/url/streams).51*52* @author Karl Helgason53*/54public final class DLSSoundbank implements Soundbank {5556private static class DLSID {57long i1;58int s1;59int s2;60int x1;61int x2;62int x3;63int x4;64int x5;65int x6;66int x7;67int x8;6869private DLSID() {70}7172DLSID(long i1, int s1, int s2, int x1, int x2, int x3, int x4,73int x5, int x6, int x7, int x8) {74this.i1 = i1;75this.s1 = s1;76this.s2 = s2;77this.x1 = x1;78this.x2 = x2;79this.x3 = x3;80this.x4 = x4;81this.x5 = x5;82this.x6 = x6;83this.x7 = x7;84this.x8 = x8;85}8687public static DLSID read(RIFFReader riff) throws IOException {88DLSID d = new DLSID();89d.i1 = riff.readUnsignedInt();90d.s1 = riff.readUnsignedShort();91d.s2 = riff.readUnsignedShort();92d.x1 = riff.readUnsignedByte();93d.x2 = riff.readUnsignedByte();94d.x3 = riff.readUnsignedByte();95d.x4 = riff.readUnsignedByte();96d.x5 = riff.readUnsignedByte();97d.x6 = riff.readUnsignedByte();98d.x7 = riff.readUnsignedByte();99d.x8 = riff.readUnsignedByte();100return d;101}102103@Override104public int hashCode() {105return (int)i1;106}107108@Override109public boolean equals(Object obj) {110if (!(obj instanceof DLSID)) {111return false;112}113DLSID t = (DLSID) obj;114return i1 == t.i1 && s1 == t.s1 && s2 == t.s2115&& x1 == t.x1 && x2 == t.x2 && x3 == t.x3 && x4 == t.x4116&& x5 == t.x5 && x6 == t.x6 && x7 == t.x7 && x8 == t.x8;117}118}119120/** X = X & Y */121private static final int DLS_CDL_AND = 0x0001;122/** X = X | Y */123private static final int DLS_CDL_OR = 0x0002;124/** X = X ^ Y */125private static final int DLS_CDL_XOR = 0x0003;126/** X = X + Y */127private static final int DLS_CDL_ADD = 0x0004;128/** X = X - Y */129private static final int DLS_CDL_SUBTRACT = 0x0005;130/** X = X * Y */131private static final int DLS_CDL_MULTIPLY = 0x0006;132/** X = X / Y */133private static final int DLS_CDL_DIVIDE = 0x0007;134/** X = X && Y */135private static final int DLS_CDL_LOGICAL_AND = 0x0008;136/** X = X || Y */137private static final int DLS_CDL_LOGICAL_OR = 0x0009;138/** X = (X < Y) */139private static final int DLS_CDL_LT = 0x000A;140/** X = (X <= Y) */141private static final int DLS_CDL_LE = 0x000B;142/** X = (X > Y) */143private static final int DLS_CDL_GT = 0x000C;144/** X = (X >= Y) */145private static final int DLS_CDL_GE = 0x000D;146/** X = (X == Y) */147private static final int DLS_CDL_EQ = 0x000E;148/** X = !X */149private static final int DLS_CDL_NOT = 0x000F;150/** 32-bit constant */151private static final int DLS_CDL_CONST = 0x0010;152/** 32-bit value returned from query */153private static final int DLS_CDL_QUERY = 0x0011;154/** 32-bit value returned from query */155private static final int DLS_CDL_QUERYSUPPORTED = 0x0012;156157private static final DLSID DLSID_GMInHardware = new DLSID(0x178f2f24,1580xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);159private static final DLSID DLSID_GSInHardware = new DLSID(0x178f2f25,1600xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);161private static final DLSID DLSID_XGInHardware = new DLSID(0x178f2f26,1620xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);163private static final DLSID DLSID_SupportsDLS1 = new DLSID(0x178f2f27,1640xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);165private static final DLSID DLSID_SupportsDLS2 = new DLSID(0xf14599e5,1660x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);167private static final DLSID DLSID_SampleMemorySize = new DLSID(0x178f2f28,1680xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);169private static final DLSID DLSID_ManufacturersID = new DLSID(0xb03e1181,1700x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);171private static final DLSID DLSID_ProductID = new DLSID(0xb03e1182,1720x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);173private static final DLSID DLSID_SamplePlaybackRate = new DLSID(0x2a91f713,1740xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);175176private long major = -1;177private long minor = -1;178179private final DLSInfo info = new DLSInfo();180181private final List<DLSInstrument> instruments = new ArrayList<>();182private final List<DLSSample> samples = new ArrayList<>();183184private boolean largeFormat = false;185private File sampleFile;186187public DLSSoundbank() {188}189190public DLSSoundbank(URL url) throws IOException {191InputStream is = url.openStream();192try {193readSoundbank(is);194} finally {195is.close();196}197}198199public DLSSoundbank(File file) throws IOException {200largeFormat = true;201sampleFile = file;202InputStream is = new FileInputStream(file);203try {204readSoundbank(is);205} finally {206is.close();207}208}209210public DLSSoundbank(InputStream inputstream) throws IOException {211readSoundbank(inputstream);212}213214private void readSoundbank(InputStream inputstream) throws IOException {215RIFFReader riff = new RIFFReader(inputstream);216if (!riff.getFormat().equals("RIFF")) {217throw new RIFFInvalidFormatException(218"Input stream is not a valid RIFF stream!");219}220if (!riff.getType().equals("DLS ")) {221throw new RIFFInvalidFormatException(222"Input stream is not a valid DLS soundbank!");223}224while (riff.hasNextChunk()) {225RIFFReader chunk = riff.nextChunk();226if (chunk.getFormat().equals("LIST")) {227if (chunk.getType().equals("INFO"))228readInfoChunk(chunk);229if (chunk.getType().equals("lins"))230readLinsChunk(chunk);231if (chunk.getType().equals("wvpl"))232readWvplChunk(chunk);233} else {234if (chunk.getFormat().equals("cdl ")) {235if (!readCdlChunk(chunk)) {236throw new RIFFInvalidFormatException(237"DLS file isn't supported!");238}239}240if (chunk.getFormat().equals("colh")) {241// skipped because we will load the entire bank into memory242// long instrumentcount = chunk.readUnsignedInt();243// System.out.println("instrumentcount = "+ instrumentcount);244}245if (chunk.getFormat().equals("ptbl")) {246// Pool Table Chunk247// skipped because we will load the entire bank into memory248}249if (chunk.getFormat().equals("vers")) {250major = chunk.readUnsignedInt();251minor = chunk.readUnsignedInt();252}253}254}255256for (Map.Entry<DLSRegion, Long> entry : temp_rgnassign.entrySet()) {257entry.getKey().sample = samples.get((int)entry.getValue().longValue());258}259260temp_rgnassign = null;261}262263private boolean cdlIsQuerySupported(DLSID uuid) {264return uuid.equals(DLSID_GMInHardware)265|| uuid.equals(DLSID_GSInHardware)266|| uuid.equals(DLSID_XGInHardware)267|| uuid.equals(DLSID_SupportsDLS1)268|| uuid.equals(DLSID_SupportsDLS2)269|| uuid.equals(DLSID_SampleMemorySize)270|| uuid.equals(DLSID_ManufacturersID)271|| uuid.equals(DLSID_ProductID)272|| uuid.equals(DLSID_SamplePlaybackRate);273}274275private long cdlQuery(DLSID uuid) {276if (uuid.equals(DLSID_GMInHardware))277return 1;278if (uuid.equals(DLSID_GSInHardware))279return 0;280if (uuid.equals(DLSID_XGInHardware))281return 0;282if (uuid.equals(DLSID_SupportsDLS1))283return 1;284if (uuid.equals(DLSID_SupportsDLS2))285return 1;286if (uuid.equals(DLSID_SampleMemorySize))287return Runtime.getRuntime().totalMemory();288if (uuid.equals(DLSID_ManufacturersID))289return 0;290if (uuid.equals(DLSID_ProductID))291return 0;292if (uuid.equals(DLSID_SamplePlaybackRate))293return 44100;294return 0;295}296297298// Reading cdl-ck Chunk299// "cdl " chunk can only appear inside : DLS,lart,lar2,rgn,rgn2300private boolean readCdlChunk(RIFFReader riff) throws IOException {301302DLSID uuid;303long x;304long y;305Stack<Long> stack = new Stack<>();306307while (riff.available() != 0) {308int opcode = riff.readUnsignedShort();309switch (opcode) {310case DLS_CDL_AND:311x = stack.pop();312y = stack.pop();313stack.push(Long.valueOf(((x != 0) && (y != 0)) ? 1 : 0));314break;315case DLS_CDL_OR:316x = stack.pop();317y = stack.pop();318stack.push(Long.valueOf(((x != 0) || (y != 0)) ? 1 : 0));319break;320case DLS_CDL_XOR:321x = stack.pop();322y = stack.pop();323stack.push(Long.valueOf(((x != 0) ^ (y != 0)) ? 1 : 0));324break;325case DLS_CDL_ADD:326x = stack.pop();327y = stack.pop();328stack.push(Long.valueOf(x + y));329break;330case DLS_CDL_SUBTRACT:331x = stack.pop();332y = stack.pop();333stack.push(Long.valueOf(x - y));334break;335case DLS_CDL_MULTIPLY:336x = stack.pop();337y = stack.pop();338stack.push(Long.valueOf(x * y));339break;340case DLS_CDL_DIVIDE:341x = stack.pop();342y = stack.pop();343stack.push(Long.valueOf(x / y));344break;345case DLS_CDL_LOGICAL_AND:346x = stack.pop();347y = stack.pop();348stack.push(Long.valueOf(((x != 0) && (y != 0)) ? 1 : 0));349break;350case DLS_CDL_LOGICAL_OR:351x = stack.pop();352y = stack.pop();353stack.push(Long.valueOf(((x != 0) || (y != 0)) ? 1 : 0));354break;355case DLS_CDL_LT:356x = stack.pop();357y = stack.pop();358stack.push(Long.valueOf((x < y) ? 1 : 0));359break;360case DLS_CDL_LE:361x = stack.pop();362y = stack.pop();363stack.push(Long.valueOf((x <= y) ? 1 : 0));364break;365case DLS_CDL_GT:366x = stack.pop();367y = stack.pop();368stack.push(Long.valueOf((x > y) ? 1 : 0));369break;370case DLS_CDL_GE:371x = stack.pop();372y = stack.pop();373stack.push(Long.valueOf((x >= y) ? 1 : 0));374break;375case DLS_CDL_EQ:376x = stack.pop();377y = stack.pop();378stack.push(Long.valueOf((x == y) ? 1 : 0));379break;380case DLS_CDL_NOT:381x = stack.pop();382y = stack.pop();383stack.push(Long.valueOf((x == 0) ? 1 : 0));384break;385case DLS_CDL_CONST:386stack.push(Long.valueOf(riff.readUnsignedInt()));387break;388case DLS_CDL_QUERY:389uuid = DLSID.read(riff);390stack.push(cdlQuery(uuid));391break;392case DLS_CDL_QUERYSUPPORTED:393uuid = DLSID.read(riff);394stack.push(Long.valueOf(cdlIsQuerySupported(uuid) ? 1 : 0));395break;396default:397break;398}399}400if (stack.isEmpty())401return false;402403return stack.pop() == 1;404}405406private void readInfoChunk(RIFFReader riff) throws IOException {407info.name = null;408while (riff.hasNextChunk()) {409RIFFReader chunk = riff.nextChunk();410String format = chunk.getFormat();411if (format.equals("INAM"))412info.name = chunk.readString(chunk.available());413else if (format.equals("ICRD"))414info.creationDate = chunk.readString(chunk.available());415else if (format.equals("IENG"))416info.engineers = chunk.readString(chunk.available());417else if (format.equals("IPRD"))418info.product = chunk.readString(chunk.available());419else if (format.equals("ICOP"))420info.copyright = chunk.readString(chunk.available());421else if (format.equals("ICMT"))422info.comments = chunk.readString(chunk.available());423else if (format.equals("ISFT"))424info.tools = chunk.readString(chunk.available());425else if (format.equals("IARL"))426info.archival_location = chunk.readString(chunk.available());427else if (format.equals("IART"))428info.artist = chunk.readString(chunk.available());429else if (format.equals("ICMS"))430info.commissioned = chunk.readString(chunk.available());431else if (format.equals("IGNR"))432info.genre = chunk.readString(chunk.available());433else if (format.equals("IKEY"))434info.keywords = chunk.readString(chunk.available());435else if (format.equals("IMED"))436info.medium = chunk.readString(chunk.available());437else if (format.equals("ISBJ"))438info.subject = chunk.readString(chunk.available());439else if (format.equals("ISRC"))440info.source = chunk.readString(chunk.available());441else if (format.equals("ISRF"))442info.source_form = chunk.readString(chunk.available());443else if (format.equals("ITCH"))444info.technician = chunk.readString(chunk.available());445}446}447448private void readLinsChunk(RIFFReader riff) throws IOException {449while (riff.hasNextChunk()) {450RIFFReader chunk = riff.nextChunk();451if (chunk.getFormat().equals("LIST")) {452if (chunk.getType().equals("ins "))453readInsChunk(chunk);454}455}456}457458private void readInsChunk(RIFFReader riff) throws IOException {459DLSInstrument instrument = new DLSInstrument(this);460461while (riff.hasNextChunk()) {462RIFFReader chunk = riff.nextChunk();463String format = chunk.getFormat();464if (format.equals("LIST")) {465if (chunk.getType().equals("INFO")) {466readInsInfoChunk(instrument, chunk);467}468if (chunk.getType().equals("lrgn")) {469while (chunk.hasNextChunk()) {470RIFFReader subchunk = chunk.nextChunk();471if (subchunk.getFormat().equals("LIST")) {472if (subchunk.getType().equals("rgn ")) {473DLSRegion split = new DLSRegion();474if (readRgnChunk(split, subchunk))475instrument.getRegions().add(split);476}477if (subchunk.getType().equals("rgn2")) {478// support for DLS level 2 regions479DLSRegion split = new DLSRegion();480if (readRgnChunk(split, subchunk))481instrument.getRegions().add(split);482}483}484}485}486if (chunk.getType().equals("lart")) {487List<DLSModulator> modlist = new ArrayList<>();488while (chunk.hasNextChunk()) {489RIFFReader subchunk = chunk.nextChunk();490if (chunk.getFormat().equals("cdl ")) {491if (!readCdlChunk(chunk)) {492modlist.clear();493break;494}495}496if (subchunk.getFormat().equals("art1"))497readArt1Chunk(modlist, subchunk);498}499instrument.getModulators().addAll(modlist);500}501if (chunk.getType().equals("lar2")) {502// support for DLS level 2 ART503List<DLSModulator> modlist = new ArrayList<>();504while (chunk.hasNextChunk()) {505RIFFReader subchunk = chunk.nextChunk();506if (chunk.getFormat().equals("cdl ")) {507if (!readCdlChunk(chunk)) {508modlist.clear();509break;510}511}512if (subchunk.getFormat().equals("art2"))513readArt2Chunk(modlist, subchunk);514}515instrument.getModulators().addAll(modlist);516}517} else {518if (format.equals("dlid")) {519instrument.guid = new byte[16];520chunk.readFully(instrument.guid);521}522if (format.equals("insh")) {523chunk.readUnsignedInt(); // Read Region Count - ignored524525int bank = chunk.read(); // LSB526bank += (chunk.read() & 127) << 7; // MSB527chunk.read(); // Read Reserved byte528int drumins = chunk.read(); // Drum Instrument529530int id = chunk.read() & 127; // Read only first 7 bits531chunk.read(); // Read Reserved byte532chunk.read(); // Read Reserved byte533chunk.read(); // Read Reserved byte534535instrument.bank = bank;536instrument.preset = id;537instrument.druminstrument = (drumins & 128) > 0;538//System.out.println("bank="+bank+" drumkit="+drumkit539// +" id="+id);540}541542}543}544instruments.add(instrument);545}546547private void readArt1Chunk(List<DLSModulator> modulators, RIFFReader riff)548throws IOException {549long size = riff.readUnsignedInt();550long count = riff.readUnsignedInt();551552if (size - 8 != 0)553riff.skip(size - 8);554555for (int i = 0; i < count; i++) {556DLSModulator modulator = new DLSModulator();557modulator.version = 1;558modulator.source = riff.readUnsignedShort();559modulator.control = riff.readUnsignedShort();560modulator.destination = riff.readUnsignedShort();561modulator.transform = riff.readUnsignedShort();562modulator.scale = riff.readInt();563modulators.add(modulator);564}565}566567private void readArt2Chunk(List<DLSModulator> modulators, RIFFReader riff)568throws IOException {569long size = riff.readUnsignedInt();570long count = riff.readUnsignedInt();571572if (size - 8 != 0)573riff.skip(size - 8);574575for (int i = 0; i < count; i++) {576DLSModulator modulator = new DLSModulator();577modulator.version = 2;578modulator.source = riff.readUnsignedShort();579modulator.control = riff.readUnsignedShort();580modulator.destination = riff.readUnsignedShort();581modulator.transform = riff.readUnsignedShort();582modulator.scale = riff.readInt();583modulators.add(modulator);584}585}586587private Map<DLSRegion, Long> temp_rgnassign = new HashMap<>();588589private boolean readRgnChunk(DLSRegion split, RIFFReader riff)590throws IOException {591while (riff.hasNextChunk()) {592RIFFReader chunk = riff.nextChunk();593String format = chunk.getFormat();594if (format.equals("LIST")) {595if (chunk.getType().equals("lart")) {596List<DLSModulator> modlist = new ArrayList<>();597while (chunk.hasNextChunk()) {598RIFFReader subchunk = chunk.nextChunk();599if (chunk.getFormat().equals("cdl ")) {600if (!readCdlChunk(chunk)) {601modlist.clear();602break;603}604}605if (subchunk.getFormat().equals("art1"))606readArt1Chunk(modlist, subchunk);607}608split.getModulators().addAll(modlist);609}610if (chunk.getType().equals("lar2")) {611// support for DLS level 2 ART612List<DLSModulator> modlist = new ArrayList<>();613while (chunk.hasNextChunk()) {614RIFFReader subchunk = chunk.nextChunk();615if (chunk.getFormat().equals("cdl ")) {616if (!readCdlChunk(chunk)) {617modlist.clear();618break;619}620}621if (subchunk.getFormat().equals("art2"))622readArt2Chunk(modlist, subchunk);623}624split.getModulators().addAll(modlist);625}626} else {627628if (format.equals("cdl ")) {629if (!readCdlChunk(chunk))630return false;631}632if (format.equals("rgnh")) {633split.keyfrom = chunk.readUnsignedShort();634split.keyto = chunk.readUnsignedShort();635split.velfrom = chunk.readUnsignedShort();636split.velto = chunk.readUnsignedShort();637split.options = chunk.readUnsignedShort();638split.exclusiveClass = chunk.readUnsignedShort();639}640if (format.equals("wlnk")) {641split.fusoptions = chunk.readUnsignedShort();642split.phasegroup = chunk.readUnsignedShort();643split.channel = chunk.readUnsignedInt();644long sampleid = chunk.readUnsignedInt();645temp_rgnassign.put(split, sampleid);646}647if (format.equals("wsmp")) {648split.sampleoptions = new DLSSampleOptions();649readWsmpChunk(split.sampleoptions, chunk);650}651}652}653return true;654}655656private void readWsmpChunk(DLSSampleOptions sampleOptions, RIFFReader riff)657throws IOException {658long size = riff.readUnsignedInt();659sampleOptions.unitynote = riff.readUnsignedShort();660sampleOptions.finetune = riff.readShort();661sampleOptions.attenuation = riff.readInt();662sampleOptions.options = riff.readUnsignedInt();663long loops = riff.readInt();664665if (size > 20)666riff.skip(size - 20);667668for (int i = 0; i < loops; i++) {669DLSSampleLoop loop = new DLSSampleLoop();670long size2 = riff.readUnsignedInt();671loop.type = riff.readUnsignedInt();672loop.start = riff.readUnsignedInt();673loop.length = riff.readUnsignedInt();674sampleOptions.loops.add(loop);675if (size2 > 16)676riff.skip(size2 - 16);677}678}679680private void readInsInfoChunk(DLSInstrument dlsinstrument, RIFFReader riff)681throws IOException {682dlsinstrument.info.name = null;683while (riff.hasNextChunk()) {684RIFFReader chunk = riff.nextChunk();685String format = chunk.getFormat();686if (format.equals("INAM")) {687dlsinstrument.info.name = chunk.readString(chunk.available());688} else if (format.equals("ICRD")) {689dlsinstrument.info.creationDate =690chunk.readString(chunk.available());691} else if (format.equals("IENG")) {692dlsinstrument.info.engineers =693chunk.readString(chunk.available());694} else if (format.equals("IPRD")) {695dlsinstrument.info.product = chunk.readString(chunk.available());696} else if (format.equals("ICOP")) {697dlsinstrument.info.copyright =698chunk.readString(chunk.available());699} else if (format.equals("ICMT")) {700dlsinstrument.info.comments =701chunk.readString(chunk.available());702} else if (format.equals("ISFT")) {703dlsinstrument.info.tools = chunk.readString(chunk.available());704} else if (format.equals("IARL")) {705dlsinstrument.info.archival_location =706chunk.readString(chunk.available());707} else if (format.equals("IART")) {708dlsinstrument.info.artist = chunk.readString(chunk.available());709} else if (format.equals("ICMS")) {710dlsinstrument.info.commissioned =711chunk.readString(chunk.available());712} else if (format.equals("IGNR")) {713dlsinstrument.info.genre = chunk.readString(chunk.available());714} else if (format.equals("IKEY")) {715dlsinstrument.info.keywords =716chunk.readString(chunk.available());717} else if (format.equals("IMED")) {718dlsinstrument.info.medium = chunk.readString(chunk.available());719} else if (format.equals("ISBJ")) {720dlsinstrument.info.subject = chunk.readString(chunk.available());721} else if (format.equals("ISRC")) {722dlsinstrument.info.source = chunk.readString(chunk.available());723} else if (format.equals("ISRF")) {724dlsinstrument.info.source_form =725chunk.readString(chunk.available());726} else if (format.equals("ITCH")) {727dlsinstrument.info.technician =728chunk.readString(chunk.available());729}730}731}732733private void readWvplChunk(RIFFReader riff) throws IOException {734while (riff.hasNextChunk()) {735RIFFReader chunk = riff.nextChunk();736if (chunk.getFormat().equals("LIST")) {737if (chunk.getType().equals("wave"))738readWaveChunk(chunk);739}740}741}742743private void readWaveChunk(RIFFReader riff) throws IOException {744DLSSample sample = new DLSSample(this);745746while (riff.hasNextChunk()) {747RIFFReader chunk = riff.nextChunk();748String format = chunk.getFormat();749if (format.equals("LIST")) {750if (chunk.getType().equals("INFO")) {751readWaveInfoChunk(sample, chunk);752}753} else {754if (format.equals("dlid")) {755sample.guid = new byte[16];756chunk.readFully(sample.guid);757}758759if (format.equals("fmt ")) {760int sampleformat = chunk.readUnsignedShort();761if (sampleformat != 1 && sampleformat != 3) {762throw new RIFFInvalidDataException(763"Only PCM samples are supported!");764}765int channels = chunk.readUnsignedShort();766long samplerate = chunk.readUnsignedInt();767// bytes per sec768/* long framerate = */ chunk.readUnsignedInt();769// block align, framesize770int framesize = chunk.readUnsignedShort();771int bits = chunk.readUnsignedShort();772AudioFormat audioformat = null;773if (sampleformat == 1) {774if (bits == 8) {775audioformat = new AudioFormat(776Encoding.PCM_UNSIGNED, samplerate, bits,777channels, framesize, samplerate, false);778} else {779audioformat = new AudioFormat(780Encoding.PCM_SIGNED, samplerate, bits,781channels, framesize, samplerate, false);782}783}784if (sampleformat == 3) {785audioformat = new AudioFormat(786Encoding.PCM_FLOAT, samplerate, bits,787channels, framesize, samplerate, false);788}789790sample.format = audioformat;791}792793if (format.equals("data")) {794if (largeFormat) {795sample.setData(new ModelByteBuffer(sampleFile,796chunk.getFilePointer(), chunk.available()));797} else {798byte[] buffer = new byte[chunk.available()];799// chunk.read(buffer);800sample.setData(buffer);801802int read = 0;803int avail = chunk.available();804while (read != avail) {805if (avail - read > 65536) {806chunk.readFully(buffer, read, 65536);807read += 65536;808} else {809chunk.readFully(buffer, read, avail - read);810read = avail;811}812}813}814}815816if (format.equals("wsmp")) {817sample.sampleoptions = new DLSSampleOptions();818readWsmpChunk(sample.sampleoptions, chunk);819}820}821}822823samples.add(sample);824825}826827private void readWaveInfoChunk(DLSSample dlssample, RIFFReader riff)828throws IOException {829dlssample.info.name = null;830while (riff.hasNextChunk()) {831RIFFReader chunk = riff.nextChunk();832String format = chunk.getFormat();833if (format.equals("INAM")) {834dlssample.info.name = chunk.readString(chunk.available());835} else if (format.equals("ICRD")) {836dlssample.info.creationDate =837chunk.readString(chunk.available());838} else if (format.equals("IENG")) {839dlssample.info.engineers = chunk.readString(chunk.available());840} else if (format.equals("IPRD")) {841dlssample.info.product = chunk.readString(chunk.available());842} else if (format.equals("ICOP")) {843dlssample.info.copyright = chunk.readString(chunk.available());844} else if (format.equals("ICMT")) {845dlssample.info.comments = chunk.readString(chunk.available());846} else if (format.equals("ISFT")) {847dlssample.info.tools = chunk.readString(chunk.available());848} else if (format.equals("IARL")) {849dlssample.info.archival_location =850chunk.readString(chunk.available());851} else if (format.equals("IART")) {852dlssample.info.artist = chunk.readString(chunk.available());853} else if (format.equals("ICMS")) {854dlssample.info.commissioned =855chunk.readString(chunk.available());856} else if (format.equals("IGNR")) {857dlssample.info.genre = chunk.readString(chunk.available());858} else if (format.equals("IKEY")) {859dlssample.info.keywords = chunk.readString(chunk.available());860} else if (format.equals("IMED")) {861dlssample.info.medium = chunk.readString(chunk.available());862} else if (format.equals("ISBJ")) {863dlssample.info.subject = chunk.readString(chunk.available());864} else if (format.equals("ISRC")) {865dlssample.info.source = chunk.readString(chunk.available());866} else if (format.equals("ISRF")) {867dlssample.info.source_form = chunk.readString(chunk.available());868} else if (format.equals("ITCH")) {869dlssample.info.technician = chunk.readString(chunk.available());870}871}872}873874public void save(String name) throws IOException {875writeSoundbank(new RIFFWriter(name, "DLS "));876}877878public void save(File file) throws IOException {879writeSoundbank(new RIFFWriter(file, "DLS "));880}881882public void save(OutputStream out) throws IOException {883writeSoundbank(new RIFFWriter(out, "DLS "));884}885886private void writeSoundbank(RIFFWriter writer) throws IOException {887RIFFWriter colh_chunk = writer.writeChunk("colh");888colh_chunk.writeUnsignedInt(instruments.size());889890if (major != -1 && minor != -1) {891RIFFWriter vers_chunk = writer.writeChunk("vers");892vers_chunk.writeUnsignedInt(major);893vers_chunk.writeUnsignedInt(minor);894}895896writeInstruments(writer.writeList("lins"));897898RIFFWriter ptbl = writer.writeChunk("ptbl");899ptbl.writeUnsignedInt(8);900ptbl.writeUnsignedInt(samples.size());901long ptbl_offset = writer.getFilePointer();902for (int i = 0; i < samples.size(); i++)903ptbl.writeUnsignedInt(0);904905RIFFWriter wvpl = writer.writeList("wvpl");906long off = wvpl.getFilePointer();907List<Long> offsettable = new ArrayList<>();908for (DLSSample sample : samples) {909offsettable.add(Long.valueOf(wvpl.getFilePointer() - off));910writeSample(wvpl.writeList("wave"), sample);911}912913// small cheat, we are going to rewrite data back in wvpl914long bak = writer.getFilePointer();915writer.seek(ptbl_offset);916writer.setWriteOverride(true);917for (Long offset : offsettable)918writer.writeUnsignedInt(offset.longValue());919writer.setWriteOverride(false);920writer.seek(bak);921922writeInfo(writer.writeList("INFO"), info);923924writer.close();925}926927private void writeSample(RIFFWriter writer, DLSSample sample)928throws IOException {929930AudioFormat audioformat = sample.getFormat();931932Encoding encoding = audioformat.getEncoding();933float sampleRate = audioformat.getSampleRate();934int sampleSizeInBits = audioformat.getSampleSizeInBits();935int channels = audioformat.getChannels();936int frameSize = audioformat.getFrameSize();937float frameRate = audioformat.getFrameRate();938boolean bigEndian = audioformat.isBigEndian();939940boolean convert_needed = false;941942if (audioformat.getSampleSizeInBits() == 8) {943if (!encoding.equals(Encoding.PCM_UNSIGNED)) {944encoding = Encoding.PCM_UNSIGNED;945convert_needed = true;946}947} else {948if (!encoding.equals(Encoding.PCM_SIGNED)) {949encoding = Encoding.PCM_SIGNED;950convert_needed = true;951}952if (bigEndian) {953bigEndian = false;954convert_needed = true;955}956}957958if (convert_needed) {959audioformat = new AudioFormat(encoding, sampleRate,960sampleSizeInBits, channels, frameSize, frameRate, bigEndian);961}962963// fmt964RIFFWriter fmt_chunk = writer.writeChunk("fmt ");965int sampleformat = 0;966if (audioformat.getEncoding().equals(Encoding.PCM_UNSIGNED))967sampleformat = 1;968else if (audioformat.getEncoding().equals(Encoding.PCM_SIGNED))969sampleformat = 1;970else if (audioformat.getEncoding().equals(Encoding.PCM_FLOAT))971sampleformat = 3;972973fmt_chunk.writeUnsignedShort(sampleformat);974fmt_chunk.writeUnsignedShort(audioformat.getChannels());975fmt_chunk.writeUnsignedInt((long) audioformat.getSampleRate());976long srate = ((long)audioformat.getFrameRate())*audioformat.getFrameSize();977fmt_chunk.writeUnsignedInt(srate);978fmt_chunk.writeUnsignedShort(audioformat.getFrameSize());979fmt_chunk.writeUnsignedShort(audioformat.getSampleSizeInBits());980fmt_chunk.write(0);981fmt_chunk.write(0);982983writeSampleOptions(writer.writeChunk("wsmp"), sample.sampleoptions);984985if (convert_needed) {986RIFFWriter data_chunk = writer.writeChunk("data");987AudioInputStream stream = AudioSystem.getAudioInputStream(988audioformat, (AudioInputStream)sample.getData());989stream.transferTo(data_chunk);990} else {991RIFFWriter data_chunk = writer.writeChunk("data");992ModelByteBuffer databuff = sample.getDataBuffer();993databuff.writeTo(data_chunk);994/*995data_chunk.write(databuff.array(),996databuff.arrayOffset(),997databuff.capacity());998*/999}10001001writeInfo(writer.writeList("INFO"), sample.info);1002}10031004private void writeInstruments(RIFFWriter writer) throws IOException {1005for (DLSInstrument instrument : instruments) {1006writeInstrument(writer.writeList("ins "), instrument);1007}1008}10091010private void writeInstrument(RIFFWriter writer, DLSInstrument instrument)1011throws IOException {10121013int art1_count = 0;1014int art2_count = 0;1015for (DLSModulator modulator : instrument.getModulators()) {1016if (modulator.version == 1)1017art1_count++;1018if (modulator.version == 2)1019art2_count++;1020}1021for (DLSRegion region : instrument.regions) {1022for (DLSModulator modulator : region.getModulators()) {1023if (modulator.version == 1)1024art1_count++;1025if (modulator.version == 2)1026art2_count++;1027}1028}10291030int version = 1;1031if (art2_count > 0)1032version = 2;10331034RIFFWriter insh_chunk = writer.writeChunk("insh");1035insh_chunk.writeUnsignedInt(instrument.getRegions().size());1036insh_chunk.writeUnsignedInt(instrument.bank +1037(instrument.druminstrument ? 2147483648L : 0));1038insh_chunk.writeUnsignedInt(instrument.preset);10391040RIFFWriter lrgn = writer.writeList("lrgn");1041for (DLSRegion region: instrument.regions)1042writeRegion(lrgn, region, version);10431044writeArticulators(writer, instrument.getModulators());10451046writeInfo(writer.writeList("INFO"), instrument.info);10471048}10491050private void writeArticulators(RIFFWriter writer,1051List<DLSModulator> modulators) throws IOException {1052int art1_count = 0;1053int art2_count = 0;1054for (DLSModulator modulator : modulators) {1055if (modulator.version == 1)1056art1_count++;1057if (modulator.version == 2)1058art2_count++;1059}1060if (art1_count > 0) {1061RIFFWriter lar1 = writer.writeList("lart");1062RIFFWriter art1 = lar1.writeChunk("art1");1063art1.writeUnsignedInt(8);1064art1.writeUnsignedInt(art1_count);1065for (DLSModulator modulator : modulators) {1066if (modulator.version == 1) {1067art1.writeUnsignedShort(modulator.source);1068art1.writeUnsignedShort(modulator.control);1069art1.writeUnsignedShort(modulator.destination);1070art1.writeUnsignedShort(modulator.transform);1071art1.writeInt(modulator.scale);1072}1073}1074}1075if (art2_count > 0) {1076RIFFWriter lar2 = writer.writeList("lar2");1077RIFFWriter art2 = lar2.writeChunk("art2");1078art2.writeUnsignedInt(8);1079art2.writeUnsignedInt(art2_count);1080for (DLSModulator modulator : modulators) {1081if (modulator.version == 2) {1082art2.writeUnsignedShort(modulator.source);1083art2.writeUnsignedShort(modulator.control);1084art2.writeUnsignedShort(modulator.destination);1085art2.writeUnsignedShort(modulator.transform);1086art2.writeInt(modulator.scale);1087}1088}1089}1090}10911092private void writeRegion(RIFFWriter writer, DLSRegion region, int version)1093throws IOException {1094RIFFWriter rgns = null;1095if (version == 1)1096rgns = writer.writeList("rgn ");1097if (version == 2)1098rgns = writer.writeList("rgn2");1099if (rgns == null)1100return;11011102RIFFWriter rgnh = rgns.writeChunk("rgnh");1103rgnh.writeUnsignedShort(region.keyfrom);1104rgnh.writeUnsignedShort(region.keyto);1105rgnh.writeUnsignedShort(region.velfrom);1106rgnh.writeUnsignedShort(region.velto);1107rgnh.writeUnsignedShort(region.options);1108rgnh.writeUnsignedShort(region.exclusiveClass);11091110if (region.sampleoptions != null)1111writeSampleOptions(rgns.writeChunk("wsmp"), region.sampleoptions);11121113if (region.sample != null) {1114if (samples.indexOf(region.sample) != -1) {1115RIFFWriter wlnk = rgns.writeChunk("wlnk");1116wlnk.writeUnsignedShort(region.fusoptions);1117wlnk.writeUnsignedShort(region.phasegroup);1118wlnk.writeUnsignedInt(region.channel);1119wlnk.writeUnsignedInt(samples.indexOf(region.sample));1120}1121}1122writeArticulators(rgns, region.getModulators());1123rgns.close();1124}11251126private void writeSampleOptions(RIFFWriter wsmp,1127DLSSampleOptions sampleoptions) throws IOException {1128wsmp.writeUnsignedInt(20);1129wsmp.writeUnsignedShort(sampleoptions.unitynote);1130wsmp.writeShort(sampleoptions.finetune);1131wsmp.writeInt(sampleoptions.attenuation);1132wsmp.writeUnsignedInt(sampleoptions.options);1133wsmp.writeInt(sampleoptions.loops.size());11341135for (DLSSampleLoop loop : sampleoptions.loops) {1136wsmp.writeUnsignedInt(16);1137wsmp.writeUnsignedInt(loop.type);1138wsmp.writeUnsignedInt(loop.start);1139wsmp.writeUnsignedInt(loop.length);1140}1141}11421143private void writeInfoStringChunk(RIFFWriter writer,1144String name, String value) throws IOException {1145if (value == null)1146return;1147RIFFWriter chunk = writer.writeChunk(name);1148chunk.writeString(value);1149int len = value.getBytes("ascii").length;1150chunk.write(0);1151len++;1152if (len % 2 != 0)1153chunk.write(0);1154}11551156private void writeInfo(RIFFWriter writer, DLSInfo info) throws IOException {1157writeInfoStringChunk(writer, "INAM", info.name);1158writeInfoStringChunk(writer, "ICRD", info.creationDate);1159writeInfoStringChunk(writer, "IENG", info.engineers);1160writeInfoStringChunk(writer, "IPRD", info.product);1161writeInfoStringChunk(writer, "ICOP", info.copyright);1162writeInfoStringChunk(writer, "ICMT", info.comments);1163writeInfoStringChunk(writer, "ISFT", info.tools);1164writeInfoStringChunk(writer, "IARL", info.archival_location);1165writeInfoStringChunk(writer, "IART", info.artist);1166writeInfoStringChunk(writer, "ICMS", info.commissioned);1167writeInfoStringChunk(writer, "IGNR", info.genre);1168writeInfoStringChunk(writer, "IKEY", info.keywords);1169writeInfoStringChunk(writer, "IMED", info.medium);1170writeInfoStringChunk(writer, "ISBJ", info.subject);1171writeInfoStringChunk(writer, "ISRC", info.source);1172writeInfoStringChunk(writer, "ISRF", info.source_form);1173writeInfoStringChunk(writer, "ITCH", info.technician);1174}11751176public DLSInfo getInfo() {1177return info;1178}11791180@Override1181public String getName() {1182return info.name;1183}11841185@Override1186public String getVersion() {1187return major + "." + minor;1188}11891190@Override1191public String getVendor() {1192return info.engineers;1193}11941195@Override1196public String getDescription() {1197return info.comments;1198}11991200public void setName(String s) {1201info.name = s;1202}12031204public void setVendor(String s) {1205info.engineers = s;1206}12071208public void setDescription(String s) {1209info.comments = s;1210}12111212@Override1213public SoundbankResource[] getResources() {1214SoundbankResource[] resources = new SoundbankResource[samples.size()];1215int j = 0;1216for (int i = 0; i < samples.size(); i++)1217resources[j++] = samples.get(i);1218return resources;1219}12201221@Override1222public DLSInstrument[] getInstruments() {1223DLSInstrument[] inslist_array =1224instruments.toArray(new DLSInstrument[instruments.size()]);1225Arrays.sort(inslist_array, new ModelInstrumentComparator());1226return inslist_array;1227}12281229public DLSSample[] getSamples() {1230return samples.toArray(new DLSSample[samples.size()]);1231}12321233@Override1234public Instrument getInstrument(Patch patch) {1235int program = patch.getProgram();1236int bank = patch.getBank();1237boolean percussion = false;1238if (patch instanceof ModelPatch)1239percussion = ((ModelPatch) patch).isPercussion();1240for (Instrument instrument : instruments) {1241Patch patch2 = instrument.getPatch();1242int program2 = patch2.getProgram();1243int bank2 = patch2.getBank();1244if (program == program2 && bank == bank2) {1245boolean percussion2 = false;1246if (patch2 instanceof ModelPatch)1247percussion2 = ((ModelPatch) patch2).isPercussion();1248if (percussion == percussion2)1249return instrument;1250}1251}1252return null;1253}12541255public void addResource(SoundbankResource resource) {1256if (resource instanceof DLSInstrument)1257instruments.add((DLSInstrument) resource);1258if (resource instanceof DLSSample)1259samples.add((DLSSample) resource);1260}12611262public void removeResource(SoundbankResource resource) {1263if (resource instanceof DLSInstrument)1264instruments.remove(resource);1265if (resource instanceof DLSSample)1266samples.remove(resource);1267}12681269public void addInstrument(DLSInstrument resource) {1270instruments.add(resource);1271}12721273public void removeInstrument(DLSInstrument resource) {1274instruments.remove(resource);1275}12761277public long getMajor() {1278return major;1279}12801281public void setMajor(long major) {1282this.major = major;1283}12841285public long getMinor() {1286return minor;1287}12881289public void setMinor(long minor) {1290this.minor = minor;1291}1292}129312941295