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


+ 428
- 155
juce_amalgamated.cpp View File

@@ -5902,10 +5902,14 @@ static const String parseAbsolutePath (String path)
{ {
if (path[1] != File::separator) 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; path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path;
} }
@@ -5915,10 +5919,14 @@ static const String parseAbsolutePath (String path)
if (path.isEmpty()) if (path.isEmpty())
return String::empty; 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(); return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
} }
@@ -5957,16 +5965,14 @@ static const String parseAbsolutePath (String path)
} }
else if (! path.startsWithChar (File::separator)) 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(); return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
} }
@@ -21866,6 +21872,14 @@ struct SMPLChunk
} }
} PACKED; } PACKED;


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

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


if (format == 3) if (format == 3)
{
usesFloatingPointData = true; 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) else if (format != 1)
{
bytesPerFrame = 0; bytesPerFrame = 0;
}


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


AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) 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: 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) 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 NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; }
HRESULT __stdcall NotifyEraseComplete (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; IDiscMaster* discMaster;
IDiscRecorder* discRecorder; IDiscRecorder* discRecorder;
IRedbookDiscMaster* redbook; IRedbookDiscMaster* redbook;
@@ -221937,7 +222022,6 @@ private:
}; };


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

hr = discMaster->SetActiveDiscRecorder (dr); 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() 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 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 int AudioCDBurner::getNumAvailableAudioBlocks() const
{ {
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
long blocksFree = 0; long blocksFree = 0;
info->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
return 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; 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; String error;
if (hr != S_OK) if (hr != S_OK)
@@ -222023,29 +222153,28 @@ const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener,
error = e; error = e;
} }


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


return error; return error;
} }


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


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


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


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


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


HeapBlock <byte> buffer (bytesPerBlock); HeapBlock <byte> buffer (bytesPerBlock);

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


@@ -222071,9 +222200,9 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0), AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0),
buffer + 2, samplesPerBlock, 4); 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; ok = false;


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


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

delete source;

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


@@ -228305,6 +228431,29 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs)
const tchar File::separator = T('/'); const tchar File::separator = T('/');
const tchar* File::separatorString = 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); bool juce_copyFile (const String& s, const String& d);


