Browse Source

Added some new functionality to AudioCDBurner.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
250231a91f
6 changed files with 850 additions and 283 deletions
  1. +3
    -3
      extras/the jucer/src/model/jucer_BinaryResources.cpp
  2. +428
    -155
      juce_amalgamated.cpp
  3. +30
    -5
      juce_amalgamated.h
  4. +72
    -9
      src/audio/audio_file_formats/juce_AudioCDBurner.h
  5. +173
    -49
      src/native/mac/juce_mac_AudioCDBurner.mm
  6. +144
    -62
      src/native/windows/juce_win32_AudioCDReader.cpp

+ 3
- 3
extras/the jucer/src/model/jucer_BinaryResources.cpp View File

@@ -295,7 +295,7 @@ void BinaryResources::fillInGeneratedCode (GeneratedCode& code) const
const String name (resources[i]->name);
const MemoryBlock& mb = resources[i]->data;
defs << "// JUCER_RESOURCE: " << name << ", " << mb.getSize()
defs << "// JUCER_RESOURCE: " << name << ", " << (int) mb.getSize()
<< ", \""
<< File (resources[i]->originalFilename)
.getRelativePathFrom (code.document->getFile())
@@ -311,7 +311,7 @@ void BinaryResources::fillInGeneratedCode (GeneratedCode& code) const
MemoryOutputStream out (65536, 16384);
int charsOnLine = line1.length();
for (int j = 0; j < mb.getSize(); ++j)
for (size_t j = 0; j < mb.getSize(); ++j)
{
const int num = ((int) (unsigned char) mb[j]);
out << num << ',';
@@ -338,7 +338,7 @@ void BinaryResources::fillInGeneratedCode (GeneratedCode& code) const
<< " = (const char*) resource_" << code.className << "_" << name
<< ";\nconst int "
<< code.className << "::" << name << "Size = "
<< mb.getSize()
<< (int) mb.getSize()
<< ";\n\n";
}


+ 428
- 155
juce_amalgamated.cpp View File

@@ -5902,10 +5902,14 @@ static const String parseAbsolutePath (String path)
{
if (path[1] != File::separator)
{
jassertfalse // using a filename that starts with a slash is a bit dodgy on
// Windows, because it needs a drive letter, which in this case
// we'll take from the CWD.. but this is a bit of an assumption that
// could be wrong..
/* When you supply a raw string to the File object constructor, it must be an absolute path.
If you're trying to parse a string that may be either a relative path or an absolute path,
you MUST provide a context against which the partial path can be evaluated - you can do
this by simply using File::getChildFile() instead of the File constructor. E.g. saying
"File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
path if that's what was supplied, or would evaluate a partial path relative to the CWD.
*/
jassertfalse

path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path;
}
@@ -5915,10 +5919,14 @@ static const String parseAbsolutePath (String path)
if (path.isEmpty())
return String::empty;

jassertfalse // using a partial filename is a bad way to initialise a file, because
// we don't know what directory to put it in.
// Here we'll assume it's in the CWD, but this might not be what was
// intended..
/* When you supply a raw string to the File object constructor, it must be an absolute path.
If you're trying to parse a string that may be either a relative path or an absolute path,
you MUST provide a context against which the partial path can be evaluated - you can do
this by simply using File::getChildFile() instead of the File constructor. E.g. saying
"File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
path if that's what was supplied, or would evaluate a partial path relative to the CWD.
*/
jassertfalse

return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
}
@@ -5957,16 +5965,14 @@ static const String parseAbsolutePath (String path)
}
else if (! path.startsWithChar (File::separator))
{
while (path.startsWith (T("./")))
path = path.substring (2);

if (path.isEmpty())
return String::empty;

jassertfalse // using a partial filename is a bad way to initialise a file, because
// we don't know what directory to put it in.
// Here we'll assume it's in the CWD, but this might not be what was
// intended..
/* When you supply a raw string to the File object constructor, it must be an absolute path.
If you're trying to parse a string that may be either a relative path or an absolute path,
you MUST provide a context against which the partial path can be evaluated - you can do
this by simply using File::getChildFile() instead of the File constructor. E.g. saying
"File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
path if that's what was supplied, or would evaluate a partial path relative to the CWD.
*/
jassert (path.startsWith (T("./"))); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD)

return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
}
@@ -21866,6 +21872,14 @@ struct SMPLChunk
}
} PACKED;

struct ExtensibleWavSubFormat
{
uint32 data1;
uint16 data2;
uint16 data3;
uint8 data4[8];
} PACKED;

#if JUCE_MSVC
#pragma pack (pop)
#endif
@@ -21910,7 +21924,7 @@ public:
if (chunkType == chunkName ("fmt "))
{
// read the format chunk
const short format = input->readShort();
const unsigned short format = input->readShort();
const short numChans = input->readShort();
sampleRate = input->readInt();
const int bytesPerSec = input->readInt();
@@ -21920,9 +21934,41 @@ public:
bitsPerSample = 8 * bytesPerFrame / numChans;

if (format == 3)
{
usesFloatingPointData = true;
}
else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
{
if (length < 40) // too short
{
bytesPerFrame = 0;
}
else
{
input->skipNextBytes (12); // skip over blockAlign, bitsPerSample and speakerPosition mask
ExtensibleWavSubFormat subFormat;
subFormat.data1 = input->readInt();
subFormat.data2 = input->readShort();
subFormat.data3 = input->readShort();
input->read (subFormat.data4, sizeof (subFormat.data4));

const ExtensibleWavSubFormat pcmFormat
= { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };

if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0)
{
const ExtensibleWavSubFormat ambisonicFormat
= { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } };

if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0)
bytesPerFrame = 0;
}
}
}
else if (format != 1)
{
bytesPerFrame = 0;
}

