- port to minGw
git-svn-id: http://moon:8086/svn/software/trunk/projects/JaySynth@235 b431acfa-c32f-4a4a-93f1-934dc6c82436
This commit is contained in:
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace CDBurnerHelpers
|
||||
{
|
||||
IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master)
|
||||
{
|
||||
CoInitialize (0);
|
||||
|
||||
IDiscMaster* dm;
|
||||
IDiscRecorder* result = nullptr;
|
||||
|
||||
if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0,
|
||||
CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
|
||||
IID_IDiscMaster,
|
||||
(void**) &dm)))
|
||||
{
|
||||
if (SUCCEEDED (dm->Open()))
|
||||
{
|
||||
IEnumDiscRecorders* drEnum = nullptr;
|
||||
|
||||
if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum)))
|
||||
{
|
||||
IDiscRecorder* dr = nullptr;
|
||||
DWORD dummy;
|
||||
int index = 0;
|
||||
|
||||
while (drEnum->Next (1, &dr, &dummy) == S_OK)
|
||||
{
|
||||
if (indexToOpen == index)
|
||||
{
|
||||
result = dr;
|
||||
break;
|
||||
}
|
||||
else if (list != nullptr)
|
||||
{
|
||||
BSTR path;
|
||||
|
||||
if (SUCCEEDED (dr->GetPath (&path)))
|
||||
list->add ((const WCHAR*) path);
|
||||
}
|
||||
|
||||
++index;
|
||||
dr->Release();
|
||||
}
|
||||
|
||||
drEnum->Release();
|
||||
}
|
||||
|
||||
if (master == 0)
|
||||
dm->Close();
|
||||
}
|
||||
|
||||
if (master != nullptr)
|
||||
*master = dm;
|
||||
else
|
||||
dm->Release();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AudioCDBurner::Pimpl : public ComBaseClassHelper <IDiscMasterProgressEvents>,
|
||||
public Timer
|
||||
{
|
||||
public:
|
||||
Pimpl (AudioCDBurner& owner_, IDiscMaster* discMaster_, IDiscRecorder* discRecorder_)
|
||||
: owner (owner_), discMaster (discMaster_), discRecorder (discRecorder_), redbook (0),
|
||||
listener (0), progress (0), shouldCancel (false)
|
||||
{
|
||||
HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook);
|
||||
jassert (SUCCEEDED (hr));
|
||||
hr = discMaster->SetActiveDiscRecorder (discRecorder);
|
||||
//jassert (SUCCEEDED (hr));
|
||||
|
||||
lastState = getDiskState();
|
||||
startTimer (2000);
|
||||
}
|
||||
|
||||
~Pimpl() {}
|
||||
|
||||
void releaseObjects()
|
||||
{
|
||||
discRecorder->Close();
|
||||
if (redbook != nullptr)
|
||||
redbook->Release();
|
||||
discRecorder->Release();
|
||||
discMaster->Release();
|
||||
Release();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT QueryCancel (boolean* pbCancel)
|
||||
{
|
||||
if (listener != nullptr && ! shouldCancel)
|
||||
shouldCancel = listener->audioCDBurnProgress (progress);
|
||||
|
||||
*pbCancel = shouldCancel;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT NotifyBlockProgress (long nCompleted, long nTotal)
|
||||
{
|
||||
progress = nCompleted / (float) nTotal;
|
||||
shouldCancel = listener != nullptr && listener->audioCDBurnProgress (progress);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT NotifyPnPActivity (void) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; }
|
||||
|
||||
class ScopedDiscOpener
|
||||
{
|
||||
public:
|
||||
ScopedDiscOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); }
|
||||
~ScopedDiscOpener() { pimpl.discRecorder->Close(); }
|
||||
|
||||
private:
|
||||
Pimpl& pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener)
|
||||
};
|
||||
|
||||
DiskState getDiskState()
|
||||
{
|
||||
const ScopedDiscOpener opener (*this);
|
||||
|
||||
long type, flags;
|
||||
HRESULT hr = discRecorder->QueryMediaType (&type, &flags);
|
||||
|
||||
if (FAILED (hr))
|
||||
return unknown;
|
||||
|
||||
if (type != 0 && (flags & MEDIA_WRITABLE) != 0)
|
||||
return writableDiskPresent;
|
||||
|
||||
if (type == 0)
|
||||
return noDisc;
|
||||
|
||||
return readOnlyDiskPresent;
|
||||
}
|
||||
|
||||
int getIntProperty (const LPOLESTR name, const int defaultReturn) const
|
||||
{
|
||||
ComSmartPtr<IPropertyStorage> prop;
|
||||
if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress())))
|
||||
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.resetAndGetPointerAddress())))
|
||||
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));
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
const DiskState state = getDiskState();
|
||||
|
||||
if (state != lastState)
|
||||
{
|
||||
lastState = state;
|
||||
owner.sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
AudioCDBurner& owner;
|
||||
DiskState lastState;
|
||||
IDiscMaster* discMaster;
|
||||
IDiscRecorder* discRecorder;
|
||||
IRedbookDiscMaster* redbook;
|
||||
AudioCDBurner::BurnProgressListener* listener;
|
||||
float progress;
|
||||
bool shouldCancel;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AudioCDBurner::AudioCDBurner (const int deviceIndex)
|
||||
{
|
||||
IDiscMaster* discMaster = nullptr;
|
||||
IDiscRecorder* discRecorder = CDBurnerHelpers::enumCDBurners (0, deviceIndex, &discMaster);
|
||||
|
||||
if (discRecorder != nullptr)
|
||||
pimpl = new Pimpl (*this, discMaster, discRecorder);
|
||||
}
|
||||
|
||||
AudioCDBurner::~AudioCDBurner()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl.release()->releaseObjects();
|
||||
}
|
||||
|
||||
StringArray AudioCDBurner::findAvailableDevices()
|
||||
{
|
||||
StringArray devs;
|
||||
CDBurnerHelpers::enumCDBurners (&devs, -1, 0);
|
||||
return devs;
|
||||
}
|
||||
|
||||
AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
|
||||
{
|
||||
ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex));
|
||||
|
||||
if (b->pimpl == 0)
|
||||
b = nullptr;
|
||||
|
||||
return b.release();
|
||||
}
|
||||
|
||||
AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
|
||||
{
|
||||
return pimpl->getDiskState();
|
||||
}
|
||||
|
||||
bool AudioCDBurner::isDiskPresent() const
|
||||
{
|
||||
return getDiskState() == writableDiskPresent;
|
||||
}
|
||||
|
||||
bool AudioCDBurner::openTray()
|
||||
{
|
||||
const Pimpl::ScopedDiscOpener opener (*pimpl);
|
||||
return SUCCEEDED (pimpl->discRecorder->Eject());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
long blocksFree = 0;
|
||||
pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
|
||||
return blocksFree;
|
||||
}
|
||||
|
||||
String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards,
|
||||
bool performFakeBurnForTesting, int writeSpeed)
|
||||
{
|
||||
pimpl->setIntProperty (L"WriteSpeed", writeSpeed > 0 ? writeSpeed : -1);
|
||||
|
||||
pimpl->listener = listener;
|
||||
pimpl->progress = 0;
|
||||
pimpl->shouldCancel = false;
|
||||
|
||||
UINT_PTR cookie;
|
||||
HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie);
|
||||
|
||||
hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting,
|
||||
ejectDiscAfterwards);
|
||||
|
||||
String error;
|
||||
if (hr != S_OK)
|
||||
{
|
||||
const char* e = "Couldn't open or write to the CD device";
|
||||
|
||||
if (hr == IMAPI_E_USERABORT)
|
||||
e = "User cancelled the write operation";
|
||||
else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN)
|
||||
e = "No Disk present";
|
||||
|
||||
error = e;
|
||||
}
|
||||
|
||||
pimpl->discMaster->ProgressUnadvise (cookie);
|
||||
pimpl->listener = 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples)
|
||||
{
|
||||
if (audioSource == 0)
|
||||
return false;
|
||||
|
||||
ScopedPointer<AudioSource> source (audioSource);
|
||||
|
||||
long bytesPerBlock;
|
||||
HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock);
|
||||
|
||||
const int samplesPerBlock = bytesPerBlock / 4;
|
||||
bool ok = true;
|
||||
|
||||
hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
|
||||
|
||||
HeapBlock <byte> buffer (bytesPerBlock);
|
||||
AudioSampleBuffer sourceBuffer (2, samplesPerBlock);
|
||||
int samplesDone = 0;
|
||||
|
||||
source->prepareToPlay (samplesPerBlock, 44100.0);
|
||||
|
||||
while (ok)
|
||||
{
|
||||
{
|
||||
AudioSourceChannelInfo info (&sourceBuffer, 0, samplesPerBlock);
|
||||
sourceBuffer.clear();
|
||||
|
||||
source->getNextAudioBlock (info);
|
||||
}
|
||||
|
||||
buffer.clear (bytesPerBlock);
|
||||
|
||||
typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian,
|
||||
AudioData::Interleaved, AudioData::NonConst> CDSampleFormat;
|
||||
|
||||
typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian,
|
||||
AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat;
|
||||
|
||||
CDSampleFormat left (buffer, 2);
|
||||
left.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (0)), samplesPerBlock);
|
||||
CDSampleFormat right (buffer + 2, 2);
|
||||
right.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (1)), samplesPerBlock);
|
||||
|
||||
hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock);
|
||||
|
||||
if (FAILED (hr))
|
||||
ok = false;
|
||||
|
||||
samplesDone += samplesPerBlock;
|
||||
|
||||
if (samplesDone >= numSamples)
|
||||
break;
|
||||
}
|
||||
|
||||
hr = pimpl->redbook->CloseAudioTrack();
|
||||
return ok && hr == S_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user