The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

458 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  24. #if JUCE_USE_CDBURNER
  25. #import <Cocoa/Cocoa.h>
  26. #import <DiscRecording/DiscRecording.h>
  27. BEGIN_JUCE_NAMESPACE
  28. #include "../../../src/juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h"
  29. #include "../../../src/juce_appframework/audio/audio_sources/juce_AudioSource.h"
  30. #include "../../../src/juce_appframework/audio/dsp/juce_AudioDataConverters.h"
  31. #include "../../../src/juce_core/threads/juce_Thread.h"
  32. END_JUCE_NAMESPACE
  33. //==============================================================================
  34. @interface OpenDiskDevice : NSObject
  35. {
  36. DRDevice* device;
  37. NSMutableArray* tracks;
  38. }
  39. - (OpenDiskDevice*) initWithDevice: (DRDevice*) device;
  40. - (void) dealloc;
  41. - (bool) isDiskPresent;
  42. - (int) getNumAvailableAudioBlocks;
  43. - (void) addSourceTrack: (juce::AudioSource*) source numSamples: (int) numSamples_;
  44. - (void) burn: (juce::AudioCDBurner::BurnProgressListener*) listener errorString: (juce::String*) error
  45. ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting;
  46. @end
  47. //==============================================================================
  48. @interface AudioTrackProducer : NSObject
  49. {
  50. juce::AudioSource* source;
  51. int readPosition, lengthInFrames;
  52. }
  53. - (AudioTrackProducer*) init: (int) lengthInFrames;
  54. - (AudioTrackProducer*) initWithAudioSource: (juce::AudioSource*) source numSamples: (int) lengthInSamples;
  55. - (void) dealloc;
  56. - (void) setupTrackProperties: (DRTrack*) track;
  57. - (void) cleanupTrackAfterBurn: (DRTrack*) track;
  58. - (BOOL) cleanupTrackAfterVerification:(DRTrack*)track;
  59. - (uint64_t) estimateLengthOfTrack:(DRTrack*)track;
  60. - (BOOL) prepareTrack:(DRTrack*)track forBurn:(DRBurn*)burn
  61. toMedia:(NSDictionary*)mediaInfo;
  62. - (BOOL) prepareTrackForVerification:(DRTrack*)track;
  63. - (uint32_t) produceDataForTrack:(DRTrack*)track intoBuffer:(char*)buffer
  64. length:(uint32_t)bufferLength atAddress:(uint64_t)address
  65. blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags;
  66. - (uint32_t) producePreGapForTrack:(DRTrack*)track
  67. intoBuffer:(char*)buffer length:(uint32_t)bufferLength
  68. atAddress:(uint64_t)address blockSize:(uint32_t)blockSize
  69. ioFlags:(uint32_t*)flags;
  70. - (BOOL) verifyDataForTrack:(DRTrack*)track inBuffer:(const char*)buffer
  71. length:(uint32_t)bufferLength atAddress:(uint64_t)address
  72. blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags;
  73. - (uint32_t) producePreGapForTrack:(DRTrack*)track
  74. intoBuffer:(char*)buffer length:(uint32_t)bufferLength
  75. atAddress:(uint64_t)address blockSize:(uint32_t)blockSize
  76. ioFlags:(uint32_t*)flags;
  77. @end
  78. //==============================================================================
  79. @implementation OpenDiskDevice
  80. - (OpenDiskDevice*) initWithDevice: (DRDevice*) device_
  81. {
  82. [super init];
  83. device = device_;
  84. tracks = [[NSMutableArray alloc] init];
  85. return self;
  86. }
  87. - (void) dealloc
  88. {
  89. [tracks release];
  90. [super dealloc];
  91. }
  92. - (bool) isDiskPresent
  93. {
  94. return [device isValid]
  95. && [[[device status] objectForKey: DRDeviceMediaStateKey]
  96. isEqualTo: DRDeviceMediaStateMediaPresent];
  97. }
  98. - (int) getNumAvailableAudioBlocks
  99. {
  100. return [[[[device status] objectForKey: DRDeviceMediaInfoKey]
  101. objectForKey: DRDeviceMediaBlocksFreeKey] intValue];
  102. }
  103. - (void) addSourceTrack: (juce::AudioSource*) source_ numSamples: (int) numSamples_
  104. {
  105. AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: numSamples_];
  106. DRTrack* t = [[DRTrack alloc] initWithProducer: p];
  107. [p setupTrackProperties: t];
  108. [tracks addObject: t];
  109. [t release];
  110. [p release];
  111. }
  112. - (void) burn: (juce::AudioCDBurner::BurnProgressListener*) listener errorString: (juce::String*) error
  113. ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting
  114. {
  115. DRBurn* burn = [DRBurn burnForDevice: device];
  116. if (! [device acquireExclusiveAccess])
  117. {
  118. *error = "Couldn't open or write to the CD device";
  119. return;
  120. }
  121. [device acquireMediaReservation];
  122. NSMutableDictionary* d = [[burn properties] mutableCopy];
  123. [d autorelease];
  124. [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey];
  125. [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey];
  126. [d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount)
  127. forKey: DRBurnCompletionActionKey];
  128. [burn setProperties: d];
  129. [burn writeLayout: tracks];
  130. for (;;)
  131. {
  132. juce::Thread::sleep (300);
  133. float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue];
  134. NSLog ([[burn status] description]);
  135. if (listener != 0 && listener->audioCDBurnProgress (progress))
  136. {
  137. [burn abort];
  138. *error = "User cancelled the write operation";
  139. break;
  140. }
  141. if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed])
  142. {
  143. *error = "Write operation failed";
  144. break;
  145. }
  146. else if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone])
  147. {
  148. break;
  149. }
  150. NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey]
  151. objectForKey: DRErrorStatusErrorStringKey];
  152. if ([err length] > 0)
  153. {
  154. *error = juce::String::fromUTF8 ((juce::uint8*) [err UTF8String]);
  155. break;
  156. }
  157. }
  158. [device releaseMediaReservation];
  159. [device releaseExclusiveAccess];
  160. }
  161. @end
  162. //==============================================================================
  163. @implementation AudioTrackProducer
  164. - (AudioTrackProducer*) init: (int) lengthInFrames_
  165. {
  166. lengthInFrames = lengthInFrames_;
  167. readPosition = 0;
  168. return self;
  169. }
  170. - (void) setupTrackProperties: (DRTrack*) track
  171. {
  172. NSMutableDictionary* p = [[track properties] mutableCopy];
  173. [p setObject:[DRMSF msfWithFrames: lengthInFrames] forKey: DRTrackLengthKey];
  174. [p setObject:[NSNumber numberWithUnsignedShort:2352] forKey: DRBlockSizeKey];
  175. [p setObject:[NSNumber numberWithInt:0] forKey: DRDataFormKey];
  176. [p setObject:[NSNumber numberWithInt:0] forKey: DRBlockTypeKey];
  177. [p setObject:[NSNumber numberWithInt:0] forKey: DRTrackModeKey];
  178. [p setObject:[NSNumber numberWithInt:0] forKey: DRSessionFormatKey];
  179. [track setProperties: p];
  180. [p release];
  181. }
  182. - (AudioTrackProducer*) initWithAudioSource: (juce::AudioSource*) source_ numSamples: (int) lengthInSamples
  183. {
  184. AudioTrackProducer* s = [self init: (lengthInSamples + 587) / 588];
  185. if (s != nil)
  186. s->source = source_;
  187. return s;
  188. }
  189. - (void) dealloc
  190. {
  191. if (source != 0)
  192. {
  193. source->releaseResources();
  194. delete source;
  195. }
  196. [super dealloc];
  197. }
  198. - (void) cleanupTrackAfterBurn: (DRTrack*) track
  199. {
  200. }
  201. - (BOOL) cleanupTrackAfterVerification:(DRTrack*)track
  202. {
  203. return true;
  204. }
  205. - (uint64_t) estimateLengthOfTrack:(DRTrack*)track
  206. {
  207. return lengthInFrames;
  208. }
  209. - (BOOL) prepareTrack:(DRTrack*)track forBurn:(DRBurn*)burn
  210. toMedia:(NSDictionary*)mediaInfo
  211. {
  212. if (source != 0)
  213. source->prepareToPlay (44100 / 75, 44100);
  214. readPosition = 0;
  215. return true;
  216. }
  217. - (BOOL) prepareTrackForVerification:(DRTrack*)track
  218. {
  219. if (source != 0)
  220. source->prepareToPlay (44100 / 75, 44100);
  221. return true;
  222. }
  223. - (uint32_t) produceDataForTrack:(DRTrack*)track intoBuffer:(char*)buffer
  224. length:(uint32_t)bufferLength atAddress:(uint64_t)address
  225. blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags
  226. {
  227. if (source != 0)
  228. {
  229. const int numSamples = juce::jmin (bufferLength / 4, (lengthInFrames * (44100 / 75)) - readPosition);
  230. if (numSamples > 0)
  231. {
  232. juce::AudioSampleBuffer tempBuffer (2, numSamples);
  233. juce::AudioSourceChannelInfo info;
  234. info.buffer = &tempBuffer;
  235. info.startSample = 0;
  236. info.numSamples = numSamples;
  237. source->getNextAudioBlock (info);
  238. juce::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (0),
  239. buffer, numSamples, 4);
  240. juce::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (1),
  241. buffer + 2, numSamples, 4);
  242. readPosition += numSamples;
  243. }
  244. return numSamples * 4;
  245. }
  246. return 0;
  247. }
  248. - (uint32_t) producePreGapForTrack:(DRTrack*)track
  249. intoBuffer:(char*)buffer length:(uint32_t)bufferLength
  250. atAddress:(uint64_t)address blockSize:(uint32_t)blockSize
  251. ioFlags:(uint32_t*)flags
  252. {
  253. zeromem (buffer, bufferLength);
  254. return bufferLength;
  255. }
  256. - (BOOL) verifyDataForTrack:(DRTrack*)track inBuffer:(const char*)buffer
  257. length:(uint32_t)bufferLength atAddress:(uint64_t)address
  258. blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags
  259. {
  260. return true;
  261. }
  262. @end
  263. BEGIN_JUCE_NAMESPACE
  264. //==============================================================================
  265. AudioCDBurner::AudioCDBurner (const int deviceIndex)
  266. : internal (0)
  267. {
  268. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  269. OpenDiskDevice* dev = [[OpenDiskDevice alloc] initWithDevice: [[DRDevice devices] objectAtIndex: deviceIndex]];
  270. internal = (void*) dev;
  271. [pool release];
  272. }
  273. AudioCDBurner::~AudioCDBurner()
  274. {
  275. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  276. OpenDiskDevice* dev = (OpenDiskDevice*) internal;
  277. if (dev != 0)
  278. [dev release];
  279. [pool release];
  280. }
  281. AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex)
  282. {
  283. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  284. AudioCDBurner* b = new AudioCDBurner (deviceIndex);
  285. if (b->internal == 0)
  286. deleteAndZero (b);
  287. [pool release];
  288. return b;
  289. }
  290. static NSArray* findDiskBurnerDevices()
  291. {
  292. NSMutableArray* results = [NSMutableArray array];
  293. NSArray* devs = [DRDevice devices];
  294. if (devs != 0)
  295. {
  296. int num = [devs count];
  297. int i;
  298. for (i = 0; i < num; ++i)
  299. {
  300. NSDictionary* dic = [[devs objectAtIndex: i] info];
  301. NSString* name = [dic valueForKey: DRDeviceProductNameKey];
  302. if (name != nil)
  303. [results addObject: name];
  304. }
  305. }
  306. return results;
  307. }
  308. const StringArray AudioCDBurner::findAvailableDevices()
  309. {
  310. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  311. NSArray* names = findDiskBurnerDevices();
  312. StringArray s;
  313. for (int i = 0; i < [names count]; ++i)
  314. s.add (String::fromUTF8 ((juce::uint8*) [[names objectAtIndex: i] UTF8String]));
  315. [pool release];
  316. return s;
  317. }
  318. bool AudioCDBurner::isDiskPresent() const
  319. {
  320. OpenDiskDevice* dev = (OpenDiskDevice*) internal;
  321. return dev != 0 && [dev isDiskPresent];
  322. }
  323. int AudioCDBurner::getNumAvailableAudioBlocks() const
  324. {
  325. OpenDiskDevice* dev = (OpenDiskDevice*) internal;
  326. return [dev getNumAvailableAudioBlocks];
  327. }
  328. bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps)
  329. {
  330. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  331. OpenDiskDevice* dev = (OpenDiskDevice*) internal;
  332. if (dev != 0)
  333. {
  334. [dev addSourceTrack: source numSamples: numSamps];
  335. [pool release];
  336. return true;
  337. }
  338. [pool release];
  339. return false;
  340. }
  341. const String AudioCDBurner::burn (juce::AudioCDBurner::BurnProgressListener* listener,
  342. const bool ejectDiscAfterwards,
  343. const bool peformFakeBurnForTesting)
  344. {
  345. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  346. juce::String error ("Couldn't open or write to the CD device");
  347. OpenDiskDevice* dev = (OpenDiskDevice*) internal;
  348. if (dev != 0)
  349. {
  350. error = juce::String::empty;
  351. [dev burn: listener
  352. errorString: &error
  353. ejectAfterwards: ejectDiscAfterwards
  354. isFake: peformFakeBurnForTesting];
  355. }
  356. [pool release];
  357. return error;
  358. }
  359. END_JUCE_NAMESPACE
  360. #endif