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 	];