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 }