PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Samples/Chap22/Drum/DrumTime.d

http://github.com/AndrejMitrovic/DWinProgramming
D | 189 lines | 139 code | 35 blank | 15 comment | 19 complexity | 230ebe7ef57b124fb52303daa310738c MD5 | raw file
  1. /+
  2. + Copyright (c) Charles Petzold, 1998.
  3. + Ported to the D Programming Language by Andrej Mitrovic, 2011.
  4. +/
  5. module DrumTime;
  6. import core.memory;
  7. import core.runtime;
  8. import core.thread;
  9. import std.algorithm : min, max;
  10. import std.conv;
  11. import std.math;
  12. import std.range;
  13. import std.string;
  14. import std.stdio;
  15. import std.utf;
  16. auto toUTF16z(S)(S s)
  17. {
  18. return toUTFz!(const(wchar)*)(s);
  19. }
  20. pragma(lib, "gdi32.lib");
  21. pragma(lib, "comdlg32.lib");
  22. pragma(lib, "winmm.lib");
  23. import core.sys.windows.windef;
  24. import core.sys.windows.winuser;
  25. import core.sys.windows.wingdi;
  26. import core.sys.windows.winbase;
  27. import core.sys.windows.commdlg;
  28. import core.sys.windows.mmsystem;
  29. enum NUM_PERC = 47;
  30. enum WM_USER_NOTIFY = (WM_USER + 1);
  31. enum WM_USER_FINISHED = (WM_USER + 2);
  32. enum WM_USER_ERROR = (WM_USER + 3);
  33. align(2) struct DRUM
  34. {
  35. short iMsecPerBeat;
  36. short iVelocity;
  37. short iNumBeats;
  38. DWORD[NUM_PERC] dwSeqPerc;
  39. DWORD[NUM_PERC] dwSeqPian;
  40. }
  41. alias DRUM* PDRUM;
  42. auto minmax(T1, T2, T3) (T1 a, T2 x, T3 b)
  43. {
  44. return min(max(x, a), b);
  45. }
  46. enum TIMER_RES = 5;
  47. __gshared BOOL bSequenceGoing, bEndSequence;
  48. __gshared DRUM drum;
  49. __gshared HMIDIOUT hMidiOut;
  50. __gshared HWND hwndNotify;
  51. __gshared int iIndex;
  52. __gshared UINT uTimerRes, uTimerID;
  53. DWORD MidiOutMessage(HMIDIOUT hMidi, int iStatus, int iChannel, int iData1, int iData2)
  54. {
  55. DWORD dwMessage;
  56. dwMessage = iStatus | iChannel | (iData1 << 8) | (iData2 << 16);
  57. return midiOutShortMsg(hMidi, dwMessage);
  58. }
  59. void DrumSetParams(PDRUM pdrum)
  60. {
  61. drum = *pdrum;
  62. }
  63. BOOL DrumBeginSequence(HWND hwnd)
  64. {
  65. TIMECAPS tc;
  66. hwndNotify = hwnd; // Save window handle for notification
  67. DrumEndSequence(TRUE); // Stop current sequence if running
  68. // Open the MIDI Mapper output port
  69. if (midiOutOpen(&hMidiOut, MIDIMAPPER, 0, 0, 0))
  70. return FALSE;
  71. // Send Program Change messages for channels 9 and 0
  72. MidiOutMessage(hMidiOut, 0xC0, 9, 0, 0);
  73. MidiOutMessage(hMidiOut, 0xC0, 0, 0, 0);
  74. // Begin sequence by setting a timer event
  75. timeGetDevCaps(&tc, TIMECAPS.sizeof);
  76. uTimerRes = minmax(tc.wPeriodMin, TIMER_RES, tc.wPeriodMax);
  77. timeBeginPeriod(uTimerRes);
  78. uTimerID = timeSetEvent(max(cast(UINT)uTimerRes, cast(UINT)drum.iMsecPerBeat),
  79. uTimerRes, &DrumTimerFunc, 0, TIME_ONESHOT);
  80. if (uTimerID == 0)
  81. {
  82. timeEndPeriod(uTimerRes);
  83. midiOutClose(hMidiOut);
  84. return FALSE;
  85. }
  86. iIndex = -1;
  87. bEndSequence = FALSE;
  88. bSequenceGoing = TRUE;
  89. return TRUE;
  90. }
  91. void DrumEndSequence(BOOL bRightAway)
  92. {
  93. if (bRightAway)
  94. {
  95. if (bSequenceGoing)
  96. {
  97. // stop the timer
  98. if (uTimerID)
  99. timeKillEvent(uTimerID);
  100. timeEndPeriod(uTimerRes);
  101. // turn off all notes
  102. MidiOutMessage(hMidiOut, 0xB0, 9, 123, 0);
  103. MidiOutMessage(hMidiOut, 0xB0, 0, 123, 0);
  104. // close the MIDI port
  105. midiOutClose(hMidiOut);
  106. bSequenceGoing = FALSE;
  107. }
  108. }
  109. else
  110. bEndSequence = TRUE;
  111. }
  112. extern (Windows)
  113. void DrumTimerFunc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
  114. {
  115. static DWORD[NUM_PERC] dwSeqPercLast, dwSeqPianLast;
  116. int i;
  117. // Note Off messages for channels 9 and 0
  118. if (iIndex != -1)
  119. {
  120. for (i = 0; i < NUM_PERC; i++)
  121. {
  122. if (dwSeqPercLast[i] & 1 << iIndex)
  123. MidiOutMessage(hMidiOut, 0x80, 9, i + 35, 0);
  124. if (dwSeqPianLast[i] & 1 << iIndex)
  125. MidiOutMessage(hMidiOut, 0x80, 0, i + 35, 0);
  126. }
  127. }
  128. // Increment index and notify window to advance bouncing ball
  129. iIndex = (iIndex + 1) % drum.iNumBeats;
  130. PostMessage(hwndNotify, WM_USER_NOTIFY, iIndex, timeGetTime());
  131. // Check if ending the sequence
  132. if (bEndSequence && iIndex == 0)
  133. {
  134. PostMessage(hwndNotify, WM_USER_FINISHED, 0, 0L);
  135. return;
  136. }
  137. // Note On messages for channels 9 and 0
  138. for (i = 0; i < NUM_PERC; i++)
  139. {
  140. if (drum.dwSeqPerc[i] & 1 << iIndex)
  141. MidiOutMessage(hMidiOut, 0x90, 9, i + 35, drum.iVelocity);
  142. if (drum.dwSeqPian[i] & 1 << iIndex)
  143. MidiOutMessage(hMidiOut, 0x90, 0, i + 35, drum.iVelocity);
  144. dwSeqPercLast[i] = drum.dwSeqPerc[i];
  145. dwSeqPianLast[i] = drum.dwSeqPian[i];
  146. }
  147. // Set a new timer event
  148. uTimerID = timeSetEvent(max(cast(int)uTimerRes, drum.iMsecPerBeat),
  149. uTimerRes, &DrumTimerFunc, 0, TIME_ONESHOT);
  150. if (uTimerID == 0)
  151. {
  152. PostMessage(hwndNotify, WM_USER_ERROR, 0, 0);
  153. }
  154. }