hasGotType = true;
}
@@ -221857,28 +221903,23 @@ const StringArray AudioCDBurner::findAvailableDevices()

AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
{
AudioCDBurner* b = new AudioCDBurner (deviceIndex);
ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex));

if (b->internal == 0)
deleteAndZero (b);
if (b->pimpl == 0)
b = 0;

return b;
return b.release();
}

class CDBurnerInfo : public IDiscMasterProgressEvents
class AudioCDBurner::Pimpl : public IDiscMasterProgressEvents
{
public:
CDBurnerInfo()
: refCount (1),
progress (0),
shouldCancel (false),
listener (0)
Pimpl()
: listener (0), progress (0), shouldCancel (false), refCount (1)
{
}

~CDBurnerInfo()
{
}
~Pimpl() {}

HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result)
{
@@ -221925,6 +221966,50 @@ public:
HRESULT __stdcall NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; }
HRESULT __stdcall NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; }

int getIntProperty (const LPOLESTR name, const int defaultReturn) const
{
ComSmartPtr<IPropertyStorage> prop;
if (FAILED (discRecorder->GetRecorderProperties (&prop)))
return defaultReturn;

PROPSPEC iPropSpec;
iPropSpec.ulKind = PRSPEC_LPWSTR;
iPropSpec.lpwstr = name;

PROPVARIANT iPropVariant;
return FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant))
? defaultReturn : (int) iPropVariant.lVal;
}

bool setIntProperty (const LPOLESTR name, const int value) const
{
ComSmartPtr<IPropertyStorage> prop;
if (FAILED (discRecorder->GetRecorderProperties (&prop)))
return false;

PROPSPEC iPropSpec;
iPropSpec.ulKind = PRSPEC_LPWSTR;
iPropSpec.lpwstr = name;

PROPVARIANT iPropVariant;
if (FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant)))
return false;

iPropVariant.lVal = (long) value;
return SUCCEEDED (prop->WriteMultiple (1, &iPropSpec, &iPropVariant, iPropVariant.vt))
&& SUCCEEDED (discRecorder->SetRecorderProperties (prop));
}

class DiskOpener
{
public:
DiskOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); }
~DiskOpener() { pimpl.discRecorder->Close(); }

private:
Pimpl& pimpl;
};

IDiscMaster* discMaster;
IDiscRecorder* discRecorder;
IRedbookDiscMaster* redbook;
@@ -221937,7 +222022,6 @@ private:
};

AudioCDBurner::AudioCDBurner (const int deviceIndex)
: internal (0)
{
IDiscMaster* discMaster;
IDiscRecorder* dr = enumCDBurners (0, deviceIndex, &discMaster);
@@ -221946,69 +222030,115 @@ AudioCDBurner::AudioCDBurner (const int deviceIndex)
{
IRedbookDiscMaster* redbook;
HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook);

hr = discMaster->SetActiveDiscRecorder (dr);

CDBurnerInfo* const info = new CDBurnerInfo();
internal = info;

info->discMaster = discMaster;
info->discRecorder = dr;
info->redbook = redbook;
pimpl = new Pimpl();
pimpl->discMaster = discMaster;
pimpl->discRecorder = dr;
pimpl->redbook = redbook;
}
}

AudioCDBurner::~AudioCDBurner()
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;

if (info != 0)
if (pimpl != 0)
{
info->discRecorder->Close();
info->redbook->Release();
info->discRecorder->Release();
info->discMaster->Release();
pimpl->discRecorder->Close();
pimpl->redbook->Release();
pimpl->discRecorder->Release();
pimpl->discMaster->Release();

info->Release();
pimpl.release()->Release();
}
}

AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
{
const Pimpl::DiskOpener opener (*pimpl);

long type, flags;
HRESULT hr = pimpl->discRecorder->QueryMediaType (&type, &flags);

if (FAILED (hr))
return unknown;

if (type != 0 && (flags & MEDIA_WRITABLE) != 0)
return writableDiskPresent;

if (type == 0)
return noDisc;
else
return readOnlyDiskPresent;
}

bool AudioCDBurner::isDiskPresent() const
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
return getDiskState() == writableDiskPresent;
}

HRESULT hr = info->discRecorder->OpenExclusive();
bool AudioCDBurner::openTray()
{
const Pimpl::DiskOpener opener (*pimpl);
return pimpl->discRecorder->Eject() == S_OK;
}

long type, flags;
hr = info->discRecorder->QueryMediaType (&type, &flags);
AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds)
{
const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds;
DiskState oldState = getDiskState();
DiskState newState = oldState;

while (newState == oldState && Time::currentTimeMillis() < timeout)
{
newState = getDiskState();
Thread::sleep (jmin (250, (int) (timeout - Time::currentTimeMillis())));
}

return newState;
}

const Array<int> AudioCDBurner::getAvailableWriteSpeeds() const
{
Array<int> results;
const int maxSpeed = pimpl->getIntProperty (L"MaxWriteSpeed", 1);
const int speeds[] = { 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 64, 80 };

for (int i = 0; i < numElementsInArray (speeds); ++i)
if (speeds[i] <= maxSpeed)
results.add (speeds[i]);

info->discRecorder->Close();
return hr == S_OK && type != 0 && (flags & MEDIA_WRITABLE) != 0;
results.addIfNotAlreadyThere (maxSpeed);
return results;
}

bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled)
{
if (pimpl->getIntProperty (L"BufferUnderrunFreeCapable", 0) == 0)
return false;

pimpl->setIntProperty (L"EnableBufferUnderrunFree", shouldBeEnabled ? -1 : 0);
return pimpl->getIntProperty (L"EnableBufferUnderrunFree", 0) != 0;
}

int AudioCDBurner::getNumAvailableAudioBlocks() const
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
long blocksFree = 0;
info->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
return blocksFree;
}

