Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_win32_AudioCDReader.cpp 37KB

9 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace CDReaderHelpers
  18. {
  19. #define FILE_ANY_ACCESS 0
  20. #ifndef FILE_READ_ACCESS
  21. #define FILE_READ_ACCESS 1
  22. #endif
  23. #ifndef FILE_WRITE_ACCESS
  24. #define FILE_WRITE_ACCESS 2
  25. #endif
  26. #define METHOD_BUFFERED 0
  27. #define IOCTL_SCSI_BASE 4
  28. #define SCSI_IOCTL_DATA_OUT 0
  29. #define SCSI_IOCTL_DATA_IN 1
  30. #define SCSI_IOCTL_DATA_UNSPECIFIED 2
  31. #define CTL_CODE2(DevType, Function, Method, Access) (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
  32. #define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE2( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS )
  33. #define IOCTL_SCSI_GET_ADDRESS CTL_CODE2( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS )
  34. #define SENSE_LEN 14
  35. #define SRB_ENABLE_RESIDUAL_COUNT 0x04
  36. #define SRB_DIR_IN 0x08
  37. #define SRB_DIR_OUT 0x10
  38. #define SRB_EVENT_NOTIFY 0x40
  39. #define SC_HA_INQUIRY 0x00
  40. #define SC_GET_DEV_TYPE 0x01
  41. #define SC_EXEC_SCSI_CMD 0x02
  42. #define SS_PENDING 0x00
  43. #define SS_COMP 0x01
  44. #define SS_ERR 0x04
  45. enum
  46. {
  47. READTYPE_ANY = 0,
  48. READTYPE_ATAPI1 = 1,
  49. READTYPE_ATAPI2 = 2,
  50. READTYPE_READ6 = 3,
  51. READTYPE_READ10 = 4,
  52. READTYPE_READ_D8 = 5,
  53. READTYPE_READ_D4 = 6,
  54. READTYPE_READ_D4_1 = 7,
  55. READTYPE_READ10_2 = 8
  56. };
  57. struct SCSI_PASS_THROUGH
  58. {
  59. USHORT Length;
  60. UCHAR ScsiStatus;
  61. UCHAR PathId;
  62. UCHAR TargetId;
  63. UCHAR Lun;
  64. UCHAR CdbLength;
  65. UCHAR SenseInfoLength;
  66. UCHAR DataIn;
  67. ULONG DataTransferLength;
  68. ULONG TimeOutValue;
  69. ULONG DataBufferOffset;
  70. ULONG SenseInfoOffset;
  71. UCHAR Cdb[16];
  72. };
  73. struct SCSI_PASS_THROUGH_DIRECT
  74. {
  75. USHORT Length;
  76. UCHAR ScsiStatus;
  77. UCHAR PathId;
  78. UCHAR TargetId;
  79. UCHAR Lun;
  80. UCHAR CdbLength;
  81. UCHAR SenseInfoLength;
  82. UCHAR DataIn;
  83. ULONG DataTransferLength;
  84. ULONG TimeOutValue;
  85. PVOID DataBuffer;
  86. ULONG SenseInfoOffset;
  87. UCHAR Cdb[16];
  88. };
  89. struct SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
  90. {
  91. SCSI_PASS_THROUGH_DIRECT spt;
  92. ULONG Filler;
  93. UCHAR ucSenseBuf[32];
  94. };
  95. struct SCSI_ADDRESS
  96. {
  97. ULONG Length;
  98. UCHAR PortNumber;
  99. UCHAR PathId;
  100. UCHAR TargetId;
  101. UCHAR Lun;
  102. };
  103. #pragma pack(1)
  104. struct SRB_GDEVBlock
  105. {
  106. BYTE SRB_Cmd;
  107. BYTE SRB_Status;
  108. BYTE SRB_HaID;
  109. BYTE SRB_Flags;
  110. DWORD SRB_Hdr_Rsvd;
  111. BYTE SRB_Target;
  112. BYTE SRB_Lun;
  113. BYTE SRB_DeviceType;
  114. BYTE SRB_Rsvd1;
  115. BYTE pad[68];
  116. };
  117. struct SRB_ExecSCSICmd
  118. {
  119. BYTE SRB_Cmd;
  120. BYTE SRB_Status;
  121. BYTE SRB_HaID;
  122. BYTE SRB_Flags;
  123. DWORD SRB_Hdr_Rsvd;
  124. BYTE SRB_Target;
  125. BYTE SRB_Lun;
  126. WORD SRB_Rsvd1;
  127. DWORD SRB_BufLen;
  128. BYTE *SRB_BufPointer;
  129. BYTE SRB_SenseLen;
  130. BYTE SRB_CDBLen;
  131. BYTE SRB_HaStat;
  132. BYTE SRB_TargStat;
  133. VOID *SRB_PostProc;
  134. BYTE SRB_Rsvd2[20];
  135. BYTE CDBByte[16];
  136. BYTE SenseArea[SENSE_LEN + 2];
  137. };
  138. struct SRB
  139. {
  140. BYTE SRB_Cmd;
  141. BYTE SRB_Status;
  142. BYTE SRB_HaId;
  143. BYTE SRB_Flags;
  144. DWORD SRB_Hdr_Rsvd;
  145. };
  146. struct TOCTRACK
  147. {
  148. BYTE rsvd;
  149. BYTE ADR;
  150. BYTE trackNumber;
  151. BYTE rsvd2;
  152. BYTE addr[4];
  153. };
  154. struct TOC
  155. {
  156. WORD tocLen;
  157. BYTE firstTrack;
  158. BYTE lastTrack;
  159. TOCTRACK tracks[100];
  160. };
  161. #pragma pack()
  162. //==============================================================================
  163. struct CDDeviceDescription
  164. {
  165. CDDeviceDescription() : ha (0), tgt (0), lun (0), scsiDriveLetter (0)
  166. {
  167. }
  168. void createDescription (const char* data)
  169. {
  170. description << String (data + 8, 8).trim() // vendor
  171. << ' ' << String (data + 16, 16).trim() // product id
  172. << ' ' << String (data + 32, 4).trim(); // rev
  173. }
  174. String description;
  175. BYTE ha, tgt, lun;
  176. char scsiDriveLetter; // will be 0 if not using scsi
  177. };
  178. //==============================================================================
  179. class CDReadBuffer
  180. {
  181. public:
  182. CDReadBuffer (const int numberOfFrames)
  183. : startFrame (0), numFrames (0), dataStartOffset (0),
  184. dataLength (0), bufferSize (2352 * numberOfFrames), index (0),
  185. buffer (bufferSize), wantsIndex (false)
  186. {
  187. }
  188. bool isZero() const noexcept
  189. {
  190. for (int i = 0; i < dataLength; ++i)
  191. if (buffer [dataStartOffset + i] != 0)
  192. return false;
  193. return true;
  194. }
  195. int startFrame, numFrames, dataStartOffset;
  196. int dataLength, bufferSize, index;
  197. HeapBlock<BYTE> buffer;
  198. bool wantsIndex;
  199. };
  200. class CDDeviceHandle;
  201. //==============================================================================
  202. class CDController
  203. {
  204. public:
  205. CDController() : initialised (false) {}
  206. virtual ~CDController() {}
  207. virtual bool read (CDReadBuffer&) = 0;
  208. virtual void shutDown() {}
  209. bool readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer = 0);
  210. int getLastIndex();
  211. public:
  212. CDDeviceHandle* deviceInfo;
  213. int framesToCheck, framesOverlap;
  214. bool initialised;
  215. void prepare (SRB_ExecSCSICmd& s);
  216. void perform (SRB_ExecSCSICmd& s);
  217. void setPaused (bool paused);
  218. };
  219. //==============================================================================
  220. class CDDeviceHandle
  221. {
  222. public:
  223. CDDeviceHandle (const CDDeviceDescription& device, HANDLE scsiHandle_)
  224. : info (device), scsiHandle (scsiHandle_), readType (READTYPE_ANY)
  225. {
  226. }
  227. ~CDDeviceHandle()
  228. {
  229. if (controller != nullptr)
  230. {
  231. controller->shutDown();
  232. controller = 0;
  233. }
  234. if (scsiHandle != 0)
  235. CloseHandle (scsiHandle);
  236. }
  237. bool readTOC (TOC* lpToc);
  238. bool readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer = 0);
  239. void openDrawer (bool shouldBeOpen);
  240. void performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s);
  241. CDDeviceDescription info;
  242. HANDLE scsiHandle;
  243. BYTE readType;
  244. private:
  245. ScopedPointer<CDController> controller;
  246. bool testController (int readType, CDController* newController, CDReadBuffer& bufferToUse);
  247. };
  248. //==============================================================================
  249. HANDLE createSCSIDeviceHandle (const char driveLetter)
  250. {
  251. TCHAR devicePath[] = { '\\', '\\', '.', '\\', driveLetter, ':', 0, 0 };
  252. DWORD flags = GENERIC_READ | GENERIC_WRITE;
  253. HANDLE h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  254. if (h == INVALID_HANDLE_VALUE)
  255. {
  256. flags ^= GENERIC_WRITE;
  257. h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  258. }
  259. return h;
  260. }
  261. void findCDDevices (Array<CDDeviceDescription>& list)
  262. {
  263. for (char driveLetter = 'b'; driveLetter <= 'z'; ++driveLetter)
  264. {
  265. TCHAR drivePath[] = { driveLetter, ':', '\\', 0, 0 };
  266. if (GetDriveType (drivePath) == DRIVE_CDROM)
  267. {
  268. HANDLE h = createSCSIDeviceHandle (driveLetter);
  269. if (h != INVALID_HANDLE_VALUE)
  270. {
  271. char buffer[100] = { 0 };
  272. SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER p = { 0 };
  273. p.spt.Length = sizeof (SCSI_PASS_THROUGH);
  274. p.spt.CdbLength = 6;
  275. p.spt.SenseInfoLength = 24;
  276. p.spt.DataIn = SCSI_IOCTL_DATA_IN;
  277. p.spt.DataTransferLength = sizeof (buffer);
  278. p.spt.TimeOutValue = 2;
  279. p.spt.DataBuffer = buffer;
  280. p.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
  281. p.spt.Cdb[0] = 0x12;
  282. p.spt.Cdb[4] = 100;
  283. DWORD bytesReturned = 0;
  284. if (DeviceIoControl (h, IOCTL_SCSI_PASS_THROUGH_DIRECT,
  285. &p, sizeof (p), &p, sizeof (p),
  286. &bytesReturned, 0) != 0)
  287. {
  288. CDDeviceDescription dev;
  289. dev.scsiDriveLetter = driveLetter;
  290. dev.createDescription (buffer);
  291. SCSI_ADDRESS scsiAddr = { 0 };
  292. scsiAddr.Length = sizeof (scsiAddr);
  293. if (DeviceIoControl (h, IOCTL_SCSI_GET_ADDRESS,
  294. 0, 0, &scsiAddr, sizeof (scsiAddr),
  295. &bytesReturned, 0) != 0)
  296. {
  297. dev.ha = scsiAddr.PortNumber;
  298. dev.tgt = scsiAddr.TargetId;
  299. dev.lun = scsiAddr.Lun;
  300. list.add (dev);
  301. }
  302. }
  303. CloseHandle (h);
  304. }
  305. }
  306. }
  307. }
  308. DWORD performScsiPassThroughCommand (SRB_ExecSCSICmd* const srb, const char driveLetter,
  309. HANDLE& deviceHandle, const bool retryOnFailure)
  310. {
  311. SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER s = { 0 };
  312. s.spt.Length = sizeof (SCSI_PASS_THROUGH);
  313. s.spt.CdbLength = srb->SRB_CDBLen;
  314. s.spt.DataIn = (BYTE) ((srb->SRB_Flags & SRB_DIR_IN)
  315. ? SCSI_IOCTL_DATA_IN
  316. : ((srb->SRB_Flags & SRB_DIR_OUT)
  317. ? SCSI_IOCTL_DATA_OUT
  318. : SCSI_IOCTL_DATA_UNSPECIFIED));
  319. s.spt.DataTransferLength = srb->SRB_BufLen;
  320. s.spt.TimeOutValue = 5;
  321. s.spt.DataBuffer = srb->SRB_BufPointer;
  322. s.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
  323. memcpy (s.spt.Cdb, srb->CDBByte, srb->SRB_CDBLen);
  324. srb->SRB_Status = SS_ERR;
  325. srb->SRB_TargStat = 0x0004;
  326. DWORD bytesReturned = 0;
  327. if (DeviceIoControl (deviceHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
  328. &s, sizeof (s), &s, sizeof (s), &bytesReturned, 0) != 0)
  329. {
  330. srb->SRB_Status = SS_COMP;
  331. }
  332. else if (retryOnFailure)
  333. {
  334. const DWORD error = GetLastError();
  335. if ((error == ERROR_MEDIA_CHANGED) || (error == ERROR_INVALID_HANDLE))
  336. {
  337. if (error != ERROR_INVALID_HANDLE)
  338. CloseHandle (deviceHandle);
  339. deviceHandle = createSCSIDeviceHandle (driveLetter);
  340. return performScsiPassThroughCommand (srb, driveLetter, deviceHandle, false);
  341. }
  342. }
  343. return srb->SRB_Status;
  344. }
  345. //==============================================================================
  346. // Controller types..
  347. class ControllerType1 : public CDController
  348. {
  349. public:
  350. ControllerType1() {}
  351. bool read (CDReadBuffer& rb)
  352. {
  353. if (rb.numFrames * 2352 > rb.bufferSize)
  354. return false;
  355. SRB_ExecSCSICmd s;
  356. prepare (s);
  357. s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
  358. s.SRB_BufLen = rb.bufferSize;
  359. s.SRB_BufPointer = rb.buffer;
  360. s.SRB_CDBLen = 12;
  361. s.CDBByte[0] = 0xBE;
  362. s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
  363. s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
  364. s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
  365. s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
  366. s.CDBByte[9] = (BYTE) (deviceInfo->readType == READTYPE_ATAPI1 ? 0x10 : 0xF0);
  367. perform (s);
  368. if (s.SRB_Status != SS_COMP)
  369. return false;
  370. rb.dataLength = rb.numFrames * 2352;
  371. rb.dataStartOffset = 0;
  372. return true;
  373. }
  374. };
  375. //==============================================================================
  376. class ControllerType2 : public CDController
  377. {
  378. public:
  379. ControllerType2() {}
  380. void shutDown()
  381. {
  382. if (initialised)
  383. {
  384. BYTE bufPointer[] = { 0, 0, 0, 8, 83, 0, 0, 0, 0, 0, 8, 0 };
  385. SRB_ExecSCSICmd s;
  386. prepare (s);
  387. s.SRB_Flags = SRB_EVENT_NOTIFY | SRB_ENABLE_RESIDUAL_COUNT;
  388. s.SRB_BufLen = 0x0C;
  389. s.SRB_BufPointer = bufPointer;
  390. s.SRB_CDBLen = 6;
  391. s.CDBByte[0] = 0x15;
  392. s.CDBByte[4] = 0x0C;
  393. perform (s);
  394. }
  395. }
  396. bool init()
  397. {
  398. SRB_ExecSCSICmd s;
  399. s.SRB_Status = SS_ERR;
  400. if (deviceInfo->readType == READTYPE_READ10_2)
  401. {
  402. BYTE bufPointer1[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 35, 6, 0, 0, 0, 0, 0, 128 };
  403. BYTE bufPointer2[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 1, 6, 32, 7, 0, 0, 0, 0 };
  404. for (int i = 0; i < 2; ++i)
  405. {
  406. prepare (s);
  407. s.SRB_Flags = SRB_EVENT_NOTIFY;
  408. s.SRB_BufLen = 0x14;
  409. s.SRB_BufPointer = (i == 0) ? bufPointer1 : bufPointer2;
  410. s.SRB_CDBLen = 6;
  411. s.CDBByte[0] = 0x15;
  412. s.CDBByte[1] = 0x10;
  413. s.CDBByte[4] = 0x14;
  414. perform (s);
  415. if (s.SRB_Status != SS_COMP)
  416. return false;
  417. }
  418. }
  419. else
  420. {
  421. BYTE bufPointer[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48 };
  422. prepare (s);
  423. s.SRB_Flags = SRB_EVENT_NOTIFY;
  424. s.SRB_BufLen = 0x0C;
  425. s.SRB_BufPointer = bufPointer;
  426. s.SRB_CDBLen = 6;
  427. s.CDBByte[0] = 0x15;
  428. s.CDBByte[4] = 0x0C;
  429. perform (s);
  430. }
  431. return s.SRB_Status == SS_COMP;
  432. }
  433. bool read (CDReadBuffer& rb)
  434. {
  435. if (rb.numFrames * 2352 > rb.bufferSize)
  436. return false;
  437. if (! initialised)
  438. {
  439. initialised = init();
  440. if (! initialised)
  441. return false;
  442. }
  443. SRB_ExecSCSICmd s;
  444. prepare (s);
  445. s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
  446. s.SRB_BufLen = rb.bufferSize;
  447. s.SRB_BufPointer = rb.buffer;
  448. s.SRB_CDBLen = 10;
  449. s.CDBByte[0] = 0x28;
  450. s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5);
  451. s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
  452. s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
  453. s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
  454. s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
  455. perform (s);
  456. if (s.SRB_Status != SS_COMP)
  457. return false;
  458. rb.dataLength = rb.numFrames * 2352;
  459. rb.dataStartOffset = 0;
  460. return true;
  461. }
  462. };
  463. //==============================================================================
  464. class ControllerType3 : public CDController
  465. {
  466. public:
  467. ControllerType3() {}
  468. bool read (CDReadBuffer& rb)
  469. {
  470. if (rb.numFrames * 2352 > rb.bufferSize)
  471. return false;
  472. if (! initialised)
  473. {
  474. setPaused (false);
  475. initialised = true;
  476. }
  477. SRB_ExecSCSICmd s;
  478. prepare (s);
  479. s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
  480. s.SRB_BufLen = rb.numFrames * 2352;
  481. s.SRB_BufPointer = rb.buffer;
  482. s.SRB_CDBLen = 12;
  483. s.CDBByte[0] = 0xD8;
  484. s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
  485. s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
  486. s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
  487. s.CDBByte[9] = (BYTE) (rb.numFrames & 0xFF);
  488. perform (s);
  489. if (s.SRB_Status != SS_COMP)
  490. return false;
  491. rb.dataLength = rb.numFrames * 2352;
  492. rb.dataStartOffset = 0;
  493. return true;
  494. }
  495. };
  496. //==============================================================================
  497. class ControllerType4 : public CDController
  498. {
  499. public:
  500. ControllerType4() {}
  501. bool selectD4Mode()
  502. {
  503. BYTE bufPointer[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 48 };
  504. SRB_ExecSCSICmd s;
  505. prepare (s);
  506. s.SRB_Flags = SRB_EVENT_NOTIFY;
  507. s.SRB_CDBLen = 6;
  508. s.SRB_BufLen = 12;
  509. s.SRB_BufPointer = bufPointer;
  510. s.CDBByte[0] = 0x15;
  511. s.CDBByte[1] = 0x10;
  512. s.CDBByte[4] = 0x08;
  513. perform (s);
  514. return s.SRB_Status == SS_COMP;
  515. }
  516. bool read (CDReadBuffer& rb)
  517. {
  518. if (rb.numFrames * 2352 > rb.bufferSize)
  519. return false;
  520. if (! initialised)
  521. {
  522. setPaused (true);
  523. if (deviceInfo->readType == READTYPE_READ_D4_1)
  524. selectD4Mode();
  525. initialised = true;
  526. }
  527. SRB_ExecSCSICmd s;
  528. prepare (s);
  529. s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
  530. s.SRB_BufLen = rb.bufferSize;
  531. s.SRB_BufPointer = rb.buffer;
  532. s.SRB_CDBLen = 10;
  533. s.CDBByte[0] = 0xD4;
  534. s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
  535. s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
  536. s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
  537. s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
  538. perform (s);
  539. if (s.SRB_Status != SS_COMP)
  540. return false;
  541. rb.dataLength = rb.numFrames * 2352;
  542. rb.dataStartOffset = 0;
  543. return true;
  544. }
  545. };
  546. //==============================================================================
  547. void CDController::prepare (SRB_ExecSCSICmd& s)
  548. {
  549. zerostruct (s);
  550. s.SRB_Cmd = SC_EXEC_SCSI_CMD;
  551. s.SRB_HaID = deviceInfo->info.ha;
  552. s.SRB_Target = deviceInfo->info.tgt;
  553. s.SRB_Lun = deviceInfo->info.lun;
  554. s.SRB_SenseLen = SENSE_LEN;
  555. }
  556. void CDController::perform (SRB_ExecSCSICmd& s)
  557. {
  558. s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
  559. deviceInfo->performScsiCommand (s.SRB_PostProc, s);
  560. }
  561. void CDController::setPaused (bool paused)
  562. {
  563. SRB_ExecSCSICmd s;
  564. prepare (s);
  565. s.SRB_Flags = SRB_EVENT_NOTIFY;
  566. s.SRB_CDBLen = 10;
  567. s.CDBByte[0] = 0x4B;
  568. s.CDBByte[8] = (BYTE) (paused ? 0 : 1);
  569. perform (s);
  570. }
  571. bool CDController::readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer)
  572. {
  573. if (overlapBuffer != nullptr)
  574. {
  575. const bool canDoJitter = (overlapBuffer->bufferSize >= 2352 * framesToCheck);
  576. const bool doJitter = canDoJitter && ! overlapBuffer->isZero();
  577. if (doJitter
  578. && overlapBuffer->startFrame > 0
  579. && overlapBuffer->numFrames > 0
  580. && overlapBuffer->dataLength > 0)
  581. {
  582. const int numFrames = rb.numFrames;
  583. if (overlapBuffer->startFrame == (rb.startFrame - framesToCheck))
  584. {
  585. rb.startFrame -= framesOverlap;
  586. if (framesToCheck < framesOverlap
  587. && numFrames + framesOverlap <= rb.bufferSize / 2352)
  588. rb.numFrames += framesOverlap;
  589. }
  590. else
  591. {
  592. overlapBuffer->dataLength = 0;
  593. overlapBuffer->startFrame = 0;
  594. overlapBuffer->numFrames = 0;
  595. }
  596. }
  597. if (! read (rb))
  598. return false;
  599. if (doJitter)
  600. {
  601. const int checkLen = framesToCheck * 2352;
  602. const int maxToCheck = rb.dataLength - checkLen;
  603. if (overlapBuffer->dataLength == 0 || overlapBuffer->isZero())
  604. return true;
  605. BYTE* const p = overlapBuffer->buffer + overlapBuffer->dataStartOffset;
  606. bool found = false;
  607. for (int i = 0; i < maxToCheck; ++i)
  608. {
  609. if (memcmp (p, rb.buffer + i, checkLen) == 0)
  610. {
  611. i += checkLen;
  612. rb.dataStartOffset = i;
  613. rb.dataLength -= i;
  614. rb.startFrame = overlapBuffer->startFrame + framesToCheck;
  615. found = true;
  616. break;
  617. }
  618. }
  619. rb.numFrames = rb.dataLength / 2352;
  620. rb.dataLength = 2352 * rb.numFrames;
  621. if (! found)
  622. return false;
  623. }
  624. if (canDoJitter)
  625. {
  626. memcpy (overlapBuffer->buffer,
  627. rb.buffer + rb.dataStartOffset + 2352 * (rb.numFrames - framesToCheck),
  628. 2352 * framesToCheck);
  629. overlapBuffer->startFrame = rb.startFrame + rb.numFrames - framesToCheck;
  630. overlapBuffer->numFrames = framesToCheck;
  631. overlapBuffer->dataLength = 2352 * framesToCheck;
  632. overlapBuffer->dataStartOffset = 0;
  633. }
  634. else
  635. {
  636. overlapBuffer->startFrame = 0;
  637. overlapBuffer->numFrames = 0;
  638. overlapBuffer->dataLength = 0;
  639. }
  640. return true;
  641. }
  642. return read (rb);
  643. }
  644. int CDController::getLastIndex()
  645. {
  646. char qdata[100];
  647. SRB_ExecSCSICmd s;
  648. prepare (s);
  649. s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
  650. s.SRB_BufLen = sizeof (qdata);
  651. s.SRB_BufPointer = (BYTE*) qdata;
  652. s.SRB_CDBLen = 12;
  653. s.CDBByte[0] = 0x42;
  654. s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5);
  655. s.CDBByte[2] = 64;
  656. s.CDBByte[3] = 1; // get current position
  657. s.CDBByte[7] = 0;
  658. s.CDBByte[8] = (BYTE) sizeof (qdata);
  659. perform (s);
  660. return s.SRB_Status == SS_COMP ? qdata[7] : 0;
  661. }
  662. //==============================================================================
  663. bool CDDeviceHandle::readTOC (TOC* lpToc)
  664. {
  665. SRB_ExecSCSICmd s = { 0 };
  666. s.SRB_Cmd = SC_EXEC_SCSI_CMD;
  667. s.SRB_HaID = info.ha;
  668. s.SRB_Target = info.tgt;
  669. s.SRB_Lun = info.lun;
  670. s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
  671. s.SRB_BufLen = 0x324;
  672. s.SRB_BufPointer = (BYTE*) lpToc;
  673. s.SRB_SenseLen = 0x0E;
  674. s.SRB_CDBLen = 0x0A;
  675. s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
  676. s.CDBByte[0] = 0x43;
  677. s.CDBByte[1] = 0x00;
  678. s.CDBByte[7] = 0x03;
  679. s.CDBByte[8] = 0x24;
  680. performScsiCommand (s.SRB_PostProc, s);
  681. return (s.SRB_Status == SS_COMP);
  682. }
  683. void CDDeviceHandle::performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s)
  684. {
  685. ResetEvent (event);
  686. DWORD status = performScsiPassThroughCommand ((SRB_ExecSCSICmd*) &s, info.scsiDriveLetter, scsiHandle, true);
  687. if (status == SS_PENDING)
  688. WaitForSingleObject (event, 4000);
  689. CloseHandle (event);
  690. }
  691. bool CDDeviceHandle::readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer)
  692. {
  693. if (controller == 0)
  694. {
  695. testController (READTYPE_ATAPI2, new ControllerType1(), buffer)
  696. || testController (READTYPE_ATAPI1, new ControllerType1(), buffer)
  697. || testController (READTYPE_READ10_2, new ControllerType2(), buffer)
  698. || testController (READTYPE_READ10, new ControllerType2(), buffer)
  699. || testController (READTYPE_READ_D8, new ControllerType3(), buffer)
  700. || testController (READTYPE_READ_D4, new ControllerType4(), buffer)
  701. || testController (READTYPE_READ_D4_1, new ControllerType4(), buffer);
  702. }
  703. buffer.index = 0;
  704. if (controller != nullptr && controller->readAudio (buffer, overlapBuffer))
  705. {
  706. if (buffer.wantsIndex)
  707. buffer.index = controller->getLastIndex();
  708. return true;
  709. }
  710. return false;
  711. }
  712. void CDDeviceHandle::openDrawer (bool shouldBeOpen)
  713. {
  714. if (shouldBeOpen)
  715. {
  716. if (controller != nullptr)
  717. {
  718. controller->shutDown();
  719. controller = nullptr;
  720. }
  721. if (scsiHandle != 0)
  722. {
  723. CloseHandle (scsiHandle);
  724. scsiHandle = 0;
  725. }
  726. }
  727. SRB_ExecSCSICmd s = { 0 };
  728. s.SRB_Cmd = SC_EXEC_SCSI_CMD;
  729. s.SRB_HaID = info.ha;
  730. s.SRB_Target = info.tgt;
  731. s.SRB_Lun = info.lun;
  732. s.SRB_SenseLen = SENSE_LEN;
  733. s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
  734. s.SRB_BufLen = 0;
  735. s.SRB_BufPointer = 0;
  736. s.SRB_CDBLen = 12;
  737. s.CDBByte[0] = 0x1b;
  738. s.CDBByte[1] = (BYTE) (info.lun << 5);
  739. s.CDBByte[4] = (BYTE) (shouldBeOpen ? 2 : 3);
  740. s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
  741. performScsiCommand (s.SRB_PostProc, s);
  742. }
  743. bool CDDeviceHandle::testController (const int type, CDController* const newController, CDReadBuffer& rb)
  744. {
  745. controller = newController;
  746. readType = (BYTE) type;
  747. controller->deviceInfo = this;
  748. controller->framesToCheck = 1;
  749. controller->framesOverlap = 3;
  750. bool passed = false;
  751. memset (rb.buffer, 0xcd, rb.bufferSize);
  752. if (controller->read (rb))
  753. {
  754. passed = true;
  755. int* p = (int*) (rb.buffer + rb.dataStartOffset);
  756. int wrong = 0;
  757. for (int i = rb.dataLength / 4; --i >= 0;)
  758. {
  759. if (*p++ == (int) 0xcdcdcdcd)
  760. {
  761. if (++wrong == 4)
  762. {
  763. passed = false;
  764. break;
  765. }
  766. }
  767. else
  768. {
  769. wrong = 0;
  770. }
  771. }
  772. }
  773. if (! passed)
  774. {
  775. controller->shutDown();
  776. controller = nullptr;
  777. }
  778. return passed;
  779. }
  780. //==============================================================================
  781. struct CDDeviceWrapper
  782. {
  783. CDDeviceWrapper (const CDDeviceDescription& device, HANDLE scsiHandle)
  784. : deviceHandle (device, scsiHandle), overlapBuffer (3), jitter (false)
  785. {
  786. // xxx jitter never seemed to actually be enabled (??)
  787. }
  788. CDDeviceHandle deviceHandle;
  789. CDReadBuffer overlapBuffer;
  790. bool jitter;
  791. };
  792. //==============================================================================
  793. int getAddressOfTrack (const TOCTRACK& t) noexcept
  794. {
  795. return (((DWORD) t.addr[0]) << 24) + (((DWORD) t.addr[1]) << 16)
  796. + (((DWORD) t.addr[2]) << 8) + ((DWORD) t.addr[3]);
  797. }
  798. const int samplesPerFrame = 44100 / 75;
  799. const int bytesPerFrame = samplesPerFrame * 4;
  800. const int framesPerIndexRead = 4;
  801. }
  802. //==============================================================================
  803. StringArray AudioCDReader::getAvailableCDNames()
  804. {
  805. using namespace CDReaderHelpers;
  806. StringArray results;
  807. Array<CDDeviceDescription> list;
  808. findCDDevices (list);
  809. for (int i = 0; i < list.size(); ++i)
  810. {
  811. String s;
  812. if (list[i].scsiDriveLetter > 0)
  813. s << String::charToString (list[i].scsiDriveLetter).toUpperCase() << ": ";
  814. s << list[i].description;
  815. results.add (s);
  816. }
  817. return results;
  818. }
  819. AudioCDReader* AudioCDReader::createReaderForCD (const int deviceIndex)
  820. {
  821. using namespace CDReaderHelpers;
  822. Array<CDDeviceDescription> list;
  823. findCDDevices (list);
  824. if (isPositiveAndBelow (deviceIndex, list.size()))
  825. {
  826. HANDLE h = createSCSIDeviceHandle (list [deviceIndex].scsiDriveLetter);
  827. if (h != INVALID_HANDLE_VALUE)
  828. {
  829. ScopedPointer<AudioCDReader> cd (new AudioCDReader (new CDDeviceWrapper (list [deviceIndex], h)));
  830. if (cd->lengthInSamples > 0)
  831. return cd.release();
  832. }
  833. }
  834. return nullptr;
  835. }
  836. AudioCDReader::AudioCDReader (void* handle_)
  837. : AudioFormatReader (0, "CD Audio"),
  838. handle (handle_),
  839. indexingEnabled (false),
  840. lastIndex (0),
  841. firstFrameInBuffer (0),
  842. samplesInBuffer (0)
  843. {
  844. using namespace CDReaderHelpers;
  845. jassert (handle_ != nullptr);
  846. refreshTrackLengths();
  847. sampleRate = 44100.0;
  848. bitsPerSample = 16;
  849. numChannels = 2;
  850. usesFloatingPointData = false;
  851. buffer.setSize (4 * bytesPerFrame, true);
  852. }
  853. AudioCDReader::~AudioCDReader()
  854. {
  855. using namespace CDReaderHelpers;
  856. CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
  857. delete device;
  858. }
  859. bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  860. int64 startSampleInFile, int numSamples)
  861. {
  862. using namespace CDReaderHelpers;
  863. CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
  864. bool ok = true;
  865. while (numSamples > 0)
  866. {
  867. const int bufferStartSample = firstFrameInBuffer * samplesPerFrame;
  868. const int bufferEndSample = bufferStartSample + samplesInBuffer;
  869. if (startSampleInFile >= bufferStartSample
  870. && startSampleInFile < bufferEndSample)
  871. {
  872. const int toDo = (int) jmin ((int64) numSamples, bufferEndSample - startSampleInFile);
  873. int* const l = destSamples[0] + startOffsetInDestBuffer;
  874. int* const r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr;
  875. const short* src = (const short*) buffer.getData();
  876. src += 2 * (startSampleInFile - bufferStartSample);
  877. for (int i = 0; i < toDo; ++i)
  878. {
  879. l[i] = src [i << 1] << 16;
  880. if (r != nullptr)
  881. r[i] = src [(i << 1) + 1] << 16;
  882. }
  883. startOffsetInDestBuffer += toDo;
  884. startSampleInFile += toDo;
  885. numSamples -= toDo;
  886. }
  887. else
  888. {
  889. const int framesInBuffer = (int) (buffer.getSize() / bytesPerFrame);
  890. const int frameNeeded = (int) (startSampleInFile / samplesPerFrame);
  891. if (firstFrameInBuffer + framesInBuffer != frameNeeded)
  892. {
  893. device->overlapBuffer.dataLength = 0;
  894. device->overlapBuffer.startFrame = 0;
  895. device->overlapBuffer.numFrames = 0;
  896. device->jitter = false;
  897. }
  898. firstFrameInBuffer = frameNeeded;
  899. lastIndex = 0;
  900. CDReadBuffer readBuffer (framesInBuffer + 4);
  901. readBuffer.wantsIndex = indexingEnabled;
  902. int i;
  903. for (i = 5; --i >= 0;)
  904. {
  905. readBuffer.startFrame = frameNeeded;
  906. readBuffer.numFrames = framesInBuffer;
  907. if (device->deviceHandle.readAudio (readBuffer, device->jitter ? &device->overlapBuffer : 0))
  908. break;
  909. else
  910. device->overlapBuffer.dataLength = 0;
  911. }
  912. if (i >= 0)
  913. {
  914. buffer.copyFrom (readBuffer.buffer + readBuffer.dataStartOffset, 0, readBuffer.dataLength);
  915. samplesInBuffer = readBuffer.dataLength >> 2;
  916. lastIndex = readBuffer.index;
  917. }
  918. else
  919. {
  920. int* l = destSamples[0] + startOffsetInDestBuffer;
  921. int* r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr;
  922. while (--numSamples >= 0)
  923. {
  924. *l++ = 0;
  925. if (r != nullptr)
  926. *r++ = 0;
  927. }
  928. // sometimes the read fails for just the very last couple of blocks, so
  929. // we'll ignore and errors in the last half-second of the disk..
  930. ok = startSampleInFile > (trackStartSamples [getNumTracks()] - 20000);
  931. break;
  932. }
  933. }
  934. }
  935. return ok;
  936. }
  937. bool AudioCDReader::isCDStillPresent() const
  938. {
  939. using namespace CDReaderHelpers;
  940. TOC toc = { 0 };
  941. return static_cast<CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc);
  942. }
  943. void AudioCDReader::refreshTrackLengths()
  944. {
  945. using namespace CDReaderHelpers;
  946. trackStartSamples.clear();
  947. zeromem (audioTracks, sizeof (audioTracks));
  948. TOC toc = { 0 };
  949. if (static_cast<CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc))
  950. {
  951. int numTracks = 1 + toc.lastTrack - toc.firstTrack;
  952. for (int i = 0; i <= numTracks; ++i)
  953. {
  954. trackStartSamples.add (samplesPerFrame * getAddressOfTrack (toc.tracks [i]));
  955. audioTracks [i] = ((toc.tracks[i].ADR & 4) == 0);
  956. }
  957. }
  958. lengthInSamples = getPositionOfTrackStart (getNumTracks());
  959. }
  960. bool AudioCDReader::isTrackAudio (int trackNum) const
  961. {
  962. return trackNum >= 0 && trackNum < getNumTracks() && audioTracks [trackNum];
  963. }
  964. void AudioCDReader::enableIndexScanning (bool b)
  965. {
  966. indexingEnabled = b;
  967. }
  968. int AudioCDReader::getLastIndex() const
  969. {
  970. return lastIndex;
  971. }
  972. int AudioCDReader::getIndexAt (int samplePos)
  973. {
  974. using namespace CDReaderHelpers;
  975. CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
  976. const int frameNeeded = samplePos / samplesPerFrame;
  977. device->overlapBuffer.dataLength = 0;
  978. device->overlapBuffer.startFrame = 0;
  979. device->overlapBuffer.numFrames = 0;
  980. device->jitter = false;
  981. firstFrameInBuffer = 0;
  982. lastIndex = 0;
  983. CDReadBuffer readBuffer (4 + framesPerIndexRead);
  984. readBuffer.wantsIndex = true;
  985. int i;
  986. for (i = 5; --i >= 0;)
  987. {
  988. readBuffer.startFrame = frameNeeded;
  989. readBuffer.numFrames = framesPerIndexRead;
  990. if (device->deviceHandle.readAudio (readBuffer))
  991. break;
  992. }
  993. if (i >= 0)
  994. return readBuffer.index;
  995. return -1;
  996. }
  997. Array<int> AudioCDReader::findIndexesInTrack (const int trackNumber)
  998. {
  999. using namespace CDReaderHelpers;
  1000. Array <int> indexes;
  1001. const int trackStart = getPositionOfTrackStart (trackNumber);
  1002. const int trackEnd = getPositionOfTrackStart (trackNumber + 1);
  1003. bool needToScan = true;
  1004. if (trackEnd - trackStart > 20 * 44100)
  1005. {
  1006. // check the end of the track for indexes before scanning the whole thing
  1007. needToScan = false;
  1008. int pos = jmax (trackStart, trackEnd - 44100 * 5);
  1009. bool seenAnIndex = false;
  1010. while (pos <= trackEnd - samplesPerFrame)
  1011. {
  1012. const int index = getIndexAt (pos);
  1013. if (index == 0)
  1014. {
  1015. // lead-out, so skip back a bit if we've not found any indexes yet..
  1016. if (seenAnIndex)
  1017. break;
  1018. pos -= 44100 * 5;
  1019. if (pos < trackStart)
  1020. break;
  1021. }
  1022. else
  1023. {
  1024. if (index > 0)
  1025. seenAnIndex = true;
  1026. if (index > 1)
  1027. {
  1028. needToScan = true;
  1029. break;
  1030. }
  1031. pos += samplesPerFrame * framesPerIndexRead;
  1032. }
  1033. }
  1034. }
  1035. if (needToScan)
  1036. {
  1037. CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
  1038. int pos = trackStart;
  1039. int last = -1;
  1040. while (pos < trackEnd - samplesPerFrame * 10)
  1041. {
  1042. const int frameNeeded = pos / samplesPerFrame;
  1043. device->overlapBuffer.dataLength = 0;
  1044. device->overlapBuffer.startFrame = 0;
  1045. device->overlapBuffer.numFrames = 0;
  1046. device->jitter = false;
  1047. firstFrameInBuffer = 0;
  1048. CDReadBuffer readBuffer (4);
  1049. readBuffer.wantsIndex = true;
  1050. int i;
  1051. for (i = 5; --i >= 0;)
  1052. {
  1053. readBuffer.startFrame = frameNeeded;
  1054. readBuffer.numFrames = framesPerIndexRead;
  1055. if (device->deviceHandle.readAudio (readBuffer))
  1056. break;
  1057. }
  1058. if (i < 0)
  1059. break;
  1060. if (readBuffer.index > last && readBuffer.index > 1)
  1061. {
  1062. last = readBuffer.index;
  1063. indexes.add (pos);
  1064. }
  1065. pos += samplesPerFrame * framesPerIndexRead;
  1066. }
  1067. indexes.removeFirstMatchingValue (trackStart);
  1068. }
  1069. return indexes;
  1070. }
  1071. void AudioCDReader::ejectDisk()
  1072. {
  1073. using namespace CDReaderHelpers;
  1074. static_cast<CDDeviceWrapper*> (handle)->deviceHandle.openDrawer (true);
  1075. }