Add remaining files
[juce-lv2.git] / juce / source / src / native / windows / juce_win32_AudioCDReader.cpp
blob033ea31e1afba5a37a054af312bba84c3e72558a
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 // (This file gets included by juce_win32_NativeCode.cpp, rather than being
27 // compiled on its own).
28 #if JUCE_INCLUDED_FILE
30 #if JUCE_USE_CDREADER
32 namespace CDReaderHelpers
35 #define FILE_ANY_ACCESS 0
36 #ifndef FILE_READ_ACCESS
37 #define FILE_READ_ACCESS 1
38 #endif
39 #ifndef FILE_WRITE_ACCESS
40 #define FILE_WRITE_ACCESS 2
41 #endif
43 #define METHOD_BUFFERED 0
44 #define IOCTL_SCSI_BASE 4
45 #define SCSI_IOCTL_DATA_OUT 0
46 #define SCSI_IOCTL_DATA_IN 1
47 #define SCSI_IOCTL_DATA_UNSPECIFIED 2
49 #define CTL_CODE2(DevType, Function, Method, Access) (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
50 #define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE2( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS )
51 #define IOCTL_SCSI_GET_ADDRESS CTL_CODE2( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS )
53 #define SENSE_LEN 14
54 #define SRB_ENABLE_RESIDUAL_COUNT 0x04
55 #define SRB_DIR_IN 0x08
56 #define SRB_DIR_OUT 0x10
57 #define SRB_EVENT_NOTIFY 0x40
58 #define SC_HA_INQUIRY 0x00
59 #define SC_GET_DEV_TYPE 0x01
60 #define SC_EXEC_SCSI_CMD 0x02
61 #define SS_PENDING 0x00
62 #define SS_COMP 0x01
63 #define SS_ERR 0x04
65 enum
67 READTYPE_ANY = 0,
68 READTYPE_ATAPI1 = 1,
69 READTYPE_ATAPI2 = 2,
70 READTYPE_READ6 = 3,
71 READTYPE_READ10 = 4,
72 READTYPE_READ_D8 = 5,
73 READTYPE_READ_D4 = 6,
74 READTYPE_READ_D4_1 = 7,
75 READTYPE_READ10_2 = 8
78 struct SCSI_PASS_THROUGH
80 USHORT Length;
81 UCHAR ScsiStatus;
82 UCHAR PathId;
83 UCHAR TargetId;
84 UCHAR Lun;
85 UCHAR CdbLength;
86 UCHAR SenseInfoLength;
87 UCHAR DataIn;
88 ULONG DataTransferLength;
89 ULONG TimeOutValue;
90 ULONG DataBufferOffset;
91 ULONG SenseInfoOffset;
92 UCHAR Cdb[16];
95 struct SCSI_PASS_THROUGH_DIRECT
97 USHORT Length;
98 UCHAR ScsiStatus;
99 UCHAR PathId;
100 UCHAR TargetId;
101 UCHAR Lun;
102 UCHAR CdbLength;
103 UCHAR SenseInfoLength;
104 UCHAR DataIn;
105 ULONG DataTransferLength;
106 ULONG TimeOutValue;
107 PVOID DataBuffer;
108 ULONG SenseInfoOffset;
109 UCHAR Cdb[16];
112 struct SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
114 SCSI_PASS_THROUGH_DIRECT spt;
115 ULONG Filler;
116 UCHAR ucSenseBuf[32];
119 struct SCSI_ADDRESS
121 ULONG Length;
122 UCHAR PortNumber;
123 UCHAR PathId;
124 UCHAR TargetId;
125 UCHAR Lun;
128 #pragma pack(1)
130 struct SRB_GDEVBlock
132 BYTE SRB_Cmd;
133 BYTE SRB_Status;
134 BYTE SRB_HaID;
135 BYTE SRB_Flags;
136 DWORD SRB_Hdr_Rsvd;
137 BYTE SRB_Target;
138 BYTE SRB_Lun;
139 BYTE SRB_DeviceType;
140 BYTE SRB_Rsvd1;
141 BYTE pad[68];
145 struct SRB_ExecSCSICmd
147 BYTE SRB_Cmd;
148 BYTE SRB_Status;
149 BYTE SRB_HaID;
150 BYTE SRB_Flags;
151 DWORD SRB_Hdr_Rsvd;
152 BYTE SRB_Target;
153 BYTE SRB_Lun;
154 WORD SRB_Rsvd1;
155 DWORD SRB_BufLen;
156 BYTE *SRB_BufPointer;
157 BYTE SRB_SenseLen;
158 BYTE SRB_CDBLen;
159 BYTE SRB_HaStat;
160 BYTE SRB_TargStat;
161 VOID *SRB_PostProc;
162 BYTE SRB_Rsvd2[20];
163 BYTE CDBByte[16];
164 BYTE SenseArea[SENSE_LEN + 2];
167 struct SRB
169 BYTE SRB_Cmd;
170 BYTE SRB_Status;
171 BYTE SRB_HaId;
172 BYTE SRB_Flags;
173 DWORD SRB_Hdr_Rsvd;
176 struct TOCTRACK
178 BYTE rsvd;
179 BYTE ADR;
180 BYTE trackNumber;
181 BYTE rsvd2;
182 BYTE addr[4];
185 struct TOC
187 WORD tocLen;
188 BYTE firstTrack;
189 BYTE lastTrack;
190 TOCTRACK tracks[100];
193 #pragma pack()
195 //==============================================================================
196 struct CDDeviceDescription
198 CDDeviceDescription() : ha (0), tgt (0), lun (0), scsiDriveLetter (0)
202 void createDescription (const char* data)
204 description << String (data + 8, 8).trim() // vendor
205 << ' ' << String (data + 16, 16).trim() // product id
206 << ' ' << String (data + 32, 4).trim(); // rev
209 String description;
210 BYTE ha, tgt, lun;
211 char scsiDriveLetter; // will be 0 if not using scsi
214 //==============================================================================
215 class CDReadBuffer
217 public:
218 CDReadBuffer (const int numberOfFrames)
219 : startFrame (0), numFrames (0), dataStartOffset (0),
220 dataLength (0), bufferSize (2352 * numberOfFrames), index (0),
221 buffer (bufferSize), wantsIndex (false)
225 bool isZero() const noexcept
227 for (int i = 0; i < dataLength; ++i)
228 if (buffer [dataStartOffset + i] != 0)
229 return false;
231 return true;
234 int startFrame, numFrames, dataStartOffset;
235 int dataLength, bufferSize, index;
236 HeapBlock<BYTE> buffer;
237 bool wantsIndex;
240 class CDDeviceHandle;
242 //==============================================================================
243 class CDController
245 public:
246 CDController() : initialised (false) {}
247 virtual ~CDController() {}
249 virtual bool read (CDReadBuffer&) = 0;
250 virtual void shutDown() {}
252 bool readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer = 0);
253 int getLastIndex();
255 public:
256 CDDeviceHandle* deviceInfo;
257 int framesToCheck, framesOverlap;
258 bool initialised;
260 void prepare (SRB_ExecSCSICmd& s);
261 void perform (SRB_ExecSCSICmd& s);
262 void setPaused (bool paused);
266 //==============================================================================
267 class CDDeviceHandle
269 public:
270 CDDeviceHandle (const CDDeviceDescription& device, HANDLE scsiHandle_)
271 : info (device), scsiHandle (scsiHandle_), readType (READTYPE_ANY)
275 ~CDDeviceHandle()
277 if (controller != nullptr)
279 controller->shutDown();
280 controller = 0;
283 if (scsiHandle != 0)
284 CloseHandle (scsiHandle);
287 bool readTOC (TOC* lpToc);
288 bool readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer = 0);
289 void openDrawer (bool shouldBeOpen);
290 void performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s);
292 CDDeviceDescription info;
293 HANDLE scsiHandle;
294 BYTE readType;
296 private:
297 ScopedPointer<CDController> controller;
299 bool testController (int readType, CDController* newController, CDReadBuffer& bufferToUse);
302 //==============================================================================
303 HANDLE createSCSIDeviceHandle (const char driveLetter)
305 TCHAR devicePath[] = { '\\', '\\', '.', '\\', driveLetter, ':', 0, 0 };
306 DWORD flags = GENERIC_READ | GENERIC_WRITE;
307 HANDLE h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
309 if (h == INVALID_HANDLE_VALUE)
311 flags ^= GENERIC_WRITE;
312 h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
315 return h;
318 void findCDDevices (Array<CDDeviceDescription>& list)
320 for (char driveLetter = 'b'; driveLetter <= 'z'; ++driveLetter)
322 TCHAR drivePath[] = { driveLetter, ':', '\\', 0, 0 };
324 if (GetDriveType (drivePath) == DRIVE_CDROM)
326 HANDLE h = createSCSIDeviceHandle (driveLetter);
328 if (h != INVALID_HANDLE_VALUE)
330 char buffer[100] = { 0 };
332 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER p = { 0 };
333 p.spt.Length = sizeof (SCSI_PASS_THROUGH);
334 p.spt.CdbLength = 6;
335 p.spt.SenseInfoLength = 24;
336 p.spt.DataIn = SCSI_IOCTL_DATA_IN;
337 p.spt.DataTransferLength = sizeof (buffer);
338 p.spt.TimeOutValue = 2;
339 p.spt.DataBuffer = buffer;
340 p.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
341 p.spt.Cdb[0] = 0x12;
342 p.spt.Cdb[4] = 100;
344 DWORD bytesReturned = 0;
346 if (DeviceIoControl (h, IOCTL_SCSI_PASS_THROUGH_DIRECT,
347 &p, sizeof (p), &p, sizeof (p),
348 &bytesReturned, 0) != 0)
350 CDDeviceDescription dev;
351 dev.scsiDriveLetter = driveLetter;
352 dev.createDescription (buffer);
354 SCSI_ADDRESS scsiAddr = { 0 };
355 scsiAddr.Length = sizeof (scsiAddr);
357 if (DeviceIoControl (h, IOCTL_SCSI_GET_ADDRESS,
358 0, 0, &scsiAddr, sizeof (scsiAddr),
359 &bytesReturned, 0) != 0)
361 dev.ha = scsiAddr.PortNumber;
362 dev.tgt = scsiAddr.TargetId;
363 dev.lun = scsiAddr.Lun;
364 list.add (dev);
368 CloseHandle (h);
374 DWORD performScsiPassThroughCommand (SRB_ExecSCSICmd* const srb, const char driveLetter,
375 HANDLE& deviceHandle, const bool retryOnFailure)
377 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER s = { 0 };
378 s.spt.Length = sizeof (SCSI_PASS_THROUGH);
379 s.spt.CdbLength = srb->SRB_CDBLen;
381 s.spt.DataIn = (BYTE) ((srb->SRB_Flags & SRB_DIR_IN)
382 ? SCSI_IOCTL_DATA_IN
383 : ((srb->SRB_Flags & SRB_DIR_OUT)
384 ? SCSI_IOCTL_DATA_OUT
385 : SCSI_IOCTL_DATA_UNSPECIFIED));
387 s.spt.DataTransferLength = srb->SRB_BufLen;
388 s.spt.TimeOutValue = 5;
389 s.spt.DataBuffer = srb->SRB_BufPointer;
390 s.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
392 memcpy (s.spt.Cdb, srb->CDBByte, srb->SRB_CDBLen);
394 srb->SRB_Status = SS_ERR;
395 srb->SRB_TargStat = 0x0004;
397 DWORD bytesReturned = 0;
399 if (DeviceIoControl (deviceHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
400 &s, sizeof (s), &s, sizeof (s), &bytesReturned, 0) != 0)
402 srb->SRB_Status = SS_COMP;
404 else if (retryOnFailure)
406 const DWORD error = GetLastError();
408 if ((error == ERROR_MEDIA_CHANGED) || (error == ERROR_INVALID_HANDLE))
410 if (error != ERROR_INVALID_HANDLE)
411 CloseHandle (deviceHandle);
413 deviceHandle = createSCSIDeviceHandle (driveLetter);
415 return performScsiPassThroughCommand (srb, driveLetter, deviceHandle, false);
419 return srb->SRB_Status;
423 //==============================================================================
424 // Controller types..
426 class ControllerType1 : public CDController
428 public:
429 ControllerType1() {}
431 bool read (CDReadBuffer& rb)
433 if (rb.numFrames * 2352 > rb.bufferSize)
434 return false;
436 SRB_ExecSCSICmd s;
437 prepare (s);
438 s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
439 s.SRB_BufLen = rb.bufferSize;
440 s.SRB_BufPointer = rb.buffer;
441 s.SRB_CDBLen = 12;
442 s.CDBByte[0] = 0xBE;
443 s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
444 s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
445 s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
446 s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
447 s.CDBByte[9] = (BYTE) (deviceInfo->readType == READTYPE_ATAPI1 ? 0x10 : 0xF0);
448 perform (s);
450 if (s.SRB_Status != SS_COMP)
451 return false;
453 rb.dataLength = rb.numFrames * 2352;
454 rb.dataStartOffset = 0;
455 return true;
459 //==============================================================================
460 class ControllerType2 : public CDController
462 public:
463 ControllerType2() {}
465 void shutDown()
467 if (initialised)
469 BYTE bufPointer[] = { 0, 0, 0, 8, 83, 0, 0, 0, 0, 0, 8, 0 };
471 SRB_ExecSCSICmd s;
472 prepare (s);
473 s.SRB_Flags = SRB_EVENT_NOTIFY | SRB_ENABLE_RESIDUAL_COUNT;
474 s.SRB_BufLen = 0x0C;
475 s.SRB_BufPointer = bufPointer;
476 s.SRB_CDBLen = 6;
477 s.CDBByte[0] = 0x15;
478 s.CDBByte[4] = 0x0C;
479 perform (s);
483 bool init()
485 SRB_ExecSCSICmd s;
486 s.SRB_Status = SS_ERR;
488 if (deviceInfo->readType == READTYPE_READ10_2)
490 BYTE bufPointer1[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 35, 6, 0, 0, 0, 0, 0, 128 };
491 BYTE bufPointer2[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 1, 6, 32, 7, 0, 0, 0, 0 };
493 for (int i = 0; i < 2; ++i)
495 prepare (s);
496 s.SRB_Flags = SRB_EVENT_NOTIFY;
497 s.SRB_BufLen = 0x14;
498 s.SRB_BufPointer = (i == 0) ? bufPointer1 : bufPointer2;
499 s.SRB_CDBLen = 6;
500 s.CDBByte[0] = 0x15;
501 s.CDBByte[1] = 0x10;
502 s.CDBByte[4] = 0x14;
503 perform (s);
505 if (s.SRB_Status != SS_COMP)
506 return false;
509 else
511 BYTE bufPointer[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48 };
513 prepare (s);
514 s.SRB_Flags = SRB_EVENT_NOTIFY;
515 s.SRB_BufLen = 0x0C;
516 s.SRB_BufPointer = bufPointer;
517 s.SRB_CDBLen = 6;
518 s.CDBByte[0] = 0x15;
519 s.CDBByte[4] = 0x0C;
520 perform (s);
523 return s.SRB_Status == SS_COMP;
526 bool read (CDReadBuffer& rb)
528 if (rb.numFrames * 2352 > rb.bufferSize)
529 return false;
531 if (! initialised)
533 initialised = init();
535 if (! initialised)
536 return false;
539 SRB_ExecSCSICmd s;
540 prepare (s);
541 s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
542 s.SRB_BufLen = rb.bufferSize;
543 s.SRB_BufPointer = rb.buffer;
544 s.SRB_CDBLen = 10;
545 s.CDBByte[0] = 0x28;
546 s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5);
547 s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
548 s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
549 s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
550 s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
551 perform (s);
553 if (s.SRB_Status != SS_COMP)
554 return false;
556 rb.dataLength = rb.numFrames * 2352;
557 rb.dataStartOffset = 0;
558 return true;
562 //==============================================================================
563 class ControllerType3 : public CDController
565 public:
566 ControllerType3() {}
568 bool read (CDReadBuffer& rb)
570 if (rb.numFrames * 2352 > rb.bufferSize)
571 return false;
573 if (! initialised)
575 setPaused (false);
576 initialised = true;
579 SRB_ExecSCSICmd s;
580 prepare (s);
581 s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
582 s.SRB_BufLen = rb.numFrames * 2352;
583 s.SRB_BufPointer = rb.buffer;
584 s.SRB_CDBLen = 12;
585 s.CDBByte[0] = 0xD8;
586 s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
587 s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
588 s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
589 s.CDBByte[9] = (BYTE) (rb.numFrames & 0xFF);
590 perform (s);
592 if (s.SRB_Status != SS_COMP)
593 return false;
595 rb.dataLength = rb.numFrames * 2352;
596 rb.dataStartOffset = 0;
597 return true;
601 //==============================================================================
602 class ControllerType4 : public CDController
604 public:
605 ControllerType4() {}
607 bool selectD4Mode()
609 BYTE bufPointer[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 48 };
611 SRB_ExecSCSICmd s;
612 prepare (s);
613 s.SRB_Flags = SRB_EVENT_NOTIFY;
614 s.SRB_CDBLen = 6;
615 s.SRB_BufLen = 12;
616 s.SRB_BufPointer = bufPointer;
617 s.CDBByte[0] = 0x15;
618 s.CDBByte[1] = 0x10;
619 s.CDBByte[4] = 0x08;
620 perform (s);
622 return s.SRB_Status == SS_COMP;
625 bool read (CDReadBuffer& rb)
627 if (rb.numFrames * 2352 > rb.bufferSize)
628 return false;
630 if (! initialised)
632 setPaused (true);
634 if (deviceInfo->readType == READTYPE_READ_D4_1)
635 selectD4Mode();
637 initialised = true;
640 SRB_ExecSCSICmd s;
641 prepare (s);
642 s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
643 s.SRB_BufLen = rb.bufferSize;
644 s.SRB_BufPointer = rb.buffer;
645 s.SRB_CDBLen = 10;
646 s.CDBByte[0] = 0xD4;
647 s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
648 s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
649 s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
650 s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
651 perform (s);
653 if (s.SRB_Status != SS_COMP)
654 return false;
656 rb.dataLength = rb.numFrames * 2352;
657 rb.dataStartOffset = 0;
658 return true;
663 //==============================================================================
664 void CDController::prepare (SRB_ExecSCSICmd& s)
666 zerostruct (s);
667 s.SRB_Cmd = SC_EXEC_SCSI_CMD;
668 s.SRB_HaID = deviceInfo->info.ha;
669 s.SRB_Target = deviceInfo->info.tgt;
670 s.SRB_Lun = deviceInfo->info.lun;
671 s.SRB_SenseLen = SENSE_LEN;
674 void CDController::perform (SRB_ExecSCSICmd& s)
676 s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
678 deviceInfo->performScsiCommand (s.SRB_PostProc, s);
681 void CDController::setPaused (bool paused)
683 SRB_ExecSCSICmd s;
684 prepare (s);
685 s.SRB_Flags = SRB_EVENT_NOTIFY;
686 s.SRB_CDBLen = 10;
687 s.CDBByte[0] = 0x4B;
688 s.CDBByte[8] = (BYTE) (paused ? 0 : 1);
689 perform (s);
692 bool CDController::readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer)
694 if (overlapBuffer != nullptr)
696 const bool canDoJitter = (overlapBuffer->bufferSize >= 2352 * framesToCheck);
697 const bool doJitter = canDoJitter && ! overlapBuffer->isZero();
699 if (doJitter
700 && overlapBuffer->startFrame > 0
701 && overlapBuffer->numFrames > 0
702 && overlapBuffer->dataLength > 0)
704 const int numFrames = rb.numFrames;
706 if (overlapBuffer->startFrame == (rb.startFrame - framesToCheck))
708 rb.startFrame -= framesOverlap;
710 if (framesToCheck < framesOverlap
711 && numFrames + framesOverlap <= rb.bufferSize / 2352)
712 rb.numFrames += framesOverlap;
714 else
716 overlapBuffer->dataLength = 0;
717 overlapBuffer->startFrame = 0;
718 overlapBuffer->numFrames = 0;
722 if (! read (rb))
723 return false;
725 if (doJitter)
727 const int checkLen = framesToCheck * 2352;
728 const int maxToCheck = rb.dataLength - checkLen;
730 if (overlapBuffer->dataLength == 0 || overlapBuffer->isZero())
731 return true;
733 BYTE* const p = overlapBuffer->buffer + overlapBuffer->dataStartOffset;
734 bool found = false;
736 for (int i = 0; i < maxToCheck; ++i)
738 if (memcmp (p, rb.buffer + i, checkLen) == 0)
740 i += checkLen;
741 rb.dataStartOffset = i;
742 rb.dataLength -= i;
743 rb.startFrame = overlapBuffer->startFrame + framesToCheck;
744 found = true;
745 break;
749 rb.numFrames = rb.dataLength / 2352;
750 rb.dataLength = 2352 * rb.numFrames;
752 if (! found)
753 return false;
756 if (canDoJitter)
758 memcpy (overlapBuffer->buffer,
759 rb.buffer + rb.dataStartOffset + 2352 * (rb.numFrames - framesToCheck),
760 2352 * framesToCheck);
762 overlapBuffer->startFrame = rb.startFrame + rb.numFrames - framesToCheck;
763 overlapBuffer->numFrames = framesToCheck;
764 overlapBuffer->dataLength = 2352 * framesToCheck;
765 overlapBuffer->dataStartOffset = 0;
767 else
769 overlapBuffer->startFrame = 0;
770 overlapBuffer->numFrames = 0;
771 overlapBuffer->dataLength = 0;
774 return true;
777 return read (rb);
780 int CDController::getLastIndex()
782 char qdata[100];
784 SRB_ExecSCSICmd s;
785 prepare (s);
786 s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
787 s.SRB_BufLen = sizeof (qdata);
788 s.SRB_BufPointer = (BYTE*) qdata;
789 s.SRB_CDBLen = 12;
790 s.CDBByte[0] = 0x42;
791 s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5);
792 s.CDBByte[2] = 64;
793 s.CDBByte[3] = 1; // get current position
794 s.CDBByte[7] = 0;
795 s.CDBByte[8] = (BYTE) sizeof (qdata);
796 perform (s);
798 return s.SRB_Status == SS_COMP ? qdata[7] : 0;
801 //==============================================================================
802 bool CDDeviceHandle::readTOC (TOC* lpToc)
804 SRB_ExecSCSICmd s = { 0 };
805 s.SRB_Cmd = SC_EXEC_SCSI_CMD;
806 s.SRB_HaID = info.ha;
807 s.SRB_Target = info.tgt;
808 s.SRB_Lun = info.lun;
809 s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
810 s.SRB_BufLen = 0x324;
811 s.SRB_BufPointer = (BYTE*) lpToc;
812 s.SRB_SenseLen = 0x0E;
813 s.SRB_CDBLen = 0x0A;
814 s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
815 s.CDBByte[0] = 0x43;
816 s.CDBByte[1] = 0x00;
817 s.CDBByte[7] = 0x03;
818 s.CDBByte[8] = 0x24;
820 performScsiCommand (s.SRB_PostProc, s);
821 return (s.SRB_Status == SS_COMP);
824 void CDDeviceHandle::performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s)
826 ResetEvent (event);
827 DWORD status = performScsiPassThroughCommand ((SRB_ExecSCSICmd*) &s, info.scsiDriveLetter, scsiHandle, true);
829 if (status == SS_PENDING)
830 WaitForSingleObject (event, 4000);
832 CloseHandle (event);
835 bool CDDeviceHandle::readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer)
837 if (controller == 0)
839 testController (READTYPE_ATAPI2, new ControllerType1(), buffer)
840 || testController (READTYPE_ATAPI1, new ControllerType1(), buffer)
841 || testController (READTYPE_READ10_2, new ControllerType2(), buffer)
842 || testController (READTYPE_READ10, new ControllerType2(), buffer)
843 || testController (READTYPE_READ_D8, new ControllerType3(), buffer)
844 || testController (READTYPE_READ_D4, new ControllerType4(), buffer)
845 || testController (READTYPE_READ_D4_1, new ControllerType4(), buffer);
848 buffer.index = 0;
850 if (controller != nullptr && controller->readAudio (buffer, overlapBuffer))
852 if (buffer.wantsIndex)
853 buffer.index = controller->getLastIndex();
855 return true;
858 return false;
861 void CDDeviceHandle::openDrawer (bool shouldBeOpen)
863 if (shouldBeOpen)
865 if (controller != nullptr)
867 controller->shutDown();
868 controller = nullptr;
871 if (scsiHandle != 0)
873 CloseHandle (scsiHandle);
874 scsiHandle = 0;
878 SRB_ExecSCSICmd s = { 0 };
879 s.SRB_Cmd = SC_EXEC_SCSI_CMD;
880 s.SRB_HaID = info.ha;
881 s.SRB_Target = info.tgt;
882 s.SRB_Lun = info.lun;
883 s.SRB_SenseLen = SENSE_LEN;
884 s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
885 s.SRB_BufLen = 0;
886 s.SRB_BufPointer = 0;
887 s.SRB_CDBLen = 12;
888 s.CDBByte[0] = 0x1b;
889 s.CDBByte[1] = (BYTE) (info.lun << 5);
890 s.CDBByte[4] = (BYTE) (shouldBeOpen ? 2 : 3);
891 s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
893 performScsiCommand (s.SRB_PostProc, s);
896 bool CDDeviceHandle::testController (const int type, CDController* const newController, CDReadBuffer& rb)
898 controller = newController;
899 readType = (BYTE) type;
901 controller->deviceInfo = this;
902 controller->framesToCheck = 1;
903 controller->framesOverlap = 3;
905 bool passed = false;
906 memset (rb.buffer, 0xcd, rb.bufferSize);
908 if (controller->read (rb))
910 passed = true;
911 int* p = (int*) (rb.buffer + rb.dataStartOffset);
912 int wrong = 0;
914 for (int i = rb.dataLength / 4; --i >= 0;)
916 if (*p++ == (int) 0xcdcdcdcd)
918 if (++wrong == 4)
920 passed = false;
921 break;
924 else
926 wrong = 0;
931 if (! passed)
933 controller->shutDown();
934 controller = nullptr;
937 return passed;
941 //==============================================================================
942 struct CDDeviceWrapper
944 CDDeviceWrapper (const CDDeviceDescription& device, HANDLE scsiHandle)
945 : deviceHandle (device, scsiHandle), overlapBuffer (3), jitter (false)
947 // xxx jitter never seemed to actually be enabled (??)
950 CDDeviceHandle deviceHandle;
951 CDReadBuffer overlapBuffer;
952 bool jitter;
955 //==============================================================================
956 int getAddressOfTrack (const TOCTRACK& t) noexcept
958 return (((DWORD) t.addr[0]) << 24) + (((DWORD) t.addr[1]) << 16)
959 + (((DWORD) t.addr[2]) << 8) + ((DWORD) t.addr[3]);
962 const int samplesPerFrame = 44100 / 75;
963 const int bytesPerFrame = samplesPerFrame * 4;
964 const int framesPerIndexRead = 4;
968 //==============================================================================
969 StringArray AudioCDReader::getAvailableCDNames()
971 using namespace CDReaderHelpers;
972 StringArray results;
974 Array<CDDeviceDescription> list;
975 findCDDevices (list);
977 for (int i = 0; i < list.size(); ++i)
979 String s;
980 if (list[i].scsiDriveLetter > 0)
981 s << String::charToString (list[i].scsiDriveLetter).toUpperCase() << ": ";
983 s << list[i].description;
984 results.add (s);
987 return results;
990 AudioCDReader* AudioCDReader::createReaderForCD (const int deviceIndex)
992 using namespace CDReaderHelpers;
994 Array<CDDeviceDescription> list;
995 findCDDevices (list);
997 if (isPositiveAndBelow (deviceIndex, list.size()))
999 HANDLE h = createSCSIDeviceHandle (list [deviceIndex].scsiDriveLetter);
1001 if (h != INVALID_HANDLE_VALUE)
1002 return new AudioCDReader (new CDDeviceWrapper (list [deviceIndex], h));
1005 return nullptr;
1008 AudioCDReader::AudioCDReader (void* handle_)
1009 : AudioFormatReader (0, "CD Audio"),
1010 handle (handle_),
1011 indexingEnabled (false),
1012 lastIndex (0),
1013 firstFrameInBuffer (0),
1014 samplesInBuffer (0)
1016 using namespace CDReaderHelpers;
1017 jassert (handle_ != nullptr);
1019 refreshTrackLengths();
1021 sampleRate = 44100.0;
1022 bitsPerSample = 16;
1023 numChannels = 2;
1024 usesFloatingPointData = false;
1026 buffer.setSize (4 * bytesPerFrame, true);
1029 AudioCDReader::~AudioCDReader()
1031 using namespace CDReaderHelpers;
1032 CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
1033 delete device;
1036 bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
1037 int64 startSampleInFile, int numSamples)
1039 using namespace CDReaderHelpers;
1040 CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
1042 bool ok = true;
1044 while (numSamples > 0)
1046 const int bufferStartSample = firstFrameInBuffer * samplesPerFrame;
1047 const int bufferEndSample = bufferStartSample + samplesInBuffer;
1049 if (startSampleInFile >= bufferStartSample
1050 && startSampleInFile < bufferEndSample)
1052 const int toDo = (int) jmin ((int64) numSamples, bufferEndSample - startSampleInFile);
1054 int* const l = destSamples[0] + startOffsetInDestBuffer;
1055 int* const r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr;
1056 const short* src = (const short*) buffer.getData();
1057 src += 2 * (startSampleInFile - bufferStartSample);
1059 for (int i = 0; i < toDo; ++i)
1061 l[i] = src [i << 1] << 16;
1063 if (r != nullptr)
1064 r[i] = src [(i << 1) + 1] << 16;
1067 startOffsetInDestBuffer += toDo;
1068 startSampleInFile += toDo;
1069 numSamples -= toDo;
1071 else
1073 const int framesInBuffer = buffer.getSize() / bytesPerFrame;
1074 const int frameNeeded = (int) (startSampleInFile / samplesPerFrame);
1076 if (firstFrameInBuffer + framesInBuffer != frameNeeded)
1078 device->overlapBuffer.dataLength = 0;
1079 device->overlapBuffer.startFrame = 0;
1080 device->overlapBuffer.numFrames = 0;
1081 device->jitter = false;
1084 firstFrameInBuffer = frameNeeded;
1085 lastIndex = 0;
1087 CDReadBuffer readBuffer (framesInBuffer + 4);
1088 readBuffer.wantsIndex = indexingEnabled;
1090 int i;
1091 for (i = 5; --i >= 0;)
1093 readBuffer.startFrame = frameNeeded;
1094 readBuffer.numFrames = framesInBuffer;
1096 if (device->deviceHandle.readAudio (readBuffer, device->jitter ? &device->overlapBuffer : 0))
1097 break;
1098 else
1099 device->overlapBuffer.dataLength = 0;
1102 if (i >= 0)
1104 buffer.copyFrom (readBuffer.buffer + readBuffer.dataStartOffset, 0, readBuffer.dataLength);
1105 samplesInBuffer = readBuffer.dataLength >> 2;
1106 lastIndex = readBuffer.index;
1108 else
1110 int* l = destSamples[0] + startOffsetInDestBuffer;
1111 int* r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr;
1113 while (--numSamples >= 0)
1115 *l++ = 0;
1117 if (r != nullptr)
1118 *r++ = 0;
1121 // sometimes the read fails for just the very last couple of blocks, so
1122 // we'll ignore and errors in the last half-second of the disk..
1123 ok = startSampleInFile > (trackStartSamples [getNumTracks()] - 20000);
1124 break;
1129 return ok;
1132 bool AudioCDReader::isCDStillPresent() const
1134 using namespace CDReaderHelpers;
1135 TOC toc = { 0 };
1136 return static_cast <CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc);
1139 void AudioCDReader::refreshTrackLengths()
1141 using namespace CDReaderHelpers;
1142 trackStartSamples.clear();
1143 zeromem (audioTracks, sizeof (audioTracks));
1145 TOC toc = { 0 };
1147 if (static_cast <CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc))
1149 int numTracks = 1 + toc.lastTrack - toc.firstTrack;
1151 for (int i = 0; i <= numTracks; ++i)
1153 trackStartSamples.add (samplesPerFrame * getAddressOfTrack (toc.tracks [i]));
1154 audioTracks [i] = ((toc.tracks[i].ADR & 4) == 0);
1158 lengthInSamples = getPositionOfTrackStart (getNumTracks());
1161 bool AudioCDReader::isTrackAudio (int trackNum) const
1163 return trackNum >= 0 && trackNum < getNumTracks() && audioTracks [trackNum];
1166 void AudioCDReader::enableIndexScanning (bool b)
1168 indexingEnabled = b;
1171 int AudioCDReader::getLastIndex() const
1173 return lastIndex;
1176 int AudioCDReader::getIndexAt (int samplePos)
1178 using namespace CDReaderHelpers;
1179 CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
1181 const int frameNeeded = samplePos / samplesPerFrame;
1183 device->overlapBuffer.dataLength = 0;
1184 device->overlapBuffer.startFrame = 0;
1185 device->overlapBuffer.numFrames = 0;
1186 device->jitter = false;
1188 firstFrameInBuffer = 0;
1189 lastIndex = 0;
1191 CDReadBuffer readBuffer (4 + framesPerIndexRead);
1192 readBuffer.wantsIndex = true;
1194 int i;
1195 for (i = 5; --i >= 0;)
1197 readBuffer.startFrame = frameNeeded;
1198 readBuffer.numFrames = framesPerIndexRead;
1200 if (device->deviceHandle.readAudio (readBuffer))
1201 break;
1204 if (i >= 0)
1205 return readBuffer.index;
1207 return -1;
1210 const Array <int> AudioCDReader::findIndexesInTrack (const int trackNumber)
1212 using namespace CDReaderHelpers;
1213 Array <int> indexes;
1215 const int trackStart = getPositionOfTrackStart (trackNumber);
1216 const int trackEnd = getPositionOfTrackStart (trackNumber + 1);
1218 bool needToScan = true;
1220 if (trackEnd - trackStart > 20 * 44100)
1222 // check the end of the track for indexes before scanning the whole thing
1223 needToScan = false;
1224 int pos = jmax (trackStart, trackEnd - 44100 * 5);
1225 bool seenAnIndex = false;
1227 while (pos <= trackEnd - samplesPerFrame)
1229 const int index = getIndexAt (pos);
1231 if (index == 0)
1233 // lead-out, so skip back a bit if we've not found any indexes yet..
1234 if (seenAnIndex)
1235 break;
1237 pos -= 44100 * 5;
1239 if (pos < trackStart)
1240 break;
1242 else
1244 if (index > 0)
1245 seenAnIndex = true;
1247 if (index > 1)
1249 needToScan = true;
1250 break;
1253 pos += samplesPerFrame * framesPerIndexRead;
1258 if (needToScan)
1260 CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
1262 int pos = trackStart;
1263 int last = -1;
1265 while (pos < trackEnd - samplesPerFrame * 10)
1267 const int frameNeeded = pos / samplesPerFrame;
1269 device->overlapBuffer.dataLength = 0;
1270 device->overlapBuffer.startFrame = 0;
1271 device->overlapBuffer.numFrames = 0;
1272 device->jitter = false;
1274 firstFrameInBuffer = 0;
1276 CDReadBuffer readBuffer (4);
1277 readBuffer.wantsIndex = true;
1279 int i;
1280 for (i = 5; --i >= 0;)
1282 readBuffer.startFrame = frameNeeded;
1283 readBuffer.numFrames = framesPerIndexRead;
1285 if (device->deviceHandle.readAudio (readBuffer))
1286 break;
1289 if (i < 0)
1290 break;
1292 if (readBuffer.index > last && readBuffer.index > 1)
1294 last = readBuffer.index;
1295 indexes.add (pos);
1298 pos += samplesPerFrame * framesPerIndexRead;
1301 indexes.removeValue (trackStart);
1304 return indexes;
1307 void AudioCDReader::ejectDisk()
1309 using namespace CDReaderHelpers;
1310 static_cast <CDDeviceWrapper*> (handle)->deviceHandle.openDrawer (true);
1313 #endif
1315 #if JUCE_USE_CDBURNER
1317 //==============================================================================
1318 namespace CDBurnerHelpers
1320 IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master)
1322 CoInitialize (0);
1324 IDiscMaster* dm;
1325 IDiscRecorder* result = nullptr;
1327 if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0,
1328 CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
1329 IID_IDiscMaster,
1330 (void**) &dm)))
1332 if (SUCCEEDED (dm->Open()))
1334 IEnumDiscRecorders* drEnum = nullptr;
1336 if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum)))
1338 IDiscRecorder* dr = nullptr;
1339 DWORD dummy;
1340 int index = 0;
1342 while (drEnum->Next (1, &dr, &dummy) == S_OK)
1344 if (indexToOpen == index)
1346 result = dr;
1347 break;
1349 else if (list != nullptr)
1351 BSTR path;
1353 if (SUCCEEDED (dr->GetPath (&path)))
1354 list->add ((const WCHAR*) path);
1357 ++index;
1358 dr->Release();
1361 drEnum->Release();
1364 if (master == 0)
1365 dm->Close();
1368 if (master != nullptr)
1369 *master = dm;
1370 else
1371 dm->Release();
1374 return result;
1378 //==============================================================================
1379 class AudioCDBurner::Pimpl : public ComBaseClassHelper <IDiscMasterProgressEvents>,
1380 public Timer
1382 public:
1383 Pimpl (AudioCDBurner& owner_, IDiscMaster* discMaster_, IDiscRecorder* discRecorder_)
1384 : owner (owner_), discMaster (discMaster_), discRecorder (discRecorder_), redbook (0),
1385 listener (0), progress (0), shouldCancel (false)
1387 HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook);
1388 jassert (SUCCEEDED (hr));
1389 hr = discMaster->SetActiveDiscRecorder (discRecorder);
1390 //jassert (SUCCEEDED (hr));
1392 lastState = getDiskState();
1393 startTimer (2000);
1396 ~Pimpl() {}
1398 void releaseObjects()
1400 discRecorder->Close();
1401 if (redbook != nullptr)
1402 redbook->Release();
1403 discRecorder->Release();
1404 discMaster->Release();
1405 Release();
1408 HRESULT __stdcall QueryCancel (boolean* pbCancel)
1410 if (listener != nullptr && ! shouldCancel)
1411 shouldCancel = listener->audioCDBurnProgress (progress);
1413 *pbCancel = shouldCancel;
1415 return S_OK;
1418 HRESULT __stdcall NotifyBlockProgress (long nCompleted, long nTotal)
1420 progress = nCompleted / (float) nTotal;
1421 shouldCancel = listener != nullptr && listener->audioCDBurnProgress (progress);
1423 return E_NOTIMPL;
1426 HRESULT __stdcall NotifyPnPActivity (void) { return E_NOTIMPL; }
1427 HRESULT __stdcall NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; }
1428 HRESULT __stdcall NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; }
1429 HRESULT __stdcall NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; }
1430 HRESULT __stdcall NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; }
1431 HRESULT __stdcall NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; }
1432 HRESULT __stdcall NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; }
1434 class ScopedDiscOpener
1436 public:
1437 ScopedDiscOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); }
1438 ~ScopedDiscOpener() { pimpl.discRecorder->Close(); }
1440 private:
1441 Pimpl& pimpl;
1443 JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener);
1446 DiskState getDiskState()
1448 const ScopedDiscOpener opener (*this);
1450 long type, flags;
1451 HRESULT hr = discRecorder->QueryMediaType (&type, &flags);
1453 if (FAILED (hr))
1454 return unknown;
1456 if (type != 0 && (flags & MEDIA_WRITABLE) != 0)
1457 return writableDiskPresent;
1459 if (type == 0)
1460 return noDisc;
1461 else
1462 return readOnlyDiskPresent;
1465 int getIntProperty (const LPOLESTR name, const int defaultReturn) const
1467 ComSmartPtr<IPropertyStorage> prop;
1468 if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress())))
1469 return defaultReturn;
1471 PROPSPEC iPropSpec;
1472 iPropSpec.ulKind = PRSPEC_LPWSTR;
1473 iPropSpec.lpwstr = name;
1475 PROPVARIANT iPropVariant;
1476 return FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant))
1477 ? defaultReturn : (int) iPropVariant.lVal;
1480 bool setIntProperty (const LPOLESTR name, const int value) const
1482 ComSmartPtr<IPropertyStorage> prop;
1483 if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress())))
1484 return false;
1486 PROPSPEC iPropSpec;
1487 iPropSpec.ulKind = PRSPEC_LPWSTR;
1488 iPropSpec.lpwstr = name;
1490 PROPVARIANT iPropVariant;
1491 if (FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant)))
1492 return false;
1494 iPropVariant.lVal = (long) value;
1495 return SUCCEEDED (prop->WriteMultiple (1, &iPropSpec, &iPropVariant, iPropVariant.vt))
1496 && SUCCEEDED (discRecorder->SetRecorderProperties (prop));
1499 void timerCallback()
1501 const DiskState state = getDiskState();
1503 if (state != lastState)
1505 lastState = state;
1506 owner.sendChangeMessage();
1510 AudioCDBurner& owner;
1511 DiskState lastState;
1512 IDiscMaster* discMaster;
1513 IDiscRecorder* discRecorder;
1514 IRedbookDiscMaster* redbook;
1515 AudioCDBurner::BurnProgressListener* listener;
1516 float progress;
1517 bool shouldCancel;
1520 //==============================================================================
1521 AudioCDBurner::AudioCDBurner (const int deviceIndex)
1523 IDiscMaster* discMaster = nullptr;
1524 IDiscRecorder* discRecorder = CDBurnerHelpers::enumCDBurners (0, deviceIndex, &discMaster);
1526 if (discRecorder != nullptr)
1527 pimpl = new Pimpl (*this, discMaster, discRecorder);
1530 AudioCDBurner::~AudioCDBurner()
1532 if (pimpl != nullptr)
1533 pimpl.release()->releaseObjects();
1536 StringArray AudioCDBurner::findAvailableDevices()
1538 StringArray devs;
1539 CDBurnerHelpers::enumCDBurners (&devs, -1, 0);
1540 return devs;
1543 AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
1545 ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex));
1547 if (b->pimpl == 0)
1548 b = nullptr;
1550 return b.release();
1553 AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
1555 return pimpl->getDiskState();
1558 bool AudioCDBurner::isDiskPresent() const
1560 return getDiskState() == writableDiskPresent;
1563 bool AudioCDBurner::openTray()
1565 const Pimpl::ScopedDiscOpener opener (*pimpl);
1566 return SUCCEEDED (pimpl->discRecorder->Eject());
1569 AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds)
1571 const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds;
1572 DiskState oldState = getDiskState();
1573 DiskState newState = oldState;
1575 while (newState == oldState && Time::currentTimeMillis() < timeout)
1577 newState = getDiskState();
1578 Thread::sleep (jmin (250, (int) (timeout - Time::currentTimeMillis())));
1581 return newState;
1584 Array<int> AudioCDBurner::getAvailableWriteSpeeds() const
1586 Array<int> results;
1587 const int maxSpeed = pimpl->getIntProperty (L"MaxWriteSpeed", 1);
1588 const int speeds[] = { 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 64, 80 };
1590 for (int i = 0; i < numElementsInArray (speeds); ++i)
1591 if (speeds[i] <= maxSpeed)
1592 results.add (speeds[i]);
1594 results.addIfNotAlreadyThere (maxSpeed);
1595 return results;
1598 bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled)
1600 if (pimpl->getIntProperty (L"BufferUnderrunFreeCapable", 0) == 0)
1601 return false;
1603 pimpl->setIntProperty (L"EnableBufferUnderrunFree", shouldBeEnabled ? -1 : 0);
1604 return pimpl->getIntProperty (L"EnableBufferUnderrunFree", 0) != 0;
1607 int AudioCDBurner::getNumAvailableAudioBlocks() const
1609 long blocksFree = 0;
1610 pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
1611 return blocksFree;
1614 String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards,
1615 bool performFakeBurnForTesting, int writeSpeed)
1617 pimpl->setIntProperty (L"WriteSpeed", writeSpeed > 0 ? writeSpeed : -1);
1619 pimpl->listener = listener;
1620 pimpl->progress = 0;
1621 pimpl->shouldCancel = false;
1623 UINT_PTR cookie;
1624 HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie);
1626 hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting,
1627 ejectDiscAfterwards);
1629 String error;
1630 if (hr != S_OK)
1632 const char* e = "Couldn't open or write to the CD device";
1634 if (hr == IMAPI_E_USERABORT)
1635 e = "User cancelled the write operation";
1636 else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN)
1637 e = "No Disk present";
1639 error = e;
1642 pimpl->discMaster->ProgressUnadvise (cookie);
1643 pimpl->listener = 0;
1645 return error;
1648 bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples)
1650 if (audioSource == 0)
1651 return false;
1653 ScopedPointer<AudioSource> source (audioSource);
1655 long bytesPerBlock;
1656 HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock);
1658 const int samplesPerBlock = bytesPerBlock / 4;
1659 bool ok = true;
1661 hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
1663 HeapBlock <byte> buffer (bytesPerBlock);
1664 AudioSampleBuffer sourceBuffer (2, samplesPerBlock);
1665 int samplesDone = 0;
1667 source->prepareToPlay (samplesPerBlock, 44100.0);
1669 while (ok)
1672 AudioSourceChannelInfo info;
1673 info.buffer = &sourceBuffer;
1674 info.numSamples = samplesPerBlock;
1675 info.startSample = 0;
1676 sourceBuffer.clear();
1678 source->getNextAudioBlock (info);
1681 buffer.clear (bytesPerBlock);
1683 typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian,
1684 AudioData::Interleaved, AudioData::NonConst> CDSampleFormat;
1686 typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian,
1687 AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat;
1689 CDSampleFormat left (buffer, 2);
1690 left.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (0)), samplesPerBlock);
1691 CDSampleFormat right (buffer + 2, 2);
1692 right.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (1)), samplesPerBlock);
1694 hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock);
1696 if (FAILED (hr))
1697 ok = false;
1699 samplesDone += samplesPerBlock;
1701 if (samplesDone >= numSamples)
1702 break;
1705 hr = pimpl->redbook->CloseAudioTrack();
1706 return ok && hr == S_OK;
1709 #endif
1710 #endif