const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener,
const bool ejectDiscAfterwards,
const bool performFakeBurnForTesting)
const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards,
bool performFakeBurnForTesting, int writeSpeed)
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;

info->listener = listener;
info->progress = 0;
info->shouldCancel = false;
pimpl->listener = listener;
pimpl->progress = 0;
pimpl->shouldCancel = false;

UINT_PTR cookie;
HRESULT hr = info->discMaster->ProgressAdvise (info, &cookie);
HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie);

hr = info->discMaster->RecordDisc (performFakeBurnForTesting,
ejectDiscAfterwards);
hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting,
ejectDiscAfterwards);

String error;
if (hr != S_OK)
@@ -222023,29 +222153,28 @@ const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener,
error = e;
}

info->discMaster->ProgressUnadvise (cookie);
info->listener = 0;
pimpl->discMaster->ProgressUnadvise (cookie);
pimpl->listener = 0;

return error;
}

bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples)
{
if (source == 0)
if (audioSource == 0)
return false;

CDBurnerInfo* const info = (CDBurnerInfo*) internal;
ScopedPointer<AudioSource> source (audioSource);

long bytesPerBlock;
HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock);
HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock);

const int samplesPerBlock = bytesPerBlock / 4;
bool ok = true;

hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));

HeapBlock <byte> buffer (bytesPerBlock);

AudioSampleBuffer sourceBuffer (2, samplesPerBlock);
int samplesDone = 0;

@@ -222071,9 +222200,9 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0),
buffer + 2, samplesPerBlock, 4);

hr = info->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock);
hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock);

if (hr != S_OK)
if (FAILED (hr))
ok = false;

samplesDone += samplesPerBlock;
@@ -222082,10 +222211,7 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
break;
}

hr = info->redbook->CloseAudioTrack();

delete source;

hr = pimpl->redbook->CloseAudioTrack();
return ok && hr == S_OK;
}

@@ -228305,6 +228431,29 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs)
const tchar File::separator = T('/');
const tchar* File::separatorString = T("/");

const File File::getCurrentWorkingDirectory()
{
HeapBlock<char> heapBuffer;

char localBuffer [1024];
char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1);
int bufferSize = 4096;

while (cwd == 0 && errno == ERANGE)
{
heapBuffer.malloc (bufferSize);
cwd = getcwd (heapBuffer, bufferSize - 1);
bufferSize += 1024;
}

return File (String::fromUTF8 ((const uint8*) cwd));
}

bool File::setAsCurrentWorkingDirectory() const
{
return chdir (getFullPathName().toUTF8()) == 0;
}

bool juce_copyFile (const String& s, const String& d);

static bool juce_stat (const String& fileName, struct stat& info)
@@ -228455,7 +228604,7 @@ const File juce_getExecutableFile()
{
Dl_info exeInfo;
dladdr ((const void*) juce_getExecutableFile, &exeInfo);
return File (exeInfo.dli_fname);
return File (String::fromUTF8 ((const uint8*) exeInfo.dli_fname));
}

// if this file doesn't exist, find a parent of it that does..
@@ -228850,17 +228999,6 @@ const File File::getSpecialLocation (const SpecialLocationType type)
return File::nonexistent;
}

const File File::getCurrentWorkingDirectory()
{
char buf [2048];
return File (String::fromUTF8 ((const uint8*) getcwd (buf, sizeof (buf))));
}

bool File::setAsCurrentWorkingDirectory() const
{
return chdir (getFullPathName().toUTF8()) == 0;
}

const String File::getVersion() const
{
return String::empty; // xxx not yet implemented
@@ -238100,6 +238238,29 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs)
const tchar File::separator = T('/');
const tchar* File::separatorString = T("/");

const File File::getCurrentWorkingDirectory()
{
HeapBlock<char> heapBuffer;

char localBuffer [1024];
char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1);
int bufferSize = 4096;

while (cwd == 0 && errno == ERANGE)
{
heapBuffer.malloc (bufferSize);
cwd = getcwd (heapBuffer, bufferSize - 1);
bufferSize += 1024;
}

return File (String::fromUTF8 ((const uint8*) cwd));
}

bool File::setAsCurrentWorkingDirectory() const
{
return chdir (getFullPathName().toUTF8()) == 0;
}

bool juce_copyFile (const String& s, const String& d);

static bool juce_stat (const String& fileName, struct stat& info)
@@ -238250,7 +238411,7 @@ const File juce_getExecutableFile()
{
Dl_info exeInfo;
dladdr ((const void*) juce_getExecutableFile, &exeInfo);
return File (exeInfo.dli_fname);
return File (String::fromUTF8 ((const uint8*) exeInfo.dli_fname));
}

// if this file doesn't exist, find a parent of it that does..
@@ -238664,19 +238825,6 @@ const File File::getSpecialLocation (const SpecialLocationType type)
return File::nonexistent;
}

const File File::getCurrentWorkingDirectory()
{
char buf [2048];
getcwd (buf, sizeof(buf));

return File (PlatformUtilities::convertToPrecomposedUnicode (buf));
}

bool File::setAsCurrentWorkingDirectory() const
{
return chdir (getFullPathName().toUTF8()) == 0;
}

