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