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