const String File::getVersion() const
{
const ScopedAutoReleasePool pool;
@@ -248313,24 +248461,25 @@ bool juce_OpenQuickTimeMovieFromStream (InputStream* movieStream, Movie& result,
// compiled on its own).
#if JUCE_INCLUDED_FILE && JUCE_USE_CDBURNER

const int kilobytesPerSecond1x = 176;

END_JUCE_NAMESPACE

#define OpenDiskDevice MakeObjCClassName(OpenDiskDevice)

@interface OpenDiskDevice : NSObject
{
@public
DRDevice* device;

NSMutableArray* tracks;
bool underrunProtection;
}

- (OpenDiskDevice*) initWithDevice: (DRDevice*) device;
- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device;
- (void) dealloc;
- (bool) isDiskPresent;
- (int) getNumAvailableAudioBlocks;
- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) numSamples_;
- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting;
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting speed: (int) burnSpeed;
@end

#define AudioTrackProducer MakeObjCClassName(AudioTrackProducer)
@@ -248370,12 +248519,13 @@ END_JUCE_NAMESPACE

@implementation OpenDiskDevice

- (OpenDiskDevice*) initWithDevice: (DRDevice*) device_
- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device_
{
[super init];

device = device_;
tracks = [[NSMutableArray alloc] init];
underrunProtection = true;
return self;
}

@@ -248385,19 +248535,6 @@ END_JUCE_NAMESPACE
[super dealloc];
}

- (bool) isDiskPresent
{
return [device isValid]
&& [[[device status] objectForKey: DRDeviceMediaStateKey]
isEqualTo: DRDeviceMediaStateMediaPresent];
}

- (int) getNumAvailableAudioBlocks
{
return [[[[device status] objectForKey: DRDeviceMediaInfoKey]
objectForKey: DRDeviceMediaBlocksFreeKey] intValue];
}

- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source_ numSamples: (int) numSamples_
{
AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: numSamples_];
@@ -248411,7 +248548,7 @@ END_JUCE_NAMESPACE
}

- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting speed: (int) burnSpeed
{
DRBurn* burn = [DRBurn burnForDevice: device];

@@ -248427,8 +248564,14 @@ END_JUCE_NAMESPACE
[d autorelease];
[d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey];
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey];
[d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount)
forKey: DRBurnCompletionActionKey];
[d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) forKey: DRBurnCompletionActionKey];

if (burnSpeed > 0)
[d setObject: [NSNumber numberWithFloat: burnSpeed * JUCE_NAMESPACE::kilobytesPerSecond1x] forKey: DRBurnRequestedSpeedKey];

if (! underrunProtection)
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnUnderrunProtectionKey];

[burn setProperties: d];

[burn writeLayout: tracks];
@@ -248599,27 +248742,123 @@ END_JUCE_NAMESPACE

BEGIN_JUCE_NAMESPACE

AudioCDBurner::AudioCDBurner (const int deviceIndex)
: internal (0)
class AudioCDBurner::Pimpl : public Timer
{
OpenDiskDevice* dev = [[OpenDiskDevice alloc] initWithDevice: [[DRDevice devices] objectAtIndex: deviceIndex]];
public:
Pimpl (AudioCDBurner& owner_, const int deviceIndex)
: device (0), owner (owner_)
{
DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex];
if (dev != 0)
{
device = [[OpenDiskDevice alloc] initWithDRDevice: dev];
lastState = getDiskState();
startTimer (1000);
}
}

~Pimpl()
{
stopTimer();
[device release];
}

void timerCallback()
{
const DiskState state = getDiskState();

if (state != lastState)
{
lastState = state;
owner.sendChangeMessage (&owner);
}
}

DiskState getDiskState() const
{
if ([device->device isValid])
{
NSDictionary* status = [device->device status];

NSString* state = [status objectForKey: DRDeviceMediaStateKey];

if ([state isEqualTo: DRDeviceMediaStateNone])
{
if ([[status objectForKey: DRDeviceIsTrayOpenKey] boolValue])
return trayOpen;

return noDisc;
}

if ([state isEqualTo: DRDeviceMediaStateMediaPresent])
{
if ([[[status objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue] > 0)
return writableDiskPresent;
else
return readOnlyDiskPresent;
}
}

return unknown;
}

bool openTray() { return [device->device isValid] && [device->device ejectMedia]; }

const Array<int> getAvailableWriteSpeeds() const
{
Array<int> results;

if ([device->device isValid])
{
NSArray* speeds = [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey];
for (unsigned int i = 0; i < [speeds count]; ++i)
{
const int kbPerSec = [[speeds objectAtIndex: i] intValue];
results.add (kbPerSec / kilobytesPerSecond1x);
}
}

return results;
}

bool setBufferUnderrunProtection (const bool shouldBeEnabled)
{
if ([device->device isValid])
{
device->underrunProtection = shouldBeEnabled;
return shouldBeEnabled && [[[device->device status] objectForKey: DRDeviceCanUnderrunProtectCDKey] boolValue];
}

return false;
}

int getNumAvailableAudioBlocks() const
{
return [[[[device->device status] objectForKey: DRDeviceMediaInfoKey]
objectForKey: DRDeviceMediaBlocksFreeKey] intValue];
}

OpenDiskDevice* device;

private:
DiskState lastState;
AudioCDBurner& owner;
};

internal = (void*) dev;
AudioCDBurner::AudioCDBurner (const int deviceIndex)
{
pimpl = new Pimpl (*this, deviceIndex);
}

AudioCDBurner::~AudioCDBurner()
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;

if (dev != 0)
[dev release];
}

AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
{
ScopedPointer <AudioCDBurner> b (new AudioCDBurner (deviceIndex));

if (b->internal == 0)
if (b->pimpl->device == 0)
b = 0;

return b.release();
@@ -248657,27 +248896,56 @@ const StringArray AudioCDBurner::findAvailableDevices()
return s;
}

AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
{
return pimpl->getDiskState();
}

bool AudioCDBurner::isDiskPresent() const
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
return getDiskState() == writableDiskPresent;
}

return dev != 0 && [dev isDiskPresent];
bool AudioCDBurner::openTray()
{
return pimpl->openTray();
}

int AudioCDBurner::getNumAvailableAudioBlocks() const
AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds)
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds;
DiskState oldState = getDiskState();
DiskState newState = oldState;

