From ceda16246c2b0c0ac8415bb684acc367d79f3fc5 Mon Sep 17 00:00:00 2001 From: Sen Date: Wed, 3 Sep 2025 11:18:13 +0200 Subject: [PATCH] add bank loader --- client/src/main/java/client/Client.java | 4 +- .../main/java/client/audio/BankLoader.java | 934 ++++++++++++++++++ .../main/java/client/audio/Instrument.java | 6 + .../src/main/java/client/audio/MidiBank.java | 74 +- .../src/main/java/client/audio/Operator.java | 17 + .../main/java/client/audio/SoundManager.java | 4 +- .../src/main/java/client/audio/WavWriter.java | 100 ++ .../src/main/java/client/util/FileUtils.java | 14 +- client/src/main/resources/banks/cyberpuck.tmb | Bin 0 -> 3328 bytes client/src/main/resources/banks/d3dtimbr.tmb | Bin 0 -> 3328 bytes .../src/main/resources/banks/d3dtimbr_mod.tmb | Bin 0 -> 3328 bytes client/src/main/resources/banks/dmx_dmx.op2 | Bin 0 -> 11908 bytes client/src/main/resources/banks/dmx_doom1.op2 | Bin 0 -> 11908 bytes client/src/main/resources/banks/dmx_doom2.op2 | Bin 0 -> 11908 bytes .../src/main/resources/banks/dmx_raptor.op2 | Bin 0 -> 11908 bytes .../src/main/resources/banks/dmx_strife.op2 | Bin 0 -> 11908 bytes client/src/main/resources/banks/earthsieg.ad | Bin 0 -> 5122 bytes client/src/main/resources/banks/fat2.op3 | Bin 0 -> 4384 bytes client/src/main/resources/banks/fat4.op3 | Bin 0 -> 4384 bytes client/src/main/resources/banks/gmoconel.tmb | Bin 0 -> 3328 bytes client/src/main/resources/banks/gmopl_mod.tmb | Bin 0 -> 3328 bytes client/src/main/resources/banks/hmi_144.bnk | Bin 0 -> 5404 bytes .../main/resources/banks/hmi_drums_144.bnk | Bin 0 -> 5404 bytes client/src/main/resources/banks/nemesis.opl | Bin 0 -> 3702 bytes client/src/main/resources/banks/op2x2.op3 | Bin 0 -> 4384 bytes .../src/main/resources/banks/std_drums_o3.o3 | Bin 0 -> 7680 bytes .../src/main/resources/banks/std_drums_sb.sb | Bin 0 -> 6656 bytes client/src/main/resources/banks/std_o3.o3 | Bin 0 -> 7680 bytes client/src/main/resources/banks/std_sb.sb | Bin 0 -> 6656 bytes client/src/main/resources/banks/swtimbr.tmb | Bin 0 -> 3328 bytes .../src/main/resources/banks/tmb_default.tmb | Bin 0 -> 3328 bytes client/src/main/resources/banks/wallace.op3 | Bin 0 -> 3112 bytes client/src/main/resources/banks/wallence.tmb | Bin 0 -> 3328 bytes client/src/main/resources/banks/warcraft.ad | Bin 0 -> 3622 bytes .../src/main/resources/banks/wolfinstein.op2 | Bin 0 -> 11908 bytes 35 files changed, 1118 insertions(+), 35 deletions(-) create mode 100644 client/src/main/java/client/audio/BankLoader.java create mode 100644 client/src/main/java/client/audio/WavWriter.java create mode 100644 client/src/main/resources/banks/cyberpuck.tmb create mode 100644 client/src/main/resources/banks/d3dtimbr.tmb create mode 100644 client/src/main/resources/banks/d3dtimbr_mod.tmb create mode 100644 client/src/main/resources/banks/dmx_dmx.op2 create mode 100644 client/src/main/resources/banks/dmx_doom1.op2 create mode 100644 client/src/main/resources/banks/dmx_doom2.op2 create mode 100644 client/src/main/resources/banks/dmx_raptor.op2 create mode 100644 client/src/main/resources/banks/dmx_strife.op2 create mode 100644 client/src/main/resources/banks/earthsieg.ad create mode 100644 client/src/main/resources/banks/fat2.op3 create mode 100644 client/src/main/resources/banks/fat4.op3 create mode 100644 client/src/main/resources/banks/gmoconel.tmb create mode 100644 client/src/main/resources/banks/gmopl_mod.tmb create mode 100644 client/src/main/resources/banks/hmi_144.bnk create mode 100644 client/src/main/resources/banks/hmi_drums_144.bnk create mode 100644 client/src/main/resources/banks/nemesis.opl create mode 100644 client/src/main/resources/banks/op2x2.op3 create mode 100644 client/src/main/resources/banks/std_drums_o3.o3 create mode 100644 client/src/main/resources/banks/std_drums_sb.sb create mode 100644 client/src/main/resources/banks/std_o3.o3 create mode 100644 client/src/main/resources/banks/std_sb.sb create mode 100644 client/src/main/resources/banks/swtimbr.tmb create mode 100644 client/src/main/resources/banks/tmb_default.tmb create mode 100644 client/src/main/resources/banks/wallace.op3 create mode 100644 client/src/main/resources/banks/wallence.tmb create mode 100644 client/src/main/resources/banks/warcraft.ad create mode 100644 client/src/main/resources/banks/wolfinstein.op2 diff --git a/client/src/main/java/client/Client.java b/client/src/main/java/client/Client.java index e91635c3..615adb77 100755 --- a/client/src/main/java/client/Client.java +++ b/client/src/main/java/client/Client.java @@ -2604,8 +2604,10 @@ public class Client implements IThreadListener { } private void startSound(boolean load) { - if(load) + if(load) { SoundManager.loadSounds(); + MidiBank.loadBanks(); + } this.audio = new AudioInterface(this.soundFrameSize, this.soundBufferSize, !this.soundEnabled); boolean started = this.audio.start(); Log.flushLog(); diff --git a/client/src/main/java/client/audio/BankLoader.java b/client/src/main/java/client/audio/BankLoader.java new file mode 100644 index 00000000..c2b4f74e --- /dev/null +++ b/client/src/main/java/client/audio/BankLoader.java @@ -0,0 +1,934 @@ +package client.audio; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; + +import client.util.FileUtils; +import common.log.Log; + +public class BankLoader { + + /* +enum bank_op { + b_op2, + b_op4, + b_op22, + b_op0 +}; + +typedef struct { + uint8_t tremolo; + uint8_t vibrato; + uint8_t sustaining; + uint8_t ksr; + uint8_t mult; + uint8_t ksl; + uint8_t level; + uint8_t attack; + uint8_t decay; + uint8_t sustain; + uint8_t release; + uint8_t waveform; +} bank_operator; + +typedef struct { + bank_operator ops[2]; + int16_t detune; + int16_t offset; + uint8_t feedback; + uint8_t am; +} bank_pair; + +typedef struct { + bank_pair channels[2]; + uint8_t op; + uint8_t fixed; + uint8_t percnum; + char name[32]; +} bank_instr; +*/ + +//#define STR_BNK_WRITERR "Fehler beim Schreiben nach Bank-Datei '%s': " +//#define STR_BNK_PARSERR "Fehler beim Verarbeiten von Bank-Datei '%s': " +//#define STR_BNK_EIO STR_BNK_READERR "E/A-Fehler" +//#define STR_BNK_EEOF STR_BNK_READERR "Ende der Datei erreicht" +//#define STR_BNK_EWIO STR_BNK_WRITERR "E/A-Fehler" +//#define STR_BNK_EEOD STR_BNK_PARSERR "Ende der Daten erreicht" + + private static byte[] bnk_read_mhbank(byte[] raw, String filename, int minsize, int maxsize, byte[] hdr) { + int size = raw.length; + if(size < (hdr == null ? 0 : hdr.length) + minsize) { + Log.SOUND.error("Fehler beim Lesen von Bank-Datei '%s': Datei zu klein", filename); + return null; + } + if(hdr != null) { + if(!Arrays.equals(raw, 0, hdr.length, hdr, 0, hdr.length)) { + Log.SOUND.error("Fehler beim Lesen von Bank-Datei '%s': Fehlerhafter Dateiheader oder falscher Dateityp", filename); + return null; + } + size -= hdr.length; + } + size = maxsize > 0 && size > maxsize ? maxsize : size; + if(size != raw.length) { + byte[] data = new byte[size]; + System.arraycopy(raw, hdr == null ? 0 : hdr.length, data, 0, data.length); + return data; + } + return raw; + } + + private static byte[] bnk_read_mbank(byte[] raw, String filename, int minsize, int maxsize) { + return bnk_read_mhbank(raw, filename, minsize, maxsize, null); + } + + private static byte[] bnk_read_hbank(byte[] raw, String filename, int size, byte[] hdr) { + return bnk_read_mhbank(raw, filename, size, size, hdr); + } + + private static byte[] bnk_read_sbank(byte[] raw, String filename, int size) { + return bnk_read_mhbank(raw, filename, size, size, null); + } + + private static char dmx_read_uint16(byte[] data, int offset) { + char value = 0; + for(int h = 0; h < 2; h++) { + value |= ((char)(data[offset + 1 - h] & 255)); + value <<= h < 1 ? 8 : 0; + } + return value; + } + + private static char dmx_read_uint16be(byte[] data, int offset) { + char value = 0; + for(int h = 0; h < 2; h++) { + value |= ((char)(data[offset + h] & 255)); + value <<= h < 1 ? 8 : 0; + } + return value; + } + + private static Instrument dmx_read_instr(byte[] data, int offset, boolean drum) { + boolean fixed = ((data[offset + 0] & 0x01) != 0) || drum; + boolean op2x2 = ((data[offset + 0] & 0x04) != 0); + int ch2detune = (int)(data[offset + 2] & 255) - 128; + int percnum = data[offset + 3] & 0x7f; + offset += 4; + Operator[] ops = new Operator[op2x2 ? 4 : 2]; + int[] fb = new int[2]; + int algo = op2x2 ? 0x08 : 0x00; + for(int ch = 0; ch < (op2x2 ? 2 : 1); ch++) { + int transpose = (int)dmx_read_uint16(data, offset + 14) + 12; + for(int op = 0; op < 2; op++) { + boolean tremolo = (data[offset + op*7+0] & 0x80) != 0; + boolean vibrato = (data[offset + op*7+0] & 0x40) != 0; + boolean sustaining = (data[offset + op*7+0] & 0x20) != 0; + boolean ksr = (data[offset + op*7+0] & 0x10) != 0; + int mult = data[offset + op*7+0] & 0x0f; + int attack = ((data[offset + op*7+1] & 255) >> 4) & 0x0f; + int decay = data[offset + op*7+1] & 0x0f; + int sustain = ((data[offset + op*7+2] & 255) >> 4) & 0x0f; + int release = data[offset + op*7+2] & 0x0f; + int waveform = data[offset + op*7+3] & 0x07; + int ksl = ((data[offset + op*7+4] & 255) >> 6) & 0x03; + int level = data[offset + op*7+5] & 0x3f; + int detune = ch == 1 ? ch2detune : 0; + ops[ch * 2 + op] = new Operator(mult, level, waveform, attack, decay, sustain, release, tremolo, vibrato, sustaining, ksr, ksl, transpose, detune); + } + fb[ch] = (data[offset + 6] >> 1) & 0x07; + algo |= (data[offset + 6] & 0x01) != 0 ? 1 << ch : 0; + offset += 16; + } + Instrument ins = new Instrument(fb[0], fb[1], algo, ops); + if(fixed) + ins.setFixed(percnum); + return ins; + } + + private static Instrument[] dmx_read_bank(byte[] raw, String filename) { + byte[] data = bnk_read_hbank(raw, filename, 175 * 68, "#OPL_II#".getBytes()); + if(data == null) + return null; + Instrument[] instr = new Instrument[256]; + for(int i = 0; i < instr.length; i++) { + instr[i] = new Instrument(); + } + int offset = 0; + for(int i = 0; i < 175; i++) { + instr[(i < 128) ? i : (i + 35)] = dmx_read_instr(data, offset, i >= 128); + offset += 36; + } + for(int i = 0; i < 175; i++) { + Instrument ins = instr[(i < 128) ? i : (i + 35)]; + int len = 0; + while(len < 32 && data[offset + len] != 0) { + ++len; + } + ins.setName(new String(data, offset, len)); + offset += 32; + } + return instr; + } + + /* + private static void tmb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { + instr->name[0] = 0; + instr->fixed = drum; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[0].ops[op].mult = data[op+0] & 0x0f; + instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[0].ops[op].decay = data[op+4] & 0x0f; + instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[0].ops[op].release = data[op+6] & 0x0f; + instr->channels[0].ops[op].waveform = data[op+8] & 0x07; + instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[0].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[0].feedback = (data[10] >> 1) & 0x07; + instr->channels[0].am = data[10] & 0x01; + instr->percnum = drum ? (data[11] & 0x7f) : 0; + instr->channels[0].offset = drum ? 0 : ((int8_t)data[11]); + instr->channels[0].detune = 0; + } + + bank_instr *tmb_read_bank(const char *filename) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_sbank(&data, &instr, filename, 256*13); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + for(int i = 0; i < 256; i++) { + tmb_read_instr(&instr[i], data, i >= 128); + data += 13; + } + free(odata); + return instr; + } + + void ibk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { + instr->fixed = drum; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[0].ops[op].mult = data[op+0] & 0x0f; + instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[0].ops[op].decay = data[op+4] & 0x0f; + instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[0].ops[op].release = data[op+6] & 0x0f; + instr->channels[0].ops[op].waveform = data[op+8] & 0x07; + instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[0].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[0].feedback = (data[10] >> 1) & 0x07; + instr->channels[0].am = data[10] & 0x01; + instr->percnum = drum ? (data[13] & 0x7f) : 0; + instr->channels[0].offset = drum ? 0 : ((int8_t)data[13]); + instr->channels[0].detune = 0; + } + + bank_instr *ibk_read_bank(const char *filename, uint8_t drum) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_hbank(&data, &instr, filename, 128*25, "IBK\x1a", 4); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + for(int i = 0; i < 128; i++) { + instr[i + (drum ? 0 : 128)].op = b_op0; + instr[i + (drum ? 0 : 128)].name[0] = 0; + } + for(int i = 0; i < 128; i++) { + ibk_read_instr(&instr[i + (drum ? 128 : 0)], data, drum); + data += 16; + } + for(int i = 0; i < 128; i++) { + bank_instr *ins = &instr[i + (drum ? 128 : 0)]; + memcpy(ins->name, data, 8); + ins->name[8] = 0; + data += 9; + } + free(odata); + return instr; + } + + void sb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { + for(int h = 0; h < 28; h++) { + instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h]; + } + instr->name[28] = 0; + instr->percnum = data[35] & 0x7f; + instr->fixed = drum; + instr->op = b_op2; + data += 36; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[0].ops[op].mult = data[op+0] & 0x0f; + instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[0].ops[op].decay = data[op+4] & 0x0f; + instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[0].ops[op].release = data[op+6] & 0x0f; + instr->channels[0].ops[op].waveform = data[op+8] & 0x07; + instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[0].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[0].feedback = (data[10] >> 1) & 0x07; + instr->channels[0].am = data[10] & 0x01; + instr->channels[0].offset = 0; + instr->channels[0].detune = 0; + } + + bank_instr *sb_read_bank(const char *filename, uint8_t drum) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*52); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + for(int i = 0; i < 128; i++) { + instr[i + (drum ? 0 : 128)].op = b_op0; + instr[i + (drum ? 0 : 128)].name[0] = 0; + } + for(int i = 0; i < 128; i++) { + sb_read_instr(&instr[i + (drum ? 128 : 0)], data, drum); + data += 52; + } + free(odata); + return instr; + } + + void sb3_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { + for(int h = 0; h < 28; h++) { + instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h]; + } + instr->name[28] = 0; + instr->percnum = data[35] & 0x7f; + instr->fixed = drum; + instr->op = (data[0] == '4') ? b_op4 : b_op2; + data += 36; + for(int ch = 0; ch < 2; ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[op+0] & 0x0f; + instr->channels[ch].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[op+4] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[op+6] & 0x0f; + instr->channels[ch].ops[op].waveform = data[op+8] & 0x07; + instr->channels[ch].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[ch].feedback = (data[10] >> 1) & 0x07; + instr->channels[ch].am = data[10] & 0x01; + instr->channels[ch].offset = 0; + instr->channels[ch].detune = 0; + data += 11; + } + } + + bank_instr *sb3_read_bank(const char *filename, uint8_t drum) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*60); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + for(int i = 0; i < 128; i++) { + instr[i + (drum ? 0 : 128)].op = b_op0; + instr[i + (drum ? 0 : 128)].name[0] = 0; + } + for(int i = 0; i < 128; i++) { + sb3_read_instr(&instr[i + (drum ? 128 : 0)], data, drum); + data += 60; + } + free(odata); + return instr; + } + + void op3_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { + instr->percnum = data[1] & 0x7f; + instr->fixed = drum; + instr->op = ((data[0] & 0x01) > 0) ? b_op4 : b_op2; + data += 2; + for(int ch = 0; ch < 2; ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f; + instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f; + instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07; + instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f; + } + instr->channels[ch].feedback = (data[5] >> 1) & 0x07; + instr->channels[ch].am = data[5] & 0x01; + instr->channels[ch].offset = 0; + instr->channels[ch].detune = 0; + data += 11; + } + } + + bank_instr *op3_read_bank(const char *filename) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_mhbank(&data, &instr, filename, 16, 256*24+16, "Junglevision Patch File\x1a", 24); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + data += 8; + uint16_t nmelo = dmx_read_uint16(&data[0]); + uint16_t ndrum = dmx_read_uint16(&data[2]); + uint16_t omelo = dmx_read_uint16(&data[4]); + uint16_t odrum = dmx_read_uint16(&data[6]); + data += 8; + if((((nmelo+ndrum)*24+16) > size) || ((omelo+nmelo) > 128) || ((odrum+ndrum) > 128)) { + fprintf(stderr, STR_BNK_EEOD"\n", filename); + free(odata); + free(instr); + return NULL; + } + for(int i = 0; i < nmelo; i++) { + op3_read_instr(&instr[i + omelo], data, 0); + data += 24; + } + for(int i = 0; i < ndrum; i++) { + op3_read_instr(&instr[i + odrum + 128], data, 1); + data += 24; + } + free(odata); + return instr; + } + + void ad_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum, uint16_t isize) { + if(isize < 12) { + return; + } + instr->percnum = data[0] & 0x7f; + instr->fixed = drum; + instr->op = (isize >= 23) ? b_op4 : b_op2; + data += 1; + instr->channels[0].feedback = instr->channels[1].feedback = (data[5] >> 1) & 0x07; + instr->channels[0].am = data[5] & 0x01; + instr->channels[1].am = (data[5] & 0x80) > 0; + for(int ch = 0; ch < ((isize >= 23) ? 2 : 1); ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f; + instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f; + instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07; + instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f; + } + instr->channels[ch].offset = 0; + instr->channels[ch].detune = 0; + data += 11; + } + } + + bank_instr *ad_read_bank(const char *filename) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_mbank(&data, &instr, filename, 2, 256*20+2); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + for(int i = 0;; i++, data += 6) { + if((i*6) >= size) { + fprintf(stderr, STR_BNK_EEOD"\n", filename); + free(odata); + free(instr); + return NULL; + } + uint8_t prog = data[0]; + if(prog == 0xff) { + break; + } + if(prog >= 128) { + continue; + } + uint8_t bank = data[1]; + if((bank != 0) && (bank != 127)) { + continue; + } + uint16_t offs = dmx_read_uint16(data+2); + uint16_t isize; + if(((offs+2) > size) || ((offs+(isize = dmx_read_uint16(odata+offs))) > size) || (isize < 2)) { + fprintf(stderr, STR_BNK_EEOD"\n", filename); + free(odata); + free(instr); + return NULL; + } + ad_read_instr(&instr[prog + ((bank == 127) ? 128 : 0)], odata+(offs+2), bank == 127, isize-2); + } + free(odata); + return instr; + } + + void bk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { + instr->fixed = drum; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = data[op*13+9] & 0x01; + instr->channels[0].ops[op].vibrato = data[op*13+10] & 0x01; + instr->channels[0].ops[op].sustaining = data[op*13+5] & 0x01; + instr->channels[0].ops[op].ksr = data[op*13+11] & 0x01; + instr->channels[0].ops[op].mult = data[op*13+1] & 0x0f; + instr->channels[0].ops[op].attack = data[op*13+3] & 0x0f; + instr->channels[0].ops[op].decay = data[op*13+6] & 0x0f; + instr->channels[0].ops[op].sustain = data[op*13+4] & 0x0f; + instr->channels[0].ops[op].release = data[op*13+7] & 0x0f; + instr->channels[0].ops[op].waveform = data[26+op] & 0x07; + instr->channels[0].ops[op].ksl = data[op*13+0] & 0x03; + instr->channels[0].ops[op].level = data[op*13+8] & 0x3f; + } + instr->channels[0].feedback = data[2] & 0x07; + instr->channels[0].am = (data[25] & 0x01) ^ 1; + instr->channels[0].offset = 0; + instr->channels[0].detune = 0; + } + + bank_instr *bk_read_bank(const char *filename, uint8_t drum) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_mbank(&data, &instr, filename, 28, 256*42+28); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + if(memcmp(data+2, "ADLIB-", 6) != 0) { + fprintf(stderr, STR_BNK_EMFHDR"\n", filename); + free(odata); + free(instr); + return NULL; + } + data += 8; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + // uint16_t nnorm = data[0]; + uint32_t ninst = dmx_read_uint16(&data[2]); + data += 20; + if((ninst*42+28) > size) { + fprintf(stderr, STR_BNK_EEOD"\n", filename); + free(odata); + free(instr); + return NULL; + } + data += ninst*12; + int pos = 0; + for(int i = 0; i < ninst; i++, data += 30) { + if(data[0] != 0) { + continue; + } + bk_read_instr(&instr[pos + (drum ? 128 : 0)], data+2, drum); + uint8_t *ndata = odata+(i*12+28); + instr[pos + (drum ? 128 : 0)].percnum = ndata[2] & 0x7f; + memcpy(instr[pos + (drum ? 128 : 0)].name, ndata+3, 8); + instr[pos + (drum ? 128 : 0)].name[8] = 0; + if(++pos == 128) { + break; + } + } + free(odata); + return instr; + } + + void tim_read_instr(bank_instr *instr, uint8_t *data) { + instr->percnum = 0; + instr->fixed = 0; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = dmx_read_uint16(&data[op*26+18]) & 0x01; + instr->channels[0].ops[op].vibrato = dmx_read_uint16(&data[op*26+20]) & 0x01; + instr->channels[0].ops[op].sustaining = dmx_read_uint16(&data[op*26+10]) & 0x01; + instr->channels[0].ops[op].ksr = dmx_read_uint16(&data[op*26+22]) & 0x01; + instr->channels[0].ops[op].mult = dmx_read_uint16(&data[op*26+2]) & 0x0f; + instr->channels[0].ops[op].attack = dmx_read_uint16(&data[op*26+6]) & 0x0f; + instr->channels[0].ops[op].decay = dmx_read_uint16(&data[op*26+12]) & 0x0f; + instr->channels[0].ops[op].sustain = dmx_read_uint16(&data[op*26+8]) & 0x0f; + instr->channels[0].ops[op].release = dmx_read_uint16(&data[op*26+14]) & 0x0f; + instr->channels[0].ops[op].waveform = dmx_read_uint16(&data[52+op*2]) & 0x07; + instr->channels[0].ops[op].ksl = dmx_read_uint16(&data[op*26+0]) & 0x03; + instr->channels[0].ops[op].level = dmx_read_uint16(&data[op*26+16]) & 0x3f; + } + instr->channels[0].feedback = dmx_read_uint16(&data[4]) & 0x07; + instr->channels[0].am = (dmx_read_uint16(&data[50]) & 0x01) ^ 1; + instr->channels[0].offset = 0; + instr->channels[0].detune = 0; + } + + bank_instr *tim_read_bank(const char *filename, uint8_t drum) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_mbank(&data, &instr, filename, 6, 256*65+6); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + uint32_t ninst = dmx_read_uint16(&data[2]); + data += 6; + if((ninst*65+6) > size) { + fprintf(stderr, STR_BNK_EEOD"\n", filename); + free(odata); + free(instr); + return NULL; + } + data += ninst*9; + for(int i = 0; (i < ninst) && (i < 128); i++, data += 56) { + tim_read_instr(&instr[i + (drum ? 128 : 0)], data); + uint8_t *ndata = odata+(i*9+6); + memcpy(instr[i + (drum ? 128 : 0)].name, ndata, 8); + instr[i + (drum ? 128 : 0)].name[8] = 0; + } + free(odata); + return instr; + } + + void wopl_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { + memcpy(instr->name, data, 32); + instr->name[31] = 0; + data += 32; + instr->percnum = data[6] & 0x7f; + instr->fixed = drum; + instr->op = (data[7] & 0x01) ? (((data[7] & 0x02) > 0) ? b_op22 : b_op4) : b_op2; + instr->channels[0].offset = (int16_t)dmx_read_uint16be(&data[0]); + instr->channels[0].detune = 0; + if(instr->op != b_op2) { + instr->channels[1].offset = (int16_t)dmx_read_uint16be(&data[2]); + instr->channels[0].detune = (int8_t)data[5]; + } + instr->channels[0].am = data[8] & 0x01; + instr->channels[0].feedback = (data[8] >> 1) & 0x07; + if(instr->op != b_op2) { + instr->channels[1].am = data[9] & 0x01; + instr->channels[1].feedback = (data[9] >> 1) & 0x07; + } + data += 10; + for(int ch = 0; ch < ((instr->op != b_op2) ? 2 : 1); ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[1-op].tremolo = (data[op*5+0] & 0x80) > 0; + instr->channels[ch].ops[1-op].vibrato = (data[op*5+0] & 0x40) > 0; + instr->channels[ch].ops[1-op].sustaining = (data[op*5+0] & 0x20) > 0; + instr->channels[ch].ops[1-op].ksr = (data[op*5+0] & 0x10) > 0; + instr->channels[ch].ops[1-op].mult = data[op*5+0] & 0x0f; + instr->channels[ch].ops[1-op].attack = (data[op*5+2] >> 4) & 0x0f; + instr->channels[ch].ops[1-op].decay = data[op*5+2] & 0x0f; + instr->channels[ch].ops[1-op].sustain = (data[op*5+3] >> 4) & 0x0f; + instr->channels[ch].ops[1-op].release = data[op*5+3] & 0x0f; + instr->channels[ch].ops[1-op].waveform = data[op*5+4] & 0x07; + instr->channels[ch].ops[1-op].ksl = (data[op*5+1] >> 6) & 0x03; + instr->channels[ch].ops[1-op].level = data[op*5+1] & 0x3f; + } + data += 10; + } + } + + bank_instr *wopl_read_bank(const char *filename) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_mhbank(&data, &instr, filename, 256*62+9, 512*66+77, "WOPL3-BANK", 10); + if(size == 0) { + return NULL; + } + uint8_t *odata = data; + int stride = (data[7] != 0) ? 66 : 62; + int off = (data[7] != 0) ? 77 : 9 + ((data[4] == 2) ? 102 : 0); + int off2 = (data[4] == 2) ? (128*stride) : 0; + if((256*stride+off+off2) > size) { + fprintf(stderr, STR_BNK_EEOD"\n", filename); + free(odata); + free(instr); + return NULL; + } + data += off; + for(int i = 0; i < 128; i++) { + wopl_read_instr(&instr[i], data, 0); + data += stride; + } + data += off2; + for(int i = 0; i < 128; i++) { + wopl_read_instr(&instr[i + 128], data, 1); + data += stride; + } + free(odata); + return instr; + } + + // SKB! + // OO-NNNNN NAME[N] FPPPPPPP AFFFAFFF + // DDDDDDDD OOOOOOOO TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR -WWW-WWW + + uint32_t skb_read_instr(bank_instr *instr, uint8_t *data, uint32_t pos, uint32_t size) { + if((pos + 1) > size) { + return 0; + } + data += pos; + instr->op = (data[0] >> 6) & 0x03; + uint8_t nlen = data[0] & 0x1f; + if((pos + 1 + nlen) > size) { + return 0; + } + memcpy(instr->name, data+1, nlen); + instr->name[nlen] = 0; + pos += 1 + nlen; + if(instr->op == b_op0) { + return pos; + } + int numch = (instr->op == b_op2) ? 1 : 2; + if((pos + 1 + (12 * numch)) > size) { + return 0; + } + data += 1 + nlen; + instr->fixed = (data[0] & 0x80) > 0; + instr->percnum = data[0] & 0x7f; + instr->channels[0].am = (data[1] & 0x08) > 0; + instr->channels[0].feedback = data[1] & 0x07; + if(numch == 2) { + instr->channels[1].am = (data[1] & 0x80) > 0; + instr->channels[1].feedback = (data[1] >> 4) & 0x07; + } + data += 2; + for(int ch = 0; ch < numch; ch++) { + instr->channels[ch].detune = ((int16_t)data[0]) - 128; + instr->channels[ch].offset = ((int16_t)data[1]) - 128; + data += 2; + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[0] & 0x0f; + instr->channels[ch].ops[op].ksl = (data[1] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[1] & 0x3f; + instr->channels[ch].ops[op].attack = (data[2] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[2] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[3] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[3] & 0x0f; + data += 4; + } + instr->channels[ch].ops[0].waveform = (data[0] >> 4) & 0x07; + instr->channels[ch].ops[1].waveform = data[0] & 0x07; + data += 1; + } + return pos + 2 + (11 * numch); + } + + bank_instr *skb_read_bank(const char *filename) { + uint8_t *data; + bank_instr *instr; + uint32_t size = bnk_read_mhbank(&data, &instr, filename, 256, 256*56, "SKB!", 4); + if(size == 0) { + return NULL; + } + uint32_t pos = 0; + for(int i = 0; i < 256; i++) { + pos = skb_read_instr(&instr[i], data, pos, size); + if(pos == 0) { + fprintf(stderr, STR_BNK_EEOD"\n", filename); + free(data); + free(instr); + return NULL; + } + } + free(data); + return instr; + } + */ + + private static Instrument[] bnk_read_bank(byte[] raw, String name, boolean drum) { + int dot = name.lastIndexOf('.'); + if(dot < 0 || dot == name.length() - 1) { + Log.SOUND.error("Bank-Datei '%s' hat keine Erweiterung", name); + return null; + } + String ext = name.substring(dot + 1); +// if(strcmp(ext, "skb") == 0) { +// return skb_read_bank(filename); +// } +// else + if(ext.equalsIgnoreCase("dmx") || ext.equalsIgnoreCase("op2") || ext.equalsIgnoreCase("lmp")) { + return dmx_read_bank(raw, name); + } +// else if(strcmp(ext, "tmb") == 0) { +// return tmb_read_bank(filename); +// } +// else if(strcmp(ext, "ibk") == 0) { +// return ibk_read_bank(filename, drum); +// } +// else if(strcmp(ext, "sb") == 0) { +// return sb_read_bank(filename, drum); +// } +// else if(strcmp(ext, "o3") == 0) { +// return sb3_read_bank(filename, drum); +// } +// else if(strcmp(ext, "op3") == 0) { +// return op3_read_bank(filename); +// } +// else if((strcmp(ext, "ad") == 0) || (strcmp(ext, "opl") == 0)) { +// return ad_read_bank(filename); +// } +// else if(strcmp(ext, "bnk") == 0) { +// return bk_read_bank(filename, drum); +// } +// else if((strcmp(ext, "tim") == 0) || (strcmp(ext, "snd") == 0)) { +// return tim_read_bank(filename, drum); +// } + // else if(strcmp(ext, "wopl") == 0) { + // return wopl_read_bank(filename); + // } + Log.SOUND.error("Format von Bank-Datei '%s' unbekannt", name); + return null; + } + + private static Instrument[] readBank(File file, boolean drum) { + byte[] raw; + try { + raw = Files.readAllBytes(file.toPath()); + } + catch(FileNotFoundException e) { + Log.SOUND.error("Fehler beim Lesen von Bank-Datei '%s': Datei nicht gefunden", file); + return null; + } + catch(IOException e) { + Log.SOUND.error(e, "Fehler beim Lesen von Bank-Datei '%s'", file); + return null; + } + return bnk_read_bank(raw, file.getAbsolutePath(), drum); + } + + private static Instrument[] readBank(String path, boolean drum) { + byte[] raw; + try { + raw = FileUtils.readBytes(path); + } + catch(FileNotFoundException e) { + Log.SOUND.error("Fehler beim Lesen von Bank-Datei '%s': Datei nicht gefunden", path); + return null; + } + catch(IOException e) { + Log.SOUND.error(e, "Fehler beim Lesen von Bank-Datei '%s'", path); + return null; + } + return bnk_read_bank(raw, path, drum); + } + + public static Instrument[] readBank(String filename, String drumname, boolean usedrum) { + Instrument[] ins1 = readBank(filename, usedrum); + if(ins1 != null && drumname != null) { + Instrument[] ins2 = readBank(drumname, !usedrum); + if(ins2 == null) + return null; + System.arraycopy(ins2, 128, ins1, 128, 128); + } + return ins1; + } + + /* + uint32_t skb_write_instr(bank_instr *instr, uint8_t *data, uint32_t pos) { + data += pos; + uint8_t nlen = strlen(instr->name); + data[0] = (instr->op << 6) | nlen; + memcpy(data+1, instr->name, nlen); + pos += 1 + nlen; + if(instr->op == b_op0) { + return pos; + } + int numch = (instr->op == b_op2) ? 1 : 2; + data += 1 + nlen; + data[0] = (instr->fixed << 7) | instr->percnum; + data[1] = ((numch == 2) ? ((instr->channels[1].am << 7) | (instr->channels[1].feedback << 4)) : 0) | (instr->channels[0].am << 3) | instr->channels[0].feedback; + data += 2; + for(int ch = 0; ch < numch; ch++) { + data[0] = (uint8_t)((int16_t)(instr->channels[ch].detune + 128)); + data[1] = (uint8_t)((int16_t)(instr->channels[ch].offset + 128)); + data += 2; + for(int op = 0; op < 2; op++) { + data[0] = (instr->channels[ch].ops[op].tremolo << 7) | (instr->channels[ch].ops[op].vibrato << 6) | (instr->channels[ch].ops[op].sustaining << 5) | + (instr->channels[ch].ops[op].ksr << 4) | instr->channels[ch].ops[op].mult; + data[1] = (instr->channels[ch].ops[op].ksl << 6) | instr->channels[ch].ops[op].level; + data[2] = (instr->channels[ch].ops[op].attack << 4) | instr->channels[ch].ops[op].decay; + data[3] = (instr->channels[ch].ops[op].sustain << 4) | instr->channels[ch].ops[op].release; + data += 4; + } + data[0] = (instr->channels[ch].ops[0].waveform << 4) | instr->channels[ch].ops[1].waveform; + data += 1; + } + return pos + 2 + (11 * numch); + } + + bool skb_write_bank(bank_instr *instr, const char *filename) { + int err; + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) { + err = errno; + fprintf(stderr, STR_BNK_OPNERR"\n", filename, strerror(err), err); + return false; + } + if(fwrite("SKB!", 1, 4, fd) != 4) { + fprintf(stderr, STR_BNK_EWIO"\n", filename); + fclose(fd); + return false; + } + uint8_t *data = malloc(256*56); + uint32_t size = 0; + for(int i = 0; i < 256; i++) { + size = skb_write_instr(&instr[i], data, size); + } + if(fwrite(data, 1, size, fd) != size) { + fprintf(stderr, STR_BNK_EWIO"\n", filename); + fclose(fd); + free(data); + return false; + } + fclose(fd); + free(data); + return true; + } + */ +} diff --git a/client/src/main/java/client/audio/Instrument.java b/client/src/main/java/client/audio/Instrument.java index d4dd5f7f..cdc7696f 100644 --- a/client/src/main/java/client/audio/Instrument.java +++ b/client/src/main/java/client/audio/Instrument.java @@ -8,6 +8,7 @@ public class Instrument { public boolean fixed; public int percnum; + public String name; public Instrument(int feedback1, int feedback2, int algo, Operator ... ops) { this.ops = ops; @@ -58,4 +59,9 @@ public class Instrument { this.percnum = percnum; return this; } + + public Instrument setName(String name) { + this.name = name; + return this; + } } diff --git a/client/src/main/java/client/audio/MidiBank.java b/client/src/main/java/client/audio/MidiBank.java index 224b5f4d..4cacdb96 100644 --- a/client/src/main/java/client/audio/MidiBank.java +++ b/client/src/main/java/client/audio/MidiBank.java @@ -2,39 +2,54 @@ package client.audio; import java.util.Arrays; +import common.log.Log; import common.util.Displayable; import common.util.Identifyable; public enum MidiBank implements Identifyable, Displayable { - DMX_DMX("dmx_dmx", "DMX"), - DMX_DOOM1("dmx_doom1", "DMX DOOM I"), - DMX_DOOM2("dmx_doom2", "DMX DOOM II"), - DMX_RAPTOR("dmx_raptor", "DMX Raptor"), - DMX_STRIFE("dmx_strife", "DMX Strife"), - WOLFINSTEIN("wolfinstein", "Wolfenstein"), - STD_O3("std_o3", "SB3 Standard"), - STD_SB("std_sb", "SB Standard"), - HMI_144("hmi_144", "HMI-144"), - CYBERPUCK("cyberpuck", "Cyberpuck"), - D3DTIMBR("d3dtimbr", "D3D"), - D3DTIMBR_MOD("d3dtimbr_mod", "D3D Mod"), - GMOCONEL("gmoconel", "GMOConel"), - GMOPL_MOD("gmopl_mod", "GM Mod"), - SWTIMBR("swtimbr", "SWTimbr"), - TMB_DEFAULT("tmb_default", "TMB"), - WALLENCE("wallence", "Wallence"), - FAT2("fat2", "FatMan 2OP"), - FAT4("fat4", "FatMan 4OP"), - OP2X2("op2x2", "2x2OP"), - WALLACE("wallace", "Wallace"), - EARTHSIEG("earthsieg", "Earthsieg"), - WARCRAFT("warcraft", "Warcraft"), - NEMESIS("nemesis", "Nemesis"); - + DMX_DMX("dmx_dmx", "op2", "DMX"), + DMX_DOOM1("dmx_doom1", "op2", "DMX DOOM I"), + DMX_DOOM2("dmx_doom2", "op2", "DMX DOOM II"), + DMX_RAPTOR("dmx_raptor", "op2", "DMX Raptor"), + DMX_STRIFE("dmx_strife", "op2", "DMX Strife"), + WOLFINSTEIN("wolfinstein", "op2", "Wolfenstein"), + STD_O3("std_o3", "std_drums_o3", "o3", "SB3"), + STD_SB("std_sb", "std_drums_sb", "sb", "SB"), + HMI_144("hmi_144", "hmi_drums_144", "bnk", "HMI-144"), + CYBERPUCK("cyberpuck", "tmb", "Cyberpuck"), + D3DTIMBR("d3dtimbr", "tmb", "D3D"), + D3DTIMBR_MOD("d3dtimbr_mod", "tmb", "D3D Mod"), + GMOCONEL("gmoconel", "tmb", "GMOConel"), + GMOPL_MOD("gmopl_mod", "tmb", "GM Mod"), + SWTIMBR("swtimbr", "tmb", "SWTimbr"), + TMB_DEFAULT("tmb_default", "tmb", "TMB"), + WALLENCE("wallence", "tmb", "Wallence"), + FAT2("fat2", "op3", "FatMan 2OP"), + FAT4("fat4", "op3", "FatMan 4OP"), + OP2X2("op2x2", "op3", "2x2OP"), + WALLACE("wallace", "op3", "Wallace"), + EARTHSIEG("earthsieg", "ad", "Earthsieg"), + WARCRAFT("warcraft", "ad", "Warcraft"), + NEMESIS("nemesis", "opl", "Nemesis"); + private final String name; private final String display; + private final String melodic; + private final String percussion; private final Instrument[] data = new Instrument[256]; + public static void loadBanks() { + for(MidiBank bank : values()) { + Log.SOUND.trace("Lade Bank %s", bank); + Instrument[] data = BankLoader.readBank(bank.melodic, bank.percussion, false); + if(data == null) + Arrays.fill(bank.data, new Instrument()); + else + System.arraycopy(data, 0, bank.data, 0, bank.data.length); + } + } + + /* static { init0(); init1(); @@ -7157,13 +7172,20 @@ public enum MidiBank implements Identifyable, Displayable { .perc(90, new Instrument(new Operator(0, 63, 0, 10, 0, 15, 8), new Operator(0, 63, 1, 9, 8, 9, 7).ksr(), false, 0, 0, 7).setFixed(60)) .perc(91, new Instrument(new Operator(1, 63, 1, 15, 0, 15, 4).ksl(3).vibrato(), new Operator(9, 63, 3, 15, 0, 15, 8), false, 0, 0, 7).setFixed(60)); } + */ - private MidiBank(String name, String display) { + private MidiBank(String name, String percussion, String extension, String display) { this.name = name; this.display = display; + this.melodic = "banks/" + name + "." + extension; + this.percussion = percussion == null ? null : "banks/" + percussion + "." + extension; Arrays.fill(this.data, new Instrument()); } + private MidiBank(String name, String extension, String display) { + this(name, null, extension, display); + } + private MidiBank instr(int id, Instrument instr) { this.data[id] = instr; return this; diff --git a/client/src/main/java/client/audio/Operator.java b/client/src/main/java/client/audio/Operator.java index 107f27ff..ab43d8ac 100644 --- a/client/src/main/java/client/audio/Operator.java +++ b/client/src/main/java/client/audio/Operator.java @@ -17,6 +17,23 @@ public class Operator { public int offset; public int detune; + Operator(int mult, int level, int waveform, int a, int d, int s, int r, boolean tremolo, boolean vibrato, boolean sustaining, boolean ksr, int ksl, int offset, int detune) { + this.mult = mult; + this.level = level; + this.waveform = waveform; + this.attack = a; + this.decay = d; + this.sustain = s; + this.release = r; + this.tremolo = tremolo; + this.vibrato = vibrato; + this.sustaining = sustaining; + this.ksr = ksr; + this.ksl = ksl; + this.offset = offset; + this.detune = detune; + } + public Operator(int mult, int level, int waveform, int a, int d, int s, int r) { this.mult = mult; this.level = 63 - level; diff --git a/client/src/main/java/client/audio/SoundManager.java b/client/src/main/java/client/audio/SoundManager.java index b37eb87a..776c1741 100755 --- a/client/src/main/java/client/audio/SoundManager.java +++ b/client/src/main/java/client/audio/SoundManager.java @@ -117,11 +117,11 @@ public class SoundManager { return CodecJOrbis.readAll(in); } catch(FileNotFoundException e) { - Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': Datei nicht gefunden", filename); + Log.IO.error("Fehler beim Lesen von OGG-Datei '%s': Datei nicht gefunden", filename); return null; } catch(Exception e) { - Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': %s", filename, e.getMessage()); + Log.IO.error("Fehler beim Lesen von OGG-Datei '%s': %s", filename, e.getMessage()); return null; } finally { diff --git a/client/src/main/java/client/audio/WavWriter.java b/client/src/main/java/client/audio/WavWriter.java new file mode 100644 index 00000000..592360f2 --- /dev/null +++ b/client/src/main/java/client/audio/WavWriter.java @@ -0,0 +1,100 @@ +package client.audio; + +public class WavWriter { + /* + static const char *wav_hdr = "RIFF"; + static const char *wav_hdr_fmt = "fmt "; + static const char *wav_hdr_data = "data"; + static const char *wav_format = "WAVE"; + #define WAV_HDRSIZE 44 + + #define STR_WAV_OPNERR "Fehler beim Oeffnen von WAV-Datei '%s': %s (%d)" + #define STR_WAV_WRITERR "Fehler beim Schreiben nach WAV-Datei '%s': " + #define STR_WAV_EIO STR_WAV_WRITERR "E/A-Fehler" + #define STR_WAV_EGEN STR_WAV_WRITERR "%s (%d)" + + typedef struct { + uint32_t samplerate; + uint16_t channels; + uint16_t bytes; + uint32_t samples; + FILE *fd; + const char *filename; + uint8_t *buffer; + } wav_handle; + + void wav_write_uint32(uint8_t *data, uint32_t value) { + for(int h = 0; h < 4; h++) { + data[h] = ((uint32_t)((value >> (8*h) & 0xff))); + } + } + + void wav_write_uint16(uint8_t *data, uint16_t value) { + for(int h = 0; h < 2; h++) { + data[h] = ((uint16_t)((value >> (8*h) & 0xff))); + } + } + + void wav_write_header(uint8_t *data, uint32_t samplerate, uint16_t channels, uint32_t samples, uint16_t bytes) { + memcpy(data+0, wav_hdr, 4); + wav_write_uint32(data+4, 36 + samples * ((uint32_t)channels) * ((uint32_t)bytes)); + memcpy(data+8, wav_format, 4); + memcpy(data+12, wav_hdr_fmt, 4); + wav_write_uint32(data+16, 16); + wav_write_uint16(data+20, 1); + wav_write_uint16(data+22, channels); + wav_write_uint32(data+24, samplerate); + wav_write_uint32(data+28, samplerate * ((uint32_t)channels) * ((uint32_t)bytes)); + wav_write_uint16(data+32, channels * bytes); + wav_write_uint16(data+34, 8 * bytes); + memcpy(data+36, wav_hdr_data, 4); + wav_write_uint32(data+40, samples * ((uint32_t)channels) * ((uint32_t)bytes)); + } + + bool wav_open(wav_handle *wav, const char *filename, uint32_t samplerate, uint16_t channels, uint16_t bytes) { + int err; + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) { + err = errno; + mid_log(STR_WAV_OPNERR, filename, strerror(err), err); + return false; + } + uint8_t pad[WAV_HDRSIZE]; + memset(pad, 0, WAV_HDRSIZE); + if(fwrite(pad, 1, WAV_HDRSIZE, fd) != WAV_HDRSIZE) { + mid_log(STR_WAV_EIO, filename); + fclose(fd); + return false; + } + wav->fd = fd; + wav->samplerate = samplerate; + wav->channels = channels; + wav->bytes = bytes; + wav->samples = 0; + wav->filename = filename; + return true; + } + + bool wav_close(wav_handle *wav) { + if(wav->fd == NULL) { + return false; + } + int err; + if(fseek(wav->fd, 0L, SEEK_SET)) { + err = errno; + mid_log(STR_WAV_EGEN, wav->filename, strerror(err), err); + fclose(wav->fd); + return false; + } + uint8_t header[WAV_HDRSIZE]; + wav_write_header(header, wav->samplerate, wav->channels, wav->samples, wav->bytes); + if(fwrite(header, 1, WAV_HDRSIZE, wav->fd) != WAV_HDRSIZE) { + mid_log(STR_WAV_EIO, wav->filename); + fclose(wav->fd); + return false; + } + fclose(wav->fd); + return true; + } +*/ +} diff --git a/client/src/main/java/client/util/FileUtils.java b/client/src/main/java/client/util/FileUtils.java index 04ce0067..86e575bf 100644 --- a/client/src/main/java/client/util/FileUtils.java +++ b/client/src/main/java/client/util/FileUtils.java @@ -12,17 +12,15 @@ import java.nio.charset.Charset; public class FileUtils { public static final Charset UTF_8 = Charset.forName("UTF-8"); - private static String read(InputStream in) throws IOException { - String s; + private static byte[] read(InputStream in) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); try { - ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; long count = 0L; int n; for(boolean u = false; -1 != (n = in.read(buffer)); count += (long)n) { output.write(buffer, 0, n); } - s = new String(output.toByteArray(), UTF_8); } finally { try { @@ -32,14 +30,18 @@ public class FileUtils { catch(IOException e) { } } - return s; + return output.toByteArray(); } public static String read(File file) throws IOException { - return read(new FileInputStream(file)); + return new String(read(new FileInputStream(file)), UTF_8); } public static String read(String resource) throws IOException { + return new String(read(getResource(resource)), UTF_8); + } + + public static byte[] readBytes(String resource) throws IOException { return read(getResource(resource)); } diff --git a/client/src/main/resources/banks/cyberpuck.tmb b/client/src/main/resources/banks/cyberpuck.tmb new file mode 100644 index 0000000000000000000000000000000000000000..4ebadab218d23b1dbe7a321fa279007057f654ec GIT binary patch literal 3328 zcmeH}=}%Nw6u{4YZ{9F01H7R^aOJU7yF_`2jY}J60FlP%Ff3BF#ThEnCe}JELb~Wh zJGBkfY6TTPsGzoqX>4&zmk)(T)bx|u8k@#8z=zhPi}N1yW(KWH&%HyH`4dcgGau%c zv)*&hx%Vlm6ZGp9O#o&9LRN_S_42J8(&S5$t{DaaT&lWB=mXoC)NRcESRk(fwW9Hn;( zbrp#XJcHrkt6&5^O97GukR~5M*NVs`vb-CCAmED0S~1&wT0kw~5<+Cqjalz;8?~Se zFzeaCsuKfOF51MVz&A(v<^&*Lx$t_Ke4rrKMo#FA*?v(w|YG_^lXSgjr3I_=)NO6)g0xtJDnIGm}P z-qcLk$qfvoz`i=OLGHJ2zgLwdGqbNsd z<*Bt(ISh|XN$|FxJ~AQML%-hp1ct|o4iBeKNRWp{a==SZAK_g^Y)5HK?gEweq0a@EoXVD11p| z+t#$hG|7GWB(R|sWT*5XYZI|Z;T>_&Id!{5Q*f&j2Lp3M2PSl24tfkM!_&s`9`dJ)eM7Dm0mFcch?Yo1 zz{bapiJ3V~E2Y`0JeRP7NTqgqR{uVi=SE~J)yg9g9tUI_P33?YoHKUO6U&4mF3~iH zL&n8z&V?pPJnky7trwQ_yDG+pjo11}3TBB*QC7qD1rCK*)T@GB(+G||CQ2-Jgvv^; zrl#+rJ#Wj!ygDS)$p$@oJrT>lR{%DWK)R_Bte&vRE|#zlk_an;S~ndY%mVFN32nZj zDjqb>Qbx7E8!;lT1#$_5jYtcZ2+$87MzH-^S1~77t{gnbuZ@DJ+IFr6uyiSa-_ISy zNC=^H{d&Cl2zqxFfwXa>WM(G1-!KUoU9Uhz-=HfNHS%I*nN42+gjU-PbR-iiJZ z=%0zTCIol7Z8Jb*DZ7=?#{3Szu|a<{>zH)@2UhJ#=ko}G6>gur!v-)9x4__bn1c<> z=0EN(#Ai0y4)d@B{CABwy#42}!AEHQ6D9fV{sw=@AL4-j#b7ANx_2&=VrTisp#k58 z*HOSFmrBU=ZIQQV7LNa^G(vLb7|Z@kPnazmixc%3gRcqnkX0?svu8<{r*kk zFJKK6$dcu>*0xX>1nVlu$uT9gz8eaPLegRm8>3JNuPXh}c5`eDztU6=4L3tkNGtFp zg^g>Nfw>ye3N~57!D~T;lF9HK5Rz!Ct-W=Nkc8UFuok4pTvQLZ-@vzV1Nb@-bT@t) QB0NH~@Muo(e=_TT04zBIV*mgE literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/d3dtimbr.tmb b/client/src/main/resources/banks/d3dtimbr.tmb new file mode 100644 index 0000000000000000000000000000000000000000..bc65895a2289a420c5c969e821d8196293841e0d GIT binary patch literal 3328 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 JHMckb{tZ+5(rf?# literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/dmx_dmx.op2 b/client/src/main/resources/banks/dmx_dmx.op2 new file mode 100644 index 0000000000000000000000000000000000000000..111091d74eaca6e2b20d7c7bb4dda994d8a98aa7 GIT binary patch literal 11908 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 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/dmx_doom1.op2 b/client/src/main/resources/banks/dmx_doom1.op2 new file mode 100644 index 0000000000000000000000000000000000000000..f4182961d24ed3524f9f0e8d8f37194b80151458 GIT binary patch literal 11908 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 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/dmx_strife.op2 b/client/src/main/resources/banks/dmx_strife.op2 new file mode 100644 index 0000000000000000000000000000000000000000..4c13b7fd59282b9d374f08b4c83fac517d4208ae GIT binary patch literal 11908 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 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/earthsieg.ad b/client/src/main/resources/banks/earthsieg.ad new file mode 100644 index 0000000000000000000000000000000000000000..a918f4c6e3f1667483a3e928e95904204f2f95ae GIT binary patch literal 5122 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 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/fat2.op3 b/client/src/main/resources/banks/fat2.op3 new file mode 100644 index 0000000000000000000000000000000000000000..729f6fc630b1a581a17853b0ebf2085459ac3833 GIT binary patch literal 4384 zcmai%ZERE58OQ(UT>JWByi1&r7Z`1FV?d0x8V;nWEE9GhuZ5-=P5{xWyJ;Z$NJs>dG7OeUfdpkruXpvzJt#mJn~GWq<_IlHN0sPJ#QaRFcS1X+$#M3>h)2c%1J^i-;0PNpV#La)L12k@8bMBDjme zd0IT0_WnK%U&6YSE~bFe7SA$Q!g^QoGdh}E4jx3(L-n!c*Y<#Kz%`BXA8dc83a%-f zl6YWjBESUiT2A$5*@WgrVlQQRT zq{o>rY^ASL`${DJI`jFFyhC~5$>bKmKuW5k@^Y@0sfLx7dCPRf(dWdh;t^;c2{X-b zA74>SMNhY%TOl~cCBDS2>40|J`dG4W+&`{@PvYv^)K0K8xei`Ymnw;$PX2Y4|4`!l zI@iHhhiEJ1Y{-5cnh{EUPPs8OSs}P)OFTG~S=jz!{*-@K=EU2*mf8l%%)M z9(@=v5$&aRqPrt*_+ofY(w{}-DQIC_ZlZc)bf6MGgAFf{_;}pm?)kj0-0}0J%~T)U zcCnIi13eGWc#R9@Aqf7Wm+H+^@oH$b>d|GC+b3dOB5%i{Qr{XoBMqY8@b)UI4-SuU zrkRK>mH3%4WtK~E1xExI`F}~~XU3K}JSO>LJn~M3)1SkV|G~3Asub%TLp~aBFcDwD zPzO_5zjbFX7;qVgRZ>51Ru4Q0+{Ay+wz=mwn+F%<_8TH^BA;>DpP}P>oPB1VogD|d z0UP_J_0{uHU$euz9-{vAytdner8c^y{j+DRf0p-2ymoIv;o`joId6JlnbZHyXYr_Q zKG7xow4Rph+jOAR@#j@3E@NnPJwq)<))!j=CNF;O1~)@TB!6mF{zJU*dl*(ZnP3bj zzX7jdWu&_IU*0tiZsW(&dNHf7_~D(h)+odb%*v5XPXBjF`$_bPhPNrV6{E~h16@L(_ZQUp3;U_!N z&oY;ZM1a1R5BzQy;89XJng_q{lAoc>n!HW*el-_nF0l{)g$6t}@`pNTBlw$`RXkiB zGu;qt^h2q>M%xQDP-}5SUcUpMZFKGr-BNvPT%WqRY9xlGea3w#c^j%?U6uUwl>dD# zyc~JSj{D8tdU0Qc=w33aUY!x=bt&?aostt*JW!#`Ncr@K^DW#}qNF9x&LmZ?mmp2k z10B_X{wq`$=mgcuuZFG+b|0q`bmWEphI^BX>UHGi>~P9IIk))|@M)-9Iv466|C!0X z%Qfc|D;{%pZlEJe?W{$CLBEC43R*`X{iO&3xnC1M10~z@{ao`SP0#(s_$Ka|E zg<@~80r;1j3;Q#4f8GLK5G#(MK+pGvMfBei9{M2esyXVKo?D&t1^n-if=UE|EuP`|Tnme|Gts@|(FMkpU~m-7JY~yTcWf%6#jbejaaUdwTBx9C3O+HiVnkxe+`YQh}(bq4$?*0uSkRDn9 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/fat4.op3 b/client/src/main/resources/banks/fat4.op3 new file mode 100644 index 0000000000000000000000000000000000000000..daf307e6a6cd24d1ae84e2e797b608bb61723fb8 GIT binary patch literal 4384 zcmaKvdr;KZ701ur7Yj(2-{OMe1AYs>Fm3npSU2ltU5J3%bc_lZ$2htwqM0;Af;crv zIlq0dXkv(>qKU>V_-fLQ#k~7Z6rv$bTc^=FPNo@S1f5Ruhr6)LO04wUpTsn6tbd&O z?q?3?cka38o_k%i<*B+SYnwJbv#I{6+(*5Q>o(?AY9D1%3x2zt#h;ECwyC0>;$=D?5en025ner0BDfl7a((m$x)?^^N9kbq0CGu429upVH_*m#koctTn945`=P%#n=qNrM zorC$42bTmWPP8MyAbm~rQ7q=GuHHmj=P3`x$H+f}3^ddJdY$6H&~;1-$F3tUg~0)e zlT{VHM)@0hzQT#KqrJ#?B}G3IT&~e!A%nOMvu7OBA%_%_45qze{53j*E3aKg%i2MT zqW~?P^6VO}Mtg2r@G?%wBgthL5P7C7yLAbj0UW-BmR<2<$SI`oFWqtREiA^2p0}}l z#RVV5!LDn>S>VPnKBQgPSgkO8OHcmTG)(;UU}pi&vA%Y+06YC;_{_cG0+sI~E~oZ6 zF;_X~pzmUQIq_>X#|jjO*MBkvukIql*9uAbul|DuT{h3t6uRQHhV~2B=!whieHL_a zs3)Fzo=jn=S@68yHDrfuvcI7K^Q>#L13V0C#doKLM-AxA`}wFI!#Sh+fX#~a$-;l) z+HA!oqwt?9{H@|oss0BC8_`)R_EB^6LZgrSJQu|N7O#xPeJ=i5bQtsABt!ccxk22z zY;c6)Z-|bde7t7_Z8G$TI%pmlv7&=@3=X31P|u(u`bTgn^zljJ5KADN(7PYG26{BG(egz$}Cwc{Sskc5C`#yX{@a@Fy|I6jc@ji62oCqax znGD5`ihKlD#<;nv>>lTqFESK|3^miyymzNo;o28!TSPut_u+fL$Ozu`&rtpKFK!V2 zPw)Qo!#?-x+QuxOy!XQ-(dYF^3IlIPk0odii~d=c@lG4^qkG6wosS0t=r$kmHKJw) z8LCgPQ%N$g?e}WMch_h6yt`|&6#8y$mdZ=>eC{=D zewHu1znRu0!#t6XoPEZOE-x&m{%j{noFwrHXRKq~no5#eC#&3`eLaTm{=BXd3@>S> zb$Nj-^_xDhoOtH(Mhw5)_@LmM>xie+9;iY`(wdHS7+$ejr`WtuZy*j(yU{ln;A}<< zK%b$712iW2>ASmwS?H2kTQbU)Ko-SGJ!usA>`}SC`5sSGdG4MjI*(rP&B5*q-s#9U zBU#!n%jOx1%j@5@5WCZ#pF!&~Zkr|I9A1}#ZW)?WG2B^8=biFT6+E+H8huyaoPzR{ zZ6)NNyv?cdo)UC>lh3DMct>Nos86%VC)}|#TYR@|63XE%*@Ba#@9t|i`5a+rFrmCJ zNcG_mm_c>sFSTnG!ILpReAXcNdcnJ2YBwm(&Cp;#c~#Jeyc+^DMV$6_lj3R+ajf1T z`4i71?(O<*0Xp)Qp0Hzh&l{O^-mxu(xGZm-g@r}c+omhw_HD$iux+B?v!6{;oYv*d zfPBf8iD+v8ui%9*zbex?f`%+qC6_Zu(!7GtC3IbzeQYCM%?YkXeidr7|BF|&e$S6| zelm;dhV@45eB)0RQ3sOW#mSpWapV_U9}`@S6HaS7hx4Yd2(_TKW_8~7RtXONpa?jI!uQv3|;-Rn;dApyw@)BY!ZDa zmyDXQSbmSJ`~exN�+|xSf7Dd= zfINe~Oa0cCMBkO$BC{wT9HM@n5=o|YAz8XlvzKX=LSq_TqTxfOg~H!!q3?QgPDtqN z#%Wqq!eN@Ln}HM2NbA*>iT8=T(_4g2ueOZN_2lqYQDt82?71C<87TimQs)oPAkMs7+R>rG zvu~qeW#e9PUmR^9Zry#VhSs$sT7&W-UhLyxEEIDo-_zBKZtKdf5VDNO5`~vSG)6p} zE|pRKyQOE)yI+#2e{hOe@7m?hXJT887-l z$s^7x2A83YVI;1whoj4o8$HoDaVh<1iDJt-atfKO$6Od5@N;=wY=Gj+3!lqH zyA|rkDRMGNZcQdhb0GLdk>aHLC=)e)l62o~Q0G9=_E~7}I&zu%D(iDSop)>#b4>pP z-LrNr_EONz=O}-_CnomAX!&M3I(3l9#3UiTHol zZU>xZ9Qg((92&Vz_X{LMp6M+olF$y?5i0KEBspl~e&SMF;<11`T|5@kcgL#~o(@C( zg10291s|e&|KMP?%0(O#x>%$ zA>u&i$UPp4b$&JvB9y1FVh#wd19L$60bc+c6Ii06N~lX9e*`MT`=n&@DX-5t-Z*4I zDff;g#6zct#5}wSKa&*4SEuDnMaU3W<#+M>!JHZTL41Jd3Uo6V2KwVcdPLKE^^+FJ zOP`Z5%hG$7q^05;XKJeKsYaY#Qdxey>ej_M{Xd%!Hk?x5Y=86QPvx-}~9{RBz! zysh`S0Y+#jYj0DXHQI!ug*kmNs`I(i4WkwW%_wmOqcm?_|KSg+KU4i}KlWX^e`k+_ zxDlOZ__orgO4?tNjQ^NF<$2Hl+3)>HvZDXvuMzP-eOSb&_p{kTlG*R$|KNT20(!1h zqq`J_)H(y&+Fr*du?>E1oEST*FqZJ z8d?5b|0LXi=Lz`aPLGS0PHf^*1ia{1C-Bz zn*WV^`NvPtC^GcL#-Ux48Z)BdEJ@C^F}m-{Slh^l#IbK5vhyUlCRNC}Y{^K7-qkSj z9f1LDV^(N$0NK6r^)U_P_XIm6K)VhIugLw^?8~hN|QiBS|*@E;oPA%_^UFAHInF zrLHpM(0b}Bk)0+(=d_mk&Be0nj-yjMwYCJ=p{-j5-$Hpln&SELrr(j{8P%gIdCmfo z%#-rm7}sFRbFnY|oLp%1mYY1yqHe9fnd(y|hw&uY9KHTlq2I0ZTsnq&eWK9Mb)Lp! z$bO+s>mO*DOMQML1e54o*mRcoi2$3g73+1V>s960dTrD+_xeQ0u|6pw9-#RTiMK^x J1;iX9`EM;IMXRbM)a;%{fuMSyqTtvy z=4syO)2mf+_k)Ly3m9brhK9c6?csH!#E|u1xYrx0(`|kQ1p-&HP|R? zP@-4!_-V}{m71A|(?n}z9N{1^=Ad<|cU_ZuZ?0|?WqD6`^xVQkt$knky^+=+`VB`2 z$M*NMH}sYhDNua=5-ZWs$;6!w4HeUtR;R`k>@maBxbLlkyJXNo3D@O-vFX& zpta$5HUtK)Mb=2CzkhmkTd3+LX=V&;e7$qclqp1cL@?_Ogv#%bW&{16UOQZKr!>0| zeu9=Qb9Bj|R%{k6Yn%J9DvoVg@9@IV$$cb-R?+?1Sk?~a(lQ(Ja&sCAsgBfoKSl<* zZLr+b*XKe3umX%zbF+<(2Zs$(wl~kYSx#2K=cWV}DL9<2a=xcO_M=oLG+(3=StJ+W zwlx$nUZxJ753ExV2Ch|CHmjiV(J>Qd&axKr3R9oWL_xHLUj9+yt16ir)7{)^$Yy06 z(mlLH0x~#kZ{m9wD0Nb?ET2!ul6wxP$0^}ZliEE%_sO@)>vB^zE1ExdSXz}WMwVEH zv6NoC&1cApMojWr7GtVNl)1csU{IQwWt({a^Lo8>z{>q%&~widbN&4Uz_r45ay!+0 zbx%dThCV1{q6lW|G#Z-)zHJG0N9&qo&^*^7ZvEMgh*(9q%1kDOM+i%yqx41=?Jv4Y zI5~ByuP^s(;3U~>9(n~IF~;9~vwgd4K1>b8Q}M&nHDjEwT7{R7!F!;Nw6$wBr??Rc z;*wUqxE8yyK?I#?i02hwj=tb8v`R#&V3-_k;L*-aJ7p)vFVN8$s5N6a=?hOHgZbiB z#vbQOM7xj2*GV9nKOZ^7MHsCA;lEk~H(lEIyBNS~T%&+X_|XG+$m#s$k9qi+E%MT> z=z#bib{<~;Sz_=3UB5!95sgLhbUZBq-_=w)CAxPuEktLOOMa=QfEP92BY9aOv7E>}avq@Eo9>N53*ZLh@ZJJWFa-tb4@x3}~FOMSY;6x>Mo+drLmy%G* z#>Z)*8r^VZ?935N(;~nO0q+5pAh&JqpVN!FQHq6tt?tV|EuRNi4A|kmoLYJkuz+gK zsMv$QY=w7`N9V3f_NO}mHAEHUx^i(-N+nvJ16x89Rjc%N{PEETQ3Hc!>UwEccRE9g z`vIz~bmVuvoKC4q;hhrJ=3p*8W*nvNuSZ7kFU^$D_H{Z3rM2P0jC}?fIGd@o_HkDx zbq4>0ocE50d+-Pqcv@P%`;M{vmhrHHv}*!9kqkY9=V>)Xw*nF&{51spR|n(&C+**S GbAJH?H~iE9 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/gmopl_mod.tmb b/client/src/main/resources/banks/gmopl_mod.tmb new file mode 100644 index 0000000000000000000000000000000000000000..d23ee4e7a60fb72c13aaa41b5f5a517b6e414496 GIT binary patch literal 3328 zcmeH}*>6-;9LK-s-p&HH(@v+IR-(42r3HOZZY_c<9hL$DlFGCNLFBg3Mq`3AGA$uS zZ-uEs)zVI(C8*&RN{DYY>Vr=T5gtrb++!p8);mKL@ORFIr2Ge(crp+3$!|Ho z?fhmK8+4{pSqpFjjIF{nSScVYB_9S#2`o}`0)4C<$y7cEaTtpcLx}YoG*PH`3|R(^5_K z29ZvVotIF{pP+&~WJg`gUox!poE#H%Q~7dSvVx1mHR$9IJBIcwuVA9^o%+K#Fj8s5 z?Z-@``s@mFKoUNF8%9n!kIQ_tkPpdRuR~Te6nYiO_2j?< zn0yQ`EK(gz=$U#9ftE&9=EL<$e7LrbGNmq1a=SD8d?b--`>`W*Z7TGs5L%-J=@mLvenGh=-Sp+FQLQy7(K1cls26X^AOtGTx4^TfEapvu0f67g5=e| zo#abo>Ji`)#X(KH+34T8p3)W)a~Zl0mQrQ{Y6dVowCfHT^|=0K(+}Gn)DkkP-ZBe+ z4pL^q0oMnOWab22N@fe<6Igd7W~=FwRmO=r@AcMKV5l`>k8&vdID$_pcI)-=QSik} z=3i^@Eikobm&n=S`d-z$xN)|0ZR+Lle!83rPv^dtPmu4o=zjWe8gd?}rB_eK$u?rg z#DTE*jv9S<_hc$sCx?P9UPCXqH6zvZqa%iww~!6>M>k(FypL-X4#6>fHPU_0*2*2C zxT5OksdW~Fk#={XVU8zl2VSoeRx0odQ)utRhKBIvuMJ)>nyLb}7E>qNs0LyzYgXd9 z?Q>j^bHJnY8XIW|QK_nVLEm?SE|(%Mb*N7n$y4Wlk`cI+a{QLBrjoI!)r zgu9BV*Hc-SqIl+lCY7M~caqE#qm4u^K|gu~bW|&6jnaVLiDr7Zi7V))UySY7C1*&Y zp_67XaVRl^25F%!v8>Z1X~YsCxtu6qif7AoiQIl_Hfw!EYWJe}2)WK6Lk=FDuJ!;_ zUB3t;y+@> zGsi&7!Q-EPPZ0CG+?~(n-=h_>g5Wj*#|hR@YzK;d&8^r;rD!G?Df;WehJ^&H2*!&3 zvf9oNEJK496*lxwsPKN2dJXr*?E8lZsG>D+U;gDKi*~>U8?q9Ds8bP3Jbh(7)qtbg z>-F{S%;ljJJ%ZZWf|*IxZ37cxz?i$EvlwY;geXMf{tGcas`)VPDgwwWPoz>k!f`s#nU9(#D z4`d?YezUB1i>8w)=+)4iFCjmZ@Ze$FZ#R7>bJ|?iZ3~;Fgk9C`F9xh1lh6;RRY&}f zk89$8Vq6pdlfbYURvQS)Qxb-{USqkCrzKRYRX?mD^Niv@?}_1A3CpTmcQyPzC!qs| z?sUJ9=izr>*DcpCNcgCF^a#sFUWEO&u2kj*&$X|Wev_E{`@{NlFE4SuZg%_AW4VPm z>=CW=-9la_hW=t%?RN5tgp;N_)%B~4|LRQFw?QB3J)*diF91VZZ9zYh*RIybatG^& z9mS92?$!EOzR1}2ZPNkgm*TqO8DH1Dw0hn*uNb-0hbDDAt8sKnB1Vbh*#kJK1kwKrZ81RQ>_z=(TFsRPqoQP_^4?y^u%1+_w+mV6KJ=PC;nY!PH{AFElN&kU_W>pUr3RHebJQv-1 zKb*;rfOB|=vC%H%$BY9!?KemA6WAY`(+z9mr|6@88EgAz$OQ^(J@i}oIqdAKVZW)* zc6BGemT=bZN#4nCu&&xTe|}5((`~aw%zwxDtk(#C zBflpefM*{6fOXTIwHQtEN7B&%JlCH_nRg)bXT+`V*UPp)lfQsoHD`+9ucOTSkonuV z{s8O0bGPoIU-W+0H#_-+XWwsh{m)UJv+iG@H^W-z=f6S6yw#ccpHb#r$ozL) zzlZhzfTyX4HFZTyDrIUcrUNbjF}^H8O0Y8A+sL&qe4cAR&-KRHGIBqIZjve&+W34i zF^LsxUAYV%>EXjFirV60R6pKQqP!Zp1QH3Ax!+VTydl z)CQoFuGC?G7kCqY1BzLMy zW6Uq=5BVZaKM#^*SFuX03scn4O>z_CCxtOntt+YmQ<L&nQa5GkHiu@FIM*N6mDB%~F;fKm;lI4<2Q5*QIpBLDm zxM7*{#<8CL<79%1UN9_YVgyQ)VV`G*Nb>Mhja*nj_H$M_KGYQmUVDMciAljt*`bIA zPL!j|K-VafR!&S{XesN<3^@h+nwy(~!NccR&z4>eTvvKjtWE=V3aTCDLz{XcUlx*1 zo~J4;2eBbWVaM_~J1EF|%bOYUaZpn=V z;D?o$6s7Sgta?;Pe76;C5GjG2-F?Qg)I}Q8oVVh{~o$QWV;ut}=)w=^eF0%H1JK4-6QwPS#wXl;Hm_i^q&gXH8MqT_kGiCaea>p6VD>LRO`ps@C2*=XT0^9Y# zMQxXR_;>#9fwWCJ%3pmWi3=Jl^akLyM~7c= zF*r@YJrbkA!G|!b8}<=r*CdQxc87io4*lrbI88K7vt`&uK^5dZ*Y4Zb?%79QnbFCTb-xwh3qi zf=3VjS3-&^z1D>hb5~`}J>2`_>q~WoZ$guxCh=iT;+oeHgDHh*&AMEo6bItO^gSI0j@85=wGy4qC+-vaUz5VZ$iStu;s=RaszXPxTW)Nx?NS(kV=NgA*1Xd4L^gE(V1|18WUK1Cx>-_Is_ zOu6P0*f7zrzMpZY%utF4%#%4fk&@_0h9^rh454_Z;6xa09PlxLXbjvb;@*hIiGLWp+{U|1{4^L$ zD6;uiA}%RlUF!i8*r9Elo+Y-lrj$*?kDW4BPJE1}CE5lfPQN>|DU8KCMF}oN(r6f- z@P5dzYLfOnE2qRbBzJy~uUr+yFCA*2^AQc40$j8lULujMhSA-iXAB;6F$wvL4@<3B z{0pwhWAD%T1JC7=OL1Je{LwRyr@v=0WYJilD*-vcFc^=ECk{YpWMUQyixVX}zlb@X K;fWN_YyS_s6@bqG literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/hmi_drums_144.bnk b/client/src/main/resources/banks/hmi_drums_144.bnk new file mode 100644 index 0000000000000000000000000000000000000000..a070fddcecb1c00639de7d25e4af50a5002fc207 GIT binary patch literal 5404 zcmeH~k8=}66vw}p-0j|7+Z_Hvk%|H>5G-eDZLv^_w1^Z+p+68s6fS8@&R%jgNn7z3 z{OP{kyKbJ&n0Cf-bjI9tlFz=kZ};tY-)?%q&08zWie5{9+Qdw!^pu z3q39{E;6e`F&<_-!nnjZV0?`6Ym8rK{08GU8NbE&IODe&zr*+h z<98XK#PZ{~*50}t#-#6E!|&mt;ZxW&{63x<{s3#5rzrL`HVmJ^Gs9Zp6JBB~QZBlT%)~0>A zjVFdb$N5IQ--tW3FLyAOq&Y7mY{d25xSfW$i^(L-`5qoslW4D2t%O*?;(nTLRg!cY z_i^3uD$0$xx*J!c5D!qMoHKriHCk6YZq*a4VT~vvtYMwywt*F6-$dQ;Bc)qjKVM*s zxT9<7F=o0^ysg*!C9CtV7(ZeBHRGp@zhV3><7bSYGmaQn7*`o@F|INGj&YrFgK^CG zd&b+06UI%(EygM19mYQ}ZZqyM?lRtGyvKN-@sEsOF#d`0&y0Uz{43+%7{6qk{R{;3 zbp!MR06HiK4b{@mfq@zWuaRe0Pv{0naf6MC?oxm!T zCt*$KywVoEk^Kelk0aCO2aukt=e2}84B;!z?hT>5))0BAZHYj@^9n?{ zSCUiJj(Q3r@Zn1G8f`uDDmV4^MJcdcHS&>Sg`_R=X+RZB^9FF7-oPcw%5p4a0mOnA zWLZ&HwS)g$th7B+GLwo{BuPnE&3Ke~`)4&Ql~!sw^pwP~56AWht-|3e@>HyiV>*`h z+J-!_BKg~PDh-?+hulbk4c=l(Hyq5jOYIk+qX}7`|2n<_mA!G#Ub}IY4z8) vx4Kp>x0vlO=+3c(J~dfw(9+a>H28l~ZL)d>YM3)PP$N4SgOl|%0uu~ z;qC|uDn*70gH#d3X_=0#LLJ*$r(GRNsakZXV%uR_>^L39wsx~wVo=ETdw!cL>p!2& zch5QZo_p@O_x=L3x&VcBq=BuZiN{D6_LFHiO1kk1nT|Kf44fk~agp@kGcpS?(kspw z0E@IRoXo~J(uZke4r<9i$ml%yiAVAX>tN0 zvhmC#T^~aylL$XJ8k38xD|_ zI7ZII&&XMLi=2&j$sqnrR^gvyHR5Cqj0|87eB@k=Bx^B&oQE0Ye9R{opqX5V4ssE; zkaY-?w__h!kLSt7_z4-pFUba+B^&V(xdi_pn-C?JqK|BbHxp>VFtQb;=uAGr#TlB@9)*@na98vKZC$Lr);yhC>2L$VWpBiG^IB3CBXDH_-$O3>C<@5!m`LtGC8@EH9Iac)BE60*)?3LE{TMk$?EOO>P_S{y@Q;t z_mVU85%M;DlC0EkkTdlea+bb8&eoU7p#Fla($~pq?HRys(L=~Nx`dpo%gI_@P0rIH za=u*~MGqvK zbs^cJZL(F*BA4kpa=Bhk-l6X#6A8lqLDZ!@3h|4)5#awl6)&28l^=EC8{3-mw7IHi zQ}k51-JQtDLDzPPJ9*`5lfeVt{R>g*Y}aDt$#HjIG^xzi?w`U^*ttsLNwY%VxtWV49D-6xIrE5lMJY;_}!x1zN-~5kB4s*i}HMY?_e;uXqu?pbG@C0m!o__ z6|X_!0IWPjO|hAgr>6l3@0@N>wMC-Y7|`w}sfv(z(a82Ank~85NQ_eHWmr`wswg*_ zrId|Omc$h;pvwM6y&uhjC*TQXIiBxsoTOlTf&;{}@Q<`%vzfG+=c8$gS842=c$y0m z#W$%xU7f2eKYC_3dG0@2LKTkvT9k?;=E>jqTQX+B<&BF;l^7)zv@froWWrW`jzaYl z%S`XC@Ro7(_xjM+1h>rHXRuS!Pp+h=HcvK+T4yUyPLLShP>@lguLv82LLk@zT3t##Dq5wRLr zYtT?CHsivNA2#^x#CJIr%>|s4`p^0>@88;K-N*lbmIK|ae-!^QkIPZm&66~ zyEwKVYf2?9Fubc=vTjJKJK}k~jCQs;{qkw;^(~TT`(W|h++wG}y9FV~2K2PUtungv z#dd1ol1_N~>w-XYHM{S)N_LNbX=AaX`oUPov!H34%(p*7lz&J=i4&LZsQjj>P6d;; zL7mej&)8ANHnKTs^Ej%$%>{P=p-fq=e;<|STg42SOVSqDm!bxTO?m930^G5oZEk~4 z>n4w*Msh!>+MF56>h>1H>+fItsTSKMD&*KsEp3?dtt$I_Fk4P>C{r@aDX4wkWY%k! ziwX?h_JXLS%|P#R(>jG}=|Mh*H=jQlg)6}(XOzN~(QJcp$qELt8<)s@v3Zi`sllD! z857oP98>nzABnff`eLP2@ik{VUr1&~b!BuU-fCtQ`a0&z=?J{n{U2lm`fsAlNaCQ0 zPtnMIqph`oA0dID?ew+ZYPz5GOy{FY+vb%vE4T$gm#FeFw!u6zlXr+(Tj1h}>+du5 zTw^QTYfx3;jG3QP0&f65XQvxJjJ?jDMHhPk@iMo+1(_#MJybmB=Dqm1Y2~1sozJ`A zlX#xuZpO}uLQeNVgAa4;SI%lb0P`$>&*^LA$4`7~%Pp+w0DC^n{N!}R4)ZfFv-@LK zFwRSSCoBV3*GWBd!-;K%zT z?iVXI!eC}WuXx%K+mN*f-SoUx97$|5VWMv`v!?3XE^5EC_Vi0ZR!|{%#?m7HS=FIUTtJQA^gx=g*0gEc*K6BDg*p zoX?rvM&#=g{6mi<3umvKh-aL8`{a6hR;FR+1j91%Tq{*Auxo|X zwf8Bi$=nrZwFy~mWmSZ)sFlf2IgV11YEwaG;T?cI(`ng^ctrB>3sj?O!(IIP4k&Xq*T9_O&)m9BWxnA~RcZ+z+nDUY8%zj&Dq_0tN zZ=I79OqtL2A+{R)@;IYefz&alnGxjnvHylbcJS#nq zuVCDix*!$4pH!8L*c)<=dc@P_ya!Ci&3l#Wn>&IxnW^&dWt4F>DLp?cMO7KQ%l{?+ zqJ<>0O7-64|H|4OHQNIk%M- a5_$X;y}uP3aEw**;qZFKIo+y~-TFUyiogK? literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/op2x2.op3 b/client/src/main/resources/banks/op2x2.op3 new file mode 100644 index 0000000000000000000000000000000000000000..7d5c2e5674c9221df685bb0a34b9240e47ff7303 GIT binary patch literal 4384 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 BO-ld( literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/std_drums_o3.o3 b/client/src/main/resources/banks/std_drums_o3.o3 new file mode 100644 index 0000000000000000000000000000000000000000..524f7a15924161a2c344486f8240941ce6b298cf GIT binary patch literal 7680 zcmeH}PiP!<6vyAp{%dRsJ1xW*OC3wae`%Xd7L>Y}O(uW#keJY2+oPG?9kPRyoj9{o z@#dlG)l|InDCFQVLG&O>6+MbtOb|iPL&1ZGZPQI{5%j&^?#yp?_NShOnL`Pb58wAb zzxQ4O5Pz|O*g$L`HV_+#4a5fi+rZ??>ex(8oz-1eopZbm6~BNg;+KLm7nMh$v42ua z!5F~wbnxHAaxh0|KbwZ9iqa^uk1WV=YJe@9bwe$;%-X9A?Pp~u$jJ;m`03Nn0Y>}S zJgeFETtwA!Q+JF|Zegxil;GLp(hWE`Y#(OeR5m*d?JY_oC@AWIpC0AiM!uqa+tmf!$L)G5csG>4l zhkzwl5)_7HDBixC)8xoJvzera4ao}F30Cvuztwz^_K3&w+&p6o)47Mx)$U!9E3IKR z4L86!OEqJ?1tkS;b@x3H%W`K8%Zlz1vDokEi50kWM{h;yU$-Ji9a>vqI5L@%owB80mzqG$0l;?I`o$ax2iP&YYWpwvf!*HFmR5R@$ z9D@({=jTN%-~9+nyO5xJbrjjT1rbX%cPT-??8Y)yn9E#MOj=(ymEeC=a}vh3lq{?{ zH}_=0T(Min(f)o@FtVj6$bPpkVp;CYx;^+tY!{|_p5I9b?gr(CP@YS7Qi5|nE80O# zuj;Pe^+f;r?E-mD!gIU11n%Gm`=T9O)*Zd3y8+DERmy#NPPqy{cdlL$vE_#CwA9ND z(`_-)0ZX1UusAh&941~#rlP-p>256HG=)%JGqJ<4c;@sGsN0v)lE76rchNJQ?lSb{ zrXm02X@G6>>r4Vp4(vY5&-`yOP)in(&<50 zY=8fW9flVk&w;#c-pZuYgSVaKRvc4ruJ_Ld%_QFEBSSaf_Mhz$(S2sQA~uhEgzRwU iTVj)P#Bf=?k(O3>lLe(NW_Bl=tdJP##`b7ucZclYWGBw- zR=f#TJ-6beN1+E#l28;8MWvoZEX4#V2zn4acu1OVV~e2g_ulTj-;7Jaqd0SzJ-qqw zeZTMT_kC{x9`G23@qpFgO9=yzc#wF2=}6`v@gSK4aZmgQvrsF)Fj;EoWh)5uS--og zvqu!e-dIl>j+d23ptW_hqQRql2^k6G5s5M*aKca~81wL)N|>6{wDnrYX}ro2$rP#v zs8Cc>8MuGzvo8Q9LY-2>>jn(bYi-N7iB66&%3IGJVPEe4vHc0vsaox(Zo1Z*kfA!y zGM$o=&cKx`AAd@jifcDI{t%tQ0_qfxXaGBVQJn?vb$!nDJYQe-&^W$ne4%d|99NV> z@Y~hj_W;KE68dJkUSK!%sE;`&b!G({(~vO?6BH

`$SrSF>Wc(0`1RKOo#dQ7|X(I z_4SAcXPr8uhGZK((BSmXzhLH;-={N5`@>}9-UMS5^P{Xk>yiHOIVgMW6))x+GAUql zZ~*S#b>9I}59Ws;Q5^+N^_jORv)Jv}B0nY*vm{gC**>YjA^32sQlX5@T%2L^rr~*T zfHAXkl)=4;uVeL~4nM0n6VVY_a4WYB{Q%ui1}1i4pI zX?p)Guom?LTR3skw2MV|lh{p~yvQW= z6DgMW+?hLfsGrV*bOhm>bMCq4@7@U?8EWW`X@y)~jfn?!ITIH}WId7n-|Pap2?xmC zr~YBqiM&qyZBi`nnMHyRp)jyLx|&SoMg9s4MT~XgeSEC9eBCB-5Y8QY&$VtJkG z9a80tX7J$wL$4DbCQGHGZwQqA9lYk~`<#tvnG@3wp*DjJ;^X9A=?4jfwpE{MCAUAiat0yB_NFwwkW=D}w%M+)8|()B z9a1ho4FK5u4k>VdJE3H9Syf3}nDKk4%Vg^w+3<(b$cVuXl6OnP!<;=_fYpv`kFDV7AK@XB z^*=)9VsoI(yVW;LZdU3&rjF}!Hl<~hRrLO-!9MfUBjkfSfpfq++;X?*X@Kthm?pO-gbEmq<^2|;bXGf>g+Ing&n~C!(SzI^JpU zEUtzWJyyu&)Jes}x!GTj7`_sk>&Wu_>q*L9@RKj*+*O<%hS0E-uE?Vf(O@r%+sQ93 z@1=TQ6&uL()I)~^OK)$gpvO|2=4w&YWOuySMDk-k*qGW^UbzWktxLdG{wk1vY_Ti! zj+tzu)Et?dbh{z19~NCC%lG=Om9@qBsD0LYqXDM>>`WGbI)^M9hp@0xDMf* zS;cpSjhK|AUqyL%j}v$(>rzs14xf(!P@L+0rIS}{4KLjH2Hf@M8a zf{o!US;-FQN*=mOj`5aOgra62#P~WXUVHRBqWkM3&lW$K;IT21ma`@oT46HZ-)c9x zyEyo?TcB|Tz5DeSjQyp)ve|Z9CzU$Pp3)*Uleyy4^N@qoSEbSmFE}h;jZA0qDW-S@ z++p?3g-W1?ttdM*NQV``-?N$l{8!ypvTb9y9eeOK*G6CR&2PhNWxXGl_)F zLf=no)WhuIYqshnetVn#oq;og5NwqGi%drMRSU{x2p<m_nR=tn|> zC~YILm`mWfe@s(jFluOPSk_Y{DM&6&C19xXiqaoRBDL3^ugn}Y-vArMmuuAxNvb7< zq`^wk17y6C<*^|~R2ZUdmYq`^coUh7hxb5Vz~}*zi0u*V*w70TStV~XmJ~JTc1da? zy8iqDO#6NlIsMO6WP23VwTbbyQ!M2E{j47~k=Qt6C6YT`$ri>dQ{OiFkm+ZP{bc+v z)vYd9P-@&vj_O-mxy@z|jjpx4&+H3W@@*L#U;3U4dWz(&8yi0WBG^o8EY$W4%w`cy zUsbetOg543Q$L{iWK?5xo!ABq(2?0X%PAz?1QL zcX=q={NLo{Nnov+pN<+D3|H*oZnpLaH*c;PJK1QmXSSR?>9W~gI9RQF{v9%Np*{2! za`B;Hi+~#1L;Ym==x~7N0?auv+K>L0-sX!UNh))+<}_d2MxIFSv}?{l8irBZQd?ra zYJ9Q9tC%&%A$`qzQLCs|!wQ}D72SrI6lJ*|@@@U6lu@tRL-pkS)Zf{9hhTa(bel%o zujnvqj(7nDrl!a!`7}dgH%4O*0r|OHs_Klm{qnnO8&aU z*uZOik494Ri9$+_rPN9gWVT-U#h@=abEbr=Qw zGo^*=V1n)E{UY4yg*&1u`VQ61hL;J%`4yEp|WcO#Lw$yPk$&=w*!j!@PY`aO}P zk$xVN!0r~aT6#K18ir{ERrs987M5(&=q-`Wc-mIi8WcZFfVVtWL7_lgrrp2lR1 zzZC?=N>UezwLQ5RSV^k$#JYa6*=5Zt)aHF+N@k_frkTxah>;MDW2!& z-yS*hDDT`WY-V$;i|i;bTv`%P06V;JkF@kAjPPb$K`)z7bIPhA7SyvDSg7*AT`sLx zekeO|&u|VtHL(5tmIia3{n$2J_Zw92;!8_{5Wsi~+_%rt&lINKZ^>zT|7_8 ziO9l2CPVKi&LB^DKKL1rs~$O-RiT%#$0FhG%bc%#?R1zcK?1;h^;S*S5X@JW%>!yY zzFzyaVxw7ntLA6tMhWyX5XqbR+527Axf^_yk=Hr?TI>qF@6?Qq!JVq5H>ze%(0K!{ z8>rb8Hk*4CtUm$=X5$+{z~4>oz&8T7)qlH_8r+MnxiN?Jn)f+%wTLXv9)~%{>#yx1 z^RvSbbJo1u?1MATs$5__h2XD}-=5wF-w1*(-+lKf!$%9;&&M?_K2E=F*w&g7+am{D zmVG(@hk2;&RnE?Pi?a_dBZSTUjmqi?O{98P!HjKagS*Yr@hHMRpEp9@cw_iD&!fXi zT8qOcuc)@FAu+bL4Z}CDmq%c|Uhh3pzBYWE+iae3s9qyFY~@{MoouZ}C;36l`w6-VKS#U;NqV zWyW2fBIn>^+1g|(MaMX^@2;B*li=ejBEQkzSQw` zuI*$&n%0(A2HRNKTBflNO9~yDkSLGBr%5pOM$^V)tkS*^s#e;kg}hqmSoc4Vd(N@j z`@wQ#|NMVH&PlZYTif~)PI13$W|I-y%sRS`yr;8gI1KN#`tA6==UU=E?Q8Utj*-Z= ze%GEz7X;}M2*vgclRql7@8Xq}wKe3gN9g|h5n%>1Rzi^@nLwB`Cce71w&quukdr+! zdshKosd5U_%b9=Tm1W69!Je_SleF?Ww??%mG~5Zl(6#Yn{L|-7#C?d_LzrONaf^8? z=}|&ZxD({~P(wY!i_axoAKF4cBKCw#HdchbTeOfHT)GXFMqy_5X1)_!VDBM3479VnG2Kv zJ*-3S+=jOLeqQs&($gPl+HHafnUj-FHmQ%e6J}PG6X~YDZZ$u~$IPlT6=)$l-N5W zsmdy^9}r(d{ea)>gqI0Ixp0`Y9n}1K6YgvG`zti&H)2oB%uYDEkR}k+%X)}md<{>h z{9F7={g5t|FF0!G0wT<|z<=`1Luge%IC)7=Ap#^IDzpE2}sPM@;Eo1fy-2$xU zG_4A+ukUR0Ymyn6w%nvk(v3MmFaxZQ8pgX&3yqt-_z-&r?0ms-q4$t1M~SMaBo)m*LCi>>8t-$WHqKRn-rulvnB)Tu{6EifNw+6H|7_^H$l{YDuXQq&ot ze$%vCyuAMWCDjMQZq1WC4v=#vT9|(yKR()c5c#&zkBFRz{ph36sR|w?8D`JtpW(T+ zuU>uy@%?}>BhYzaaJy!P)I(0n>F%NSH2Z4s$G_{`p`lxaoR~eCGqbbT`yfK(97Xl@ z2XWwRjlZHo>c;HQ2eWx=GM=$yF;E}uLh*%XLQ}!L7d2(hl`*myD03$=jTvzgjMdI-%o4}pxMACMOwAI)gQqXSKTFRy7#(~j-xeP)^rZFmF|A-m;+ zXyx6LxFgs*aM@fROQe1|0rs|7}u$O*BSetO{m9q{7lO;~*))N8U0!T;I`0}Z9FeBtFiMhqeoLKo3CPeH($e6zkN5?k7+?G2< z%ylN?-d3f`>1DbZ3)|+RW4GRikjw+vchr)Tx^Z6{hJh1L{9unN=RmOl!(M7A>=|G= zx_IGdcimBUq^=Vqq!G;+y?uOoZC?+ z2d5vXY?E!iLQpT8bDFOS=W-V>RVur3Uxu^-4yd~ES+8lE@X<0RW@Q~$LP8AkjKm$bjgzE?M=(9io~h)YarT+UYQ*;{B4;o= zk+JhKsDg>HxiEja4No0^>3J`&Nqopibi9pE5rf8AUTdw5cWIbQ*lk<|)nVw0WeMHWO)^2`ZP#f!RWx18GIars zv)nn+`25_ie&33hJ483ibm)0=rrwF(Os5WhOQpaMLJaC*`{)P1MELEeQdK^_Ukly3fH9#hOejz{lY^Q@ z@YGnefRwpFP5S zMvb5Ag>KzC9JtmsDL^9`6Kw?iK#vqa8?2n|oxn$gV(k`3i@jQQ<&^mx;tdj26y zhdu)*G$KV|=nO_^asD*UTF+K_@gccGn7w+=$;{@-rXa+i0NWFFt*-vPrPVdH<_JT= zLneNy9FjYS(C5E}xt-{Rc`Ele!gRqs#f+8A!Q1QXSzI`P*}fz95W?)!C#?cWo`gia zJ;X2ua3b&+%q(_hBEBZfy?Wa8T8}V@_v==pwQjaVV)QmSsiK=nAqY!dcMxE2t)aXG7tG2FKF zoBeQkM?WI=00Y(I&ca<1xf7BMx983Io1nkVfjuQmr*0M|oqR5Bxk}A(IW5M&pzoCW z0OUY;=mlYx9G%K_Yc-m0#O%{2oH!gbO7oh_xfH0aZCg4w=*?|z&%L?@xdWGdCH8RU zLM}4bwsaPl5_=##3Hz=TYoz76UHY`CCKs>1?ByGYPn6smPzj}CP=K8q+q8{!m#+jX z)%%csGy8~gmJwo551&79S+{!SOqZY3HlK6Rv^i<#EuIF_ZHUfo8~Lki>b|^k2KqI- zPb5Bgz=1tPd4!-|)_alGShu{|xTgB;0W+iCzezzM)~k@Vfiui+#0UN6M9zku%O4@e zn+pvcb#;F~x(s_2Fx;LYJDI%E`0;A4-Ux$I6Q`HTZuQVx$Ple1DH zCuV1lka-HnN1~o&_?^(^EhBin?5?wK!hw4v?`BY8y2G^Qwrqqm`r6@f&mQ9IXu*QZ z)B)J$%kqua4?;KNmp^%67-?*`B+OyQNyf=tY`Nn&(@{T+udH4Bo|kWgiJA5hhst4) zFsPU18s6GdjFC-v-(}jgtdTvpJ-RkJp7;Drq^E2zu>Eob0KP(dAN+fK1sQpSI_otm>-M6 zos?wyZMeLTLA&NR@?~;*Q1#t->Dx=7n`B~E#!~L0rEc5@7juo!LpEIV;zRrvvS92u zxq`P{mdbgQd=;l(`r8q8F7)Mc858~~aS!u?K)m|dy%XW%GdJCYDnU0MpZ&!w_`v)3 Sg5o!kqqX?Ub>~@6xBmkbVAkXS literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/swtimbr.tmb b/client/src/main/resources/banks/swtimbr.tmb new file mode 100644 index 0000000000000000000000000000000000000000..6c3e72cb7d6d75372ca0cc9eb8081afb32d1db55 GIT binary patch literal 3328 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 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/wallace.op3 b/client/src/main/resources/banks/wallace.op3 new file mode 100644 index 0000000000000000000000000000000000000000..9642a8dfdb9a3c4b0305d84ee583955c9c5bd11c GIT binary patch literal 3112 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 K$N7hn^?w1_2nO;1 literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/warcraft.ad b/client/src/main/resources/banks/warcraft.ad new file mode 100644 index 0000000000000000000000000000000000000000..b373a06bfa29b12b95ea3bde4cb4dcec686d2bb1 GIT binary patch literal 3622 zcmY+{dvH|M9S895y?6I!v#giwhCFx(xk#K!U>!@RChtY~m5TX?4dSvq6>`DDKP zJNKM(&$)Z{2Jkxo4GXD`71Y2w>cD2|#4c*0hbEzqy6_53#@p15kLW0zryg9PHz7tX zIGn&}_-G2Us29_y52Z8}b#x4t(KM`~>1d-Fc!FkPKOKvsbR1r$woTC%b zPbVQvv*h$(!=l-kKy#2!b1|DvMirfc0KEmxG!Gl-t=LBM@eI8UN9a_XqPOF9It?Gt zJMbATzy(@}?`RRer_3t}nD^NpMqKV#* z)pQkF=>rJSAokH_JV$?mm*{G|Ngu?Y=o)-Z*Wx052v_OD_<^p2#|``x0@}Bw&7XYju+`h{ElwIdvr5Cp+Cnz=@xuTAIA{g3fCy$ z7f7eukV^%MXpZpHT(OW&7Axo!v5wv%Hq$(@i{2`FXujy9w~1HiRPi>wU3^5RiSzUh zafKF$7%dbI51$s{qtiteogt>tVo^$GiaI(=ETgl<8ahX`(Gu|ly;JO`rQ#^POT0|) z7H`qH;w<%xbF@tK({d4}^8~+is1O#d6ccEb$fwm}Hmwm=v{nRYooJ@_hz)eU*hUwK zXXrw4gw~5wbdh+SHi!@CV(}SWA}-K?_>MM;?`e~8TYOqXCLJCoT4y*4mu7~q@tY6r z9+hw-)!s-kS=;X8r)JszYfA^N$)o2+%kCei)X-3o=JLA3*EQMaf%oEYYoJV?6|pG$ zLS#^;0m#MjJ1XZ7QYKsZ8_o+n6dHzJ3=4lCq)!iC_ z$Pvxs#lU5GUCye35_#O+sO^qrYo;BFBwAKq56D`qM76BzD$`uHbH~_3IyjaIJt`&l zX4S*BBlM}d&Ysv~Lhr--FUppMzJWZrdAFG=?H#zH(q@(JYsiDA2rV@-X&t>s;IjSU zuVk`2`qG(Py($%UM1OqU$Uf7)OqFwKnxgtlPoy=bOf9vaPS9M12zpeX3tD`do)cr~H#-QAYXgwDiH_pHv4BRnxn_EI?SXojY9W>e*L z!JZ{!;kF$@wG4K)Wx|~;k8(NBU9F88>?d2)uGk9#rCe*gI6?K&*{y4?AX-PMw6EJx zDOsg1CO5}RqV1McMjajl@CSLDs4Ks zqz8I0UPye4C!O-beL6JRki7&mx@O4zYfP7^Wo3Z9v|^pA*@x|UaP^_QSk`(c^lp;# zEUcFEw4d_wy&-6nPgcF`^Hp-?cdT;ATFJ-p81x40(q+v~vvcHiWvMcm_q83AXV%G9 znX(U@-pc$K)d)#x-%f#BM_jdZ zKEJk@za8M-aD>c5Lp*zYh^v+2C*T9S6?)o7A_lc*Uzh-95$? zO_O~kT7H81LD_N&hBa9I@ToHdh+LIzVt?icIVt`(V&G|WRo=tMRA?p*j*@p9li%Wk z5wk9_RG(DVN=MZ0fEh{F`6?Zgee$g##`DRC|5kH6?(2_gHLP)ce9jLmSerhg7GO=2 z>-k|hYcogG=8Q-+7OwJ^{WvC9sdPkbzU7LS!%C`)_Oq62VXy$!BF8bg54o1Rzuz#E zRt{d`6SC6MdmOqO?%nbiwq~JP>xnpKaP%eK%J?kZmEwxXI|?LLcM*=seaPjD1T;?y zhR3kBV8nQ(=BPmrOdW%AmC7^>{Q*fj&K_pEQ)?L74U?a}!8Bb94DFRI<8LzEt}P$h zqtfe4)f%q5rcrPThCNtgyMUQ(H>V!wk$Swssxq%mVaYCKQ%UG@*b)! z|JhnTS@_k%On3eLi{Cg6>kRhFJ7PM;wL617@@btK;*J@X7i%*(PF@IuPTle*SEVym znsX6_mWFHcdt0I7`>1C6oY&+V%iKiGm#Ecg%hq0&|CY7OGpn?$GG4>iK3BUOZ;{)7 z;i}=9xNuk|zk91ni-+YIQe*kI)bX$D@$sBz?TtP1H_qW6RXLs4iww)*KX;NXD)hOC c7_J+0DbVVeLVa>P(FMA1Hrq(c>n+a literal 0 HcmV?d00001 diff --git a/client/src/main/resources/banks/wolfinstein.op2 b/client/src/main/resources/banks/wolfinstein.op2 new file mode 100644 index 0000000000000000000000000000000000000000..ddf4cde84d5322c1203cf821ca43a17aa3258f12 GIT binary patch literal 11908 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 jHdh