1
0
Fork 0

implement all important opl bank types

This commit is contained in:
Sen 2025-09-05 11:13:21 +02:00
parent 6c9640203b
commit 5244bdc960
Signed by: sen
GPG key ID: 3AC50A6F47D1B722

View file

@ -9,50 +9,7 @@ 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_EEOD "Fehler beim Verarbeiten von Bank-Datei '%s': Ende der Daten erreicht"
public abstract class BankLoader {
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) {
@ -95,15 +52,6 @@ typedef struct {
}
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;
@ -161,7 +109,7 @@ typedef struct {
while(len < 32 && data[offset + len] != 0) {
++len;
}
ins.setName(new String(data, offset, len));
ins.setName(new String(data, offset, len).trim());
offset += 32;
}
return instr;
@ -204,468 +152,289 @@ typedef struct {
return instr;
}
/*
private static Instrument sb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
private static Instrument sb_read_instr(byte[] data, int offset, boolean drum) {
char[] name = new char[28];
for(int h = 0; h < 28; h++) {
instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h];
name[h] = (data[offset + h] == 0x1a) ? ' ' : (char)data[offset + h];
}
instr->name[28] = 0;
instr->percnum = data[35] & 0x7f;
instr->fixed = drum;
instr->op = b_op2;
data += 36;
int percnum = data[offset + 35] & 0x7f;
offset += 36;
Operator[] ops = new Operator[2];
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;
boolean tremolo = (data[offset + op+0] & 0x80) != 0;
boolean vibrato = (data[offset + op+0] & 0x40) != 0;
boolean sustaining = (data[offset + op+0] & 0x20) != 0;
boolean ksr = (data[offset + op+0] & 0x10) != 0;
int mult = data[offset + op+0] & 0x0f;
int attack = ((data[offset + op+4] & 255) >> 4) & 0x0f;
int decay = data[offset + op+4] & 0x0f;
int sustain = ((data[offset + op+6] & 255) >> 4) & 0x0f;
int release = data[offset + op+6] & 0x0f;
int waveform = data[offset + op+8] & 0x07;
int ksl = ((data[offset + op+2] & 255) >> 6) & 0x03;
int level = data[offset + op+2] & 0x3f;
ops[op] = new Operator(mult, level, waveform, attack, decay, sustain, release, tremolo, vibrato, sustaining, ksr, ksl, 0, 0);
}
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;
Instrument ins = new Instrument((data[offset + 10] >> 1) & 0x07, 0, data[offset + 10] & 0x01, ops);
if(drum)
ins.setFixed(percnum);
ins.setName(new String(name).trim());
return ins;
}
private static Instrument[] sb_read_bank(byte[] raw, String filename, boolean drum) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*52);
if(size == 0) {
return NULL;
byte[] data = bnk_read_sbank(raw, filename, 128 * 52);
if(data == null)
return null;
Instrument[] instr = new Instrument[256];
for(int i = 0; i < instr.length; i++) {
instr[i] = new Instrument();
}
uint8_t *odata = data;
int offset = 0;
for(int i = 0; i < 128; i++) {
instr[i + (drum ? 0 : 128)].op = b_op0;
instr[i + (drum ? 0 : 128)].name[0] = 0;
instr[i + (drum ? 128 : 0)] = sb_read_instr(data, offset, drum);
offset += 52;
}
for(int i = 0; i < 128; i++) {
sb_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
data += 52;
}
free(odata);
return instr;
}
private static Instrument sb3_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
private static Instrument sb3_read_instr(byte[] data, int offset, boolean drum) {
char[] name = new char[28];
for(int h = 0; h < 28; h++) {
instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h];
name[h] = (data[offset + h] == 0x1a) ? ' ' : (char)data[offset + 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++) {
int percnum = data[offset + 35] & 0x7f;
boolean op4 = data[offset + 0] == '4';
offset += 36;
Operator[] ops = new Operator[op4 ? 4 : 2];
int[] fb = new int[2];
int algo = op4 ? 0x04 : 0x00;
for(int ch = 0; ch < (op4 ? 2 : 1); 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;
boolean tremolo = (data[offset + op+0] & 0x80) != 0;
boolean vibrato = (data[offset + op+0] & 0x40) != 0;
boolean sustaining = (data[offset + op+0] & 0x20) != 0;
boolean ksr = (data[offset + op+0] & 0x10) != 0;
int mult = data[offset + op+0] & 0x0f;
int attack = ((data[offset + op+4] & 255) >> 4) & 0x0f;
int decay = data[offset + op+4] & 0x0f;
int sustain = ((data[offset + op+6] & 255) >> 4) & 0x0f;
int release = data[offset + op+6] & 0x0f;
int waveform = data[offset + op+8] & 0x07;
int ksl = ((data[offset + op+2] & 255) >> 6) & 0x03;
int level = data[offset + op+2] & 0x3f;
ops[ch * 2 + op] = new Operator(mult, level, waveform, attack, decay, sustain, release, tremolo, vibrato, sustaining, ksr, ksl, 0, 0);
}
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;
fb[ch] = (data[offset + 10] >> 1) & 0x07;
algo |= op4 ? (data[offset + 10] & 0x01) << (1 - ch) : data[offset + 10] & 0x01;
offset += 11;
}
Instrument ins = new Instrument(fb[0], fb[1], algo, ops);
if(drum)
ins.setFixed(percnum);
ins.setName(new String(name).trim());
return ins;
}
private static Instrument[] sb3_read_bank(byte[] raw, String filename, boolean drum) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*60);
if(size == 0) {
return NULL;
byte[] data = bnk_read_sbank(raw, filename, 128*60);
if(data == null)
return null;
Instrument[] instr = new Instrument[256];
for(int i = 0; i < instr.length; i++) {
instr[i] = new Instrument();
}
uint8_t *odata = data;
int offset = 0;
for(int i = 0; i < 128; i++) {
instr[i + (drum ? 0 : 128)].op = b_op0;
instr[i + (drum ? 0 : 128)].name[0] = 0;
instr[i + (drum ? 128 : 0)] = sb3_read_instr(data, offset, drum);
offset += 60;
}
for(int i = 0; i < 128; i++) {
sb3_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
data += 60;
}
free(odata);
return instr;
}
private static Instrument 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++) {
private static Instrument op3_read_instr(byte[] data, int offset, boolean drum) {
int percnum = data[offset + 1] & 0x7f;
boolean op4 = (data[offset + 0] & 0x01) != 0;
offset += 2;
Operator[] ops = new Operator[op4 ? 4 : 2];
int[] fb = new int[2];
int algo = op4 ? 0x04 : 0x00;
for(int ch = 0; ch < (op4 ? 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;
boolean tremolo = (data[offset + op*6+0] & 0x80) != 0;
boolean vibrato = (data[offset + op*6+0] & 0x40) != 0;
boolean sustaining = (data[offset + op*6+0] & 0x20) != 0;
boolean ksr = (data[offset + op*6+0] & 0x10) != 0;
int mult = data[offset + op*6+0] & 0x0f;
int attack = ((data[offset + op*6+2] & 255) >> 4) & 0x0f;
int decay = data[offset + op*6+2] & 0x0f;
int sustain = ((data[offset + op*6+3] & 255) >> 4) & 0x0f;
int release = data[offset + op*6+3] & 0x0f;
int waveform = data[offset + op*6+4] & 0x07;
int ksl = ((data[offset + op*6+1] & 255) >> 6) & 0x03;
int level = data[offset + op*6+1] & 0x3f;
ops[ch * 2 + op] = new Operator(mult, level, waveform, attack, decay, sustain, release, tremolo, vibrato, sustaining, ksr, ksl, 0, 0);
}
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;
fb[ch] = (data[offset + 5] >> 1) & 0x07;
algo |= op4 ? (data[offset + 5] & 0x01) << (1 - ch) : data[offset + 5] & 0x01;
offset += 11;
}
Instrument ins = new Instrument(fb[0], fb[1], algo, ops);
if(drum)
ins.setFixed(percnum);
return ins;
}
private static Instrument[] op3_read_bank(byte[] raw, String 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;
byte[] data = bnk_read_mhbank(raw, filename, 16, 256 * 24 + 16, "Junglevision Patch File\u001a".getBytes());
if(data == null)
return null;
Instrument[] instr = new Instrument[256];
for(int i = 0; i < instr.length; i++) {
instr[i] = new Instrument();
}
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;
int offset = 8;
int nmelo = dmx_read_uint16(data, offset + 0);
int ndrum = dmx_read_uint16(data, offset + 2);
int omelo = dmx_read_uint16(data, offset + 4);
int odrum = dmx_read_uint16(data, offset + 6);
offset += 8;
if((nmelo + ndrum) * 24 + 16 > data.length || omelo + nmelo > 128 || odrum + ndrum > 128) {
Log.SOUND.error("Fehler beim Verarbeiten von Bank-Datei '%s': Ende der Daten erreicht", filename);
return null;
}
for(int i = 0; i < nmelo; i++) {
op3_read_instr(&instr[i + omelo], data, 0);
data += 24;
instr[i + omelo] = op3_read_instr(data, offset, false);
offset += 24;
}
for(int i = 0; i < ndrum; i++) {
op3_read_instr(&instr[i + odrum + 128], data, 1);
data += 24;
instr[i + odrum + 128] = op3_read_instr(data, offset, true);
offset += 24;
}
free(odata);
return instr;
}
private static Instrument 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++) {
private static Instrument ad_read_instr(byte[] data, int offset, boolean drum, int isize) {
if(isize < 12)
return new Instrument();
int percnum = data[offset + 0] & 0x7f;
boolean op4 = isize >= 23;
offset += 1;
int feedback = (data[offset + 5] >> 1) & 0x07;
int algo = op4 ? 0x04 | (data[offset + 5] & 0x01) << 1 | ((data[offset + 5] & 0x80) != 0 ? 1 : 0) : data[offset + 5] & 0x01;
Operator[] ops = new Operator[op4 ? 4 : 2];
for(int ch = 0; ch < (op4 ? 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;
boolean tremolo = (data[offset + op*6+0] & 0x80) != 0;
boolean vibrato = (data[offset + op*6+0] & 0x40) != 0;
boolean sustaining = (data[offset + op*6+0] & 0x20) != 0;
boolean ksr = (data[offset + op*6+0] & 0x10) != 0;
int mult = data[offset + op*6+0] & 0x0f;
int attack = ((data[offset + op*6+2] & 255) >> 4) & 0x0f;
int decay = data[offset + op*6+2] & 0x0f;
int sustain = ((data[offset + op*6+3] & 255) >> 4) & 0x0f;
int release = data[offset + op*6+3] & 0x0f;
int waveform = data[offset + op*6+4] & 0x07;
int ksl = ((data[offset + op*6+1] & 255) >> 6) & 0x03;
int level = data[offset + op*6+1] & 0x3f;
ops[ch * 2 + op] = new Operator(mult, level, waveform, attack, decay, sustain, release, tremolo, vibrato, sustaining, ksr, ksl, 0, 0);
}
instr->channels[ch].offset = 0;
instr->channels[ch].detune = 0;
data += 11;
offset += 11;
}
Instrument ins = new Instrument(feedback, 0, algo, ops);
if(drum)
ins.setFixed(percnum);
return ins;
}
private static Instrument[] ad_read_bank(byte[] raw, String 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;
byte[] data = bnk_read_mbank(raw, filename, 2, 256 * 20 + 2);
if(data == null)
return null;
Instrument[] instr = new Instrument[256];
for(int i = 0; i < instr.length; i++) {
instr[i] = new Instrument();
}
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;
int offset = 0;
for(int i = 0;; i++, offset += 6) {
if(i * 6 >= data.length) {
Log.SOUND.error("Fehler beim Verarbeiten von Bank-Datei '%s': Ende der Daten erreicht", filename);
return null;
}
uint8_t prog = data[0];
if(prog == 0xff) {
int prog = data[offset + 0] & 255;
if(prog == 0xff)
break;
}
if(prog >= 128) {
if(prog >= 128)
continue;
}
uint8_t bank = data[1];
if((bank != 0) && (bank != 127)) {
int bank = data[offset + 1] & 255;
if(bank != 0 && bank != 127)
continue;
int offs = dmx_read_uint16(data, offset + 2);
int isize;
if(offs + 2 > data.length || offs + (isize = dmx_read_uint16(data, offs)) > data.length || isize < 2) {
Log.SOUND.error("Fehler beim Verarbeiten von Bank-Datei '%s': Ende der Daten erreicht", filename);
return null;
}
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);
instr[prog + (bank == 127 ? 128 : 0)] = ad_read_instr(data, offs + 2, bank == 127, isize - 2);
}
free(odata);
return instr;
}
private static Instrument bk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
instr->fixed = drum;
instr->op = b_op2;
private static Instrument bk_read_instr(byte[] data, int offset, boolean drum, int percnum) {
Operator[] ops = new Operator[2];
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;
boolean tremolo = (data[offset + op*13+9] & 0x01) != 0;
boolean vibrato = (data[offset + op*13+10] & 0x01) != 0;
boolean sustaining = (data[offset + op*13+5] & 0x01) != 0;
boolean ksr = (data[offset + op*13+11] & 0x01) != 0;
int mult = data[offset + op*13+1] & 0x0f;
int attack = data[offset + op*13+3] & 0x0f;
int decay = data[offset + op*13+6] & 0x0f;
int sustain = data[offset + op*13+4] & 0x0f;
int release = data[offset + op*13+7] & 0x0f;
int waveform = data[offset + 26+op] & 0x07;
int ksl = data[offset + op*13+0] & 0x03;
int level = data[offset + op*13+8] & 0x3f;
ops[op] = new Operator(mult, level, waveform, attack, decay, sustain, release, tremolo, vibrato, sustaining, ksr, ksl, 0, 0);
}
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;
Instrument ins = new Instrument(data[offset + 2] & 0x07, 0, (data[offset + 25] & 0x01) ^ 1, ops);
if(drum)
ins.setFixed(percnum);
return ins;
}
private static Instrument[] bk_read_bank(byte[] raw, String filename, boolean 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;
byte[] data = bnk_read_mbank(raw, filename, 28, 256 * 42 + 28);
if(data == null)
return null;
if(!Arrays.equals(data, 2, 2 + 6, "ADLIB-".getBytes(), 0, 6)) {
Log.SOUND.error("Fehler beim Lesen von Bank-Datei '%s': Fehlerhafter Dateiheader oder falscher Dateityp", filename);
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;
Instrument[] instr = new Instrument[256];
for(int i = 0; i < instr.length; i++) {
instr[i] = new Instrument();
}
data += 8;
for(int i = 0; i < 256; i++) {
instr[i].op = b_op0;
instr[i].name[0] = 0;
int offset = 8;
int ninst = dmx_read_uint16(data, offset + 2);
offset += 20;
if(ninst * 42 + 28 > data.length) {
Log.SOUND.error("Fehler beim Verarbeiten von Bank-Datei '%s': Ende der Daten erreicht", filename);
return null;
}
// 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;
offset += ninst * 12;
int pos = 0;
for(int i = 0; i < ninst; i++, data += 30) {
if(data[0] != 0) {
for(int i = 0; i < ninst; i++, offset += 30) {
if(data[offset + 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) {
int ndata = i * 12 + 28;
instr[pos + (drum ? 128 : 0)] = bk_read_instr(data, offset + 2, drum, data[ndata + 2] & 0x7f);
instr[pos + (drum ? 128 : 0)].setName(new String(data, ndata + 3, 8).trim());
if(++pos == 128)
break;
}
}
free(odata);
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(ext.equalsIgnoreCase("dmx") || ext.equalsIgnoreCase("op2") || ext.equalsIgnoreCase("lmp")) {
return dmx_read_bank(raw, name);
}
else if(ext.equalsIgnoreCase("tmb")) {
return tmb_read_bank(raw, name);
}
// else if(ext.equalsIgnoreCase("sb")) {
// return sb_read_bank(raw, name, drum);
// }
// else if(ext.equalsIgnoreCase("o3")) {
// return sb3_read_bank(raw, name, drum);
// }
// else if(ext.equalsIgnoreCase("op3")) {
// return op3_read_bank(raw, name);
// }
// else if(ext.equalsIgnoreCase("ad") || ext.equalsIgnoreCase("opl")) {
// return ad_read_bank(raw, name);
// }
// else if(ext.equalsIgnoreCase("bnk")) {
// return bk_read_bank(raw, name, drum);
// }
Log.SOUND.error("Format von Bank-Datei '%s' unbekannt", name);
return null;
// else if(strcmp(ext, "skb") == 0) {
// return skb_read_bank(filename);
// }
// else if(strcmp(ext, "ibk") == 0) {
// return ibk_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);
// }
}
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;
}
*/
/*
void ibk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
@ -774,155 +543,86 @@ typedef struct {
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(ext.equalsIgnoreCase("dmx") || ext.equalsIgnoreCase("op2") || ext.equalsIgnoreCase("lmp")) {
return dmx_read_bank(raw, name);
}
else if(ext.equalsIgnoreCase("tmb")) {
return tmb_read_bank(raw, name);
}
else if(ext.equalsIgnoreCase("sb")) {
return sb_read_bank(raw, name, drum);
}
else if(ext.equalsIgnoreCase("o3")) {
return sb3_read_bank(raw, name, drum);
}
else if(ext.equalsIgnoreCase("op3")) {
return op3_read_bank(raw, name);
}
else if(ext.equalsIgnoreCase("ad") || ext.equalsIgnoreCase("opl")) {
return ad_read_bank(raw, name);
}
else if(ext.equalsIgnoreCase("bnk")) {
return bk_read_bank(raw, name, drum);
}
// else if(ext.equalsIgnoreCase("ibk")) {
// return ibk_read_bank(raw, name, drum);
// }
// else if(ext.equalsIgnoreCase("tim") || ext.equalsIgnoreCase("snd")) {
// return tim_read_bank(raw, name, drum);
// }
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;
}
}