while (newState == oldState && Time::currentTimeMillis() < timeout)
{
newState = getDiskState();
Thread::sleep (100);
}

return [dev getNumAvailableAudioBlocks];
return newState;
}

bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
const Array<int> AudioCDBurner::getAvailableWriteSpeeds() const
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
return pimpl->getAvailableWriteSpeeds();
}

if (dev != 0)
bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled)
{
return pimpl->setBufferUnderrunProtection (shouldBeEnabled);
}

int AudioCDBurner::getNumAvailableAudioBlocks() const
{
return pimpl->getNumAvailableAudioBlocks();
}

bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
{
if ([pimpl->device->device isValid])
{
[dev addSourceTrack: source numSamples: numSamps];
[pimpl->device addSourceTrack: source numSamples: numSamps];
return true;
}

@@ -248685,25 +248953,30 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
}

const String AudioCDBurner::burn (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener* listener,
const bool ejectDiscAfterwards,
const bool peformFakeBurnForTesting)
bool ejectDiscAfterwards,
bool performFakeBurnForTesting,
int writeSpeed)
{
String error ("Couldn't open or write to the CD device");

OpenDiskDevice* dev = (OpenDiskDevice*) internal;

if (dev != 0)
if ([pimpl->device->device isValid])
{
error = String::empty;
[dev burn: listener
errorString: &error

[pimpl->device burn: listener
errorString: &error
ejectAfterwards: ejectDiscAfterwards
isFake: peformFakeBurnForTesting];
isFake: performFakeBurnForTesting
speed: writeSpeed];
}

return error;
}

#endif

#if JUCE_INCLUDED_FILE && JUCE_USE_CDREADER

void AudioCDReader::ejectDisk()
{
const ScopedAutoReleasePool p;


+ 30
- 5
juce_amalgamated.h View File

@@ -4377,7 +4377,7 @@ public:

static const String descriptionOfSizeInBytes (const int64 bytes);

const String& getFullPathName() const { return fullPath; }
const String& getFullPathName() const throw() { return fullPath; }

const String getFileName() const;

@@ -14016,7 +14016,7 @@ public:

#if JUCE_USE_CDBURNER

class AudioCDBurner
class AudioCDBurner : public ChangeBroadcaster
{
public:

@@ -14026,8 +14026,28 @@ public:

~AudioCDBurner();

enum DiskState
{
unknown, /**< An error condition, if the device isn't responding. */
trayOpen, /**< The drive is currently open. Note that a slot-loading drive
may seem to be permanently open. */
noDisc, /**< The drive has no disk in it. */
writableDiskPresent, /**< The drive contains a writeable disk. */
readOnlyDiskPresent /**< The drive contains a read-only disk. */
};

DiskState getDiskState() const;

bool isDiskPresent() const;

bool openTray();

DiskState waitUntilStateChange (int timeOutMilliseconds);

const Array<int> getAvailableWriteSpeeds() const;

bool setBufferUnderrunProtection (const bool shouldBeEnabled);

int getNumAvailableAudioBlocks() const;

bool addAudioTrack (AudioSource* source, int numSamples);
@@ -14042,15 +14062,20 @@ public:
};

const String burn (BurnProgressListener* listener,
const bool ejectDiscAfterwards,
const bool peformFakeBurnForTesting);
bool ejectDiscAfterwards,
bool performFakeBurnForTesting,
int writeSpeed);

void abortBurn();

juce_UseDebuggingNewOperator

private:
AudioCDBurner (const int deviceIndex);

void* internal;
class Pimpl;
friend class ScopedPointer<Pimpl>;
ScopedPointer<Pimpl> pimpl;
};

#endif


+ 72
- 9
src/audio/audio_file_formats/juce_AudioCDBurner.h View File

@@ -35,7 +35,7 @@
//==============================================================================
/**
*/
class AudioCDBurner
class AudioCDBurner : public ChangeBroadcaster
{
public:
//==============================================================================
@@ -55,10 +55,53 @@ public:
~AudioCDBurner();
//==============================================================================
/** Returns true if there's a writable disk in the drive.
enum DiskState
{
unknown, /**< An error condition, if the device isn't responding. */
trayOpen, /**< The drive is currently open. Note that a slot-loading drive
may seem to be permanently open. */
noDisc, /**< The drive has no disk in it. */
writableDiskPresent, /**< The drive contains a writeable disk. */
readOnlyDiskPresent /**< The drive contains a read-only disk. */
};
/** Returns the current status of the device.
To get informed when the drive's status changes, attach a ChangeListener to
the AudioCDBurner.
*/
DiskState getDiskState() const;
/** Returns true if there's a writable disk in the drive. */
bool isDiskPresent() const;
/** Sends an eject signal to the drive.
The eject will happen asynchronously, so you can use getDiskState() and
waitUntilStateChange() to monitor its progress.
*/
bool openTray();
/** Blocks the current thread until the drive's state changes, or until the timeout expires.
@returns the device's new state
*/
DiskState waitUntilStateChange (int timeOutMilliseconds);
//==============================================================================
/** Returns the set of possible write speeds that the device can handle.
These are as a multiple of 'normal' speed, so e.g. '24x' returns 24, etc.
Note that if there's no media present in the drive, this value may be unavailable!
@see setWriteSpeed, getWriteSpeed
*/
const Array<int> getAvailableWriteSpeeds() const;
//==============================================================================
/** Tries to enable or disable buffer underrun safety on devices that support it.
@returns true if it's now enabled. If the device doesn't support it, this
will always return false.
*/
bool setBufferUnderrunProtection (const bool shouldBeEnabled);
//==============================================================================
/** Returns the number of free blocks on the disk.
There are 75 blocks per second, at 44100Hz.
@@ -75,9 +118,9 @@ public:
*/
bool addAudioTrack (AudioSource* source, int numSamples);
/**
Return true to cancel the current burn operation
//==============================================================================
/** Receives progress callbacks during a cd-burn operation.
@see AudioCDBurner::burn()
*/
class BurnProgressListener
{
@@ -87,14 +130,32 @@ public:
/** Called at intervals to report on the progress of the AudioCDBurner.
To cancel the burn, return true from this.
To cancel the burn, return true from this method.
*/
virtual bool audioCDBurnProgress (float proportionComplete) = 0;
};
/** Runs the burn process.
This method will block until the operation is complete.
@param listener the object to receive callbacks about progress
@param ejectDiscAfterwards whether to eject the disk after the burn completes
@param performFakeBurnForTesting if true, no data will actually be written to the disk
@param writeSpeed one of the write speeds from getAvailableWriteSpeeds(), or
0 or less to mean the fastest speed.
*/
const String burn (BurnProgressListener* listener,
const bool ejectDiscAfterwards,
const bool peformFakeBurnForTesting);
bool ejectDiscAfterwards,
bool performFakeBurnForTesting,
int writeSpeed);
/** If a burn operation is currently in progress, this tells it to stop
as soon as possible.
It's also possible to stop the burn process by returning true from
BurnProgressListener::audioCDBurnProgress()
*/
void abortBurn();
//==============================================================================
juce_UseDebuggingNewOperator
@@ -102,7 +163,9 @@ public:
private:
AudioCDBurner (const int deviceIndex);
void* internal;
class Pimpl;
friend class ScopedPointer<Pimpl>;
ScopedPointer<Pimpl> pimpl;
};


