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.

228 lines
7.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #if JUCE_USE_LAME_AUDIO_FORMAT
  18. class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter
  19. {
  20. public:
  21. Writer (OutputStream* destStream, const String& formatName,
  22. const File& lameApp, int vbr, int cbr,
  23. double sampleRate, unsigned int numberOfChannels,
  24. unsigned int bitsPerSample, const StringPairArray& metadata)
  25. : AudioFormatWriter (destStream, formatName, sampleRate,
  26. numberOfChannels, bitsPerSample),
  27. vbrLevel (vbr), cbrBitrate (cbr),
  28. tempWav (".wav")
  29. {
  30. WavAudioFormat wavFormat;
  31. if (FileOutputStream* out = tempWav.getFile().createOutputStream())
  32. {
  33. writer = wavFormat.createWriterFor (out, sampleRate, numChannels,
  34. bitsPerSample, metadata, 0);
  35. args.add (lameApp.getFullPathName());
  36. args.add ("--quiet");
  37. if (cbrBitrate == 0)
  38. {
  39. args.add ("--vbr-new");
  40. args.add ("-V");
  41. args.add (String (vbrLevel));
  42. }
  43. else
  44. {
  45. args.add ("--cbr");
  46. args.add ("-b");
  47. args.add (String (cbrBitrate));
  48. }
  49. addMetadataArg (metadata, "id3title", "--tt");
  50. addMetadataArg (metadata, "id3artist", "--ta");
  51. addMetadataArg (metadata, "id3album", "--tl");
  52. addMetadataArg (metadata, "id3comment", "--tc");
  53. addMetadataArg (metadata, "id3date", "--ty");
  54. addMetadataArg (metadata, "id3genre", "--tg");
  55. addMetadataArg (metadata, "id3trackNumber", "--tn");
  56. }
  57. }
  58. void addMetadataArg (const StringPairArray& metadata, const char* key, const char* lameFlag)
  59. {
  60. const String value (metadata.getValue (key, String::empty));
  61. if (value.isNotEmpty())
  62. {
  63. args.add (lameFlag);
  64. args.add (value);
  65. }
  66. }
  67. ~Writer()
  68. {
  69. if (writer != nullptr)
  70. {
  71. writer = nullptr;
  72. if (! convertToMP3())
  73. convertToMP3(); // try again
  74. }
  75. }
  76. bool write (const int** samplesToWrite, int numSamples)
  77. {
  78. return writer != nullptr && writer->write (samplesToWrite, numSamples);
  79. }
  80. private:
  81. int vbrLevel, cbrBitrate;
  82. TemporaryFile tempWav;
  83. ScopedPointer<AudioFormatWriter> writer;
  84. StringArray args;
  85. bool runLameChildProcess (const TemporaryFile& tempMP3, const StringArray& args) const
  86. {
  87. ChildProcess cp;
  88. if (cp.start (args))
  89. {
  90. const String childOutput (cp.readAllProcessOutput());
  91. DBG (childOutput); (void) childOutput;
  92. cp.waitForProcessToFinish (10000);
  93. return tempMP3.getFile().getSize() > 0;
  94. }
  95. return false;
  96. }
  97. bool convertToMP3() const
  98. {
  99. TemporaryFile tempMP3 (".mp3");
  100. StringArray args2 (args);
  101. args2.add (tempWav.getFile().getFullPathName());
  102. args2.add (tempMP3.getFile().getFullPathName());
  103. DBG (args2.joinIntoString (" "));
  104. if (runLameChildProcess (tempMP3, args2))
  105. {
  106. FileInputStream fis (tempMP3.getFile());
  107. if (fis.openedOk() && output->writeFromInputStream (fis, -1) > 0)
  108. {
  109. output->flush();
  110. return true;
  111. }
  112. }
  113. return false;
  114. }
  115. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Writer)
  116. };
  117. //==============================================================================
  118. static const char* const lameFormatName = "MP3 file";
  119. static const char* const lameExtensions[] = { ".mp3", nullptr };
  120. LAMEEncoderAudioFormat::LAMEEncoderAudioFormat (const File& lameApplication)
  121. : AudioFormat (TRANS (lameFormatName), StringArray (lameExtensions)),
  122. lameApp (lameApplication)
  123. {
  124. }
  125. LAMEEncoderAudioFormat::~LAMEEncoderAudioFormat()
  126. {
  127. }
  128. bool LAMEEncoderAudioFormat::canHandleFile (const File&)
  129. {
  130. return false;
  131. }
  132. Array<int> LAMEEncoderAudioFormat::getPossibleSampleRates()
  133. {
  134. const int rates[] = { 32000, 44100, 48000, 0 };
  135. return Array <int> (rates);
  136. }
  137. Array<int> LAMEEncoderAudioFormat::getPossibleBitDepths()
  138. {
  139. const int depths[] = { 16, 0 };
  140. return Array <int> (depths);
  141. }
  142. bool LAMEEncoderAudioFormat::canDoStereo() { return true; }
  143. bool LAMEEncoderAudioFormat::canDoMono() { return true; }
  144. bool LAMEEncoderAudioFormat::isCompressed() { return true; }
  145. StringArray LAMEEncoderAudioFormat::getQualityOptions()
  146. {
  147. const char* vbrOptions[] = { "VBR quality 0 (best)", "VBR quality 1", "VBR quality 2", "VBR quality 3",
  148. "VBR quality 4 (normal)", "VBR quality 5", "VBR quality 6", "VBR quality 7", "VBR quality 8",
  149. "VBR quality 9 (smallest)",
  150. nullptr };
  151. StringArray opts (vbrOptions);
  152. const int cbrRates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
  153. for (int i = 0; i < numElementsInArray (cbrRates); ++i)
  154. opts.add (String (cbrRates[i]) + " Kb/s CBR");
  155. return opts;
  156. }
  157. AudioFormatReader* LAMEEncoderAudioFormat::createReaderFor (InputStream*, const bool)
  158. {
  159. return nullptr;
  160. }
  161. AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* streamToWriteTo,
  162. double sampleRateToUse,
  163. unsigned int numberOfChannels,
  164. int bitsPerSample,
  165. const StringPairArray& metadataValues,
  166. int qualityOptionIndex)
  167. {
  168. int vbr = 4;
  169. int cbr = 0;
  170. const String qual (getQualityOptions() [qualityOptionIndex]);
  171. if (qual.contains ("VBR"))
  172. vbr = qual.retainCharacters ("0123456789").getIntValue();
  173. else
  174. cbr = qual.getIntValue();
  175. return new Writer (streamToWriteTo, getFormatName(), lameApp, vbr, cbr,
  176. sampleRateToUse, numberOfChannels, bitsPerSample, metadataValues);
  177. }
  178. #endif