1 module pixelperfectengine.audio.m2.types; 2 3 public import core.time : Duration, hnsecs, nsecs; 4 import std.typecons : BitFlags; 5 import collections.treemap; 6 7 /** 8 * Contains opcodes for M2 operations. 9 */ 10 public enum OpCode : ubyte { 11 nullcmd = 0x00, ///Null command 12 shwait = 0x01, ///Short wait (24 bits) 13 lnwait = 0x02, ///Long wait (48 bits) 14 emit = 0x03, ///Emit command list to device 15 jmp = 0x04, ///Conditional jump 16 chain_par = 0x05, ///Chain in pattern in parallel 17 chain_ser = 0x06, ///Chain in pattern in serial 18 //Math operations on registers begin 19 add = 0x07, ///Add two registers, store in third 20 sub = 0x08, ///Subtract two registers, store in third 21 mul = 0x09, ///Multiply two registers, store in third 22 div = 0x0a, ///Divide two registers, store in third 23 mod = 0x0b, ///Modulo two registers, store in third 24 and = 0x0c, ///Binary and two registers, store in third 25 or = 0x0d, ///Binary or two registers, store in third 26 xor = 0x0e, ///Binary xor two registers, store in third 27 not = 0x0f, ///Binary invert RA, store in RD 28 lshi = 0x10, ///Logically shift left RA by RB immediate value, store in RD 29 rshi = 0x11, ///Logically shift right RA by RB immediate value, store in RD 30 rasi = 0x12, ///Arithmetically shift right RA by RB immediate value, store in RD 31 adds = 0x13, ///Add two registers, store in third (signed) 32 subs = 0x14, ///Subtract two registers, store in third (signed) 33 muls = 0x15, ///Multiply two registers, store in third (signed) 34 divs = 0x16, ///Divide two registers, store in third (signed) 35 lsh = 0x17, ///Logically shift left RA by RB, store in RD 36 rsh = 0x18, ///Logically shift right RA by RB, store in RD 37 ras = 0x19, ///Arithmetically shift right RA by RB, store in RD 38 mov = 0x1a, ///Move register content of RA to RD 39 satadd = 0x1b, ///Saturated add 40 satsub = 0x1c, ///Saturated subtract 41 satmul = 0x1d, ///Saturated multiply 42 satadds = 0x1e, ///Saturated signed add 43 satsubs = 0x1f, ///Saturated signed subtract 44 satmuls = 0x20, ///Saturated signed multiply 45 //Math operations on registers end 46 cmp = 0x40, ///Compare two register values 47 chain = 0x41, ///Abandon current pattern to next one 48 emit_r = 0x42, ///Emit command with value from register 49 cue = 0x48, ///Set cue point/marker 50 trnsps = 0x49, ///Transpose 51 ctrl = 0xf0, ///Control command 52 display = 0xff, ///Display command 53 } 54 /** 55 * Contains compare codes for M2 cmp operations. 56 */ 57 public enum CmpCode : ubyte { 58 init, 59 eq, ///Equal 60 ne, ///Not equal 61 gt, ///Greater than 62 ge, ///Greater or equal 63 lt, ///Less than 64 le, ///Less or equal 65 ze, ///RA is zero 66 nz, ///RA is not zero 67 ng, ///RA is negative 68 po, ///RA is positive 69 sgt, ///Signed greater than 70 sge, ///Signed greater or equal 71 slt, ///Signed less than 72 sle, ///Signed less or equal 73 } 74 /** 75 * Contains jump condition coded for M2 jmp operations. 76 */ 77 public enum JmpCode : ubyte { 78 nc, ///Always jump 79 eq, ///Jump if condition code is equal to condition register 80 ne, ///Jump if condition code is not equal to condition register 81 sh, ///Jump if at least some of the same bits are high in both the condition code and the condition register 82 op, ///Jump is all the bits are opposite in the condition code from the condition register 83 } 84 /** 85 * Defines display command codes. 86 */ 87 public enum DisplayCmdCode : ubyte { 88 init, 89 setVal = 0x01, 90 setVal64 = 0x02, 91 92 strCue = 0xF0, 93 strNotation = 0xF1, 94 strLyrics = 0xF2, 95 96 strPrevBlC = 0xFF, 97 } 98 /** 99 * Defines values that can be modified by the display command. 100 */ 101 public enum SetDispValCode : ushort { 102 init, 103 BPM = 0x00_01, 104 timeSignature = 0x00_02, 105 clef = 0x00_03, 106 keySignature = 0x00_04, 107 } 108 /** 109 * Defines control command codes. 110 */ 111 public enum CtrlCmdCode : ubyte { 112 init, 113 setRegister = 0x01, 114 setEnvVal = 0x02, 115 } 116 /** 117 * Defines environment values that can be modified by the control command. 118 */ 119 public enum SetEnvValCode : ushort { 120 init, 121 setTimeMultLocal = 0x00_01, 122 setTimeMultGlobal = 0x00_02, 123 } 124 /** 125 * Defines the time formats that are possible within the M2 format. 126 */ 127 public enum M2TimeFormat : ubyte { 128 ms, 129 us, 130 hns, 131 fmt3, 132 fmt4, 133 fmt5, 134 } 135 ///Compare register number. 136 public enum CR = 127; 137 ///ID used to designate inactive pattern slots. 138 public enum PATTERN_SLOT_INACTIVE_ID = uint.max; 139 140 /** 141 * Defines an M2 pattern slot status data. 142 */ 143 public struct M2PatternSlot { 144 public enum StatusFlags : uint { 145 isRunning = 1<<0, ///Set if pattern is running 146 hasEnded = 1<<1, ///Set if pattern has ended(slot can be reused) 147 suspend = 1<<2, ///Set if pattern is on suspension 148 } 149 public uint[128] localReg; ///Local register bank 150 public BitFlags!StatusFlags status; ///Status flags 151 public uint lastCue; ///ID of the last reached cue 152 public uint id = PATTERN_SLOT_INACTIVE_ID;///ID of the currently played pattern 153 public uint position; ///Position within the pattern 154 public uint timeMult = 0x1_00_00; ///Time multiplier (16bit precision) 155 public uint backLink = uint.max; ///Backlinking for pattern nesting 156 public Duration timeToWait; ///Time until next command chunk 157 public Duration patternTime; ///Stores the current time of the pattern 158 public void reset() @nogc @safe nothrow { 159 status = status.init; 160 foreach (ref uint key; localReg) { 161 key = 0; 162 } 163 lastCue = 0; 164 position = 0; 165 timeMult = 0x1_00_00; 166 timeToWait = hnsecs(0); 167 patternTime = hnsecs(0); 168 } 169 } 170 /** 171 * Defines M2 song data. 172 */ 173 public struct M2Song { 174 public uint[128] globalReg; ///Global (shared) register bank 175 public M2PatternSlot[] ptrnSl; ///Pattern slots that can be used for the 176 //public uint[] activePtrnNums; 177 public uint globTimeMult = 0x1_00_00;///Time multiplier (16bit precision) 178 public ulong timebase; ///nsecs of a single tic 179 public TreeMap!(uint, uint[]) ptrnData; 180 this (uint parPtrnNum, M2TimeFormat timefrmt, uint timeper, uint timeres) @safe nothrow { 181 ptrnSl.length = parPtrnNum; 182 final switch (timefrmt) with(M2TimeFormat) { 183 case ms: 184 timebase = 1_000_000; 185 break; 186 case us: 187 timebase = 1_000; 188 break; 189 case hns: 190 timebase = 100; 191 break; 192 case fmt3: 193 timebase = cast(ulong)((1 / (cast(real)timeper / timeres)) * 1_000_000_000); 194 break; 195 case fmt4: 196 timebase = cast(ulong)((1 / (timeper / 256.0 / timeres)) * 1_000_000_000); 197 break; 198 case fmt5: 199 timebase = cast(ulong)((1 / (timeper / 65_536.0 / timeres)) * 1_000_000_000); 200 break; 201 } 202 203 } 204 } 205 /** 206 * Contains all officially recognized M2 file data. 207 */ 208 public struct M2File { 209 public M2Song songdata; 210 public string[string] metadata; 211 public string[uint] devicelist; 212 public ushort patternNum; 213 public M2TimeFormat timeFormat; 214 public uint timeFrmtPer; 215 public uint timeFrmtRes; 216 } 217 ///Used by the sequencer for reading command data. 218 package struct DataReaderHelper { 219 union { 220 uint word; 221 ushort[2] hwords; 222 ubyte[4] bytes; 223 } 224 this (uint base) @nogc @safe pure nothrow { 225 word = base; 226 } 227 } 228 ///Used for note lookup when reading and writing textual M2 files. 229 package immutable string[128] NOTE_LOOKUP_TABLE = 230 [ 231 "C-00","C#00","D-00","D#00","E-00","F-00","F#00","G-00", //0 232 "G#00","A-00","A#00","B-00","C-0", "C#0", "D-0", "D#0", //1 233 "E-0", "F-0", "F#0", "G-0", "G#0", "A-0", "A#0", "B-0", //2 234 "C-1", "C#1", "D-1", "D#1", "E-1", "F-1", "F#1", "G-1", //3 235 "G#1", "A-1", "A#1", "B-1", "C-2", "C#2", "D-2", "D#2", //4 236 "E-2", "F-2", "F#2", "G-2", "G#2", "A-2", "A#2", "B-2", //5 237 "C-3", "C#3", "D-3", "D#3", "E-3", "F-3", "F#3", "G-3", //6 238 "G#3", "A-3", "A#3", "B-3", "C-4", "C#4", "D-4", "D#4", //7 239 "E-4", "F-4", "F#4", "G-4", "G#4", "A-4", "A#4", "B-4", //8 240 "C-5", "C#5", "D-5", "D#5", "E-5", "F-5", "F#5", "G-5", //9 241 "G#5", "A-5", "A#5", "B-5", "C-6", "C#6", "D-6", "D#6", //10 242 "E-6", "F-6", "F#6", "G-6", "G#6", "A-6", "A#6", "B-6", //11 243 "C-7", "C#7", "D-7", "D#7", "E-7", "F-7", "F#7", "G-7", //12 244 "G#7", "A-7", "A#7", "B-7", "C-8", "C#8", "D-8", "D#8", //13 245 "E-8", "F-8", "F#8", "G-8", "G#8", "A-8", "A#8", "B-8", //14 246 "C-9", "C#9", "D-9", "D#9", "E-9", "F-9", "F#9", "G-9", //15 247 ];