+ 173
- 49
src/native/mac/juce_mac_AudioCDBurner.mm View File

@@ -28,24 +28,25 @@
#if JUCE_INCLUDED_FILE && JUCE_USE_CDBURNER
//==============================================================================
const int kilobytesPerSecond1x = 176;
END_JUCE_NAMESPACE
#define OpenDiskDevice MakeObjCClassName(OpenDiskDevice)
@interface OpenDiskDevice : NSObject
{
@public
DRDevice* device;
NSMutableArray* tracks;
bool underrunProtection;
}
- (OpenDiskDevice*) initWithDevice: (DRDevice*) device;
- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device;
- (void) dealloc;
- (bool) isDiskPresent;
- (int) getNumAvailableAudioBlocks;
- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) numSamples_;
- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting;
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting speed: (int) burnSpeed;
@end
//==============================================================================
@@ -87,12 +88,13 @@ END_JUCE_NAMESPACE
//==============================================================================
@implementation OpenDiskDevice
- (OpenDiskDevice*) initWithDevice: (DRDevice*) device_
- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device_
{
[super init];
device = device_;
tracks = [[NSMutableArray alloc] init];
underrunProtection = true;
return self;
}
@@ -102,19 +104,6 @@ END_JUCE_NAMESPACE
[super dealloc];
}
- (bool) isDiskPresent
{
return [device isValid]
&& [[[device status] objectForKey: DRDeviceMediaStateKey]
isEqualTo: DRDeviceMediaStateMediaPresent];
}
- (int) getNumAvailableAudioBlocks
{
return [[[[device status] objectForKey: DRDeviceMediaInfoKey]
objectForKey: DRDeviceMediaBlocksFreeKey] intValue];
}
- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source_ numSamples: (int) numSamples_
{
AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: numSamples_];
@@ -128,7 +117,7 @@ END_JUCE_NAMESPACE
}
- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting
ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting speed: (int) burnSpeed
{
DRBurn* burn = [DRBurn burnForDevice: device];
@@ -144,8 +133,14 @@ END_JUCE_NAMESPACE
[d autorelease];
[d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey];
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey];
[d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount)
forKey: DRBurnCompletionActionKey];
[d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) forKey: DRBurnCompletionActionKey];
if (burnSpeed > 0)
[d setObject: [NSNumber numberWithFloat: burnSpeed * JUCE_NAMESPACE::kilobytesPerSecond1x] forKey: DRBurnRequestedSpeedKey];
if (! underrunProtection)
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnUnderrunProtectionKey];
[burn setProperties: d];
[burn writeLayout: tracks];
@@ -320,27 +315,124 @@ END_JUCE_NAMESPACE
BEGIN_JUCE_NAMESPACE
//==============================================================================
AudioCDBurner::AudioCDBurner (const int deviceIndex)
: internal (0)
class AudioCDBurner::Pimpl : public Timer
{
OpenDiskDevice* dev = [[OpenDiskDevice alloc] initWithDevice: [[DRDevice devices] objectAtIndex: deviceIndex]];
public:
Pimpl (AudioCDBurner& owner_, const int deviceIndex)
: device (0), owner (owner_)
{
DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex];
if (dev != 0)
{
device = [[OpenDiskDevice alloc] initWithDRDevice: dev];
lastState = getDiskState();
startTimer (1000);
}
}
~Pimpl()
{
stopTimer();
[device release];
}
void timerCallback()
{
const DiskState state = getDiskState();
internal = (void*) dev;
if (state != lastState)
{
lastState = state;
owner.sendChangeMessage (&owner);
}
}
DiskState getDiskState() const
{
if ([device->device isValid])
{
NSDictionary* status = [device->device status];
NSString* state = [status objectForKey: DRDeviceMediaStateKey];
if ([state isEqualTo: DRDeviceMediaStateNone])
{
if ([[status objectForKey: DRDeviceIsTrayOpenKey] boolValue])
return trayOpen;
return noDisc;
}
if ([state isEqualTo: DRDeviceMediaStateMediaPresent])
{
if ([[[status objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue] > 0)
return writableDiskPresent;
else
return readOnlyDiskPresent;
}
}
return unknown;
}
bool openTray() { return [device->device isValid] && [device->device ejectMedia]; }
const Array<int> getAvailableWriteSpeeds() const
{
Array<int> results;
if ([device->device isValid])
{
NSArray* speeds = [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey];
for (unsigned int i = 0; i < [speeds count]; ++i)
{
const int kbPerSec = [[speeds objectAtIndex: i] intValue];
results.add (kbPerSec / kilobytesPerSecond1x);
}
}
return results;
}
bool setBufferUnderrunProtection (const bool shouldBeEnabled)
{
if ([device->device isValid])
{
device->underrunProtection = shouldBeEnabled;
return shouldBeEnabled && [[[device->device status] objectForKey: DRDeviceCanUnderrunProtectCDKey] boolValue];
}
return false;
}
int getNumAvailableAudioBlocks() const
{
return [[[[device->device status] objectForKey: DRDeviceMediaInfoKey]
objectForKey: DRDeviceMediaBlocksFreeKey] intValue];
}
OpenDiskDevice* device;
private:
DiskState lastState;
AudioCDBurner& owner;
};
//==============================================================================
AudioCDBurner::AudioCDBurner (const int deviceIndex)
{
pimpl = new Pimpl (*this, deviceIndex);
}
AudioCDBurner::~AudioCDBurner()
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
if (dev != 0)
[dev release];
}
AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
{
ScopedPointer <AudioCDBurner> b (new AudioCDBurner (deviceIndex));
if (b->internal == 0)
if (b->pimpl->device == 0)
b = 0;
return b.release();
@@ -378,27 +470,56 @@ const StringArray AudioCDBurner::findAvailableDevices()
return s;
}
AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
{
return pimpl->getDiskState();
}
bool AudioCDBurner::isDiskPresent() const
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
return getDiskState() == writableDiskPresent;
}
return dev != 0 && [dev isDiskPresent];
bool AudioCDBurner::openTray()
{
return pimpl->openTray();
}
int AudioCDBurner::getNumAvailableAudioBlocks() const
AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds)
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds;
DiskState oldState = getDiskState();
DiskState newState = oldState;
return [dev getNumAvailableAudioBlocks];
while (newState == oldState && Time::currentTimeMillis() < timeout)
{
newState = getDiskState();
Thread::sleep (100);
}
return newState;
}
bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
const Array<int> AudioCDBurner::getAvailableWriteSpeeds() const
{
return pimpl->getAvailableWriteSpeeds();
}
bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled)
{
return pimpl->setBufferUnderrunProtection (shouldBeEnabled);
}
int AudioCDBurner::getNumAvailableAudioBlocks() const
{
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
return pimpl->getNumAvailableAudioBlocks();
}
if (dev != 0)
bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
{
if ([pimpl->device->device isValid])
{
[dev addSourceTrack: source numSamples: numSamps];
[pimpl->device addSourceTrack: source numSamples: numSamps];
return true;
}
@@ -406,32 +527,35 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
}
const String AudioCDBurner::burn (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener* listener,
const bool ejectDiscAfterwards,
const bool peformFakeBurnForTesting)
bool ejectDiscAfterwards,
bool performFakeBurnForTesting,
int writeSpeed)
{
String error ("Couldn't open or write to the CD device");
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
if (dev != 0)
if ([pimpl->device->device isValid])
{
error = String::empty;
[dev burn: listener
errorString: &error
[pimpl->device burn: listener
errorString: &error
ejectAfterwards: ejectDiscAfterwards
isFake: peformFakeBurnForTesting];
isFake: performFakeBurnForTesting
speed: writeSpeed];
}
return error;
}
#endif
//==============================================================================
#if JUCE_INCLUDED_FILE && JUCE_USE_CDREADER
void AudioCDReader::ejectDisk()
{
const ScopedAutoReleasePool p;
[[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())];
}
#endif

