From 713af1216393de32d53a54f09435393c7c05b26e Mon Sep 17 00:00:00 2001 From: ZILtoid1991 Date: Sun, 7 Jul 2024 22:11:02 +0200 Subject: [PATCH] Fixing many IMBC-related bugs --- assets/m2testcases/volumeswell.imbc | 22 ++- docs/formats/imbc.md | 2 +- .../src/pixelperfectengine/audio/m2/rw_text.d | 171 +++++++----------- .../src/pixelperfectengine/audio/m2/seq.d | 79 +++++--- .../pixelperfectengine/audio/modules/qm816.d | 2 + test1/app.d | 4 +- 6 files changed, 147 insertions(+), 133 deletions(-) diff --git a/assets/m2testcases/volumeswell.imbc b/assets/m2testcases/volumeswell.imbc index 5188b27..000dc7a 100644 --- a/assets/m2testcases/volumeswell.imbc +++ b/assets/m2testcases/volumeswell.imbc @@ -27,16 +27,23 @@ $[0]: pc 5 16 $[0]: pc 6 16 $[0]: pc 7 16 +$[0]: ccl 0 7 0x3F_FF_FF_FF;Reset volume levels +$[0]: ccl 1 7 0x3F_FF_FF_FF +$[0]: ccl 2 7 0x3F_FF_FF_FF + $[0]: nn 0 0x7FFF C-4 ;Play a C major $[0]: nn 1 0x7FFF E-4 $[0]: nn 2 0x7FFF G-4 wait q ;Wait for a quarter note (test for pop elimination) +$[0]: ccl 0 7 0 ;Set all volume values to zero immediate +$[0]: ccl 1 7 0 +$[0]: ccl 2 7 0 ctrl setReg R5 1024 ;Target ctrl setReg R4 1 ;Increment amount ctrl setReg R3 0 ;Counter @loopentry: -lshi R3 16 R0 ;Shift R3 left by 16 bits, store in R0 +lshi R3 20 R0 ;Shift R3 left by 20 bits, store in R0 $[0]: ccl 0 7 R0 ;Emit volume change command $[0]: ccl 1 7 R0 $[0]: ccl 2 7 R0 @@ -45,6 +52,19 @@ wait i ;Wait for a 64th note (test for low-frequency rumble) cmpgt R5 R3 ;Compare if R5 is greater than R3 jmpsh 1 loopentry ;Jump if last compare was true +ctrl setReg R3 0 ;Counter reset + +@loopentry0: +sub R5 R3 R1 ;Subtract R3 from R5, store in R1 +lshi R1 20 R0 ;Shift R1 left by 20 bits, store in R0 +$[0]: ccl 0 7 R0 ;Emit volume change command +$[0]: ccl 1 7 R0 +$[0]: ccl 2 7 R0 +add R3 R4 R3 ;Increment R3 by R4 +wait u ;Wait for a 1024th note (test for mid-frequency noise) +cmpgt R5 R3 ;Compare if R5 is greater than R3 +jmpsh 1 loopentry0 ;Jump if last compare was true + $[0]: nf 0 0x7FFF C-4 ;End C major $[0]: nf 1 0x7FFF E-4 $[0]: nf 2 0x7FFF G-4 diff --git a/docs/formats/imbc.md b/docs/formats/imbc.md index dfe7add..21eaf3d 100644 --- a/docs/formats/imbc.md +++ b/docs/formats/imbc.md @@ -565,7 +565,7 @@ Referenced pattern will be played instead of the current one, with no returning. Bytecode layout: ``` -[42]{Register data source: 8 bits}{Device: 16 bits}{Register note or identifier source: 8 bits}{Channel register identifier: 8 bits}{aux register: 8 bits}{4 bits padding}{D}{N}{C}{A} +[42]{Register data source: 8 bits}{Device: 16 bits}{Register note or identifier source: 8 bits}{Channel register identifier: 8 bits}{aux register: 8 bits}{4 bits padding}{D}{N}{C}{A}{8 bytes of MIDI 2.0 data} ``` Human readable form: ``` diff --git a/pixelperfectengine/src/pixelperfectengine/audio/m2/rw_text.d b/pixelperfectengine/src/pixelperfectengine/audio/m2/rw_text.d index e879336..09a1bff 100644 --- a/pixelperfectengine/src/pixelperfectengine/audio/m2/rw_text.d +++ b/pixelperfectengine/src/pixelperfectengine/audio/m2/rw_text.d @@ -10,6 +10,7 @@ import std.format.read : formattedRead; import std.conv : to; import std.algorithm.searching : canFind, startsWith, countUntil; import collections.sortedlist; +import collections.hashmap; import midi2.types.enums; import midi2.types.structs; @@ -117,6 +118,8 @@ package ulong parseRhythm(string n, float bpm, long timebase) { return cast(ulong)(duration * whNoteLen); } ///Reads textual M2 files and compiles them into binary. +///Bugs: +/// * comment stripping is somehow inconsistent, sometimes just stops working altogether, thus certain checks are disabled for now. public M2File loadM2FromText(string src) { enum Context { @@ -133,7 +136,8 @@ public M2File loadM2FromText(string src) { size_t lineNum; uint lineLen; float currBPM = 120; - size_t[string] positionLabels; + //size_t[string] positionLabels; + HashMap!(string, size_t) positionLabels; } struct NoteData { uint device; @@ -179,9 +183,9 @@ public M2File loadM2FromText(string src) { if (words[0] == "END") { //Calculate line numbers then close current pattern parsing. ptrnData[$-1].lineLen = cast(uint)(lineNum - ptrnData[$-1].lineNum - 1); context = Context.init; - } else if (startsWith(words[0], "@")) { - ptrnData[$-1].positionLabels[words[0][1..$]] = lineNum; - } + } /* else if (startsWith(words[0], "@") && endsWith(words[0], ":")) { + ptrnData[$-1].positionLabels[words[0][1..$-1]] = lineNum; + } */ break; case Context.headerParse: switch (words[0]) { @@ -282,7 +286,7 @@ public M2File loadM2FromText(string src) { //Initialize song data result.songdata = M2Song(result.patternNum, result.timeFormat, result.timeFrmtPer, result.timeFrmtRes); //Second pass: parse patterns - foreach (size_t i, PatternData key; ptrnData) { + foreach (size_t i, ref PatternData key; ptrnData) { result.songdata.ptrnData[cast(uint)i] = []; //NoteData[] noteMacroHandler; SortedList!(NoteData, "a > b") noteMacroHandler; @@ -317,40 +321,42 @@ public M2File loadM2FromText(string src) { *ptrn ~= cmdStr; } } - void insertJmpCmd(const sizediff_t currLineNum, uint cmdCode, string[] words) { - const int targetAm = cast(int)(key.positionLabels[words[1]] - currLineNum); - const uint conditionMask = cast(uint)parsenum(words[0]); + void insertJmpCmd(const sizediff_t currLineNum, uint cmdCode, string[] instr) { + enforce(key.positionLabels.has(instr[1]), "Position label not found"); + flushEmitStr(); + const int targetAm = cast(int)(key.positionLabels[instr[1]] - currLineNum); + const uint conditionMask = cast(uint)parsenum(instr[0]); insertCmd([cmdCode, conditionMask, targetAm]); } - void insertMathCmd(const ubyte cmdCode, string[] words) { - enforce(words.length == 3, "Incorrect number of registers"); - const int ra = parseRegister(words[0]); - const int rb = parseRegister(words[1]); - const int rd = parseRegister(words[2]); - enforce((ra|rb|rd) <= -1, "Bad register number"); + void insertMathCmd(const ubyte cmdCode, string[] instr) { + //enforce(instr.length == 3, "Incorrect number of registers"); + const int ra = parseRegister(instr[0]); + const int rb = parseRegister(instr[1]); + const int rd = parseRegister(instr[2]); + //enforce((ra|rb|rd) <= -1, "Bad register number"); insertCmd([M2Command([cmdCode, cast(ubyte)ra, cast(ubyte)rb, cast(ubyte)rd]).word]); } - void insertShImmCmd(const ubyte cmdCode, string[] words) { - enforce(words.length == 3, "Incorrect number of registers"); - const int ra = parseRegister(words[0]); - const int rb = cast(int)parsenum(words[1]); - const int rd = parseRegister(words[2]); - enforce((ra|rd) <= -1, "Bad register number"); + void insertShImmCmd(const ubyte cmdCode, string[] instr) { + //enforce(instr.length == 3, "Incorrect number of registers"); + const int ra = parseRegister(instr[0]); + const int rb = cast(int)parsenum(instr[1]); + const int rd = parseRegister(instr[2]); + //enforce((ra|rd) <= -1, "Bad register number"); enforce(rb <= 31 && rb >= 0, "Bad immediate amount"); insertCmd([M2Command([cast(ubyte)cmdCode, cast(ubyte)ra, cast(ubyte)rb, cast(ubyte)rd]).word]); } - void insertTwoOpCmd(const ubyte cmdCode, string[] words) { - enforce(words.length == 2, "Incorrect number of registers"); - const int ra = parseRegister(words[0]); - const int rd = parseRegister(words[1]); + void insertTwoOpCmd(const ubyte cmdCode, string[] instr) { + //enforce(instr.length == 2, "Incorrect number of registers"); + const int ra = parseRegister(instr[0]); + const int rd = parseRegister(instr[1]); enforce((ra|rd) <= -1, "Bad register number"); insertCmd([M2Command([cmdCode, cast(ubyte)ra, cast(ubyte)0x00, cast(ubyte)rd]).word]); } - void insertCmpInstr(const ubyte cmprCode, string[] words) { - enforce(words.length == 2, "Incorrect number of registers"); - const int ra = parseRegister(words[0]); - const int rb = parseRegister(words[1]); - enforce((ra|rb) <= -1, "Bad register number"); + void insertCmpInstr(const ubyte cmprCode, string[] instr) { + //enforce(instr.length == 2, "Incorrect number of registers"); + const int ra = parseRegister(instr[0]); + const int rb = parseRegister(instr[1]); + //enforce((ra|rb) <= -1, "Bad register number"); insertCmd([M2Command([OpCode.cmp, cmprCode, cast(ubyte)ra, cast(ubyte)rb]).word]); } void insertMIDI2Cmd(bool longfield, bool note)(const ubyte cmdCode, string chField, string upperField, @@ -382,8 +388,9 @@ public M2File loadM2FromText(string src) { } static if (longfield) { rNote = parseRegister(upperField); - if (rNote != -1) emitWithRegVal |= 0x04; - else { + if (rNote != -1) { + emitWithRegVal |= 0x04; + } else { const uint lf = cast(uint)parsenum(upperField); lower = lf & 0x7F; upper = lf>>7; @@ -391,8 +398,9 @@ public M2File loadM2FromText(string src) { } else { if (upperField.length) { rNote = parseRegister(upperField); - if (rNote != -1) emitWithRegVal |= 0x04; - else { + if (rNote != -1) { + emitWithRegVal |= 0x04; + } else { static if (note) { upper = cast(uint)parseNote(upperField); } @@ -414,21 +422,24 @@ public M2File loadM2FromText(string src) { M2Command cmdUprHl = M2Command([OpCode.emit_r, cast(ubyte)rValue, 0, 0]); cmdUprHl.hwords[1] = cast(ushort)currDevNum; M2Command cmdLwrHl = M2Command([cast(ubyte)rNote, cast(ubyte)rCh, cast(ubyte)rAux, cast(ubyte)emitWithRegVal]); - currEmitStr ~= [cmdUprHl.word, cmdLwrHl.word, midiCMD.base, value]; - /* insertCmd([0x42_00_00_00 | currDevNum | (rValue<<16), (rNote<<24) | (rCh<<16) | (rAux<<8) | emitWithRegVal, - cmdCode | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | upper | lower, value]); */ + insertCmd([cmdUprHl.word, cmdLwrHl.word, midiCMD.base, value]); + //currEmitStr ~= [cmdUprHl.word, cmdLwrHl.word, midiCMD.base, value]; } else { UMP midiCMD = UMP(MessageType.MIDI2, cast(ubyte)(channel>>4), cmdCode, cast(ubyte)(channel&0x0F), cast(ubyte)upper, cast(ubyte)lower); currEmitStr ~= [midiCMD.base, value]; - //currEmitStr ~= [cmdCode | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | upper | lower, value]; } } for (sizediff_t lineNum = key.lineNum ; lineNum < key.lineNum + key.lineLen ; lineNum++) { string[] words = removeComment(lines[lineNum]).split!isWhite(); if (!words.length) continue;//If line is used as padding or just for comments, don't try to parse it - if (words[0][0] == '$') { //parse MIDI emit commands - const sizediff_t f = countUntil(words[0], '['), t =countUntil(words[0], ']'); + if (words[0][0] == '@') { //parse jump label + flushEmitStr(); //Flush emit string as it might contain data + const sizediff_t endOfJumpLabel = countUntil(words[0], ':'); + auto ptrn = result.songdata.ptrnData.ptrOf(cast(uint)i); + key.positionLabels[words[0][1..endOfJumpLabel]] = ptrn.length; + } else if (words[0][0] == '$') { //parse MIDI emit commands + const sizediff_t f = countUntil(words[0], '['), t = countUntil(words[0], ']'); const uint deviceNum = cast(uint)parsenum(words[0][f + 1..t]); enforce(deviceNum <= 65_535, "Device number too large"); if (currEmitStr.length > 251 || currDevNum != deviceNum) flushEmitStr(); //flush emit string if it's not guaranteed that a 4 word long data won't fit, or device isn't equal @@ -538,53 +549,27 @@ public M2File loadM2FromText(string src) { //MIDI 1.0 end //MIDI 2.0 begin case "nf": //MIDI note off - /* const uint channel = cast(uint)parsenum(words[2]); - const uint note = parseNote(words[3]); - const uint vel = cast(uint)parsenum(words[4]); - enforce(channel <= 255, "Channel number too high"); - enforce(vel <= 65_535, "Velocity number too high"); - currEmitStr ~= [0x20_80_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | (note<<8), vel<<16]; */ + string auxField; if (words.length == 6) auxField = words[5]; insertMIDI2Cmd!(false, true)(MIDI2_0Cmd.NoteOff, words[2], words[4], null, words[3], auxField); break; case "nn": //MIDI note on - /* const uint channel = cast(uint)parsenum(words[2]); - const uint note = parseNote(words[3]); - const uint vel = cast(uint)parsenum(words[4]); - enforce(channel <= 255, "Channel number too high"); - enforce(vel <= 65_535, "Velocity number too high"); - currEmitStr ~= [0x20_90_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | (note<<8), vel<<16]; */ + string auxField; if (words.length == 6) auxField = words[5]; insertMIDI2Cmd!(false, true)(MIDI2_0Cmd.NoteOn, words[2], words[4], null, words[3], auxField); break; case "ppres": //Poly aftertouch - /* const uint channel = cast(uint)parsenum(words[2]); - const uint note = parseNote(words[3]); - const uint vel = cast(uint)parsenum(words[4]); - enforce(channel <= 255, "Channel number too high"); - currEmitStr ~= [0x20_A0_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | (note<<8), vel]; */ + insertMIDI2Cmd!(false, false)(MIDI2_0Cmd.PolyAftrTch, words[2], words[4], null, words[3], null); break; case "pccr": //Poly registered per-note controller change - /* const uint channel = cast(uint)parsenum(words[2]); - const uint note = parseNote(words[3]); - const uint index = cast(uint)parsenum(words[4]); - const uint val = cast(uint)parsenum(words[5]); - enforce(channel <= 255, "Channel number too high"); - enforce(index <= 255, "Index number too high"); - currEmitStr ~= [0x20_00_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | (note<<8) | index, val]; */ + insertMIDI2Cmd!(false, false)(MIDI2_0Cmd.PolyCtrlChR, words[2], words[3], words[4], words[5], null); break; case "pcca": //Poly assignable per-note controller change - /* const uint channel = cast(uint)parsenum(words[2]); - const uint note = parseNote(words[3]); - const uint index = cast(uint)parsenum(words[4]); - const uint val = cast(uint)parsenum(words[5]); - enforce(channel <= 255, "Channel number too high"); - enforce(index <= 255, "Index number too high"); - currEmitStr ~= [0x20_10_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | (note<<8) | index, val]; */ + insertMIDI2Cmd!(false, false)(MIDI2_0Cmd.PolyCtrlCh, words[2], words[3], words[4], words[5], null); break; case "pnoteman": //Poly management message @@ -602,12 +587,7 @@ public M2File loadM2FromText(string src) { currEmitStr ~= []; break; case "ccl": //Legacy controller change - /* const uint channel = cast(uint)parsenum(words[2]); - const uint index = cast(uint)parsenum(words[3]); - const uint val = cast(uint)parsenum(words[4]); - enforce(channel <= 255, "Channel number too high"); - enforce(index <= 127, "Index number too high"); - currEmitStr ~= [0x20_B0_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | (index<<8), val]; */ + insertMIDI2Cmd!(false, false)(MIDI2_0Cmd.CtrlChOld, words[2], words[3], null, words[4], null); break; case "ccr": @@ -622,18 +602,6 @@ public M2File loadM2FromText(string src) { case "rcc": insertMIDI2Cmd!(true, false)(MIDI2_0Cmd.RelCtrlCh, words[2], words[3], null, words[4], null); break; - /* case "cc", "ccr", "rcc", "rccr"://Controller change commands - const uint channel = cast(uint)parsenum(words[2]); - const uint index = cast(uint)parsenum(words[3]); - const uint val = cast(uint)parsenum(words[4]); - const uint cmdNum = words[1] == "ccr" ? 0x20_20_00_00 : - (words[1] == "cc" ? 0x20_30_00_00 : - (words[1] == "rccr" ? 0x20_40_00_00 : 0x20_50_00_00)); - enforce(channel <= 255, "Channel number too high"); - enforce(index <= 16_383, "Index number too high"); - currEmitStr ~= [cmdNum | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | - ((index & 0x3F_80)<<1) | (index & 0x7F), val]; - break; */ case "pc": //Program change const uint channel = cast(uint)parsenum(words[2]); const uint prg = cast(uint)parsenum(words[3]); @@ -652,25 +620,15 @@ public M2File loadM2FromText(string src) { currEmitStr ~= [midiCMD.base, (prg<<24) | bank]; break; case "cpres": //Channel aftertouch - /* const uint channel = cast(uint)parsenum(words[2]); - const uint val = cast(uint)parsenum(words[3]); - enforce(channel <= 255, "Channel number too high"); - currEmitStr ~= [0x20_D0_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16), val]; */ + insertMIDI2Cmd!(false, false)(MIDI2_0Cmd.ChAftrTch, words[2], null, null, words[3], null); break; case "pb": //Pitch bend - /* const uint channel = cast(uint)parsenum(words[2]); - const uint val = cast(uint)parsenum(words[3]); - enforce(channel <= 255, "Channel number too high"); - currEmitStr ~= [0x20_E0_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16), val]; */ + insertMIDI2Cmd!(false, false)(MIDI2_0Cmd.PitchBend, words[2], null, null, words[3], null); break; case "ppb": //Poly pitch bend - /* const uint channel = cast(uint)parsenum(words[2]); - const uint note = parseNote(words[3]); - const uint val = cast(uint)parsenum(words[4]); - enforce(channel <= 255, "Channel number too high"); - currEmitStr ~= [0x20_60_00_00 | ((channel & 0xF0)<<20) | ((channel & 0x0F)<<16) | (note<<8), val]; */ + insertMIDI2Cmd!(false, false)(MIDI2_0Cmd.PitchBend, words[2], words[3], null, words[4], null); break; //MIDI 2.0 end @@ -859,6 +817,17 @@ public M2File loadM2FromText(string src) { insertCmpInstr(CmpCode.sle, words[1..$]); break; case "ctrl": + M2Command ctrlCMD = M2Command([OpCode.ctrl, 0, 0, 0]); + switch (words[1]) { + case "setReg": + ctrlCMD.bytes[1] = CtrlCmdCode.setRegister; + ctrlCMD.bytes[2] = cast(ubyte)parseRegister(words[2]); + uint val = cast(uint)parsenum(words[3]); + insertCmd([ctrlCMD.word, val]); + break; + default: + break; + } break; case "display": M2Command displCMD = M2Command([OpCode.display, 0, 0, 0]); diff --git a/pixelperfectengine/src/pixelperfectengine/audio/m2/seq.d b/pixelperfectengine/src/pixelperfectengine/audio/m2/seq.d index e85722e..5f45aa2 100644 --- a/pixelperfectengine/src/pixelperfectengine/audio/m2/seq.d +++ b/pixelperfectengine/src/pixelperfectengine/audio/m2/seq.d @@ -128,41 +128,67 @@ public class SequencerM2 : Sequencer { emitMIDIData(patternData[ptrn.position..ptrn.position + dataAm], device); ptrn.position += dataAm; break; + case OpCode.emit_r: //MIDI data emit with register data + const regDataSrc = data.bytes[1] & 0x80 ? songdata.globalReg[data.bytes[1]&0x7F] : ptrn.localReg[data.bytes[1]]; + const device = data.hwords[1]; + M2Command data1 = M2Command(patternData[ptrn.position]);//Read second word + ptrn.position++; + const regNoteSrc = data1.bytes[0] & 0x80 ? songdata.globalReg[data1.bytes[0]&0x7F] : ptrn.localReg[data1.bytes[0]]; + const regChSrc = data1.bytes[1] & 0x80 ? songdata.globalReg[data1.bytes[1]&0x7F] : ptrn.localReg[data1.bytes[1]]; + const regAuxSrc = data1.bytes[2] & 0x80 ? songdata.globalReg[data1.bytes[2]&0x7F] : ptrn.localReg[data1.bytes[2]]; + UMP data2 = UMP(patternData[ptrn.position]);//Read MIDI command chunk + ptrn.position++; + uint data3 = patternData[ptrn.position];//Read MIDI command chunk + ptrn.position++; + if (data1.bytes[3] & 0x08) { + if (data2.status == MIDI2_0Cmd.NoteOn || data2.status == MIDI2_0Cmd.NoteOff) { + data3 = regDataSrc & 0xFFFF_0000; + if (data1.bytes[3] & 0x01) { + data3 |= regAuxSrc>>16; + } + } + } + if (data1.bytes[3] & 0x04) { + if (data2.status == MIDI2_0Cmd.CtrlCh || data2.status == MIDI2_0Cmd.CtrlChOld + || data2.status == MIDI2_0Cmd.CtrlChR || data2.status == MIDI2_0Cmd.RelCtrlCh + || data2.status == MIDI2_0Cmd.RelCtrlChR) { + data2.note = cast(ubyte)(regNoteSrc>>7); + data2.value = cast(ubyte)(regNoteSrc); + } else { + data2.note = cast(ubyte)(regNoteSrc); + } + } + if (data1.bytes[3] & 0x02) { + data2.channel = cast(ubyte)regChSrc; + data2.group = cast(ubyte)(regChSrc>>4); + } + uint[2] dataToBeEmited; + dataToBeEmited[0] = data2.base; + dataToBeEmited[1] = data3; + emitMIDIData(dataToBeEmited, device); + break; case OpCode.jmp: //Conditional jump + const uint conditionMask = patternData[ptrn.position]; + ptrn.position++; + const int jumpAm = patternData[ptrn.position]; + ptrn.position++; switch (data.bytes[1]) { case JmpCode.nc: //Always jump - ptrn.position += cast(int)patternData[ptrn.position + 1] - 1; + ptrn.position += jumpAm - 2; break; case JmpCode.eq: //Jump if condition code and condition register are equal - if (ptrn.localReg[CR] == patternData[ptrn.position]) { - ptrn.position += cast(int)patternData[ptrn.position + 1] - 1; - } else { - ptrn.position += 2; - } + if (ptrn.localReg[CR] == conditionMask) ptrn.position += jumpAm - 2; break; case JmpCode.ne: //Jump if condition code and condition register are not equal - if (ptrn.localReg[CR] != patternData[ptrn.position]) { - ptrn.position += cast(int)patternData[ptrn.position + 1] - 1; - } else { - ptrn.position += 2; - } + if (ptrn.localReg[CR] != conditionMask) ptrn.position += jumpAm - 2; break; case JmpCode.sh: //Jump if at least some bits of the condition code is also high in the condition register - if (ptrn.localReg[CR] & patternData[ptrn.position]) { - ptrn.position += cast(int)patternData[ptrn.position + 1] - 1; - } else { - ptrn.position += 2; - } + if (ptrn.localReg[CR] & conditionMask) ptrn.position += jumpAm - 2; break; case JmpCode.op: //Jump if condition code bits are opposite of CR - if (ptrn.localReg[CR] == ~patternData[ptrn.position]) { - ptrn.position += cast(int)patternData[ptrn.position + 1] - 1; - } else { - ptrn.position += 2; - } + if (ptrn.localReg[CR] == ~conditionMask) ptrn.position += jumpAm - 2; break; default: - ptrn.position += 2; errors.unrecognizedCode = true; if (status.cfg_StopOnError) { status.play = false; @@ -439,8 +465,9 @@ public class SequencerM2 : Sequencer { case OpCode.ctrl: //Control commands switch (data.bytes[1]) { case CtrlCmdCode.setRegister: - if (data.bytes[3] & 0x80) songdata.globalReg[data.bytes[3] & 0x7F] = patternData[ptrn.position]; - else ptrn.localReg[data.bytes[3]] = patternData[ptrn.position]; + const newVal = patternData[ptrn.position]; + if (data.bytes[2] & 0x80) songdata.globalReg[data.bytes[2] & 0x7F] = newVal; + else ptrn.localReg[data.bytes[2]] = newVal; ptrn.position += 1; break; case CtrlCmdCode.setEnvVal: @@ -615,8 +642,4 @@ public class SequencerM2 : Sequencer { emitMIDIData_intrnl!(false)(data, dd, am); } } - ///TODO: Implement - private void emitMIDIwRegData(uint[] data, uint targetID, uint channel, uint value, uint note, uint aux) @nogc nothrow { - - } } \ No newline at end of file diff --git a/pixelperfectengine/src/pixelperfectengine/audio/modules/qm816.d b/pixelperfectengine/src/pixelperfectengine/audio/modules/qm816.d index d399634..036b034 100644 --- a/pixelperfectengine/src/pixelperfectengine/audio/modules/qm816.d +++ b/pixelperfectengine/src/pixelperfectengine/audio/modules/qm816.d @@ -1420,6 +1420,8 @@ public class QM816 : AudioModule { case 107: setUnregisteredParam(data1, [4, 13], data0.channel); break; + + default: break; } diff --git a/test1/app.d b/test1/app.d index ce313e8..44cc621 100644 --- a/test1/app.d +++ b/test1/app.d @@ -423,7 +423,7 @@ public class AudioDevKit : InputListener, SystemEventListener { import pixelperfectengine.concrete.dialogs.filedialog; wh.addWindow(new FileDialog("Load MIDI file.", "loadMidiDialog", &onMIDIFileLoad, [FileDialog.FileAssociationDescriptor("MIDI file", ["*.mid"]), - FileDialog.FileAssociationDescriptor("M2 file", ["*.m2"])], "./")); + FileDialog.FileAssociationDescriptor("Intelligent MIDI Bytecode file", ["*.imbc", "*.imb"])], "./")); } public void onMIDIFileLoad(Event ev) { import mididi; @@ -439,7 +439,7 @@ public class AudioDevKit : InputListener, SystemEventListener { wh.message("Error!", "No routing table has been initialized in current audio configuration!"); } break; - case ".m2": + case ".imbc", ".imb": m2Seq.loadSong(loadM2File(fe.getFullPath), mcfg); state.m2Toggle = true; mm.midiSeq = m2Seq;