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.

233 lines
7.2KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. #if JUCE_USE_LAME_AUDIO_FORMAT
  22. class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter
  23. {
  24. public:
  25. Writer (OutputStream* destStream, const String& formatName,
  26. const File& appFile, int vbr, int cbr,
  27. double sampleRate, unsigned int numberOfChannels,
  28. int bitsPerSample, const StringPairArray& metadata)
  29. : AudioFormatWriter (destStream, formatName, sampleRate,
  30. numberOfChannels, (unsigned int) bitsPerSample),
  31. vbrLevel (vbr), cbrBitrate (cbr),
  32. tempWav (".wav")
  33. {
  34. WavAudioFormat wavFormat;
  35. if (FileOutputStream* out = tempWav.getFile().createOutputStream())
  36. {
  37. writer = wavFormat.createWriterFor (out, sampleRate, numChannels,
  38. bitsPerSample, metadata, 0);
  39. args.add (appFile.getFullPathName());
  40. args.add ("--quiet");
  41. if (cbrBitrate == 0)
  42. {
  43. args.add ("--vbr-new");
  44. args.add ("-V");
  45. args.add (String (vbrLevel));
  46. }
  47. else
  48. {
  49. args.add ("--cbr");
  50. args.add ("-b");
  51. args.add (String (cbrBitrate));
  52. }
  53. addMetadataArg (metadata, "id3title", "--tt");
  54. addMetadataArg (metadata, "id3artist", "--ta");
  55. addMetadataArg (metadata, "id3album", "--tl");
  56. addMetadataArg (metadata, "id3comment", "--tc");
  57. addMetadataArg (metadata, "id3date", "--ty");
  58. addMetadataArg (metadata, "id3genre", "--tg");
  59. addMetadataArg (metadata, "id3trackNumber", "--tn");
  60. }
  61. }
  62. void addMetadataArg (const StringPairArray& metadata, const char* key, const char* lameFlag)
  63. {
  64. const String value (metadata.getValue (key, String()));
  65. if (value.isNotEmpty())
  66. {
  67. args.add (lameFlag);
  68. args.add (value);
  69. }
  70. }
  71. ~Writer()
  72. {
  73. if (writer != nullptr)
  74. {
  75. writer = nullptr;
  76. if (! convertToMP3())
  77. convertToMP3(); // try again
  78. }
  79. }
  80. bool write (const int** samplesToWrite, int numSamples)
  81. {
  82. return writer != nullptr && writer->write (samplesToWrite, numSamples);
  83. }
  84. private:
  85. int vbrLevel, cbrBitrate;
  86. TemporaryFile tempWav;
  87. ScopedPointer<AudioFormatWriter> writer;
  88. StringArray args;
  89. bool runLameChildProcess (const TemporaryFile& tempMP3, const StringArray& processArgs) const
  90. {
  91. ChildProcess cp;
  92. if (cp.start (processArgs))
  93. {
  94. const String childOutput (cp.readAllProcessOutput());
  95. DBG (childOutput); ignoreUnused (childOutput);
  96. cp.waitForProcessToFinish (10000);
  97. return tempMP3.getFile().getSize() > 0;
  98. }
  99. return false;
  100. }
  101. bool convertToMP3() const
  102. {
  103. TemporaryFile tempMP3 (".mp3");
  104. StringArray args2 (args);
  105. args2.add (tempWav.getFile().getFullPathName());
  106. args2.add (tempMP3.getFile().getFullPathName());
  107. DBG (args2.joinIntoString (" "));
  108. if (runLameChildProcess (tempMP3, args2))
  109. {
  110. FileInputStream fis (tempMP3.getFile());
  111. if (fis.openedOk() && output->writeFromInputStream (fis, -1) > 0)
  112. {
  113. output->flush();
  114. return true;
  115. }
  116. }
  117. return false;
  118. }
  119. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Writer)
  120. };
  121. //==============================================================================
  122. LAMEEncoderAudioFormat::LAMEEncoderAudioFormat (const File& lameApplication)
  123. : AudioFormat ("MP3 file", ".mp3"),
  124. lameApp (lameApplication)
  125. {
  126. }
  127. LAMEEncoderAudioFormat::~LAMEEncoderAudioFormat()
  128. {
  129. }
  130. bool LAMEEncoderAudioFormat::canHandleFile (const File&)
  131. {
  132. return false;
  133. }
  134. Array<int> LAMEEncoderAudioFormat::getPossibleSampleRates()
  135. {
  136. const int rates[] = { 32000, 44100, 48000, 0 };
  137. return Array<int> (rates);
  138. }
  139. Array<int> LAMEEncoderAudioFormat::getPossibleBitDepths()
  140. {
  141. const int depths[] = { 16, 0 };
  142. return Array<int> (depths);
  143. }
  144. bool LAMEEncoderAudioFormat::canDoStereo() { return true; }
  145. bool LAMEEncoderAudioFormat::canDoMono() { return true; }
  146. bool LAMEEncoderAudioFormat::isCompressed() { return true; }
  147. StringArray LAMEEncoderAudioFormat::getQualityOptions()
  148. {
  149. static const char* vbrOptions[] = { "VBR quality 0 (best)", "VBR quality 1", "VBR quality 2", "VBR quality 3",
  150. "VBR quality 4 (normal)", "VBR quality 5", "VBR quality 6", "VBR quality 7",
  151. "VBR quality 8", "VBR quality 9 (smallest)", nullptr };
  152. StringArray opts (vbrOptions);
  153. const int cbrRates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
  154. for (int i = 0; i < numElementsInArray (cbrRates); ++i)
  155. opts.add (String (cbrRates[i]) + " Kb/s CBR");
  156. return opts;
  157. }
  158. AudioFormatReader* LAMEEncoderAudioFormat::createReaderFor (InputStream*, const bool)
  159. {
  160. return nullptr;
  161. }
  162. AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* streamToWriteTo,
  163. double sampleRateToUse,
  164. unsigned int numberOfChannels,
  165. int bitsPerSample,
  166. const StringPairArray& metadataValues,
  167. int qualityOptionIndex)
  168. {
  169. if (streamToWriteTo == nullptr)
  170. return nullptr;
  171. int vbr = 4;
  172. int cbr = 0;
  173. const String qual (getQualityOptions() [qualityOptionIndex]);
  174. if (qual.contains ("VBR"))
  175. vbr = qual.retainCharacters ("0123456789").getIntValue();
  176. else
  177. cbr = qual.getIntValue();
  178. return new Writer (streamToWriteTo, getFormatName(), lameApp, vbr, cbr,
  179. sampleRateToUse, numberOfChannels, bitsPerSample, metadataValues);
  180. }
  181. #endif
  182. } // namespace juce