DAEPlay.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. #include "DAEPlay.h"
  2. #include "api__in_cdda.h"
  3. #include "../nu/AutoWide.h"
  4. #include <strsafe.h>
  5. int DAEPlay::getTrackInfo()
  6. {
  7. CDROM_TOC tableOfContents = {0};
  8. DWORD tocSize = sizeof(tableOfContents);
  9. if (!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC, NULL, 0, &tableOfContents, tocSize, &tocSize, 0))
  10. {
  11. return 1;
  12. }
  13. for (int i = tableOfContents.FirstTrack - 1 ; i < tableOfContents.LastTrack ; i += 1)
  14. {
  15. if (i == g_track)
  16. {
  17. track_length = MSFToBlocks(tableOfContents.TrackData[i+1].Address) - (start_address = MSFToBlocks(tableOfContents.TrackData[i].Address));
  18. return 0;
  19. }
  20. }
  21. return 1;
  22. }
  23. DAEPlay::CDTextArray* DAEPlay::getCDText()
  24. {
  25. // if we've already cached it, return asap
  26. if (cd_text != 0)
  27. {
  28. return cd_text;
  29. }
  30. CDROM_TOC tableOfContents = {0};
  31. DWORD tocSize = sizeof(tableOfContents), returned = 0;
  32. if (!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC, NULL, 0, &tableOfContents, tocSize, &tocSize, 0))
  33. {
  34. // issue accessing drive
  35. return (cd_text = (CDTextArray *)-1);
  36. }
  37. // MMC-3 Draft Revision 10g: Table 222 Q Sub-channel control field
  38. tableOfContents.TrackData[0].Control &= 5;
  39. if (!(tableOfContents.TrackData[0].Control == 0/* || tableOfContents.TrackData[0 - 1].Control == 1*/))
  40. {
  41. // invalid format
  42. return (cd_text = (CDTextArray *)-1);
  43. }
  44. CDROM_READ_TOC_EX tableOfContentsEx = {0};
  45. tableOfContentsEx.Format = CDROM_READ_TOC_EX_FORMAT_CDTEXT;
  46. WORD tocSizeEx = 0;
  47. // Read the contents to get the size of the actual data
  48. if (!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC_EX, &tableOfContentsEx, sizeof(tableOfContentsEx), &tocSizeEx, sizeof(tocSizeEx), &returned, 0))
  49. {
  50. // issue accessing drive
  51. return (cd_text = (CDTextArray *)-1);
  52. }
  53. // The bytes are swapped so we need to switch them around
  54. tocSizeEx = ((tocSizeEx>>8) | (tocSizeEx<<8)) + sizeof(tocSizeEx);
  55. // Allocate a buffer for reading the actual CD Text data block
  56. char *pCDTextData = new char[tocSizeEx];
  57. if (!pCDTextData)
  58. {
  59. return (cd_text = (CDTextArray *)-1);
  60. }
  61. memset(pCDTextData, 0, tocSizeEx);
  62. // Now read the data
  63. if(!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC_EX, &tableOfContentsEx, sizeof(tableOfContentsEx), pCDTextData, tocSizeEx, &returned, 0))
  64. {
  65. delete []pCDTextData;
  66. return (cd_text = (CDTextArray *)-1);
  67. }
  68. tocSizeEx = (WORD)(returned - sizeof(CDROM_TOC_CD_TEXT_DATA));
  69. if(!tocSizeEx)
  70. {
  71. delete []pCDTextData;
  72. return (cd_text = (CDTextArray *)-1);
  73. }
  74. // This is the stuff we really need. It's an array of packs with the data (mostly text)
  75. CDROM_TOC_CD_TEXT_DATA_BLOCK* pCDTextBlock = ((CDROM_TOC_CD_TEXT_DATA*)(BYTE*)pCDTextData)->Descriptors;
  76. // As we go through the packets we'll store the strings in this array. The strings are often in different packets and need to be concatenated together.
  77. cd_text = new CDTextArray[CD_TEXT_NUM_PACKS];
  78. UINT m_nGenreCode = -1;
  79. // Loop through all the packets extracting the data we need. Each packet starts with a packet type, the track number, language code,
  80. // character offset, and character size. The packets end with a 2 byte CRC. We don't need most of this stuff to display the info. We can get
  81. // the packets for any track and they'll have all the data for each track (it's duplicated).
  82. for( ;tocSizeEx >= sizeof(CDROM_TOC_CD_TEXT_DATA_BLOCK); tocSizeEx -= sizeof(CDROM_TOC_CD_TEXT_DATA_BLOCK), pCDTextBlock++)
  83. {
  84. if (pCDTextBlock->TrackNumber > tableOfContents.LastTrack) // Track is beyond what is on the disc
  85. continue;
  86. // Only looking for CD Text item packets
  87. if ((pCDTextBlock->PackType -= 0x80) >= 0x10)
  88. continue;
  89. // Genre is encoded with the code and the supplemental text in the same packet
  90. if (m_nGenreCode == -1 && pCDTextBlock->PackType == CD_TEXT_GENRE)
  91. {
  92. // TODO
  93. m_nGenreCode = pCDTextBlock->Text[0]*16 + pCDTextBlock->Text[1];
  94. /*CString Text = !pCDTextBlock->Unicode
  95. ? CString(CStringA((CHAR*)pCDTextBlock->Text+2, CD_TEXT_FIELD_LENGTH-2))
  96. : CString(CStringW((WCHAR*)pCDTextBlock->WText+2, CD_TEXT_FIELD_LENGTH-2));
  97. cd_text[pCDTextBlock->PackType][0] = Text;*/
  98. }
  99. else
  100. {
  101. // Parse the text. There could be more than one item in the text block separated by null bytes so we need to check the whole thing
  102. // The text is in a block of up to 12 characters. We'll just keep adding to our buffers until we go through all of the packets.
  103. int nLengthRemaining = CD_TEXT_FIELD_LENGTH;
  104. UINT nTrack = pCDTextBlock->TrackNumber;
  105. UINT nOffset = 0;
  106. // We're at the end of text when:
  107. // Used up 12 chars
  108. // Got to a null
  109. // On the last track
  110. while (nTrack <= tableOfContents.LastTrack && nLengthRemaining > 0 && nOffset < CD_TEXT_FIELD_LENGTH)
  111. {
  112. wchar_t *text = (wchar_t*)calloc(nLengthRemaining + 1, sizeof(wchar_t));
  113. if (!text) continue;
  114. lstrcpyn(text, (pCDTextBlock->Unicode ? pCDTextBlock->WText + nOffset : AutoWide((char *)pCDTextBlock->Text + nOffset)), nLengthRemaining + 1);
  115. if (!cd_text[pCDTextBlock->PackType][nTrack])
  116. cd_text[pCDTextBlock->PackType][nTrack] = (wchar_t*)calloc(lstrlen(text) + 1, sizeof(wchar_t));
  117. else // TODO error handling
  118. cd_text[pCDTextBlock->PackType][nTrack] = (wchar_t*)realloc(cd_text[pCDTextBlock->PackType][nTrack], (lstrlen(cd_text[pCDTextBlock->PackType][nTrack]) + lstrlen(text) + 1) * sizeof(wchar_t));
  119. if (text[0])
  120. lstrcat(cd_text[pCDTextBlock->PackType][nTrack], text);
  121. nOffset += lstrlen(text) + 1;
  122. nLengthRemaining = nLengthRemaining - lstrlen(text) - 1;
  123. ++nTrack;
  124. free(text);
  125. }
  126. }
  127. } while (0);
  128. delete []pCDTextData;
  129. return cd_text;
  130. }
  131. int DAEPlay::threadProc2()
  132. {
  133. while (1)
  134. {
  135. if (need_seek != -1)
  136. {
  137. current_sector = ((need_seek * CD_BLOCKS_PER_SECOND) / 1000) / DEF_SECTORS_PER_READ;
  138. bytes_in_sbuf = 0;
  139. line.outMod->Flush(need_seek);
  140. need_seek = -1;
  141. }
  142. if (fillBuffer(killswitch))
  143. {
  144. PostMessage(line.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  145. return 0;
  146. }
  147. if (!bytes_in_sbuf && !killswitch)
  148. {
  149. //wait for output to be finished
  150. line.outMod->Write(NULL, 0);
  151. while (!killswitch && line.outMod->IsPlaying()) Sleep(10);
  152. if (!killswitch)
  153. {
  154. CloseHandle(hDrive);
  155. hDrive = INVALID_HANDLE_VALUE;
  156. PostMessage(line.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  157. }
  158. return 0;
  159. }
  160. if (killswitch)
  161. {
  162. CloseHandle(hDrive);
  163. hDrive = INVALID_HANDLE_VALUE;
  164. return 0;
  165. }
  166. char sample_buffer[576*4*2] = {0};
  167. int bytes = sizeof(sample_buffer) / 2; // enough room for dsp bullcrap
  168. bytes = min((int)bytes_in_sbuf, (int)bytes);
  169. memcpy(sample_buffer, sbuf, bytes);
  170. if (bytes_in_sbuf > bytes) memcpy(sbuf, sbuf + bytes, bytes_in_sbuf - bytes);
  171. bytes_in_sbuf -= bytes;
  172. line.VSAAddPCMData(sample_buffer, g_nch, 16, line.outMod->GetWrittenTime());
  173. line.SAAddPCMData(sample_buffer, g_nch, 16, line.outMod->GetWrittenTime());
  174. if (line.dsp_isactive())
  175. bytes = line.dsp_dosamples((short *)sample_buffer, bytes / g_nch / 2, 16, g_nch, 44100) * (g_nch * 2);
  176. while (line.outMod->CanWrite() < bytes && !killswitch) Sleep(66);
  177. if (killswitch)
  178. {
  179. CloseHandle(hDrive);
  180. hDrive = INVALID_HANDLE_VALUE;
  181. return 0;
  182. }
  183. line.outMod->Write(sample_buffer, bytes);
  184. }
  185. CloseHandle(hDrive);
  186. hDrive = INVALID_HANDLE_VALUE;
  187. return 0;
  188. }
  189. void DAEPlay::stop()
  190. {
  191. if (hThread)
  192. {
  193. killswitch = 1;
  194. WaitForSingleObject(hThread, INFINITE);
  195. }
  196. line.outMod->Close();
  197. }
  198. int DAEPlay::open(wchar_t drive, int track) //called by winampGetExtendedRead
  199. {
  200. g_track = track-1;
  201. if (g_track < 0)
  202. return 1;
  203. g_drive = drive;
  204. wchar_t CDDevice[8]=L"\\\\.\\x:";
  205. CDDevice[4] = drive;
  206. if (hDrive != INVALID_HANDLE_VALUE)
  207. CloseHandle(hDrive);
  208. hDrive = CreateFile(CDDevice, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  209. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  210. if (hDrive == INVALID_HANDLE_VALUE)
  211. return 1;
  212. // TODO only store the request track!
  213. if (getTrackInfo())
  214. return 1;
  215. g_playlength = (track_length / CD_BLOCKS_PER_SECOND) * 1000;
  216. bytes_in_sbuf = 0;
  217. if (!sbuf)
  218. sbuf = (unsigned char *)malloc(2352 * buf_size);
  219. if (!sbuf)
  220. return 1;
  221. data = new BYTE[DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR];
  222. if (!data)
  223. return 1;
  224. return 0;
  225. }
  226. int DAEPlay::play(wchar_t drive, int track) //called by winamp2's normal(old) play() interface
  227. {
  228. int old_drive = g_drive;
  229. if (open(drive, track)) return 1;
  230. // do this here as it helps to prevent an audio glitch on first playback and volume is set low
  231. setvolume(a_v, a_p);
  232. int maxlat = line.outMod->Open(44100, g_nch, 16, -1, -1);
  233. if (maxlat < 0)
  234. {
  235. g_drive = 0;
  236. return 1;
  237. }
  238. // to prevent re-spinning as we're not going to get cd-text later
  239. // if it's not able to be obtained when first opening the device.
  240. if (old_drive != drive)
  241. {
  242. if ((int)cd_text > 0)
  243. delete []cd_text;
  244. cd_text = NULL;
  245. getCDText();
  246. }
  247. line.SetInfo(1411, 44, g_nch, 1);
  248. line.SAVSAInit(maxlat, 44100);
  249. line.VSASetInfo(g_nch, 44100);
  250. line.is_seekable = 1;
  251. bytes_in_sbuf = 0;
  252. current_sector = 0;
  253. killswitch = 0;
  254. DWORD thread_id = 0;
  255. hThread = CreateThread(NULL, NULL, &threadProc, (LPVOID)this, NULL, &thread_id);
  256. SetThreadPriority(hThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
  257. //open the device thru MCI (for getfileinfo to work properly)
  258. g_playtrack = track;
  259. return 0;
  260. }
  261. int DAEPlay::fillBuffer(int kill)
  262. {
  263. if (!kill && bytes_in_sbuf <= 0)
  264. {
  265. if (current_sector < (track_length / DEF_SECTORS_PER_READ))
  266. {
  267. // Contains an offset into the CD-ROM disc where data will be read. You can calculate this offset by multiplying the starting sector number for the request times 2048.
  268. RAW_READ_INFO _RawReadInfo = {0};
  269. _RawReadInfo.DiskOffset.QuadPart = ((current_sector * DEF_SECTORS_PER_READ) + start_address) * CDROM_COOKED_BYTES_PER_SECTOR;
  270. _RawReadInfo.TrackMode = CDDA;
  271. _RawReadInfo.SectorCount = DEF_SECTORS_PER_READ;
  272. DWORD data_length = DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR;
  273. if (!DeviceIoControl(hDrive, IOCTL_CDROM_RAW_READ, &_RawReadInfo, sizeof(_RawReadInfo),
  274. data, data_length, &data_length, 0))
  275. {
  276. CloseHandle(hDrive);
  277. hDrive = INVALID_HANDLE_VALUE;
  278. return 1;
  279. }
  280. // TODO make sure the buffer size is enough for our needs
  281. memcpy(sbuf, data, DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR);
  282. bytes_in_sbuf += DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR;
  283. }
  284. current_sector++;
  285. }
  286. return 0;
  287. }
  288. int DAEPlay::read(char *dest, int len, int *killswitch) //called by winampGetExtendedRead_getData
  289. {
  290. int l = 0;
  291. // TODO make sure this will handle a CD ejection...
  292. while (l < len && !*killswitch)
  293. {
  294. if (fillBuffer(*killswitch))
  295. {
  296. return -1;
  297. }
  298. if (!bytes_in_sbuf) break;
  299. int bytes = min(bytes_in_sbuf, len - l);
  300. memcpy(dest + l, sbuf, bytes);
  301. if (bytes_in_sbuf > bytes) memcpy(sbuf, sbuf + bytes, bytes_in_sbuf - bytes);
  302. bytes_in_sbuf -= bytes;
  303. l += bytes;
  304. }
  305. return l;
  306. }
  307. DAEPlay::DAEPlay()
  308. {
  309. sbuf = NULL;
  310. data = NULL;
  311. cd_text = NULL;
  312. bytes_in_sbuf = 0;
  313. buf_size = 64; //make it configitem
  314. g_track = -1;
  315. // fix these three as that's normal
  316. g_nch = 2;
  317. g_srate = 44100;
  318. g_bps = 16;
  319. killswitch = 0;
  320. hDrive = INVALID_HANDLE_VALUE;
  321. hThread = NULL;
  322. need_seek = -1;
  323. current_sector = 0;
  324. start_address = 0;
  325. track_length = 0;
  326. }
  327. DAEPlay::~DAEPlay()
  328. {
  329. if (hDrive != INVALID_HANDLE_VALUE)
  330. {
  331. CloseHandle(hDrive);
  332. hDrive = INVALID_HANDLE_VALUE;
  333. }
  334. if (sbuf)
  335. {
  336. free(sbuf);
  337. sbuf = NULL;
  338. }
  339. if (data)
  340. {
  341. delete []data;
  342. data = NULL;
  343. }
  344. if ((int)cd_text > 0)
  345. {
  346. delete []cd_text;
  347. }
  348. cd_text = NULL;
  349. }