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.

263 lines
8.0KB

  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 CDReaderHelpers
  19. {
  20. inline const XmlElement* getElementForKey (const XmlElement& xml, const String& key)
  21. {
  22. forEachXmlChildElementWithTagName (xml, child, "key")
  23. if (child->getAllSubText().trim() == key)
  24. return child->getNextElement();
  25. return nullptr;
  26. }
  27. static int getIntValueForKey (const XmlElement& xml, const String& key, int defaultValue = -1)
  28. {
  29. const XmlElement* const block = getElementForKey (xml, key);
  30. return block != nullptr ? block->getAllSubText().trim().getIntValue() : defaultValue;
  31. }
  32. // Get the track offsets for a CD given an XmlElement representing its TOC.Plist.
  33. // Returns NULL on success, otherwise a const char* representing an error.
  34. static const char* getTrackOffsets (XmlDocument& xmlDocument, Array<int>& offsets)
  35. {
  36. const ScopedPointer<XmlElement> xml (xmlDocument.getDocumentElement());
  37. if (xml == nullptr)
  38. return "Couldn't parse XML in file";
  39. const XmlElement* const dict = xml->getChildByName ("dict");
  40. if (dict == nullptr)
  41. return "Couldn't get top level dictionary";
  42. const XmlElement* const sessions = getElementForKey (*dict, "Sessions");
  43. if (sessions == nullptr)
  44. return "Couldn't find sessions key";
  45. const XmlElement* const session = sessions->getFirstChildElement();
  46. if (session == nullptr)
  47. return "Couldn't find first session";
  48. const int leadOut = getIntValueForKey (*session, "Leadout Block");
  49. if (leadOut < 0)
  50. return "Couldn't find Leadout Block";
  51. const XmlElement* const trackArray = getElementForKey (*session, "Track Array");
  52. if (trackArray == nullptr)
  53. return "Couldn't find Track Array";
  54. forEachXmlChildElement (*trackArray, track)
  55. {
  56. const int trackValue = getIntValueForKey (*track, "Start Block");
  57. if (trackValue < 0)
  58. return "Couldn't find Start Block in the track";
  59. offsets.add (trackValue * AudioCDReader::samplesPerFrame - 88200);
  60. }
  61. offsets.add (leadOut * AudioCDReader::samplesPerFrame - 88200);
  62. return nullptr;
  63. }
  64. static void findDevices (Array<File>& cds)
  65. {
  66. File volumes ("/Volumes");
  67. volumes.findChildFiles (cds, File::findDirectories, false);
  68. for (int i = cds.size(); --i >= 0;)
  69. if (! cds.getReference(i).getChildFile (".TOC.plist").exists())
  70. cds.remove (i);
  71. }
  72. struct TrackSorter
  73. {
  74. static int getCDTrackNumber (const File& file)
  75. {
  76. return file.getFileName().initialSectionContainingOnly ("0123456789").getIntValue();
  77. }
  78. static int compareElements (const File& first, const File& second)
  79. {
  80. const int firstTrack = getCDTrackNumber (first);
  81. const int secondTrack = getCDTrackNumber (second);
  82. jassert (firstTrack > 0 && secondTrack > 0);
  83. return firstTrack - secondTrack;
  84. }
  85. };
  86. }
  87. //==============================================================================
  88. StringArray AudioCDReader::getAvailableCDNames()
  89. {
  90. Array<File> cds;
  91. CDReaderHelpers::findDevices (cds);
  92. StringArray names;
  93. for (int i = 0; i < cds.size(); ++i)
  94. names.add (cds.getReference(i).getFileName());
  95. return names;
  96. }
  97. AudioCDReader* AudioCDReader::createReaderForCD (const int index)
  98. {
  99. Array<File> cds;
  100. CDReaderHelpers::findDevices (cds);
  101. if (cds[index].exists())
  102. return new AudioCDReader (cds[index]);
  103. return nullptr;
  104. }
  105. AudioCDReader::AudioCDReader (const File& volume)
  106. : AudioFormatReader (0, "CD Audio"),
  107. volumeDir (volume),
  108. currentReaderTrack (-1),
  109. reader (0)
  110. {
  111. sampleRate = 44100.0;
  112. bitsPerSample = 16;
  113. numChannels = 2;
  114. usesFloatingPointData = false;
  115. refreshTrackLengths();
  116. }
  117. AudioCDReader::~AudioCDReader()
  118. {
  119. }
  120. void AudioCDReader::refreshTrackLengths()
  121. {
  122. tracks.clear();
  123. trackStartSamples.clear();
  124. lengthInSamples = 0;
  125. volumeDir.findChildFiles (tracks, File::findFiles | File::ignoreHiddenFiles, false, "*.aiff");
  126. CDReaderHelpers::TrackSorter sorter;
  127. tracks.sort (sorter);
  128. const File toc (volumeDir.getChildFile (".TOC.plist"));
  129. if (toc.exists())
  130. {
  131. XmlDocument doc (toc);
  132. const char* error = CDReaderHelpers::getTrackOffsets (doc, trackStartSamples);
  133. (void) error; // could be logged..
  134. lengthInSamples = trackStartSamples.getLast() - trackStartSamples.getFirst();
  135. }
  136. }
  137. bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  138. int64 startSampleInFile, int numSamples)
  139. {
  140. while (numSamples > 0)
  141. {
  142. int track = -1;
  143. for (int i = 0; i < trackStartSamples.size() - 1; ++i)
  144. {
  145. if (startSampleInFile < trackStartSamples.getUnchecked (i + 1))
  146. {
  147. track = i;
  148. break;
  149. }
  150. }
  151. if (track < 0)
  152. return false;
  153. if (track != currentReaderTrack)
  154. {
  155. reader = nullptr;
  156. FileInputStream* const in = tracks [track].createInputStream();
  157. if (in != nullptr)
  158. {
  159. BufferedInputStream* const bin = new BufferedInputStream (in, 65536, true);
  160. AiffAudioFormat format;
  161. reader = format.createReaderFor (bin, true);
  162. if (reader == nullptr)
  163. currentReaderTrack = -1;
  164. else
  165. currentReaderTrack = track;
  166. }
  167. }
  168. if (reader == nullptr)
  169. return false;
  170. const int startPos = (int) (startSampleInFile - trackStartSamples.getUnchecked (track));
  171. const int numAvailable = (int) jmin ((int64) numSamples, reader->lengthInSamples - startPos);
  172. reader->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer, startPos, numAvailable);
  173. numSamples -= numAvailable;
  174. startSampleInFile += numAvailable;
  175. }
  176. return true;
  177. }
  178. bool AudioCDReader::isCDStillPresent() const
  179. {
  180. return volumeDir.exists();
  181. }
  182. void AudioCDReader::ejectDisk()
  183. {
  184. JUCE_AUTORELEASEPOOL
  185. [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())];
  186. }
  187. bool AudioCDReader::isTrackAudio (int trackNum) const
  188. {
  189. return tracks [trackNum].hasFileExtension (".aiff");
  190. }
  191. void AudioCDReader::enableIndexScanning (bool)
  192. {
  193. // any way to do this on a Mac??
  194. }
  195. int AudioCDReader::getLastIndex() const
  196. {
  197. return 0;
  198. }
  199. Array<int> AudioCDReader::findIndexesInTrack (const int /*trackNumber*/)
  200. {
  201. return Array<int>();
  202. }