+ 144
- 62
src/native/windows/juce_win32_AudioCDReader.cpp View File

@@ -2141,28 +2141,24 @@ const StringArray AudioCDBurner::findAvailableDevices()
AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
{
AudioCDBurner* b = new AudioCDBurner (deviceIndex);
ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex));
if (b->internal == 0)
deleteAndZero (b);
if (b->pimpl == 0)
b = 0;
return b;
return b.release();
}
class CDBurnerInfo : public IDiscMasterProgressEvents
//==============================================================================
class AudioCDBurner::Pimpl : public IDiscMasterProgressEvents
{
public:
CDBurnerInfo()
: refCount (1),
progress (0),
shouldCancel (false),
listener (0)
Pimpl()
: listener (0), progress (0), shouldCancel (false), refCount (1)
{
}
~CDBurnerInfo()
{
}
~Pimpl() {}
HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result)
{
@@ -2209,6 +2205,50 @@ public:
HRESULT __stdcall NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; }
HRESULT __stdcall NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; }
int getIntProperty (const LPOLESTR name, const int defaultReturn) const
{
ComSmartPtr<IPropertyStorage> prop;
if (FAILED (discRecorder->GetRecorderProperties (&prop)))
return defaultReturn;
PROPSPEC iPropSpec;
iPropSpec.ulKind = PRSPEC_LPWSTR;
iPropSpec.lpwstr = name;
PROPVARIANT iPropVariant;
return FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant))
? defaultReturn : (int) iPropVariant.lVal;
}
bool setIntProperty (const LPOLESTR name, const int value) const
{
ComSmartPtr<IPropertyStorage> prop;
if (FAILED (discRecorder->GetRecorderProperties (&prop)))
return false;
PROPSPEC iPropSpec;
iPropSpec.ulKind = PRSPEC_LPWSTR;
iPropSpec.lpwstr = name;
PROPVARIANT iPropVariant;
if (FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant)))
return false;
iPropVariant.lVal = (long) value;
return SUCCEEDED (prop->WriteMultiple (1, &iPropSpec, &iPropVariant, iPropVariant.vt))
&& SUCCEEDED (discRecorder->SetRecorderProperties (prop));
}
class DiskOpener
{
public:
DiskOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); }
~DiskOpener() { pimpl.discRecorder->Close(); }
private:
Pimpl& pimpl;
};
IDiscMaster* discMaster;
IDiscRecorder* discRecorder;
IRedbookDiscMaster* redbook;
@@ -2220,8 +2260,8 @@ private:
int refCount;
};
//==============================================================================
AudioCDBurner::AudioCDBurner (const int deviceIndex)
: internal (0)
{
IDiscMaster* discMaster;
IDiscRecorder* dr = enumCDBurners (0, deviceIndex, &discMaster);
@@ -2230,69 +2270,115 @@ AudioCDBurner::AudioCDBurner (const int deviceIndex)
{
IRedbookDiscMaster* redbook;
HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook);
hr = discMaster->SetActiveDiscRecorder (dr);
CDBurnerInfo* const info = new CDBurnerInfo();
internal = info;
info->discMaster = discMaster;
info->discRecorder = dr;
info->redbook = redbook;
pimpl = new Pimpl();
pimpl->discMaster = discMaster;
pimpl->discRecorder = dr;
pimpl->redbook = redbook;
}
}
AudioCDBurner::~AudioCDBurner()
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
if (info != 0)
if (pimpl != 0)
{
info->discRecorder->Close();
info->redbook->Release();
info->discRecorder->Release();
info->discMaster->Release();
pimpl->discRecorder->Close();
pimpl->redbook->Release();
pimpl->discRecorder->Release();
pimpl->discMaster->Release();
info->Release();
pimpl.release()->Release();
}
}
AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
{
const Pimpl::DiskOpener opener (*pimpl);
long type, flags;
HRESULT hr = pimpl->discRecorder->QueryMediaType (&type, &flags);
if (FAILED (hr))
return unknown;
if (type != 0 && (flags & MEDIA_WRITABLE) != 0)
return writableDiskPresent;
if (type == 0)
return noDisc;
else
return readOnlyDiskPresent;
}
bool AudioCDBurner::isDiskPresent() const
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
return getDiskState() == writableDiskPresent;
}
bool AudioCDBurner::openTray()
{
const Pimpl::DiskOpener opener (*pimpl);
return pimpl->discRecorder->Eject() == S_OK;
}
HRESULT hr = info->discRecorder->OpenExclusive();
AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds)
{
const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds;
DiskState oldState = getDiskState();
DiskState newState = oldState;
long type, flags;
hr = info->discRecorder->QueryMediaType (&type, &flags);
while (newState == oldState && Time::currentTimeMillis() < timeout)
{
newState = getDiskState();
Thread::sleep (jmin (250, (int) (timeout - Time::currentTimeMillis())));
}
info->discRecorder->Close();
return hr == S_OK && type != 0 && (flags & MEDIA_WRITABLE) != 0;
return newState;
}
const Array<int> AudioCDBurner::getAvailableWriteSpeeds() const
{
Array<int> results;
const int maxSpeed = pimpl->getIntProperty (L"MaxWriteSpeed", 1);
const int speeds[] = { 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 64, 80 };
for (int i = 0; i < numElementsInArray (speeds); ++i)
if (speeds[i] <= maxSpeed)
results.add (speeds[i]);
results.addIfNotAlreadyThere (maxSpeed);
return results;
}
bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled)
{
if (pimpl->getIntProperty (L"BufferUnderrunFreeCapable", 0) == 0)
return false;
pimpl->setIntProperty (L"EnableBufferUnderrunFree", shouldBeEnabled ? -1 : 0);
return pimpl->getIntProperty (L"EnableBufferUnderrunFree", 0) != 0;
}
int AudioCDBurner::getNumAvailableAudioBlocks() const
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
long blocksFree = 0;
info->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
return blocksFree;
}
const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener,
const bool ejectDiscAfterwards,
const bool performFakeBurnForTesting)
const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards,
bool performFakeBurnForTesting, int writeSpeed)
{
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
info->listener = listener;
info->progress = 0;
info->shouldCancel = false;
pimpl->listener = listener;
pimpl->progress = 0;
pimpl->shouldCancel = false;
UINT_PTR cookie;
HRESULT hr = info->discMaster->ProgressAdvise (info, &cookie);
HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie);
hr = info->discMaster->RecordDisc (performFakeBurnForTesting,
ejectDiscAfterwards);
hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting,
ejectDiscAfterwards);
String error;
if (hr != S_OK)
@@ -2307,29 +2393,28 @@ const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener,
error = e;
}
info->discMaster->ProgressUnadvise (cookie);
info->listener = 0;
pimpl->discMaster->ProgressUnadvise (cookie);
pimpl->listener = 0;
return error;
}
bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples)
{
if (source == 0)
if (audioSource == 0)
return false;
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
ScopedPointer<AudioSource> source (audioSource);
long bytesPerBlock;
HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock);
HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock);
const int samplesPerBlock = bytesPerBlock / 4;
bool ok = true;
hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
HeapBlock <byte> buffer (bytesPerBlock);
AudioSampleBuffer sourceBuffer (2, samplesPerBlock);
int samplesDone = 0;
@@ -2355,9 +2440,9 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0),
buffer + 2, samplesPerBlock, 4);
hr = info->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock);
hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock);
if (hr != S_OK)
if (FAILED (hr))
ok = false;
samplesDone += samplesPerBlock;
@@ -2366,10 +2451,7 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
break;
}
hr = info->redbook->CloseAudioTrack();
delete source;
hr = pimpl->redbook->CloseAudioTrack();
return ok && hr == S_OK;
}


Loading…
Cancel
Save