PageRenderTime 55ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/Samples/Chap22/AddSynth/AddSynth.d

http://github.com/AndrejMitrovic/DWinProgramming
D | 306 lines | 234 code | 62 blank | 10 comment | 21 complexity | 916293666c499a3331221da235e9742c MD5 | raw file
  1. /+
  2. + Copyright (c) Charles Petzold, 1998.
  3. + Ported to the D Programming Language by Andrej Mitrovic, 2011.
  4. +/
  5. module AddSynth;
  6. import core.memory;
  7. import core.runtime;
  8. import core.thread;
  9. import std.array : replicate;
  10. import std.conv;
  11. import std.math;
  12. import std.range;
  13. import std.string;
  14. import std.exception;
  15. import std.stdio;
  16. import std.utf;
  17. auto toUTF16z(S)(S s)
  18. {
  19. return toUTFz!(const(wchar)*)(s);
  20. }
  21. pragma(lib, "gdi32.lib");
  22. pragma(lib, "comdlg32.lib");
  23. pragma(lib, "winmm.lib");
  24. import core.sys.windows.windef;
  25. import core.sys.windows.winuser;
  26. import core.sys.windows.wingdi;
  27. import core.sys.windows.winbase;
  28. import core.sys.windows.commdlg;
  29. import core.sys.windows.mmsystem;
  30. import resource;
  31. import WaveTable;
  32. string appName = "AddSynth";
  33. string description = "AddSynth";
  34. HINSTANCE hinst;
  35. extern (Windows)
  36. int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  37. {
  38. int result;
  39. try
  40. {
  41. Runtime.initialize();
  42. result = myWinMain(hInstance, hPrevInstance, lpCmdLine, iCmdShow);
  43. Runtime.terminate();
  44. }
  45. catch (Throwable o)
  46. {
  47. MessageBox(null, o.toString().toUTF16z, "Error", MB_OK | MB_ICONEXCLAMATION);
  48. result = 0;
  49. }
  50. return result;
  51. }
  52. int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  53. {
  54. if (DialogBox(hInstance, appName.toUTF16z, NULL, &DlgProc) == -1)
  55. {
  56. MessageBox(NULL, "This program requires Windows NT!", appName.toUTF16z, MB_ICONERROR);
  57. }
  58. return 0;
  59. }
  60. enum ID_TIMER = 1;
  61. enum SAMPLE_RATE = 22050;
  62. enum MAX_PARTIALS = 21;
  63. double SineGenerator(double dFreq, ref double pdAngle)
  64. {
  65. double dAmp;
  66. dAmp = sin(pdAngle);
  67. pdAngle += 2 * PI * dFreq / SAMPLE_RATE;
  68. if (pdAngle >= 2 * PI)
  69. pdAngle -= 2 * PI;
  70. return dAmp;
  71. }
  72. // Fill a buffer with a composite waveform
  73. VOID FillBuffer(INS ins, ref PBYTE pBuffer, int iNumSamples)
  74. {
  75. static double[MAX_PARTIALS] dAngle;
  76. dAngle[] = 0.0;
  77. double dAmp, dFrq, dComp, dFrac;
  78. int i, iPrt, iMsecTime, iCompMaxAmp, iMaxAmp, iSmp;
  79. // Calculate the composite maximum amplitude
  80. iCompMaxAmp = 0;
  81. for (iPrt = 0; iPrt < ins.pprt.length; iPrt++)
  82. {
  83. iMaxAmp = 0;
  84. for (i = 0; i < ins.pprt[iPrt].ampArray.length; i++)
  85. iMaxAmp = max(iMaxAmp, ins.pprt[iPrt].ampArray[i].iValue);
  86. iCompMaxAmp += iMaxAmp;
  87. }
  88. // Loop through each sample
  89. for (iSmp = 0; iSmp < iNumSamples; iSmp++)
  90. {
  91. dComp = 0;
  92. iMsecTime = cast(int)(1000 * iSmp / SAMPLE_RATE);
  93. // Loop through each partial
  94. for (iPrt = 0; iPrt < ins.pprt.length; iPrt++)
  95. {
  96. dAmp = 0;
  97. dFrq = 0;
  98. for (i = 0; i < ins.pprt[iPrt].ampArray.length - 1; i++)
  99. {
  100. if (iMsecTime >= ins.pprt[iPrt].ampArray[i].iTime &&
  101. iMsecTime <= ins.pprt[iPrt].ampArray[i + 1].iTime)
  102. {
  103. dFrac = cast(double)(iMsecTime -
  104. ins.pprt[iPrt].ampArray[i].iTime) /
  105. (ins.pprt[iPrt].ampArray[i + 1].iTime -
  106. ins.pprt[iPrt].ampArray[i].iTime);
  107. dAmp = dFrac * ins.pprt[iPrt].ampArray[i + 1].iValue +
  108. (1 - dFrac) * ins.pprt[iPrt].ampArray[i].iValue;
  109. break;
  110. }
  111. }
  112. for (i = 0; i < ins.pprt[iPrt].freqArray.length - 1; i++)
  113. {
  114. if (iMsecTime >= ins.pprt[iPrt].freqArray[i ].iTime &&
  115. iMsecTime <= ins.pprt[iPrt].freqArray[i + 1].iTime)
  116. {
  117. dFrac = cast(double)(iMsecTime - ins.pprt[iPrt].freqArray[i ].iTime) /
  118. (ins.pprt[iPrt].freqArray[i + 1].iTime -
  119. ins.pprt[iPrt].freqArray[i ].iTime);
  120. dFrq = dFrac * ins.pprt[iPrt].freqArray[i + 1].iValue +
  121. (1 - dFrac) * ins.pprt[iPrt].freqArray[i ].iValue;
  122. break;
  123. }
  124. }
  125. dComp += dAmp * SineGenerator(dFrq, dAngle[iPrt]);
  126. }
  127. pBuffer[iSmp] = cast(BYTE)(127 + 127 * dComp / iCompMaxAmp);
  128. }
  129. }
  130. BOOL MakeWaveFile(INS ins, string szFileName)
  131. {
  132. DWORD dwWritten;
  133. HANDLE hFile;
  134. int iChunkSize, iPcmSize, iNumSamples;
  135. PBYTE pBuffer;
  136. WAVEFORMATEX waveform;
  137. hFile = CreateFile(szFileName.toUTF16z, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  138. enforce(hFile != NULL);
  139. iNumSamples = (cast(int)ins.iMsecTime * SAMPLE_RATE / 1000 + 1) / 2 * 2;
  140. iPcmSize = PCMWAVEFORMAT.sizeof;
  141. iChunkSize = 12 + iPcmSize + 8 + iNumSamples;
  142. pBuffer = cast(typeof(pBuffer))GC.malloc(iNumSamples);
  143. if (pBuffer is null)
  144. {
  145. CloseHandle(hFile);
  146. throw new Exception("Can't allocate buffer.");
  147. }
  148. FillBuffer(ins, pBuffer, iNumSamples);
  149. waveform.wFormatTag = WAVE_FORMAT_PCM;
  150. waveform.nChannels = 1;
  151. waveform.nSamplesPerSec = SAMPLE_RATE;
  152. waveform.nAvgBytesPerSec = SAMPLE_RATE;
  153. waveform.nBlockAlign = 1;
  154. waveform.wBitsPerSample = 8;
  155. waveform.cbSize = 0;
  156. // Note: WriteFile doesn't know anything about UTF8+, have to use ASCII
  157. // to write the file section names.
  158. string RIFF = "RIFF";
  159. string WAVEfmt = "WAVEfmt ";
  160. string DATA = "data";
  161. WriteFile(hFile, cast(void*)(RIFF.toStringz), 4, &dwWritten, NULL);
  162. WriteFile(hFile, &iChunkSize, 4, &dwWritten, NULL);
  163. WriteFile(hFile, cast(void*)(WAVEfmt.toStringz), 8, &dwWritten, NULL);
  164. WriteFile(hFile, &iPcmSize, 4, &dwWritten, NULL);
  165. WriteFile(hFile, &waveform, WAVEFORMATEX.sizeof - 2, &dwWritten, NULL);
  166. WriteFile(hFile, cast(void*)(DATA.toStringz), 4, &dwWritten, NULL);
  167. WriteFile(hFile, &iNumSamples, 4, &dwWritten, NULL);
  168. WriteFile(hFile, pBuffer, iNumSamples, &dwWritten, NULL);
  169. CloseHandle(hFile);
  170. GC.free(pBuffer);
  171. if (cast(int)dwWritten != iNumSamples)
  172. {
  173. DeleteFile(szFileName.toUTF16z);
  174. return FALSE;
  175. }
  176. return TRUE;
  177. }
  178. void TestAndCreateFile(HWND hwnd, INS ins, string szFileName, int idButton)
  179. {
  180. if (GetFileAttributes(szFileName.toUTF16z) != -1)
  181. EnableWindow(GetDlgItem(hwnd, idButton), TRUE);
  182. else
  183. {
  184. if (MakeWaveFile(ins, szFileName))
  185. EnableWindow(GetDlgItem(hwnd, idButton), TRUE);
  186. else
  187. {
  188. string message = format("Could not create %s.", szFileName);
  189. MessageBeep(MB_ICONEXCLAMATION);
  190. MessageBox(hwnd, message.toUTF16z, appName.toUTF16z, MB_OK | MB_ICONEXCLAMATION);
  191. }
  192. }
  193. }
  194. extern (Windows)
  195. BOOL DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  196. {
  197. string szTrum = "Trumpet.wav";
  198. string szOboe = "Oboe.wav";
  199. string szClar = "Clarinet.wav";
  200. switch (message)
  201. {
  202. case WM_INITDIALOG:
  203. SetTimer(hwnd, ID_TIMER, 1, NULL);
  204. return TRUE;
  205. case WM_TIMER:
  206. KillTimer(hwnd, ID_TIMER);
  207. SetCursor(LoadCursor(NULL, IDC_WAIT));
  208. ShowCursor(TRUE);
  209. TestAndCreateFile(hwnd, insTrum, szTrum, IDC_TRUMPET);
  210. TestAndCreateFile(hwnd, insOboe, szOboe, IDC_OBOE);
  211. TestAndCreateFile(hwnd, insClar, szClar, IDC_CLARINET);
  212. SetDlgItemText(hwnd, IDC_TEXT, " ");
  213. SetFocus(GetDlgItem(hwnd, IDC_TRUMPET));
  214. ShowCursor(FALSE);
  215. SetCursor(LoadCursor(NULL, IDC_ARROW));
  216. return TRUE;
  217. case WM_COMMAND:
  218. switch (LOWORD(wParam))
  219. {
  220. case IDC_TRUMPET:
  221. PlaySound(szTrum.toUTF16z, NULL, SND_FILENAME | SND_SYNC);
  222. return TRUE;
  223. case IDC_OBOE:
  224. PlaySound(szOboe.toUTF16z, NULL, SND_FILENAME | SND_SYNC);
  225. return TRUE;
  226. case IDC_CLARINET:
  227. PlaySound(szClar.toUTF16z, NULL, SND_FILENAME | SND_SYNC);
  228. return TRUE;
  229. default:
  230. }
  231. break;
  232. case WM_SYSCOMMAND:
  233. switch (LOWORD(wParam))
  234. {
  235. case SC_CLOSE:
  236. EndDialog(hwnd, 0);
  237. return TRUE;
  238. default:
  239. }
  240. break;
  241. default:
  242. }
  243. return FALSE;
  244. }