1
0

WavEncoder.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. #include "main.h"
  2. #include "../nsv/enc_if.h"
  3. #include <mmreg.h>
  4. #include <msacm.h>
  5. #include "WavEncoder.h"
  6. void initWfx();
  7. #define BUFSIZE 0x20000
  8. extern EXT_WFX convert_wfx;
  9. extern WAVEFORMATEX wfx_default;
  10. static DWORD FileTell(HANDLE hFile) { return SetFilePointer(hFile, 0, 0, FILE_CURRENT);}
  11. static void FileAlign(HANDLE hFile) {if (FileTell(hFile)&1) SetFilePointer(hFile, 1, 0, FILE_CURRENT);}
  12. #define rev32(X) ((((DWORD)(X)&0xFF)<<24)|(((DWORD)(X)&0xFF00)<<8)|(((DWORD)(X)&0xFF0000)>>8)|(((DWORD)(X)&0xFF000000)>>24))
  13. WavEncoder::WavEncoder(int srate, int nch, int bps, int res_srate , int res_bps, int res_nch )
  14. {
  15. m_did_header = 0;
  16. m_srate = srate;
  17. m_nch = nch;
  18. m_bps = bps;
  19. m_error = 0;
  20. hStream = 0;
  21. hStreamResample = 0;
  22. //_asm { int 3 };
  23. m_acm_resample_buf = NULL;
  24. m_acm_resample_outbuf = NULL;
  25. m_bytes_done = 0;
  26. m_hlen = 0;
  27. m_nsam = 0;
  28. if (res_srate && (res_srate != srate || res_bps != bps || res_nch != nch))
  29. {
  30. //manual resample (ie: burning)
  31. m_acm_buf = (unsigned char *)malloc(BUFSIZE);
  32. m_acm_outbuf = (unsigned char *)malloc(BUFSIZE);
  33. m_bytes_inbuf = 0;
  34. m_bytes_outbuf = 0;
  35. m_wfx_src.wFormatTag = WAVE_FORMAT_PCM;
  36. m_wfx_src.nChannels = nch;
  37. m_wfx_src.nSamplesPerSec = srate;
  38. m_wfx_src.nAvgBytesPerSec = srate * nch * (bps >> 3);
  39. m_wfx_src.nBlockAlign = nch * (bps >> 3);
  40. m_wfx_src.wBitsPerSample = bps;
  41. m_wfx_src.cbSize = 0;
  42. m_convert_wfx.wfx = wfx_default;
  43. m_convert_wfx.wfx.nSamplesPerSec = res_srate;
  44. m_convert_wfx.wfx.nChannels = res_nch;
  45. m_convert_wfx.wfx.wBitsPerSample = res_bps;
  46. m_convert_wfx.wfx.nAvgBytesPerSec = res_srate * res_nch * (res_bps / 8);
  47. m_convert_wfx.wfx.nBlockAlign = res_nch * (res_bps / 8);
  48. m_convert_wfx.wfx.cbSize = 0;
  49. MMRESULT rs = acmStreamOpen(&hStream, 0, &m_wfx_src, &m_convert_wfx.wfx, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME);
  50. if (rs || !hStream)
  51. {
  52. m_error = 1;
  53. return ;
  54. }
  55. ZeroMemory(&ahd, sizeof(ahd));
  56. ahd.cbStruct = sizeof(ahd);
  57. ahd.pbSrc = m_acm_buf;
  58. ahd.cbSrcLength = BUFSIZE;
  59. ahd.pbDst = m_acm_outbuf;
  60. ahd.cbDstLength = BUFSIZE;
  61. if (acmStreamPrepareHeader(hStream, &ahd, 0)) m_error = 1;
  62. return ;
  63. }
  64. //resample defined in config
  65. // fucko: don't use compression settings if we're in a sep process, just generate raw wav
  66. // sep process isn't the best way, but we'll give it a shot
  67. if (!config_wav_convert)
  68. {
  69. m_acm_buf = NULL;
  70. m_acm_outbuf = NULL;
  71. }
  72. else
  73. {
  74. m_acm_buf = (unsigned char *)malloc(BUFSIZE);
  75. m_acm_outbuf = (unsigned char *)malloc(BUFSIZE);
  76. m_bytes_inbuf = 0;
  77. m_bytes_outbuf = 0;
  78. initWfx();
  79. m_convert_wfx = convert_wfx;
  80. m_wfx_src.wFormatTag = WAVE_FORMAT_PCM;
  81. m_wfx_src.nChannels = nch;
  82. m_wfx_src.nSamplesPerSec = srate;
  83. m_wfx_src.nAvgBytesPerSec = srate * nch * (bps >> 3);
  84. m_wfx_src.nBlockAlign = nch * (bps >> 3);
  85. m_wfx_src.wBitsPerSample = bps;
  86. m_wfx_src.cbSize = 0;
  87. MMRESULT rs = acmStreamOpen(&hStream, 0, &m_wfx_src, &m_convert_wfx.wfx, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME);
  88. if (rs)
  89. {
  90. // need resampling
  91. WAVEFORMATEX wfx1;
  92. ZeroMemory(&wfx1, sizeof(wfx1));
  93. wfx1.wFormatTag = WAVE_FORMAT_PCM;
  94. if (acmFormatSuggest(0, &m_convert_wfx.wfx, &wfx1, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG)) m_error = 1;
  95. else if (acmStreamOpen(&hStream, 0, &wfx1, &m_convert_wfx.wfx, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME)) m_error = 1;
  96. else if (acmStreamOpen(&hStreamResample, 0, &m_wfx_src, &wfx1, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME)) m_error = 1;
  97. else
  98. {
  99. ZeroMemory(&ahdResample, sizeof(ahdResample));
  100. ahdResample.cbStruct = sizeof(ahdResample);
  101. ahdResample.pbSrc = m_acm_resample_buf = (unsigned char *)malloc(BUFSIZE);
  102. ahdResample.cbSrcLength = BUFSIZE;
  103. ahdResample.pbDst = m_acm_resample_outbuf = (unsigned char *)malloc(BUFSIZE);
  104. ahdResample.cbDstLength = BUFSIZE;
  105. if (acmStreamPrepareHeader(hStreamResample, &ahdResample, 0)) m_error = 1;
  106. m_bytes_inbuf_resample = 0;
  107. m_bytes_outbuf_resample = 0;
  108. }
  109. }
  110. if (!hStream)
  111. {
  112. m_error = 1;
  113. return ;
  114. }
  115. ZeroMemory(&ahd, sizeof(ahd));
  116. ahd.cbStruct = sizeof(ahd);
  117. ahd.pbSrc = m_acm_buf;
  118. ahd.cbSrcLength = BUFSIZE;
  119. ahd.pbDst = m_acm_outbuf;
  120. ahd.cbDstLength = BUFSIZE;
  121. if (acmStreamPrepareHeader(hStream, &ahd, 0)) m_error = 1;
  122. }
  123. }
  124. WavEncoder::~WavEncoder()
  125. {
  126. free(m_acm_buf);
  127. free(m_acm_outbuf);
  128. free(m_acm_resample_buf);
  129. free(m_acm_resample_outbuf);
  130. if (hStream)
  131. {
  132. if (ahd.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStream, &ahd, 0);
  133. acmStreamClose(hStream, 0);
  134. }
  135. if (hStreamResample)
  136. {
  137. if (ahdResample.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStreamResample, &ahdResample, 0);
  138. acmStreamClose(hStreamResample, 0);
  139. }
  140. }
  141. int WavEncoder::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail)
  142. {
  143. char *pin = (char *)in;
  144. char *pout = (char *)out;
  145. int retval = 0;
  146. if (!m_did_header && config_wav_do_header)
  147. {
  148. int s = 44;
  149. if (hStream)
  150. {
  151. s = 4 + 4 + 12 - 4;
  152. int t;
  153. if (m_convert_wfx.wfx.wFormatTag == WAVE_FORMAT_PCM) t = 0x10;
  154. else t = sizeof(WAVEFORMATEX) + m_convert_wfx.wfx.cbSize;
  155. s += 4 + t;
  156. if (s&1) s++;
  157. if (m_convert_wfx.wfx.wFormatTag != WAVE_FORMAT_PCM)
  158. s += 12;
  159. s += 8;
  160. }
  161. if (out_avail < s) return 0;
  162. //xx bytes of randomness
  163. m_hlen = s;
  164. m_did_header = 1;
  165. out_avail -= s;
  166. pout += s;
  167. retval = s;
  168. }
  169. if (!hStream)
  170. {
  171. //no ACM conversion
  172. int l = min(out_avail, in_avail);
  173. memcpy(pout, pin, l);
  174. *in_used = l;
  175. m_bytes_done += l;
  176. return l;
  177. }
  178. if (!m_bytes_outbuf)
  179. {
  180. if (hStreamResample)
  181. {
  182. if (!m_bytes_outbuf_resample)
  183. {
  184. DWORD flags = ACM_STREAMCONVERTF_BLOCKALIGN;
  185. int l = min(in_avail, BUFSIZE - m_bytes_inbuf_resample);
  186. if (l < 0) l = 0;
  187. if (l > 0) memcpy(m_acm_resample_buf + m_bytes_inbuf_resample, in, l);
  188. m_bytes_inbuf_resample += l;
  189. *in_used = l;
  190. m_nsam += l;
  191. ahdResample.cbSrcLength = m_bytes_inbuf_resample;
  192. acmStreamConvert(hStreamResample, &ahdResample, flags);
  193. m_bytes_inbuf_resample -= ahdResample.cbSrcLengthUsed;
  194. memcpy(m_acm_resample_buf, m_acm_resample_buf + ahdResample.cbSrcLengthUsed, m_bytes_inbuf_resample); //memmove
  195. m_bytes_outbuf_resample = ahdResample.cbDstLengthUsed;
  196. }
  197. in = (void*)m_acm_resample_outbuf;
  198. in_avail = m_bytes_outbuf_resample;
  199. m_bytes_outbuf_resample = 0;
  200. in_used = NULL;
  201. }
  202. DWORD flags = ACM_STREAMCONVERTF_BLOCKALIGN;
  203. int l = min(in_avail, BUFSIZE - m_bytes_inbuf);
  204. if (l < 0) l = 0;
  205. if (l > 0) memcpy(m_acm_buf + m_bytes_inbuf, in, l);
  206. m_bytes_inbuf += l;
  207. if (in_used)
  208. {
  209. *in_used = l;
  210. m_nsam += l;
  211. }
  212. if (m_bytes_inbuf)
  213. {
  214. ahd.cbSrcLength = m_bytes_inbuf;
  215. acmStreamConvert(hStream, &ahd, flags);
  216. m_bytes_inbuf -= ahd.cbSrcLengthUsed;
  217. memcpy(m_acm_buf, m_acm_buf + ahd.cbSrcLengthUsed, m_bytes_inbuf); //memmove
  218. m_bytes_outbuf = ahd.cbDstLengthUsed;
  219. m_bytes_done += l;
  220. }
  221. }
  222. if (m_bytes_outbuf)
  223. {
  224. int l = min(out_avail, m_bytes_outbuf);
  225. memcpy(pout, m_acm_outbuf, l);
  226. m_bytes_outbuf -= l;
  227. memcpy(m_acm_outbuf, m_acm_outbuf + l, m_bytes_outbuf);
  228. retval += l;
  229. }
  230. return retval;
  231. }
  232. void WavEncoder::FinishAudio(HANDLE fh, WavEncoder *coder)
  233. {
  234. if (!config_wav_do_header) return ;
  235. int len, i;
  236. const unsigned char ispred1[4] = {0x52 , 0x49 , 0x46 , 0x46 };
  237. const unsigned char ispred2[12] = {0x57, 0x41 , 0x56 , 0x45 , 0x66 , 0x6d , 0x74 , 0x20 , 0x10 , 0x0 , 0x0 , 0x0 };
  238. char c;
  239. int bps = coder->m_bps;
  240. int srate = coder->m_srate;
  241. int nch = coder->m_nch;
  242. len = coder->m_bytes_done;
  243. DWORD a;
  244. FileAlign(fh);
  245. SetFilePointer(fh, 0, 0, FILE_BEGIN);
  246. WriteFile(fh, ispred1, sizeof(ispred1), &a, NULL);
  247. i = len + (m_hlen) - 8;
  248. if (i&1) i++;
  249. WriteFile(fh, &i, 4, &a, NULL);
  250. WriteFile(fh, ispred2, sizeof(ispred2) - (hStream ? 4 : 0), &a, NULL);
  251. if (!hStream)
  252. {
  253. c = 1;
  254. WriteFile(fh, &c, 1, &a, NULL);
  255. c = 0;
  256. WriteFile(fh, &c, 1, &a, NULL);
  257. c = nch;
  258. WriteFile(fh, &c, 1, &a, NULL);
  259. c = 0;
  260. WriteFile(fh, &c, 1, &a, NULL);
  261. for (i = 0;i < 32;i += 8)
  262. {
  263. c = (srate >> i) & 0xff;
  264. WriteFile(fh, &c, 1, &a, NULL);
  265. }
  266. int tmp = srate * nch * (bps / 8);
  267. for (i = 0;i < 32;i += 8)
  268. {
  269. c = (tmp >> i) & 0xff;
  270. WriteFile(fh, &c, 1, &a, NULL);
  271. }
  272. tmp = (bps / 8) * nch;
  273. for (i = 0;i < 16;i += 8)
  274. {
  275. c = (tmp >> i) & 0xff;
  276. WriteFile(fh, &c, 1, &a, NULL);
  277. }
  278. c = bps;
  279. WriteFile(fh, &c, 1, &a, NULL);
  280. c = 0;
  281. WriteFile(fh, &c, 1, &a, NULL);
  282. const unsigned char iza[4] = {0x64 , 0x61 , 0x74 , 0x61};
  283. WriteFile(fh, iza, 4, &a, NULL);
  284. for (i = 0;i < 32;i += 8)
  285. {
  286. c = (len >> i) & 0xff;
  287. WriteFile(fh, &c, 1, &a, NULL);
  288. }
  289. }
  290. else
  291. {
  292. int t;
  293. if (m_convert_wfx.wfx.wFormatTag == WAVE_FORMAT_PCM) t = 0x10;
  294. else t = sizeof(WAVEFORMATEX) + m_convert_wfx.wfx.cbSize;
  295. WriteFile(fh, &t, 4, &a, 0);
  296. WriteFile(fh, &m_convert_wfx.wfx, t, &a, 0);
  297. FileAlign(fh);
  298. DWORD fact_ofs = 0;
  299. if (m_convert_wfx.wfx.wFormatTag != WAVE_FORMAT_PCM)
  300. {
  301. t = rev32('fact');
  302. WriteFile(fh, &t, 4, &a, 0);
  303. t = 4;
  304. WriteFile(fh, &t, 4, &a, 0);
  305. fact_ofs = FileTell(fh);
  306. SetFilePointer(fh, 4, 0, FILE_CURRENT);
  307. }
  308. t = rev32('data');
  309. WriteFile(fh, &t, 4, &a, 0);
  310. DWORD data_ofs = FileTell(fh);
  311. {
  312. DWORD t, bw;
  313. SetFilePointer(fh, 4, 0, FILE_BEGIN);
  314. t = GetFileSize(fh, 0) - 8;
  315. WriteFile(fh, &t, 4, &bw, 0);
  316. DWORD data_size = GetFileSize(fh, 0) - (data_ofs + 4);
  317. SetFilePointer(fh, data_ofs, 0, FILE_BEGIN);
  318. WriteFile(fh, &data_size, 4, &bw, 0);
  319. if (fact_ofs)
  320. {
  321. SetFilePointer(fh, fact_ofs, 0, FILE_BEGIN);
  322. t = coder->m_nsam / ((coder->m_bps >> 3) * coder->m_nch);
  323. WriteFile(fh, &t, 4, &bw, 0);
  324. }
  325. }
  326. }
  327. }
  328. int WavEncoder::GetLastError() { return m_error; }