11namespace tracktion {
inline namespace engine
14#if TRACKTION_ENABLE_FFMPEG
23 PROCESS_INFORMATION pi;
25 ZeroMemory (&si,
sizeof (si));
27 ZeroMemory (&pi,
sizeof (pi));
37 batFile.getFile ().replaceWithText (cmd);
48 false, CREATE_NO_WINDOW,
nullptr,
nullptr, &si, &pi))
51 WaitForSingleObject (pi.hProcess, INFINITE);
53 CloseHandle (pi.hProcess);
54 CloseHandle (pi.hThread);
70 double sampleRateIn,
unsigned int numberOfChannels,
72 : AudioFormatWriter (destStream, formatName, sampleRateIn,
73 numberOfChannels, (unsigned
int) bitsPerSampleIn),
74 vbrLevel (vbr), cbrBitrate (cbr), ffmpeg (exe), metadata (md)
76 if (
auto out = tempWav.getFile().createOutputStream())
77 writer.reset (
juce::WavAudioFormat().createWriterFor (out.release(), sampleRateIn, numChannels, bitsPerSampleIn, metadata, 0));
82 if (writer !=
nullptr)
91 bool write (
const int** samplesToWrite,
int numSamples)
override
93 return writer !=
nullptr && writer->write (samplesToWrite, numSamples);
97 int vbrLevel, cbrBitrate;
107 [[maybe_unused]]
auto output = readOutputFromSystem (processArgs.
joinIntoString (
" "));
113 bool convertToMP3()
const
119 args.
add (ffmpeg.getFullPathName().quoted());
121 args.
add (tempWav.getFile().getFullPathName().quoted());
122 args.
add (
"-codec:a");
123 args.
add (
"libmp3lame");
136 addMetadataArg (args,
"id3title",
"title");
137 addMetadataArg (args,
"id3artist",
"artist");
138 addMetadataArg (args,
"id3album",
"album");
139 addMetadataArg (args,
"id3comment",
"comment");
140 addMetadataArg (args,
"id3date",
"date");
141 addMetadataArg (args,
"id3genre",
"genre");
142 addMetadataArg (args,
"id3trackNumber",
"track");
146 if (runFFmpegChildProcess (tempMP3, args))
150 if (fis.openedOk() && output->writeFromInputStream (fis, -1) > 0)
160 void addMetadataArg (
juce::StringArray& args,
const char* key,
const char* ffmpegFlag)
const
162 auto value = metadata.getValue (key, {});
164 if (value.isNotEmpty())
166 args.
add (
"-metadata");
178FFmpegEncoderAudioFormat::FFmpegEncoderAudioFormat (
const juce::File& ffmpeg)
179 : AudioFormat (
"MP3 file",
".mp3"), ffmpegExe (ffmpeg)
183FFmpegEncoderAudioFormat::~FFmpegEncoderAudioFormat()
187bool FFmpegEncoderAudioFormat::canHandleFile (
const juce::File&)
194 return { 32000, 44100, 48000 };
202bool FFmpegEncoderAudioFormat::canDoStereo() {
return true; }
203bool FFmpegEncoderAudioFormat::canDoMono() {
return true; }
204bool FFmpegEncoderAudioFormat::isCompressed() {
return true; }
208 static const char* vbrOptions[] = {
"VBR quality 0 (best)",
"VBR quality 1",
"VBR quality 2",
"VBR quality 3",
209 "VBR quality 4 (normal)",
"VBR quality 5",
"VBR quality 6",
"VBR quality 7",
210 "VBR quality 8",
"VBR quality 9 (smallest)",
nullptr };
214 const int cbrRates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
216 for (
int i = 0; i <
std::ssize (cbrRates); ++i)
228 double sampleRateToUse,
229 unsigned int numberOfChannels,
232 int qualityOptionIndex)
234 if (streamToWriteTo ==
nullptr)
240 const juce::String qual (getQualityOptions() [qualityOptionIndex]);
242 if (qual.contains (
"VBR"))
243 vbr = qual.retainCharacters (
"0123456789").getIntValue();
245 cbr = qual.getIntValue();
247 return new Writer (streamToWriteTo, getFormatName(), ffmpegExe, vbr, cbr, sampleRateToUse, numberOfChannels, bitsPerSample, metadataValues);
bool existsAsFile() const
const String & getFullPathName() const noexcept
File getChildFile(StringRef relativeOrAbsolutePath) const
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
String loadFileAsString() const
String joinIntoString(StringRef separatorString, int startIndex=0, int numberOfElements=-1) const
void add(String stringToAdd)
int length() const noexcept
const char * toRawUTF8() const
String quoted(juce_wchar quoteCharacter='"') const
size_t getNumBytesAsUTF8() const noexcept
static String formatted(const String &formatStr, Args... args)
const File & getFile() const noexcept
void ignoreUnused(Types &&...) noexcept