static bool juce_stat (const String& fileName, struct stat& info) static bool juce_stat (const String& fileName, struct stat& info)
@@ -228455,7 +228604,7 @@ const File juce_getExecutableFile()
{ {
Dl_info exeInfo; Dl_info exeInfo;
dladdr ((const void*) juce_getExecutableFile, &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.. // 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; 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 const String File::getVersion() const
{ {
return String::empty; // xxx not yet implemented 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::separator = T('/');
const tchar* File::separatorString = 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); bool juce_copyFile (const String& s, const String& d);


static bool juce_stat (const String& fileName, struct stat& info) static bool juce_stat (const String& fileName, struct stat& info)
@@ -238250,7 +238411,7 @@ const File juce_getExecutableFile()
{ {
Dl_info exeInfo; Dl_info exeInfo;
dladdr ((const void*) juce_getExecutableFile, &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.. // 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; 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 String File::getVersion() const
{ {
const ScopedAutoReleasePool pool; const ScopedAutoReleasePool pool;
@@ -248313,24 +248461,25 @@ bool juce_OpenQuickTimeMovieFromStream (InputStream* movieStream, Movie& result,
// compiled on its own). // compiled on its own).
#if JUCE_INCLUDED_FILE && JUCE_USE_CDBURNER #if JUCE_INCLUDED_FILE && JUCE_USE_CDBURNER


const int kilobytesPerSecond1x = 176;

END_JUCE_NAMESPACE END_JUCE_NAMESPACE


#define OpenDiskDevice MakeObjCClassName(OpenDiskDevice) #define OpenDiskDevice MakeObjCClassName(OpenDiskDevice)


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

NSMutableArray* tracks; NSMutableArray* tracks;
bool underrunProtection;
} }


- (OpenDiskDevice*) initWithDevice: (DRDevice*) device;
- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device;
- (void) dealloc; - (void) dealloc;
- (bool) isDiskPresent;
- (int) getNumAvailableAudioBlocks;
- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) numSamples_; - (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) numSamples_;
- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error - (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 @end


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


@implementation OpenDiskDevice @implementation OpenDiskDevice


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


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


@@ -248385,19 +248535,6 @@ END_JUCE_NAMESPACE
[super dealloc]; [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_ - (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source_ numSamples: (int) numSamples_
{ {
AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: 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 - (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]; DRBurn* burn = [DRBurn burnForDevice: device];


@@ -248427,8 +248564,14 @@ END_JUCE_NAMESPACE
[d autorelease]; [d autorelease];
[d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey];
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; [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 setProperties: d];


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


BEGIN_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() AudioCDBurner::~AudioCDBurner()
{ {
OpenDiskDevice* dev = (OpenDiskDevice*) internal;

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


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


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


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


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

bool AudioCDBurner::isDiskPresent() const 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; return true;
} }


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


const String AudioCDBurner::burn (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener* listener, 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"); 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; error = String::empty;
[dev burn: listener
errorString: &error

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


return error; return error;
} }


#endif

#if JUCE_INCLUDED_FILE && JUCE_USE_CDREADER

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


+ 30
- 5
juce_amalgamated.h View File

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


static const String descriptionOfSizeInBytes (const int64 bytes); static const String descriptionOfSizeInBytes (const int64 bytes);


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


const String getFileName() const; const String getFileName() const;


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


#if JUCE_USE_CDBURNER #if JUCE_USE_CDBURNER


class AudioCDBurner
class AudioCDBurner : public ChangeBroadcaster
{ {
public: public:


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


~AudioCDBurner(); ~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 isDiskPresent() const;


bool openTray();

DiskState waitUntilStateChange (int timeOutMilliseconds);

const Array<int> getAvailableWriteSpeeds() const;

bool setBufferUnderrunProtection (const bool shouldBeEnabled);

int getNumAvailableAudioBlocks() const; int getNumAvailableAudioBlocks() const;


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


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

void abortBurn();


juce_UseDebuggingNewOperator juce_UseDebuggingNewOperator


private: private:
AudioCDBurner (const int deviceIndex); AudioCDBurner (const int deviceIndex);


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


#endif #endif


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

@@ -35,7 +35,7 @@
//============================================================================== //==============================================================================
/** /**
*/ */
class AudioCDBurner
class AudioCDBurner : public ChangeBroadcaster
{ {
public: public:
//============================================================================== //==============================================================================
@@ -55,10 +55,53 @@ public:
~AudioCDBurner(); ~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; 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. /** Returns the number of free blocks on the disk.
There are 75 blocks per second, at 44100Hz. There are 75 blocks per second, at 44100Hz.
@@ -75,9 +118,9 @@ public:
*/ */
bool addAudioTrack (AudioSource* source, int numSamples); 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 class BurnProgressListener
{ {
@@ -87,14 +130,32 @@ public:
/** Called at intervals to report on the progress of the AudioCDBurner. /** 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; 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 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 juce_UseDebuggingNewOperator
@@ -102,7 +163,9 @@ public:
private: private:
AudioCDBurner (const int deviceIndex); 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 #if JUCE_INCLUDED_FILE && JUCE_USE_CDBURNER
//============================================================================== //==============================================================================
const int kilobytesPerSecond1x = 176;
END_JUCE_NAMESPACE END_JUCE_NAMESPACE
#define OpenDiskDevice MakeObjCClassName(OpenDiskDevice) #define OpenDiskDevice MakeObjCClassName(OpenDiskDevice)
@interface OpenDiskDevice : NSObject @interface OpenDiskDevice : NSObject
{ {
@public
DRDevice* device; DRDevice* device;
NSMutableArray* tracks; NSMutableArray* tracks;
bool underrunProtection;
} }
- (OpenDiskDevice*) initWithDevice: (DRDevice*) device;
- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device;
- (void) dealloc; - (void) dealloc;
- (bool) isDiskPresent;
- (int) getNumAvailableAudioBlocks;
- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) numSamples_; - (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) numSamples_;
- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error - (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 @end
//============================================================================== //==============================================================================
@@ -87,12 +88,13 @@ END_JUCE_NAMESPACE
//============================================================================== //==============================================================================
@implementation OpenDiskDevice @implementation OpenDiskDevice
- (OpenDiskDevice*) initWithDevice: (DRDevice*) device_
- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device_
{ {
[super init]; [super init];
device = device_; device = device_;
tracks = [[NSMutableArray alloc] init]; tracks = [[NSMutableArray alloc] init];
underrunProtection = true;
return self; return self;
} }
@@ -102,19 +104,6 @@ END_JUCE_NAMESPACE
[super dealloc]; [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_ - (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source_ numSamples: (int) numSamples_
{ {
AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: 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 - (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]; DRBurn* burn = [DRBurn burnForDevice: device];
@@ -144,8 +133,14 @@ END_JUCE_NAMESPACE
[d autorelease]; [d autorelease];
[d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey];
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; [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 setProperties: d];
[burn writeLayout: tracks]; [burn writeLayout: tracks];
@@ -320,27 +315,124 @@ END_JUCE_NAMESPACE
BEGIN_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() AudioCDBurner::~AudioCDBurner()
{ {
OpenDiskDevice* dev = (OpenDiskDevice*) internal;
if (dev != 0)
[dev release];
} }
AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
{ {
ScopedPointer <AudioCDBurner> b (new AudioCDBurner (deviceIndex)); ScopedPointer <AudioCDBurner> b (new AudioCDBurner (deviceIndex));
if (b->internal == 0)
if (b->pimpl->device == 0)
b = 0; b = 0;
return b.release(); return b.release();
@@ -378,27 +470,56 @@ const StringArray AudioCDBurner::findAvailableDevices()
return s; return s;
} }
AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
{
return pimpl->getDiskState();
}
bool AudioCDBurner::isDiskPresent() const 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; return true;
} }
@@ -406,32 +527,35 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
} }
const String AudioCDBurner::burn (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener* listener, 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"); 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; error = String::empty;
[dev burn: listener
errorString: &error
[pimpl->device burn: listener
errorString: &error
ejectAfterwards: ejectDiscAfterwards ejectAfterwards: ejectDiscAfterwards
isFake: peformFakeBurnForTesting];
isFake: performFakeBurnForTesting
speed: writeSpeed];
} }
return error; return error;
} }
#endif
//============================================================================== //==============================================================================
#if JUCE_INCLUDED_FILE && JUCE_USE_CDREADER
void AudioCDReader::ejectDisk() void AudioCDReader::ejectDisk()
{ {
const ScopedAutoReleasePool p; const ScopedAutoReleasePool p;
[[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())]; [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())];
} }
#endif #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* 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: 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) 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 NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; }
HRESULT __stdcall NotifyEraseComplete (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; IDiscMaster* discMaster;
IDiscRecorder* discRecorder; IDiscRecorder* discRecorder;
IRedbookDiscMaster* redbook; IRedbookDiscMaster* redbook;
@@ -2220,8 +2260,8 @@ private:
int refCount; int refCount;
}; };
//==============================================================================
AudioCDBurner::AudioCDBurner (const int deviceIndex) AudioCDBurner::AudioCDBurner (const int deviceIndex)
: internal (0)
{ {
IDiscMaster* discMaster; IDiscMaster* discMaster;
IDiscRecorder* dr = enumCDBurners (0, deviceIndex, &discMaster); IDiscRecorder* dr = enumCDBurners (0, deviceIndex, &discMaster);
@@ -2230,69 +2270,115 @@ AudioCDBurner::AudioCDBurner (const int deviceIndex)
{ {
IRedbookDiscMaster* redbook; IRedbookDiscMaster* redbook;
HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook); HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook);
hr = discMaster->SetActiveDiscRecorder (dr); 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() 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 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 int AudioCDBurner::getNumAvailableAudioBlocks() const
{ {
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
long blocksFree = 0; long blocksFree = 0;
info->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
return 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; 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; String error;
if (hr != S_OK) if (hr != S_OK)
@@ -2307,29 +2393,28 @@ const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener,
error = e; error = e;
} }
info->discMaster->ProgressUnadvise (cookie);
info->listener = 0;
pimpl->discMaster->ProgressUnadvise (cookie);
pimpl->listener = 0;
return error; return error;
} }
bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples)
{ {
if (source == 0)
if (audioSource == 0)
return false; return false;
CDBurnerInfo* const info = (CDBurnerInfo*) internal;
ScopedPointer<AudioSource> source (audioSource);
long bytesPerBlock; long bytesPerBlock;
HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock);
HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock);
const int samplesPerBlock = bytesPerBlock / 4; const int samplesPerBlock = bytesPerBlock / 4;
bool ok = true; bool ok = true;
hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
HeapBlock <byte> buffer (bytesPerBlock); HeapBlock <byte> buffer (bytesPerBlock);
AudioSampleBuffer sourceBuffer (2, samplesPerBlock); AudioSampleBuffer sourceBuffer (2, samplesPerBlock);
int samplesDone = 0; int samplesDone = 0;
@@ -2355,9 +2440,9 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0), AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0),
buffer + 2, samplesPerBlock, 4); 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; ok = false;
samplesDone += samplesPerBlock; samplesDone += samplesPerBlock;
@@ -2366,10 +2451,7 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples)
break; break;
} }
hr = info->redbook->CloseAudioTrack();
delete source;
hr = pimpl->redbook->CloseAudioTrack();
return ok && hr == S_OK; return ok && hr == S_OK;
} }


Loading…
Cancel
Save