diff --git a/client/src/main/java/client/audio/BankLoader.java b/client/src/main/java/client/audio/BankLoader.java index 063cdc03..eee3c2c1 100644 --- a/client/src/main/java/client/audio/BankLoader.java +++ b/client/src/main/java/client/audio/BankLoader.java @@ -51,12 +51,7 @@ typedef struct { } 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" +//#define STR_BNK_EEOD "Fehler beim Verarbeiten von Bank-Datei '%s': Ende der Daten erreicht" private static byte[] bnk_read_mhbank(byte[] raw, String filename, int minsize, int maxsize, byte[] hdr) { int size = raw.length; @@ -171,12 +166,54 @@ typedef struct { } return instr; } - + + private static Instrument tmb_read_instr(byte[] data, int offset, boolean drum) { + Operator[] ops = new Operator[2]; + int transpose = drum ? 0 : (int)((byte)data[11]); + for(int op = 0; op < 2; op++) { + boolean tremolo = (data[op+0] & 0x80) != 0; + boolean vibrato = (data[op+0] & 0x40) != 0; + boolean sustaining = (data[op+0] & 0x20) != 0; + boolean ksr = (data[op+0] & 0x10) != 0; + int mult = data[op+0] & 0x0f; + int attack = ((data[op+4] & 255) >> 4) & 0x0f; + int decay = data[op+4] & 0x0f; + int sustain = ((data[op+6] & 255) >> 4) & 0x0f; + int release = data[op+6] & 0x0f; + int waveform = data[op+8] & 0x07; + int ksl = ((data[op+2] & 255) >> 6) & 0x03; + int level = data[op+2] & 0x3f; + ops[op] = new Operator(mult, level, waveform, attack, decay, sustain, release, tremolo, vibrato, sustaining, ksr, ksl, transpose, 0); + } + Instrument ins = new Instrument((data[10] >> 1) & 0x07, 0, data[10] & 0x01, ops); + if(drum) + ins.setFixed(data[11] & 0x7f); + return ins; + } + + private static Instrument[] tmb_read_bank(byte[] raw, String filename) { + byte[] data = bnk_read_sbank(raw, filename, 256 * 13); + if(data == null) + return null; + Instrument[] instr = new Instrument[256]; + int offset = 0; + for(int i = 0; i < 256; i++) { + instr[i] = tmb_read_instr(data, offset, i >= 128); + offset += 13; + } + return instr; + } + /* - private static void tmb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) { - instr->name[0] = 0; + private static Instrument 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; @@ -193,27 +230,444 @@ typedef struct { } 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].offset = 0; instr->channels[0].detune = 0; } - bank_instr *tmb_read_bank(const char *filename) { + 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, 256*13); + 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 < 256; i++) { - tmb_read_instr(&instr[i], data, i >= 128); - data += 13; + 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; } + private static Instrument 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; + } + } + + 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; + } + 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; + } + + 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++) { + 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; + } + } + + 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; + } + 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; + } + + 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++) { + 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; + } + } + + 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; + } + 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; + } + + private static Instrument 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; + } + + 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; + } + 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; + } + */ + + 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) { instr->fixed = drum; instr->op = b_op2; @@ -263,318 +717,9 @@ typedef struct { 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; @@ -780,155 +925,4 @@ typedef struct { 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/WavWriter.java b/client/src/main/java/client/audio/WavWriter.java index 592360f2..dc7c9b78 100644 --- a/client/src/main/java/client/audio/WavWriter.java +++ b/client/src/main/java/client/audio/WavWriter.java @@ -2,56 +2,56 @@ 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 + private static final byte[] wav_hdr = "RIFF".getBytes(); + private static final byte[] wav_hdr_fmt = "fmt ".getBytes(); + private static final byte[] wav_hdr_data = "data".getBytes(); + private static final byte[] wav_format = "WAVE".getBytes(); + private static final int 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; + private final int samplerate; + private final int channels; + private final OutputStream fd; + private final File filename; + private final byte[] buffer; - void wav_write_uint32(uint8_t *data, uint32_t value) { + private int samples; + + private void wav_write_uint32(byte[] data, int offset, int 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) { + private void wav_write_uint16(byte[] data, int offset, short 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) { + private static byte[] makeHeader(int samplerate, int channels, int samples, int bytes) { + byte[] data = new byte[WAV_HDRSIZE]; memcpy(data+0, wav_hdr, 4); - wav_write_uint32(data+4, 36 + samples * ((uint32_t)channels) * ((uint32_t)bytes)); + wav_write_uint32(data+4, 36 + samples * channels * 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_uint32(data+28, samplerate * channels * 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)); + wav_write_uint32(data+40, samples * channels * bytes); + return data; } - bool wav_open(wav_handle *wav, const char *filename, uint32_t samplerate, uint16_t channels, uint16_t bytes) { + public WavWriter(File filename, int samplerate, int channels) throws IOException { int err; FILE *fd = fopen(filename, "wb"); if(fd == NULL) { @@ -69,13 +69,10 @@ public class WavWriter { 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) { + public boolean close() { if(wav->fd == NULL) { return false; } @@ -87,7 +84,7 @@ public class WavWriter { return false; } uint8_t header[WAV_HDRSIZE]; - wav_write_header(header, wav->samplerate, wav->channels, wav->samples, wav->bytes); + wav_write_header(header, wav->samplerate, wav->channels, wav->samples, 2); if(fwrite(header, 1, WAV_HDRSIZE, wav->fd) != WAV_HDRSIZE) { mid_log(STR_WAV_EIO, wav->filename); fclose(wav->fd); @@ -96,5 +93,21 @@ public class WavWriter { fclose(wav->fd); return true; } + + public void write(short[] samples, int offset, int count) { + if(wav->fd == NULL) { + return; + } + for(int c = 0; c < (count * ((uint32_t)wav->channels)); c++) { + wav_write_uint16((wav->buffer)+(c*SND_SMPSIZE), samples[c]); + } + if(fwrite(wav->buffer, 1, SND_SMPSIZE * ((uint32_t)wav->channels) * count, wav->fd) != (SND_SMPSIZE * ((uint32_t)wav->channels) * count)) { + fprintf(stderr, STR_WAV_EIO"\n", wav->filename); + fclose(wav->fd); + wav->fd = NULL; + return; + } + wav->samples += count; + } */ }