1 module test1.app;
2 
3 import std.stdio;
4 import std.string;
5 import std.conv;
6 import std.format;
7 import std.random;
8 
9 import bindbc.sdl;
10 
11 import midi2.types.structs;
12 import midi2.types.enums;
13 
14 import pixelperfectengine.graphics.outputscreen;
15 import pixelperfectengine.graphics.raster;
16 import pixelperfectengine.graphics.layers;
17 
18 import pixelperfectengine.graphics.bitmap;
19 
20 import pixelperfectengine.system.input;
21 import pixelperfectengine.system.file;
22 import pixelperfectengine.system.etc;
23 import pixelperfectengine.system.config;
24 
25 import pixelperfectengine.system.common;
26 
27 import pixelperfectengine.audio.base.handler;
28 import pixelperfectengine.audio.base.modulebase;
29 import pixelperfectengine.audio.modules.qm816;
30 import core.thread;
31 
32 
33 /** 
34  * Audio subsystem test.
35  * 
36  */
37 int main(string[] args) {
38 	initialzeSDL();
39 	TestAudio app = new TestAudio(args);
40 	app.whereTheMagicHappens();
41 	return 0;
42 }
43 /** 
44  * Testcase for the audio system.
45  * Capable of playing back external files.
46  */
47 public class TestAudio : InputListener, SystemEventListener {
48 	AudioDeviceHandler adh;
49 	ModuleManager	mm;
50 	QM816			fmsynth;
51 	OutputScreen	output;
52 	InputHandler	ih;
53 	Raster			r;
54 	TileLayer		textOut;
55 	uint			state;
56 	ubyte			noteBase = 60;
57 	enum StateFlags {
58 		isRunning		=	1<<0,
59 		driverInitialized=	1<<1,
60 		deviceInitialized=	1<<2,
61 	}
62 	
63 	public this(string[] args) {
64 		state |= StateFlags.isRunning;
65 		Image fontSource = loadImage(File("../system/cp437_8x16.png"));
66 		output = new OutputScreen("Audio test", 848 * 2, 480 * 2);
67 		r = new Raster(848,480,output,0);
68 		output.setMainRaster(r);
69 		textOut = new TileLayer(8, 16, RenderingMode.Copy);
70 		r.addPaletteChunk(loadPaletteFromImage(fontSource));
71 		r.addLayer(textOut, 0);
72 
73 		{
74 			MappingElement[] map;
75 			map.length = 106 * 30;
76 			textOut.loadMapping(106,30, map);
77 			Bitmap8Bit[] tiles = loadBitmapSheetFromImage!Bitmap8Bit(fontSource, 8, 16);
78 			foreach (i, key; tiles) {
79 				textOut.addTile(key, to!wchar(i),1);
80 			}
81 		}
82 
83 		ih = new InputHandler();
84 		ih.systemEventListener = this;
85 		ih.inputListener = this;
86 		
87 		{
88 			import pixelperfectengine.system.input.scancode;
89 			ih.addBinding(BindingCode(ScanCode.GRAVE, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("grave"));
90 			ih.addBinding(BindingCode(ScanCode.n1, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num1"));
91 			ih.addBinding(BindingCode(ScanCode.n2, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num2"));
92 			ih.addBinding(BindingCode(ScanCode.n3, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num3"));
93 			ih.addBinding(BindingCode(ScanCode.n4, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num4"));
94 			ih.addBinding(BindingCode(ScanCode.n5, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num5"));
95 			ih.addBinding(BindingCode(ScanCode.n6, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num6"));
96 			ih.addBinding(BindingCode(ScanCode.n7, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num7"));
97 			ih.addBinding(BindingCode(ScanCode.n8, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num8"));
98 			ih.addBinding(BindingCode(ScanCode.n9, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num9"));
99 			ih.addBinding(BindingCode(ScanCode.n0, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("num0"));
100 			ih.addBinding(BindingCode(ScanCode.MINUS, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("minus"));
101 			ih.addBinding(BindingCode(ScanCode.EQUALS, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("equals"));
102 			ih.addBinding(BindingCode(ScanCode.Q, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("q"));
103 			ih.addBinding(BindingCode(ScanCode.W, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("w"));
104 			ih.addBinding(BindingCode(ScanCode.E, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("e"));
105 			ih.addBinding(BindingCode(ScanCode.R, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("r"));
106 			ih.addBinding(BindingCode(ScanCode.T, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("t"));
107 			ih.addBinding(BindingCode(ScanCode.Y, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("y"));
108 			ih.addBinding(BindingCode(ScanCode.U, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("u"));
109 			ih.addBinding(BindingCode(ScanCode.I, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("i"));
110 			ih.addBinding(BindingCode(ScanCode.O, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("o"));
111 			ih.addBinding(BindingCode(ScanCode.P, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("p"));
112 			ih.addBinding(BindingCode(ScanCode.LEFTBRACKET, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("["));
113 			ih.addBinding(BindingCode(ScanCode.RIGHTBRACKET, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("]"));
114 			//ih.addBinding(BindingCode(ScanCode.GRAVE, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("grave"));
115 
116 		}
117 
118 		adh = new AudioDeviceHandler(48_000, 2, 1024);
119 		
120 		int line;
121 		textOut.writeTextToMap(0, line++, 0, "Available drivers:");
122 		foreach (i, key; adh.getDrivers) {
123 			textOut.writeTextToMap(0, line, 0, to!wstring(i + 1));
124 			textOut.writeTextToMap(2, line++, 0, to!wstring(key));
125 		}
126 		
127 	}
128 	void whereTheMagicHappens() {
129 		while (state & StateFlags.isRunning) {
130 			r.refresh();
131 			ih.test();
132 		}
133 		if (mm !is null) {
134 			writeln(mm.suspendAudioThread());
135 		}
136 	}
137 
138 	public void clearScreen() {
139 		for (int y ; y < 30 ; y++)
140 			for (int x ; x < 106 ; x++)
141 				textOut.writeMapping(x, y, MappingElement(' '));
142 	}
143 	
144 	public void initDriver(int num) {
145 		clearScreen();
146 
147 		adh.initAudioDriver(num);
148 
149 		int line;
150 		textOut.writeTextToMap(0, line++, 0, "Available devices:");
151 		foreach (i, key; adh.getDevices) {
152 			textOut.writeTextToMap(0, line, 0, to!wstring(i + 1));
153 			textOut.writeTextToMap(2, line++, 0, to!wstring(key));
154 		}
155 		if (line == 1) {
156 			textOut.writeTextToMap(0, line, 0, "` ");
157 			textOut.writeTextToMap(2, line++, 0, "Default");
158 		}
159 
160 		state |= StateFlags.driverInitialized;
161 	}
162 
163 	public void initDevice(int num) {
164 		clearScreen();
165 		try {
166 			adh.initAudioDevice(num);
167 
168 			//int line;
169 			textOut.writeTextToMap(0, 0, 0, "Sample Rate:");
170 			textOut.writeTextToMap(14, 0, 0, to!wstring(adh.getSamplingFrequency));
171 			textOut.writeTextToMap(21, 0, 0, "Channels:");
172 			textOut.writeTextToMap(31, 0, 0, to!wstring(adh.getChannels));
173 			textOut.writeTextToMap(35, 0, 0, "Bits:");
174 			textOut.writeTextToMap(40, 0, 0, to!wstring(adh.getFormat & 0xFF));
175 			textOut.writeTextToMap(0, 1, 0, "Synth:");
176 			textOut.writeTextToMap(7, 1, 0, "QM816");
177 		} catch (AudioInitException e) {
178 			textOut.writeTextToMap(14, 0, 0, to!wstring(e.msg));
179 		}
180 		mm = new ModuleManager(adh, 192, 10);
181 		fmsynth = new QM816();
182 		mm.addModule(fmsynth, null, null, [0,1], [0,1]);
183 
184 		//Initialize audio thread
185 		ThreadID status = mm.runAudioThread();
186 		if (status == ThreadID.init)
187 			throw new Exception("Audio thread error!");
188 
189 		state |= StateFlags.deviceInitialized;
190 	}
191 
192 	public void keyEvent(uint id, BindingCode code, uint timestamp, bool isPressed) {
193 		if (isPressed) {
194 			if (!(state & StateFlags.driverInitialized)) {
195 				switch (id) {
196 					case hashCalc("num1"):
197 						initDriver(0);
198 						break;
199 					case hashCalc("num2"):
200 						initDriver(1);
201 						break;
202 					case hashCalc("num3"):
203 						initDriver(2);
204 						break;
205 					case hashCalc("num4"):
206 						initDriver(3);
207 						break;
208 					case hashCalc("num5"):
209 						initDriver(4);
210 						break;
211 					case hashCalc("num6"):
212 						initDriver(5);
213 						break;
214 					case hashCalc("num7"):
215 						initDriver(6);
216 						break;
217 					case hashCalc("num8"):
218 						initDriver(7);
219 						break;
220 					case hashCalc("num9"):
221 						initDriver(8);
222 						break;
223 					case hashCalc("num0"):
224 						initDriver(9);
225 						break;
226 					case hashCalc("grave"):
227 						initDriver(-1);
228 						break;
229 					default:
230 						break;
231 				}
232 			} else if (!(state & StateFlags.deviceInitialized)) {
233 				switch (id) {
234 					case hashCalc("num1"):
235 						initDevice(0);
236 						break;
237 					case hashCalc("num2"):
238 						initDevice(1);
239 						break;
240 					case hashCalc("num3"):
241 						initDevice(2);
242 						break;
243 					case hashCalc("num4"):
244 						initDevice(3);
245 						break;
246 					case hashCalc("num5"):
247 						initDevice(4);
248 						break;
249 					case hashCalc("num6"):
250 						initDevice(5);
251 						break;
252 					case hashCalc("num7"):
253 						initDevice(6);
254 						break;
255 					case hashCalc("num8"):
256 						initDevice(7);
257 						break;
258 					case hashCalc("num9"):
259 						initDevice(8);
260 						break;
261 					case hashCalc("num0"):
262 						initDevice(9);
263 						break;
264 					case hashCalc("grave"):
265 						initDevice(-1);
266 						break;
267 					default:
268 						break;
269 				}
270 			} else {
271 				UMP midipacket;
272 				switch (id) {
273 					case hashCalc("q"):		//C
274 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, noteBase, MIDI2_0NoteAttrTyp.None);
275 						break;
276 					case hashCalc("num2"):	//C#
277 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 1), 
278 								MIDI2_0NoteAttrTyp.None);
279 						break;
280 					case hashCalc("w"):		//D
281 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 2), 
282 								MIDI2_0NoteAttrTyp.None);
283 						break;
284 					case hashCalc("num3"):	//D#
285 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 3), 
286 								MIDI2_0NoteAttrTyp.None);
287 						break;
288 					case hashCalc("e"):		//E
289 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 4), 
290 								MIDI2_0NoteAttrTyp.None);
291 						break;
292 					case hashCalc("r"):		//F
293 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 5), 
294 								MIDI2_0NoteAttrTyp.None);
295 						break;
296 					case hashCalc("num5"):	//F#
297 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 6), 
298 								MIDI2_0NoteAttrTyp.None);
299 						break;
300 					case hashCalc("t"):		//G
301 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 7), 
302 								MIDI2_0NoteAttrTyp.None);
303 						break;
304 					case hashCalc("num6"):	//G#
305 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 8), 
306 								MIDI2_0NoteAttrTyp.None);
307 						break;
308 					case hashCalc("y"):		//A
309 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 9), 
310 								MIDI2_0NoteAttrTyp.None);
311 						break;
312 					case hashCalc("num7"):	//A#
313 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 10), 
314 								MIDI2_0NoteAttrTyp.None);
315 						break;
316 					case hashCalc("u"):		//B
317 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 11), 
318 								MIDI2_0NoteAttrTyp.None);
319 						break;
320 					case hashCalc("i"):		//C+
321 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 12), 
322 								MIDI2_0NoteAttrTyp.None);
323 						break;
324 					case hashCalc("num9"):	//C#+
325 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 13), 
326 								MIDI2_0NoteAttrTyp.None);
327 						break;
328 					case hashCalc("o"):		//D+
329 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 14), 
330 								MIDI2_0NoteAttrTyp.None);
331 						break;
332 					case hashCalc("num0"):	//D#+
333 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 15), 
334 								MIDI2_0NoteAttrTyp.None);
335 						break;
336 					case hashCalc("p"):		//E+
337 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 16), 
338 								MIDI2_0NoteAttrTyp.None);
339 						break;
340 					case hashCalc("["):		//F+
341 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 17), 
342 								MIDI2_0NoteAttrTyp.None);
343 						break;
344 					case hashCalc("equals")://F#+
345 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 18), 
346 								MIDI2_0NoteAttrTyp.None);
347 						break;
348 					case hashCalc("]"):		//G+
349 						midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOn, 0x0, cast(ubyte)(noteBase + 19), 
350 								MIDI2_0NoteAttrTyp.None);
351 						break;
352 					default:
353 						break;
354 				}
355 				if (midipacket.msgType == MessageType.MIDI2) {
356 					fmsynth.midiReceive([midipacket.base, uint.max, 0, 0],0);
357 				}
358 			}
359 		} else if (state & StateFlags.deviceInitialized) {
360 			UMP midipacket;
361 			switch (id) {
362 				case hashCalc("q"):		//C
363 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, noteBase, MIDI2_0NoteAttrTyp.None);
364 					break;
365 				case hashCalc("num2"):	//C#
366 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 1), MIDI2_0NoteAttrTyp.None);
367 					break;
368 				case hashCalc("w"):		//D
369 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 2), MIDI2_0NoteAttrTyp.None);
370 					break;
371 				case hashCalc("num3"):	//D#
372 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 3), MIDI2_0NoteAttrTyp.None);
373 					break;
374 				case hashCalc("e"):		//E
375 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 4), MIDI2_0NoteAttrTyp.None);
376 					break;
377 				case hashCalc("r"):		//F
378 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 5), MIDI2_0NoteAttrTyp.None);
379 					break;
380 				case hashCalc("num5"):	//F#
381 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 6), MIDI2_0NoteAttrTyp.None);
382 					break;
383 				case hashCalc("t"):		//G
384 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 7), MIDI2_0NoteAttrTyp.None);
385 					break;
386 				case hashCalc("num6"):	//G#
387 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 8), MIDI2_0NoteAttrTyp.None);
388 					break;
389 				case hashCalc("y"):		//A
390 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 9), MIDI2_0NoteAttrTyp.None);
391 					break;
392 				case hashCalc("num7"):	//A#
393 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 10), MIDI2_0NoteAttrTyp.None);
394 					break;
395 				case hashCalc("u"):		//B
396 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 11), MIDI2_0NoteAttrTyp.None);
397 					break;
398 				case hashCalc("i"):		//C+
399 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 12), MIDI2_0NoteAttrTyp.None);
400 					break;
401 				case hashCalc("num9"):	//C#+
402 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 13), MIDI2_0NoteAttrTyp.None);
403 					break;
404 				case hashCalc("o"):		//D+
405 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 14), MIDI2_0NoteAttrTyp.None);
406 					break;
407 				case hashCalc("num0"):	//D#+
408 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 15), MIDI2_0NoteAttrTyp.None);
409 					break;
410 				case hashCalc("p"):		//E+
411 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 16), MIDI2_0NoteAttrTyp.None);
412 					break;
413 				case hashCalc("["):		//F+
414 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 17), MIDI2_0NoteAttrTyp.None);
415 					break;
416 				case hashCalc("equals")://F#+
417 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 18), MIDI2_0NoteAttrTyp.None);
418 					break;
419 				case hashCalc("]"):		//G+
420 					midipacket = UMP(MessageType.MIDI2, 0x0, MIDI2_0Cmd.NoteOff, 0x0, cast(ubyte)(noteBase + 19), MIDI2_0NoteAttrTyp.None);
421 					break;
422 				default:
423 					break;
424 			}
425 			if (midipacket.msgType == MessageType.MIDI2) {
426 				fmsynth.midiReceive([midipacket.base, uint.max, 0, 0],0);
427 			}
428 		}
429 	}
430 	
431 	public void axisEvent(uint id, BindingCode code, uint timestamp, float value) {
432 		
433 	}
434 	
435 	public void onQuit() {
436 		state &= ~StateFlags.isRunning;
437 	}
438 	
439 	public void controllerAdded(uint id) {
440 		
441 	}
442 	
443 	public void controllerRemoved(uint id) {
444 		
445 	}
446 	
447 }