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 import std.typecons : BitFlags;
9 
10 import bindbc.sdl;
11 
12 import midi2.types.structs;
13 import midi2.types.enums;
14 
15 import pixelperfectengine.concrete.window;
16 import pixelperfectengine.concrete.windowhandler;
17 import pixelperfectengine.concrete.eventchainsystem;
18 
19 import pixelperfectengine.graphics.outputscreen;
20 import pixelperfectengine.graphics.raster;
21 import pixelperfectengine.graphics.layers;
22 
23 import pixelperfectengine.graphics.bitmap;
24 
25 import pixelperfectengine.system.input;
26 import pixelperfectengine.system.file;
27 import pixelperfectengine.system.etc;
28 import pixelperfectengine.system.systemutility;
29 import pixelperfectengine.system.config;
30 
31 import pixelperfectengine.system.common;
32 
33 import pixelperfectengine.audio.base.handler;
34 import pixelperfectengine.audio.base.modulebase;
35 import pixelperfectengine.audio.base.config;
36 //import pixelperfectengine.audio.modules.qm816;
37 import core.thread;
38 import iota.audio.midi;
39 import iota.audio.midiin;
40 
41 import test1.audioconfig;
42 import test1.preseteditor;
43 import test1.modulerouter;
44 
45 /** 
46  * Audio subsystem test.
47  */
48 int main(string[] args) {
49 	initialzeSDL();
50 	AudioDevKit app = new AudioDevKit(args);
51 	app.whereTheMagicHappens();
52 	return 0;
53 }
54 ///Top level window, so far only containing the MenuBar.
55 public class TopLevelWindow : Window {
56 	MenuBar mb;
57 	AudioDevKit app;
58 	public this(int width, int height, AudioDevKit app) {
59 		super(Box(0, 0, width, height), ""d, [], null);
60 		this.app = app;
61 		PopUpMenuElement[] menuElements;
62 
63 		menuElements ~= new PopUpMenuElement("file", "File", "", [
64 			new PopUpMenuElement("new", "New project"),
65 			new PopUpMenuElement("load", "Load project"),
66 			new PopUpMenuElement("save", "Save project"),
67 			new PopUpMenuElement("saveAs", "Save project as"),
68 			new PopUpMenuElement("exit", "Exit application", "Alt + F4")
69 		]);
70 
71 		menuElements ~= new PopUpMenuElement("edit", "Edit", "", [
72 			new PopUpMenuElement("undo", "Undo"),
73 			new PopUpMenuElement("redo", "Redo"),
74 			new PopUpMenuElement("copy", "Copy"),
75 			new PopUpMenuElement("cut", "Cut"),
76 			new PopUpMenuElement("paste", "Paste")
77 		]);
78 
79 		menuElements ~= new PopUpMenuElement("view", "View", "", [
80 			new PopUpMenuElement("router", "Routing layout editor"),
81 			new PopUpMenuElement("sequencer", "Sequencer")
82 		]);
83 
84 		menuElements ~= new PopUpMenuElement("audio", "Audio", "", [
85 			new PopUpMenuElement("stAudio", "Start/Stop Audio thread"),
86 			new PopUpMenuElement("cfgcompile", "Compile current configuration"),
87 		]);
88 
89 		menuElements ~= new PopUpMenuElement("help", "Help", "", [
90 			new PopUpMenuElement("helpFile", "Content"),
91 			new PopUpMenuElement("about", "About")
92 		]);
93 
94 		mb = new MenuBar("mb", Box(0, 0, width-1, 15), menuElements);
95 		addElement(mb);
96 		mb.onMenuEvent = &app.onMenuEvent;
97 	}
98 	public override void draw(bool drawHeaderOnly = false) {
99 		if(output.output.width != position.width || output.output.height != position.height)
100 			output = new BitmapDrawer(position.width(), position.height());
101 		
102 		StyleSheet ss = getStyleSheet();
103 		const Box bodyarea = Box(0, 0, position.width - 1, position.height - 1);
104 		drawFilledBox(bodyarea, ss.getColor("window"));
105 
106 		foreach (WindowElement we; elements) {
107 			we.draw();
108 		}
109 		
110 	}
111 }
112 /** 
113  * Testcase for the audio system.
114  * Capable of playing back external files.
115  */
116 public class AudioDevKit : InputListener, SystemEventListener {
117 	AudioDeviceHandler adh;
118 	ModuleManager	mm;
119 	AudioModule		selectedModule;
120 	OutputScreen	output;
121 	InputHandler	ih;
122 	Raster			mainRaster;
123 	AudioSpecs		aS;
124 	SpriteLayer		windowing;
125 	MIDIInput		midiIn;
126 	WindowHandler	wh;
127 	Window			tlw;
128 	PresetEditor	preEdit;
129 	ModuleRouter	router;
130 	ModuleConfig	mcfg;
131 	BitFlags!StateFlags	state;
132 	UndoableStack	eventStack;
133 	string			selectedModID;
134 	string			path;
135 	
136 	//ubyte[32][6][2]	level;
137 	enum StateFlags {
138 		isRunning		=	1<<0,
139 		audioThreadRunning=	1<<1,
140 		configurationCompiled=1<<2,
141 	}
142 	
143 	public this(string[] args) {
144 		state.isRunning = true;
145 		//Image fontSource = loadImage(File("../system/cp437_8x16.png"));
146 		output = new OutputScreen("PixelPerfectEngine Audio Development Kit", 848 * 2, 480 * 2);
147 		mainRaster = new Raster(848,480,output,0);
148 		windowing = new SpriteLayer(RenderingMode.Copy);
149 		//windowing.addSprite(new Bitmap8Bit(848, 480), -65_536, 0, 0);
150 		wh = new WindowHandler(1696,960,848,480,windowing);
151 		mainRaster.loadPalette(loadPaletteFromFile("../system/concreteGUIE1.tga"));
152 		mainRaster.addLayer(windowing, 0);
153 		INIT_CONCRETE();
154 		{
155 			Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUI_ADK.tga", 16, 16);
156 			globalDefaultStyle.setImage(customGUIElems[6], "newA");
157 			globalDefaultStyle.setImage(customGUIElems[7], "newB");
158 			globalDefaultStyle.setImage(customGUIElems[8], "saveA");
159 			globalDefaultStyle.setImage(customGUIElems[9], "saveB");
160 			globalDefaultStyle.setImage(customGUIElems[10], "loadA");
161 			globalDefaultStyle.setImage(customGUIElems[11], "loadB");
162 			globalDefaultStyle.setImage(customGUIElems[12], "settingsA");
163 			globalDefaultStyle.setImage(customGUIElems[13], "settingsB");
164 			globalDefaultStyle.setImage(customGUIElems[14], "globalsA");
165 			globalDefaultStyle.setImage(customGUIElems[15], "globalsB");
166 			globalDefaultStyle.setImage(customGUIElems[16], "addA");
167 			globalDefaultStyle.setImage(customGUIElems[17], "addB");
168 			globalDefaultStyle.setImage(customGUIElems[18], "removeA");
169 			globalDefaultStyle.setImage(customGUIElems[19], "removeB");
170 			globalDefaultStyle.setImage(customGUIElems[20], "soloA");
171 			globalDefaultStyle.setImage(customGUIElems[21], "soloB");
172 			globalDefaultStyle.setImage(customGUIElems[22], "muteA");
173 			globalDefaultStyle.setImage(customGUIElems[23], "muteB");
174 			globalDefaultStyle.setImage(customGUIElems[24], "importA");
175 			globalDefaultStyle.setImage(customGUIElems[25], "importB");
176 			globalDefaultStyle.setImage(customGUIElems[26], "exportA");
177 			globalDefaultStyle.setImage(customGUIElems[27], "exportB");
178 			globalDefaultStyle.setImage(customGUIElems[28], "macroA");
179 			globalDefaultStyle.setImage(customGUIElems[29], "macroB");
180 		}
181 
182 		ih = new InputHandler();
183 		ih.systemEventListener = this;
184 		ih.inputListener = this;
185 		ih.mouseListener = wh;
186 		WindowElement.inputHandler = ih;
187 
188 		AudioDeviceHandler.initAudioDriver(OS_PREFERRED_DRIVER);
189 	
190 		initMIDI();
191 		Bitmap4Bit background = new Bitmap4Bit(848, 480);
192 		wh.addBackground(background);
193 		wh.addWindow(new AudioConfig(this));
194 		eventStack = new UndoableStack(10);
195 	}
196 	void whereTheMagicHappens() {
197 		while (state.isRunning) {
198 			mainRaster.refresh();
199 			ih.test();
200 			
201 		}
202 		if (mm !is null) {
203 			synchronized
204 				writeln(mm.suspendAudioThread());
205 		}
206 	}
207 	public void onStart() {
208 		tlw = new TopLevelWindow(848, 480, this);
209 		wh.setBaseWindow(tlw);
210 		mcfg = new ModuleConfig(mm);
211 	}
212 	public void onMenuEvent(Event ev) {
213 		MenuEvent me = cast(MenuEvent)ev;
214 		switch (me.itemSource) {
215 			case "preEdit":
216 				openPresetEditor();
217 				break;
218 			case "router":
219 				openRouter();
220 				break;
221 			case "stAudio":
222 				onAudioThreadSwitch();
223 				break;
224 			case "cfgcompile":
225 				onCompileAudioConfig();
226 				break;
227 			case "exit":
228 				state.isRunning = false;
229 				break;
230 			case "new":
231 				onNew();
232 				break;
233 			case "load":
234 				onLoad();
235 				break;
236 			case "save":
237 				onSave();
238 				break;
239 			case "saveAs":
240 				onSaveAs();
241 				break;
242 			default: break;
243 		}
244 	}
245 	public void onAudioThreadSwitch() {
246 		if (state.audioThreadRunning) {
247 			const int errorCode = mm.suspendAudioThread();
248 			state.audioThreadRunning = false;
249 			if (errorCode) {
250 				wh.message("Audio thread error!", "An error occured during audio thread runtime!\nError code:" ~ 
251 						errorCode.to!dstring);
252 			}
253 		} else {
254 			const int errorCode = mm.runAudioThread();
255 			if (!errorCode) {
256 				state.audioThreadRunning = true;
257 			} else {
258 				wh.message("Audio thread error!", "Failed to initialize audio thread!\nError code:" ~ errorCode.to!dstring);
259 			}
260 		}
261 	}
262 	public void onCompileAudioConfig() {
263 		try {
264 			mcfg.compile(state.audioThreadRunning);
265 		} catch (Exception e) {
266 			writeln(e);
267 		}
268 	}
269 	public void onNew() {
270 		mcfg = new ModuleConfig(mm);
271 	}
272 	public void onLoad() {
273 		import pixelperfectengine.concrete.dialogs.filedialog;
274 		wh.addWindow(new FileDialog("Load audio configuration file.", "loadConfigDialog", &onLoadConfigurationFile, 
275 			[FileDialog.FileAssociationDescriptor("SDLang file", ["*.sdl"])], "./"));
276 	}
277 	public void onLoadConfigurationFile(Event ev) {
278 		FileEvent fe = cast(FileEvent)ev;
279 		path = fe.getFullPath;
280 		File f = File(path);
281 		char[] c;
282 		c.length = cast(size_t)f.size();
283 		f.rawRead(c);
284 		mcfg.loadConfig(c.idup);
285 		if (router !is null) {
286 			router.refreshRoutingTable();
287 			router.refreshModuleList();
288 		}
289 	}
290 	public void onSave() {
291 		if (!path.length) {
292 			onSaveAs();
293 		} else {
294 			try {
295 				mcfg.save(path);
296 			} catch(Exception e) {
297 				debug writeln(e);
298 			}
299 		}
300 	}
301 	public void onSaveAs() {
302 		import pixelperfectengine.concrete.dialogs.filedialog;
303 		wh.addWindow(new FileDialog("Save audio configuration file.", "saveConfigDialog", &onSaveConfigurationFile, 
304 			[FileDialog.FileAssociationDescriptor("SDLang file", ["*.sdl"])], "./", true));
305 	}
306 	public void onSaveConfigurationFile(Event ev) {
307 		FileEvent fe = cast(FileEvent)ev;
308 		path = fe.getFullPath;
309 		try {
310 			mcfg.save(path);
311 		} catch(Exception e) {
312 			debug writeln(e);
313 		}
314 	}
315 	public void openRouter() {
316 		if (router is null)
317 			router = new ModuleRouter(this);
318 		if (wh.whichWindow(router) == -1)
319 			wh.addWindow(router);
320 		
321 	}
322 	public void openPresetEditor() {
323 		if (selectedModule !is null)
324 			wh.addWindow(new PresetEditor(this));
325 	}
326 	public void keyEvent(uint id, BindingCode code, uint timestamp, bool isPressed) {
327 		
328 	}
329 	public void midiInCallback(ubyte[] data, size_t timestamp) @nogc nothrow {
330 		if (selectedModule !is null) {
331 			UMP msb = UMP(MessageType.MIDI1, 0, 0, 0);
332 			msb.bytes[1] = data.length > 0 ? data[0] : 0;
333 			msb.bytes[2] = data.length > 1 ? data[1] : 0;
334 			msb.bytes[3] = data.length > 2 ? data[2] : 0;
335 			selectedModule.midiReceive(msb);
336 		}
337 	}
338 	public void axisEvent(uint id, BindingCode code, uint timestamp, float value) {
339 		
340 	}
341 	
342 	public void onQuit() {
343 		state.isRunning = false;
344 	}
345 	
346 	public void controllerAdded(uint id) {
347 		
348 	}
349 	
350 	public void controllerRemoved(uint id) {
351 		
352 	}
353 	
354 }