PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp

https://bitbucket.org/teamaxe/markingtool
C++ | 412 lines | 299 code | 86 blank | 27 comment | 66 complexity | 52d489f05e0d11e9f494bbe1d35de082 MD5 | raw file
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. namespace CDBurnerHelpers
  19. {
  20. IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master)
  21. {
  22. CoInitialize (0);
  23. IDiscMaster* dm;
  24. IDiscRecorder* result = nullptr;
  25. if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0,
  26. CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
  27. IID_IDiscMaster,
  28. (void**) &dm)))
  29. {
  30. if (SUCCEEDED (dm->Open()))
  31. {
  32. IEnumDiscRecorders* drEnum = nullptr;
  33. if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum)))
  34. {
  35. IDiscRecorder* dr = nullptr;
  36. DWORD dummy;
  37. int index = 0;
  38. while (drEnum->Next (1, &dr, &dummy) == S_OK)
  39. {
  40. if (indexToOpen == index)
  41. {
  42. result = dr;
  43. break;
  44. }
  45. else if (list != nullptr)
  46. {
  47. BSTR path;
  48. if (SUCCEEDED (dr->GetPath (&path)))
  49. list->add ((const WCHAR*) path);
  50. }
  51. ++index;
  52. dr->Release();
  53. }
  54. drEnum->Release();
  55. }
  56. if (master == 0)
  57. dm->Close();
  58. }
  59. if (master != nullptr)
  60. *master = dm;
  61. else
  62. dm->Release();
  63. }
  64. return result;
  65. }
  66. }
  67. //==============================================================================
  68. class AudioCDBurner::Pimpl : public ComBaseClassHelper <IDiscMasterProgressEvents>,
  69. public Timer
  70. {
  71. public:
  72. Pimpl (AudioCDBurner& owner_, IDiscMaster* discMaster_, IDiscRecorder* discRecorder_)
  73. : owner (owner_), discMaster (discMaster_), discRecorder (discRecorder_), redbook (0),
  74. listener (0), progress (0), shouldCancel (false)
  75. {
  76. HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook);
  77. jassert (SUCCEEDED (hr));
  78. hr = discMaster->SetActiveDiscRecorder (discRecorder);
  79. //jassert (SUCCEEDED (hr));
  80. lastState = getDiskState();
  81. startTimer (2000);
  82. }
  83. ~Pimpl() {}
  84. void releaseObjects()
  85. {
  86. discRecorder->Close();
  87. if (redbook != nullptr)
  88. redbook->Release();
  89. discRecorder->Release();
  90. discMaster->Release();
  91. Release();
  92. }
  93. JUCE_COMRESULT QueryCancel (boolean* pbCancel)
  94. {
  95. if (listener != nullptr && ! shouldCancel)
  96. shouldCancel = listener->audioCDBurnProgress (progress);
  97. *pbCancel = shouldCancel;
  98. return S_OK;
  99. }
  100. JUCE_COMRESULT NotifyBlockProgress (long nCompleted, long nTotal)
  101. {
  102. progress = nCompleted / (float) nTotal;
  103. shouldCancel = listener != nullptr && listener->audioCDBurnProgress (progress);
  104. return E_NOTIMPL;
  105. }
  106. JUCE_COMRESULT NotifyPnPActivity (void) { return E_NOTIMPL; }
  107. JUCE_COMRESULT NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; }
  108. JUCE_COMRESULT NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; }
  109. JUCE_COMRESULT NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; }
  110. JUCE_COMRESULT NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; }
  111. JUCE_COMRESULT NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; }
  112. JUCE_COMRESULT NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; }
  113. class ScopedDiscOpener
  114. {
  115. public:
  116. ScopedDiscOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); }
  117. ~ScopedDiscOpener() { pimpl.discRecorder->Close(); }
  118. private:
  119. Pimpl& pimpl;
  120. JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener)
  121. };
  122. DiskState getDiskState()
  123. {
  124. const ScopedDiscOpener opener (*this);
  125. long type, flags;
  126. HRESULT hr = discRecorder->QueryMediaType (&type, &flags);
  127. if (FAILED (hr))
  128. return unknown;
  129. if (type != 0 && (flags & MEDIA_WRITABLE) != 0)
  130. return writableDiskPresent;
  131. if (type == 0)
  132. return noDisc;
  133. return readOnlyDiskPresent;
  134. }
  135. int getIntProperty (const LPOLESTR name, const int defaultReturn) const
  136. {
  137. ComSmartPtr<IPropertyStorage> prop;
  138. if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress())))
  139. return defaultReturn;
  140. PROPSPEC iPropSpec;
  141. iPropSpec.ulKind = PRSPEC_LPWSTR;
  142. iPropSpec.lpwstr = name;
  143. PROPVARIANT iPropVariant;
  144. return FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant))
  145. ? defaultReturn : (int) iPropVariant.lVal;
  146. }
  147. bool setIntProperty (const LPOLESTR name, const int value) const
  148. {
  149. ComSmartPtr<IPropertyStorage> prop;
  150. if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress())))
  151. return false;
  152. PROPSPEC iPropSpec;
  153. iPropSpec.ulKind = PRSPEC_LPWSTR;
  154. iPropSpec.lpwstr = name;
  155. PROPVARIANT iPropVariant;
  156. if (FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant)))
  157. return false;
  158. iPropVariant.lVal = (long) value;
  159. return SUCCEEDED (prop->WriteMultiple (1, &iPropSpec, &iPropVariant, iPropVariant.vt))
  160. && SUCCEEDED (discRecorder->SetRecorderProperties (prop));
  161. }
  162. void timerCallback()
  163. {
  164. const DiskState state = getDiskState();
  165. if (state != lastState)
  166. {
  167. lastState = state;
  168. owner.sendChangeMessage();
  169. }
  170. }
  171. AudioCDBurner& owner;
  172. DiskState lastState;
  173. IDiscMaster* discMaster;
  174. IDiscRecorder* discRecorder;
  175. IRedbookDiscMaster* redbook;
  176. AudioCDBurner::BurnProgressListener* listener;
  177. float progress;
  178. bool shouldCancel;
  179. };
  180. //==============================================================================
  181. AudioCDBurner::AudioCDBurner (const int deviceIndex)
  182. {
  183. IDiscMaster* discMaster = nullptr;
  184. IDiscRecorder* discRecorder = CDBurnerHelpers::enumCDBurners (0, deviceIndex, &discMaster);
  185. if (discRecorder != nullptr)
  186. pimpl = new Pimpl (*this, discMaster, discRecorder);
  187. }
  188. AudioCDBurner::~AudioCDBurner()
  189. {
  190. if (pimpl != nullptr)
  191. pimpl.release()->releaseObjects();
  192. }
  193. StringArray AudioCDBurner::findAvailableDevices()
  194. {
  195. StringArray devs;
  196. CDBurnerHelpers::enumCDBurners (&devs, -1, 0);
  197. return devs;
  198. }
  199. AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
  200. {
  201. ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex));
  202. if (b->pimpl == 0)
  203. b = nullptr;
  204. return b.release();
  205. }
  206. AudioCDBurner::DiskState AudioCDBurner::getDiskState() const
  207. {
  208. return pimpl->getDiskState();
  209. }
  210. bool AudioCDBurner::isDiskPresent() const
  211. {
  212. return getDiskState() == writableDiskPresent;
  213. }
  214. bool AudioCDBurner::openTray()
  215. {
  216. const Pimpl::ScopedDiscOpener opener (*pimpl);
  217. return SUCCEEDED (pimpl->discRecorder->Eject());
  218. }
  219. AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds)
  220. {
  221. const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds;
  222. DiskState oldState = getDiskState();
  223. DiskState newState = oldState;
  224. while (newState == oldState && Time::currentTimeMillis() < timeout)
  225. {
  226. newState = getDiskState();
  227. Thread::sleep (jmin (250, (int) (timeout - Time::currentTimeMillis())));
  228. }
  229. return newState;
  230. }
  231. Array<int> AudioCDBurner::getAvailableWriteSpeeds() const
  232. {
  233. Array<int> results;
  234. const int maxSpeed = pimpl->getIntProperty (L"MaxWriteSpeed", 1);
  235. const int speeds[] = { 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 64, 80 };
  236. for (int i = 0; i < numElementsInArray (speeds); ++i)
  237. if (speeds[i] <= maxSpeed)
  238. results.add (speeds[i]);
  239. results.addIfNotAlreadyThere (maxSpeed);
  240. return results;
  241. }
  242. bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled)
  243. {
  244. if (pimpl->getIntProperty (L"BufferUnderrunFreeCapable", 0) == 0)
  245. return false;
  246. pimpl->setIntProperty (L"EnableBufferUnderrunFree", shouldBeEnabled ? -1 : 0);
  247. return pimpl->getIntProperty (L"EnableBufferUnderrunFree", 0) != 0;
  248. }
  249. int AudioCDBurner::getNumAvailableAudioBlocks() const
  250. {
  251. long blocksFree = 0;
  252. pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree);
  253. return blocksFree;
  254. }
  255. String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards,
  256. bool performFakeBurnForTesting, int writeSpeed)
  257. {
  258. pimpl->setIntProperty (L"WriteSpeed", writeSpeed > 0 ? writeSpeed : -1);
  259. pimpl->listener = listener;
  260. pimpl->progress = 0;
  261. pimpl->shouldCancel = false;
  262. UINT_PTR cookie;
  263. HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie);
  264. hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting,
  265. ejectDiscAfterwards);
  266. String error;
  267. if (hr != S_OK)
  268. {
  269. const char* e = "Couldn't open or write to the CD device";
  270. if (hr == IMAPI_E_USERABORT)
  271. e = "User cancelled the write operation";
  272. else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN)
  273. e = "No Disk present";
  274. error = e;
  275. }
  276. pimpl->discMaster->ProgressUnadvise (cookie);
  277. pimpl->listener = 0;
  278. return error;
  279. }
  280. bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples)
  281. {
  282. if (audioSource == 0)
  283. return false;
  284. ScopedPointer<AudioSource> source (audioSource);
  285. long bytesPerBlock;
  286. HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock);
  287. const int samplesPerBlock = bytesPerBlock / 4;
  288. bool ok = true;
  289. hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
  290. HeapBlock <byte> buffer (bytesPerBlock);
  291. AudioSampleBuffer sourceBuffer (2, samplesPerBlock);
  292. int samplesDone = 0;
  293. source->prepareToPlay (samplesPerBlock, 44100.0);
  294. while (ok)
  295. {
  296. {
  297. AudioSourceChannelInfo info (&sourceBuffer, 0, samplesPerBlock);
  298. sourceBuffer.clear();
  299. source->getNextAudioBlock (info);
  300. }
  301. buffer.clear (bytesPerBlock);
  302. typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian,
  303. AudioData::Interleaved, AudioData::NonConst> CDSampleFormat;
  304. typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian,
  305. AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat;
  306. CDSampleFormat left (buffer, 2);
  307. left.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (0)), samplesPerBlock);
  308. CDSampleFormat right (buffer + 2, 2);
  309. right.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (1)), samplesPerBlock);
  310. hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock);
  311. if (FAILED (hr))
  312. ok = false;
  313. samplesDone += samplesPerBlock;
  314. if (samplesDone >= numSamples)
  315. break;
  316. }
  317. hr = pimpl->redbook->CloseAudioTrack();
  318. return ok && hr == S_OK;
  319. }