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 }